aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--angular-debug.js53
-rw-r--r--src/services.js15
-rw-r--r--src/widgets.js2
-rw-r--r--test/servicesSpec.js13
-rw-r--r--test/widgetsSpec.js5
5 files changed, 80 insertions, 8 deletions
diff --git a/angular-debug.js b/angular-debug.js
index d80db4df..c96947a3 100644
--- a/angular-debug.js
+++ b/angular-debug.js
@@ -33,6 +33,7 @@ var consoleNode,
PRIORITY_FIRST = -99999,
PRIORITY_WATCH = -1000,
PRIORITY_LAST = 99999,
+ PRIORITY = {'FIRST': PRIORITY_FIRST, 'LAST': PRIORITY_LAST, 'WATCH':PRIORITY_WATCH},
NOOP = 'noop',
NG_EXCEPTION = 'ng-exception',
NG_VALIDATION_ERROR = 'ng-validation-error',
@@ -501,17 +502,34 @@ function toJsonArray(buf, obj, pretty, stack){
* bind to a new instance of elements. It also provides a list
* of child paths which contain child templates
*/
-function Template() {
+function Template(priority) {
this.paths = [];
this.children = [];
this.inits = [];
+ this.priority = priority || 0;
}
Template.prototype = {
init: function(element, scope) {
+ var inits = {};
+ this.collectInits(element, inits);
+ foreachSorted(inits, function(queue){
+ foreach(queue, function(fn){
+ fn(scope);
+ });
+ });
+ },
+
+ collectInits: function(element, inits) {
+ var queue = inits[this.priority];
+ if (!queue) {
+ inits[this.priority] = queue = [];
+ }
element = jqLite(element);
foreach(this.inits, function(fn) {
- scope.$tryEval(fn, element, element);
+ queue.push(function(scope) {
+ scope.$tryEval(fn, element, element);
+ });
});
var i,
@@ -520,7 +538,7 @@ Template.prototype = {
paths = this.paths,
length = paths.length;
for (i = 0; i < length; i++) {
- children[i].init(childNodes[paths[i]], scope);
+ children[i].collectInits(childNodes[paths[i]], inits);
}
},
@@ -575,13 +593,13 @@ Compiler.prototype = {
};
},
- templatize: function(element){
+ templatize: function(element, priority){
var self = this,
widget,
directiveFns = self.directives,
descend = true,
directives = true,
- template = new Template(),
+ template,
selfApi = {
compile: bind(self, self.compile),
comment:function(text) {return jqLite(document.createComment(text));},
@@ -590,7 +608,11 @@ Compiler.prototype = {
descend: function(value){ if(isDefined(value)) descend = value; return descend;},
directives: function(value){ if(isDefined(value)) directives = value; return directives;}
};
-
+ priority = element.attr('ng-eval-order') || priority || 0;
+ if (isString(priority)) {
+ priority = PRIORITY[uppercase(priority)] || 0;
+ }
+ template = new Template(priority);
eachAttribute(element, function(value, name){
if (!widget) {
if (widget = self.widgets['@' + name]) {
@@ -632,7 +654,7 @@ Compiler.prototype = {
// Process non text child nodes
if (descend) {
eachNode(element, function(child, i){
- template.addChild(i, self.templatize(child));
+ template.addChild(i, self.templatize(child, priority));
});
}
return template.empty() ? null : template;
@@ -3339,6 +3361,8 @@ angularWidget('NG:SWITCH', function ngSwitch(element){
element.append(switchCase.element);
childScope.$tryEval(switchCase.change, element);
switchCase.template(switchCase.element, childScope);
+ if (scope.$invalidWidgets)
+ scope.$invalidWidgets.clearOrphans();
childScope.$init();
}
});
@@ -3491,6 +3515,21 @@ angularService("$invalidWidgets", function(){
});
return count;
};
+ invalidWidgets.clearOrphans = function() {
+ for(var i = 0; i < invalidWidgets.length;) {
+ var widget = invalidWidgets[i];
+ if (isOrphan(widget[0])) {
+ invalidWidgets.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ };
+ function isOrphan(widget) {
+ if (widget == window.document) return false;
+ var parent = widget.parentNode;
+ return !parent || isOrphan(parent);
+ }
return invalidWidgets;
});
var browserSingleton;
diff --git a/src/services.js b/src/services.js
index e2132f1c..e91a8eca 100644
--- a/src/services.js
+++ b/src/services.js
@@ -115,5 +115,20 @@ angularService("$invalidWidgets", function(){
});
return count;
};
+ invalidWidgets.clearOrphans = function() {
+ for(var i = 0; i < invalidWidgets.length;) {
+ var widget = invalidWidgets[i];
+ if (isOrphan(widget[0])) {
+ invalidWidgets.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ };
+ function isOrphan(widget) {
+ if (widget == window.document) return false;
+ var parent = widget.parentNode;
+ return !parent || isOrphan(parent);
+ }
return invalidWidgets;
});
diff --git a/src/widgets.js b/src/widgets.js
index f5f02813..b281ac2e 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -225,6 +225,8 @@ angularWidget('NG:SWITCH', function ngSwitch(element){
element.append(switchCase.element);
childScope.$tryEval(switchCase.change, element);
switchCase.template(switchCase.element, childScope);
+ if (scope.$invalidWidgets)
+ scope.$invalidWidgets.clearOrphans();
childScope.$init();
}
});
diff --git a/test/servicesSpec.js b/test/servicesSpec.js
index 91cc1f0e..618d9a15 100644
--- a/test/servicesSpec.js
+++ b/test/servicesSpec.js
@@ -80,11 +80,22 @@ describe("service $invalidWidgets", function(){
});
it("should count number of invalid widgets", function(){
- var scope = compile('<input name="price" ng-required></input>').$init();
+ var scope = compile('<input name="price" ng-required ng-validate="number"></input>').$init();
expect(scope.$invalidWidgets.length).toEqual(1);
scope.price = 123;
scope.$eval();
expect(scope.$invalidWidgets.length).toEqual(0);
scope.$element.remove();
+ scope.price = 'abc';
+ scope.$eval();
+ expect(scope.$invalidWidgets.length).toEqual(1);
+
+ jqLite(document.body).append(scope.$element);
+ scope.$invalidWidgets.clearOrphans();
+ expect(scope.$invalidWidgets.length).toEqual(1);
+
+ jqLite(document.body).html('');
+ scope.$invalidWidgets.clearOrphans();
+ expect(scope.$invalidWidgets.length).toEqual(0);
});
});
diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js
index c6158c37..c64f03ca 100644
--- a/test/widgetsSpec.js
+++ b/test/widgetsSpec.js
@@ -221,10 +221,15 @@ describe('ng:switch', function(){
it('should call init on switch', function(){
var scope = compile('<ng:switch on="url" change="name=\'works\'"><div ng-switch-when="a">{{name}}</div></ng:include>');
+ var cleared = false;
scope.url = 'a';
+ scope.$invalidWidgets = {clearOrphans: function(){
+ cleared = true;
+ }};
scope.$init();
expect(scope.name).toEqual(undefined);
expect(scope.$element.text()).toEqual('works');
+ expect(cleared).toEqual(true);
});
});