angularjs-rails 1.5.0 → 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  /**
2
- * @license AngularJS v1.5.0
2
+ * @license AngularJS v1.5.5
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
- (function(window, angular, undefined) {'use strict';
6
+ (function(window, angular) {'use strict';
7
7
 
8
8
  var $resourceMinErr = angular.$$minErr('$resource');
9
9
 
@@ -711,7 +711,7 @@ angular.module('ngResource', ['ng']).
711
711
  return $q.reject(response);
712
712
  });
713
713
 
714
- promise.finally(function() {
714
+ promise['finally'](function() {
715
715
  value.$resolved = true;
716
716
  if (!isInstanceCall && cancellable) {
717
717
  value.$cancelRequest = angular.noop;
@@ -1,9 +1,9 @@
1
1
  /**
2
- * @license AngularJS v1.5.0
2
+ * @license AngularJS v1.5.5
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
- (function(window, angular, undefined) {'use strict';
6
+ (function(window, angular) {'use strict';
7
7
 
8
8
  /**
9
9
  * @ngdoc module
@@ -218,9 +218,9 @@ function $RouteProvider() {
218
218
 
219
219
  path = path
220
220
  .replace(/([().])/g, '\\$1')
221
- .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
222
- var optional = option === '?' ? option : null;
223
- var star = option === '*' ? option : null;
221
+ .replace(/(\/)?:(\w+)(\*\?|[\?\*])?/g, function(_, slash, key, option) {
222
+ var optional = (option === '?' || option === '*?') ? '?' : null;
223
+ var star = (option === '*' || option === '*?') ? '*' : null;
224
224
  keys.push({ name: key, optional: !!optional });
225
225
  slash = slash || '';
226
226
  return ''
@@ -749,11 +749,20 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
749
749
  * Requires the {@link ngRoute `ngRoute`} module to be installed.
750
750
  *
751
751
  * @animations
752
- * enter - animation is used to bring new content into the browser.
753
- * leave - animation is used to animate existing content away.
752
+ * | Animation | Occurs |
753
+ * |----------------------------------|-------------------------------------|
754
+ * | {@link ng.$animate#enter enter} | when the new element is inserted to the DOM |
755
+ * | {@link ng.$animate#leave leave} | when the old element is removed from to the DOM |
754
756
  *
755
757
  * The enter and leave animation occur concurrently.
756
758
  *
759
+ * @knownIssue If `ngView` is contained in an asynchronously loaded template (e.g. in another
760
+ * directive's templateUrl or in a template loaded using `ngInclude`), then you need to
761
+ * make sure that `$route` is instantiated in time to capture the initial
762
+ * `$locationChangeStart` event and load the appropriate view. One way to achieve this
763
+ * is to have it as a dependency in a `.run` block:
764
+ * `myModule.run(['$route', function() {}]);`
765
+ *
757
766
  * @scope
758
767
  * @priority 400
759
768
  * @param {string=} onload Expression to evaluate whenever the view updates.
@@ -1,9 +1,9 @@
1
1
  /**
2
- * @license AngularJS v1.5.0
2
+ * @license AngularJS v1.5.5
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
- (function(window, angular, undefined) {'use strict';
6
+ (function(window, angular) {'use strict';
7
7
 
8
8
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9
9
  * Any commits to this file should be reviewed with security in mind. *
@@ -349,7 +349,7 @@ function htmlParser(html, handler) {
349
349
  mXSSAttempts--;
350
350
 
351
351
  // strip custom-namespaced attributes on IE<=11
352
- if (document.documentMode <= 11) {
352
+ if (window.document.documentMode) {
353
353
  stripCustomNsAttrs(inertBodyElement);
354
354
  }
355
355
  html = inertBodyElement.innerHTML; //trigger mXSS
@@ -489,7 +489,7 @@ function htmlSanitizeWriter(buf, uriValidator) {
489
489
  * @param node Root element to process
490
490
  */
