Config 1.1: URL

In Genelet, URL has the fixed format:

http://WEBSITE/HANDLER/role/mime/component?action=string&query...

where HANDLER is handler name. A web project should use one and only one handler. role is name for group of visitors, e.g. public or member. Genelet supports user-account authentication and OAuth. mime is the mime type of pages, e.g. json, xml and html.  Switching mimes will turn a page from one mime format to another. For example, replacing html by json in the URL will bring you the JSON view of the same HTML page. component is an object name, usually mapping to a database table. Finally, action=string defines action (i.e. function, or object method) on the component. And query contains additional query parameters.

A web project will consist of many components and actions.

You may use the URL routing tool in Chapter 4.1 to customize URLs. But in most cases, you should stick with the fixed format URL principle.

By inspecting the URL, Genelet knows how to process authentication, which componenet to dispatch, and which data type should be returned back to visitor.

genelet.js

This is genelet.js:

var opts = {lines: 13, length: 20, width: 10, radius: 30, corners: 1,
  rotate: 42, direction: 1, color: '#000', speed: 1.1, trail: 60, shadow: false,
  hwaccel: false, className: 'spinner', zIndex: 2e9, top: '50%', left: '50%'
};

var app = angular.module('app_wavelet', []);

app.controller('body_wavelet', function($scope, $http, $window, $location) {

  var spinner_name = document.getElementById('spinner');
  var spinner = new Spinner(opts).spin(spinner_name);

  var Initial = function() {
    $scope.showing = {};
    for (var i=0;i<GOTO.modals.length;i++) $scope.showing[GOTO.modals[i]]=false;
  }

  var ajax_page = function(role, comp, query, method, landing) {
    spinner.spin(spinner_name);

    var url = GOTO.url+"/"+role+"/"+GOTO.json+"/"+comp;

    if (angular.isUndefined(method)) method = 'GET';
    if (angular.isUndefined(query.action)) query.action = GOTO.action;
    var hash = {'url': url, 'method': method};
    if (method==='GET') {
      var pairs = [];
      for (var k in query) pairs.push(k+"="+encodeURIComponent(query[k]));
      hash.url += "?"+pairs.join("&");
    } else {
      hash.data = query;
    }
    $http(hash)
    .error(function(err, status, headers, config) {
      spinner.stop(spinner_name);
      $window.alert("Systm error. Status: "+status);
      return false;
    })
    .success(function(data) {
      spinner.stop(spinner_name);
      var a = "Lists";
      if (!data.Success && data.data != "logged" && data.data != "logout") {
        if (data.data == "challenge" || data.data == "failed") {
          if (angular.isUndefined(data.errorstr)) data.errorstr = data.data;
          $scope.names = data;
          $scope.partial_header = GOTO.role+"/"+GOTO.header+"."+GOTO.html;
          $scope.partial        = role+"/"+GOTO.login+"."+GOTO.html;
          $scope.partial_footer = GOTO.role+"/"+GOTO.footer+"."+GOTO.html;
        } else {
          $window.alert(data.data);
        }
        return false;
      }

      if (landing===true) {
        if (data[a]) $scope.single = angular.copy(data[a][0]);
        $scope.names = data;
        return true;
      } else if (landing===false) {
        $scope[$scope.note] = data;
        return true;
      } else if (angular.isString(landing)) {
        $scope.current_modal = landing;
        $scope.showing[landing] = !$scope.showing[landing];
        if (data[a]) $scope.single = angular.copy(data[a][0]);
        $scope[landing] = data;
        return true;
      } else if (angular.isObject(landing)) {
        if (angular.isString(landing.operator)) {
          var single = data[a][0];
          var lists;
          if (landing.target) {
            var a = landing.target.split('.');
            if (a.length==4) {
              var pos = $scope[a[0]][a[1]].map(function(e) {return e[a[2]];}).indexOf(data[a[2]]);
              if (pos < 0) return false;
              lists = $scope[a[0]][a[1]][pos][a[3]];
              if (angular.isUndefined(lists)) {
                $scope[a[0]][a[1]][pos][a[3]]=[];
                lists = $scope[a[0]][a[1]][pos][a[3]];
              }
            }
            if (a.length==3) {
              var s = $scope.names[$scope.names.action][0][a[0]];
              var pos=s.map(function(e) {return e[a[1]];}).indexOf(data[a[1]]);
              if (pos < 0) return false;
              lists = s[pos][a[2]];
              if (angular.isUndefined(lists)) {
                s[pos][a[2]]=[];
                lists = s[pos][a[2]];
              }
            }
            if (a.length==2) lists = $scope[a[0]][a[1]];
            if (a.length==1) lists = $scope[a[0]];
          } else {
            lists = $scope.names['Lists'];
          }
          if (angular.isUndefined(single) || angular.isUndefined(lists))
            return false;
          if (landing.operator==='insert') {
            if (landing.extra)
              angular.forEach(landing.extra, function(v,k) { single[k]=v; });
            lists.push(single);
          } else {
            var pos = lists.map(function(e) {return e[landing.id_name];}).indexOf(single[landing.id_name]);
            if (pos<0) {
                var id = parseInt(single[landing.id_name]);
                if (angular.isNumber(id)) {
                    pos = lists.map(function(e) {return e[landing.id_name];}).indexOf(id);
                }
            }
            if (pos < 0) return false; if (landing.operator==='delete') { lists.splice(pos,1); } else if (landing.operator==='update') { angular.forEach(lists[pos], function(v,k) { if (angular.isDefined(single[k])) lists[pos][k]=single[k]; }); } } return true; } else { var s = landing.query || {}; s.action = landing.action || GOTO.action; var r = landing.role || role; var c = landing.component || comp; return ajax_page(r, c, s, "GET", landing.refresh); } } if (data['Lists']) $scope.single = angular.copy(data['Lists'][0]); $scope.names = data; for (var k in $location.search()) $location.search(k, null); if (method==='GET') { for (var k in query) $location.search(k, query[k]); } else { Initial(); } $scope.partial_header = role+"/"+GOTO.header+"."+GOTO.html; $scope.partial = role+"/"+comp+"/"+query.action+"."+GOTO.html; $scope.partial_footer = role+"/"+GOTO.footer+"."+GOTO.html; $location.path("/"+role+"/"+comp); $location.replace(); }); return true; }; var parts = $location.path().split("/"); var role = GOTO.role; var component = GOTO.component; if (parts.length>=3) {
    role = parts[1];
    component = parts[2];
  }

  Initial();
  ajax_page(role, component, angular.copy($location.search()), "GET");

  var add_hidden = function(q) {
    if (angular.isUndefined(q)) q = {};
    var name = $scope.current_modal;
    angular.forEach($window.document.getElementsByTagName('input'), function(v,k) {
      if (v.type==='hidden') {
        var z = v.name.split('.');
        if (z.length===2 && z[0]===name && $scope.showing[name]) q[z[1]] = v.value;
        else if (z.length===1 && (angular.isUndefined(name) || !$scope.showing[name])) q[z[0]] = v.value;
      }
    });
  };

  var wrapper = function(r,c,a,q,method,landing) {
    if (angular.isUndefined(q)) q = {};
    q.action=a;
    return ajax_page(r, c, q, method, landing);
  };
  $scope.go = function(r,c,a,q,landing) {
    return wrapper(r,c,a,q,"GET",landing);
  }
  $scope.send = function(r,c,a,q,landing) {
    add_hidden(q);
    return wrapper(r,c,a,q,"POST",landing);
  }
  $scope.login = function(r,a,q,landing) {
    q.provider = a
    return ajax_page(r,GOTO.login,q,"POST",landing);
  }
  $scope.drop_go = function(r,c,a,q,landing) {
    if (angular.isString(landing)) return false;
    $scope.showing[$scope.current_modal]=!$scope.showing[$scope.current_modal];
    return wrapper(r,c,a,q,"GET",landing);
  }
  $scope.drop_send = function(r,c,a,q,landing) {
    if (angular.isString(landing)) return false;
    add_hidden(q);
    $scope.showing[$scope.current_modal]=!$scope.showing[$scope.current_modal];
    return wrapper(r,c,a,q,"POST",landing);
  }

  $scope.api_go = function(r,c,a,q,name,optional) {
    if (optional && angular.isObject($scope[name])) return;

    if (angular.isUndefined(q)) q = {};
    q.action=a;
    $scope.note = name;
    return ajax_page(r, c, q, 'GET', false);
  }

  $scope.going = function(name,f,s){
    $scope.single = angular.extend({},f,s);
    $scope.current_modal = name;
    $scope.showing[name] = !$scope.showing[name];
  };

  $scope.is_active = function(view_location) {
    return view_location === $location.path();
  };

  $scope.init_selection = function(arr, attrname_id) {
    var lists=[];
    angular.forEach(arr, function(v,k) {
      if(parseInt(v.attrname_id)===attrname_id) this.push(v.value_id);
    }, lists);
    return lists;
  };
  $scope.init_selection_one = function(arr,attrname_id) {
    var lists={};
    angular.forEach(arr, function(v,k) {
      if(parseInt(v.attrname_id)===attrname_id) lists=v;
    });
    return lists;
  };
  $scope.init_selection_multiple = function(arr, ref) {
    var lists={attrname_id:[]};
    angular.forEach(ref, function(v,k) {
      lists[v.attrname_id] = [];
    });
    angular.forEach(arr, function(v,k) {
      if(parseInt(v.attrname_id)>1000) {
        lists.attrname_id.push(v.attrname_id);
        lists[v.attrname_id].push(v.value_id);
      }
    });
    return lists;
  };
  $scope.init_general = function(arr, attr) {
    var lists=[];
    angular.forEach(arr, function(v,k) {
      this.push(v[attr]);
    }, lists);
    return lists;
  };
  $scope.toggle_selection = function(lists, value_id) {
    if (angular.isUndefined(lists)) lists=[];
    var idx = lists.indexOf(value_id);
    if (idx>-1) lists.splice(idx,1);
    else lists.push(value_id);
  };
  $scope.toggle_weight = function(lists, item, id_name) {
    var x=$scope.init_general(lists, id_name);
    var idx = x.indexOf(item[id_name]);
    if (idx>-1) lists.splice(idx,1);
    else lists.push(item);
  };
  $scope.show_weight = function(lists, item, id_name) {
    var x=$scope.init_general(lists, id_name);
    var idx = x.indexOf(item[id_name]);
    return (idx>-1) ? false : true;
  };
});

app.directive('modal', function() {
  return {
    template: '

‘, transclude: true, restrict: ‘E’, replace:true, scope:true, link: function postLink(scope, element, attrs) { var x = angular.element(element); scope.title = attrs.title; scope.json_url = attrs.json_url; scope.$watch(attrs.visible, function (value) { if(value == true) x.modal(‘show’); else x.modal(‘hide’); }); x.on(‘shown.bs.modal’, function() { scope.$apply(function() { var a = attrs.visible.split(‘.’); scope.$parent[a[0]][a[1]] = true; }); }); x.on(‘hidden.bs.modal’, function() { scope.$apply(function() { var a = attrs.visible.split(‘.’); scope.$parent[a[0]][a[1]] = false; }); }); } }; });