| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
 | function Route(template, defaults) {
  this.template = template = template + '#';
  this.defaults = defaults || {};
  var urlParams = this.urlParams = {};
  foreach(template.split(/\W/), function(param){
    if (param && template.match(new RegExp(":" + param + "\\W"))) {
      urlParams[param] = true;
    }
  });
}
Route.prototype = {
  url: function(params) {
    var path = [];
    var self = this;
    var url = this.template;
    params = params || {};
    foreach(this.urlParams, function(value, urlParam){
      var value = params[urlParam] || self.defaults[urlParam] || "";
      url = url.replace(new RegExp(":" + urlParam + "(\\W)"), value + "$1");
    });
    url = url.replace(/\/?#$/, '');
    var query = [];
    foreach(params, function(value, key){
      if (!self.urlParams[key]) {
        query.push(encodeURI(key) + '=' + encodeURI(value));
      }
    });
    return url + (query.length ? '?' + query.join('&') : '');
  }
};
function ResourceFactory(xhr) {
  this.xhr = xhr;
}
ResourceFactory.DEFAULT_ACTIONS = {
  'get':    {method:'GET'},
  'save':   {method:'POST'},
  'query':  {method:'GET', isArray:true},
  'remove': {method:'DELETE'},
  'delete': {method:'DELETE'}
};
ResourceFactory.prototype = {
  route: function(url, paramDefaults, actions){
    var self = this;
    var route = new Route(url);
    actions = $.extend({}, ResourceFactory.DEFAULT_ACTIONS, actions);
    function extractParams(data){
      var ids = {};
      foreach(paramDefaults || {}, function(value, key){
        ids[key] = value.charAt && value.charAt(0) == '@' ? Scope.getter(data, value.substr(1)) : value;
      });
      return ids;
    }
    function Resource(value){
      copy(value || {}, this);
    };
    foreach(actions, function(action, name){
      var isGet = action.method == 'GET';
      var isPost = action.method == 'POST';
      Resource[name] = function (a1, a2, a3) {
        var params = {};
        var data;
        var callback = noop;
        switch(arguments.length) {
        case 3: callback = a3;
        case 2:
          if (typeof a2 == 'function') {
            callback = a2;
          } else {
            params = a1;
            data = a2;
            break;
          }
        case 1: if (isPost) data = a1; else params = a1; break;
        case 0: break;
        default:
          throw "Expected between 0-3 arguments [params, data, callback], got " + arguments.length + " arguments."
        }
        var value = action.isArray ? [] : new Resource(data);
        self.xhr(action.method, route.url($.extend({}, action.params || {}, extractParams(data), params)), data, function(response) {
          if (action.isArray) {
            foreach(response, function(item){
              value.push(new Resource(item));
            });
          } else {
            copy(response, value);
          }
          (callback||noop)(value);
        });
        return value;
      };
      Resource.bind = function(additionalParamDefaults){
        return self.route(url, $.extend({}, paramDefaults, additionalParamDefaults), actions);
      };
      if (!isGet) {
        Resource.prototype['$' + name] = function(a1, a2){
          var params = {};
          var callback = noop;
          switch(arguments.length) {
          case 2: params = a1, callback = a2;
          case 1: if (typeof a1 == 'function') callback = a1; else params = a1;
          case 0: break;
          default:
            throw "Expected between 1-3 arguments [params, data, callback], got " + arguments.length + " arguments."
          }
          var self = this;
          Resource[name](params, this, function(response){
            copy(response, self);
            callback(self);
          });
        };
      }
    });
    return Resource;
  }
};
 |