aboutsummaryrefslogtreecommitdiffstats
path: root/src/Binder.js
diff options
context:
space:
mode:
authorMisko Hevery2010-01-25 20:02:24 -0800
committerMisko Hevery2010-01-25 20:02:24 -0800
commit0b630972b15676b1c1b6c59edd564e4ee331ec70 (patch)
treebd31ca5b69b6ea03d906a3107dfe38a8c1adcb8e /src/Binder.js
parent19bbee030ba012b8fc4835c1d17e039804b2b94b (diff)
parent473e57e22532f9b85fc9dcc1bcc53e12a10154c2 (diff)
downloadangular.js-0b630972b15676b1c1b6c59edd564e4ee331ec70.tar.bz2
merge
Diffstat (limited to 'src/Binder.js')
-rw-r--r--src/Binder.js599
1 files changed, 305 insertions, 294 deletions
diff --git a/src/Binder.js b/src/Binder.js
index 86e99fb8..e516ec32 100644
--- a/src/Binder.js
+++ b/src/Binder.js
@@ -1,14 +1,14 @@
-// Copyright (C) 2009 BRAT Tech LLC
-nglr.Binder = function(doc, widgetFactory, urlWatcher, config) {
+function Binder(doc, widgetFactory, datastore, location, config) {
this.doc = doc;
- this.urlWatcher = urlWatcher;
+ this.location = location;
+ this.datastore = datastore;
this.anchor = {};
this.widgetFactory = widgetFactory;
this.config = config || {};
this.updateListeners = [];
-};
+}
-nglr.Binder.parseBindings = function(string) {
+Binder.parseBindings = function(string) {
var results = [];
var lastIndex = 0;
var index;
@@ -28,314 +28,325 @@ nglr.Binder.parseBindings = function(string) {
return results.length === 0 ? [ string ] : results;
};
-nglr.Binder.hasBinding = function(string) {
- var bindings = nglr.Binder.parseBindings(string);
- return bindings.length > 1 || nglr.Binder.binding(bindings[0]) !== null;
+Binder.hasBinding = function(string) {
+ var bindings = Binder.parseBindings(string);
+ return bindings.length > 1 || Binder.binding(bindings[0]) !== null;
};
-nglr.Binder.binding = function(string) {
+Binder.binding = function(string) {
var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);
return binding ? binding[1] : null;
};
-nglr.Binder.prototype.parseQueryString = function(query) {
- var params = {};
- query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,
- function (match, left, right) {
- if (left) params[decodeURIComponent(left)] = decodeURIComponent(right);
- });
- return params;
-};
-
-nglr.Binder.prototype.parseAnchor = function(url) {
- var self = this;
- url = url || this.urlWatcher.getUrl();
-
- var anchorIndex = url.indexOf('#');
- if (anchorIndex < 0) return;
- var anchor = url.substring(anchorIndex + 1);
-
- var anchorQuery = this.parseQueryString(anchor);
- jQuery.each(self.anchor, function(key, newValue) {
- delete self.anchor[key];
- });
- jQuery.each(anchorQuery, function(key, newValue) {
- self.anchor[key] = newValue;
- });
-};
-
-nglr.Binder.prototype.onUrlChange = function (url) {
- console.log("URL change detected", url);
- this.parseAnchor(url);
- this.updateView();
-};
-
-nglr.Binder.prototype.updateAnchor = function() {
- var url = this.urlWatcher.getUrl();
- var anchorIndex = url.indexOf('#');
- if (anchorIndex > -1)
- url = url.substring(0, anchorIndex);
- url += "#";
- var sep = '';
- for (var key in this.anchor) {
- var value = this.anchor[key];
- if (typeof value === 'undefined' || value === null) {
- delete this.anchor[key];
- } else {
- url += sep + encodeURIComponent(key);
- if (value !== true)
- url += "=" + encodeURIComponent(value);
- sep = '&';
- }
- }
- this.urlWatcher.setUrl(url);
- return url;
-};
-
-nglr.Binder.prototype.updateView = function() {
- var start = new Date().getTime();
- var scope = jQuery(this.doc).scope();
- scope.set("$invalidWidgets", []);
- scope.updateView();
- var end = new Date().getTime();
- this.updateAnchor();
- _.each(this.updateListeners, function(fn) {fn();});
-};
-
-nglr.Binder.prototype.executeInit = function() {
- jQuery("[ng-init]", this.doc).each(function() {
- var jThis = jQuery(this);
- var scope = jThis.scope();
- try {
- scope.eval(jThis.attr('ng-init'));
- } catch (e) {
- nglr.alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + nglr.toJson(e, true));
+Binder.prototype = {
+ parseQueryString: function(query) {
+ var params = {};
+ query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,
+ function (match, left, right) {
+ if (left) params[decodeURIComponent(left)] = decodeURIComponent(right);
+ });
+ return params;
+ },
+
+ parseAnchor: function() {
+ var self = this, url = this.location['get']() || "";
+
+ var anchorIndex = url.indexOf('#');
+ if (anchorIndex < 0) return;
+ var anchor = url.substring(anchorIndex + 1);
+
+ var anchorQuery = this.parseQueryString(anchor);
+ foreach(self.anchor, function(newValue, key) {
+ delete self.anchor[key];
+ });
+ foreach(anchorQuery, function(newValue, key) {
+ self.anchor[key] = newValue;
+ });
+ },
+
+ onUrlChange: function() {
+ this.parseAnchor();
+ this.updateView();
+ },
+
+ updateAnchor: function() {
+ var url = this.location['get']() || "";
+ var anchorIndex = url.indexOf('#');
+ if (anchorIndex > -1)
+ url = url.substring(0, anchorIndex);
+ url += "#";
+ var sep = '';
+ for (var key in this.anchor) {
+ var value = this.anchor[key];
+ if (typeof value === 'undefined' || value === null) {
+ delete this.anchor[key];
+ } else {
+ url += sep + encodeURIComponent(key);
+ if (value !== true)
+ url += "=" + encodeURIComponent(value);
+ sep = '&';
+ }
}
- });
-};
-
-nglr.Binder.prototype.entity = function (scope) {
- jQuery("[ng-entity]", this.doc).attr("ng-watch", function() {
- try {
- var jNode = jQuery(this);
- var decl = scope.entity(jNode.attr("ng-entity"));
- return decl + (jNode.attr('ng-watch') || "");
- } catch (e) {
- nglr.alert(e);
+ this.location['set'](url);
+ return url;
+ },
+
+ updateView: function() {
+ var start = new Date().getTime();
+ var scope = jQuery(this.doc).scope();
+ scope.set("$invalidWidgets", []);
+ scope.updateView();
+ var end = new Date().getTime();
+ this.updateAnchor();
+ _.each(this.updateListeners, function(fn) {fn();});
+ },
+
+ docFindWithSelf: function(exp){
+ var doc = jQuery(this.doc);
+ var selection = doc.find(exp);
+ if (doc.is(exp)){
+ selection = selection.andSelf();
}
- });
-};
-
-nglr.Binder.prototype.compile = function() {
- var jNode = jQuery(this.doc);
- var self = this;
- if (this.config.autoSubmit) {
- var submits = jQuery(":submit", this.doc).not("[ng-action]");
- submits.attr("ng-action", "$save()");
- submits.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr", '{disabled:"{{$invalidWidgets}}"}');
- }
- this.precompile(this.doc)(this.doc, jNode.scope(), "");
- jQuery("a[ng-action]", this.doc).live('click', function (event) {
- var jNode = jQuery(this);
- try {
- jNode.scope().eval(jNode.attr('ng-action'));
- jNode.removeAttr('ng-error');
- jNode.removeClass("ng-exception");
- } catch (e) {
- jNode.addClass("ng-exception");
- jNode.attr('ng-error', nglr.toJson(e, true));
+ return selection;
+ },
+
+ executeInit: function() {
+ this.docFindWithSelf("[ng-init]").each(function() {
+ var jThis = jQuery(this);
+ var scope = jThis.scope();
+ try {
+ scope.eval(jThis.attr('ng-init'));
+ } catch (e) {
+ alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + toJson(e, true));
+ }
+ });
+ },
+
+ entity: function (scope) {
+ var self = this;
+ this.docFindWithSelf("[ng-entity]").attr("ng-watch", function() {
+ try {
+ var jNode = jQuery(this);
+ var decl = scope.entity(jNode.attr("ng-entity"), self.datastore);
+ return decl + (jNode.attr('ng-watch') || "");
+ } catch (e) {
+ log(e);
+ alert(e);
+ }
+ });
+ },
+
+ compile: function() {
+ var jNode = jQuery(this.doc);
+ if (this.config['autoSubmit']) {
+ var submits = this.docFindWithSelf(":submit").not("[ng-action]");
+ submits.attr("ng-action", "$save()");
+ submits.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr", '{disabled:"{{$invalidWidgets}}"}');
}
- self.updateView();
- return false;
- });
-};
-
-nglr.Binder.prototype.translateBinding = function(node, parentPath, factories) {
- var path = parentPath.concat();
- var offset = path.pop();
- var parts = nglr.Binder.parseBindings(node.nodeValue);
- if (parts.length > 1 || nglr.Binder.binding(parts[0])) {
- var parent = node.parentNode;
- if (nglr.isLeafNode(parent)) {
- parent.setAttribute('ng-bind-template', node.nodeValue);
- factories.push({path:path, fn:function(node, scope, prefix) {
- return new nglr.BindUpdater(node, node.getAttribute('ng-bind-template'));
- }});
- } else {
- for (var i = 0; i < parts.length; i++) {
- var part = parts[i];
- var binding = nglr.Binder.binding(part);
- var newNode;
- if (binding) {
- newNode = document.createElement("span");
- var jNewNode = jQuery(newNode);
- jNewNode.attr("ng-bind", binding);
- if (i === 0) {
- factories.push({path:path.concat(offset + i), fn:nglr.Binder.prototype.ng_bind});
+ this.precompile(this.doc)(this.doc, jNode.scope(), "");
+ this.docFindWithSelf("a[ng-action]").live('click', function (event) {
+ var jNode = jQuery(this);
+ var scope = jNode.scope();
+ try {
+ scope.eval(jNode.attr('ng-action'));
+ jNode.removeAttr('ng-error');
+ jNode.removeClass("ng-exception");
+ } catch (e) {
+ jNode.addClass("ng-exception");
+ jNode.attr('ng-error', toJson(e, true));
+ }
+ scope.get('$updateView')();
+ return false;
+ });
+ },
+
+ translateBinding: function(node, parentPath, factories) {
+ var path = parentPath.concat();
+ var offset = path.pop();
+ var parts = Binder.parseBindings(node.nodeValue);
+ if (parts.length > 1 || Binder.binding(parts[0])) {
+ var parent = node.parentNode;
+ if (isLeafNode(parent)) {
+ parent.setAttribute('ng-bind-template', node.nodeValue);
+ factories.push({path:path, fn:function(node, scope, prefix) {
+ return new BindUpdater(node, node.getAttribute('ng-bind-template'));
+ }});
+ } else {
+ for (var i = 0; i < parts.length; i++) {
+ var part = parts[i];
+ var binding = Binder.binding(part);
+ var newNode;
+ if (binding) {
+ newNode = document.createElement("span");
+ var jNewNode = jQuery(newNode);
+ jNewNode.attr("ng-bind", binding);
+ if (i === 0) {
+ factories.push({path:path.concat(offset + i), fn:this.ng_bind});
+ }
+ } else if (msie && part.charAt(0) == ' ') {
+ newNode = document.createElement("span");
+ newNode.innerHTML = '&nbsp;' + part.substring(1);
+ } else {
+ newNode = document.createTextNode(part);
}
- } else if (nglr.msie && part.charAt(0) == ' ') {
- newNode = document.createElement("span");
- newNode.innerHTML = '&nbsp;' + part.substring(1);
- } else {
- newNode = document.createTextNode(part);
+ parent.insertBefore(newNode, node);
}
- parent.insertBefore(newNode, node);
}
+ parent.removeChild(node);
}
- parent.removeChild(node);
- }
-};
-
-nglr.Binder.prototype.precompile = function(root) {
- var factories = [];
- this.precompileNode(root, [], factories);
- return function (template, scope, prefix) {
- var len = factories.length;
- for (var i = 0; i < len; i++) {
- var factory = factories[i];
- var node = template;
- var path = factory.path;
- for (var j = 0; j < path.length; j++) {
- node = node.childNodes[path[j]];
+ },
+
+ precompile: function(root) {
+ var factories = [];
+ this.precompileNode(root, [], factories);
+ return function (template, scope, prefix) {
+ var len = factories.length;
+ for (var i = 0; i < len; i++) {
+ var factory = factories[i];
+ var node = template;
+ var path = factory.path;
+ for (var j = 0; j < path.length; j++) {
+ node = node.childNodes[path[j]];
+ }
+ try {
+ scope.addWidget(factory.fn(node, scope, prefix));
+ } catch (e) {
+ alert(e);
+ }
}
- try {
- scope.addWidget(factory.fn(node, scope, prefix));
- } catch (e) {
- nglr.alert(e);
+ };
+ },
+
+ precompileNode: function(node, path, factories) {
+ var nodeType = node.nodeType;
+ if (nodeType == Node.TEXT_NODE) {
+ this.translateBinding(node, path, factories);
+ return;
+ } else if (nodeType != Node.ELEMENT_NODE && nodeType != Node.DOCUMENT_NODE) {
+ return;
+ }
+
+ if (!node.getAttribute) return;
+ var nonBindable = node.getAttribute('ng-non-bindable');
+ if (nonBindable || nonBindable === "") return;
+
+ var attributes = node.attributes;
+ if (attributes) {
+ var bindings = node.getAttribute('ng-bind-attr');
+ node.removeAttribute('ng-bind-attr');
+ bindings = bindings ? fromJson(bindings) : {};
+ var attrLen = attributes.length;
+ for (var i = 0; i < attrLen; i++) {
+ var attr = attributes[i];
+ var attrName = attr.name;
+ // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
+ var attrValue = msie && attrName == 'href' ?
+ decodeURI(node.getAttribute(attrName, 2)) : attr.value;
+ if (Binder.hasBinding(attrValue)) {
+ bindings[attrName] = attrValue;
+ }
+ }
+ var json = toJson(bindings);
+ if (json.length > 2) {
+ node.setAttribute("ng-bind-attr", json);
}
}
- };
-};
-
-nglr.Binder.prototype.precompileNode = function(node, path, factories) {
- var nodeType = node.nodeType;
- if (nodeType == Node.TEXT_NODE) {
- this.translateBinding(node, path, factories);
- return;
- } else if (nodeType != Node.ELEMENT_NODE && nodeType != Node.DOCUMENT_NODE) {
- return;
- }
-
- if (!node.getAttribute) return;
- var nonBindable = node.getAttribute('ng-non-bindable');
- if (nonBindable || nonBindable === "") return;
-
- var attributes = node.attributes;
- if (attributes) {
- var bindings = node.getAttribute('ng-bind-attr');
- node.removeAttribute('ng-bind-attr');
- bindings = bindings ? nglr.fromJson(bindings) : {};
- var attrLen = attributes.length;
- for (var i = 0; i < attrLen; i++) {
- var attr = attributes[i];
- var attrName = attr.name;
- // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
- var attrValue = nglr.msie && attrName == 'href' ?
- decodeURI(node.getAttribute(attrName, 2)) : attr.value;
- if (nglr.Binder.hasBinding(attrValue)) {
- bindings[attrName] = attrValue;
+
+ if (!node.getAttribute) log(node);
+ var repeaterExpression = node.getAttribute('ng-repeat');
+ if (repeaterExpression) {
+ node.removeAttribute('ng-repeat');
+ var precompiled = this.precompile(node);
+ var view = document.createComment("ng-repeat: " + repeaterExpression);
+ var parentNode = node.parentNode;
+ parentNode.insertBefore(view, node);
+ parentNode.removeChild(node);
+ function template(childScope, prefix, i) {
+ var clone = jQuery(node).clone();
+ clone.css('display', '');
+ clone.attr('ng-repeat-index', "" + i);
+ clone.data('scope', childScope);
+ precompiled(clone[0], childScope, prefix + i + ":");
+ return clone;
}
+ factories.push({path:path, fn:function(node, scope, prefix) {
+ return new RepeaterUpdater(jQuery(node), repeaterExpression, template, prefix);
+ }});
+ return;
}
- var json = nglr.toJson(bindings);
- if (json.length > 2) {
- node.setAttribute("ng-bind-attr", json);
+
+ if (node.getAttribute('ng-eval')) factories.push({path:path, fn:this.ng_eval});
+ if (node.getAttribute('ng-bind')) factories.push({path:path, fn:this.ng_bind});
+ if (node.getAttribute('ng-bind-attr')) factories.push({path:path, fn:this.ng_bind_attr});
+ if (node.getAttribute('ng-hide')) factories.push({path:path, fn:this.ng_hide});
+ if (node.getAttribute('ng-show')) factories.push({path:path, fn:this.ng_show});
+ if (node.getAttribute('ng-class')) factories.push({path:path, fn:this.ng_class});
+ if (node.getAttribute('ng-class-odd')) factories.push({path:path, fn:this.ng_class_odd});
+ if (node.getAttribute('ng-class-even')) factories.push({path:path, fn:this.ng_class_even});
+ if (node.getAttribute('ng-style')) factories.push({path:path, fn:this.ng_style});
+ if (node.getAttribute('ng-watch')) factories.push({path:path, fn:this.ng_watch});
+ var nodeName = node.nodeName;
+ if ((nodeName == 'INPUT' ) ||
+ nodeName == 'TEXTAREA' ||
+ nodeName == 'SELECT' ||
+ nodeName == 'BUTTON') {
+ var self = this;
+ factories.push({path:path, fn:function(node, scope, prefix) {
+ node.name = prefix + node.name.split(":").pop();
+ return self.widgetFactory.createController(jQuery(node), scope);
+ }});
}
- }
-
- if (!node.getAttribute) console.log(node);
- var repeaterExpression = node.getAttribute('ng-repeat');
- if (repeaterExpression) {
- node.removeAttribute('ng-repeat');
- var precompiled = this.precompile(node);
- var view = document.createComment("ng-repeat: " + repeaterExpression);
- var parentNode = node.parentNode;
- parentNode.insertBefore(view, node);
- parentNode.removeChild(node);
- var template = function(childScope, prefix, i) {
- var clone = jQuery(node).clone();
- clone.css('display', '');
- clone.attr('ng-repeat-index', "" + i);
- clone.data('scope', childScope);
- precompiled(clone[0], childScope, prefix + i + ":");
- return clone;
- };
- factories.push({path:path, fn:function(node, scope, prefix) {
- return new nglr.RepeaterUpdater(jQuery(node), repeaterExpression, template, prefix);
- }});
- return;
- }
-
- if (node.getAttribute('ng-eval')) factories.push({path:path, fn:this.ng_eval});
- if (node.getAttribute('ng-bind')) factories.push({path:path, fn:this.ng_bind});
- if (node.getAttribute('ng-bind-attr')) factories.push({path:path, fn:this.ng_bind_attr});
- if (node.getAttribute('ng-hide')) factories.push({path:path, fn:this.ng_hide});
- if (node.getAttribute('ng-show')) factories.push({path:path, fn:this.ng_show});
- if (node.getAttribute('ng-class')) factories.push({path:path, fn:this.ng_class});
- if (node.getAttribute('ng-class-odd')) factories.push({path:path, fn:this.ng_class_odd});
- if (node.getAttribute('ng-class-even')) factories.push({path:path, fn:this.ng_class_even});
- if (node.getAttribute('ng-style')) factories.push({path:path, fn:this.ng_style});
- if (node.getAttribute('ng-watch')) factories.push({path:path, fn:this.ng_watch});
- var nodeName = node.nodeName;
- if ((nodeName == 'INPUT' ) ||
- nodeName == 'TEXTAREA' ||
- nodeName == 'SELECT' ||
- nodeName == 'BUTTON') {
- var self = this;
- factories.push({path:path, fn:function(node, scope, prefix) {
- node.name = prefix + node.name.split(":").pop();
- return self.widgetFactory.createController(jQuery(node), scope);
- }});
- }
- if (nodeName == 'OPTION') {
- var html = jQuery('<select/>').append(jQuery(node).clone()).html();
- if (!html.match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi)) {
- node.value = node.text;
+ if (nodeName == 'OPTION') {
+ var html = jQuery('<select/>').append(jQuery(node).clone()).html();
+ if (!html.match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi)) {
+ node.value = node.text;
+ }
}
+
+ var children = node.childNodes;
+ for (var k = 0; k < children.length; k++) {
+ this.precompileNode(children[k], path.concat(k), factories);
+ }
+ },
+
+ ng_eval: function(node) {
+ return new EvalUpdater(node, node.getAttribute('ng-eval'));
+ },
+
+ ng_bind: function(node) {
+ return new BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}");
+ },
+
+ ng_bind_attr: function(node) {
+ return new BindAttrUpdater(node, fromJson(node.getAttribute('ng-bind-attr')));
+ },
+
+ ng_hide: function(node) {
+ return new HideUpdater(node, node.getAttribute('ng-hide'));
+ },
+
+ ng_show: function(node) {
+ return new ShowUpdater(node, node.getAttribute('ng-show'));
+ },
+
+ ng_class: function(node) {
+ return new ClassUpdater(node, node.getAttribute('ng-class'));
+ },
+
+ ng_class_even: function(node) {
+ return new ClassEvenUpdater(node, node.getAttribute('ng-class-even'));
+ },
+
+ ng_class_odd: function(node) {
+ return new ClassOddUpdater(node, node.getAttribute('ng-class-odd'));
+ },
+
+ ng_style: function(node) {
+ return new StyleUpdater(node, node.getAttribute('ng-style'));
+ },
+
+ ng_watch: function(node, scope) {
+ scope.watch(node.getAttribute('ng-watch'));
}
-
- var children = node.childNodes;
- for (var k = 0; k < children.length; k++) {
- this.precompileNode(children[k], path.concat(k), factories);
- }
-};
-
-nglr.Binder.prototype.ng_eval = function(node) {
- return new nglr.EvalUpdater(node, node.getAttribute('ng-eval'));
-};
-
-nglr.Binder.prototype.ng_bind = function(node) {
- return new nglr.BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}");
-};
-
-nglr.Binder.prototype.ng_bind_attr = function(node) {
- return new nglr.BindAttrUpdater(node, nglr.fromJson(node.getAttribute('ng-bind-attr')));
-};
-
-nglr.Binder.prototype.ng_hide = function(node) {
- return new nglr.HideUpdater(node, node.getAttribute('ng-hide'));
-};
-
-nglr.Binder.prototype.ng_show = function(node) {
- return new nglr.ShowUpdater(node, node.getAttribute('ng-show'));
-};
-
-nglr.Binder.prototype.ng_class = function(node) {
- return new nglr.ClassUpdater(node, node.getAttribute('ng-class'));
-};
-
-nglr.Binder.prototype.ng_class_even = function(node) {
- return new nglr.ClassEvenUpdater(node, node.getAttribute('ng-class-even'));
-};
-
-nglr.Binder.prototype.ng_class_odd = function(node) {
- return new nglr.ClassOddUpdater(node, node.getAttribute('ng-class-odd'));
-};
-
-nglr.Binder.prototype.ng_style = function(node) {
- return new nglr.StyleUpdater(node, node.getAttribute('ng-style'));
-};
-
-nglr.Binder.prototype.ng_watch = function(node, scope) {
- scope.watch(node.getAttribute('ng-watch'));
-};
+}; \ No newline at end of file