angularjs-rails 1.5.6 → 1.5.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.5.6
2
+ * @license AngularJS v1.5.8
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -167,6 +167,12 @@ ngTouch.factory('$swipe', [function() {
167
167
  move: 'touchmove',
168
168
  end: 'touchend',
169
169
  cancel: 'touchcancel'
170
+ },
171
+ 'pointer': {
172
+ start: 'pointerdown',
173
+ move: 'pointermove',
174
+ end: 'pointerup',
175
+ cancel: 'pointercancel'
170
176
  }
171
177
  };
172
178
 
@@ -201,15 +207,15 @@ ngTouch.factory('$swipe', [function() {
201
207
  * The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
202
208
  * object containing event handlers.
203
209
  * The pointer types that should be used can be specified via the optional
204
- * third argument, which is an array of strings `'mouse'` and `'touch'`. By default,
205
- * `$swipe` will listen for `mouse` and `touch` events.
210
+ * third argument, which is an array of strings `'mouse'`, `'touch'` and `'pointer'`. By default,
211
+ * `$swipe` will listen for `mouse`, `touch` and `pointer` events.
206
212
  *
207
213
  * The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
208
214
  * receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw
209
215
  * `event`. `cancel` receives the raw `event` as its single parameter.
210
216
  *
211
- * `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is
212
- * watching for `touchmove` or `mousemove` events. These events are ignored until the total
217
+ * `start` is called on either `mousedown`, `touchstart` or `pointerdown`. After this event, `$swipe` is
218
+ * watching for `touchmove`, `mousemove` or `pointermove` events. These events are ignored until the total
213
219
  * distance moved in either dimension exceeds a small threshold.
214
220
  *
215
221
  * Once this threshold is exceeded, either the horizontal or vertical delta is greater.
@@ -217,12 +223,12 @@ ngTouch.factory('$swipe', [function() {
217
223
  * - If the vertical distance is greater, this is a scroll, and we let the browser take over.
218
224
  * A `cancel` event is sent.
219
225
  *
220
- * `move` is called on `mousemove` and `touchmove` after the above logic has determined that
226
+ * `move` is called on `mousemove`, `touchmove` and `pointermove` after the above logic has determined that
221
227
  * a swipe is in progress.
222
228
  *
223
- * `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`.
229
+ * `end` is called when a swipe is successfully completed with a `touchend`, `mouseup` or `pointerup`.
224
230
  *
225
- * `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling
231
+ * `cancel` is called either on a `touchcancel` or `pointercancel` from the browser, or when we begin scrolling
226
232
  * as described above.
227
233
  *
228
234
  */
@@ -236,7 +242,7 @@ ngTouch.factory('$swipe', [function() {
236
242
  // Whether a swipe is active.
237
243
  var active = false;
238
244
 
239
- pointerTypes = pointerTypes || ['mouse', 'touch'];
245
+ pointerTypes = pointerTypes || ['mouse', 'touch', 'pointer'];
240
246
  element.on(getEvents(pointerTypes, 'start'), function(event) {
241
247
  startCoords = getCoordinates(event);
242
248
  active = true;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.5.6
2
+ * @license AngularJS v1.5.8
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -57,7 +57,7 @@ function minErr(module, ErrorConstructor) {
57
57
  return match;
58
58
  });
59
59
 
60
- message += '\nhttp://errors.angularjs.org/1.5.6/' +
60
+ message += '\nhttp://errors.angularjs.org/1.5.8/' +
61
61
  (module ? module + '/' : '') + code;
62
62
 
63
63
  for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
@@ -126,7 +126,6 @@ function minErr(module, ErrorConstructor) {
126
126
  includes: true,
127
127
  arrayRemove: true,
128
128
  copy: true,
129
- shallowCopy: true,
130
129
  equals: true,
131
130
  csp: true,
132
131
  jq: true,
@@ -822,7 +821,13 @@ function arrayRemove(array, value) {
822
821
  * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
823
822
  * are deleted and then all elements/properties from the source are copied to it.
824
823
  * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
825
- * * If `source` is identical to 'destination' an exception will be thrown.
824
+ * * If `source` is identical to `destination` an exception will be thrown.
825
+ *
826
+ * <br />
827
+ * <div class="alert alert-warning">
828
+ * Only enumerable properties are taken into account. Non-enumerable properties (both on `source`
829
+ * and on `destination`) will be ignored.
830
+ * </div>
826
831
  *
827
832
  * @param {*} source The source that will be used to make a copy.
828
833
  * Can be any type, including primitives, `null`, and `undefined`.
@@ -831,41 +836,42 @@ function arrayRemove(array, value) {
831
836
  * @returns {*} The copy or updated `destination`, if `destination` was specified.
832
837
  *
833
838
  * @example
834
- <example module="copyExample">
835
- <file name="index.html">
836
- <div ng-controller="ExampleController">
837
- <form novalidate class="simple-form">
838
- Name: <input type="text" ng-model="user.name" /><br />
839
- E-mail: <input type="email" ng-model="user.email" /><br />
840
- Gender: <input type="radio" ng-model="user.gender" value="male" />male
841
- <input type="radio" ng-model="user.gender" value="female" />female<br />
842
- <button ng-click="reset()">RESET</button>
843
- <button ng-click="update(user)">SAVE</button>
844
- </form>
845
- <pre>form = {{user | json}}</pre>
846
- <pre>master = {{master | json}}</pre>
847
- </div>
848
-
849
- <script>
850
- angular.module('copyExample', [])
851
- .controller('ExampleController', ['$scope', function($scope) {
852
- $scope.master= {};
853
-
854
- $scope.update = function(user) {
855
- // Example with 1 argument
856
- $scope.master= angular.copy(user);
857
- };
839
+ <example module="copyExample">
840
+ <file name="index.html">
841
+ <div ng-controller="ExampleController">
842
+ <form novalidate class="simple-form">
843
+ <label>Name: <input type="text" ng-model="user.name" /></label><br />
844
+ <label>Age: <input type="number" ng-model="user.age" /></label><br />
845
+ Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
846
+ <label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
847
+ <button ng-click="reset()">RESET</button>
848
+ <button ng-click="update(user)">SAVE</button>
849
+ </form>
850
+ <pre>form = {{user | json}}</pre>
851
+ <pre>master = {{master | json}}</pre>
852
+ </div>
853
+ </file>
854
+ <file name="script.js">
855
+ // Module: copyExample
856
+ angular.
857
+ module('copyExample', []).
858
+ controller('ExampleController', ['$scope', function($scope) {
859
+ $scope.master = {};
860
+
861
+ $scope.reset = function() {
862
+ // Example with 1 argument
863
+ $scope.user = angular.copy($scope.master);
864
+ };
858
865
 
859
- $scope.reset = function() {
860
- // Example with 2 arguments
861
- angular.copy($scope.master, $scope.user);
862
- };
866
+ $scope.update = function(user) {
867
+ // Example with 2 arguments
868
+ angular.copy(user, $scope.master);
869
+ };
863
870
 
864
- $scope.reset();
865
- }]);
866
- </script>
867
- </file>
868
- </example>
871
+ $scope.reset();
872
+ }]);
873
+ </file>
874
+ </example>
869
875
  */
870
876
  function copy(source, destination) {
871
877
  var stackSource = [];
@@ -972,7 +978,7 @@ function copy(source, destination) {
972
978
  case '[object Uint8ClampedArray]':
973
979
  case '[object Uint16Array]':
974
980
  case '[object Uint32Array]':
975
- return new source.constructor(copyElement(source.buffer));
981
+ return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length);
976
982
 
977
983
  case '[object ArrayBuffer]':
978
984
  //Support: IE10
@@ -1004,31 +1010,6 @@ function copy(source, destination) {
1004
1010
  }
1005
1011
  }
1006
1012
 
1007
- /**
1008
- * Creates a shallow copy of an object, an array or a primitive.
1009
- *
1010
- * Assumes that there are no proto properties for objects.
1011
- */
1012
- function shallowCopy(src, dst) {
1013
- if (isArray(src)) {
1014
- dst = dst || [];
1015
-
1016
- for (var i = 0, ii = src.length; i < ii; i++) {
1017
- dst[i] = src[i];
1018
- }
1019
- } else if (isObject(src)) {
1020
- dst = dst || {};
1021
-
1022
- for (var key in src) {
1023
- if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1024
- dst[key] = src[key];
1025
- }
1026
- }
1027
- }
1028
-
1029
- return dst || src;
1030
- }
1031
-
1032
1013
 
1033
1014
  /**
1034
1015
  * @ngdoc function
@@ -2369,7 +2350,34 @@ function setupModuleLoader(window) {
2369
2350
 
2370
2351
  }
2371
2352
 
2372
- /* global: toDebugString: true */
2353
+ /* global shallowCopy: true */
2354
+
2355
+ /**
2356
+ * Creates a shallow copy of an object, an array or a primitive.
2357
+ *
2358
+ * Assumes that there are no proto properties for objects.
2359
+ */
2360
+ function shallowCopy(src, dst) {
2361
+ if (isArray(src)) {
2362
+ dst = dst || [];
2363
+
2364
+ for (var i = 0, ii = src.length; i < ii; i++) {
2365
+ dst[i] = src[i];
2366
+ }
2367
+ } else if (isObject(src)) {
2368
+ dst = dst || {};
2369
+
2370
+ for (var key in src) {
2371
+ if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
2372
+ dst[key] = src[key];
2373
+ }
2374
+ }
2375
+ }
2376
+
2377
+ return dst || src;
2378
+ }
2379
+
2380
+ /* global toDebugString: true */
2373
2381
 
2374
2382
  function serializeObject(obj) {
2375
2383
  var seen = [];
@@ -2473,6 +2481,7 @@ function toDebugString(obj) {
2473
2481
  $HttpParamSerializerJQLikeProvider,
2474
2482
  $HttpBackendProvider,
2475
2483
  $xhrFactoryProvider,
2484
+ $jsonpCallbacksProvider,
2476
2485
  $LocationProvider,
2477
2486
  $LogProvider,
2478
2487
  $ParseProvider,
@@ -2510,11 +2519,11 @@ function toDebugString(obj) {
2510
2519
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2511
2520
  */
2512
2521
  var version = {
2513
- full: '1.5.6', // all of these placeholder strings will be replaced by grunt's
2522
+ full: '1.5.8', // all of these placeholder strings will be replaced by grunt's
2514
2523
  major: 1, // package task
2515
2524
  minor: 5,
2516
- dot: 6,
2517
- codeName: 'arrow-stringification'
2525
+ dot: 8,
2526
+ codeName: 'arbitrary-fallbacks'
2518
2527
  };
2519
2528
 
2520
2529
 
@@ -2545,7 +2554,7 @@ function publishExternalAPI(angular) {
2545
2554
  'isDate': isDate,
2546
2555
  'lowercase': lowercase,
2547
2556
  'uppercase': uppercase,
2548
- 'callbacks': {counter: 0},
2557
+ 'callbacks': {$$counter: 0},
2549
2558
  'getTestability': getTestability,
2550
2559
  '$$minErr': minErr,
2551
2560
  '$$csp': csp,
@@ -2634,6 +2643,7 @@ function publishExternalAPI(angular) {
2634
2643
  $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2635
2644
  $httpBackend: $HttpBackendProvider,
2636
2645
  $xhrFactory: $xhrFactoryProvider,
2646
+ $jsonpCallbacks: $jsonpCallbacksProvider,
2637
2647
  $location: $LocationProvider,
2638
2648
  $log: $LogProvider,
2639
2649
  $parse: $ParseProvider,
@@ -2710,7 +2720,7 @@ function publishExternalAPI(angular) {
2710
2720
  * ## Angular's jqLite
2711
2721
  * jqLite provides only the following jQuery methods:
2712
2722
  *
2713
- * - [`addClass()`](http://api.jquery.com/addClass/)
2723
+ * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
2714
2724
  * - [`after()`](http://api.jquery.com/after/)
2715
2725
  * - [`append()`](http://api.jquery.com/append/)
2716
2726
  * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
@@ -2737,7 +2747,7 @@ function publishExternalAPI(angular) {
2737
2747
  * - [`ready()`](http://api.jquery.com/ready/)
2738
2748
  * - [`remove()`](http://api.jquery.com/remove/)
2739
2749
  * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2740
- * - [`removeClass()`](http://api.jquery.com/removeClass/)
2750
+ * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument
2741
2751
  * - [`removeData()`](http://api.jquery.com/removeData/)
2742
2752
  * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2743
2753
  * - [`text()`](http://api.jquery.com/text/)
@@ -2872,7 +2882,7 @@ function jqLiteBuildFragment(html, context) {
2872
2882
  nodes.push(context.createTextNode(html));
2873
2883
  } else {
2874
2884
  // Convert html into DOM nodes
2875
- tmp = tmp || fragment.appendChild(context.createElement("div"));
2885
+ tmp = fragment.appendChild(context.createElement("div"));
2876
2886
  tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2877
2887
  wrap = wrapMap[tag] || wrapMap._default;
2878
2888
  tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
@@ -4685,10 +4695,10 @@ function createInjector(modulesToLoad, strictDi) {
4685
4695
  if (msie <= 11) {
4686
4696
  return false;
4687
4697
  }
4688
- // Workaround for MS Edge.
4689
- // Check https://connect.microsoft.com/IE/Feedback/Details/2211653
4698
+ // Support: Edge 12-13 only
4699
+ // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
4690
4700
  return typeof func === 'function'
4691
- && /^(?:class\s|constructor\()/.test(stringifyFn(func));
4701
+ && /^(?:class\b|constructor\()/.test(stringifyFn(func));
4692
4702
  }
4693
4703
 
4694
4704
  function invoke(fn, self, locals, serviceName) {
@@ -5429,7 +5439,13 @@ var $AnimateProvider = ['$provide', function($provide) {
5429
5439
  * @param {DOMElement} parent the parent element which will append the element as
5430
5440
  * a child (so long as the after element is not present)
5431
5441
  * @param {DOMElement=} after the sibling element after which the element will be appended
5432
- * @param {object=} options an optional collection of options/styles that will be applied to the element
5442
+ * @param {object=} options an optional collection of options/styles that will be applied to the element.
5443
+ * The object can have the following properties:
5444
+ *
5445
+ * - **addClass** - `{string}` - space-separated CSS classes to add to element
5446
+ * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5447
+ * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5448
+ * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5433
5449
  *
5434
5450
  * @return {Promise} the animation callback promise
5435
5451
  */
@@ -5455,7 +5471,13 @@ var $AnimateProvider = ['$provide', function($provide) {
5455
5471
  * @param {DOMElement} parent the parent element which will append the element as
5456
5472
  * a child (so long as the after element is not present)
5457
5473
  * @param {DOMElement=} after the sibling element after which the element will be appended
5458
- * @param {object=} options an optional collection of options/styles that will be applied to the element
5474
+ * @param {object=} options an optional collection of options/styles that will be applied to the element.
5475
+ * The object can have the following properties:
5476
+ *
5477
+ * - **addClass** - `{string}` - space-separated CSS classes to add to element
5478
+ * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5479
+ * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5480
+ * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5459
5481
  *
5460
5482
  * @return {Promise} the animation callback promise
5461
5483
  */
@@ -5476,7 +5498,13 @@ var $AnimateProvider = ['$provide', function($provide) {
5476
5498
  * digest once the animation has completed.
5477
5499
  *
5478
5500
  * @param {DOMElement} element the element which will be removed from the DOM
5479
- * @param {object=} options an optional collection of options/styles that will be applied to the element
5501
+ * @param {object=} options an optional collection of options/styles that will be applied to the element.
5502
+ * The object can have the following properties:
5503
+ *
5504
+ * - **addClass** - `{string}` - space-separated CSS classes to add to element
5505
+ * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5506
+ * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5507
+ * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5480
5508
  *
5481
5509
  * @return {Promise} the animation callback promise
5482
5510
  */
@@ -5500,7 +5528,13 @@ var $AnimateProvider = ['$provide', function($provide) {
5500
5528
  *
5501
5529
  * @param {DOMElement} element the element which the CSS classes will be applied to
5502
5530
  * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5503
- * @param {object=} options an optional collection of options/styles that will be applied to the element
5531
+ * @param {object=} options an optional collection of options/styles that will be applied to the element.
5532
+ * The object can have the following properties:
5533
+ *
5534
+ * - **addClass** - `{string}` - space-separated CSS classes to add to element
5535
+ * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5536
+ * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5537
+ * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5504
5538
  *
5505
5539
  * @return {Promise} the animation callback promise
5506
5540
  */
@@ -5524,7 +5558,13 @@ var $AnimateProvider = ['$provide', function($provide) {
5524
5558
  *
5525
5559
  * @param {DOMElement} element the element which the CSS classes will be applied to
5526
5560
  * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5527
- * @param {object=} options an optional collection of options/styles that will be applied to the element
5561
+ * @param {object=} options an optional collection of options/styles that will be applied to the element.
5562
+ * The object can have the following properties:
5563
+ *
5564
+ * - **addClass** - `{string}` - space-separated CSS classes to add to element
5565
+ * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5566
+ * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5567
+ * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5528
5568
  *
5529
5569
  * @return {Promise} the animation callback promise
5530
5570
  */
@@ -5549,7 +5589,13 @@ var $AnimateProvider = ['$provide', function($provide) {
5549
5589
  * @param {DOMElement} element the element which the CSS classes will be applied to
5550
5590
  * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5551
5591
  * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5552
- * @param {object=} options an optional collection of options/styles that will be applied to the element
5592
+ * @param {object=} options an optional collection of options/styles that will be applied to the element.
5593
+ * The object can have the following properties:
5594
+ *
5595
+ * - **addClass** - `{string}` - space-separated CSS classes to add to element
5596
+ * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5597
+ * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5598
+ * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5553
5599
  *
5554
5600
  * @return {Promise} the animation callback promise
5555
5601
  */
@@ -5590,7 +5636,13 @@ var $AnimateProvider = ['$provide', function($provide) {
5590
5636
  * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5591
5637
  * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5592
5638
  * (Note that if no animation is detected then this value will not be applied to the element.)
5593
- * @param {object=} options an optional collection of options/styles that will be applied to the element
5639
+ * @param {object=} options an optional collection of options/styles that will be applied to the element.
5640
+ * The object can have the following properties:
5641
+ *
5642
+ * - **addClass** - `{string}` - space-separated CSS classes to add to element
5643
+ * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5644
+ * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5645
+ * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5594
5646
  *
5595
5647
  * @return {Promise} the animation callback promise
5596
5648
  */
@@ -6678,8 +6730,9 @@ function $TemplateCacheProvider() {
6678
6730
  * There are many different options for a directive.
6679
6731
  *
6680
6732
  * The difference resides in the return value of the factory function.
6681
- * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6682
- * or just the `postLink` function (all other properties will have the default values).
6733
+ * You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)}
6734
+ * that defines the directive properties, or just the `postLink` function (all other properties will have
6735
+ * the default values).
6683
6736
  *
6684
6737
  * <div class="alert alert-success">
6685
6738
  * **Best Practice:** It's recommended to use the "directive definition object" form.
@@ -6743,6 +6796,125 @@ function $TemplateCacheProvider() {
6743
6796
  * });
6744
6797
  * ```
6745
6798
  *
6799
+ * ### Life-cycle hooks
6800
+ * Directive controllers can provide the following methods that are called by Angular at points in the life-cycle of the
6801
+ * directive:
6802
+ * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
6803
+ * had their bindings initialized (and before the pre &amp; post linking functions for the directives on
6804
+ * this element). This is a good place to put initialization code for your controller.
6805
+ * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
6806
+ * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
6807
+ * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
6808
+ * component such as cloning the bound value to prevent accidental mutation of the outer value.
6809
+ * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
6810
+ * changes. Any actions that you wish to take in response to the changes that you detect must be
6811
+ * invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
6812
+ * could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not
6813
+ * be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
6814
+ * if detecting changes, you must store the previous value(s) for comparison to the current values.
6815
+ * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
6816
+ * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
6817
+ * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
6818
+ * components will have their `$onDestroy()` hook called before child components.
6819
+ * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
6820
+ * function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
6821
+ * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
6822
+ * they are waiting for their template to load asynchronously and their own compilation and linking has been
6823
+ * suspended until that occurs.
6824
+ *
6825
+ * #### Comparison with Angular 2 life-cycle hooks
6826
+ * Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are
6827
+ * some differences that you should be aware of, especially when it comes to moving your code from Angular 1 to Angular 2:
6828
+ *
6829
+ * * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`.
6830
+ * * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor.
6831
+ * In Angular 2 you can only define hooks on the prototype of the Component class.
6832
+ * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to
6833
+ * `ngDoCheck` in Angular 2
6834
+ * * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
6835
+ * propagated throughout the application.
6836
+ * Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
6837
+ * error or do nothing depending upon the state of `enableProdMode()`.
6838
+ *
6839
+ * #### Life-cycle hook examples
6840
+ *
6841
+ * This example shows how you can check for mutations to a Date object even though the identity of the object
6842
+ * has not changed.
6843
+ *
6844
+ * <example name="doCheckDateExample" module="do-check-module">
6845
+ * <file name="app.js">
6846
+ * angular.module('do-check-module', [])
6847
+ * .component('app', {
6848
+ * template:
6849
+ * 'Month: <input ng-model="$ctrl.month" ng-change="$ctrl.updateDate()">' +
6850
+ * 'Date: {{ $ctrl.date }}' +
6851
+ * '<test date="$ctrl.date"></test>',
6852
+ * controller: function() {
6853
+ * this.date = new Date();
6854
+ * this.month = this.date.getMonth();
6855
+ * this.updateDate = function() {
6856
+ * this.date.setMonth(this.month);
6857
+ * };
6858
+ * }
6859
+ * })
6860
+ * .component('test', {
6861
+ * bindings: { date: '<' },
6862
+ * template:
6863
+ * '<pre>{{ $ctrl.log | json }}</pre>',
6864
+ * controller: function() {
6865
+ * var previousValue;
6866
+ * this.log = [];
6867
+ * this.$doCheck = function() {
6868
+ * var currentValue = this.date && this.date.valueOf();
6869
+ * if (previousValue !== currentValue) {
6870
+ * this.log.push('doCheck: date mutated: ' + this.date);
6871
+ * previousValue = currentValue;
6872
+ * }
6873
+ * };
6874
+ * }
6875
+ * });
6876
+ * </file>
6877
+ * <file name="index.html">
6878
+ * <app></app>
6879
+ * </file>
6880
+ * </example>
6881
+ *
6882
+ * This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the
6883
+ * actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
6884
+ * arrays or objects can have a negative impact on your application performance)
6885
+ *
6886
+ * <example name="doCheckArrayExample" module="do-check-module">
6887
+ * <file name="index.html">
6888
+ * <div ng-init="items = []">
6889
+ * <button ng-click="items.push(items.length)">Add Item</button>
6890
+ * <button ng-click="items = []">Reset Items</button>
6891
+ * <pre>{{ items }}</pre>
6892
+ * <test items="items"></test>
6893
+ * </div>
6894
+ * </file>
6895
+ * <file name="app.js">
6896
+ * angular.module('do-check-module', [])
6897
+ * .component('test', {
6898
+ * bindings: { items: '<' },
6899
+ * template:
6900
+ * '<pre>{{ $ctrl.log | json }}</pre>',
6901
+ * controller: function() {
6902
+ * this.log = [];
6903
+ *
6904
+ * this.$doCheck = function() {
6905
+ * if (this.items_ref !== this.items) {
6906
+ * this.log.push('doCheck: items changed');
6907
+ * this.items_ref = this.items;
6908
+ * }
6909
+ * if (!angular.equals(this.items_clone, this.items)) {
6910
+ * this.log.push('doCheck: items mutated');
6911
+ * this.items_clone = angular.copy(this.items);
6912
+ * }
6913
+ * };
6914
+ * }
6915
+ * });
6916
+ * </file>
6917
+ * </example>
6746
6918
  *
6747
6919
  *
6748
6920
  * ### Directive Definition Object
@@ -6918,25 +7090,6 @@ function $TemplateCacheProvider() {
6918
7090
  * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
6919
7091
  * `true` if the specified slot contains content (i.e. one or more DOM nodes).
6920
7092
  *
6921
- * The controller can provide the following methods that act as life-cycle hooks:
6922
- * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
6923
- * had their bindings initialized (and before the pre &amp; post linking functions for the directives on
6924
- * this element). This is a good place to put initialization code for your controller.
6925
- * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
6926
- * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
6927
- * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
6928
- * component such as cloning the bound value to prevent accidental mutation of the outer value.
6929
- * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
6930
- * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
6931
- * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
6932
- * components will have their `$onDestroy()` hook called before child components.
6933
- * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
6934
- * function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
6935
- * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
6936
- * they are waiting for their template to load asynchronously and their own compilation and linking has been
6937
- * suspended until that occurs.
6938
- *
6939
- *
6940
7093
  * #### `require`
6941
7094
  * Require another directive and inject its controller as the fourth argument to the linking function. The
6942
7095
  * `require` property can be a string, an array or an object:
@@ -7134,8 +7287,8 @@ function $TemplateCacheProvider() {
7134
7287
  * any other controller.
7135
7288
  *
7136
7289
  * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
7137
- * This is the same as the `$transclude`
7138
- * parameter of directive controllers, see there for details.
7290
+ * This is the same as the `$transclude` parameter of directive controllers,
7291
+ * see {@link ng.$compile#-controller- the controller section for details}.
7139
7292
  * `function([scope], cloneLinkingFn, futureParentElement)`.
7140
7293
  *
7141
7294
  * #### Pre-linking function
@@ -7937,11 +8090,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7937
8090
  }
7938
8091
  // We must run this hook in an apply since the $$postDigest runs outside apply
7939
8092
  $rootScope.$apply(function() {
8093
+ var errors = [];
7940
8094
  for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
7941
- onChangesQueue[i]();
8095
+ try {
8096
+ onChangesQueue[i]();
8097
+ } catch (e) {
8098
+ errors.push(e);
8099
+ }
7942
8100
  }
7943
8101
  // Reset the queue to trigger a new schedule next time there is a change
7944
8102
  onChangesQueue = undefined;
8103
+ if (errors.length) {
8104
+ throw errors;
8105
+ }
7945
8106
  });
7946
8107
  } finally {
7947
8108
  onChangesTtl++;
@@ -8582,19 +8743,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8582
8743
  addTextInterpolateDirective(directives, node.nodeValue);
8583
8744
  break;
8584
8745
  case NODE_TYPE_COMMENT: /* Comment */
8585
- try {
8586
- match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8587
- if (match) {
8588
- nName = directiveNormalize(match[1]);
8589
- if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8590
- attrs[nName] = trim(match[2]);
8591
- }
8592
- }
8593
- } catch (e) {
8594
- // turns out that under some circumstances IE9 throws errors when one attempts to read
8595
- // comment's node value.
8596
- // Just ignore it and continue. (Can't seem to reproduce in test case.)
8597
- }
8746
+ collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective);
8598
8747
  break;
8599
8748
  }
8600
8749
 
@@ -8602,6 +8751,24 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8602
8751
  return directives;
8603
8752
  }
8604
8753
 
8754
+ function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
8755
+ // function created because of performance, try/catch disables
8756
+ // the optimization of the whole function #14848
8757
+ try {
8758
+ var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8759
+ if (match) {
8760
+ var nName = directiveNormalize(match[1]);
8761
+ if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8762
+ attrs[nName] = trim(match[2]);
8763
+ }
8764
+ }
8765
+ } catch (e) {
8766
+ // turns out that under some circumstances IE9 throws errors when one attempts to read
8767
+ // comment's node value.
8768
+ // Just ignore it and continue. (Can't seem to reproduce in test case.)
8769
+ }
8770
+ }
8771
+
8605
8772
  /**
8606
8773
  * Given a node with an directive-start it collects all of the siblings until it finds
8607
8774
  * directive-end.
@@ -9117,10 +9284,22 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
9117
9284
  forEach(elementControllers, function(controller) {
9118
9285
  var controllerInstance = controller.instance;
9119
9286
  if (isFunction(controllerInstance.$onChanges)) {
9120
- controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
9287
+ try {
9288
+ controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
9289
+ } catch (e) {
9290
+ $exceptionHandler(e);
9291
+ }
9121
9292
  }
9122
9293
  if (isFunction(controllerInstance.$onInit)) {
9123
- controllerInstance.$onInit();
9294
+ try {
9295
+ controllerInstance.$onInit();
9296
+ } catch (e) {
9297
+ $exceptionHandler(e);
9298
+ }
9299
+ }
9300
+ if (isFunction(controllerInstance.$doCheck)) {
9301
+ controllerScope.$watch(function() { controllerInstance.$doCheck(); });
9302
+ controllerInstance.$doCheck();
9124
9303
  }
9125
9304
  if (isFunction(controllerInstance.$onDestroy)) {
9126
9305
  controllerScope.$on('$destroy', function callOnDestroyHook() {
@@ -9384,18 +9563,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
9384
9563
 
9385
9564
  // copy the new attributes on the old attrs object
9386
9565
  forEach(src, function(value, key) {
9387
- if (key == 'class') {
9388
- safeAddClass($element, value);
9389
- dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
9390
- } else if (key == 'style') {
9391
- $element.attr('style', $element.attr('style') + ';' + value);
9392
- dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
9393
- // `dst` will never contain hasOwnProperty as DOM parser won't let it.
9394
- // You will get an "InvalidCharacterError: DOM Exception 5" error if you
9395
- // have an attribute like "has-own-property" or "data-has-own-property", etc.
9396
- } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
9566
+ // Check if we already set this attribute in the loop above.
9567
+ // `dst` will never contain hasOwnProperty as DOM parser won't let it.
9568
+ // You will get an "InvalidCharacterError: DOM Exception 5" error if you
9569
+ // have an attribute like "has-own-property" or "data-has-own-property", etc.
9570
+ if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') {
9397
9571
  dst[key] = value;
9398
- dstAttr[key] = srcAttr[key];
9572
+
9573
+ if (key !== 'class' && key !== 'style') {
9574
+ dstAttr[key] = srcAttr[key];
9575
+ }
9399
9576
  }
9400
9577
  });
9401
9578
  }
@@ -9770,7 +9947,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
9770
9947
  forEach(bindings, function initializeBinding(definition, scopeName) {
9771
9948
  var attrName = definition.attrName,
9772
9949
  optional = definition.optional,
9773
- mode = definition.mode, // @, =, or &
9950
+ mode = definition.mode, // @, =, <, or &
9774
9951
  lastValue,
9775
9952
  parentGet, parentSet, compare, removeWatch;
9776
9953
 
@@ -10256,18 +10433,21 @@ function $DocumentProvider() {
10256
10433
  *
10257
10434
  * ## Example:
10258
10435
  *
10436
+ * The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught
10437
+ * errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead
10438
+ * of `$log.error()`.
10439
+ *
10259
10440
  * ```js
10260
- * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
10261
- * return function(exception, cause) {
10262
- * exception.message += ' (caused by "' + cause + '")';
10263
- * throw exception;
10264
- * };
10265
- * });
10441
+ * angular.
10442
+ * module('exceptionOverwrite', []).
10443
+ * factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
10444
+ * return function myExceptionHandler(exception, cause) {
10445
+ * logErrorsToBackend(exception, cause);
10446
+ * $log.warn(exception, cause);
10447
+ * };
10448
+ * }]);
10266
10449
  * ```
10267
10450
  *
10268
- * This example will override the normal action of `$exceptionHandler`, to make angular
10269
- * exceptions fail hard when they happen, instead of just logging to the console.
10270
- *
10271
10451
  * <hr />
10272
10452
  * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
10273
10453
  * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
@@ -10277,7 +10457,7 @@ function $DocumentProvider() {
10277
10457
  * `try { ... } catch(e) { $exceptionHandler(e); }`
10278
10458
  *
10279
10459
  * @param {Error} exception Exception associated with the error.
10280
- * @param {string=} cause optional information about the context in which
10460
+ * @param {string=} cause Optional information about the context in which
10281
10461
  * the error was thrown.
10282
10462
  *
10283
10463
  */
@@ -10347,7 +10527,7 @@ function $HttpParamSerializerProvider() {
10347
10527
  * * `{'foo': 'bar'}` results in `foo=bar`
10348
10528
  * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
10349
10529
  * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
10350
- * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
10530
+ * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object)
10351
10531
  *
10352
10532
  * Note that serializer will sort the request parameters alphabetically.
10353
10533
  * */
@@ -10898,7 +11078,7 @@ function $HttpProvider() {
10898
11078
  *
10899
11079
  * ### Overriding the Default Transformations Per Request
10900
11080
  *
10901
- * If you wish override the request/response transformations only for a single request then provide
11081
+ * If you wish to override the request/response transformations only for a single request then provide
10902
11082
  * `transformRequest` and/or `transformResponse` properties on the configuration object passed
10903
11083
  * into `$http`.
10904
11084
  *
@@ -10941,7 +11121,7 @@ function $HttpProvider() {
10941
11121
  * * cache a specific response - set config.cache value to TRUE or to a cache object
10942
11122
  *
10943
11123
  * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
10944
- * then the default `$cacheFactory($http)` object is used.
11124
+ * then the default `$cacheFactory("$http")` object is used.
10945
11125
  *
10946
11126
  * The default cache value can be set by updating the
10947
11127
  * {@link ng.$http#defaults `$http.defaults.cache`} property or the
@@ -11269,48 +11449,25 @@ function $HttpProvider() {
11269
11449
  config.headers = mergeHeaders(requestConfig);
11270
11450
  config.method = uppercase(config.method);
11271
11451
  config.paramSerializer = isString(config.paramSerializer) ?
11272
- $injector.get(config.paramSerializer) : config.paramSerializer;
11273
-
11274
- var serverRequest = function(config) {
11275
- var headers = config.headers;
11276
- var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
11452
+ $injector.get(config.paramSerializer) : config.paramSerializer;
11277
11453
 
11278
- // strip content-type if data is undefined
11279
- if (isUndefined(reqData)) {
11280
- forEach(headers, function(value, header) {
11281
- if (lowercase(header) === 'content-type') {
11282
- delete headers[header];
11283
- }
11284
- });
11285
- }
11286
-
11287
- if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
11288
- config.withCredentials = defaults.withCredentials;
11289
- }
11290
-
11291
- // send request
11292
- return sendReq(config, reqData).then(transformResponse, transformResponse);
11293
- };
11294
-
11295
- var chain = [serverRequest, undefined];
11454
+ var requestInterceptors = [];
11455
+ var responseInterceptors = [];
11296
11456
  var promise = $q.when(config);
11297
11457
 
11298
11458
  // apply interceptors
11299
11459
  forEach(reversedInterceptors, function(interceptor) {
11300
11460
  if (interceptor.request || interceptor.requestError) {
11301
- chain.unshift(interceptor.request, interceptor.requestError);
11461
+ requestInterceptors.unshift(interceptor.request, interceptor.requestError);
11302
11462
  }
11303
11463
  if (interceptor.response || interceptor.responseError) {
11304
- chain.push(interceptor.response, interceptor.responseError);
11464
+ responseInterceptors.push(interceptor.response, interceptor.responseError);
11305
11465
  }
11306
11466
  });
11307
11467
 
11308
- while (chain.length) {
11309
- var thenFn = chain.shift();
11310
- var rejectFn = chain.shift();
11311
-
11312
- promise = promise.then(thenFn, rejectFn);
11313
- }
11468
+ promise = chainInterceptors(promise, requestInterceptors);
11469
+ promise = promise.then(serverRequest);
11470
+ promise = chainInterceptors(promise, responseInterceptors);
11314
11471
 
11315
11472
  if (useLegacyPromise) {
11316
11473
  promise.success = function(fn) {
@@ -11337,14 +11494,18 @@ function $HttpProvider() {
11337
11494
 
11338
11495
  return promise;
11339
11496
 
11340
- function transformResponse(response) {
11341
- // make a copy since the response must be cacheable
11342
- var resp = extend({}, response);
11343
- resp.data = transformData(response.data, response.headers, response.status,
11344
- config.transformResponse);
11345
- return (isSuccess(response.status))
11346
- ? resp
11347
- : $q.reject(resp);
11497
+
11498
+ function chainInterceptors(promise, interceptors) {
11499
+ for (var i = 0, ii = interceptors.length; i < ii;) {
11500
+ var thenFn = interceptors[i++];
11501
+ var rejectFn = interceptors[i++];
11502
+
11503
+ promise = promise.then(thenFn, rejectFn);
11504
+ }
11505
+
11506
+ interceptors.length = 0;
11507
+
11508
+ return promise;
11348
11509
  }
11349
11510
 
11350
11511
  function executeHeaderFns(headers, config) {
@@ -11388,6 +11549,37 @@ function $HttpProvider() {
11388
11549
  // execute if header value is a function for merged headers
11389
11550
  return executeHeaderFns(reqHeaders, shallowCopy(config));
11390
11551
  }
11552
+
11553
+ function serverRequest(config) {
11554
+ var headers = config.headers;
11555
+ var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
11556
+
11557
+ // strip content-type if data is undefined
11558
+ if (isUndefined(reqData)) {
11559
+ forEach(headers, function(value, header) {
11560
+ if (lowercase(header) === 'content-type') {
11561
+ delete headers[header];
11562
+ }
11563
+ });
11564
+ }
11565
+
11566
+ if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
11567
+ config.withCredentials = defaults.withCredentials;
11568
+ }
11569
+
11570
+ // send request
11571
+ return sendReq(config, reqData).then(transformResponse, transformResponse);
11572
+ }
11573
+
11574
+ function transformResponse(response) {
11575
+ // make a copy since the response must be cacheable
11576
+ var resp = extend({}, response);
11577
+ resp.data = transformData(response.data, response.headers, response.status,
11578
+ config.transformResponse);
11579
+ return (isSuccess(response.status))
11580
+ ? resp
11581
+ : $q.reject(resp);
11582
+ }
11391
11583
  }
11392
11584
 
11393
11585
  $http.pendingRequests = [];
@@ -11434,6 +11626,8 @@ function $HttpProvider() {
11434
11626
  *
11435
11627
  * @description
11436
11628
  * Shortcut method to perform `JSONP` request.
11629
+ * If you would like to customise where and how the callbacks are stored then try overriding
11630
+ * or decorating the {@link $jsonpCallbacks} service.
11437
11631
  *
11438
11632
  * @param {string} url Relative or absolute URL specifying the destination of the request.
11439
11633
  * The name of the callback should be the string `JSON_CALLBACK`.
@@ -11707,7 +11901,7 @@ function $xhrFactoryProvider() {
11707
11901
  /**
11708
11902
  * @ngdoc service
11709
11903
  * @name $httpBackend
11710
- * @requires $window
11904
+ * @requires $jsonpCallbacks
11711
11905
  * @requires $document
11712
11906
  * @requires $xhrFactory
11713
11907
  *
@@ -11722,8 +11916,8 @@ function $xhrFactoryProvider() {
11722
11916
  * $httpBackend} which can be trained with responses.
11723
11917
  */
11724
11918
  function $HttpBackendProvider() {
11725
- this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
11726
- return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
11919
+ this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) {
11920
+ return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]);
11727
11921
  }];
11728
11922
  }
11729
11923
 
@@ -11733,17 +11927,13 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
11733
11927
  $browser.$$incOutstandingRequestCount();
11734
11928
  url = url || $browser.url();
11735
11929
 
11736
- if (lowercase(method) == 'jsonp') {
11737
- var callbackId = '_' + (callbacks.counter++).toString(36);
11738
- callbacks[callbackId] = function(data) {
11739
- callbacks[callbackId].data = data;
11740
- callbacks[callbackId].called = true;
11741
- };
11742
-
11743
- var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
11744
- callbackId, function(status, text) {
11745
- completeRequest(callback, status, callbacks[callbackId].data, "", text);
11746
- callbacks[callbackId] = noop;
11930
+ if (lowercase(method) === 'jsonp') {
11931
+ var callbackPath = callbacks.createCallback(url);
11932
+ var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
11933
+ // jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
11934
+ var response = (status === 200) && callbacks.getResponse(callbackPath);
11935
+ completeRequest(callback, status, response, "", text);
11936
+ callbacks.removeCallback(callbackPath);
11747
11937
  });
11748
11938
  } else {
11749
11939
 
@@ -11845,7 +12035,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
11845
12035
  }
11846
12036
  };
11847
12037
 
11848
- function jsonpReq(url, callbackId, done) {
12038
+ function jsonpReq(url, callbackPath, done) {
12039
+ url = url.replace('JSON_CALLBACK', callbackPath);
11849
12040
  // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
11850
12041
  // - fetches local scripts via XHR and evals them
11851
12042
  // - adds and immediately removes script elements from the document
@@ -11863,7 +12054,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
11863
12054
  var text = "unknown";
11864
12055
 
11865
12056
  if (event) {
11866
- if (event.type === "load" && !callbacks[callbackId].called) {
12057
+ if (event.type === "load" && !callbacks.wasCalled(callbackPath)) {
11867
12058
  event = { type: "error" };
11868
12059
  }
11869
12060
  text = event.type;
@@ -12062,7 +12253,7 @@ function $InterpolateProvider() {
12062
12253
  *
12063
12254
  * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
12064
12255
  *
12065
- * ####Escaped Interpolation
12256
+ * #### Escaped Interpolation
12066
12257
  * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
12067
12258
  * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
12068
12259
  * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
@@ -12485,6 +12676,87 @@ function $IntervalProvider() {
12485
12676
  }];
12486
12677
  }
12487
12678
 
12679
+ /**
12680
+ * @ngdoc service
12681
+ * @name $jsonpCallbacks
12682
+ * @requires $window
12683
+ * @description
12684
+ * This service handles the lifecycle of callbacks to handle JSONP requests.
12685
+ * Override this service if you wish to customise where the callbacks are stored and
12686
+ * how they vary compared to the requested url.
12687
+ */
12688
+ var $jsonpCallbacksProvider = function() {
12689
+ this.$get = ['$window', function($window) {
12690
+ var callbacks = $window.angular.callbacks;
12691
+ var callbackMap = {};
12692
+
12693
+ function createCallback(callbackId) {
12694
+ var callback = function(data) {
12695
+ callback.data = data;
12696
+ callback.called = true;
12697
+ };
12698
+ callback.id = callbackId;
12699
+ return callback;
12700
+ }
12701
+
12702
+ return {
12703
+ /**
12704
+ * @ngdoc method
12705
+ * @name $jsonpCallbacks#createCallback
12706
+ * @param {string} url the url of the JSONP request
12707
+ * @returns {string} the callback path to send to the server as part of the JSONP request
12708
+ * @description
12709
+ * {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback
12710
+ * to pass to the server, which will be used to call the callback with its payload in the JSONP response.
12711
+ */
12712
+ createCallback: function(url) {
12713
+ var callbackId = '_' + (callbacks.$$counter++).toString(36);
12714
+ var callbackPath = 'angular.callbacks.' + callbackId;
12715
+ var callback = createCallback(callbackId);
12716
+ callbackMap[callbackPath] = callbacks[callbackId] = callback;
12717
+ return callbackPath;
12718
+ },
12719
+ /**
12720
+ * @ngdoc method
12721
+ * @name $jsonpCallbacks#wasCalled
12722
+ * @param {string} callbackPath the path to the callback that was sent in the JSONP request
12723
+ * @returns {boolean} whether the callback has been called, as a result of the JSONP response
12724
+ * @description
12725
+ * {@link $httpBackend} calls this method to find out whether the JSONP response actually called the
12726
+ * callback that was passed in the request.
12727
+ */
12728
+ wasCalled: function(callbackPath) {
12729
+ return callbackMap[callbackPath].called;
12730
+ },
12731
+ /**
12732
+ * @ngdoc method
12733
+ * @name $jsonpCallbacks#getResponse
12734
+ * @param {string} callbackPath the path to the callback that was sent in the JSONP request
12735
+ * @returns {*} the data received from the response via the registered callback
12736
+ * @description
12737
+ * {@link $httpBackend} calls this method to get hold of the data that was provided to the callback
12738
+ * in the JSONP response.
12739
+ */
12740
+ getResponse: function(callbackPath) {
12741
+ return callbackMap[callbackPath].data;
12742
+ },
12743
+ /**
12744
+ * @ngdoc method
12745
+ * @name $jsonpCallbacks#removeCallback
12746
+ * @param {string} callbackPath the path to the callback that was sent in the JSONP request
12747
+ * @description
12748
+ * {@link $httpBackend} calls this method to remove the callback after the JSONP request has
12749
+ * completed or timed-out.
12750
+ */
12751
+ removeCallback: function(callbackPath) {
12752
+ var callback = callbackMap[callbackPath];
12753
+ delete callbacks[callback.id];
12754
+ delete callbackMap[callbackPath];
12755
+ }
12756
+ };
12757
+ }];
12758
+ };
12759
+
12488
12760
  /**
12489
12761
  * @ngdoc service
12490
12762
  * @name $locale
@@ -12824,6 +13096,12 @@ function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
12824
13096
 
12825
13097
  var locationPrototype = {
12826
13098
 
13099
+ /**
13100
+ * Ensure absolute url is initialized.
13101
+ * @private
13102
+ */
13103
+ $$absUrl:'',
13104
+
12827
13105
  /**
12828
13106
  * Are we in html5 mode?
12829
13107
  * @private
@@ -14197,7 +14475,7 @@ AST.prototype = {
14197
14475
  var args = [];
14198
14476
  if (this.peekToken().text !== ')') {
14199
14477
  do {
14200
- args.push(this.expression());
14478
+ args.push(this.filterChain());
14201
14479
  } while (this.expect(','));
14202
14480
  }
14203
14481
  return args;
@@ -15924,7 +16202,7 @@ function $ParseProvider() {
15924
16202
  *
15925
16203
  * **Methods**
15926
16204
  *
15927
- * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
16205
+ * - `then(successCallback, [errorCallback], [notifyCallback])` – regardless of when the promise was or
15928
16206
  * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
15929
16207
  * as soon as the result is available. The callbacks are called with a single argument: the result
15930
16208
  * or rejection reason. Additionally, the notify callback may be called zero or more times to
@@ -15935,7 +16213,8 @@ function $ParseProvider() {
15935
16213
  * with the value which is resolved in that promise using
15936
16214
  * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
15937
16215
  * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
15938
- * resolved or rejected from the notifyCallback method.
16216
+ * resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback
16217
+ * arguments are optional.
15939
16218
  *
15940
16219
  * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
15941
16220
  *
@@ -16350,6 +16629,30 @@ function qFactory(nextTick, exceptionHandler) {
16350
16629
  return deferred.promise;
16351
16630
  }
16352
16631
 
16632
+ /**
16633
+ * @ngdoc method
16634
+ * @name $q#race
16635
+ * @kind function
16636
+ *
16637
+ * @description
16638
+ * Returns a promise that resolves or rejects as soon as one of those promises
16639
+ * resolves or rejects, with the value or reason from that promise.
16640
+ *
16641
+ * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
16642
+ * @returns {Promise} a promise that resolves or rejects as soon as one of the `promises`
16643
+ * resolves or rejects, with the value or reason from that promise.
16644
+ */
16645
+
16646
+ function race(promises) {
16647
+ var deferred = defer();
16648
+
16649
+ forEach(promises, function(promise) {
16650
+ when(promise).then(deferred.resolve, deferred.reject);
16651
+ });
16652
+
16653
+ return deferred.promise;
16654
+ }
16655
+
16353
16656
  var $Q = function Q(resolver) {
16354
16657
  if (!isFunction(resolver)) {
16355
16658
  throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
@@ -16379,6 +16682,7 @@ function qFactory(nextTick, exceptionHandler) {
16379
16682
  $Q.when = when;
16380
16683
  $Q.resolve = resolve;
16381
16684
  $Q.all = all;
16685
+ $Q.race = race;
16382
16686
 
16383
16687
  return $Q;
16384
16688
  }
@@ -19730,10 +20034,11 @@ function $FilterProvider($provide) {
19730
20034
  * - `Object`: A pattern object can be used to filter specific properties on objects contained
19731
20035
  * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
19732
20036
  * which have property `name` containing "M" and property `phone` containing "1". A special
19733
- * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
19734
- * property of the object or its nested object properties. That's equivalent to the simple
19735
- * substring match with a `string` as described above. The predicate can be negated by prefixing
19736
- * the string with `!`.
20037
+ * property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match
20038
+ * against any property of the object or its nested object properties. That's equivalent to the
20039
+ * simple substring match with a `string` as described above. The special property name can be
20040
+ * overwritten, using the `anyPropertyKey` parameter.
20041
+ * The predicate can be negated by prefixing the string with `!`.
19737
20042
  * For example `{name: "!M"}` predicate will return an array of items which have property `name`
19738
20043
  * not containing "M".
19739
20044
  *
@@ -19767,6 +20072,9 @@ function $FilterProvider($provide) {
19767
20072
  * Primitive values are converted to strings. Objects are not compared against primitives,
19768
20073
  * unless they have a custom `toString` method (e.g. `Date` objects).
19769
20074
  *
20075
+ * @param {string=} anyPropertyKey The special property name that matches against any property.
20076
+ * By default `$`.
20077
+ *
19770
20078
  * @example
19771
20079
  <example>
19772
20080
  <file name="index.html">
@@ -19835,8 +20143,9 @@ function $FilterProvider($provide) {
19835
20143
  </file>
19836
20144
  </example>
19837
20145
  */
20146
+
19838
20147
  function filterFilter() {
19839
- return function(array, expression, comparator) {
20148
+ return function(array, expression, comparator, anyPropertyKey) {
19840
20149
  if (!isArrayLike(array)) {
19841
20150
  if (array == null) {
19842
20151
  return array;
@@ -19845,6 +20154,7 @@ function filterFilter() {
19845
20154
  }
19846
20155
  }
19847
20156
 
20157
+ anyPropertyKey = anyPropertyKey || '$';
19848
20158
  var expressionType = getTypeForFilter(expression);
19849
20159
  var predicateFn;
19850
20160
  var matchAgainstAnyProp;
@@ -19861,7 +20171,7 @@ function filterFilter() {
19861
20171
  //jshint -W086
19862
20172
  case 'object':
19863
20173
  //jshint +W086
19864
- predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
20174
+ predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp);
19865
20175
  break;
19866
20176
  default:
19867
20177
  return array;
@@ -19872,8 +20182,8 @@ function filterFilter() {
19872
20182
  }
19873
20183
 
19874
20184
  // Helper functions for `filterFilter`
19875
- function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
19876
- var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
20185
+ function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) {
20186
+ var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression);
19877
20187
  var predicateFn;
19878
20188
 
19879
20189
  if (comparator === true) {
@@ -19901,25 +20211,25 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
19901
20211
 
19902
20212
  predicateFn = function(item) {
19903
20213
  if (shouldMatchPrimitives && !isObject(item)) {
19904
- return deepCompare(item, expression.$, comparator, false);
20214
+ return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false);
19905
20215
  }
19906
- return deepCompare(item, expression, comparator, matchAgainstAnyProp);
20216
+ return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp);
19907
20217
  };
19908
20218
 
19909
20219
  return predicateFn;
19910
20220
  }
19911
20221
 
19912
- function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
20222
+ function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) {
19913
20223
  var actualType = getTypeForFilter(actual);
19914
20224
  var expectedType = getTypeForFilter(expected);
19915
20225
 
19916
20226
  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
19917
- return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
20227
+ return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp);
19918
20228
  } else if (isArray(actual)) {
19919
20229
  // In case `actual` is an array, consider it a match
19920
20230
  // if ANY of it's items matches `expected`
19921
20231
  return actual.some(function(item) {
19922
- return deepCompare(item, expected, comparator, matchAgainstAnyProp);
20232
+ return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp);
19923
20233
  });
19924
20234
  }
19925
20235
 
@@ -19928,11 +20238,11 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatc
19928
20238
  var key;
19929
20239
  if (matchAgainstAnyProp) {
19930
20240
  for (key in actual) {
19931
- if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
20241
+ if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
19932
20242
  return true;
19933
20243
  }
19934
20244
  }
19935
- return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
20245
+ return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false);
19936
20246
  } else if (expectedType === 'object') {
19937
20247
  for (key in expected) {
19938
20248
  var expectedVal = expected[key];
@@ -19940,9 +20250,9 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatc
19940
20250
  continue;
19941
20251
  }
19942
20252
 
19943
- var matchAnyProperty = key === '$';
20253
+ var matchAnyProperty = key === anyPropertyKey;
19944
20254
  var actualVal = matchAnyProperty ? actual : actual[key];
19945
- if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
20255
+ if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) {
19946
20256
  return false;
19947
20257
  }
19948
20258
  }
@@ -20680,21 +20990,22 @@ var uppercaseFilter = valueFn(uppercase);
20680
20990
  * @kind function
20681
20991
  *
20682
20992
  * @description
20683
- * Creates a new array or string containing only a specified number of elements. The elements
20684
- * are taken from either the beginning or the end of the source array, string or number, as specified by
20685
- * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
20686
- * converted to a string.
20687
- *
20688
- * @param {Array|string|number} input Source array, string or number to be limited.
20689
- * @param {string|number} limit The length of the returned array or string. If the `limit` number
20993
+ * Creates a new array or string containing only a specified number of elements. The elements are
20994
+ * taken from either the beginning or the end of the source array, string or number, as specified by
20995
+ * the value and sign (positive or negative) of `limit`. Other array-like objects are also supported
20996
+ * (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input,
20997
+ * it is converted to a string.
20998
+ *
20999
+ * @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited.
21000
+ * @param {string|number} limit - The length of the returned array or string. If the `limit` number
20690
21001
  * is positive, `limit` number of items from the beginning of the source array/string are copied.
20691
21002
  * If the number is negative, `limit` number of items from the end of the source array/string
20692
21003
  * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
20693
21004
  * the input will be returned unchanged.
20694
- * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
20695
- * indicates an offset from the end of `input`. Defaults to `0`.
20696
- * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
20697
- * had less than `limit` elements.
21005
+ * @param {(string|number)=} begin - Index at which to begin limitation. As a negative index,
21006
+ * `begin` indicates an offset from the end of `input`. Defaults to `0`.
21007
+ * @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had
21008
+ * less than `limit` elements.
20698
21009
  *
20699
21010
  * @example
20700
21011
  <example module="limitToExample">
@@ -20782,67 +21093,157 @@ function limitToFilter() {
20782
21093
  if (isNaN(limit)) return input;
20783
21094
 
20784
21095
  if (isNumber(input)) input = input.toString();
20785
- if (!isArray(input) && !isString(input)) return input;
21096
+ if (!isArrayLike(input)) return input;
20786
21097
 
20787
21098
  begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
20788
21099
  begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
20789
21100
 
20790
21101
  if (limit >= 0) {
20791
- return input.slice(begin, begin + limit);
21102
+ return sliceFn(input, begin, begin + limit);
20792
21103
  } else {
20793
21104
  if (begin === 0) {
20794
- return input.slice(limit, input.length);
21105
+ return sliceFn(input, limit, input.length);
20795
21106
  } else {
20796
- return input.slice(Math.max(0, begin + limit), begin);
21107
+ return sliceFn(input, Math.max(0, begin + limit), begin);
20797
21108
  }
20798
21109
  }
20799
21110
  };
20800
21111
  }
20801
21112
 
21113
+ function sliceFn(input, begin, end) {
21114
+ if (isString(input)) return input.slice(begin, end);
21115
+
21116
+ return slice.call(input, begin, end);
21117
+ }
21118
+
20802
21119
  /**
20803
21120
  * @ngdoc filter
20804
21121
  * @name orderBy
20805
21122
  * @kind function
20806
21123
  *
20807
21124
  * @description
20808
- * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
20809
- * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
20810
- * as expected, make sure they are actually being saved as numbers and not strings.
20811
- * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
21125
+ * Returns an array containing the items from the specified `collection`, ordered by a `comparator`
21126
+ * function based on the values computed using the `expression` predicate.
21127
+ *
21128
+ * For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in
21129
+ * `[{id: 'bar'}, {id: 'foo'}]`.
21130
+ *
21131
+ * The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray,
21132
+ * String, etc).
21133
+ *
21134
+ * The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker
21135
+ * for the preceeding one. The `expression` is evaluated against each item and the output is used
21136
+ * for comparing with other items.
21137
+ *
21138
+ * You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in
21139
+ * ascending order.
21140
+ *
21141
+ * The comparison is done using the `comparator` function. If none is specified, a default, built-in
21142
+ * comparator is used (see below for details - in a nutshell, it compares numbers numerically and
21143
+ * strings alphabetically).
21144
+ *
21145
+ * ### Under the hood
20812
21146
  *
20813
- * @param {Array} array The array (or array-like object) to sort.
20814
- * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
20815
- * used by the comparator to determine the order of elements.
21147
+ * Ordering the specified `collection` happens in two phases:
21148
+ *
21149
+ * 1. All items are passed through the predicate (or predicates), and the returned values are saved
21150
+ * along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed
21151
+ * through a predicate that extracts the value of the `label` property, would be transformed to:
21152
+ * ```
21153
+ * {
21154
+ * value: 'foo',
21155
+ * type: 'string',
21156
+ * index: ...
21157
+ * }
21158
+ * ```
21159
+ * 2. The comparator function is used to sort the items, based on the derived values, types and
21160
+ * indices.
21161
+ *
21162
+ * If you use a custom comparator, it will be called with pairs of objects of the form
21163
+ * `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal
21164
+ * (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the
21165
+ * second, or `1` otherwise.
21166
+ *
21167
+ * In order to ensure that the sorting will be deterministic across platforms, if none of the
21168
+ * specified predicates can distinguish between two items, `orderBy` will automatically introduce a
21169
+ * dummy predicate that returns the item's index as `value`.
21170
+ * (If you are using a custom comparator, make sure it can handle this predicate as well.)
21171
+ *
21172
+ * Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
21173
+ * value for an item, `orderBy` will try to convert that object to a primitive value, before passing
21174
+ * it to the comparator. The following rules govern the conversion:
21175
+ *
21176
+ * 1. If the object has a `valueOf()` method that returns a primitive, its return value will be
21177
+ * used instead.<br />
21178
+ * (If the object has a `valueOf()` method that returns another object, then the returned object
21179
+ * will be used in subsequent steps.)
21180
+ * 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that
21181
+ * returns a primitive, its return value will be used instead.<br />
21182
+ * (If the object has a `toString()` method that returns another object, then the returned object
21183
+ * will be used in subsequent steps.)
21184
+ * 3. No conversion; the object itself is used.
21185
+ *
21186
+ * ### The default comparator
21187
+ *
21188
+ * The default, built-in comparator should be sufficient for most usecases. In short, it compares
21189
+ * numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
21190
+ * using their index in the original collection, and sorts values of different types by type.
21191
+ *
21192
+ * More specifically, it follows these steps to determine the relative order of items:
21193
+ *
21194
+ * 1. If the compared values are of different types, compare the types themselves alphabetically.
21195
+ * 2. If both values are of type `string`, compare them alphabetically in a case- and
21196
+ * locale-insensitive way.
21197
+ * 3. If both values are objects, compare their indices instead.
21198
+ * 4. Otherwise, return:
21199
+ * - `0`, if the values are equal (by strict equality comparison, i.e. using `===`).
21200
+ * - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator).
21201
+ * - `1`, otherwise.
21202
+ *
21203
+ * **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
21204
+ * saved as numbers and not strings.
21205
+ *
21206
+ * @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
21207
+ * @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
21208
+ * predicates) to be used by the comparator to determine the order of elements.
20816
21209
  *
20817
21210
  * Can be one of:
20818
21211
  *
20819
- * - `function`: Getter function. The result of this function will be sorted using the
20820
- * `<`, `===`, `>` operator.
20821
- * - `string`: An Angular expression. The result of this expression is used to compare elements
20822
- * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
20823
- * 3 first characters of a property called `name`). The result of a constant expression
20824
- * is interpreted as a property name to be used in comparisons (for example `"special name"`
20825
- * to sort object by the value of their `special name` property). An expression can be
20826
- * optionally prefixed with `+` or `-` to control ascending or descending sort order
20827
- * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
20828
- * element itself is used to compare where sorting.
20829
- * - `Array`: An array of function or string predicates. The first predicate in the array
20830
- * is used for sorting, but when two items are equivalent, the next predicate is used.
21212
+ * - `Function`: A getter function. This function will be called with each item as argument and
21213
+ * the return value will be used for sorting.
21214
+ * - `string`: An Angular expression. This expression will be evaluated against each item and the
21215
+ * result will be used for sorting. For example, use `'label'` to sort by a property called
21216
+ * `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label`
21217
+ * property.<br />
21218
+ * (The result of a constant expression is interpreted as a property name to be used for
21219
+ * comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a
21220
+ * property called `special name`.)<br />
21221
+ * An expression can be optionally prefixed with `+` or `-` to control the sorting direction,
21222
+ * ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided,
21223
+ * (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons.
21224
+ * - `Array`: An array of function and/or string predicates. If a predicate cannot determine the
21225
+ * relative order of two items, the next predicate is used as a tie-breaker.
20831
21226
  *
20832
- * If the predicate is missing or empty then it defaults to `'+'`.
21227
+ * **Note:** If the predicate is missing or empty then it defaults to `'+'`.
20833
21228
  *
20834
- * @param {boolean=} reverse Reverse the order of the array.
20835
- * @returns {Array} Sorted copy of the source array.
21229
+ * @param {boolean=} reverse - If `true`, reverse the sorting order.
21230
+ * @param {(Function)=} comparator - The comparator function used to determine the relative order of
21231
+ * value pairs. If omitted, the built-in comparator will be used.
21232
+ *
21233
+ * @returns {Array} - The sorted array.
20836
21234
  *
20837
21235
  *
20838
21236
  * @example
20839
- * The example below demonstrates a simple ngRepeat, where the data is sorted
20840
- * by age in descending order (predicate is set to `'-age'`).
20841
- * `reverse` is not set, which means it defaults to `false`.
20842
- <example module="orderByExample">
21237
+ * ### Ordering a table with `ngRepeat`
21238
+ *
21239
+ * The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by
21240
+ * age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means
21241
+ * it defaults to the built-in comparator.
21242
+ *
21243
+ <example name="orderBy-static" module="orderByExample1">
20843
21244
  <file name="index.html">
20844
21245
  <div ng-controller="ExampleController">
20845
- <table class="friend">
21246
+ <table class="friends">
20846
21247
  <tr>
20847
21248
  <th>Name</th>
20848
21249
  <th>Phone Number</th>
@@ -20857,43 +21258,77 @@ function limitToFilter() {
20857
21258
  </div>
20858
21259
  </file>
20859
21260
  <file name="script.js">
20860
- angular.module('orderByExample', [])
21261
+ angular.module('orderByExample1', [])
20861
21262
  .controller('ExampleController', ['$scope', function($scope) {
20862
- $scope.friends =
20863
- [{name:'John', phone:'555-1212', age:10},
20864
- {name:'Mary', phone:'555-9876', age:19},
20865
- {name:'Mike', phone:'555-4321', age:21},
20866
- {name:'Adam', phone:'555-5678', age:35},
20867
- {name:'Julie', phone:'555-8765', age:29}];
21263
+ $scope.friends = [
21264
+ {name: 'John', phone: '555-1212', age: 10},
21265
+ {name: 'Mary', phone: '555-9876', age: 19},
21266
+ {name: 'Mike', phone: '555-4321', age: 21},
21267
+ {name: 'Adam', phone: '555-5678', age: 35},
21268
+ {name: 'Julie', phone: '555-8765', age: 29}
21269
+ ];
20868
21270
  }]);
20869
21271
  </file>
21272
+ <file name="style.css">
21273
+ .friends {
21274
+ border-collapse: collapse;
21275
+ }
21276
+
21277
+ .friends th {
21278
+ border-bottom: 1px solid;
21279
+ }
21280
+ .friends td, .friends th {
21281
+ border-left: 1px solid;
21282
+ padding: 5px 10px;
21283
+ }
21284
+ .friends td:first-child, .friends th:first-child {
21285
+ border-left: none;
21286
+ }
21287
+ </file>
21288
+ <file name="protractor.js" type="protractor">
21289
+ // Element locators
21290
+ var names = element.all(by.repeater('friends').column('friend.name'));
21291
+
21292
+ it('should sort friends by age in reverse order', function() {
21293
+ expect(names.get(0).getText()).toBe('Adam');
21294
+ expect(names.get(1).getText()).toBe('Julie');
21295
+ expect(names.get(2).getText()).toBe('Mike');
21296
+ expect(names.get(3).getText()).toBe('Mary');
21297
+ expect(names.get(4).getText()).toBe('John');
21298
+ });
21299
+ </file>
20870
21300
  </example>
21301
+ * <hr />
20871
21302
  *
20872
- * The predicate and reverse parameters can be controlled dynamically through scope properties,
20873
- * as shown in the next example.
20874
21303
  * @example
20875
- <example module="orderByExample">
21304
+ * ### Changing parameters dynamically
21305
+ *
21306
+ * All parameters can be changed dynamically. The next example shows how you can make the columns of
21307
+ * a table sortable, by binding the `expression` and `reverse` parameters to scope properties.
21308
+ *
21309
+ <example name="orderBy-dynamic" module="orderByExample2">
20876
21310
  <file name="index.html">
20877
21311
  <div ng-controller="ExampleController">
20878
- <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
21312
+ <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
20879
21313
  <hr/>
20880
- <button ng-click="predicate=''">Set to unsorted</button>
20881
- <table class="friend">
21314
+ <button ng-click="propertyName = null; reverse = false">Set to unsorted</button>
21315
+ <hr/>
21316
+ <table class="friends">
20882
21317
  <tr>
20883
- <th>
20884
- <button ng-click="order('name')">Name</button>
20885
- <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
20886
- </th>
20887
- <th>
20888
- <button ng-click="order('phone')">Phone Number</button>
20889
- <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
20890
- </th>
20891
- <th>
20892
- <button ng-click="order('age')">Age</button>
20893
- <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
20894
- </th>
21318
+ <th>
21319
+ <button ng-click="sortBy('name')">Name</button>
21320
+ <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
21321
+ </th>
21322
+ <th>
21323
+ <button ng-click="sortBy('phone')">Phone Number</button>
21324
+ <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
21325
+ </th>
21326
+ <th>
21327
+ <button ng-click="sortBy('age')">Age</button>
21328
+ <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
21329
+ </th>
20895
21330
  </tr>
20896
- <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
21331
+ <tr ng-repeat="friend in friends | orderBy:propertyName:reverse">
20897
21332
  <td>{{friend.name}}</td>
20898
21333
  <td>{{friend.phone}}</td>
20899
21334
  <td>{{friend.age}}</td>
@@ -20902,100 +21337,335 @@ function limitToFilter() {
20902
21337
  </div>
20903
21338
  </file>
20904
21339
  <file name="script.js">
20905
- angular.module('orderByExample', [])
21340
+ angular.module('orderByExample2', [])
20906
21341
  .controller('ExampleController', ['$scope', function($scope) {
20907
- $scope.friends =
20908
- [{name:'John', phone:'555-1212', age:10},
20909
- {name:'Mary', phone:'555-9876', age:19},
20910
- {name:'Mike', phone:'555-4321', age:21},
20911
- {name:'Adam', phone:'555-5678', age:35},
20912
- {name:'Julie', phone:'555-8765', age:29}];
20913
- $scope.predicate = 'age';
21342
+ var friends = [
21343
+ {name: 'John', phone: '555-1212', age: 10},
21344
+ {name: 'Mary', phone: '555-9876', age: 19},
21345
+ {name: 'Mike', phone: '555-4321', age: 21},
21346
+ {name: 'Adam', phone: '555-5678', age: 35},
21347
+ {name: 'Julie', phone: '555-8765', age: 29}
21348
+ ];
21349
+
21350
+ $scope.propertyName = 'age';
20914
21351
  $scope.reverse = true;
20915
- $scope.order = function(predicate) {
20916
- $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
20917
- $scope.predicate = predicate;
21352
+ $scope.friends = friends;
21353
+
21354
+ $scope.sortBy = function(propertyName) {
21355
+ $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
21356
+ $scope.propertyName = propertyName;
20918
21357
  };
20919
21358
  }]);
20920
- </file>
21359
+ </file>
20921
21360
  <file name="style.css">
21361
+ .friends {
21362
+ border-collapse: collapse;
21363
+ }
21364
+
21365
+ .friends th {
21366
+ border-bottom: 1px solid;
21367
+ }
21368
+ .friends td, .friends th {
21369
+ border-left: 1px solid;
21370
+ padding: 5px 10px;
21371
+ }
21372
+ .friends td:first-child, .friends th:first-child {
21373
+ border-left: none;
21374
+ }
21375
+
20922
21376
  .sortorder:after {
20923
- content: '\25b2';
21377
+ content: '\25b2'; // BLACK UP-POINTING TRIANGLE
20924
21378
  }
20925
21379
  .sortorder.reverse:after {
20926
- content: '\25bc';
21380
+ content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE
20927
21381
  }
20928
21382
  </file>
21383
+ <file name="protractor.js" type="protractor">
21384
+ // Element locators
21385
+ var unsortButton = element(by.partialButtonText('unsorted'));
21386
+ var nameHeader = element(by.partialButtonText('Name'));
21387
+ var phoneHeader = element(by.partialButtonText('Phone'));
21388
+ var ageHeader = element(by.partialButtonText('Age'));
21389
+ var firstName = element(by.repeater('friends').column('friend.name').row(0));
21390
+ var lastName = element(by.repeater('friends').column('friend.name').row(4));
21391
+
21392
+ it('should sort friends by some property, when clicking on the column header', function() {
21393
+ expect(firstName.getText()).toBe('Adam');
21394
+ expect(lastName.getText()).toBe('John');
21395
+
21396
+ phoneHeader.click();
21397
+ expect(firstName.getText()).toBe('John');
21398
+ expect(lastName.getText()).toBe('Mary');
21399
+
21400
+ nameHeader.click();
21401
+ expect(firstName.getText()).toBe('Adam');
21402
+ expect(lastName.getText()).toBe('Mike');
21403
+
21404
+ ageHeader.click();
21405
+ expect(firstName.getText()).toBe('John');
21406
+ expect(lastName.getText()).toBe('Adam');
21407
+ });
21408
+
21409
+ it('should sort friends in reverse order, when clicking on the same column', function() {
21410
+ expect(firstName.getText()).toBe('Adam');
21411
+ expect(lastName.getText()).toBe('John');
21412
+
21413
+ ageHeader.click();
21414
+ expect(firstName.getText()).toBe('John');
21415
+ expect(lastName.getText()).toBe('Adam');
21416
+
21417
+ ageHeader.click();
21418
+ expect(firstName.getText()).toBe('Adam');
21419
+ expect(lastName.getText()).toBe('John');
21420
+ });
21421
+
21422
+ it('should restore the original order, when clicking "Set to unsorted"', function() {
21423
+ expect(firstName.getText()).toBe('Adam');
21424
+ expect(lastName.getText()).toBe('John');
21425
+
21426
+ unsortButton.click();
21427
+ expect(firstName.getText()).toBe('John');
21428
+ expect(lastName.getText()).toBe('Julie');
21429
+ });
21430
+ </file>
20929
21431
  </example>
21432
+ * <hr />
20930
21433
  *
20931
- * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
20932
- * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
20933
- * desired parameters.
21434
+ * @example
21435
+ * ### Using `orderBy` inside a controller
20934
21436
  *
20935
- * Example:
21437
+ * It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and
21438
+ * calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory
21439
+ * and retrieve the `orderBy` filter with `$filter('orderBy')`.)
20936
21440
  *
20937
- * @example
20938
- <example module="orderByExample">
20939
- <file name="index.html">
20940
- <div ng-controller="ExampleController">
20941
- <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
20942
- <table class="friend">
20943
- <tr>
20944
- <th>
20945
- <button ng-click="order('name')">Name</button>
20946
- <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
20947
- </th>
20948
- <th>
20949
- <button ng-click="order('phone')">Phone Number</button>
20950
- <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
20951
- </th>
20952
- <th>
20953
- <button ng-click="order('age')">Age</button>
20954
- <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
20955
- </th>
20956
- </tr>
20957
- <tr ng-repeat="friend in friends">
20958
- <td>{{friend.name}}</td>
20959
- <td>{{friend.phone}}</td>
20960
- <td>{{friend.age}}</td>
20961
- </tr>
20962
- </table>
20963
- </div>
20964
- </file>
21441
+ <example name="orderBy-call-manually" module="orderByExample3">
21442
+ <file name="index.html">
21443
+ <div ng-controller="ExampleController">
21444
+ <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
21445
+ <hr/>
21446
+ <button ng-click="sortBy(null)">Set to unsorted</button>
21447
+ <hr/>
21448
+ <table class="friends">
21449
+ <tr>
21450
+ <th>
21451
+ <button ng-click="sortBy('name')">Name</button>
21452
+ <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
21453
+ </th>
21454
+ <th>
21455
+ <button ng-click="sortBy('phone')">Phone Number</button>
21456
+ <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
21457
+ </th>
21458
+ <th>
21459
+ <button ng-click="sortBy('age')">Age</button>
21460
+ <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
21461
+ </th>
21462
+ </tr>
21463
+ <tr ng-repeat="friend in friends">
21464
+ <td>{{friend.name}}</td>
21465
+ <td>{{friend.phone}}</td>
21466
+ <td>{{friend.age}}</td>
21467
+ </tr>
21468
+ </table>
21469
+ </div>
21470
+ </file>
21471
+ <file name="script.js">
21472
+ angular.module('orderByExample3', [])
21473
+ .controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) {
21474
+ var friends = [
21475
+ {name: 'John', phone: '555-1212', age: 10},
21476
+ {name: 'Mary', phone: '555-9876', age: 19},
21477
+ {name: 'Mike', phone: '555-4321', age: 21},
21478
+ {name: 'Adam', phone: '555-5678', age: 35},
21479
+ {name: 'Julie', phone: '555-8765', age: 29}
21480
+ ];
21481
+
21482
+ $scope.propertyName = 'age';
21483
+ $scope.reverse = true;
21484
+ $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
20965
21485
 
20966
- <file name="script.js">
20967
- angular.module('orderByExample', [])
20968
- .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
20969
- var orderBy = $filter('orderBy');
20970
- $scope.friends = [
20971
- { name: 'John', phone: '555-1212', age: 10 },
20972
- { name: 'Mary', phone: '555-9876', age: 19 },
20973
- { name: 'Mike', phone: '555-4321', age: 21 },
20974
- { name: 'Adam', phone: '555-5678', age: 35 },
20975
- { name: 'Julie', phone: '555-8765', age: 29 }
20976
- ];
20977
- $scope.order = function(predicate) {
20978
- $scope.predicate = predicate;
20979
- $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
20980
- $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
20981
- };
20982
- $scope.order('age', true);
20983
- }]);
20984
- </file>
21486
+ $scope.sortBy = function(propertyName) {
21487
+ $scope.reverse = (propertyName !== null && $scope.propertyName === propertyName)
21488
+ ? !$scope.reverse : false;
21489
+ $scope.propertyName = propertyName;
21490
+ $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
21491
+ };
21492
+ }]);
21493
+ </file>
21494
+ <file name="style.css">
21495
+ .friends {
21496
+ border-collapse: collapse;
21497
+ }
21498
+
21499
+ .friends th {
21500
+ border-bottom: 1px solid;
21501
+ }
21502
+ .friends td, .friends th {
21503
+ border-left: 1px solid;
21504
+ padding: 5px 10px;
21505
+ }
21506
+ .friends td:first-child, .friends th:first-child {
21507
+ border-left: none;
21508
+ }
20985
21509
 
20986
- <file name="style.css">
20987
21510
  .sortorder:after {
20988
- content: '\25b2';
21511
+ content: '\25b2'; // BLACK UP-POINTING TRIANGLE
20989
21512
  }
20990
21513
  .sortorder.reverse:after {
20991
- content: '\25bc';
21514
+ content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE
20992
21515
  }
20993
- </file>
20994
- </example>
21516
+ </file>
21517
+ <file name="protractor.js" type="protractor">
21518
+ // Element locators
21519
+ var unsortButton = element(by.partialButtonText('unsorted'));
21520
+ var nameHeader = element(by.partialButtonText('Name'));
21521
+ var phoneHeader = element(by.partialButtonText('Phone'));
21522
+ var ageHeader = element(by.partialButtonText('Age'));
21523
+ var firstName = element(by.repeater('friends').column('friend.name').row(0));
21524
+ var lastName = element(by.repeater('friends').column('friend.name').row(4));
21525
+
21526
+ it('should sort friends by some property, when clicking on the column header', function() {
21527
+ expect(firstName.getText()).toBe('Adam');
21528
+ expect(lastName.getText()).toBe('John');
21529
+
21530
+ phoneHeader.click();
21531
+ expect(firstName.getText()).toBe('John');
21532
+ expect(lastName.getText()).toBe('Mary');
21533
+
21534
+ nameHeader.click();
21535
+ expect(firstName.getText()).toBe('Adam');
21536
+ expect(lastName.getText()).toBe('Mike');
21537
+
21538
+ ageHeader.click();
21539
+ expect(firstName.getText()).toBe('John');
21540
+ expect(lastName.getText()).toBe('Adam');
21541
+ });
21542
+
21543
+ it('should sort friends in reverse order, when clicking on the same column', function() {
21544
+ expect(firstName.getText()).toBe('Adam');
21545
+ expect(lastName.getText()).toBe('John');
21546
+
21547
+ ageHeader.click();
21548
+ expect(firstName.getText()).toBe('John');
21549
+ expect(lastName.getText()).toBe('Adam');
21550
+
21551
+ ageHeader.click();
21552
+ expect(firstName.getText()).toBe('Adam');
21553
+ expect(lastName.getText()).toBe('John');
21554
+ });
21555
+
21556
+ it('should restore the original order, when clicking "Set to unsorted"', function() {
21557
+ expect(firstName.getText()).toBe('Adam');
21558
+ expect(lastName.getText()).toBe('John');
21559
+
21560
+ unsortButton.click();
21561
+ expect(firstName.getText()).toBe('John');
21562
+ expect(lastName.getText()).toBe('Julie');
21563
+ });
21564
+ </file>
21565
+ </example>
21566
+ * <hr />
21567
+ *
21568
+ * @example
21569
+ * ### Using a custom comparator
21570
+ *
21571
+ * If you have very specific requirements about the way items are sorted, you can pass your own
21572
+ * comparator function. For example, you might need to compare some strings in a locale-sensitive
21573
+ * way. (When specifying a custom comparator, you also need to pass a value for the `reverse`
21574
+ * argument - passing `false` retains the default sorting order, i.e. ascending.)
21575
+ *
21576
+ <example name="orderBy-custom-comparator" module="orderByExample4">
21577
+ <file name="index.html">
21578
+ <div ng-controller="ExampleController">
21579
+ <div class="friends-container custom-comparator">
21580
+ <h3>Locale-sensitive Comparator</h3>
21581
+ <table class="friends">
21582
+ <tr>
21583
+ <th>Name</th>
21584
+ <th>Favorite Letter</th>
21585
+ </tr>
21586
+ <tr ng-repeat="friend in friends | orderBy:'favoriteLetter':false:localeSensitiveComparator">
21587
+ <td>{{friend.name}}</td>
21588
+ <td>{{friend.favoriteLetter}}</td>
21589
+ </tr>
21590
+ </table>
21591
+ </div>
21592
+ <div class="friends-container default-comparator">
21593
+ <h3>Default Comparator</h3>
21594
+ <table class="friends">
21595
+ <tr>
21596
+ <th>Name</th>
21597
+ <th>Favorite Letter</th>
21598
+ </tr>
21599
+ <tr ng-repeat="friend in friends | orderBy:'favoriteLetter'">
21600
+ <td>{{friend.name}}</td>
21601
+ <td>{{friend.favoriteLetter}}</td>
21602
+ </tr>
21603
+ </table>
21604
+ </div>
21605
+ </div>
21606
+ </file>
21607
+ <file name="script.js">
21608
+ angular.module('orderByExample4', [])
21609
+ .controller('ExampleController', ['$scope', function($scope) {
21610
+ $scope.friends = [
21611
+ {name: 'John', favoriteLetter: 'Ä'},
21612
+ {name: 'Mary', favoriteLetter: 'Ü'},
21613
+ {name: 'Mike', favoriteLetter: 'Ö'},
21614
+ {name: 'Adam', favoriteLetter: 'H'},
21615
+ {name: 'Julie', favoriteLetter: 'Z'}
21616
+ ];
21617
+
21618
+ $scope.localeSensitiveComparator = function(v1, v2) {
21619
+ // If we don't get strings, just compare by index
21620
+ if (v1.type !== 'string' || v2.type !== 'string') {
21621
+ return (v1.index < v2.index) ? -1 : 1;
21622
+ }
21623
+
21624
+ // Compare strings alphabetically, taking locale into account
21625
+ return v1.value.localeCompare(v2.value);
21626
+ };
21627
+ }]);
21628
+ </file>
21629
+ <file name="style.css">
21630
+ .friends-container {
21631
+ display: inline-block;
21632
+ margin: 0 30px;
21633
+ }
21634
+
21635
+ .friends {
21636
+ border-collapse: collapse;
21637
+ }
21638
+
21639
+ .friends th {
21640
+ border-bottom: 1px solid;
21641
+ }
21642
+ .friends td, .friends th {
21643
+ border-left: 1px solid;
21644
+ padding: 5px 10px;
21645
+ }
21646
+ .friends td:first-child, .friends th:first-child {
21647
+ border-left: none;
21648
+ }
21649
+ </file>
21650
+ <file name="protractor.js" type="protractor">
21651
+ // Element locators
21652
+ var container = element(by.css('.custom-comparator'));
21653
+ var names = container.all(by.repeater('friends').column('friend.name'));
21654
+
21655
+ it('should sort friends by favorite letter (in correct alphabetical order)', function() {
21656
+ expect(names.get(0).getText()).toBe('John');
21657
+ expect(names.get(1).getText()).toBe('Adam');
21658
+ expect(names.get(2).getText()).toBe('Mike');
21659
+ expect(names.get(3).getText()).toBe('Mary');
21660
+ expect(names.get(4).getText()).toBe('Julie');
21661
+ });
21662
+ </file>
21663
+ </example>
21664
+ *
20995
21665
  */
20996
21666
  orderByFilter.$inject = ['$parse'];
20997
21667
  function orderByFilter($parse) {
20998
- return function(array, sortPredicate, reverseOrder) {
21668
+ return function(array, sortPredicate, reverseOrder, compareFn) {
20999
21669
 
21000
21670
  if (array == null) return array;
21001
21671
  if (!isArrayLike(array)) {
@@ -21005,11 +21675,12 @@ function orderByFilter($parse) {
21005
21675
  if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
21006
21676
  if (sortPredicate.length === 0) { sortPredicate = ['+']; }
21007
21677
 
21008
- var predicates = processPredicates(sortPredicate, reverseOrder);
21009
- // Add a predicate at the end that evaluates to the element index. This makes the
21010
- // sort stable as it works as a tie-breaker when all the input predicates cannot
21011
- // distinguish between two elements.
21012
- predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
21678
+ var predicates = processPredicates(sortPredicate);
21679
+
21680
+ var descending = reverseOrder ? -1 : 1;
21681
+
21682
+ // Define the `compare()` function. Use a default comparator if none is specified.
21683
+ var compare = isFunction(compareFn) ? compareFn : defaultCompare;
21013
21684
 
21014
21685
  // The next three lines are a version of a Swartzian Transform idiom from Perl
21015
21686
  // (sometimes called the Decorate-Sort-Undecorate idiom)
@@ -21021,8 +21692,12 @@ function orderByFilter($parse) {
21021
21692
  return array;
21022
21693
 
21023
21694
  function getComparisonObject(value, index) {
21695
+ // NOTE: We are adding an extra `tieBreaker` value based on the element's index.
21696
+ // This will be used to keep the sort stable when none of the input predicates can
21697
+ // distinguish between two elements.
21024
21698
  return {
21025
21699
  value: value,
21700
+ tieBreaker: {value: index, type: 'number', index: index},
21026
21701
  predicateValues: predicates.map(function(predicate) {
21027
21702
  return getPredicateValue(predicate.get(value), index);
21028
21703
  })
@@ -21030,18 +21705,19 @@ function orderByFilter($parse) {
21030
21705
  }
21031
21706
 
21032
21707
  function doComparison(v1, v2) {
21033
- var result = 0;
21034
- for (var index=0, length = predicates.length; index < length; ++index) {
21035
- result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
21036
- if (result) break;
21708
+ for (var i = 0, ii = predicates.length; i < ii; i++) {
21709
+ var result = compare(v1.predicateValues[i], v2.predicateValues[i]);
21710
+ if (result) {
21711
+ return result * predicates[i].descending * descending;
21712
+ }
21037
21713
  }
21038
- return result;
21714
+
21715
+ return compare(v1.tieBreaker, v2.tieBreaker) * descending;
21039
21716
  }
21040
21717
  };
21041
21718
 
21042
- function processPredicates(sortPredicate, reverseOrder) {
21043
- reverseOrder = reverseOrder ? -1 : 1;
21044
- return sortPredicate.map(function(predicate) {
21719
+ function processPredicates(sortPredicates) {
21720
+ return sortPredicates.map(function(predicate) {
21045
21721
  var descending = 1, get = identity;
21046
21722
 
21047
21723
  if (isFunction(predicate)) {
@@ -21059,7 +21735,7 @@ function orderByFilter($parse) {
21059
21735
  }
21060
21736
  }
21061
21737
  }
21062
- return { get: get, descending: descending * reverseOrder };
21738
+ return {get: get, descending: descending};
21063
21739
  });
21064
21740
  }
21065
21741
 
@@ -21074,9 +21750,9 @@ function orderByFilter($parse) {
21074
21750
  }
21075
21751
  }
21076
21752
 
21077
- function objectValue(value, index) {
21753
+ function objectValue(value) {
21078
21754
  // If `valueOf` is a valid function use that
21079
- if (typeof value.valueOf === 'function') {
21755
+ if (isFunction(value.valueOf)) {
21080
21756
  value = value.valueOf();
21081
21757
  if (isPrimitive(value)) return value;
21082
21758
  }
@@ -21085,8 +21761,8 @@ function orderByFilter($parse) {
21085
21761
  value = value.toString();
21086
21762
  if (isPrimitive(value)) return value;
21087
21763
  }
21088
- // We have a basic object so we use the position of the object in the collection
21089
- return index;
21764
+
21765
+ return value;
21090
21766
  }
21091
21767
 
21092
21768
  function getPredicateValue(value, index) {
@@ -21094,23 +21770,39 @@ function orderByFilter($parse) {
21094
21770
  if (value === null) {
21095
21771
  type = 'string';
21096
21772
  value = 'null';
21097
- } else if (type === 'string') {
21098
- value = value.toLowerCase();
21099
21773
  } else if (type === 'object') {
21100
- value = objectValue(value, index);
21774
+ value = objectValue(value);
21101
21775
  }
21102
- return { value: value, type: type };
21776
+ return {value: value, type: type, index: index};
21103
21777
  }
21104
21778
 
21105
- function compare(v1, v2) {
21779
+ function defaultCompare(v1, v2) {
21106
21780
  var result = 0;
21107
- if (v1.type === v2.type) {
21108
- if (v1.value !== v2.value) {
21109
- result = v1.value < v2.value ? -1 : 1;
21781
+ var type1 = v1.type;
21782
+ var type2 = v2.type;
21783
+
21784
+ if (type1 === type2) {
21785
+ var value1 = v1.value;
21786
+ var value2 = v2.value;
21787
+
21788
+ if (type1 === 'string') {
21789
+ // Compare strings case-insensitively
21790
+ value1 = value1.toLowerCase();
21791
+ value2 = value2.toLowerCase();
21792
+ } else if (type1 === 'object') {
21793
+ // For basic objects, use the position of the object
21794
+ // in the collection instead of the value
21795
+ if (isObject(value1)) value1 = v1.index;
21796
+ if (isObject(value2)) value2 = v2.index;
21797
+ }
21798
+
21799
+ if (value1 !== value2) {
21800
+ result = value1 < value2 ? -1 : 1;
21110
21801
  }
21111
21802
  } else {
21112
- result = v1.type < v2.type ? -1 : 1;
21803
+ result = type1 < type2 ? -1 : 1;
21113
21804
  }
21805
+
21114
21806
  return result;
21115
21807
  }
21116
21808
  }
@@ -21390,9 +22082,11 @@ var htmlAnchorDirective = valueFn({
21390
22082
  *
21391
22083
  * @description
21392
22084
  *
21393
- * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
22085
+ * Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy.
22086
+ * Note that `readonly` applies only to `input` elements with specific types. [See the input docs on
22087
+ * MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information.
21394
22088
  *
21395
- * A special directive is necessary because we cannot use interpolation inside the `readOnly`
22089
+ * A special directive is necessary because we cannot use interpolation inside the `readonly`
21396
22090
  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21397
22091
  *
21398
22092
  * @example
@@ -21429,6 +22123,13 @@ var htmlAnchorDirective = valueFn({
21429
22123
  * A special directive is necessary because we cannot use interpolation inside the `selected`
21430
22124
  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21431
22125
  *
22126
+ * <div class="alert alert-warning">
22127
+ * **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only
22128
+ * sets the `selected` attribute on the element. If you are using `ngModel` on the select, you
22129
+ * should not use `ngSelected` on the options, as `ngModel` will set the select value and
22130
+ * selected options.
22131
+ * </div>
22132
+ *
21432
22133
  * @example
21433
22134
  <example>
21434
22135
  <file name="index.html">
@@ -21465,6 +22166,11 @@ var htmlAnchorDirective = valueFn({
21465
22166
  * A special directive is necessary because we cannot use interpolation inside the `open`
21466
22167
  * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21467
22168
  *
22169
+ * ## A note about browser compatibility
22170
+ *
22171
+ * Edge, Firefox, and Internet Explorer do not support the `details` element, it is
22172
+ * recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
22173
+ *
21468
22174
  * @example
21469
22175
  <example>
21470
22176
  <file name="index.html">
@@ -22155,7 +22861,9 @@ var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-
22155
22861
  // 9. Fragment
22156
22862
  // 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
22157
22863
  var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
22158
- var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
22864
+ /* jshint maxlen:220 */
22865
+ var EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
22866
+ /* jshint maxlen:200 */
22159
22867
  var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
22160
22868
  var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
22161
22869
  var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
@@ -23540,7 +24248,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23540
24248
 
23541
24249
  attr.$observe('min', function(val) {
23542
24250
  if (isDefined(val) && !isNumber(val)) {
23543
- val = parseFloat(val, 10);
24251
+ val = parseFloat(val);
23544
24252
  }
23545
24253
  minVal = isNumber(val) && !isNaN(val) ? val : undefined;
23546
24254
  // TODO(matsko): implement validateLater to reduce number of validations
@@ -23556,7 +24264,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23556
24264
 
23557
24265
  attr.$observe('max', function(val) {
23558
24266
  if (isDefined(val) && !isNumber(val)) {
23559
- val = parseFloat(val, 10);
24267
+ val = parseFloat(val);
23560
24268
  }
23561
24269
  maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
23562
24270
  // TODO(matsko): implement validateLater to reduce number of validations
@@ -24363,6 +25071,11 @@ function classDirective(name, selector) {
24363
25071
  * When the expression changes, the previously added classes are removed and only then are the
24364
25072
  * new classes added.
24365
25073
  *
25074
+ * @knownIssue
25075
+ * You should not use {@link guide/interpolation interpolation} in the value of the `class`
25076
+ * attribute, when using the `ngClass` directive on the same element.
25077
+ * See {@link guide/interpolation#known-issues here} for more info.
25078
+ *
24366
25079
  * @animations
24367
25080
  * | Animation | Occurs |
24368
25081
  * |----------------------------------|-------------------------------------|
@@ -28308,7 +29021,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
28308
29021
 
28309
29022
  for (var i = options.items.length - 1; i >= 0; i--) {
28310
29023
  var option = options.items[i];
28311
- if (option.group) {
29024
+ if (isDefined(option.group)) {
28312
29025
  jqLiteRemove(option.element.parentNode);
28313
29026
  } else {
28314
29027
  jqLiteRemove(option.element);
@@ -28340,7 +29053,8 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
28340
29053
  listFragment.appendChild(groupElement);
28341
29054
 
28342
29055
  // Update the label on the group element
28343
- groupElement.label = option.group;
29056
+ // "null" is special cased because of Safari
29057
+ groupElement.label = option.group === null ? 'null' : option.group;
28344
29058
 
28345
29059
  // Store it for use later
28346
29060
  groupElementMap[option.group] = groupElement;
@@ -28676,7 +29390,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
28676
29390
  * it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
28677
29391
  *
28678
29392
  * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
28679
- * objects, and will throw if used with one.
29393
+ * objects, and will throw an error if used with one.
28680
29394
  *
28681
29395
  * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
28682
29396
  * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
@@ -29525,6 +30239,11 @@ var ngHideDirective = ['$animate', function($animate) {
29525
30239
  * @description
29526
30240
  * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
29527
30241
  *
30242
+ * @knownIssue
30243
+ * You should not use {@link guide/interpolation interpolation} in the value of the `style`
30244
+ * attribute, when using the `ngStyle` directive on the same element.
30245
+ * See {@link guide/interpolation#known-issues here} for more info.
30246
+ *
29528
30247
  * @element ANY
29529
30248
  * @param {expression} ngStyle
29530
30249
  *
@@ -29936,37 +30655,63 @@ var ngSwitchDefaultDirective = ngDirective({
29936
30655
  * </example>
29937
30656
  */
29938
30657
  var ngTranscludeMinErr = minErr('ngTransclude');
29939
- var ngTranscludeDirective = ngDirective({
29940
- restrict: 'EAC',
29941
- link: function($scope, $element, $attrs, controller, $transclude) {
30658
+ var ngTranscludeDirective = ['$compile', function($compile) {
30659
+ return {
30660
+ restrict: 'EAC',
30661
+ terminal: true,
30662
+ compile: function ngTranscludeCompile(tElement) {
29942
30663
 
29943
- if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
29944
- // If the attribute is of the form: `ng-transclude="ng-transclude"`
29945
- // then treat it like the default
29946
- $attrs.ngTransclude = '';
29947
- }
30664
+ // Remove and cache any original content to act as a fallback
30665
+ var fallbackLinkFn = $compile(tElement.contents());
30666
+ tElement.empty();
29948
30667
 
29949
- function ngTranscludeCloneAttachFn(clone) {
29950
- if (clone.length) {
29951
- $element.empty();
29952
- $element.append(clone);
29953
- }
29954
- }
30668
+ return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) {
29955
30669
 
29956
- if (!$transclude) {
29957
- throw ngTranscludeMinErr('orphan',
29958
- 'Illegal use of ngTransclude directive in the template! ' +
29959
- 'No parent directive that requires a transclusion found. ' +
29960
- 'Element: {0}',
29961
- startingTag($element));
29962
- }
30670
+ if (!$transclude) {
30671
+ throw ngTranscludeMinErr('orphan',
30672
+ 'Illegal use of ngTransclude directive in the template! ' +
30673
+ 'No parent directive that requires a transclusion found. ' +
30674
+ 'Element: {0}',
30675
+ startingTag($element));
30676
+ }
29963
30677
 
29964
- // If there is no slot name defined or the slot name is not optional
29965
- // then transclude the slot
29966
- var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
29967
- $transclude(ngTranscludeCloneAttachFn, null, slotName);
29968
- }
29969
- });
30678
+
30679
+ // If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default
30680
+ if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
30681
+ $attrs.ngTransclude = '';
30682
+ }
30683
+ var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
30684
+
30685
+ // If the slot is required and no transclusion content is provided then this call will throw an error
30686
+ $transclude(ngTranscludeCloneAttachFn, null, slotName);
30687
+
30688
+ // If the slot is optional and no transclusion content is provided then use the fallback content
30689
+ if (slotName && !$transclude.isSlotFilled(slotName)) {
30690
+ useFallbackContent();
30691
+ }
30692
+
30693
+ function ngTranscludeCloneAttachFn(clone, transcludedScope) {
30694
+ if (clone.length) {
30695
+ $element.append(clone);
30696
+ } else {
30697
+ useFallbackContent();
30698
+ // There is nothing linked against the transcluded scope since no content was available,
30699
+ // so it should be safe to clean up the generated scope.
30700
+ transcludedScope.$destroy();
30701
+ }
30702
+ }
30703
+
30704
+ function useFallbackContent() {
30705
+ // Since this is the fallback content rather than the transcluded content,
30706
+ // we link against the scope of this directive rather than the transcluded scope
30707
+ fallbackLinkFn($scope, function(clone) {
30708
+ $element.append(clone);
30709
+ });
30710
+ }
30711
+ };
30712
+ }
30713
+ };
30714
+ }];
29970
30715
 
29971
30716
  /**
29972
30717
  * @ngdoc directive