aboutsummaryrefslogtreecommitdiffstats
path: root/src/directives.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/directives.js')
-rw-r--r--src/directives.js234
1 files changed, 150 insertions, 84 deletions
diff --git a/src/directives.js b/src/directives.js
index 26cbfe2c..747da3f5 100644
--- a/src/directives.js
+++ b/src/directives.js
@@ -1,121 +1,187 @@
-
-angular.directive("auth", function(expression, element){
+angularDirective("ng-init", function(expression){
return function(){
- if(expression == "eager") {
- this.$users.fetchCurrent();
- }
+ this.$eval(expression);
};
});
-
-//expression = "book=Book:{year=2000}"
-angular.directive("entity", function(expression, element){
- //parse expression, ignore element
- var entityName; // "Book";
- var instanceName; // "book";
- var defaults; // {year: 2000};
-
- parse(expression);
-
+angularDirective("ng-eval", function(expression){
return function(){
- this[entityName] = this.$datastore.entity(entityName, defaults);
- this[instanceName] = this[entityName]();
- this.$watch("$anchor."+instanceName, function(newAnchor){
- this[instanceName] = this[entityName].get(this.$anchor[instanceName]);
- });
+ this.$addEval(expression);
};
});
-
-angular.directive("init", function(expression, element){
- return function(){
- this.$eval(expresssion);
+angularDirective("ng-bind", function(expression){
+ return function(element) {
+ this.$watch(expression, function(value){
+ element.text(value);
+ });
};
});
-
-//translation of {{ }} to ng-bind is external to this
-angular.directive("bind", function(expression, element){
- return function() {
- this.$watch(expression, function(value){
- element.innerText = value;
+var bindTemplateCache = {};
+function compileBindTemplate(template){
+ var fn = bindTemplateCache[template];
+ if (!fn) {
+ var bindings = [];
+ foreach(parseBindings(template), function(text){
+ var exp = binding(text);
+ bindings.push(exp ? function(){
+ return this.$eval(exp);
+ } : function(){
+ return text;
+ });
+ });
+ bindTemplateCache[template] = fn = function(){
+ var parts = [], self = this;
+ foreach(bindings, function(fn){
+ parts.push(fn.call(self));
+ });
+ return parts.join('');
+ };
+ }
+ return fn;
+};
+angularDirective("ng-bind-template", function(expression){
+ var templateFn = compileBindTemplate(expression);
+ return function(element) {
+ var lastValue;
+ this.$addEval(function() {
+ var value = templateFn.call(this);
+ if (value != lastValue) {
+ element.text(value);
+ lastValue = value;
+ }
});
};
});
-
-// translation of {{ }} to ng-bind-attr is external to this
-// <a href="http://example.com?id={{book.$id}}" alt="{{book.$name}}">link</a>
-// becomes
-// <a href="" ng-bind-attr="{href:'http://example.com?id={{book.$id}}', alt:'{{book.$name}}'}">link</a>
-angular.directive("bind-attr", function(expression, element){
- return function(expression, element){
- var jElement = jQuery(element);
- this.$watch(expression, _(jElement.attr).bind(jElement));
+angularDirective("ng-bind-attr", function(expression){
+ return function(element){
+ this.$addEval(function(){
+ foreach(this.$eval(expression), function(value, key){
+ element.attr(key, compileBindTemplate(value).call(this));
+ }, this);
+ });
};
});
-angular.directive("repeat", function(expression, element){
- var anchor = document.createComment(expression);
- jQuery(element).replace(anchor);
- var template = this.compile(element);
- var lhs = "item";
- var rhs = "items";
+angularDirective("ng-non-bindable", function(){
+ this.descend(false);
+});
+
+angularDirective("ng-repeat", function(expression, element){
+ var reference = this.comment("ng-repeat: " + expression),
+ r = element.removeAttr('ng-repeat'),
+ template = this.compile(element),
+ match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
+ lhs, rhs, valueIdent, keyIdent;
+ if (! match) {
+ throw "Expected ng-repeat in form of 'item in collection' but got '" +
+ expression + "'.";
+ }
+ lhs = match[1];
+ rhs = match[2];
+ match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
+ if (!match) {
+ throw "'item' in 'item in collection' should be identifier or (key, value) but got '" +
+ keyValue + "'.";
+ }
+ valueIdent = match[3] || match[1];
+ keyIdent = match[2];
+
+ var parent = element.parent();
+ element.replaceWith(reference);
return function(){
- var children = [];
- this.$eval(rhs, function(items){
- foreach(children, function(child){
- child.element.remove();
- });
- foreach(items, function(item){
- var child = template(item); // create scope
- element.addChild(child.element, anchor);
- children.push(child);
+ var children = [],
+ currentScope = this;
+ this.$addEval(rhs, function(items){
+ var index = 0, childCount = children.length, childScope, lastElement = reference;
+ foreach(items || [], function(value, key){
+ if (index < childCount) {
+ // reuse existing child
+ childScope = children[index];
+ } else {
+ // grow children
+ childScope = template(element.clone(), currentScope);
+ childScope.init();
+ childScope.scope.set('$index', index);
+ childScope.element.attr('ng-index', index);
+ lastElement.after(childScope.element);
+ children.push(childScope);
+ }
+ childScope.scope.set(valueIdent, value);
+ if (keyIdent) childScope.scope.set(keyIdent, key);
+ childScope.scope.updateView();
+ lastElement = childScope.element;
+ index ++;
});
+ // shrink children
+ while(children.length > index) {
+ children.pop().element.remove();
+ }
});
};
-});
+}, {exclusive: true});
+angularDirective("ng-action", function(expression, element){
+ return function(){
+ var self = this;
+ element.click(function(){
+ self.$eval(expression);
+ });
+ };
+});
-//ng-non-bindable
-angular.directive("non-bindable", function(expression, element){
- return false;
+angularDirective("ng-watch", function(expression, element){
+ var match = expression.match(/^([^.]*):(.*)$/);
+ if (!match) {
+ throw "Expecting watch expression 'ident_to_watch: watch_statement' got '"
+ + expression + "'";
+ }
+ return function(){
+ this.$watch(match[1], match[2]);
+ };
});
-//Styling
-//
-//ng-class
-//ng-class-odd, ng-class-even
-//ng-style
-//ng-show, ng-hide
+function ngClass(selector) {
+ return function(expression, element){
+ var existing = element[0].className + ' ';
+ return function(element){
+ this.$addEval(expression, function(value){
+ if (selector(this.$index)) {
+ if (isArray(value)) value = value.join(' ');
+ element[0].className = (existing + value).replace(/\s\s+/g, ' ');
+ }
+ });
+ };
+ };
+}
+angularDirective("ng-class", ngClass(function(){return true;}));
+angularDirective("ng-class-odd", ngClass(function(i){return i % 2 == 1;}));
+angularDirective("ng-class-even", ngClass(function(i){return i % 2 == 0;}));
-angular.directive("action", function(expression, element){
- return function(){
- var self = this;
- jQuery(element).click(function(){
- self.$eval(expression);
+angularDirective("ng-show", function(expression, element){
+ return function(element){
+ this.$addEval(expression, function(value){
+ element.css('display', toBoolean(value) ? '' : 'none');
});
};
});
-//ng-eval
-angular.directive("eval", function(expression, element){
- return function(){
- this.$eval(expression);
+angularDirective("ng-hide", function(expression, element){
+ return function(element){
+ this.$addEval(expression, function(value){
+ element.css('display', toBoolean(value) ? 'none' : '');
+ });
};
});
-//ng-watch
-// <div ng-watch="$anchor.book: book=Book.get();"/>
-angular.directive("watch", function(expression, element){
- var watches = {
- 'lhs':'rhs'
- }; // parse
- return function(){
- this.$watch(watches);
+
+angularDirective("ng-style", function(expression, element){
+ return function(element){
+ this.$addEval(expression, function(value){
+ element.css(value);
+ });
};
});
-//widget related
-//ng-validate, ng-required, ng-formatter
-//ng-error