491
491
  function stripCustomNsAttrs(node) {
492
- if (node.nodeType === Node.ELEMENT_NODE) {
492
+ if (node.nodeType === window.Node.ELEMENT_NODE) {
493
493
  var attrs = node.attributes;
494
494
  for (var i = 0, l = attrs.length; i < l; i++) {
495
495
  var attrNode = attrs[i];
@@ -9190,11 +9190,11 @@ return jQuery;
9190
9190
  }));
9191
9191
 
9192
9192
  /**
9193
- * @license AngularJS v1.5.0
9193
+ * @license AngularJS v1.5.5
9194
9194
  * (c) 2010-2016 Google, Inc. http://angularjs.org
9195
9195
  * License: MIT
9196
9196
  */
9197
- (function(window, document){
9197
+ (function(window){
9198
9198
  var _jQuery = window.jQuery.noConflict(true);
9199
9199
 
9200
9200
  /**
@@ -9249,7 +9249,7 @@ function minErr(module, ErrorConstructor) {
9249
9249
  return match;
9250
9250
  });
9251
9251
 
9252
- message += '\nhttp://errors.angularjs.org/1.5.0/' +
9252
+ message += '\nhttp://errors.angularjs.org/1.5.5/' +
9253
9253
  (module ? module + '/' : '') + code;
9254
9254
 
9255
9255
  for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
@@ -9363,6 +9363,7 @@ function minErr(module, ErrorConstructor) {
9363
9363
  * @ngdoc module
9364
9364
  * @name ng
9365
9365
  * @module ng
9366
+ * @installation
9366
9367
  * @description
9367
9368
  *
9368
9369
  * # ng (core module)
@@ -9429,7 +9430,7 @@ var
9429
9430
  * documentMode is an IE-only property
9430
9431
  * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
9431
9432
  */
9432
- msie = document.documentMode;
9433
+ msie = window.document.documentMode;
9433
9434
 
9434
9435
 
9435
9436
  /**
@@ -9477,7 +9478,7 @@ function isArrayLike(obj) {
9477
9478
  *
9478
9479
  * Unlike ES262's
9479
9480
  * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
9480
- * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
9481
+ * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
9481
9482
  * return the value provided.
9482
9483
  *
9483
9484
  ```js
@@ -9718,7 +9719,7 @@ function identity($) {return $;}
9718
9719
  identity.$inject = [];
9719
9720
 
9720
9721
 
9721
- function valueFn(value) {return function() {return value;};}
9722
+ function valueFn(value) {return function valueRef() {return value;};}
9722
9723
 
9723
9724
  function hasCustomToString(obj) {
9724
9725
  return isFunction(obj.toString) && obj.toString !== toString;
@@ -10080,7 +10081,7 @@ function copy(source, destination) {
10080
10081
 
10081
10082
  function copyRecurse(source, destination) {
10082
10083
  var h = destination.$$hashKey;
10083
- var result, key;
10084
+ var key;
10084
10085
  if (isArray(source)) {
10085
10086
  for (var i = 0, ii = source.length; i < ii; i++) {
10086
10087
  destination.push(copyElement(source[i]));
@@ -10174,6 +10175,9 @@ function copy(source, destination) {
10174
10175
  var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
10175
10176
  re.lastIndex = source.lastIndex;
10176
10177
  return re;
10178
+
10179
+ case '[object Blob]':
10180
+ return new source.constructor([source], {type: source.type});
10177
10181
  }
10178
10182
 
10179
10183
  if (isFunction(source.cloneNode)) {
@@ -10236,6 +10240,41 @@ function shallowCopy(src, dst) {
10236
10240
  * @param {*} o1 Object or value to compare.
10237
10241
  * @param {*} o2 Object or value to compare.
10238
10242
  * @returns {boolean} True if arguments are equal.
10243
+ *
10244
+ * @example
10245
+ <example module="equalsExample" name="equalsExample">
10246
+ <file name="index.html">
10247
+ <div ng-controller="ExampleController">
10248
+ <form novalidate>
10249
+ <h3>User 1</h3>
10250
+ Name: <input type="text" ng-model="user1.name">
10251
+ Age: <input type="number" ng-model="user1.age">
10252
+
10253
+ <h3>User 2</h3>
10254
+ Name: <input type="text" ng-model="user2.name">
10255
+ Age: <input type="number" ng-model="user2.age">
10256
+
10257
+ <div>
10258
+ <br/>
10259
+ <input type="button" value="Compare" ng-click="compare()">
10260
+ </div>
10261
+ User 1: <pre>{{user1 | json}}</pre>
10262
+ User 2: <pre>{{user2 | json}}</pre>
10263
+ Equal: <pre>{{result}}</pre>
10264
+ </form>
10265
+ </div>
10266
+ </file>
10267
+ <file name="script.js">
10268
+ angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
10269
+ $scope.user1 = {};
10270
+ $scope.user2 = {};
10271
+ $scope.result;
10272
+ $scope.compare = function() {
10273
+ $scope.result = angular.equals($scope.user1, $scope.user2);
10274
+ };
10275
+ }]);
10276
+ </file>
10277
+ </example>
10239
10278
  */
10240
10279
  function equals(o1, o2) {
10241
10280
  if (o1 === o2) return true;
@@ -10282,8 +10321,8 @@ var csp = function() {
10282
10321
  if (!isDefined(csp.rules)) {
10283
10322
 
10284
10323
 
10285
- var ngCspElement = (document.querySelector('[ng-csp]') ||
10286
- document.querySelector('[data-ng-csp]'));
10324
+ var ngCspElement = (window.document.querySelector('[ng-csp]') ||
10325
+ window.document.querySelector('[data-ng-csp]'));
10287
10326
 
10288
10327
  if (ngCspElement) {
10289
10328
  var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
@@ -10358,7 +10397,7 @@ var jq = function() {
10358
10397
  var i, ii = ngAttrPrefixes.length, prefix, name;
10359
10398
  for (i = 0; i < ii; ++i) {
10360
10399
  prefix = ngAttrPrefixes[i];
10361
- if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
10400
+ if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
10362
10401
  name = el.getAttribute(prefix + 'jq');
10363
10402
  break;
10364
10403
  }
@@ -10423,7 +10462,7 @@ function toJsonReplacer(key, value) {
10423
10462
  val = undefined;
10424
10463
  } else if (isWindow(value)) {
10425
10464
  val = '$WINDOW';
10426
- } else if (value && document === value) {
10465
+ } else if (value && window.document === value) {
10427
10466
  val = '$DOCUMENT';
10428
10467
  } else if (isScope(value)) {
10429
10468
  val = '$SCOPE';
@@ -10663,10 +10702,17 @@ function getNgAttribute(element, ngAttr) {
10663
10702
  * designates the **root element** of the application and is typically placed near the root element
10664
10703
  * of the page - e.g. on the `<body>` or `<html>` tags.
10665
10704
  *
10666
- * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
10667
- * found in the document will be used to define the root element to auto-bootstrap as an
10668
- * application. To run multiple applications in an HTML document you must manually bootstrap them using
10669
- * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
10705
+ * There are a few things to keep in mind when using `ngApp`:
10706
+ * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
10707
+ * found in the document will be used to define the root element to auto-bootstrap as an
10708
+ * application. To run multiple applications in an HTML document you must manually bootstrap them using
10709
+ * {@link angular.bootstrap} instead.
10710
+ * - AngularJS applications cannot be nested within each other.
10711
+ * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
10712
+ * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
10713
+ * {@link ngRoute.ngView `ngView`}.
10714
+ * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
10715
+ * causing animations to stop working and making the injector inaccessible from outside the app.
10670
10716
  *
10671
10717
  * You can specify an **AngularJS module** to be used as the root module for the application. This
10672
10718
  * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
@@ -10806,16 +10852,25 @@ function angularInit(element, bootstrap) {
10806
10852
  * @description
10807
10853
  * Use this function to manually start up angular application.
10808
10854
  *
10809
- * See: {@link guide/bootstrap Bootstrap}
10810
- *
10811
- * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
10812
- * They must use {@link ng.directive:ngApp ngApp}.
10855
+ * For more information, see the {@link guide/bootstrap Bootstrap guide}.
10813
10856
  *
10814
10857
  * Angular will detect if it has been loaded into the browser more than once and only allow the
10815
10858
  * first loaded script to be bootstrapped and will report a warning to the browser console for
10816
10859
  * each of the subsequent scripts. This prevents strange results in applications, where otherwise
10817
10860
  * multiple instances of Angular try to work on the DOM.
10818
10861
  *
10862
+ * <div class="alert alert-warning">
10863
+ * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
10864
+ * They must use {@link ng.directive:ngApp ngApp}.
10865
+ * </div>
10866
+ *
10867
+ * <div class="alert alert-warning">
10868
+ * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
10869
+ * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
10870
+ * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
10871
+ * causing animations to stop working and making the injector inaccessible from outside the app.
10872
+ * </div>
10873
+ *
10819
10874
  * ```html
10820
10875
  * <!doctype html>
10821
10876
  * <html>
@@ -10859,11 +10914,11 @@ function bootstrap(element, modules, config) {
10859
10914
  element = jqLite(element);
10860
10915
 
10861
10916
  if (element.injector()) {
10862
- var tag = (element[0] === document) ? 'document' : startingTag(element);
10917
+ var tag = (element[0] === window.document) ? 'document' : startingTag(element);
10863
10918
  //Encode angle brackets to prevent input from being sanitized to empty string #8683
10864
10919
  throw ngMinErr(
10865
10920
  'btstrpd',
10866
- "App Already Bootstrapped with this Element '{0}'",
10921
+ "App already bootstrapped with this element '{0}'",
10867
10922
  tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
10868
10923
  }
10869
10924
 
@@ -11310,9 +11365,9 @@ function setupModuleLoader(window) {
11310
11365
  * @ngdoc method
11311
11366
  * @name angular.Module#decorator
11312
11367
  * @module ng
11313
- * @param {string} The name of the service to decorate.
11314
- * @param {Function} This function will be invoked when the service needs to be
11315
- * instantiated and should return the decorated service instance.
11368
+ * @param {string} name The name of the service to decorate.
11369
+ * @param {Function} decorFn This function will be invoked when the service needs to be
11370
+ * instantiated and should return the decorated service instance.
11316
11371
  * @description
11317
11372
  * See {@link auto.$provide#decorator $provide.decorator()}.
11318
11373
  */
@@ -11616,11 +11671,11 @@ function toDebugString(obj) {
11616
11671
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
11617
11672
  */
11618
11673
  var version = {
11619
- full: '1.5.0', // all of these placeholder strings will be replaced by grunt's
11674
+ full: '1.5.5', // all of these placeholder strings will be replaced by grunt's
11620
11675
  major: 1, // package task
11621
11676
  minor: 5,
11622
- dot: 0,
11623
- codeName: 'ennoblement-facilitation'
11677
+ dot: 5,
11678
+ codeName: 'material-conspiration'
11624
11679
  };
11625
11680
 
11626
11681
 
@@ -11877,6 +11932,9 @@ function publishExternalAPI(angular) {
11877
11932
  * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
11878
11933
  * parent element is reached.
11879
11934
  *
11935
+ * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
11936
+ * https://github.com/angular/angular.js/issues/14251 for more information.
11937
+ *
11880
11938
  * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
11881
11939
  * @returns {Object} jQuery object.
11882
11940
  */
@@ -12003,7 +12061,7 @@ function jqLiteBuildFragment(html, context) {
12003
12061
  }
12004
12062
 
12005
12063
  function jqLiteParseHTML(html, context) {
12006
- context = context || document;
12064
+ context = context || window.document;
12007
12065
  var parsed;
12008
12066
 
12009
12067
  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
@@ -12029,7 +12087,7 @@ function jqLiteWrapNode(node, wrapper) {
12029
12087
 
12030
12088
 
12031
12089
  // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
12032
- var jqLiteContains = Node.prototype.contains || function(arg) {
12090
+ var jqLiteContains = window.Node.prototype.contains || function(arg) {
12033
12091
  // jshint bitwise: false
12034
12092
  return !!(this.compareDocumentPosition(arg) & 16);
12035
12093
  // jshint bitwise: true
@@ -12301,8 +12359,8 @@ var JQLitePrototype = JQLite.prototype = {
12301
12359
  }
12302
12360
 
12303
12361
  // check if document is already loaded
12304
- if (document.readyState === 'complete') {
12305
- setTimeout(trigger);
12362
+ if (window.document.readyState === 'complete') {
12363
+ window.setTimeout(trigger);
12306
12364
  } else {
12307
12365
  this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
12308
12366
  // we can not use jqLite since we are not done loading and jQuery could be loaded later.
@@ -12992,6 +13050,7 @@ var $$HashMapProvider = [function() {
12992
13050
  /**
12993
13051
  * @ngdoc module
12994
13052
  * @name auto
13053
+ * @installation
12995
13054
  * @description
12996
13055
  *
12997
13056
  * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
@@ -13005,7 +13064,7 @@ var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
13005
13064
  var $injectorMinErr = minErr('$injector');
13006
13065
 
13007
13066
  function extractArgs(fn) {
13008
- var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
13067
+ var fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, ''),
13009
13068
  args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
13010
13069
  return args;
13011
13070
  }
@@ -13479,14 +13538,13 @@ function annotate(fn, strictDi, name) {
13479
13538
  * @description
13480
13539
  *
13481
13540
  * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
13482
- * number, an array, an object or a function. This is short for registering a service where its
13541
+ * number, an array, an object or a function. This is short for registering a service where its
13483
13542
  * provider's `$get` property is a factory function that takes no arguments and returns the **value
13484
- * service**.
13543
+ * service**. That also means it is not possible to inject other services into a value service.
13485
13544
  *
13486
13545
  * Value services are similar to constant services, except that they cannot be injected into a
13487
13546
  * module configuration function (see {@link angular.Module#config}) but they can be overridden by
13488
- * an Angular
13489
- * {@link auto.$provide#decorator decorator}.
13547
+ * an Angular {@link auto.$provide#decorator decorator}.
13490
13548
  *
13491
13549
  * @param {string} name The name of the instance.
13492
13550
  * @param {*} value The value.
@@ -13511,8 +13569,11 @@ function annotate(fn, strictDi, name) {
13511
13569
  * @name $provide#constant
13512
13570
  * @description
13513
13571
  *
13514
- * Register a **constant service**, such as a string, a number, an array, an object or a function,
13515
- * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
13572
+ * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
13573
+ * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
13574
+ * possible to inject other services into a constant.
13575
+ *
13576
+ * But unlike {@link auto.$provide#value value}, a constant can be
13516
13577
  * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
13517
13578
  * be overridden by an Angular {@link auto.$provide#decorator decorator}.
13518
13579
  *
@@ -14151,7 +14212,7 @@ function prepareAnimateOptions(options) {
14151
14212
  }
14152
14213
 
14153
14214
  var $$CoreAnimateJsProvider = function() {
14154
- this.$get = function() {};
14215
+ this.$get = noop;
14155
14216
  };
14156
14217
 
14157
14218
  // this is prefixed with Core since it conflicts with
@@ -14423,6 +14484,9 @@ var $AnimateProvider = ['$provide', function($provide) {
14423
14484
  * // remove all the animation event listeners listening for `enter`
14424
14485
  * $animate.off('enter');
14425
14486
  *
14487
+ * // remove listeners for all animation events from the container element
14488
+ * $animate.off(container);
14489
+ *
14426
14490
  * // remove all the animation event listeners listening for `enter` on the given element and its children
14427
14491
  * $animate.off('enter', container);
14428
14492
  *
@@ -14431,7 +14495,9 @@ var $AnimateProvider = ['$provide', function($provide) {
14431
14495
  * $animate.off('enter', container, callback);
14432
14496
  * ```
14433
14497
  *
14434
- * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
14498
+ * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
14499
+ * addClass, removeClass, etc...), or the container element. If it is the element, all other
14500
+ * arguments are ignored.
14435
14501
  * @param {DOMElement=} container the container element the event listener was placed on
14436
14502
  * @param {Function=} callback the callback function that was registered as the listener
14437
14503
  */
@@ -14971,7 +15037,6 @@ var $CoreAnimateCssProvider = function() {
14971
15037
  */
14972
15038
  function Browser(window, document, $log, $sniffer) {
14973
15039
  var self = this,
14974
- rawDocument = document[0],
14975
15040
  location = window.location,
14976
15041
  history = window.history,
14977
15042
  setTimeout = window.setTimeout,
@@ -15034,7 +15099,14 @@ function Browser(window, document, $log, $sniffer) {
15034
15099
  var cachedState, lastHistoryState,
15035
15100
  lastBrowserUrl = location.href,
15036
15101
  baseElement = document.find('base'),
15037
- pendingLocation = null;
15102
+ pendingLocation = null,
15103
+ getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
15104
+ try {
15105
+ return history.state;
15106
+ } catch (e) {
15107
+ // MSIE can reportedly throw when there is no state (UNCONFIRMED).
15108
+ }
15109
+ };
15038
15110
 
15039
15111
  cacheState();
15040
15112
  lastHistoryState = cachedState;
@@ -15142,14 +15214,6 @@ function Browser(window, document, $log, $sniffer) {
15142
15214
  fireUrlChange();
15143
15215
  }
15144
15216
 
15145
- function getCurrentState() {
15146
- try {
15147
- return history.state;
15148
- } catch (e) {
15149
- // MSIE can reportedly throw when there is no state (UNCONFIRMED).
15150
- }
15151
- }
15152
-
15153
15217
  // This variable should be used *only* inside the cacheState function.
15154
15218
  var lastCachedState = null;
15155
15219
  function cacheState() {
@@ -16001,9 +16065,23 @@ function $TemplateCacheProvider() {
16001
16065
  * `true` if the specified slot contains content (i.e. one or more DOM nodes).
16002
16066
  *
16003
16067
  * The controller can provide the following methods that act as life-cycle hooks:
16004
- * * `$onInit` - Called on each controller after all the controllers on an element have been constructed and
16068
+ * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
16005
16069
  * had their bindings initialized (and before the pre &amp; post linking functions for the directives on
16006
16070
  * this element). This is a good place to put initialization code for your controller.
16071
+ * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
16072
+ * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
16073
+ * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
16074
+ * component such as cloning the bound value to prevent accidental mutation of the outer value.
16075
+ * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
16076
+ * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
16077
+ * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
16078
+ * components will have their `$onDestroy()` hook called before child components.
16079
+ * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
16080
+ * function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
16081
+ * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
16082
+ * they are waiting for their template to load asynchronously and their own compilation and linking has been
16083
+ * suspended until that occurs.
16084
+ *
16007
16085
  *
16008
16086
  * #### `require`
16009
16087
  * Require another directive and inject its controller as the fourth argument to the linking function. The
@@ -16540,6 +16618,9 @@ function $TemplateCacheProvider() {
16540
16618
 
16541
16619
  var $compileMinErr = minErr('$compile');
16542
16620
 
16621
+ function UNINITIALIZED_VALUE() {}
16622
+ var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
16623
+
16543
16624
  /**
16544
16625
  * @ngdoc provider
16545
16626
  * @name $compileProvider
@@ -16559,13 +16640,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16559
16640
  // The assumption is that future DOM event attribute names will begin with
16560
16641
  // 'on' and be composed of only English letters.
16561
16642
  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
16643
+ var bindingCache = createMap();
16562
16644
 
16563
16645
  function parseIsolateBindings(scope, directiveName, isController) {
16564
16646
  var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
16565
16647
 
16566
- var bindings = {};
16648
+ var bindings = createMap();
16567
16649
 
16568
16650
  forEach(scope, function(definition, scopeName) {
16651
+ if (definition in bindingCache) {
16652
+ bindings[scopeName] = bindingCache[definition];
16653
+ return;
16654
+ }
16569
16655
  var match = definition.match(LOCAL_REGEXP);
16570
16656
 
16571
16657
  if (!match) {
@@ -16583,6 +16669,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16583
16669
  optional: match[3] === '?',
16584
16670
  attrName: match[4] || scopeName
16585
16671
  };
16672
+ if (match[4]) {
16673
+ bindingCache[definition] = bindings[scopeName];
16674
+ }
16586
16675
  });
16587
16676
 
16588
16677
  return bindings;
@@ -16628,11 +16717,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16628
16717
  function assertValidDirectiveName(name) {
16629
16718
  var letter = name.charAt(0);
16630
16719
  if (!letter || letter !== lowercase(letter)) {
16631
- throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
16720
+ throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
16632
16721
  }
16633
16722
  if (name !== name.trim()) {
16634
16723
  throw $compileMinErr('baddir',
16635
- "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
16724
+ "Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
16636
16725
  name);
16637
16726
  }
16638
16727
  }
@@ -16652,7 +16741,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16652
16741
  * {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
16653
16742
  * @returns {ng.$compileProvider} Self for chaining.
16654
16743
  */
16655
- this.directive = function registerDirective(name, directiveFactory) {
16744
+ this.directive = function registerDirective(name, directiveFactory) {
16656
16745
  assertNotHasOwnProperty(name, 'directive');
16657
16746
  if (isString(name)) {
16658
16747
  assertValidDirectiveName(name);
@@ -16675,11 +16764,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16675
16764
  directive.name = directive.name || name;
16676
16765
  directive.require = directive.require || (directive.controller && directive.name);
16677
16766
  directive.restrict = directive.restrict || 'EA';
16678
- var bindings = directive.$$bindings =
16679
- parseDirectiveBindings(directive, directive.name);
16680
- if (isObject(bindings.isolateScope)) {
16681
- directive.$$isolateBindings = bindings.isolateScope;
16682
- }
16683
16767
  directive.$$moduleName = directiveFactory.$$moduleName;
16684
16768
  directives.push(directive);
16685
16769
  } catch (e) {
@@ -16735,7 +16819,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16735
16819
  * See {@link ng.$compile#-bindtocontroller- `bindToController`}.
16736
16820
  * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
16737
16821
  * Disabled by default.
16738
- * - `$...` `{function()=}` additional annotations to provide to the directive factory function.
16822
+ * - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
16823
+ * this component's controller. The object keys specify the property names under which the required
16824
+ * controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
16825
+ * - `$...` – additional properties to attach to the directive factory function and the controller
16826
+ * constructor function. (This is used by the component router to annotate)
16739
16827
  *
16740
16828
  * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
16741
16829
  * @description
@@ -16767,7 +16855,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16767
16855
  *
16768
16856
  * myMod.component('myComp', {
16769
16857
  * templateUrl: 'views/my-comp.html',
16770
- * controller: 'MyCtrl as ctrl',
16858
+ * controller: 'MyCtrl',
16859
+ * controllerAs: 'ctrl',
16771
16860
  * bindings: {name: '@'}
16772
16861
  * });
16773
16862
  *
@@ -16792,7 +16881,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16792
16881
  }
16793
16882
 
16794
16883
  var template = (!options.template && !options.templateUrl ? '' : options.template);
16795
- return {
16884
+ var ddo = {
16796
16885
  controller: controller,
16797
16886
  controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
16798
16887
  template: makeInjectable(template),
@@ -16803,13 +16892,27 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16803
16892
  restrict: 'E',
16804
16893
  require: options.require
16805
16894
  };
16895
+
16896
+ // Copy annotations (starting with $) over to the DDO
16897
+ forEach(options, function(val, key) {
16898
+ if (key.charAt(0) === '$') ddo[key] = val;
16899
+ });
16900
+
16901
+ return ddo;
16806
16902
  }
16807
16903
 
16808
- // Copy any annotation properties (starting with $) over to the factory function
16904
+ // TODO(pete) remove the following `forEach` before we release 1.6.0
16905
+ // The component-router@0.2.0 looks for the annotations on the controller constructor
16906
+ // Nothing in Angular looks for annotations on the factory function but we can't remove
16907
+ // it from 1.5.x yet.
16908
+
16909
+ // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
16809
16910
  // These could be used by libraries such as the new component router
16810
16911
  forEach(options, function(val, key) {
16811
16912
  if (key.charAt(0) === '$') {
16812
16913
  factory[key] = val;
16914
+ // Don't try to copy over annotations to named controller
16915
+ if (isFunction(controller)) controller[key] = val;
16813
16916
  }
16814
16917
  });
16815
16918
 
@@ -16909,6 +17012,36 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16909
17012
  return debugInfoEnabled;
16910
17013
  };
16911
17014
 
17015
+
17016
+ var TTL = 10;
17017
+ /**
17018
+ * @ngdoc method
17019
+ * @name $compileProvider#onChangesTtl
17020
+ * @description
17021
+ *
17022
+ * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
17023
+ * assuming that the model is unstable.
17024
+ *
17025
+ * The current default is 10 iterations.
17026
+ *
17027
+ * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
17028
+ * in several iterations of calls to these hooks. However if an application needs more than the default 10
17029
+ * iterations to stabilize then you should investigate what is causing the model to continuously change during
17030
+ * the `$onChanges` hook execution.
17031
+ *
17032
+ * Increasing the TTL could have performance implications, so you should not change it without proper justification.
17033
+ *
17034
+ * @param {number} limit The number of `$onChanges` hook iterations.
17035
+ * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
17036
+ */
17037
+ this.onChangesTtl = function(value) {
17038
+ if (arguments.length) {
17039
+ TTL = value;
17040
+ return this;
17041
+ }
17042
+ return TTL;
17043
+ };
17044
+
16912
17045
  this.$get = [
16913
17046
  '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
16914
17047
  '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
@@ -16916,8 +17049,38 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16916
17049
  $controller, $rootScope, $sce, $animate, $$sanitizeUri) {
16917
17050
 
16918
17051
  var SIMPLE_ATTR_NAME = /^\w/;
16919
- var specialAttrHolder = document.createElement('div');
16920
- var Attributes = function(element, attributesToCopy) {
17052
+ var specialAttrHolder = window.document.createElement('div');
17053
+
17054
+
17055
+
17056
+ var onChangesTtl = TTL;
17057
+ // The onChanges hooks should all be run together in a single digest
17058
+ // When changes occur, the call to trigger their hooks will be added to this queue
17059
+ var onChangesQueue;
17060
+
17061
+ // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
17062
+ function flushOnChangesQueue() {
17063
+ try {
17064
+ if (!(--onChangesTtl)) {
17065
+ // We have hit the TTL limit so reset everything
17066
+ onChangesQueue = undefined;
17067
+ throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
17068
+ }
17069
+ // We must run this hook in an apply since the $$postDigest runs outside apply
17070
+ $rootScope.$apply(function() {
17071
+ for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
17072
+ onChangesQueue[i]();
17073
+ }
17074
+ // Reset the queue to trigger a new schedule next time there is a change
17075
+ onChangesQueue = undefined;
17076
+ });
17077
+ } finally {
17078
+ onChangesTtl++;
17079
+ }
17080
+ }
17081
+
17082
+
17083
+ function Attributes(element, attributesToCopy) {
16921
17084
  if (attributesToCopy) {
16922
17085
  var keys = Object.keys(attributesToCopy);
16923
17086
  var i, l, key;
@@ -16931,7 +17094,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16931
17094
  }
16932
17095
 
16933
17096
  this.$$element = element;
16934
- };
17097
+ }
16935
17098
 
16936
17099
  Attributes.prototype = {
16937
17100
  /**
@@ -17212,6 +17375,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
17212
17375
  safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
17213
17376
  } : noop;
17214
17377
 
17378
+ compile.$$createComment = function(directiveName, comment) {
17379
+ var content = '';
17380
+ if (debugInfoEnabled) {
17381
+ content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
17382
+ }
17383
+ return window.document.createComment(content);
17384
+ };
17385
+
17215
17386
  return compile;
17216
17387
 
17217
17388
  //================================
@@ -17232,7 +17403,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
17232
17403
  var domNode = $compileNodes[i];
17233
17404
 
17234
17405
  if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
17235
- jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
17406
+ jqLiteWrapNode(domNode, $compileNodes[i] = window.document.createElement('span'));
17236
17407
  }
17237
17408
  }
17238
17409
 
@@ -17425,8 +17596,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
17425
17596
  }
17426
17597
 
17427
17598
  function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
17428
-
17429
- var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
17599
+ function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
17430
17600
 
17431
17601
  if (!transcludedScope) {
17432
17602
  transcludedScope = scope.$new(false, containingScope);
@@ -17438,7 +17608,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
17438
17608
  transcludeControllers: controllers,
17439
17609
  futureParentElement: futureParentElement
17440
17610
  });
17441
- };
17611
+ }
17442
17612
 
17443
17613
  // We need to attach the transclusion slots onto the `boundTranscludeFn`
17444
17614
  // so that they are available inside the `controllersBoundTransclude` function
@@ -17603,7 +17773,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
17603
17773
  * @returns {Function}
17604
17774
  */
17605
17775
  function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
17606
- return function(scope, element, attrs, controllers, transcludeFn) {
17776
+ return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
17607
17777
  element = groupScan(element[0], attrStart, attrEnd);
17608
17778
  return linkFn(scope, element, attrs, controllers, transcludeFn);
17609
17779
  };
@@ -17621,23 +17791,21 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
17621
17791
  * @returns {Function}
17622
17792
  */
17623
17793
  function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
17624
- if (eager) {
17625
- return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
17626
- }
17794
+ var compiled;
17627
17795
 
17628
- var compiled;
17629
-
17630
- return function() {
17631
- if (!compiled) {
17632
- compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
17633
-
17634
- // Null out all of these references in order to make them eligible for garbage collection
17635
- // since this is a potentially long lived closure
17636
- $compileNodes = transcludeFn = previousCompileContext = null;
17637
- }
17796
+ if (eager) {
17797
+ return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
17798
+ }
17799
+ return function lazyCompilation() {
17800
+ if (!compiled) {
17801
+ compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
17638
17802
 
17639
- return compiled.apply(this, arguments);
17640
- };
17803
+ // Null out all of these references in order to make them eligible for garbage collection
17804
+ // since this is a potentially long lived closure
17805
+ $compileNodes = transcludeFn = previousCompileContext = null;
17806
+ }
17807
+ return compiled.apply(this, arguments);
17808
+ };
17641
17809
  }
17642
17810
 
17643
17811
  /**
@@ -17773,11 +17941,21 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
17773
17941
  terminalPriority = directive.priority;
17774
17942
  $template = $compileNode;
17775
17943
  $compileNode = templateAttrs.$$element =
17776
- jqLite(document.createComment(' ' + directiveName + ': ' +
17777
- templateAttrs[directiveName] + ' '));
17944
+ jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
17778
17945
  compileNode = $compileNode[0];
17779
17946
  replaceWith(jqCollection, sliceArgs($template), compileNode);
17780
17947
 
17948
+ // Support: Chrome < 50
17949
+ // https://github.com/angular/angular.js/issues/14041
17950
+
17951
+ // In the versions of V8 prior to Chrome 50, the document fragment that is created
17952
+ // in the `replaceWith` function is improperly garbage collected despite still
17953
+ // being referenced by the `parentNode` property of all of the child nodes. By adding
17954
+ // a reference to the fragment via a different property, we can avoid that incorrect
17955
+ // behavior.
17956
+ // TODO: remove this line after Chrome 50 has been released
17957
+ $template[0].$$parentNode = $template[0].parentNode;
17958
+
17781
17959
  childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
17782
17960
  replaceDirective && replaceDirective.name, {
17783
17961
  // Don't pass in:
@@ -17918,7 +18096,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
17918
18096
  replaceDirective = directive;
17919
18097
  }
17920
18098
 
18099
+ /* jshint -W021 */
17921
18100
  nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
18101
+ /* jshint +W021 */
17922
18102
  templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
17923
18103
  controllerDirectives: controllerDirectives,
17924
18104
  newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
@@ -17980,85 +18160,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
17980
18160
  }
17981
18161
  }
17982
18162
 
17983
-
17984
- function getControllers(directiveName, require, $element, elementControllers) {
17985
- var value;
17986
-
17987
- if (isString(require)) {
17988
- var match = require.match(REQUIRE_PREFIX_REGEXP);
17989
- var name = require.substring(match[0].length);
17990
- var inheritType = match[1] || match[3];
17991
- var optional = match[2] === '?';
17992
-
17993
- //If only parents then start at the parent element
17994
- if (inheritType === '^^') {
17995
- $element = $element.parent();
17996
- //Otherwise attempt getting the controller from elementControllers in case
17997
- //the element is transcluded (and has no data) and to avoid .data if possible
17998
- } else {
17999
- value = elementControllers && elementControllers[name];
18000
- value = value && value.instance;
18001
- }
18002
-
18003
- if (!value) {
18004
- var dataName = '$' + name + 'Controller';
18005
- value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
18006
- }
18007
-
18008
- if (!value && !optional) {
18009
- throw $compileMinErr('ctreq',
18010
- "Controller '{0}', required by directive '{1}', can't be found!",
18011
- name, directiveName);
18012
- }
18013
- } else if (isArray(require)) {
18014
- value = [];
18015
- for (var i = 0, ii = require.length; i < ii; i++) {
18016
- value[i] = getControllers(directiveName, require[i], $element, elementControllers);
18017
- }
18018
- } else if (isObject(require)) {
18019
- value = {};
18020
- forEach(require, function(controller, property) {
18021
- value[property] = getControllers(directiveName, controller, $element, elementControllers);
18022
- });
18023
- }
18024
-
18025
- return value || null;
18026
- }
18027
-
18028
- function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
18029
- var elementControllers = createMap();
18030
- for (var controllerKey in controllerDirectives) {
18031
- var directive = controllerDirectives[controllerKey];
18032
- var locals = {
18033
- $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
18034
- $element: $element,
18035
- $attrs: attrs,
18036
- $transclude: transcludeFn
18037
- };
18038
-
18039
- var controller = directive.controller;
18040
- if (controller == '@') {
18041
- controller = attrs[directive.name];
18042
- }
18043
-
18044
- var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
18045
-
18046
- // For directives with element transclusion the element is a comment,
18047
- // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
18048
- // clean up (http://bugs.jquery.com/ticket/8335).
18049
- // Instead, we save the controllers for the element in a local hash and attach to .data
18050
- // later, once we have the actual element.
18051
- elementControllers[directive.name] = controllerInstance;
18052
- if (!hasElementTranscludeDirective) {
18053
- $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
18054
- }
18055
- }
18056
- return elementControllers;
18057
- }
18058
-
18059
18163
  function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
18060
18164
  var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
18061
- attrs, removeScopeBindingWatches, removeControllerBindingWatches;
18165
+ attrs, scopeBindingInfo;
18062
18166
 
18063
18167
  if (compileNode === linkNode) {
18064
18168
  attrs = templateAttrs;
@@ -18087,7 +18191,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18087
18191
  }
18088
18192
 
18089
18193
  if (controllerDirectives) {
18090
- elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
18194
+ elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
18091
18195
  }
18092
18196
 
18093
18197
  if (newIsolateScopeDirective) {
@@ -18097,11 +18201,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18097
18201
  compile.$$addScopeClass($element, true);
18098
18202
  isolateScope.$$isolateBindings =
18099
18203
  newIsolateScopeDirective.$$isolateBindings;
18100
- removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
18204
+ scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
18101
18205
  isolateScope.$$isolateBindings,
18102
18206
  newIsolateScopeDirective);
18103
- if (removeScopeBindingWatches) {
18104
- isolateScope.$on('$destroy', removeScopeBindingWatches);
18207
+ if (scopeBindingInfo.removeWatches) {
18208
+ isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
18105
18209
  }
18106
18210
  }
18107
18211
 
@@ -18112,8 +18216,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18112
18216
  var bindings = controllerDirective.$$bindings.bindToController;
18113
18217
 
18114
18218
  if (controller.identifier && bindings) {
18115
- removeControllerBindingWatches =
18219
+ controller.bindingInfo =
18116
18220
  initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
18221
+ } else {
18222
+ controller.bindingInfo = {};
18117
18223
  }
18118
18224
 
18119
18225
  var controllerResult = controller();
@@ -18122,8 +18228,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18122
18228
  // from setupControllers
18123
18229
  controller.instance = controllerResult;
18124
18230
  $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
18125
- removeControllerBindingWatches && removeControllerBindingWatches();
18126
- removeControllerBindingWatches =
18231
+ controller.bindingInfo.removeWatches && controller.bindingInfo.removeWatches();
18232
+ controller.bindingInfo =
18127
18233
  initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
18128
18234
  }
18129
18235
  }
@@ -18136,10 +18242,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18136
18242
  }
18137
18243
  });
18138
18244
 
18139
- // Trigger the `$onInit` method on all controllers that have one
18245
+ // Handle the init and destroy lifecycle hooks on all controllers that have them
18140
18246
  forEach(elementControllers, function(controller) {
18141
- if (isFunction(controller.instance.$onInit)) {
18142
- controller.instance.$onInit();
18247
+ var controllerInstance = controller.instance;
18248
+ if (isFunction(controllerInstance.$onChanges)) {
18249
+ controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
18250
+ }
18251
+ if (isFunction(controllerInstance.$onInit)) {
18252
+ controllerInstance.$onInit();
18253
+ }
18254
+ if (isFunction(controllerInstance.$onDestroy)) {
18255
+ controllerScope.$on('$destroy', function callOnDestroyHook() {
18256
+ controllerInstance.$onDestroy();
18257
+ });
18143
18258
  }
18144
18259
  });
18145
18260
 
@@ -18176,6 +18291,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18176
18291
  );
18177
18292
  }
18178
18293
 
18294
+ // Trigger $postLink lifecycle hooks
18295
+ forEach(elementControllers, function(controller) {
18296
+ var controllerInstance = controller.instance;
18297
+ if (isFunction(controllerInstance.$postLink)) {
18298
+ controllerInstance.$postLink();
18299
+ }
18300
+ });
18301
+
18179
18302
  // This is the function that is injected as `$transclude`.
18180
18303
  // Note: all arguments are optional!
18181
18304
  function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
@@ -18215,6 +18338,78 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18215
18338
  }
18216
18339
  }
18217
18340
 
18341
+ function getControllers(directiveName, require, $element, elementControllers) {
18342
+ var value;
18343
+
18344
+ if (isString(require)) {
18345
+ var match = require.match(REQUIRE_PREFIX_REGEXP);
18346
+ var name = require.substring(match[0].length);
18347
+ var inheritType = match[1] || match[3];
18348
+ var optional = match[2] === '?';
18349
+
18350
+ //If only parents then start at the parent element
18351
+ if (inheritType === '^^') {
18352
+ $element = $element.parent();
18353
+ //Otherwise attempt getting the controller from elementControllers in case
18354
+ //the element is transcluded (and has no data) and to avoid .data if possible
18355
+ } else {
18356
+ value = elementControllers && elementControllers[name];
18357
+ value = value && value.instance;
18358
+ }
18359
+
18360
+ if (!value) {
18361
+ var dataName = '$' + name + 'Controller';
18362
+ value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
18363
+ }
18364
+
18365
+ if (!value && !optional) {
18366
+ throw $compileMinErr('ctreq',
18367
+ "Controller '{0}', required by directive '{1}', can't be found!",
18368
+ name, directiveName);
18369
+ }
18370
+ } else if (isArray(require)) {
18371
+ value = [];
18372
+ for (var i = 0, ii = require.length; i < ii; i++) {
18373
+ value[i] = getControllers(directiveName, require[i], $element, elementControllers);
18374
+ }
18375
+ } else if (isObject(require)) {
18376
+ value = {};
18377
+ forEach(require, function(controller, property) {
18378
+ value[property] = getControllers(directiveName, controller, $element, elementControllers);
18379
+ });
18380
+ }
18381
+
18382
+ return value || null;
18383
+ }
18384
+
18385
+ function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
18386
+ var elementControllers = createMap();
18387
+ for (var controllerKey in controllerDirectives) {
18388
+ var directive = controllerDirectives[controllerKey];
18389
+ var locals = {
18390
+ $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
18391
+ $element: $element,
18392
+ $attrs: attrs,
18393
+ $transclude: transcludeFn
18394
+ };
18395
+
18396
+ var controller = directive.controller;
18397
+ if (controller == '@') {
18398
+ controller = attrs[directive.name];
18399
+ }
18400
+
18401
+ var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
18402
+
18403
+ // For directives with element transclusion the element is a comment.
18404
+ // In this case .data will not attach any data.
18405
+ // Instead, we save the controllers for the element in a local hash and attach to .data
18406
+ // later, once we have the actual element.
18407
+ elementControllers[directive.name] = controllerInstance;
18408
+ $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
18409
+ }
18410
+ return elementControllers;
18411
+ }
18412
+
18218
18413
  // Depending upon the context in which a directive finds itself it might need to have a new isolated
18219
18414
  // or child scope created. For instance:
18220
18415
  // * if the directive has been pulled into a template because another directive with a higher priority
@@ -18255,6 +18450,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18255
18450
  if (startAttrName) {
18256
18451
  directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
18257
18452
  }
18453
+ if (!directive.$$bindings) {
18454
+ var bindings = directive.$$bindings =
18455
+ parseDirectiveBindings(directive, directive.name);
18456
+ if (isObject(bindings.isolateScope)) {
18457
+ directive.$$isolateBindings = bindings.isolateScope;
18458
+ }
18459
+ }
18258
18460
  tDirectives.push(directive);
18259
18461
  match = directive;
18260
18462
  }
@@ -18502,7 +18704,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18502
18704
  switch (type) {
18503
18705
  case 'svg':
18504
18706
  case 'math':
18505
- var wrapper = document.createElement('div');
18707
+ var wrapper = window.document.createElement('div');
18506
18708
  wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
18507
18709
  return wrapper.childNodes[0].childNodes;
18508
18710
  default:
@@ -18646,7 +18848,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18646
18848
  // - remove them from the DOM
18647
18849
  // - allow them to still be traversed with .nextSibling
18648
18850
  // - allow a single fragment.qSA to fetch all elements being removed
18649
- var fragment = document.createDocumentFragment();
18851
+ var fragment = window.document.createDocumentFragment();
18650
18852
  for (i = 0; i < removeCount; i++) {
18651
18853
  fragment.appendChild(elementsToRemove[i]);
18652
18854
  }
@@ -18692,7 +18894,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18692
18894
  // only occurs for isolate scopes and new scopes with controllerAs.
18693
18895
  function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
18694
18896
  var removeWatchCollection = [];
18695
- forEach(bindings, function(definition, scopeName) {
18897
+ var initialChanges = {};
18898
+ var changes;
18899
+ forEach(bindings, function initializeBinding(definition, scopeName) {
18696
18900
  var attrName = definition.attrName,
18697
18901
  optional = definition.optional,
18698
18902
  mode = definition.mode, // @, =, or &
@@ -18706,7 +18910,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18706
18910
  destination[scopeName] = attrs[attrName] = void 0;
18707
18911
  }
18708
18912
  attrs.$observe(attrName, function(value) {
18709
- if (isString(value)) {
18913
+ if (isString(value) || isBoolean(value)) {
18914
+ var oldValue = destination[scopeName];
18915
+ recordChanges(scopeName, value, oldValue);
18710
18916
  destination[scopeName] = value;
18711
18917
  }
18712
18918
  });
@@ -18721,6 +18927,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18721
18927
  // the value to boolean rather than a string, so we special case this situation
18722
18928
  destination[scopeName] = lastValue;
18723
18929
  }
18930
+ initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
18724
18931
  break;
18725
18932
 
18726
18933
  case '=':
@@ -18734,7 +18941,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18734
18941
  if (parentGet.literal) {
18735
18942
  compare = equals;
18736
18943
  } else {
18737
- compare = function(a, b) { return a === b || (a !== a && b !== b); };
18944
+ compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
18738
18945
  }
18739
18946
  parentSet = parentGet.assign || function() {
18740
18947
  // reset the change, or we will throw this exception on every $digest
@@ -18776,9 +18983,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18776
18983
  parentGet = $parse(attrs[attrName]);
18777
18984
 
18778
18985
  destination[scopeName] = parentGet(scope);
18986
+ initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
18779
18987
 
18780
- removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) {
18781
- destination[scopeName] = newParentValue;
18988
+ removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
18989
+ if (newValue === oldValue) {
18990
+ // If the new and old values are identical then this is the first time the watch has been triggered
18991
+ // So instead we use the current value on the destination as the old value
18992
+ oldValue = destination[scopeName];
18993
+ }
18994
+ recordChanges(scopeName, newValue, oldValue);
18995
+ destination[scopeName] = newValue;
18782
18996
  }, parentGet.literal);
18783
18997
 
18784
18998
  removeWatchCollection.push(removeWatch);
@@ -18798,15 +19012,52 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18798
19012
  }
18799
19013
  });
18800
19014
 
18801
- return removeWatchCollection.length && function removeWatches() {
18802
- for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
18803
- removeWatchCollection[i]();
19015
+ function recordChanges(key, currentValue, previousValue) {
19016
+ if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
19017
+ // If we have not already scheduled the top level onChangesQueue handler then do so now
19018
+ if (!onChangesQueue) {
19019
+ scope.$$postDigest(flushOnChangesQueue);
19020
+ onChangesQueue = [];
19021
+ }
19022
+ // If we have not already queued a trigger of onChanges for this controller then do so now
19023
+ if (!changes) {
19024
+ changes = {};
19025
+ onChangesQueue.push(triggerOnChangesHook);
19026
+ }
19027
+ // If the has been a change on this property already then we need to reuse the previous value
19028
+ if (changes[key]) {
19029
+ previousValue = changes[key].previousValue;
19030
+ }
19031
+ // Store this change
19032
+ changes[key] = new SimpleChange(previousValue, currentValue);
19033
+ }
19034
+ }
19035
+
19036
+ function triggerOnChangesHook() {
19037
+ destination.$onChanges(changes);
19038
+ // Now clear the changes so that we schedule onChanges when more changes arrive
19039
+ changes = undefined;
19040
+ }
19041
+
19042
+ return {
19043
+ initialChanges: initialChanges,
19044
+ removeWatches: removeWatchCollection.length && function removeWatches() {
19045
+ for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
19046
+ removeWatchCollection[i]();
19047
+ }
18804
19048
  }
18805
19049
  };
18806
19050
  }
18807
19051
  }];
18808
19052
  }
18809
19053
 
19054
+ function SimpleChange(previous, current) {
19055
+ this.previousValue = previous;
19056
+ this.currentValue = current;
19057
+ }
19058
+ SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
19059
+
19060
+
18810
19061
  var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
18811
19062
  /**
18812
19063
  * Converts all accepted directives format into proper directive name.
@@ -18936,6 +19187,15 @@ function $ControllerProvider() {
18936
19187
  var controllers = {},
18937
19188
  globals = false;
18938
19189
 
19190
+ /**
19191
+ * @ngdoc method
19192
+ * @name $controllerProvider#has
19193
+ * @param {string} name Controller name to check.
19194
+ */
19195
+ this.has = function(name) {
19196
+ return controllers.hasOwnProperty(name);
19197
+ };
19198
+
18939
19199
  /**
18940
19200
  * @ngdoc method
18941
19201
  * @name $controllerProvider#register
@@ -18992,7 +19252,7 @@ function $ControllerProvider() {
18992
19252
  * It's just a simple call to {@link auto.$injector $injector}, but extracted into
18993
19253
  * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
18994
19254
  */
18995
- return function(expression, locals, later, ident) {
19255
+ return function $controller(expression, locals, later, ident) {
18996
19256
  // PRIVATE API:
18997
19257
  // param `later` --- indicates that the controller's constructor is invoked at a later time.
18998
19258
  // If true, $controller will allocate the object with the correct
@@ -19043,7 +19303,7 @@ function $ControllerProvider() {
19043
19303
  }
19044
19304
 
19045
19305
  var instantiate;
19046
- return instantiate = extend(function() {
19306
+ return instantiate = extend(function $controllerInit() {
19047
19307
  var result = $injector.invoke(expression, instance, locals, constructor);
19048
19308
  if (result !== instance && (isObject(result) || isFunction(result))) {
19049
19309
  instance = result;
@@ -19229,7 +19489,7 @@ function $HttpParamSerializerProvider() {
19229
19489
  forEachSorted(params, function(value, key) {
19230
19490
  if (value === null || isUndefined(value)) return;
19231
19491
  if (isArray(value)) {
19232
- forEach(value, function(v, k) {
19492
+ forEach(value, function(v) {
19233
19493
  parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
19234
19494
  });
19235
19495
  } else {
@@ -19439,10 +19699,9 @@ function $HttpProvider() {
19439
19699
  *
19440
19700
  * Object containing default values for all {@link ng.$http $http} requests.
19441
19701
  *
19442
- * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
19443
- * that will provide the cache for all requests who set their `cache` property to `true`.
19444
- * If you set the `defaults.cache = false` then only requests that specify their own custom
19445
- * cache object will be cached. See {@link $http#caching $http Caching} for more information.
19702
+ * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
19703
+ * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
19704
+ * by default. See {@link $http#caching $http Caching} for more information.
19446
19705
  *
19447
19706
  * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
19448
19707
  * Defaults value is `'XSRF-TOKEN'`.
@@ -19733,6 +19992,15 @@ function $HttpProvider() {
19733
19992
  * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
19734
19993
  * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
19735
19994
  *
19995
+ * <div class="alert alert-warning">
19996
+ * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
19997
+ * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
19998
+ * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
19999
+ * function will be reflected on the scope and in any templates where the object is data-bound.
20000
+ * To prevent this, transform functions should have no side-effects.
20001
+ * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
20002
+ * </div>
20003
+ *
19736
20004
  * ### Default Transformations
19737
20005
  *
19738
20006
  * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
@@ -19790,26 +20058,35 @@ function $HttpProvider() {
19790
20058
  *
19791
20059
  * ## Caching
19792
20060
  *
19793
- * To enable caching, set the request configuration `cache` property to `true` (to use default
19794
- * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
19795
- * When the cache is enabled, `$http` stores the response from the server in the specified
19796
- * cache. The next time the same request is made, the response is served from the cache without
19797
- * sending a request to the server.
20061
+ * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
20062
+ * set the config.cache value or the default cache value to TRUE or to a cache object (created
20063
+ * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
20064
+ * precedence over the default cache value.
20065
+ *
20066
+ * In order to:
20067
+ * * cache all responses - set the default cache value to TRUE or to a cache object
20068
+ * * cache a specific response - set config.cache value to TRUE or to a cache object
20069
+ *
20070
+ * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
20071
+ * then the default `$cacheFactory($http)` object is used.
19798
20072
  *
19799
- * Note that even if the response is served from cache, delivery of the data is asynchronous in
19800
- * the same way that real requests are.
20073
+ * The default cache value can be set by updating the
20074
+ * {@link ng.$http#defaults `$http.defaults.cache`} property or the
20075
+ * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
19801
20076
  *
19802
- * If there are multiple GET requests for the same URL that should be cached using the same
19803
- * cache, but the cache is not populated yet, only one request to the server will be made and
19804
- * the remaining requests will be fulfilled using the response from the first request.
20077
+ * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
20078
+ * the relevant cache object. The next time the same request is made, the response is returned
20079
+ * from the cache without sending a request to the server.
19805
20080
  *
19806
- * You can change the default cache to a new object (built with
19807
- * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
19808
- * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
19809
- * their `cache` property to `true` will now use this cache object.
20081
+ * Take note that:
20082
+ *
20083
+ * * Only GET and JSONP requests are cached.
20084
+ * * The cache key is the request URL including search parameters; headers are not considered.
20085
+ * * Cached responses are returned asynchronously, in the same way as responses from the server.
20086
+ * * If multiple identical requests are made using the same cache, which is not yet populated,
20087
+ * one request will be made to the server and remaining requests will return the same response.
20088
+ * * A cache-control header on the response does not affect if or how responses are cached.
19810
20089
  *
19811
- * If you set the default cache to `false` then only requests that specify their own custom
19812
- * cache object will be cached.
19813
20090
  *
19814
20091
  * ## Interceptors
19815
20092
  *
@@ -19966,6 +20243,12 @@ function $HttpProvider() {
19966
20243
  * - **headers** – `{Object}` – Map of strings or functions which return strings representing
19967
20244
  * HTTP headers to send to the server. If the return value of a function is null, the
19968
20245
  * header will not be sent. Functions accept a config object as an argument.
20246
+ * - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
20247
+ * To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
20248
+ * The handler will be called in the context of a `$apply` block.
20249
+ * - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
20250
+ * object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
20251
+ * The handler will be called in the context of a `$apply` block.
19969
20252
  * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
19970
20253
  * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
19971
20254
  * - **transformRequest** –
@@ -19979,7 +20262,7 @@ function $HttpProvider() {
19979
20262
  * transform function or an array of such functions. The transform function takes the http
19980
20263
  * response body, headers and status and returns its transformed (typically deserialized) version.
19981
20264
  * See {@link ng.$http#overriding-the-default-transformations-per-request
19982
- * Overriding the Default TransformationjqLiks}
20265
+ * Overriding the Default Transformations}
19983
20266
  * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
19984
20267
  * prepare the string representation of request parameters (specified as an object).
19985
20268
  * If specified as string, it is interpreted as function registered with the
@@ -19987,10 +20270,9 @@ function $HttpProvider() {
19987
20270
  * by registering it as a {@link auto.$provide#service service}.
19988
20271
  * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
19989
20272
  * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
19990
- * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
19991
- * GET request, otherwise if a cache instance built with
19992
- * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
19993
- * caching.
20273
+ * - **cache** – `{boolean|Object}` – A boolean value or object created with
20274
+ * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
20275
+ * See {@link $http#caching $http Caching} for more information.
19994
20276
  * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
19995
20277
  * that should abort the request when resolved.
19996
20278
  * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
@@ -20425,11 +20707,35 @@ function $HttpProvider() {
20425
20707
  }
20426
20708
 
20427
20709
  $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
20428
- config.withCredentials, config.responseType);
20710
+ config.withCredentials, config.responseType,
20711
+ createApplyHandlers(config.eventHandlers),
20712
+ createApplyHandlers(config.uploadEventHandlers));
20429
20713
  }
20430
20714
 
20431
20715
  return promise;
20432
20716
 
20717
+ function createApplyHandlers(eventHandlers) {
20718
+ if (eventHandlers) {
20719
+ var applyHandlers = {};
20720
+ forEach(eventHandlers, function(eventHandler, key) {
20721
+ applyHandlers[key] = function(event) {
20722
+ if (useApplyAsync) {
20723
+ $rootScope.$applyAsync(callEventHandler);
20724
+ } else if ($rootScope.$$phase) {
20725
+ callEventHandler();
20726
+ } else {
20727
+ $rootScope.$apply(callEventHandler);
20728
+ }
20729
+
20730
+ function callEventHandler() {
20731
+ eventHandler(event);
20732
+ }
20733
+ };
20734
+ });
20735
+ return applyHandlers;
20736
+ }
20737
+ }
20738
+
20433
20739
 
20434
20740
  /**
20435
20741
  * Callback registered to $httpBackend():
@@ -20550,7 +20856,7 @@ function $HttpBackendProvider() {
20550
20856
 
20551
20857
  function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
20552
20858
  // TODO(vojta): fix the signature
20553
- return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
20859
+ return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
20554
20860
  $browser.$$incOutstandingRequestCount();
20555
20861
  url = url || $browser.url();
20556
20862
 
@@ -20610,6 +20916,14 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
20610
20916
  xhr.onerror = requestError;
20611
20917
  xhr.onabort = requestError;
20612
20918
 
20919
+ forEach(eventHandlers, function(value, key) {
20920
+ xhr.addEventListener(key, value);
20921
+ });
20922
+
20923
+ forEach(uploadEventHandlers, function(value, key) {
20924
+ xhr.upload.addEventListener(key, value);
20925
+ });
20926
+
20613
20927
  if (withCredentials) {
20614
20928
  xhr.withCredentials = true;
20615
20929
  }
@@ -22588,7 +22902,7 @@ Lexer.prototype = {
22588
22902
  this.readString(ch);
22589
22903
  } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
22590
22904
  this.readNumber();
22591
- } else if (this.isIdent(ch)) {
22905
+ } else if (this.isIdentifierStart(this.peekMultichar())) {
22592
22906
  this.readIdent();
22593
22907
  } else if (this.is(ch, '(){}[].,;:?')) {
22594
22908
  this.tokens.push({index: this.index, text: ch});
@@ -22632,12 +22946,49 @@ Lexer.prototype = {
22632
22946
  ch === '\n' || ch === '\v' || ch === '\u00A0');
22633
22947
  },
22634
22948
 
22635
- isIdent: function(ch) {
22949
+ isIdentifierStart: function(ch) {
22950
+ return this.options.isIdentifierStart ?
22951
+ this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
22952
+ this.isValidIdentifierStart(ch);
22953
+ },
22954
+
22955
+ isValidIdentifierStart: function(ch) {
22636
22956
  return ('a' <= ch && ch <= 'z' ||
22637
22957
  'A' <= ch && ch <= 'Z' ||
22638
22958
  '_' === ch || ch === '$');
22639
22959
  },
22640
22960
 
22961
+ isIdentifierContinue: function(ch) {
22962
+ return this.options.isIdentifierContinue ?
22963
+ this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
22964
+ this.isValidIdentifierContinue(ch);
22965
+ },
22966
+
22967
+ isValidIdentifierContinue: function(ch, cp) {
22968
+ return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
22969
+ },
22970
+
22971
+ codePointAt: function(ch) {
22972
+ if (ch.length === 1) return ch.charCodeAt(0);
22973
+ /*jshint bitwise: false*/
22974
+ return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
22975
+ /*jshint bitwise: true*/
22976
+ },
22977
+
22978
+ peekMultichar: function() {
22979
+ var ch = this.text.charAt(this.index);
22980
+ var peek = this.peek();
22981
+ if (!peek) {
22982
+ return ch;
22983
+ }
22984
+ var cp1 = ch.charCodeAt(0);
22985
+ var cp2 = peek.charCodeAt(0);
22986
+ if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
22987
+ return ch + peek;
22988
+ }
22989
+ return ch;
22990
+ },
22991
+
22641
22992
  isExpOperator: function(ch) {
22642
22993
  return (ch === '-' || ch === '+' || this.isNumber(ch));
22643
22994
  },
@@ -22686,12 +23037,13 @@ Lexer.prototype = {
22686
23037
 
22687
23038
  readIdent: function() {
22688
23039
  var start = this.index;
23040
+ this.index += this.peekMultichar().length;
22689
23041
  while (this.index < this.text.length) {
22690
- var ch = this.text.charAt(this.index);
22691
- if (!(this.isIdent(ch) || this.isNumber(ch))) {
23042
+ var ch = this.peekMultichar();
23043
+ if (!this.isIdentifierContinue(ch)) {
22692
23044
  break;
22693
23045
  }
22694
- this.index++;
23046
+ this.index += ch.length;
22695
23047
  }
22696
23048
  this.tokens.push({
22697
23049
  index: start,
@@ -22901,8 +23253,10 @@ AST.prototype = {
22901
23253
  primary = this.arrayDeclaration();
22902
23254
  } else if (this.expect('{')) {
22903
23255
  primary = this.object();
22904
- } else if (this.constants.hasOwnProperty(this.peek().text)) {
22905
- primary = copy(this.constants[this.consume().text]);
23256
+ } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
23257
+ primary = copy(this.selfReferential[this.consume().text]);
23258
+ } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
23259
+ primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
22906
23260
  } else if (this.peek().identifier) {
22907
23261
  primary = this.identifier();
22908
23262
  } else if (this.peek().constant) {
@@ -23054,15 +23408,7 @@ AST.prototype = {
23054
23408
  return false;
23055
23409
  },
23056
23410
 
23057
-
23058
- /* `undefined` is not a constant, it is an identifier,
23059
- * but using it as an identifier is not supported
23060
- */
23061
- constants: {
23062
- 'true': { type: AST.Literal, value: true },
23063
- 'false': { type: AST.Literal, value: false },
23064
- 'null': { type: AST.Literal, value: null },
23065
- 'undefined': {type: AST.Literal, value: undefined },
23411
+ selfReferential: {
23066
23412
  'this': {type: AST.ThisExpression },
23067
23413
  '$locals': {type: AST.LocalsExpression }
23068
23414
  }
@@ -23627,7 +23973,13 @@ ASTCompiler.prototype = {
23627
23973
  },
23628
23974
 
23629
23975
  nonComputedMember: function(left, right) {
23630
- return left + '.' + right;
23976
+ var SAFE_IDENTIFIER = /[$_a-zA-Z][$_a-zA-Z0-9]*/;
23977
+ var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
23978
+ if (SAFE_IDENTIFIER.test(right)) {
23979
+ return left + '.' + right;
23980
+ } else {
23981
+ return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
23982
+ }
23631
23983
  },
23632
23984
 
23633
23985
  computedMember: function(left, right) {
@@ -23752,7 +24104,7 @@ ASTInterpreter.prototype = {
23752
24104
  forEach(ast.body, function(expression) {
23753
24105
  expressions.push(self.recurse(expression.expression));
23754
24106
  });
23755
- var fn = ast.body.length === 0 ? function() {} :
24107
+ var fn = ast.body.length === 0 ? noop :
23756
24108
  ast.body.length === 1 ? expressions[0] :
23757
24109
  function(scope, locals) {
23758
24110
  var lastValue;
@@ -23893,7 +24245,7 @@ ASTInterpreter.prototype = {
23893
24245
  return context ? {value: locals} : locals;
23894
24246
  };
23895
24247
  case AST.NGValueParameter:
23896
- return function(scope, locals, assign, inputs) {
24248
+ return function(scope, locals, assign) {
23897
24249
  return context ? {value: assign} : assign;
23898
24250
  };
23899
24251
  }
@@ -24107,7 +24459,7 @@ var Parser = function(lexer, $filter, options) {
24107
24459
  this.lexer = lexer;
24108
24460
  this.$filter = $filter;
24109
24461
  this.options = options;
24110
- this.ast = new AST(this.lexer);
24462
+ this.ast = new AST(lexer, options);
24111
24463
  this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
24112
24464
  new ASTCompiler(this.ast, $filter);
24113
24465
  };
@@ -24184,16 +24536,73 @@ function getValueOf(value) {
24184
24536
  function $ParseProvider() {
24185
24537
  var cacheDefault = createMap();
24186
24538
  var cacheExpensive = createMap();
24539
+ var literals = {
24540
+ 'true': true,
24541
+ 'false': false,
24542
+ 'null': null,
24543
+ 'undefined': undefined
24544
+ };
24545
+ var identStart, identContinue;
24546
+
24547
+ /**
24548
+ * @ngdoc method
24549
+ * @name $parseProvider#addLiteral
24550
+ * @description
24551
+ *
24552
+ * Configure $parse service to add literal values that will be present as literal at expressions.
24553
+ *
24554
+ * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
24555
+ * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
24556
+ *
24557
+ **/
24558
+ this.addLiteral = function(literalName, literalValue) {
24559
+ literals[literalName] = literalValue;
24560
+ };
24561
+
24562
+ /**
24563
+ * @ngdoc method
24564
+ * @name $parseProvider#setIdentifierFns
24565
+ * @description
24566
+ *
24567
+ * Allows defining the set of characters that are allowed in Angular expressions. The function
24568
+ * `identifierStart` will get called to know if a given character is a valid character to be the
24569
+ * first character for an identifier. The function `identifierContinue` will get called to know if
24570
+ * a given character is a valid character to be a follow-up identifier character. The functions
24571
+ * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
24572
+ * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
24573
+ * mind that the `string` parameter can be two characters long depending on the character
24574
+ * representation. It is expected for the function to return `true` or `false`, whether that
24575
+ * character is allowed or not.
24576
+ *
24577
+ * Since this function will be called extensivelly, keep the implementation of these functions fast,
24578
+ * as the performance of these functions have a direct impact on the expressions parsing speed.
24579
+ *
24580
+ * @param {function=} identifierStart The function that will decide whether the given character is
24581
+ * a valid identifier start character.
24582
+ * @param {function=} identifierContinue The function that will decide whether the given character is
24583
+ * a valid identifier continue character.
24584
+ */
24585
+ this.setIdentifierFns = function(identifierStart, identifierContinue) {
24586
+ identStart = identifierStart;
24587
+ identContinue = identifierContinue;
24588
+ return this;
24589
+ };
24187
24590
 
24188
24591
  this.$get = ['$filter', function($filter) {
24189
24592
  var noUnsafeEval = csp().noUnsafeEval;
24190
24593
  var $parseOptions = {
24191
24594
  csp: noUnsafeEval,
24192
- expensiveChecks: false
24595
+ expensiveChecks: false,
24596
+ literals: copy(literals),
24597
+ isIdentifierStart: isFunction(identStart) && identStart,
24598
+ isIdentifierContinue: isFunction(identContinue) && identContinue
24193
24599
  },
24194
24600
  $parseOptionsExpensive = {
24195
24601
  csp: noUnsafeEval,
24196
- expensiveChecks: true
24602
+ expensiveChecks: true,
24603
+ literals: copy(literals),
24604
+ isIdentifierStart: isFunction(identStart) && identStart,
24605
+ isIdentifierContinue: isFunction(identContinue) && identContinue
24197
24606
  };
24198
24607
  var runningChecksEnabled = false;
24199
24608
 
@@ -24442,15 +24851,15 @@ function $ParseProvider() {
24442
24851
  * [Kris Kowal's Q](https://github.com/kriskowal/q).
24443
24852
  *
24444
24853
  * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
24445
- * implementations, and the other which resembles ES6 promises to some degree.
24854
+ * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
24446
24855
  *
24447
24856
  * # $q constructor
24448
24857
  *
24449
24858
  * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
24450
- * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
24859
+ * function as the first argument. This is similar to the native Promise implementation from ES6,
24451
24860
  * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
24452
24861
  *
24453
- * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
24862
+ * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
24454
24863
  * available yet.
24455
24864
  *
24456
24865
  * It can be used like so:
@@ -24615,7 +25024,7 @@ function $ParseProvider() {
24615
25024
  * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
24616
25025
  * all the important functionality needed for common async tasks.
24617
25026
  *
24618
- * # Testing
25027
+ * # Testing
24619
25028
  *
24620
25029
  * ```js
24621
25030
  * it('should simulate promise', inject(function($q, $rootScope) {
@@ -25803,7 +26212,7 @@ function $RootScopeProvider() {
25803
26212
  dirty, ttl = TTL,
25804
26213
  next, current, target = this,
25805
26214
  watchLog = [],
25806
- logIdx, logMsg, asyncTask;
26215
+ logIdx, asyncTask;
25807
26216
 
25808
26217
  beginPhase('$digest');
25809
26218
  // Check for changes to browser url that happened in sync before the call to $digest
@@ -27588,6 +27997,10 @@ function $SceProvider() {
27588
27997
  function $SnifferProvider() {
27589
27998
  this.$get = ['$window', '$document', function($window, $document) {
27590
27999
  var eventSupport = {},
28000
+ // Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
28001
+ // the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
28002
+ isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
28003
+ hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
27591
28004
  android =
27592
28005
  toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
27593
28006
  boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
@@ -27632,7 +28045,7 @@ function $SnifferProvider() {
27632
28045
  // so let's not use the history API also
27633
28046
  // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
27634
28047
  // jshint -W018
27635
- history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
28048
+ history: !!(hasHistoryPushState && !(android < 4) && !boxee),
27636
28049
  // jshint +W018
27637
28050
  hasEvent: function(event) {
27638
28051
  // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
@@ -27658,7 +28071,7 @@ function $SnifferProvider() {
27658
28071
  }];
27659
28072
  }
27660
28073
 
27661
- var $compileMinErr = minErr('$compile');
28074
+ var $templateRequestMinErr = minErr('$compile');
27662
28075
 
27663
28076
  /**
27664
28077
  * @ngdoc provider
@@ -27754,7 +28167,7 @@ function $TemplateRequestProvider() {
27754
28167
 
27755
28168
  function handleError(resp) {
27756
28169
  if (!ignoreRequestError) {
27757
- throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
28170
+ throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
27758
28171
  tpl, resp.status, resp.statusText);
27759
28172
  }
27760
28173
  return $q.reject(resp);
@@ -27984,7 +28397,7 @@ function $TimeoutProvider() {
27984
28397
  // doesn't know about mocked locations and resolves URLs to the real document - which is
27985
28398
  // exactly the behavior needed here. There is little value is mocking these out for this
27986
28399
  // service.
27987
- var urlParsingNode = document.createElement("a");
28400
+ var urlParsingNode = window.document.createElement("a");
27988
28401
  var originUrl = urlResolve(window.location.href);
27989
28402
 
27990
28403
 
@@ -28676,7 +29089,7 @@ function currencyFilter($locale) {
28676
29089
  * Formats a number as text.
28677
29090
  *
28678
29091
  * If the input is null or undefined, it will just be returned.
28679
- * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
29092
+ * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
28680
29093
  * If the input is not a number an empty string is returned.
28681
29094
  *
28682
29095
  *
@@ -28684,7 +29097,9 @@ function currencyFilter($locale) {
28684
29097
  * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
28685
29098
  * If this is not provided then the fraction size is computed from the current locale's number
28686
29099
  * formatting pattern. In the case of the default locale, it will be 3.
28687
- * @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
29100
+ * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
29101
+ * locale (e.g., in the en_US locale it will have "." as the decimal separator and
29102
+ * include "," group separators after each third digit).
28688
29103
  *
28689
29104
  * @example
28690
29105
  <example module="numberFilterExample">
@@ -28766,7 +29181,7 @@ function parse(numStr) {
28766
29181
  }
28767
29182
 
28768
29183
  // Count the number of leading zeros.
28769
- for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++);
29184
+ for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
28770
29185
 
28771
29186
  if (i == (zeros = numStr.length)) {
28772
29187
  // The digits are all zero.
@@ -28812,18 +29227,37 @@ function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
28812
29227
  var digit = digits[roundAt];
28813
29228
 
28814
29229
  if (roundAt > 0) {
28815
- digits.splice(roundAt);
29230
+ // Drop fractional digits beyond `roundAt`
29231
+ digits.splice(Math.max(parsedNumber.i, roundAt));
29232
+
29233
+ // Set non-fractional digits beyond `roundAt` to 0
29234
+ for (var j = roundAt; j < digits.length; j++) {
29235
+ digits[j] = 0;
29236
+ }
28816
29237
  } else {
28817
29238
  // We rounded to zero so reset the parsedNumber
29239
+ fractionLen = Math.max(0, fractionLen);
28818
29240
  parsedNumber.i = 1;
28819
- digits.length = roundAt = fractionSize + 1;
28820
- for (var i=0; i < roundAt; i++) digits[i] = 0;
29241
+ digits.length = Math.max(1, roundAt = fractionSize + 1);
29242
+ digits[0] = 0;
29243
+ for (var i = 1; i < roundAt; i++) digits[i] = 0;
28821
29244
  }
28822
29245
 
28823
- if (digit >= 5) digits[roundAt - 1]++;
29246
+ if (digit >= 5) {
29247
+ if (roundAt - 1 < 0) {
29248
+ for (var k = 0; k > roundAt; k--) {
29249
+ digits.unshift(0);
29250
+ parsedNumber.i++;
29251
+ }
29252
+ digits.unshift(1);
29253
+ parsedNumber.i++;
29254
+ } else {
29255
+ digits[roundAt - 1]++;
29256
+ }
29257
+ }
28824
29258
 
28825
29259
  // Pad out with zeros to get the required fraction length
28826
- for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
29260
+ for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
28827
29261
 
28828
29262
 
28829
29263
  // Do any carrying, e.g. a digit was rounded up to 10
@@ -28895,7 +29329,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
28895
29329
 
28896
29330
  // format the integer digits with grouping separators
28897
29331
  var groups = [];
28898
- if (digits.length > pattern.lgSize) {
29332
+ if (digits.length >= pattern.lgSize) {
28899
29333
  groups.unshift(digits.splice(-pattern.lgSize).join(''));
28900
29334
  }
28901
29335
  while (digits.length > pattern.gSize) {
@@ -28922,11 +29356,15 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
28922
29356
  }
28923
29357
  }
28924
29358
 
28925
- function padNumber(num, digits, trim) {
29359
+ function padNumber(num, digits, trim, negWrap) {
28926
29360
  var neg = '';
28927
- if (num < 0) {
28928
- neg = '-';
28929
- num = -num;
29361
+ if (num < 0 || (negWrap && num <= 0)) {
29362
+ if (negWrap) {
29363
+ num = -num + 1;
29364
+ } else {
29365
+ num = -num;
29366
+ neg = '-';
29367
+ }
28930
29368
  }
28931
29369
  num = '' + num;
28932
29370
  while (num.length < digits) num = ZERO_CHAR + num;
@@ -28937,7 +29375,7 @@ function padNumber(num, digits, trim) {
28937
29375
  }
28938
29376
 
28939
29377
 
28940
- function dateGetter(name, size, offset, trim) {
29378
+ function dateGetter(name, size, offset, trim, negWrap) {
28941
29379
  offset = offset || 0;
28942
29380
  return function(date) {
28943
29381
  var value = date['get' + name]();
@@ -28945,14 +29383,15 @@ function dateGetter(name, size, offset, trim) {
28945
29383
  value += offset;
28946
29384
  }
28947
29385
  if (value === 0 && offset == -12) value = 12;
28948
- return padNumber(value, size, trim);
29386
+ return padNumber(value, size, trim, negWrap);
28949
29387
  };
28950
29388
  }
28951
29389
 
28952
- function dateStrGetter(name, shortForm) {
29390
+ function dateStrGetter(name, shortForm, standAlone) {
28953
29391
  return function(date, formats) {
28954
29392
  var value = date['get' + name]();
28955
- var get = uppercase(shortForm ? ('SHORT' + name) : name);
29393
+ var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
29394
+ var get = uppercase(propPrefix + name);
28956
29395
 
28957
29396
  return formats[get][value];
28958
29397
  };
@@ -29007,13 +29446,14 @@ function longEraGetter(date, formats) {
29007
29446
  }
29008
29447
 
29009
29448
  var DATE_FORMATS = {
29010
- yyyy: dateGetter('FullYear', 4),
29011
- yy: dateGetter('FullYear', 2, 0, true),
29012
- y: dateGetter('FullYear', 1),
29449
+ yyyy: dateGetter('FullYear', 4, 0, false, true),
29450
+ yy: dateGetter('FullYear', 2, 0, true, true),
29451
+ y: dateGetter('FullYear', 1, 0, false, true),
29013
29452
  MMMM: dateStrGetter('Month'),
29014
29453
  MMM: dateStrGetter('Month', true),
29015
29454
  MM: dateGetter('Month', 2, 1),
29016
29455
  M: dateGetter('Month', 1, 1),
29456
+ LLLL: dateStrGetter('Month', false, true),
29017
29457
  dd: dateGetter('Date', 2),
29018
29458
  d: dateGetter('Date', 1),
29019
29459
  HH: dateGetter('Hours', 2),
@@ -29039,7 +29479,7 @@ var DATE_FORMATS = {
29039
29479
  GGGG: longEraGetter
29040
29480
  };
29041
29481
 
29042
- var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
29482
+ var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
29043
29483
  NUMBER_STRING = /^\-?\d+$/;
29044
29484
 
29045
29485
  /**
@@ -29059,6 +29499,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|
29059
29499
  * * `'MMM'`: Month in year (Jan-Dec)
29060
29500
  * * `'MM'`: Month in year, padded (01-12)
29061
29501
  * * `'M'`: Month in year (1-12)
29502
+ * * `'LLLL'`: Stand-alone month in year (January-December)
29062
29503
  * * `'dd'`: Day in month, padded (01-31)
29063
29504
  * * `'d'`: Day in month (1-31)
29064
29505
  * * `'EEEE'`: Day in Week,(Sunday-Saturday)
@@ -30740,8 +31181,8 @@ var ngFormDirective = formDirectiveFactory(true);
30740
31181
  ngModelMinErr: false,
30741
31182
  */
30742
31183
 
30743
- // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
30744
- var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
31184
+ // Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
31185
+ var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/;
30745
31186
  // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
30746
31187
  // Note: We are being more lenient, because browsers are too.
30747
31188
  // 1. Scheme
@@ -30757,12 +31198,18 @@ var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-
30757
31198
  var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
30758
31199
  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;
30759
31200
  var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
30760
- var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
30761
- var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
30762
- var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
30763
- var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
31201
+ var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
31202
+ var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
31203
+ var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
31204
+ var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
30764
31205
  var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
30765
31206
 
31207
+ var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
31208
+ var PARTIAL_VALIDATION_TYPES = createMap();
31209
+ forEach('date,datetime-local,month,time,week'.split(','), function(type) {
31210
+ PARTIAL_VALIDATION_TYPES[type] = true;
31211
+ });
31212
+
30766
31213
  var inputType = {
30767
31214
 
30768
31215
  /**
@@ -31118,7 +31565,7 @@ var inputType = {
31118
31565
  }]);
31119
31566
  </script>
31120
31567
  <form name="myForm" ng-controller="DateController as dateCtrl">
31121
- <label for="exampleInput">Pick a between 8am and 5pm:</label>
31568
+ <label for="exampleInput">Pick a time between 8am and 5pm:</label>
31122
31569
  <input type="time" id="exampleInput" name="input" ng-model="example.value"
31123
31570
  placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
31124
31571
  <div role="alert">
@@ -31839,7 +32286,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
31839
32286
  if (!$sniffer.android) {
31840
32287
  var composing = false;
31841
32288
 
31842
- element.on('compositionstart', function(data) {
32289
+ element.on('compositionstart', function() {
31843
32290
  composing = true;
31844
32291
  });
31845
32292
 
@@ -31849,6 +32296,8 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
31849
32296
  });
31850
32297
  }
31851
32298
 
32299
+ var timeout;
32300
+
31852
32301
  var listener = function(ev) {
31853
32302
  if (timeout) {
31854
32303
  $browser.defer.cancel(timeout);
@@ -31878,8 +32327,6 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
31878
32327
  if ($sniffer.hasEvent('input')) {
31879
32328
  element.on('input', listener);
31880
32329
  } else {
31881
- var timeout;
31882
-
31883
32330
  var deferListener = function(ev, input, origValue) {
31884
32331
  if (!timeout) {
31885
32332
  timeout = $browser.defer(function() {
@@ -31911,6 +32358,26 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
31911
32358
  // or form autocomplete on newer browser, we need "change" event to catch it
31912
32359
  element.on('change', listener);
31913
32360
 
32361
+ // Some native input types (date-family) have the ability to change validity without
32362
+ // firing any input/change events.
32363
+ // For these event types, when native validators are present and the browser supports the type,
32364
+ // check for validity changes on various DOM events.
32365
+ if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
32366
+ element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
32367
+ if (!timeout) {
32368
+ var validity = this[VALIDITY_STATE_PROPERTY];
32369
+ var origBadInput = validity.badInput;
32370
+ var origTypeMismatch = validity.typeMismatch;
32371
+ timeout = $browser.defer(function() {
32372
+ timeout = null;
32373
+ if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
32374
+ listener(ev);
32375
+ }
32376
+ });
32377
+ }
32378
+ });
32379
+ }
32380
+
31914
32381
  ctrl.$render = function() {
31915
32382
  // Workaround for Firefox validation #12102.
31916
32383
  var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
@@ -32861,7 +33328,11 @@ function classDirective(name, selector) {
32861
33328
  updateClasses(oldClasses, newClasses);
32862
33329
  }
32863
33330
  }
32864
- oldVal = shallowCopy(newVal);
33331
+ if (isArray(newVal)) {
33332
+ oldVal = newVal.map(function(v) { return shallowCopy(v); });
33333
+ } else {
33334
+ oldVal = shallowCopy(newVal);
33335
+ }
32865
33336
  }
32866
33337
  }
32867
33338
  };
@@ -32931,9 +33402,10 @@ function classDirective(name, selector) {
32931
33402
  * new classes added.
32932
33403
  *
32933
33404
  * @animations
32934
- * **add** - happens just before the class is applied to the elements
32935
- *
32936
- * **remove** - happens just before the class is removed from the element
33405
+ * | Animation | Occurs |
33406
+ * |----------------------------------|-------------------------------------|
33407
+ * | {@link ng.$animate#addClass addClass} | just before the class is applied to the element |
33408
+ * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
32937
33409
  *
32938
33410
  * @element ANY
32939
33411
  * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
@@ -34192,8 +34664,10 @@ forEach(
34192
34664
  * and `leave` effects.
34193
34665
  *
34194
34666
  * @animations
34195
- * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
34196
- * leave - happens just before the `ngIf` contents are removed from the DOM
34667
+ * | Animation | Occurs |
34668
+ * |----------------------------------|-------------------------------------|
34669
+ * | {@link ng.$animate#enter enter} | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
34670
+ * | {@link ng.$animate#leave leave} | just before the `ngIf` contents are removed from the DOM |
34197
34671
  *
34198
34672
  * @element ANY
34199
34673
  * @scope
@@ -34234,7 +34708,7 @@ forEach(
34234
34708
  </file>
34235
34709
  </example>
34236
34710
  */
34237
- var ngIfDirective = ['$animate', function($animate) {
34711
+ var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
34238
34712
  return {
34239
34713
  multiElement: true,
34240
34714
  transclude: 'element',
@@ -34250,7 +34724,7 @@ var ngIfDirective = ['$animate', function($animate) {
34250
34724
  if (!childScope) {
34251
34725
  $transclude(function(clone, newScope) {
34252
34726
  childScope = newScope;
34253
- clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
34727
+ clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
34254
34728
  // Note: We only need the first/last node of the cloned nodes.
34255
34729
  // However, we need to keep the reference to the jqlite wrapper as it might be changed later
34256
34730
  // by a directive with templateUrl when its template arrives.
@@ -34305,8 +34779,10 @@ var ngIfDirective = ['$animate', function($animate) {
34305
34779
  * access on some browsers.
34306
34780
  *
34307
34781
  * @animations
34308
- * enter - animation is used to bring new content into the browser.
34309
- * leave - animation is used to animate existing content away.
34782
+ * | Animation | Occurs |
34783
+ * |----------------------------------|-------------------------------------|
34784
+ * | {@link ng.$animate#enter enter} | when the expression changes, on the new include |
34785
+ * | {@link ng.$animate#leave leave} | when the expression changes, on the old include |
34310
34786
  *
34311
34787
  * The enter and leave animation occur concurrently.
34312
34788
  *
@@ -34572,7 +35048,7 @@ var ngIncludeFillContentDirective = ['$compile',
34572
35048
  // support innerHTML, so detect this here and try to generate the contents
34573
35049
  // specially.
34574
35050
  $element.empty();
34575
- $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
35051
+ $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
34576
35052
  function namespaceAdaptedClone(clone) {
34577
35053
  $element.append(clone);
34578
35054
  }, {futureParentElement: $element});
@@ -35047,9 +35523,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
35047
35523
  };
35048
35524
  ngModelSet = function($scope, newValue) {
35049
35525
  if (isFunction(parsedNgModel($scope))) {
35050
- invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
35526
+ invokeModelSetter($scope, {$$$p: newValue});
35051
35527
  } else {
35052
- parsedNgModelAssign($scope, ctrl.$modelValue);
35528
+ parsedNgModelAssign($scope, newValue);
35053
35529
  }
35054
35530
  };
35055
35531
  } else if (!parsedNgModel.assign) {
@@ -35074,7 +35550,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
35074
35550
  * the `$viewValue` are different from last time.
35075
35551
  *
35076
35552
  * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
35077
- * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
35553
+ * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
35078
35554
  * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
35079
35555
  * invoked if you only change a property on the objects.
35080
35556
  */
@@ -35426,7 +35902,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
35426
35902
  setValidity(name, undefined);
35427
35903
  validatorPromises.push(promise.then(function() {
35428
35904
  setValidity(name, true);
35429
- }, function(error) {
35905
+ }, function() {
35430
35906
  allValid = false;
35431
35907
  setValidity(name, false);
35432
35908
  }));
@@ -35900,7 +36376,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
35900
36376
  });
35901
36377
  }
35902
36378
 
35903
- element.on('blur', function(ev) {
36379
+ element.on('blur', function() {
35904
36380
  if (modelCtrl.$touched) return;
35905
36381
 
35906
36382
  if ($rootScope.$$phase) {
@@ -36486,7 +36962,7 @@ var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s
36486
36962
  // jshint maxlen: 100
36487
36963
 
36488
36964
 
36489
- var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36965
+ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
36490
36966
 
36491
36967
  function parseOptionsExpression(optionsExp, selectElement, scope) {
36492
36968
 
@@ -36583,8 +37059,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36583
37059
  var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
36584
37060
  var value = optionValues[key];
36585
37061
 
36586
- var locals = getLocals(optionValues[key], key);
36587
- var selectValue = getTrackByValueFn(optionValues[key], locals);
37062
+ var locals = getLocals(value, key);
37063
+ var selectValue = getTrackByValueFn(value, locals);
36588
37064
  watchedArray.push(selectValue);
36589
37065
 
36590
37066
  // Only need to watch the displayFn if there is a specific label expression
@@ -36647,8 +37123,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36647
37123
 
36648
37124
  // we can't just jqLite('<option>') since jqLite is not smart enough
36649
37125
  // to create it in <select> and IE barfs otherwise.
36650
- var optionTemplate = document.createElement('option'),
36651
- optGroupTemplate = document.createElement('optgroup');
37126
+ var optionTemplate = window.document.createElement('option'),
37127
+ optGroupTemplate = window.document.createElement('optgroup');
36652
37128
 
36653
37129
  function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
36654
37130
 
@@ -36673,7 +37149,10 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36673
37149
 
36674
37150
  var options;
36675
37151
  var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
36676
-
37152
+ // This stores the newly created options before they are appended to the select.
37153
+ // Since the contents are removed from the fragment when it is appended,
37154
+ // we only need to create it once.
37155
+ var listFragment = $document[0].createDocumentFragment();
36677
37156
 
36678
37157
  var renderEmptyOption = function() {
36679
37158
  if (!providedEmptyOption) {
@@ -36708,15 +37187,21 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36708
37187
  selectCtrl.writeValue = function writeNgOptionsValue(value) {
36709
37188
  var option = options.getOptionFromViewValue(value);
36710
37189
 
36711
- if (option && !option.disabled) {
37190
+ if (option) {
37191
+ // Don't update the option when it is already selected.
37192
+ // For example, the browser will select the first option by default. In that case,
37193
+ // most properties are set automatically - except the `selected` attribute, which we
37194
+ // set always
37195
+
36712
37196
  if (selectElement[0].value !== option.selectValue) {
36713
37197
  removeUnknownOption();
36714
37198
  removeEmptyOption();
36715
37199
 
36716
37200
  selectElement[0].value = option.selectValue;
36717
37201
  option.element.selected = true;
36718
- option.element.setAttribute('selected', 'selected');
36719
37202
  }
37203
+
37204
+ option.element.setAttribute('selected', 'selected');
36720
37205
  } else {
36721
37206
  if (value === null || providedEmptyOption) {
36722
37207
  removeUnknownOption();
@@ -36764,7 +37249,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36764
37249
  if (value) {
36765
37250
  value.forEach(function(item) {
36766
37251
  var option = options.getOptionFromViewValue(item);
36767
- if (option && !option.disabled) option.element.selected = true;
37252
+ if (option) option.element.selected = true;
36768
37253
  });
36769
37254
  }
36770
37255
  };
@@ -36816,6 +37301,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36816
37301
  emptyOption = jqLite(optionTemplate.cloneNode(false));
36817
37302
  }
36818
37303
 
37304
+ selectElement.empty();
37305
+
36819
37306
  // We need to do this here to ensure that the options object is defined
36820
37307
  // when we first hit it in writeNgOptionsValue
36821
37308
  updateOptions();
@@ -36825,6 +37312,12 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36825
37312
 
36826
37313
  // ------------------------------------------------------------------ //
36827
37314
 
37315
+ function addOptionElement(option, parent) {
37316
+ var optionElement = optionTemplate.cloneNode(false);
37317
+ parent.appendChild(optionElement);
37318
+ updateOptionElement(option, optionElement);
37319
+ }
37320
+
36828
37321
 
36829
37322
  function updateOptionElement(option, element) {
36830
37323
  option.element = element;
@@ -36841,133 +37334,66 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
36841
37334
  if (option.value !== element.value) element.value = option.selectValue;
36842
37335
  }
36843
37336
 
36844
- function addOrReuseElement(parent, current, type, templateElement) {
36845
- var element;
36846
- // Check whether we can reuse the next element
36847
- if (current && lowercase(current.nodeName) === type) {
36848
- // The next element is the right type so reuse it
36849
- element = current;
36850
- } else {
36851
- // The next element is not the right type so create a new one
36852
- element = templateElement.cloneNode(false);
36853
- if (!current) {
36854
- // There are no more elements so just append it to the select
36855
- parent.appendChild(element);
36856
- } else {
36857
- // The next element is not a group so insert the new one
36858
- parent.insertBefore(element, current);
36859
- }
36860
- }
36861
- return element;
36862
- }
36863
-
36864
-
36865
- function removeExcessElements(current) {
36866
- var next;
36867
- while (current) {
36868
- next = current.nextSibling;
36869
- jqLiteRemove(current);
36870
- current = next;
36871
- }
36872
- }
36873
-
36874
-
36875
- function skipEmptyAndUnknownOptions(current) {
36876
- var emptyOption_ = emptyOption && emptyOption[0];
36877
- var unknownOption_ = unknownOption && unknownOption[0];
37337
+ function updateOptions() {
37338
+ var previousValue = options && selectCtrl.readValue();
36878
37339
 
36879
- // We cannot rely on the extracted empty option being the same as the compiled empty option,
36880
- // because the compiled empty option might have been replaced by a comment because
36881
- // it had an "element" transclusion directive on it (such as ngIf)
36882
- if (emptyOption_ || unknownOption_) {
36883
- while (current &&
36884
- (current === emptyOption_ ||
36885
- current === unknownOption_ ||
36886
- current.nodeType === NODE_TYPE_COMMENT ||
36887
- (nodeName_(current) === 'option' && current.value === ''))) {
36888
- current = current.nextSibling;
37340
+ // We must remove all current options, but cannot simply set innerHTML = null
37341
+ // since the providedEmptyOption might have an ngIf on it that inserts comments which we
37342
+ // must preserve.
37343
+ // Instead, iterate over the current option elements and remove them or their optgroup
37344
+ // parents
37345
+ if (options) {
37346
+
37347
+ for (var i = options.items.length - 1; i >= 0; i--) {
37348
+ var option = options.items[i];
37349
+ if (option.group) {
37350
+ jqLiteRemove(option.element.parentNode);
37351
+ } else {
37352
+ jqLiteRemove(option.element);
37353
+ }
36889
37354
  }
36890
37355
  }
36891
- return current;
36892
- }
36893
-
36894
-
36895
- function updateOptions() {
36896
-
36897
- var previousValue = options && selectCtrl.readValue();
36898
37356
 
36899
37357
  options = ngOptions.getOptions();
36900
37358
 
36901
- var groupMap = {};
36902
- var currentElement = selectElement[0].firstChild;
37359
+ var groupElementMap = {};
36903
37360
 
36904
37361
  // Ensure that the empty option is always there if it was explicitly provided
36905
37362
  if (providedEmptyOption) {
36906
37363
  selectElement.prepend(emptyOption);
36907
37364
  }
36908
37365
 
36909
- currentElement = skipEmptyAndUnknownOptions(currentElement);
36910
-
36911
- options.items.forEach(function updateOption(option) {
36912
- var group;
37366
+ options.items.forEach(function addOption(option) {
36913
37367
  var groupElement;
36914
- var optionElement;
36915
37368
 
36916
37369
  if (isDefined(option.group)) {
36917
37370
 
36918
37371
  // This option is to live in a group
36919
37372
  // See if we have already created this group
36920
- group = groupMap[option.group];
37373
+ groupElement = groupElementMap[option.group];
36921
37374
 
36922
- if (!group) {
37375
+ if (!groupElement) {
36923
37376
 
36924
- // We have not already created this group
36925
- groupElement = addOrReuseElement(selectElement[0],
36926
- currentElement,
36927
- 'optgroup',
36928
- optGroupTemplate);
36929
- // Move to the next element
36930
- currentElement = groupElement.nextSibling;
37377
+ groupElement = optGroupTemplate.cloneNode(false);
37378
+ listFragment.appendChild(groupElement);
36931
37379
 
36932
37380
  // Update the label on the group element
36933
37381
  groupElement.label = option.group;
36934
37382
 
36935
37383
  // Store it for use later
36936
- group = groupMap[option.group] = {
36937
- groupElement: groupElement,
36938
- currentOptionElement: groupElement.firstChild
36939
- };
36940
-
37384
+ groupElementMap[option.group] = groupElement;
36941
37385
  }
36942
37386
 
36943
- // So now we have a group for this option we add the option to the group
36944
- optionElement = addOrReuseElement(group.groupElement,
36945
- group.currentOptionElement,
36946
- 'option',
36947
- optionTemplate);
36948
- updateOptionElement(option, optionElement);
36949
- // Move to the next element
36950
- group.currentOptionElement = optionElement.nextSibling;
37387
+ addOptionElement(option, groupElement);
36951
37388
 
36952
37389
  } else {
36953
37390
 
36954
37391
  // This option is not in a group
36955
- optionElement = addOrReuseElement(selectElement[0],
36956
- currentElement,
36957
- 'option',
36958
- optionTemplate);
36959
- updateOptionElement(option, optionElement);
36960
- // Move to the next element
36961
- currentElement = optionElement.nextSibling;
37392
+ addOptionElement(option, listFragment);
36962
37393
  }
36963
37394
  });
36964
37395
 
36965
-
36966
- // Now remove all excess options and group
36967
- Object.keys(groupMap).forEach(function(key) {
36968
- removeExcessElements(groupMap[key].currentOptionElement);
36969
- });
36970
- removeExcessElements(currentElement);
37396
+ selectElement[0].appendChild(listFragment);
36971
37397
 
36972
37398
  ngModelCtrl.$render();
36973
37399
 
@@ -37275,17 +37701,23 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
37275
37701
  * <div ng-repeat="(key, value) in myObj"> ... </div>
37276
37702
  * ```
37277
37703
  *
37278
- * You need to be aware that the JavaScript specification does not define the order of keys
37279
- * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
37280
- * used to sort the keys alphabetically.)
37704
+ * However, there are a limitations compared to array iteration:
37705
+ *
37706
+ * - The JavaScript specification does not define the order of keys
37707
+ * returned for an object, so Angular relies on the order returned by the browser
37708
+ * when running `for key in myObj`. Browsers generally follow the strategy of providing
37709
+ * keys in the order in which they were defined, although there are exceptions when keys are deleted
37710
+ * and reinstated. See the
37711
+ * [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
37712
+ *
37713
+ * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
37714
+ * it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
37281
37715
  *
37282
- * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
37283
- * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
37284
- * keys in the order in which they were defined, although there are exceptions when keys are deleted
37285
- * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
37716
+ * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
37717
+ * objects, and will throw if used with one.
37286
37718
  *
37287
- * If this is not desired, the recommended workaround is to convert your object into an array
37288
- * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
37719
+ * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
37720
+ * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
37289
37721
  * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
37290
37722
  * or implement a `$watch` on the object yourself.
37291
37723
  *
@@ -37403,11 +37835,11 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
37403
37835
  * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
37404
37836
  *
37405
37837
  * @animations
37406
- * **.enter** - when a new item is added to the list or when an item is revealed after a filter
37407
- *
37408
- * **.leave** - when an item is removed from the list or when an item is filtered out
37409
- *
37410
- * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
37838
+ * | Animation | Occurs |
37839
+ * |----------------------------------|-------------------------------------|
37840
+ * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
37841
+ * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
37842
+ * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
37411
37843
  *
37412
37844
  * See the example below for defining CSS animations with ngRepeat.
37413
37845
  *
@@ -37555,7 +37987,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
37555
37987
  </file>
37556
37988
  </example>
37557
37989
  */
37558
- var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
37990
+ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
37559
37991
  var NG_REMOVED = '$$NG_REMOVED';
37560
37992
  var ngRepeatMinErr = minErr('ngRepeat');
37561
37993
 
@@ -37590,7 +38022,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
37590
38022
  $$tlb: true,
37591
38023
  compile: function ngRepeatCompile($element, $attr) {
37592
38024
  var expression = $attr.ngRepeat;
37593
- var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
38025
+ var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
37594
38026
 
37595
38027
  var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
37596
38028
 
@@ -37754,7 +38186,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
37754
38186
 
37755
38187
  if (getBlockStart(block) != nextNode) {
37756
38188
  // existing item which got moved
37757
- $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
38189
+ $animate.move(getBlockNodes(block.clone), null, previousNode);
37758
38190
  }
37759
38191
  previousNode = getBlockEnd(block);
37760
38192
  updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
@@ -37766,8 +38198,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
37766
38198
  var endNode = ngRepeatEndComment.cloneNode(false);
37767
38199
  clone[clone.length++] = endNode;
37768
38200
 
37769
- // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
37770
- $animate.enter(clone, null, jqLite(previousNode));
38201
+ $animate.enter(clone, null, previousNode);
37771
38202
  previousNode = endNode;
37772
38203
  // Note: We only need the first/last node of the cloned nodes.
37773
38204
  // However, we need to keep the reference to the jqlite wrapper as it might be changed later
@@ -37870,12 +38301,14 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
37870
38301
  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
37871
38302
  * ```
37872
38303
  *
37873
- * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
38304
+ * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
37874
38305
  * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
37875
38306
  *
37876
38307
  * @animations
37877
- * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
37878
- * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
38308
+ * | Animation | Occurs |
38309
+ * |----------------------------------|-------------------------------------|
38310
+ * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
38311
+ * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
37879
38312
  *
37880
38313
  * @element ANY
37881
38314
  * @param {expression} ngShow If the {@link guide/expression expression} is truthy
@@ -38034,12 +38467,15 @@ var ngShowDirective = ['$animate', function($animate) {
38034
38467
  * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
38035
38468
  * ```
38036
38469
  *
38037
- * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
38470
+ * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
38038
38471
  * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
38039
38472
  *
38040
38473
  * @animations
38041
- * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
38042
- * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
38474
+ * | Animation | Occurs |
38475
+ * |----------------------------------|-------------------------------------|
38476
+ * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
38477
+ * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
38478
+ *
38043
38479
  *
38044
38480
  * @element ANY
38045
38481
  * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
@@ -38201,8 +38637,10 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
38201
38637
  * </div>
38202
38638
 
38203
38639
  * @animations
38204
- * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
38205
- * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
38640
+ * | Animation | Occurs |
38641
+ * |----------------------------------|-------------------------------------|
38642
+ * | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container |
38643
+ * | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM |
38206
38644
  *
38207
38645
  * @usage
38208
38646
  *
@@ -38301,7 +38739,7 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
38301
38739
  </file>
38302
38740
  </example>
38303
38741
  */
38304
- var ngSwitchDirective = ['$animate', function($animate) {
38742
+ var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
38305
38743
  return {
38306
38744
  require: 'ngSwitch',
38307
38745
 
@@ -38342,7 +38780,7 @@ var ngSwitchDirective = ['$animate', function($animate) {
38342
38780
  selectedTransclude.transclude(function(caseElement, selectedScope) {
38343
38781
  selectedScopes.push(selectedScope);
38344
38782
  var anchor = selectedTransclude.element;
38345
- caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
38783
+ caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
38346
38784
  var block = { clone: caseElement };
38347
38785
 
38348
38786
  selectedElements.push(block);
@@ -38636,7 +39074,7 @@ function chromeHack(optionElement) {
38636
39074
  * added `<option>` elements, perhaps by an `ngRepeat` directive.
38637
39075
  */
38638
39076
  var SelectController =
38639
- ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
39077
+ ['$element', '$scope', function($element, $scope) {
38640
39078
 
38641
39079
  var self = this,
38642
39080
  optionsMap = new HashMap();
@@ -38650,7 +39088,7 @@ var SelectController =
38650
39088
  //
38651
39089
  // We can't just jqLite('<option>') since jqLite is not smart enough
38652
39090
  // to create it in <select> and IE barfs otherwise.
38653
- self.unknownOption = jqLite(document.createElement('option'));
39091
+ self.unknownOption = jqLite(window.document.createElement('option'));
38654
39092
  self.renderUnknownOption = function(val) {
38655
39093
  var unknownVal = '? ' + hashKey(val) + ' ?';
38656
39094
  self.unknownOption.val(unknownVal);
@@ -39458,7 +39896,9 @@ var minlengthDirective = function() {
39458
39896
 
39459
39897
  if (window.angular.bootstrap) {
39460
39898
  //AngularJS is already loaded, so we can return here...
39461
- console.log('WARNING: Tried to load angular more than once.');
39899
+ if (window.console) {
39900
+ console.log('WARNING: Tried to load angular more than once.');
39901
+ }
39462
39902
  return;
39463
39903
  }
39464
39904
 
@@ -39714,7 +40154,7 @@ angular.scenario.matcher = angular.scenario.matcher || function(name, fn) {
39714
40154
  */
39715
40155
  angular.scenario.setUpAndRun = function(config) {
39716
40156
  var href = window.location.href;
39717
- var body = _jQuery(document.body);
40157
+ var body = _jQuery(window.document.body);
39718
40158
  var output = [];
39719
40159
  var objModel = new angular.scenario.ObjectModel($runner);
39720
40160
 
@@ -39997,7 +40437,7 @@ _jQuery.fn.bindings = function(windowJquery, bindExp) {
39997
40437
  evnt = new TransitionEvent(eventType, eventData);
39998
40438
  }
39999
40439
  catch (e) {
40000
- evnt = document.createEvent('TransitionEvent');
40440
+ evnt = window.document.createEvent('TransitionEvent');
40001
40441
  evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
40002
40442
  }
40003
40443
  }
@@ -40010,14 +40450,14 @@ _jQuery.fn.bindings = function(windowJquery, bindExp) {
40010
40450
  evnt = new AnimationEvent(eventType, eventData);
40011
40451
  }
40012
40452
  catch (e) {
40013
- evnt = document.createEvent('AnimationEvent');
40453
+ evnt = window.document.createEvent('AnimationEvent');
40014
40454
  evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
40015
40455
  }
40016
40456
  }
40017
40457
  } else if (/touch/.test(eventType) && supportsTouchEvents()) {
40018
40458
  evnt = createTouchEvent(element, eventType, x, y);
40019
40459
  } else {
40020
- evnt = document.createEvent('MouseEvents');
40460
+ evnt = window.document.createEvent('MouseEvents');
40021
40461
  x = x || 0;
40022
40462
  y = y || 0;
40023
40463
  evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
@@ -40056,12 +40496,12 @@ _jQuery.fn.bindings = function(windowJquery, bindExp) {
40056
40496
  if ('_cached' in supportsTouchEvents) {
40057
40497
  return supportsTouchEvents._cached;
40058
40498
  }
40059
- if (!document.createTouch || !document.createTouchList) {
40499
+ if (!window.document.createTouch || !window.document.createTouchList) {
40060
40500
  supportsTouchEvents._cached = false;
40061
40501
  return false;
40062
40502
  }
40063
40503
  try {
40064
- document.createEvent('TouchEvent');
40504
+ window.document.createEvent('TouchEvent');
40065
40505
  } catch (e) {
40066
40506
  supportsTouchEvents._cached = false;
40067
40507
  return false;
@@ -40071,12 +40511,12 @@ _jQuery.fn.bindings = function(windowJquery, bindExp) {
40071
40511
  }
40072
40512
 
40073
40513
  function createTouchEvent(element, eventType, x, y) {
40074
- var evnt = new Event(eventType);
40514
+ var evnt = new window.Event(eventType);
40075
40515
  x = x || 0;
40076
40516
  y = y || 0;
40077
40517
 
40078
- var touch = document.createTouch(window, element, Date.now(), x, y, x, y);
40079
- var touches = document.createTouchList(touch);
40518
+ var touch = window.document.createTouch(window, element, Date.now(), x, y, x, y);
40519
+ var touches = window.document.createTouchList(touch);
40080
40520
 
40081
40521
  evnt.touches = touches;
40082
40522
 
@@ -40148,7 +40588,7 @@ angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorF
40148
40588
  self.context.find('#test-frames').append('<iframe>');
40149
40589
  frame = self.getFrame_();
40150
40590
 
40151
- frame.load(function() {
40591
+ frame.on('load', function() {
40152
40592
  frame.off();
40153
40593
  try {
40154
40594
  var $window = self.getWindow_();
@@ -41267,7 +41707,7 @@ angular.scenario.dsl('binding', function() {
41267
41707
  */
41268
41708
  angular.scenario.dsl('input', function() {
41269
41709
  var chain = {};
41270
- var supportInputEvent = 'oninput' in document.createElement('div') && !(msie && msie <= 11);
41710
+ var supportInputEvent = 'oninput' in window.document.createElement('div') && !msie;
41271
41711
 
41272
41712
  chain.enter = function(value, event) {
41273
41713
  return this.addFutureAction("input '" + this.name + "' enter '" + value + "'",
@@ -41826,7 +42266,7 @@ bindJQuery();
41826
42266
  publishExternalAPI(angular);
41827
42267
 
41828
42268
  var $runner = new angular.scenario.Runner(window),
41829
- scripts = document.getElementsByTagName('script'),
42269
+ scripts = window.document.getElementsByTagName('script'),
41830
42270
  script = scripts[scripts.length - 1],
41831
42271
  config = {};
41832
42272
 
@@ -41838,11 +42278,11 @@ angular.forEach(script.attributes, function(attr) {
41838
42278
  });
41839
42279
 
41840
42280
  if (config.autotest) {
41841
- JQLite(document).ready(function() {
42281
+ JQLite(window.document).ready(function() {
41842
42282
  angular.scenario.setUpAndRun(config);
41843
42283
  });
41844
42284
  }
41845
- })(window, document);
42285
+ })(window);
41846
42286
 
41847
42287
 
41848
42288
  !window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";\n\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],\n.ng-cloak, .x-ng-cloak,\n.ng-hide:not(.ng-hide-animate) {\n display: none !important;\n}\n\nng\\:form {\n display: block;\n}\n\n.ng-animate-shim {\n visibility:hidden;\n}\n\n.ng-anchor {\n position:absolute;\n}\n</style>');