aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Angular.js54
-rw-r--r--src/Resource.js117
-rw-r--r--src/Scope.js32
3 files changed, 172 insertions, 31 deletions
diff --git a/src/Angular.js b/src/Angular.js
index 6cb3f602..9b3634be 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -20,22 +20,23 @@ if (typeof Node == 'undefined') {
function noop() {}
if (!window['console']) window['console']={'log':noop, 'error':noop};
-var consoleNode, msie,
+var consoleNode, msie,
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
foreach = _.each,
extend = _.extend,
identity = _.identity,
- angular = window['angular'] || (window['angular'] = {}),
- angularValidator = angular['validator'] || (angular['validator'] = {}),
- angularFilter = angular['filter'] || (angular['filter'] = {}),
- angularFormatter = angular['formatter'] || (angular['formatter'] = {}),
+ angular = window['angular'] || (window['angular'] = {}),
+ angularValidator = angular['validator'] || (angular['validator'] = {}),
+ angularFilter = angular['filter'] || (angular['filter'] = {}),
+ angularFormatter = angular['formatter'] || (angular['formatter'] = {}),
angularCallbacks = angular['callbacks'] || (angular['callbacks'] = {}),
angularAlert = angular['alert'] || (angular['alert'] = function(){
- log(arguments); window.alert.apply(window, arguments);
+ log(arguments); window.alert.apply(window, arguments);
});
+angular['copy'] = copy;
var isVisible = isVisible || function (element) {
- return jQuery(element).is(":visible");
+ return jQuery(element).is(":visible");
}
function log(a, b, c){
@@ -101,6 +102,29 @@ function isLeafNode (node) {
}
}
+function copy(source, destination){
+ if (!destination) {
+ if (!source) {
+ return source;
+ } else if (_.isArray(source)) {
+ return copy(source, []);
+ } else {
+ return copy(source, {});
+ }
+ } else {
+ if (_.isArray(source)) {
+ while(destination.length) {
+ destination.pop();
+ }
+ } else {
+ _(destination).each(function(value, key){
+ delete destination[key];
+ });
+ }
+ return $.extend(true, destination, source);
+ }
+};
+
function setHtml(node, html) {
if (isLeafNode(node)) {
if (msie) {
@@ -218,7 +242,7 @@ UrlWatcher.prototype = {
};
pull();
},
-
+
set: function(url) {
var existingURL = this.location.href;
if (!existingURL.match(/#/))
@@ -227,7 +251,7 @@ UrlWatcher.prototype = {
this.location.href = url;
this.existingURL = url;
},
-
+
get: function() {
return window.location.href;
}
@@ -326,10 +350,10 @@ function wireAngular(element, config) {
binder.entity(scope);
binder.compile();
controlBar.bind();
-
+
//TODO: remove this code
new PopUp(element).bind();
-
+
var self = _(exposeMethods(scope, {
'set': scope.set,
'get': scope.get,
@@ -338,8 +362,8 @@ function wireAngular(element, config) {
'init':function(){
config['location']['listen'](_(binder.onUrlChange).bind(binder));
binder.parseAnchor();
- binder.executeInit();
- binder.updateView();
+ binder.executeInit();
+ binder.updateView();
return self;
},
'element':element[0],
@@ -349,7 +373,7 @@ function wireAngular(element, config) {
return self;
}
-angular['startUrlWatcher'] = function(){
+angular['startUrlWatcher'] = function(){
var watcher = new UrlWatcher(window['location']);
watcher.watch();
return exposeMethods(watcher, {'listen':watcher.listen, 'set':watcher.set, 'get':watcher.get});
@@ -366,6 +390,6 @@ angular['compile'] = function(element, config) {
configureLogging(config);
configureJQueryPlugins();
-
+
return wireAngular(jQuery(element), config);
};
diff --git a/src/Resource.js b/src/Resource.js
new file mode 100644
index 00000000..c0c6d220
--- /dev/null
+++ b/src/Resource.js
@@ -0,0 +1,117 @@
+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, idPaths, actions){
+ var self = this;
+ var route = new Route(url);
+ actions = $.extend({}, ResourceFactory.DEFAULT_ACTIONS, actions);
+ function extractIds(data){
+ var ids = {};
+ foreach(idPaths, function(path, id){
+ ids[id] = Scope.getter(data, path);
+ });
+ 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.method(action.method, route.url($.extend({}, action.params || {}, extractIds(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;
+ };
+
+ 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."
+ }
+ Resource[name](params, this, callback);
+ };
+ }
+ });
+ return Resource;
+ }
+};
+
diff --git a/src/Scope.js b/src/Scope.js
index 4de57dd2..daf4b36c 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -53,11 +53,11 @@ Scope.prototype = {
});
});
},
-
+
addWidget: function(controller) {
if (controller) this.widgets.push(controller);
},
-
+
isProperty: function(exp) {
for ( var i = 0; i < exp.length; i++) {
var ch = exp.charAt(i);
@@ -67,12 +67,12 @@ Scope.prototype = {
}
return true;
},
-
+
get: function(path) {
// log('SCOPE.get', path, Scope.getter(this.state, path));
return Scope.getter(this.state, path);
},
-
+
set: function(path, value) {
// log('SCOPE.set', path, value);
var element = path.split('.');
@@ -89,11 +89,11 @@ Scope.prototype = {
instance[element.shift()] = value;
return value;
},
-
+
setEval: function(expressionText, value) {
this.eval(expressionText + "=" + toJson(value));
},
-
+
eval: function(expressionText, context) {
// log('Scope.eval', expressionText);
var expression = Scope.expressionCache[expressionText];
@@ -108,7 +108,7 @@ Scope.prototype = {
context.self = this.state;
return expression(context);
},
-
+
//TODO: Refactor. This function needs to be an execution closure for widgets
// move to widgets
// remove expression, just have inner closure.
@@ -126,8 +126,8 @@ Scope.prototype = {
}
return true;
} catch (e){
- error('Eval Widget Error:', e);
var jsonError = toJson(e, true);
+ error('Eval Widget Error:', jsonError);
widget.hasError = true;
jQuery(widget.view).
addClass('ng-exception').
@@ -138,7 +138,7 @@ Scope.prototype = {
return false;
}
},
-
+
validate: function(expressionText, value, element) {
var expression = Scope.expressionCache[expressionText];
if (!expression) {
@@ -148,21 +148,21 @@ Scope.prototype = {
var self = {scope:this, self:this.state, '$element':element};
return expression(self)(self, value);
},
-
+
entity: function(entityDeclaration, datastore) {
var expression = new Parser(entityDeclaration).entityDeclaration();
return expression({scope:this, datastore:datastore});
},
-
+
clearInvalid: function() {
var invalid = this.state['$invalidWidgets'];
while(invalid.length > 0) {invalid.pop();}
},
-
+
markInvalid: function(widget) {
this.state['$invalidWidgets'].push(widget);
},
-
+
watch: function(declaration) {
var self = this;
new Parser(declaration).watch()({
@@ -178,7 +178,7 @@ Scope.prototype = {
}
});
},
-
+
addWatchListener: function(watchExpression, listener) {
var watcher = this.watchListeners[watchExpression];
if (!watcher) {
@@ -187,7 +187,7 @@ Scope.prototype = {
}
watcher.listeners.push(listener);
},
-
+
fireWatchers: function() {
var self = this;
var fired = false;
@@ -203,4 +203,4 @@ Scope.prototype = {
});
return fired;
}
-}; \ No newline at end of file
+};