From 4e7f652b5f4f7943511bf532fb6753b042a0dbf2 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 8 Dec 2016 13:56:34 +0000 Subject: [PATCH 001/697] chore(package): update dist-tag for master/1.7 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 74d142c3a8bf..f66ed7f1aa4c 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "angularjs", "license": "MIT", "branchVersion": "^1.5.8", - "branchPattern": "1.6.*", - "distTag": "latest", + "branchPattern": "1.7.*", + "distTag": "next", "repository": { "type": "git", "url": "https://github.com/angular/angular.js.git" From a9e91461a892e140e3200b35eaf44060322cbb41 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Thu, 8 Dec 2016 11:12:45 +0200 Subject: [PATCH 002/697] fix(jqLite): silently ignore `after()` if element has no parent Previously, the element was always assumed to have a parent and an error was thrown when that was not the case. This commit makes jqLite consistent with jQuery, which silently ignores a call on elements that do not have a parent. Fixes #15331 Closes #15367 Closes #15475 --- src/jqLite.js | 13 ++++++++----- test/jqLiteSpec.js | 8 ++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/jqLite.js b/src/jqLite.js index e882407be63f..1f61c80999d0 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -979,12 +979,15 @@ forEach({ after: function(element, newElement) { var index = element, parent = element.parentNode; - newElement = new JQLite(newElement); - for (var i = 0, ii = newElement.length; i < ii; i++) { - var node = newElement[i]; - parent.insertBefore(node, index.nextSibling); - index = node; + if (parent) { + newElement = new JQLite(newElement); + + for (var i = 0, ii = newElement.length; i < ii; i++) { + var node = newElement[i]; + parent.insertBefore(node, index.nextSibling); + index = node; + } } }, diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js index 886ca7c2cd42..5333b029a142 100644 --- a/test/jqLiteSpec.js +++ b/test/jqLiteSpec.js @@ -2182,6 +2182,14 @@ describe('jqLite', function() { span.after('abc'); expect(root.html().toLowerCase()).toEqual('abc'); }); + + + it('should not throw when the element has no parent', function() { + var span = jqLite(''); + expect(function() { span.after('abc'); }).not.toThrow(); + expect(span.length).toBe(1); + expect(span[0].outerHTML).toBe(''); + }); }); From 9595337f67df619a2666c9a63b4394a119b9ef3e Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 8 Dec 2016 23:42:36 +0000 Subject: [PATCH 003/697] chore(package): update docs app to run on 1.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f66ed7f1aa4c..b05194cdbca1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "angularjs", "license": "MIT", - "branchVersion": "^1.5.8", + "branchVersion": "^1.6.0", "branchPattern": "1.7.*", "distTag": "next", "repository": { From 5b477614ff92dcdf5eff62e2143b7fb43aab5653 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Fri, 9 Dec 2016 01:12:59 +0000 Subject: [PATCH 004/697] chore(docs): fix plnkrOpener to use $onInit Since 1.6.0 does not preassign bindings before running the controller constructor function, we must move initialisation into the `$onInit` method. --- docs/app/src/examples.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/app/src/examples.js b/docs/app/src/examples.js index b888db3b087d..b5b21c5393fa 100644 --- a/docs/app/src/examples.js +++ b/docs/app/src/examples.js @@ -159,10 +159,11 @@ angular.module('examples', []) }; - // Initialize the example data, so it's ready when clicking the open button. - // Otherwise pop-up blockers will prevent a new window from opening - ctrl.prepareExampleData(ctrl.example.path); - + ctrl.$onInit = function() { + // Initialize the example data, so it's ready when clicking the open button. + // Otherwise pop-up blockers will prevent a new window from opening + ctrl.prepareExampleData(ctrl.example.path); + }; }] }; }]) From 1c0c2605d3ca7c4ad6d838b6a1f28755632f9afc Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Wed, 23 Nov 2016 17:54:53 +0200 Subject: [PATCH 005/697] fix($rootScope): when adding/removing watchers during $digest Previously, adding a watcher during a `$digest` (i.e. from within a watcher), would result in the next watcher getting skipped. Similarly, removing a watcher during a `$digest` could result in the current watcher being run twice (if the removed watcher had not run yet in the current `$digest`). This commit fixes both cases by keeping track of the current watcher index during a digest and properly updating it when adding/removing watchers. Fixes #15422 Closes #15424 --- src/ng/rootScope.js | 15 +++++--- test/ng/rootScopeSpec.js | 77 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index e26442f2fa56..89279608464c 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -416,15 +416,21 @@ function $RootScopeProvider() { if (!array) { array = scope.$$watchers = []; + array.$$digestWatchIndex = -1; } // we use unshift since we use a while loop in $digest for speed. // the while loop reads in reverse order. array.unshift(watcher); + array.$$digestWatchIndex++; incrementWatchersCount(this, 1); return function deregisterWatch() { - if (arrayRemove(array, watcher) >= 0) { + var index = arrayRemove(array, watcher); + if (index >= 0) { incrementWatchersCount(scope, -1); + if (index < array.$$digestWatchIndex) { + array.$$digestWatchIndex--; + } } lastDirtyWatch = null; }; @@ -757,7 +763,6 @@ function $RootScopeProvider() { $digest: function() { var watch, value, last, fn, get, watchers, - length, dirty, ttl = TTL, next, current, target = this, watchLog = [], @@ -798,10 +803,10 @@ function $RootScopeProvider() { do { // "traverse the scopes" loop if ((watchers = current.$$watchers)) { // process our watches - length = watchers.length; - while (length--) { + watchers.$$digestWatchIndex = watchers.length; + while (watchers.$$digestWatchIndex--) { try { - watch = watchers[length]; + watch = watchers[watchers.$$digestWatchIndex]; // Most common watches are on primitives, in which case we can short // circuit it with === operator, only when === fails do we use .equals if (watch) { diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index 66a5eeeed3e7..93f3dc9e6eef 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -498,6 +498,78 @@ describe('Scope', function() { expect(watch2).toHaveBeenCalled(); })); + + it('should not skip watchers when adding new watchers during digest', + inject(function($rootScope) { + var log = []; + + var watchFn1 = function() { log.push(1); }; + var watchFn2 = function() { log.push(2); }; + var watchFn3 = function() { log.push(3); }; + var addWatcherOnce = function(newValue, oldValue) { + if (newValue === oldValue) { + $rootScope.$watch(watchFn3); + } + }; + + $rootScope.$watch(watchFn1, addWatcherOnce); + $rootScope.$watch(watchFn2); + + $rootScope.$digest(); + + expect(log).toEqual([1, 2, 3, 1, 2, 3]); + }) + ); + + + it('should not run the current watcher twice when removing a watcher during digest', + inject(function($rootScope) { + var log = []; + var removeWatcher3; + + var watchFn3 = function() { log.push(3); }; + var watchFn2 = function() { log.push(2); }; + var watchFn1 = function() { log.push(1); }; + var removeWatcherOnce = function(newValue, oldValue) { + if (newValue === oldValue) { + removeWatcher3(); + } + }; + + $rootScope.$watch(watchFn1, removeWatcherOnce); + $rootScope.$watch(watchFn2); + removeWatcher3 = $rootScope.$watch(watchFn3); + + $rootScope.$digest(); + + expect(log).toEqual([1, 2, 1, 2]); + }) + ); + + + it('should not skip watchers when removing itself during digest', + inject(function($rootScope) { + var log = []; + var removeWatcher1; + + var watchFn3 = function() { log.push(3); }; + var watchFn2 = function() { log.push(2); }; + var watchFn1 = function() { log.push(1); }; + var removeItself = function() { + removeWatcher1(); + }; + + removeWatcher1 = $rootScope.$watch(watchFn1, removeItself); + $rootScope.$watch(watchFn2); + $rootScope.$watch(watchFn3); + + $rootScope.$digest(); + + expect(log).toEqual([1, 2, 3, 2, 3]); + }) + ); + + it('should not infinitely digest when current value is NaN', inject(function($rootScope) { $rootScope.$watch(function() { return NaN;}); @@ -598,7 +670,7 @@ describe('Scope', function() { $rootScope.$digest(); - expect(log).toEqual(['watch1', 'watchAction1', 'watch1', 'watch3', 'watchAction3', + expect(log).toEqual(['watch1', 'watchAction1', 'watch3', 'watchAction3', 'watch1', 'watch3']); scope.$destroy(); log.reset(); @@ -895,8 +967,7 @@ describe('Scope', function() { $rootScope.$watch(log.fn('w5'), log.fn('w5action')); }); $rootScope.$digest(); - expect(log).toEqual(['w1', 'w2', 'w3', 'w4', 'w4action', - 'w1', 'w2', 'w3', 'w4', 'w5', 'w5action', + expect(log).toEqual(['w1', 'w2', 'w3', 'w4', 'w4action', 'w5', 'w5action', 'w1', 'w2', 'w3', 'w4', 'w5']); })); From d05f894aff18ccacdded9cd7ce8d362947f4e6a2 Mon Sep 17 00:00:00 2001 From: Aman Mittal Date: Fri, 9 Dec 2016 13:18:45 +0530 Subject: [PATCH 006/697] docs(guide/external-resources): add "AngularJS in Action" book Closes #15480 --- docs/content/guide/external-resources.ngdoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/content/guide/external-resources.ngdoc b/docs/content/guide/external-resources.ngdoc index 90d3ece38022..bc2063187f52 100644 --- a/docs/content/guide/external-resources.ngdoc +++ b/docs/content/guide/external-resources.ngdoc @@ -119,7 +119,8 @@ You can find a larger list of Angular external libraries at [ngmodules.org](http ### Books * [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston * [AngularJS Essentials (Free eBook)](https://www.packtpub.com/packt/free-ebook/angularjs-essentials) by Rodrigo Branas -* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda +* [AngularJS in Action](https://www.manning.com/books/angularjs-in-action) by Lukas Ruebbelke +* [AngularJS: Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda * [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen * [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri * [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller From 7c80d8afa9d974b284921a1999b840d0ca30e3c9 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Fri, 7 Oct 2016 18:58:53 +0300 Subject: [PATCH 007/697] refactor(ngClass): remove redundant `$observe`r and dependency on `$animate` Includes the following commits (see #15246 for details): - **refactor(ngClass): remove unnecessary dependency on `$animate`** - **refactor(ngClass): remove redundant `$observe`r** The code was added in b41fe9f in order to support having both `ngClass` and interpolation in `class` work together. `ngClass` has changed considerably since b41fe9f and for simple cases to work the `$observe`r is no longer necessary (as indicated by the expanded test still passing). That said, it is a [documented known issue][1] that `ngClass` should not be used together with interpolation in `class` and more complicated cases do not work anyway. [1]: https://docs.angularjs.org/api/ng/directive/ngClass#known-issues --- src/ng/directive/ngClass.js | 17 ++-- test/ng/directive/ngClassSpec.js | 143 +++++++++++++++++++++---------- 2 files changed, 103 insertions(+), 57 deletions(-) diff --git a/src/ng/directive/ngClass.js b/src/ng/directive/ngClass.js index 7e138b065dc2..190ead08ceb0 100644 --- a/src/ng/directive/ngClass.js +++ b/src/ng/directive/ngClass.js @@ -8,7 +8,8 @@ function classDirective(name, selector) { name = 'ngClass' + name; - return ['$animate', function($animate) { + + return [function() { return { restrict: 'AC', link: function(scope, element, attr) { @@ -16,11 +17,6 @@ function classDirective(name, selector) { scope.$watch(attr[name], ngClassWatchAction, true); - attr.$observe('class', function(value) { - ngClassWatchAction(scope.$eval(attr[name])); - }); - - if (name !== 'ngClass') { scope.$watch('$index', function($index, old$index) { /* eslint-disable no-bitwise */ @@ -69,12 +65,9 @@ function classDirective(name, selector) { var toRemove = arrayDifference(oldClasses, newClasses); toAdd = digestClassCounts(toAdd, 1); toRemove = digestClassCounts(toRemove, -1); - if (toAdd && toAdd.length) { - $animate.addClass(element, toAdd); - } - if (toRemove && toRemove.length) { - $animate.removeClass(element, toRemove); - } + + attr.$addClass(toAdd); + attr.$removeClass(toRemove); } function ngClassWatchAction(newVal) { diff --git a/test/ng/directive/ngClassSpec.js b/test/ng/directive/ngClassSpec.js index b30d1318317c..dd91a3224edb 100644 --- a/test/ng/directive/ngClassSpec.js +++ b/test/ng/directive/ngClassSpec.js @@ -244,21 +244,34 @@ describe('ngClass', function() { })); - it('should allow ngClassOdd/Even on the same element with overlapping classes', inject(function($rootScope, $compile, $animate) { - var className; - - element = $compile('