angularjs-rails 1.2.20 → 1.2.21

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.0-beta.15
2
+ * @license AngularJS v1.3.0-beta.17
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.0-beta.15
2
+ * @license AngularJS v1.3.0-beta.17
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -68,7 +68,7 @@ function minErr(module) {
68
68
  return match;
69
69
  });
70
70
 
71
- message = message + '\nhttp://errors.angularjs.org/1.3.0-beta.15/' +
71
+ message = message + '\nhttp://errors.angularjs.org/1.3.0-beta.17/' +
72
72
  (module ? module + '/' : '') + code;
73
73
  for (i = 2; i < arguments.length; i++) {
74
74
  message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
@@ -126,6 +126,7 @@ function minErr(module) {
126
126
  isFile: true,
127
127
  isBlob: true,
128
128
  isBoolean: true,
129
+ isPromiseLike: true,
129
130
  trim: true,
130
131
  isElement: true,
131
132
  makeMap: true,
@@ -648,6 +649,11 @@ function isBoolean(value) {
648
649
  }
649
650
 
650
651
 
652
+ function isPromiseLike(obj) {
653
+ return obj && isFunction(obj.then);
654
+ }
655
+
656
+
651
657
  var trim = (function() {
652
658
  // native trim is way faster: http://jsperf.com/angular-trim-test
653
659
  // but IE doesn't have it... :-(
@@ -814,7 +820,7 @@ function isLeafNode (node) {
814
820
  </div>
815
821
 
816
822
  <script>
817
- angular.module('copyExample')
823
+ angular.module('copyExample', [])
818
824
  .controller('ExampleController', ['$scope', function($scope) {
819
825
  $scope.master= {};
820
826
 
@@ -848,7 +854,8 @@ function copy(source, destination, stackSource, stackDest) {
848
854
  } else if (isDate(source)) {
849
855
  destination = new Date(source.getTime());
850
856
  } else if (isRegExp(source)) {
851
- destination = new RegExp(source.source);
857
+ destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
858
+ destination.lastIndex = source.lastIndex;
852
859
  } else if (isObject(source)) {
853
860
  var emptyObject = Object.create(Object.getPrototypeOf(source));
854
861
  destination = copy(source, emptyObject, stackSource, stackDest);
@@ -1000,12 +1007,25 @@ function equals(o1, o2) {
1000
1007
  return false;
1001
1008
  }
1002
1009
 
1010
+ var csp = function() {
1011
+ if (isDefined(csp.isActive_)) return csp.isActive_;
1012
+
1013
+ var active = !!(document.querySelector('[ng-csp]') ||
1014
+ document.querySelector('[data-ng-csp]'));
1015
+
1016
+ if (!active) {
1017
+ try {
1018
+ /* jshint -W031, -W054 */
1019
+ new Function('');
1020
+ /* jshint +W031, +W054 */
1021
+ } catch (e) {
1022
+ active = true;
1023
+ }
1024
+ }
1025
+
1026
+ return (csp.isActive_ = active);
1027
+ };
1003
1028
 
1004
- function csp() {
1005
- return (document.securityPolicy && document.securityPolicy.isActive) ||
1006
- (document.querySelector &&
1007
- !!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]')));
1008
- }
1009
1029
 
1010
1030
 
1011
1031
  function concat(array1, array2, index) {
@@ -1165,7 +1185,7 @@ function parseKeyValue(/**string*/keyValue) {
1165
1185
  var obj = {}, key_value, key;
1166
1186
  forEach((keyValue || "").split('&'), function(keyValue) {
1167
1187
  if ( keyValue ) {
1168
- key_value = keyValue.split('=');
1188
+ key_value = keyValue.replace(/\+/g,'%20').split('=');
1169
1189
  key = tryDecodeURIComponent(key_value[0]);
1170
1190
  if ( isDefined(key) ) {
1171
1191
  var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
@@ -1241,7 +1261,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
1241
1261
  var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1242
1262
 
1243
1263
  function getNgAttribute(element, ngAttr) {
1244
- var attr, i, ii = ngAttrPrefixes.length, j, jj;
1264
+ var attr, i, ii = ngAttrPrefixes.length;
1245
1265
  element = jqLite(element);
1246
1266
  for (i=0; i<ii; ++i) {
1247
1267
  attr = ngAttrPrefixes[i] + ngAttr;
@@ -1380,46 +1400,26 @@ function getNgAttribute(element, ngAttr) {
1380
1400
  </example>
1381
1401
  */
1382
1402
  function angularInit(element, bootstrap) {
1383
- var elements = [element],
1384
- appElement,
1403
+ var appElement,
1385
1404
  module,
1386
- config = {},
1387
- names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],
1388
- options = {
1389
- 'boolean': ['strict-di']
1390
- },
1391
- NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;
1405
+ config = {};
1392
1406
 
1393
- function append(element) {
1394
- element && elements.push(element);
1395
- }
1407
+ // The element `element` has priority over any other element
1408
+ forEach(ngAttrPrefixes, function(prefix) {
1409
+ var name = prefix + 'app';
1396
1410
 
1397
- forEach(names, function(name) {
1398
- names[name] = true;
1399
- append(document.getElementById(name));
1400
- name = name.replace(':', '\\:');
1401
- if (element.querySelectorAll) {
1402
- forEach(element.querySelectorAll('.' + name), append);
1403
- forEach(element.querySelectorAll('.' + name + '\\:'), append);
1404
- forEach(element.querySelectorAll('[' + name + ']'), append);
1411
+ if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1412
+ appElement = element;
1413
+ module = element.getAttribute(name);
1405
1414
  }
1406
1415
  });
1416
+ forEach(ngAttrPrefixes, function(prefix) {
1417
+ var name = prefix + 'app';
1418
+ var candidate;
1407
1419
 
1408
- forEach(elements, function(element) {
1409
- if (!appElement) {
1410
- var className = ' ' + element.className + ' ';
1411
- var match = NG_APP_CLASS_REGEXP.exec(className);
1412
- if (match) {
1413
- appElement = element;
1414
- module = (match[2] || '').replace(/\s+/g, ',');
1415
- } else {
1416
- forEach(element.attributes, function(attr) {
1417
- if (!appElement && names[attr.name]) {
1418
- appElement = element;
1419
- module = attr.value;
1420
- }
1421
- });
1422
- }
1420
+ if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1421
+ appElement = candidate;
1422
+ module = candidate.getAttribute(name);
1423
1423
  }
1424
1424
  });
1425
1425
  if (appElement) {
@@ -2067,11 +2067,11 @@ function setupModuleLoader(window) {
2067
2067
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2068
2068
  */
2069
2069
  var version = {
2070
- full: '1.3.0-beta.15', // all of these placeholder strings will be replaced by grunt's
2070
+ full: '1.3.0-beta.17', // all of these placeholder strings will be replaced by grunt's
2071
2071
  major: 1, // package task
2072
2072
  minor: 3,
2073
2073
  dot: 0,
2074
- codeName: 'unbelievable-advancement'
2074
+ codeName: 'turing-autocompletion'
2075
2075
  };
2076
2076
 
2077
2077
 
@@ -2614,25 +2614,22 @@ function jqLiteController(element, name) {
2614
2614
  }
2615
2615
 
2616
2616
  function jqLiteInheritedData(element, name, value) {
2617
- element = jqLite(element);
2618
-
2619
2617
  // if element is the document object work with the html element instead
2620
2618
  // this makes $(document).scope() possible
2621
- if(element[0].nodeType == 9) {
2622
- element = element.find('html');
2619
+ if(element.nodeType == 9) {
2620
+ element = element.documentElement;
2623
2621
  }
2624
2622
  var names = isArray(name) ? name : [name];
2625
2623
 
2626
- while (element.length) {
2627
- var node = element[0];
2624
+ while (element) {
2628
2625
  for (var i = 0, ii = names.length; i < ii; i++) {
2629
- if ((value = element.data(names[i])) !== undefined) return value;
2626
+ if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2630
2627
  }
2631
2628
 
2632
2629
  // If dealing with a document fragment node with a host element, and no parent, use the host
2633
2630
  // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2634
2631
  // to lookup parent controllers.
2635
- element = jqLite(node.parentNode || (node.nodeType === 11 && node.host));
2632
+ element = element.parentNode || (element.nodeType === 11 && element.host);
2636
2633
  }
2637
2634
  }
2638
2635
 
@@ -2715,18 +2712,25 @@ function getAliasedAttrName(element, name) {
2715
2712
  return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
2716
2713
  }
2717
2714
 
2715
+ forEach({
2716
+ data: jqLiteData,
2717
+ removeData: jqLiteRemoveData
2718
+ }, function(fn, name) {
2719
+ JQLite[name] = fn;
2720
+ });
2721
+
2718
2722
  forEach({
2719
2723
  data: jqLiteData,
2720
2724
  inheritedData: jqLiteInheritedData,
2721
2725
 
2722
2726
  scope: function(element) {
2723
2727
  // Can't use jqLiteData here directly so we stay compatible with jQuery!
2724
- return jqLite(element).data('$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2728
+ return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2725
2729
  },
2726
2730
 
2727
2731
  isolateScope: function(element) {
2728
2732
  // Can't use jqLiteData here directly so we stay compatible with jQuery!
2729
- return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
2733
+ return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
2730
2734
  },
2731
2735
 
2732
2736
  controller: jqLiteController,
@@ -3147,7 +3151,9 @@ forEach({
3147
3151
  clone: jqLiteClone,
3148
3152
 
3149
3153
  triggerHandler: function(element, eventName, eventData) {
3150
- var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
3154
+ // Copy event handlers in case event handlers array is modified during execution.
3155
+ var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName],
3156
+ eventFnsCopy = shallowCopy(eventFns || []);
3151
3157
 
3152
3158
  eventData = eventData || [];
3153
3159
 
@@ -3161,7 +3167,7 @@ forEach({
3161
3167
  stopPropagation: noop
3162
3168
  }];
3163
3169
 
3164
- forEach(eventFns, function(fn) {
3170
+ forEach(eventFnsCopy, function(fn) {
3165
3171
  fn.apply(element, event.concat(eventData));
3166
3172
  });
3167
3173
  }
@@ -3284,7 +3290,7 @@ HashMap.prototype = {
3284
3290
  *
3285
3291
  * // use the injector to kick off your application
3286
3292
  * // use the type inference to auto inject arguments, or use implicit injection
3287
- * $injector.invoke(function($rootScope, $compile, $document){
3293
+ * $injector.invoke(function($rootScope, $compile, $document) {
3288
3294
  * $compile($document)($rootScope);
3289
3295
  * $rootScope.$digest();
3290
3296
  * });
@@ -3358,8 +3364,8 @@ function annotate(fn, strictDi, name) {
3358
3364
  }
3359
3365
  fnText = fn.toString().replace(STRIP_COMMENTS, '');
3360
3366
  argDecl = fnText.match(FN_ARGS);
3361
- forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
3362
- arg.replace(FN_ARG, function(all, underscore, name){
3367
+ forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3368
+ arg.replace(FN_ARG, function(all, underscore, name) {
3363
3369
  $inject.push(name);
3364
3370
  });
3365
3371
  });
@@ -3394,7 +3400,7 @@ function annotate(fn, strictDi, name) {
3394
3400
  * ```js
3395
3401
  * var $injector = angular.injector();
3396
3402
  * expect($injector.get('$injector')).toBe($injector);
3397
- * expect($injector.invoke(function($injector){
3403
+ * expect($injector.invoke(function($injector) {
3398
3404
  * return $injector;
3399
3405
  * }).toBe($injector);
3400
3406
  * ```
@@ -4030,7 +4036,7 @@ function createInjector(modulesToLoad, strictDi) {
4030
4036
  }
4031
4037
  }
4032
4038
 
4033
- function invoke(fn, self, locals, serviceName){
4039
+ function invoke(fn, self, locals, serviceName) {
4034
4040
  if (typeof locals === 'string') {
4035
4041
  serviceName = locals;
4036
4042
  locals = null;
@@ -4729,16 +4735,15 @@ function Browser(window, document, $log, $sniffer) {
4729
4735
  * @returns {Object} Hash of all cookies (if called without any parameter)
4730
4736
  */
4731
4737
  self.cookies = function(name, value) {
4732
- /* global escape: false, unescape: false */
4733
4738
  var cookieLength, cookieArray, cookie, i, index;
4734
4739
 
4735
4740
  if (name) {
4736
4741
  if (value === undefined) {
4737
- rawDocument.cookie = escape(name) + "=;path=" + cookiePath +
4742
+ rawDocument.cookie = encodeURIComponent(name) + "=;path=" + cookiePath +
4738
4743
  ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
4739
4744
  } else {
4740
4745
  if (isString(value)) {
4741
- cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) +
4746
+ cookieLength = (rawDocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) +
4742
4747
  ';path=' + cookiePath).length + 1;
4743
4748
 
4744
4749
  // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
@@ -4762,12 +4767,12 @@ function Browser(window, document, $log, $sniffer) {
4762
4767
  cookie = cookieArray[i];
4763
4768
  index = cookie.indexOf('=');
4764
4769
  if (index > 0) { //ignore nameless cookies
4765
- name = unescape(cookie.substring(0, index));
4770
+ name = decodeURIComponent(cookie.substring(0, index));
4766
4771
  // the first value that is seen for a cookie is the most
4767
4772
  // specific one. values for the same cookie name that
4768
4773
  // follow are for less specific paths.
4769
4774
  if (lastCookies[name] === undefined) {
4770
- lastCookies[name] = unescape(cookie.substring(index + 1));
4775
+ lastCookies[name] = decodeURIComponent(cookie.substring(index + 1));
4771
4776
  }
4772
4777
  }
4773
4778
  }
@@ -5340,6 +5345,13 @@ function $TemplateCacheProvider() {
5340
5345
  * The directive definition object provides instructions to the {@link ng.$compile
5341
5346
  * compiler}. The attributes are:
5342
5347
  *
5348
+ * ### `multiElement`
5349
+ * When this property is set to true, the HTML compiler will collect DOM nodes between
5350
+ * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
5351
+ * together as the directive elements. It is recomended that this feature be used on directives
5352
+ * which are not strictly behavioural (such as {@link api/ng.directive:ngClick ngClick}), and which
5353
+ * do not manipulate or replace child nodes (such as {@link api/ng.directive:ngInclude ngInclude}).
5354
+ *
5343
5355
  * #### `priority`
5344
5356
  * When there are multiple directives defined on a single DOM element, sometimes it
5345
5357
  * is necessary to specify the order in which the directives are applied. The `priority` is used
@@ -5435,7 +5447,7 @@ function $TemplateCacheProvider() {
5435
5447
  * String of subset of `EACM` which restricts the directive to a specific directive
5436
5448
  * declaration style. If omitted, the default (attributes only) is used.
5437
5449
  *
5438
- * * `E` - Element name: `<my-directive></my-directive>`
5450
+ * * `E` - Element name (default): `<my-directive></my-directive>`
5439
5451
  * * `A` - Attribute (default): `<div my-directive="exp"></div>`
5440
5452
  * * `C` - Class: `<div class="my-directive: exp;"></div>`
5441
5453
  * * `M` - Comment: `<!-- directive: my-directive exp -->`
@@ -5455,14 +5467,16 @@ function $TemplateCacheProvider() {
5455
5467
  * If no `type` is specified, then the type is considered to be html.
5456
5468
  *
5457
5469
  * #### `template`
5458
- * replace the current element with the contents of the HTML. The replacement process
5459
- * migrates all of the attributes / classes from the old element to the new one. See the
5460
- * {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
5461
- * Directives Guide} for an example.
5470
+ * HTML markup that may:
5471
+ * * Replace the contents of the directive's element (defualt).
5472
+ * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
5473
+ * * Wrap the contents of the directive's element (if `transclude` is true).
5474
+ *
5475
+ * Value may be:
5462
5476
  *
5463
- * You can specify `template` as a string representing the template or as a function which takes
5464
- * two arguments `tElement` and `tAttrs` (described in the `compile` function api below) and
5465
- * returns a string value representing the template.
5477
+ * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
5478
+ * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
5479
+ * function api below) and returns a string value.
5466
5480
  *
5467
5481
  *
5468
5482
  * #### `templateUrl`
@@ -5477,11 +5491,14 @@ function $TemplateCacheProvider() {
5477
5491
  *
5478
5492
  *
5479
5493
  * #### `replace` ([*DEPRECATED*!], will be removed in next major release)
5480
- * specify where the template should be inserted. Defaults to `false`.
5494
+ * specify what the template should replace. Defaults to `false`.
5481
5495
  *
5482
- * * `true` - the template will replace the current element.
5483
- * * `false` - the template will replace the contents of the current element.
5496
+ * * `true` - the template will replace the directive's element.
5497
+ * * `false` - the template will replace the contents of the directive's element.
5484
5498
  *
5499
+ * The replacement process migrates all of the attributes / classes from the old element to the new
5500
+ * one. See the {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
5501
+ * Directives Guide} for an example.
5485
5502
  *
5486
5503
  * #### `transclude`
5487
5504
  * compile the content of the element and make it available to the directive.
@@ -5495,6 +5512,11 @@ function $TemplateCacheProvider() {
5495
5512
  * * `true` - transclude the content of the directive.
5496
5513
  * * `'element'` - transclude the whole element including any directives defined at lower priority.
5497
5514
  *
5515
+ * <div class="alert alert-warning">
5516
+ * **Note:** When testing an element transclude directive you must not place the directive at the root of the
5517
+ * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
5518
+ * Testing Transclusion Directives}.
5519
+ * </div>
5498
5520
  *
5499
5521
  * #### `compile`
5500
5522
  *
@@ -5793,7 +5815,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
5793
5815
  directive.index = index;
5794
5816
  directive.name = directive.name || name;
5795
5817
  directive.require = directive.require || (directive.controller && directive.name);
5796
- directive.restrict = directive.restrict || 'A';
5818
+ directive.restrict = directive.restrict || 'EA';
5797
5819
  directives.push(directive);
5798
5820
  } catch (e) {
5799
5821
  $exceptionHandler(e);
@@ -6143,7 +6165,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6143
6165
  : null;
6144
6166
 
6145
6167
  if (nodeLinkFn && nodeLinkFn.scope) {
6146
- safeAddClass(jqLite(nodeList[i]), 'ng-scope');
6168
+ safeAddClass(attrs.$$element, 'ng-scope');
6147
6169
  }
6148
6170
 
6149
6171
  childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
@@ -6165,7 +6187,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6165
6187
  return linkFnFound ? compositeLinkFn : null;
6166
6188
 
6167
6189
  function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
6168
- var nodeLinkFn, childLinkFn, node, $node, childScope, i, ii, n, childBoundTranscludeFn;
6190
+ var nodeLinkFn, childLinkFn, node, childScope, i, ii, n, childBoundTranscludeFn;
6169
6191
 
6170
6192
  // copy nodeList so that linking doesn't break due to live list updates.
6171
6193
  var nodeListLength = nodeList.length,
@@ -6178,12 +6200,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6178
6200
  node = stableNodeList[n];
6179
6201
  nodeLinkFn = linkFns[i++];
6180
6202
  childLinkFn = linkFns[i++];
6181
- $node = jqLite(node);
6182
6203
 
6183
6204
  if (nodeLinkFn) {
6184
6205
  if (nodeLinkFn.scope) {
6185
6206
  childScope = scope.$new();
6186
- $node.data('$scope', childScope);
6207
+ jqLite.data(node, '$scope', childScope);
6187
6208
  } else {
6188
6209
  childScope = scope;
6189
6210
  }
@@ -6271,10 +6292,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6271
6292
  }
6272
6293
 
6273
6294
  var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
6274
- if (ngAttrName === directiveNName + 'Start') {
6275
- attrStartName = name;
6276
- attrEndName = name.substr(0, name.length - 5) + 'end';
6277
- name = name.substr(0, name.length - 6);
6295
+ if (directiveIsMultiElement(directiveNName)) {
6296
+ if (ngAttrName === directiveNName + 'Start') {
6297
+ attrStartName = name;
6298
+ attrEndName = name.substr(0, name.length - 5) + 'end';
6299
+ name = name.substr(0, name.length - 6);
6300
+ }
6278
6301
  }
6279
6302
 
6280
6303
  nName = directiveNormalize(name.toLowerCase());
@@ -6483,12 +6506,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6483
6506
  if (directiveValue == 'element') {
6484
6507
  hasElementTranscludeDirective = true;
6485
6508
  terminalPriority = directive.priority;
6486
- $template = groupScan(compileNode, attrStart, attrEnd);
6509
+ $template = $compileNode;
6487
6510
  $compileNode = templateAttrs.$$element =
6488
6511
  jqLite(document.createComment(' ' + directiveName + ': ' +
6489
6512
  templateAttrs[directiveName] + ' '));
6490
6513
  compileNode = $compileNode[0];
6491
- replaceWith(jqCollection, jqLite(sliceArgs($template)), compileNode);
6514
+ replaceWith(jqCollection, sliceArgs($template), compileNode);
6492
6515
 
6493
6516
  childTranscludeFn = compile($template, transcludeFn, terminalPriority,
6494
6517
  replaceDirective && replaceDirective.name, {
@@ -6665,29 +6688,26 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6665
6688
  function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
6666
6689
  var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn;
6667
6690
 
6668
- if (compileNode === linkNode) {
6669
- attrs = templateAttrs;
6670
- } else {
6671
- attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
6672
- }
6691
+ attrs = (compileNode === linkNode)
6692
+ ? templateAttrs
6693
+ : shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
6673
6694
  $element = attrs.$$element;
6674
6695
 
6675
6696
  if (newIsolateScopeDirective) {
6676
6697
  var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
6677
- var $linkNode = jqLite(linkNode);
6678
6698
 
6679
6699
  isolateScope = scope.$new(true);
6680
6700
 
6681
6701
  if (templateDirective && (templateDirective === newIsolateScopeDirective ||
6682
6702
  templateDirective === newIsolateScopeDirective.$$originalDirective)) {
6683
- $linkNode.data('$isolateScope', isolateScope) ;
6703
+ $element.data('$isolateScope', isolateScope);
6684
6704
  } else {
6685
- $linkNode.data('$isolateScopeNoTemplate', isolateScope);
6705
+ $element.data('$isolateScopeNoTemplate', isolateScope);
6686
6706
  }
6687
6707
 
6688
6708
 
6689
6709
 
6690
- safeAddClass($linkNode, 'ng-isolate-scope');
6710
+ safeAddClass($element, 'ng-isolate-scope');
6691
6711
 
6692
6712
  forEach(newIsolateScopeDirective.scope, function(definition, scopeName) {
6693
6713
  var match = definition.match(LOCAL_REGEXP) || [],
@@ -6731,8 +6751,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6731
6751
  attrs[attrName], newIsolateScopeDirective.name);
6732
6752
  };
6733
6753
  lastValue = isolateScope[scopeName] = parentGet(scope);
6734
- isolateScope.$watch(function parentValueWatch() {
6735
- var parentValue = parentGet(scope);
6754
+ var unwatch = scope.$watch($parse(attrs[attrName], function parentValueWatch(parentValue) {
6736
6755
  if (!compare(parentValue, isolateScope[scopeName])) {
6737
6756
  // we are out of sync and need to copy
6738
6757
  if (!compare(parentValue, lastValue)) {
@@ -6743,9 +6762,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6743
6762
  parentSet(scope, parentValue = isolateScope[scopeName]);
6744
6763
  }
6745
6764
  }
6746
- parentValueWatch.$$unwatch = parentGet.$$unwatch;
6747
6765
  return lastValue = parentValue;
6748
- }, null, parentGet.literal);
6766
+ }), null, parentGet.literal);
6767
+ isolateScope.$on('$destroy', unwatch);
6749
6768
  break;
6750
6769
 
6751
6770
  case '&':
@@ -6890,6 +6909,27 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6890
6909
  }
6891
6910
 
6892
6911
 
6912
+ /**
6913
+ * looks up the directive and returns true if it is a multi-element directive,
6914
+ * and therefore requires DOM nodes between -start and -end markers to be grouped
6915
+ * together.
6916
+ *
6917
+ * @param {string} name name of the directive to look up.
6918
+ * @returns true if directive was registered as multi-element.
6919
+ */
6920
+ function directiveIsMultiElement(name) {
6921
+ if (hasDirectives.hasOwnProperty(name)) {
6922
+ for(var directive, directives = $injector.get(name + Suffix),
6923
+ i = 0, ii = directives.length; i<ii; i++) {
6924
+ directive = directives[i];
6925
+ if (directive.multiElement) {
6926
+ return true;
6927
+ }
6928
+ }
6929
+ }
6930
+ return false;
6931
+ }
6932
+
6893
6933
  /**
6894
6934
  * When the element is replaced with HTML template then the new attributes
6895
6935
  * on the template need to be merged with the existing attributes in the DOM.
@@ -7081,9 +7121,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7081
7121
  return function textInterpolateLinkFn(scope, node) {
7082
7122
  var parent = node.parent(),
7083
7123
  bindings = parent.data('$binding') || [];
7084
- // Need to interpolate again in case this is using one-time bindings in multiple clones
7085
- // of transcluded templates.
7086
- interpolateFn = $interpolate(text);
7087
7124
  bindings.push(interpolateFn);
7088
7125
  parent.data('$binding', bindings);
7089
7126
  if (!hasCompileParent) safeAddClass(parent, 'ng-binding');
@@ -7525,11 +7562,7 @@ function parseHeaders(headers) {
7525
7562
  val = trim(line.substr(i + 1));
7526
7563
 
7527
7564
  if (key) {
7528
- if (parsed[key]) {
7529
- parsed[key] += ', ' + val;
7530
- } else {
7531
- parsed[key] = val;
7532
- }
7565
+ parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
7533
7566
  }
7534
7567
  });
7535
7568
 
@@ -8294,7 +8327,7 @@ function $HttpProvider() {
8294
8327
  * Shortcut method to perform `JSONP` request.
8295
8328
  *
8296
8329
  * @param {string} url Relative or absolute URL specifying the destination of the request.
8297
- * Should contain `JSON_CALLBACK` string.
8330
+ * The name of the callback should be the string `JSON_CALLBACK`.
8298
8331
  * @param {Object=} config Optional configuration object
8299
8332
  * @returns {HttpPromise} Future object
8300
8333
  */
@@ -8328,8 +8361,7 @@ function $HttpProvider() {
8328
8361
 
8329
8362
  /**
8330
8363
  * @ngdoc method
8331
- * @name ng.$http#patch
8332
- * @methodOf ng.$http
8364
+ * @name $http#patch
8333
8365
  *
8334
8366
  * @description
8335
8367
  * Shortcut method to perform `PATCH` request.
@@ -8408,7 +8440,7 @@ function $HttpProvider() {
8408
8440
  if (cache) {
8409
8441
  cachedResp = cache.get(url);
8410
8442
  if (isDefined(cachedResp)) {
8411
- if (cachedResp.then) {
8443
+ if (isPromiseLike(cachedResp)) {
8412
8444
  // cached request has already been sent, but there is no response yet
8413
8445
  cachedResp.then(removePendingReq, removePendingReq);
8414
8446
  return cachedResp;
@@ -8490,27 +8522,29 @@ function $HttpProvider() {
8490
8522
 
8491
8523
 
8492
8524
  function buildUrl(url, params) {
8493
- if (!params) return url;
8494
- var parts = [];
8495
- forEachSorted(params, function(value, key) {
8496
- if (value === null || isUndefined(value)) return;
8497
- if (!isArray(value)) value = [value];
8498
-
8499
- forEach(value, function(v) {
8500
- if (isObject(v)) {
8501
- v = toJson(v);
8502
- }
8503
- parts.push(encodeUriQuery(key) + '=' +
8504
- encodeUriQuery(v));
8505
- });
8506
- });
8507
- if(parts.length > 0) {
8508
- url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
8525
+ if (!params) return url;
8526
+ var parts = [];
8527
+ forEachSorted(params, function(value, key) {
8528
+ if (value === null || isUndefined(value)) return;
8529
+ if (!isArray(value)) value = [value];
8530
+
8531
+ forEach(value, function(v) {
8532
+ if (isObject(v)) {
8533
+ if (isDate(v)){
8534
+ v = v.toISOString();
8535
+ } else if (isObject(v)) {
8536
+ v = toJson(v);
8537
+ }
8509
8538
  }
8510
- return url;
8511
- }
8512
-
8513
-
8539
+ parts.push(encodeUriQuery(key) + '=' +
8540
+ encodeUriQuery(v));
8541
+ });
8542
+ });
8543
+ if(parts.length > 0) {
8544
+ url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
8545
+ }
8546
+ return url;
8547
+ }
8514
8548
  }];
8515
8549
  }
8516
8550
 
@@ -8646,7 +8680,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
8646
8680
 
8647
8681
  if (timeout > 0) {
8648
8682
  var timeoutId = $browserDefer(timeoutRequest, timeout);
8649
- } else if (timeout && timeout.then) {
8683
+ } else if (isPromiseLike(timeout)) {
8650
8684
  timeout.then(timeoutRequest);
8651
8685
  }
8652
8686
 
@@ -8840,9 +8874,9 @@ function $InterpolateProvider() {
8840
8874
  *
8841
8875
  * // "allOrNothing" mode
8842
8876
  * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
8843
- * expect(exp(context, true)).toBeUndefined();
8877
+ * expect(exp(context)).toBeUndefined();
8844
8878
  * context.name = 'Angular';
8845
- * expect(exp(context, true)).toEqual('Hello Angular!');
8879
+ * expect(exp(context)).toEqual('Hello Angular!');
8846
8880
  * ```
8847
8881
  *
8848
8882
  * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
@@ -8910,8 +8944,7 @@ function $InterpolateProvider() {
8910
8944
  hasInterpolation = false,
8911
8945
  hasText = false,
8912
8946
  exp,
8913
- concat = [],
8914
- lastValuesCache = { values: {}, results: {}};
8947
+ concat = [];
8915
8948
 
8916
8949
  while(index < textLength) {
8917
8950
  if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
@@ -8920,7 +8953,7 @@ function $InterpolateProvider() {
8920
8953
  separators.push(text.substring(index, startIndex));
8921
8954
  exp = text.substring(startIndex + startSymbolLength, endIndex);
8922
8955
  expressions.push(exp);
8923
- parseFns.push($parse(exp));
8956
+ parseFns.push($parse(exp, parseStringifyInterceptor));
8924
8957
  index = endIndex + endSymbolLength;
8925
8958
  hasInterpolation = true;
8926
8959
  } else {
@@ -8961,6 +8994,7 @@ function $InterpolateProvider() {
8961
8994
 
8962
8995
  var compute = function(values) {
8963
8996
  for(var i = 0, ii = expressions.length; i < ii; i++) {
8997
+ if (allOrNothing && isUndefined(values[i])) return;
8964
8998
  concat[2*i] = separators[i];
8965
8999
  concat[(2*i)+1] = values[i];
8966
9000
  }
@@ -8969,13 +9003,9 @@ function $InterpolateProvider() {
8969
9003
  };
8970
9004
 
8971
9005
  var getValue = function (value) {
8972
- if (trustedContext) {
8973
- value = $sce.getTrusted(trustedContext, value);
8974
- } else {
8975
- value = $sce.valueOf(value);
8976
- }
8977
-
8978
- return value;
9006
+ return trustedContext ?
9007
+ $sce.getTrusted(trustedContext, value) :
9008
+ $sce.valueOf(value);
8979
9009
  };
8980
9010
 
8981
9011
  var stringify = function (value) {
@@ -8999,64 +9029,49 @@ function $InterpolateProvider() {
8999
9029
  };
9000
9030
 
9001
9031
  return extend(function interpolationFn(context) {
9002
- var scopeId = (context && context.$id) || 'notAScope';
9003
- var lastValues = lastValuesCache.values[scopeId];
9004
- var lastResult = lastValuesCache.results[scopeId];
9005
9032
  var i = 0;
9006
9033
  var ii = expressions.length;
9007
9034
  var values = new Array(ii);
9008
- var val;
9009
- var inputsChanged = lastResult === undefined ? true: false;
9010
-
9011
-
9012
- // if we haven't seen this context before, initialize the cache and try to setup
9013
- // a cleanup routine that purges the cache when the scope goes away.
9014
- if (!lastValues) {
9015
- lastValues = [];
9016
- inputsChanged = true;
9017
- if (context && context.$on) {
9018
- context.$on('$destroy', function() {
9019
- lastValuesCache.values[scopeId] = null;
9020
- lastValuesCache.results[scopeId] = null;
9021
- });
9022
- }
9023
- }
9024
-
9025
9035
 
9026
9036
  try {
9027
- interpolationFn.$$unwatch = true;
9028
9037
  for (; i < ii; i++) {
9029
- val = getValue(parseFns[i](context));
9030
- if (allOrNothing && isUndefined(val)) {
9031
- interpolationFn.$$unwatch = undefined;
9032
- return;
9033
- }
9034
- val = stringify(val);
9035
- if (val !== lastValues[i]) {
9036
- inputsChanged = true;
9037
- }
9038
- values[i] = val;
9039
- interpolationFn.$$unwatch = interpolationFn.$$unwatch && parseFns[i].$$unwatch;
9038
+ values[i] = parseFns[i](context);
9040
9039
  }
9041
9040
 
9042
- if (inputsChanged) {
9043
- lastValuesCache.values[scopeId] = values;
9044
- lastValuesCache.results[scopeId] = lastResult = compute(values);
9045
- }
9041
+ return compute(values);
9046
9042
  } catch(err) {
9047
9043
  var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
9048
9044
  err.toString());
9049
9045
  $exceptionHandler(newErr);
9050
9046
  }
9051
9047
 
9052
- return lastResult;
9053
9048
  }, {
9054
9049
  // all of these properties are undocumented for now
9055
9050
  exp: text, //just for compatibility with regular watchers created via $watch
9056
9051
  separators: separators,
9057
- expressions: expressions
9052
+ expressions: expressions,
9053
+ $$watchDelegate: function (scope, listener, objectEquality, deregisterNotifier) {
9054
+ var lastValue;
9055
+ return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
9056
+ var currValue = compute(values);
9057
+ if (isFunction(listener)) {
9058
+ listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
9059
+ }
9060
+ lastValue = currValue;
9061
+ }, objectEquality, deregisterNotifier);
9062
+ }
9058
9063
  });
9059
9064
  }
9065
+
9066
+ function parseStringifyInterceptor(value) {
9067
+ try {
9068
+ return stringify(getValue(value));
9069
+ } catch(err) {
9070
+ var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
9071
+ err.toString());
9072
+ $exceptionHandler(newErr);
9073
+ }
9074
+ }
9060
9075
  }
9061
9076
 
9062
9077
 
@@ -9177,7 +9192,7 @@ function $IntervalProvider() {
9177
9192
  * // Make sure that the interval nis destroyed too
9178
9193
  * $scope.stopFight();
9179
9194
  * });
9180
- * })
9195
+ * }])
9181
9196
  * // Register the 'myCurrentTime' directive factory method.
9182
9197
  * // We inject $interval and dateFilter service since the factory method is DI.
9183
9198
  * .directive('myCurrentTime', ['$interval', 'dateFilter',
@@ -9206,7 +9221,7 @@ function $IntervalProvider() {
9206
9221
  * $interval.cancel(stopTime);
9207
9222
  * });
9208
9223
  * }
9209
- * });
9224
+ * }]);
9210
9225
  * </script>
9211
9226
  *
9212
9227
  * <div>
@@ -10622,11 +10637,7 @@ Lexer.prototype = {
10622
10637
  string += String.fromCharCode(parseInt(hex, 16));
10623
10638
  } else {
10624
10639
  var rep = ESCAPE[ch];
10625
- if (rep) {
10626
- string += rep;
10627
- } else {
10628
- string += ch;
10629
- }
10640
+ string = string + (rep || ch);
10630
10641
  }
10631
10642
  escape = false;
10632
10643
  } else if (ch === '\\') {
@@ -11187,7 +11198,7 @@ function getterFn(path, options, fullExp) {
11187
11198
  // we simply dereference 's' on any .dot notation
11188
11199
  ? 's'
11189
11200
  // but if we are first then we check locals first, and if so read it first
11190
- : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n';
11201
+ : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '.' + key + ';\n';
11191
11202
  });
11192
11203
  code += 'return s;';
11193
11204
 
@@ -11269,69 +11280,89 @@ function $ParseProvider() {
11269
11280
  this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
11270
11281
  $parseOptions.csp = $sniffer.csp;
11271
11282
 
11272
- return function(exp) {
11273
- var parsedExpression,
11274
- oneTime;
11283
+ return function(exp, interceptorFn) {
11284
+ var parsedExpression, oneTime,
11285
+ cacheKey = (exp = trim(exp));
11275
11286
 
11276
11287
  switch (typeof exp) {
11277
11288
  case 'string':
11289
+ if (cache.hasOwnProperty(cacheKey)) {
11290
+ parsedExpression = cache[cacheKey];
11291
+ } else {
11292
+ if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
11293
+ oneTime = true;
11294
+ exp = exp.substring(2);
11295
+ }
11278
11296
 
11279
- exp = trim(exp);
11280
-
11281
- if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
11282
- oneTime = true;
11283
- exp = exp.substring(2);
11284
- }
11285
-
11286
- if (cache.hasOwnProperty(exp)) {
11287
- return oneTime ? oneTimeWrapper(cache[exp]) : cache[exp];
11288
- }
11297
+ var lexer = new Lexer($parseOptions);
11298
+ var parser = new Parser(lexer, $filter, $parseOptions);
11299
+ parsedExpression = parser.parse(exp);
11289
11300
 
11290
- var lexer = new Lexer($parseOptions);
11291
- var parser = new Parser(lexer, $filter, $parseOptions);
11292
- parsedExpression = parser.parse(exp);
11301
+ if (parsedExpression.constant) parsedExpression.$$watchDelegate = constantWatch;
11302
+ else if (oneTime) parsedExpression.$$watchDelegate = oneTimeWatch;
11293
11303
 
11294
- if (exp !== 'hasOwnProperty') {
11295
- // Only cache the value if it's not going to mess up the cache object
11296
- // This is more performant that using Object.prototype.hasOwnProperty.call
11297
- cache[exp] = parsedExpression;
11304
+ if (cacheKey !== 'hasOwnProperty') {
11305
+ // Only cache the value if it's not going to mess up the cache object
11306
+ // This is more performant that using Object.prototype.hasOwnProperty.call
11307
+ cache[cacheKey] = parsedExpression;
11308
+ }
11298
11309
  }
11299
-
11300
- return oneTime || parsedExpression.constant ? oneTimeWrapper(parsedExpression) : parsedExpression;
11310
+ return addInterceptor(parsedExpression, interceptorFn);
11301
11311
 
11302
11312
  case 'function':
11303
- return exp;
11313
+ return addInterceptor(exp, interceptorFn);
11304
11314
 
11305
11315
  default:
11306
- return noop;
11316
+ return addInterceptor(noop, interceptorFn);
11307
11317
  }
11318
+ };
11308
11319
 
11309
- function oneTimeWrapper(expression) {
11310
- var stable = false,
11311
- lastValue;
11312
- oneTimeParseFn.literal = expression.literal;
11313
- oneTimeParseFn.constant = expression.constant;
11314
- oneTimeParseFn.assign = expression.assign;
11315
- return oneTimeParseFn;
11316
-
11317
- function oneTimeParseFn(self, locals) {
11318
- if (!stable) {
11319
- lastValue = expression.constant && lastValue ? lastValue : expression(self, locals);
11320
- oneTimeParseFn.$$unwatch = isDefined(lastValue);
11321
- if (oneTimeParseFn.$$unwatch && self && self.$$postDigestQueue) {
11322
- self.$$postDigestQueue.push(function () {
11323
- // create a copy if the value is defined and it is not a $sce value
11324
- if ((stable = isDefined(lastValue)) &&
11325
- (lastValue === null || !lastValue.$$unwrapTrustedValue)) {
11326
- lastValue = copy(lastValue, null);
11327
- }
11328
- });
11320
+ function oneTimeWatch(scope, listener, objectEquality, deregisterNotifier, parsedExpression) {
11321
+ var unwatch, lastValue;
11322
+ return unwatch = scope.$watch(function oneTimeWatch(scope) {
11323
+ return parsedExpression(scope);
11324
+ }, function oneTimeListener(value, old, scope) {
11325
+ lastValue = value;
11326
+ if (isFunction(listener)) {
11327
+ listener.apply(this, arguments);
11328
+ }
11329
+ if (isDefined(value)) {
11330
+ scope.$$postDigest(function () {
11331
+ if (isDefined(lastValue)) {
11332
+ unwatch();
11329
11333
  }
11330
- }
11331
- return lastValue;
11334
+ });
11335
+ }
11336
+ }, objectEquality, deregisterNotifier);
11337
+ }
11338
+
11339
+ function constantWatch(scope, listener, objectEquality, deregisterNotifier, parsedExpression) {
11340
+ var unwatch;
11341
+ return unwatch = scope.$watch(function constantWatch(scope) {
11342
+ return parsedExpression(scope);
11343
+ }, function constantListener(value, old, scope) {
11344
+ if (isFunction(listener)) {
11345
+ listener.apply(this, arguments);
11332
11346
  }
11347
+ unwatch();
11348
+ }, objectEquality, deregisterNotifier);
11349
+ }
11350
+
11351
+ function addInterceptor(parsedExpression, interceptorFn) {
11352
+ if (isFunction(interceptorFn)) {
11353
+ var fn = function interceptedExpression(scope, locals) {
11354
+ var value = parsedExpression(scope, locals);
11355
+ var result = interceptorFn(value, scope, locals);
11356
+ // we only return the interceptor's result if the
11357
+ // initial value is defined (for bind-once)
11358
+ return isDefined(value) ? result : value;
11359
+ };
11360
+ fn.$$watchDelegate = parsedExpression.$$watchDelegate;
11361
+ return fn;
11362
+ } else {
11363
+ return parsedExpression;
11333
11364
  }
11334
- };
11365
+ }
11335
11366
  }];
11336
11367
  }
11337
11368
 
@@ -11343,6 +11374,46 @@ function $ParseProvider() {
11343
11374
  * @description
11344
11375
  * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
11345
11376
  *
11377
+ * $q can be used in two fashions --- One, which is more similar to Kris Kowal's Q or jQuery's Deferred
11378
+ * implementations, the other resembles ES6 promises to some degree.
11379
+ *
11380
+ * # $q constructor
11381
+ *
11382
+ * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
11383
+ * function as the first argument). This is similar to the native Promise implementation from ES6 Harmony,
11384
+ * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
11385
+ *
11386
+ * While the constructor-style use is supported, not all of the supporting methods from Harmony promises are
11387
+ * available yet.
11388
+ *
11389
+ * It can be used like so:
11390
+ *
11391
+ * ```js
11392
+ * return $q(function(resolve, reject) {
11393
+ * // perform some asynchronous operation, resolve or reject the promise when appropriate.
11394
+ * setInterval(function() {
11395
+ * if (pollStatus > 0) {
11396
+ * resolve(polledValue);
11397
+ * } else if (pollStatus < 0) {
11398
+ * reject(polledValue);
11399
+ * } else {
11400
+ * pollStatus = pollAgain(function(value) {
11401
+ * polledValue = value;
11402
+ * });
11403
+ * }
11404
+ * }, 10000);
11405
+ * }).
11406
+ * then(function(value) {
11407
+ * // handle success
11408
+ * }, function(reason) {
11409
+ * // handle failure
11410
+ * });
11411
+ * ```
11412
+ *
11413
+ * Note, progress/notify callbacks are not currently supported via the ES6-style interface.
11414
+ *
11415
+ * However, the more traditional CommonJS style usage is still available, and documented below.
11416
+ *
11346
11417
  * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
11347
11418
  * interface for interacting with an object that represents the result of an action that is
11348
11419
  * performed asynchronously, and may or may not be finished at any given point in time.
@@ -11389,7 +11460,6 @@ function $ParseProvider() {
11389
11460
  * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
11390
11461
  * section on serial or parallel joining of promises.
11391
11462
  *
11392
- *
11393
11463
  * # The Deferred API
11394
11464
  *
11395
11465
  * A new instance of deferred is constructed by calling `$q.defer()`.
@@ -11498,6 +11568,12 @@ function $ParseProvider() {
11498
11568
  * expect(resolvedValue).toEqual(123);
11499
11569
  * }));
11500
11570
  * ```
11571
+ *
11572
+ * @param {function(function, function)} resolver Function which is responsible for resolving or
11573
+ * rejecting the newly created promise. The first parameteter is a function which resolves the
11574
+ * promise, the second parameter is a function which rejects the promise.
11575
+ *
11576
+ * @returns {Promise} The newly created promise.
11501
11577
  */
11502
11578
  function $QProvider() {
11503
11579
 
@@ -11645,7 +11721,7 @@ function qFactory(nextTick, exceptionHandler) {
11645
11721
  } catch(e) {
11646
11722
  return makePromise(e, false);
11647
11723
  }
11648
- if (callbackOutput && isFunction(callbackOutput.then)) {
11724
+ if (isPromiseLike(callbackOutput)) {
11649
11725
  return callbackOutput.then(function() {
11650
11726
  return makePromise(value, isResolved);
11651
11727
  }, function(error) {
@@ -11670,7 +11746,7 @@ function qFactory(nextTick, exceptionHandler) {
11670
11746
 
11671
11747
 
11672
11748
  var ref = function(value) {
11673
- if (value && isFunction(value.then)) return value;
11749
+ if (isPromiseLike(value)) return value;
11674
11750
  return {
11675
11751
  then: function(callback) {
11676
11752
  var result = defer();
@@ -11854,12 +11930,38 @@ function qFactory(nextTick, exceptionHandler) {
11854
11930
  return deferred.promise;
11855
11931
  }
11856
11932
 
11857
- return {
11858
- defer: defer,
11859
- reject: reject,
11860
- when: when,
11861
- all: all
11933
+ var $Q = function Q(resolver) {
11934
+ if (!isFunction(resolver)) {
11935
+ // TODO(@caitp): minErr this
11936
+ throw new TypeError('Expected resolverFn');
11937
+ }
11938
+
11939
+ if (!(this instanceof Q)) {
11940
+ // More useful when $Q is the Promise itself.
11941
+ return new Q(resolver);
11942
+ }
11943
+
11944
+ var deferred = defer();
11945
+
11946
+ function resolveFn(value) {
11947
+ deferred.resolve(value);
11948
+ }
11949
+
11950
+ function rejectFn(reason) {
11951
+ deferred.reject(reason);
11952
+ }
11953
+
11954
+ resolver(resolveFn, rejectFn);
11955
+
11956
+ return deferred.promise;
11862
11957
  };
11958
+
11959
+ $Q.defer = defer;
11960
+ $Q.reject = reject;
11961
+ $Q.when = when;
11962
+ $Q.all = all;
11963
+
11964
+ return $Q;
11863
11965
  }
11864
11966
 
11865
11967
  function $$RAFProvider(){ //rAF
@@ -12209,20 +12311,25 @@ function $RootScopeProvider(){
12209
12311
  *
12210
12312
  * - `string`: Evaluated as {@link guide/expression expression}
12211
12313
  * - `function(scope)`: called with current `scope` as a parameter.
12212
- * @param {(function()|string)=} listener Callback called whenever the return value of
12213
- * the `watchExpression` changes.
12214
- *
12215
- * - `string`: Evaluated as {@link guide/expression expression}
12216
- * - `function(newValue, oldValue, scope)`: called with current and previous values as
12217
- * parameters.
12314
+ * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
12315
+ * of `watchExpression` changes.
12218
12316
  *
12317
+ * - `newVal` contains the current value of the `watchExpression`
12318
+ * - `oldVal` contains the previous value of the `watchExpression`
12319
+ * - `scope` refers to the current scope
12219
12320
  * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
12220
12321
  * comparing for reference equality.
12322
+ * @param {function()=} deregisterNotifier Function to call when the deregistration function
12323
+ * get called.
12221
12324
  * @returns {function()} Returns a deregistration function for this listener.
12222
12325
  */
12223
- $watch: function(watchExp, listener, objectEquality) {
12326
+ $watch: function(watchExp, listener, objectEquality, deregisterNotifier) {
12327
+ var get = compileToFn(watchExp, 'watch');
12328
+
12329
+ if (get.$$watchDelegate) {
12330
+ return get.$$watchDelegate(this, listener, objectEquality, deregisterNotifier, get);
12331
+ }
12224
12332
  var scope = this,
12225
- get = compileToFn(watchExp, 'watch'),
12226
12333
  array = scope.$$watchers,
12227
12334
  watcher = {
12228
12335
  fn: listener,
@@ -12234,10 +12341,8 @@ function $RootScopeProvider(){
12234
12341
 
12235
12342
  lastDirtyWatch = null;
12236
12343
 
12237
- // in the case user pass string, we need to compile it, do we really need this ?
12238
12344
  if (!isFunction(listener)) {
12239
- var listenFn = compileToFn(listener || noop, 'listener');
12240
- watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
12345
+ watcher.fn = noop;
12241
12346
  }
12242
12347
 
12243
12348
  if (!array) {
@@ -12250,6 +12355,9 @@ function $RootScopeProvider(){
12250
12355
  return function deregisterWatch() {
12251
12356
  arrayRemove(array, watcher);
12252
12357
  lastDirtyWatch = null;
12358
+ if (isFunction(deregisterNotifier)) {
12359
+ deregisterNotifier();
12360
+ }
12253
12361
  };
12254
12362
  },
12255
12363
 
@@ -12276,7 +12384,6 @@ function $RootScopeProvider(){
12276
12384
  * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
12277
12385
  * those of `watchExpression`
12278
12386
  * The `scope` refers to the current scope.
12279
- *
12280
12387
  * @returns {function()} Returns a de-registration function for all listeners.
12281
12388
  */
12282
12389
  $watchGroup: function(watchExpressions, listener) {
@@ -12285,37 +12392,43 @@ function $RootScopeProvider(){
12285
12392
  var deregisterFns = [];
12286
12393
  var changeCount = 0;
12287
12394
  var self = this;
12288
- var unwatchFlags = new Array(watchExpressions.length);
12289
- var unwatchCount = watchExpressions.length;
12395
+ var masterUnwatch;
12396
+
12397
+ if (watchExpressions.length === 1) {
12398
+ // Special case size of one
12399
+ return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
12400
+ newValues[0] = value;
12401
+ oldValues[0] = oldValue;
12402
+ listener.call(this, newValues, (value === oldValue) ? newValues : oldValues, scope);
12403
+ });
12404
+ }
12290
12405
 
12291
12406
  forEach(watchExpressions, function (expr, i) {
12292
- var exprFn = $parse(expr);
12293
- deregisterFns.push(self.$watch(exprFn, function (value, oldValue) {
12407
+ var unwatch = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
12294
12408
  newValues[i] = value;
12295
12409
  oldValues[i] = oldValue;
12296
12410
  changeCount++;
12297
- if (unwatchFlags[i] && !exprFn.$$unwatch) unwatchCount++;
12298
- if (!unwatchFlags[i] && exprFn.$$unwatch) unwatchCount--;
12299
- unwatchFlags[i] = exprFn.$$unwatch;
12300
- }));
12411
+ }, false, function watchGroupDeregNotifier() {
12412
+ arrayRemove(deregisterFns, unwatch);
12413
+ if (!deregisterFns.length) {
12414
+ masterUnwatch();
12415
+ }
12416
+ });
12417
+
12418
+ deregisterFns.push(unwatch);
12301
12419
  }, this);
12302
12420
 
12303
- deregisterFns.push(self.$watch(watchGroupFn, function () {
12304
- listener(newValues, oldValues, self);
12305
- if (unwatchCount === 0) {
12306
- watchGroupFn.$$unwatch = true;
12307
- } else {
12308
- watchGroupFn.$$unwatch = false;
12309
- }
12310
- }));
12421
+ masterUnwatch = self.$watch(function watchGroupChangeWatch() {
12422
+ return changeCount;
12423
+ }, function watchGroupChangeAction(value, oldValue) {
12424
+ listener(newValues, (value === oldValue) ? newValues : oldValues, self);
12425
+ });
12311
12426
 
12312
12427
  return function deregisterWatchGroup() {
12313
- forEach(deregisterFns, function (fn) {
12314
- fn();
12315
- });
12428
+ while (deregisterFns.length) {
12429
+ deregisterFns[0]();
12430
+ }
12316
12431
  };
12317
-
12318
- function watchGroupFn() {return changeCount;}
12319
12432
  },
12320
12433
 
12321
12434
 
@@ -12386,15 +12499,15 @@ function $RootScopeProvider(){
12386
12499
  // only track veryOldValue if the listener is asking for it
12387
12500
  var trackVeryOldValue = (listener.length > 1);
12388
12501
  var changeDetected = 0;
12389
- var objGetter = $parse(obj);
12502
+ var changeDetector = $parse(obj, $watchCollectionInterceptor);
12390
12503
  var internalArray = [];
12391
12504
  var internalObject = {};
12392
12505
  var initRun = true;
12393
12506
  var oldLength = 0;
12394
12507
 
12395
- function $watchCollectionWatch() {
12396
- newValue = objGetter(self);
12397
- var newLength, key;
12508
+ function $watchCollectionInterceptor(_value) {
12509
+ newValue = _value;
12510
+ var newLength, key, bothNaN;
12398
12511
 
12399
12512
  if (!isObject(newValue)) { // if primitive
12400
12513
  if (oldValue !== newValue) {
@@ -12418,7 +12531,7 @@ function $RootScopeProvider(){
12418
12531
  }
12419
12532
  // copy the items to oldValue and look for changes.
12420
12533
  for (var i = 0; i < newLength; i++) {
12421
- var bothNaN = (oldValue[i] !== oldValue[i]) &&
12534
+ bothNaN = (oldValue[i] !== oldValue[i]) &&
12422
12535
  (newValue[i] !== newValue[i]);
12423
12536
  if (!bothNaN && (oldValue[i] !== newValue[i])) {
12424
12537
  changeDetected++;
@@ -12438,7 +12551,9 @@ function $RootScopeProvider(){
12438
12551
  if (newValue.hasOwnProperty(key)) {
12439
12552
  newLength++;
12440
12553
  if (oldValue.hasOwnProperty(key)) {
12441
- if (oldValue[key] !== newValue[key]) {
12554
+ bothNaN = (oldValue[key] !== oldValue[key]) &&
12555
+ (newValue[key] !== newValue[key]);
12556
+ if (!bothNaN && (oldValue[key] !== newValue[key])) {
12442
12557
  changeDetected++;
12443
12558
  oldValue[key] = newValue[key];
12444
12559
  }
@@ -12460,7 +12575,6 @@ function $RootScopeProvider(){
12460
12575
  }
12461
12576
  }
12462
12577
  }
12463
- $watchCollectionWatch.$$unwatch = objGetter.$$unwatch;
12464
12578
  return changeDetected;
12465
12579
  }
12466
12580
 
@@ -12493,7 +12607,7 @@ function $RootScopeProvider(){
12493
12607
  }
12494
12608
  }
12495
12609
 
12496
- return this.$watch($watchCollectionWatch, $watchCollectionAction);
12610
+ return this.$watch(changeDetector, $watchCollectionAction);
12497
12611
  },
12498
12612
 
12499
12613
  /**
@@ -12556,7 +12670,6 @@ function $RootScopeProvider(){
12556
12670
  dirty, ttl = TTL,
12557
12671
  next, current, target = this,
12558
12672
  watchLog = [],
12559
- stableWatchesCandidates = [],
12560
12673
  logIdx, logMsg, asyncTask;
12561
12674
 
12562
12675
  beginPhase('$digest');
@@ -12607,7 +12720,6 @@ function $RootScopeProvider(){
12607
12720
  logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
12608
12721
  watchLog[logIdx].push(logMsg);
12609
12722
  }
12610
- if (watch.get.$$unwatch) stableWatchesCandidates.push({watch: watch, array: watchers});
12611
12723
  } else if (watch === lastDirtyWatch) {
12612
12724
  // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
12613
12725
  // have already been tested.
@@ -12654,13 +12766,6 @@ function $RootScopeProvider(){
12654
12766
  $exceptionHandler(e);
12655
12767
  }
12656
12768
  }
12657
-
12658
- for (length = stableWatchesCandidates.length - 1; length >= 0; --length) {
12659
- var candidate = stableWatchesCandidates[length];
12660
- if (candidate.watch.get.$$unwatch) {
12661
- arrayRemove(candidate.array, candidate.watch);
12662
- }
12663
- }
12664
12769
  },
12665
12770
 
12666
12771
 
@@ -13975,11 +14080,9 @@ function $SceProvider() {
13975
14080
  if (parsed.literal && parsed.constant) {
13976
14081
  return parsed;
13977
14082
  } else {
13978
- return function sceParseAsTrusted(self, locals) {
13979
- var result = sce.getTrusted(type, parsed(self, locals));
13980
- sceParseAsTrusted.$$unwatch = parsed.$$unwatch;
13981
- return result;
13982
- };
14083
+ return $parse(expr, function (value) {
14084
+ return sce.getTrusted(type, value);
14085
+ });
13983
14086
  }
13984
14087
  };
13985
14088
 
@@ -15372,11 +15475,7 @@ function dateFilter($locale) {
15372
15475
  format = format || 'mediumDate';
15373
15476
  format = $locale.DATETIME_FORMATS[format] || format;
15374
15477
  if (isString(date)) {
15375
- if (NUMBER_STRING.test(date)) {
15376
- date = int(date);
15377
- } else {
15378
- date = jsonStringToDate(date);
15379
- }
15478
+ date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
15380
15479
  }
15381
15480
 
15382
15481
  if (isNumber(date)) {
@@ -15651,7 +15750,7 @@ function limitToFilter(){
15651
15750
  * @example
15652
15751
  <example module="orderByExample">
15653
15752
  <file name="index.html">
15654
- <div ng-controller="Ctrl">
15753
+ <div ng-controller="ExampleController">
15655
15754
  <table class="friend">
15656
15755
  <tr>
15657
15756
  <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
@@ -15732,6 +15831,10 @@ function orderByFilter($parse){
15732
15831
  var t1 = typeof v1;
15733
15832
  var t2 = typeof v2;
15734
15833
  if (t1 == t2) {
15834
+ if (isDate(v1) && isDate(v2)) {
15835
+ v1 = v1.valueOf();
15836
+ v2 = v2.valueOf();
15837
+ }
15735
15838
  if (t1 == "string") {
15736
15839
  v1 = v1.toLowerCase();
15737
15840
  v2 = v2.toLowerCase();
@@ -16154,6 +16257,7 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) {
16154
16257
  var normalized = directiveNormalize('ng-' + attrName);
16155
16258
  ngAttributeAliasDirectives[normalized] = function() {
16156
16259
  return {
16260
+ restrict: 'A',
16157
16261
  priority: 100,
16158
16262
  link: function(scope, element, attr) {
16159
16263
  scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
@@ -16206,8 +16310,12 @@ forEach(['src', 'srcset', 'href'], function(attrName) {
16206
16310
  }
16207
16311
 
16208
16312
  attr.$observe(normalized, function(value) {
16209
- if (!value)
16210
- return;
16313
+ if (!value) {
16314
+ if (attrName === 'href') {
16315
+ attr.$set(name, null);
16316
+ }
16317
+ return;
16318
+ }
16211
16319
 
16212
16320
  attr.$set(name, value);
16213
16321
 
@@ -16795,7 +16903,9 @@ var inputType = {
16795
16903
  * @description
16796
16904
  * Input with date validation and transformation. In browsers that do not yet support
16797
16905
  * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
16798
- * date format (yyyy-MM-dd), for example: `2009-01-06`. The model must always be a Date object.
16906
+ * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
16907
+ * modern browsers do not yet support this input type, it is important to provide cues to users on the
16908
+ * expected input format via a placeholder or label. The model must always be a Date object.
16799
16909
  *
16800
16910
  * @param {string} ngModel Assignable angular expression to data-bind to.
16801
16911
  * @param {string=} name Property name of the form under which the control is published.
@@ -18838,6 +18948,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
18838
18948
  */
18839
18949
  var ngModelDirective = function() {
18840
18950
  return {
18951
+ restrict: 'A',
18841
18952
  require: ['ngModel', '^?form', '^?ngModelOptions'],
18842
18953
  controller: NgModelController,
18843
18954
  link: {
@@ -18939,6 +19050,7 @@ var ngModelDirective = function() {
18939
19050
  * </example>
18940
19051
  */
18941
19052
  var ngChangeDirective = valueFn({
19053
+ restrict: 'A',
18942
19054
  require: 'ngModel',
18943
19055
  link: function(scope, element, attr, ctrl) {
18944
19056
  ctrl.$viewChangeListeners.push(function() {
@@ -18950,6 +19062,7 @@ var ngChangeDirective = valueFn({
18950
19062
 
18951
19063
  var requiredDirective = function() {
18952
19064
  return {
19065
+ restrict: 'A',
18953
19066
  require: '?ngModel',
18954
19067
  link: function(scope, elm, attr, ctrl) {
18955
19068
  if (!ctrl) return;
@@ -18969,6 +19082,7 @@ var requiredDirective = function() {
18969
19082
 
18970
19083
  var patternDirective = function() {
18971
19084
  return {
19085
+ restrict: 'A',
18972
19086
  require: '?ngModel',
18973
19087
  link: function(scope, elm, attr, ctrl) {
18974
19088
  if (!ctrl) return;
@@ -18999,6 +19113,7 @@ var patternDirective = function() {
18999
19113
 
19000
19114
  var maxlengthDirective = function() {
19001
19115
  return {
19116
+ restrict: 'A',
19002
19117
  require: '?ngModel',
19003
19118
  link: function(scope, elm, attr, ctrl) {
19004
19119
  if (!ctrl) return;
@@ -19017,6 +19132,7 @@ var maxlengthDirective = function() {
19017
19132
 
19018
19133
  var minlengthDirective = function() {
19019
19134
  return {
19135
+ restrict: 'A',
19020
19136
  require: '?ngModel',
19021
19137
  link: function(scope, elm, attr, ctrl) {
19022
19138
  if (!ctrl) return;
@@ -19039,62 +19155,93 @@ var minlengthDirective = function() {
19039
19155
  * @name ngList
19040
19156
  *
19041
19157
  * @description
19042
- * Text input that converts between a delimited string and an array of strings. The delimiter
19043
- * can be a fixed string (by default a comma) or a regular expression.
19158
+ * Text input that converts between a delimited string and an array of strings. The default
19159
+ * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
19160
+ * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
19161
+ *
19162
+ * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
19163
+ * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
19164
+ * list item is respected. This implies that the user of the directive is responsible for
19165
+ * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
19166
+ * tab or newline character.
19167
+ * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
19168
+ * when joining the list items back together) and whitespace around each list item is stripped
19169
+ * before it is added to the model.
19170
+ *
19171
+ * ### Example with Validation
19172
+ *
19173
+ * <example name="ngList-directive" module="listExample">
19174
+ * <file name="app.js">
19175
+ * angular.module('listExample', [])
19176
+ * .controller('ExampleController', ['$scope', function($scope) {
19177
+ * $scope.names = ['morpheus', 'neo', 'trinity'];
19178
+ * }]);
19179
+ * </file>
19180
+ * <file name="index.html">
19181
+ * <form name="myForm" ng-controller="ExampleController">
19182
+ * List: <input name="namesInput" ng-model="names" ng-list required>
19183
+ * <span class="error" ng-show="myForm.namesInput.$error.required">
19184
+ * Required!</span>
19185
+ * <br>
19186
+ * <tt>names = {{names}}</tt><br/>
19187
+ * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
19188
+ * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
19189
+ * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19190
+ * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19191
+ * </form>
19192
+ * </file>
19193
+ * <file name="protractor.js" type="protractor">
19194
+ * var listInput = element(by.model('names'));
19195
+ * var names = element(by.binding('{{names}}'));
19196
+ * var valid = element(by.binding('myForm.namesInput.$valid'));
19197
+ * var error = element(by.css('span.error'));
19198
+ *
19199
+ * it('should initialize to model', function() {
19200
+ * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
19201
+ * expect(valid.getText()).toContain('true');
19202
+ * expect(error.getCssValue('display')).toBe('none');
19203
+ * });
19044
19204
  *
19045
- * @element input
19046
- * @param {string=} ngList optional delimiter that should be used to split the value. If
19047
- * specified in form `/something/` then the value will be converted into a regular expression.
19205
+ * it('should be invalid if empty', function() {
19206
+ * listInput.clear();
19207
+ * listInput.sendKeys('');
19048
19208
  *
19049
- * @example
19050
- <example name="ngList-directive" module="listExample">
19051
- <file name="index.html">
19052
- <script>
19053
- angular.module('listExample', [])
19054
- .controller('ExampleController', ['$scope', function($scope) {
19055
- $scope.names = ['igor', 'misko', 'vojta'];
19056
- }]);
19057
- </script>
19058
- <form name="myForm" ng-controller="ExampleController">
19059
- List: <input name="namesInput" ng-model="names" ng-list required>
19060
- <span class="error" ng-show="myForm.namesInput.$error.required">
19061
- Required!</span>
19062
- <br>
19063
- <tt>names = {{names}}</tt><br/>
19064
- <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
19065
- <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
19066
- <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19067
- <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19068
- </form>
19069
- </file>
19070
- <file name="protractor.js" type="protractor">
19071
- var listInput = element(by.model('names'));
19072
- var names = element(by.binding('{{names}}'));
19073
- var valid = element(by.binding('myForm.namesInput.$valid'));
19074
- var error = element(by.css('span.error'));
19075
-
19076
- it('should initialize to model', function() {
19077
- expect(names.getText()).toContain('["igor","misko","vojta"]');
19078
- expect(valid.getText()).toContain('true');
19079
- expect(error.getCssValue('display')).toBe('none');
19080
- });
19081
-
19082
- it('should be invalid if empty', function() {
19083
- listInput.clear();
19084
- listInput.sendKeys('');
19085
-
19086
- expect(names.getText()).toContain('');
19087
- expect(valid.getText()).toContain('false');
19088
- expect(error.getCssValue('display')).not.toBe('none'); });
19089
- </file>
19090
- </example>
19209
+ * expect(names.getText()).toContain('');
19210
+ * expect(valid.getText()).toContain('false');
19211
+ * expect(error.getCssValue('display')).not.toBe('none');
19212
+ * });
19213
+ * </file>
19214
+ * </example>
19215
+ *
19216
+ * ### Example - splitting on whitespace
19217
+ * <example name="ngList-directive-newlines">
19218
+ * <file name="index.html">
19219
+ * <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
19220
+ * <pre>{{ list | json }}</pre>
19221
+ * </file>
19222
+ * <file name="protractor.js" type="protractor">
19223
+ * it("should split the text by newlines", function() {
19224
+ * var listInput = element(by.model('list'));
19225
+ * var output = element(by.binding('{{ list | json }}'));
19226
+ * listInput.sendKeys('abc\ndef\nghi');
19227
+ * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
19228
+ * });
19229
+ * </file>
19230
+ * </example>
19231
+ *
19232
+ * @element input
19233
+ * @param {string=} ngList optional delimiter that should be used to split the value.
19091
19234
  */
19092
19235
  var ngListDirective = function() {
19093
19236
  return {
19237
+ restrict: 'A',
19094
19238
  require: 'ngModel',
19095
19239
  link: function(scope, element, attr, ctrl) {
19096
- var match = /\/(.*)\//.exec(attr.ngList),
19097
- separator = match && new RegExp(match[1]) || attr.ngList || ',';
19240
+ // We want to control whitespace trimming so we use this convoluted approach
19241
+ // to access the ngList attribute, which doesn't pre-trim the attribute
19242
+ var ngList = element.attr(attr.$attr.ngList) || ', ';
19243
+ var trimValues = attr.ngTrim !== 'false';
19244
+ var separator = trimValues ? trim(ngList) : ngList;
19098
19245
 
19099
19246
  var parse = function(viewValue) {
19100
19247
  // If the viewValue is invalid (say required but empty) it will be `undefined`
@@ -19104,7 +19251,7 @@ var ngListDirective = function() {
19104
19251
 
19105
19252
  if (viewValue) {
19106
19253
  forEach(viewValue.split(separator), function(value) {
19107
- if (value) list.push(trim(value));
19254
+ if (value) list.push(trimValues ? trim(value) : value);
19108
19255
  });
19109
19256
  }
19110
19257
 
@@ -19114,7 +19261,7 @@ var ngListDirective = function() {
19114
19261
  ctrl.$parsers.push(parse);
19115
19262
  ctrl.$formatters.push(function(value) {
19116
19263
  if (isArray(value)) {
19117
- return value.join(', ');
19264
+ return value.join(ngList);
19118
19265
  }
19119
19266
 
19120
19267
  return undefined;
@@ -19184,6 +19331,7 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
19184
19331
  */
19185
19332
  var ngValueDirective = function() {
19186
19333
  return {
19334
+ restrict: 'A',
19187
19335
  priority: 100,
19188
19336
  compile: function(tpl, tplAttr) {
19189
19337
  if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
@@ -19346,6 +19494,7 @@ var ngValueDirective = function() {
19346
19494
  */
19347
19495
  var ngModelOptionsDirective = function() {
19348
19496
  return {
19497
+ restrict: 'A',
19349
19498
  controller: ['$scope', '$attrs', function($scope, $attrs) {
19350
19499
  var that = this;
19351
19500
  this.$options = $scope.$eval($attrs.ngModelOptions);
@@ -19377,7 +19526,7 @@ var ngModelOptionsDirective = function() {
19377
19526
  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
19378
19527
  * `{{ expression }}` which is similar but less verbose.
19379
19528
  *
19380
- * It is preferable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
19529
+ * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
19381
19530
  * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
19382
19531
  * element attribute, it makes the bindings invisible to the user while the page is loading.
19383
19532
  *
@@ -19541,19 +19690,25 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
19541
19690
  </example>
19542
19691
  */
19543
19692
  var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) {
19544
- return function(scope, element, attr) {
19545
- element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
19693
+ return {
19694
+ restrict: 'A',
19695
+ compile: function (tElement, tAttrs) {
19696
+ tElement.addClass('ng-binding');
19546
19697
 
19547
- var parsed = $parse(attr.ngBindHtml);
19548
- function getStringValue() {
19549
- var value = parsed(scope);
19550
- getStringValue.$$unwatch = parsed.$$unwatch;
19551
- return (value || '').toString();
19552
- }
19698
+ return function (scope, element, attr) {
19699
+ element.data('$binding', attr.ngBindHtml);
19700
+ var parsed = $parse(attr.ngBindHtml);
19701
+ var changeDetector = $parse(attr.ngBindHtml, function getStringValue(value) {
19702
+ return (value || '').toString();
19703
+ });
19553
19704
 
19554
- scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) {
19555
- element.html($sce.getTrustedHtml(parsed(scope)) || '');
19556
- });
19705
+ scope.$watch(changeDetector, function ngBindHtmlWatchAction() {
19706
+ // we re-evaluate the expr because we want a TrustedValueHolderType
19707
+ // for $sce, not a string
19708
+ element.html($sce.getTrustedHtml(parsed(scope)) || '');
19709
+ });
19710
+ };
19711
+ }
19557
19712
  };
19558
19713
  }];
19559
19714
 
@@ -20199,6 +20354,7 @@ var ngCloakDirective = ngDirective({
20199
20354
  */
20200
20355
  var ngControllerDirective = [function() {
20201
20356
  return {
20357
+ restrict: 'A',
20202
20358
  scope: true,
20203
20359
  controller: '@',
20204
20360
  priority: 500
@@ -20216,8 +20372,10 @@ var ngControllerDirective = [function() {
20216
20372
  * This is necessary when developing things like Google Chrome Extensions.
20217
20373
  *
20218
20374
  * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
20219
- * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
20220
- * any of these restrictions.
20375
+ * For Angular to be CSP compatible there are only two things that we need to do differently:
20376
+ *
20377
+ * - don't use `Function` constructor to generate optimized value getters
20378
+ * - don't inject custom stylesheet into the document
20221
20379
  *
20222
20380
  * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
20223
20381
  * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
@@ -20228,7 +20386,18 @@ var ngControllerDirective = [function() {
20228
20386
  * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
20229
20387
  * To make those directives work in CSP mode, include the `angular-csp.css` manually.
20230
20388
  *
20231
- * In order to use this feature put the `ngCsp` directive on the root element of the application.
20389
+ * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
20390
+ * autodetection however triggers a CSP error to be logged in the console:
20391
+ *
20392
+ * ```
20393
+ * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
20394
+ * script in the following Content Security Policy directive: "default-src 'self'". Note that
20395
+ * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
20396
+ * ```
20397
+ *
20398
+ * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
20399
+ * directive on the root element of the application or on the `angular.js` script tag, whichever
20400
+ * appears first in the html document.
20232
20401
  *
20233
20402
  * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
20234
20403
  *
@@ -20243,9 +20412,9 @@ var ngControllerDirective = [function() {
20243
20412
  ```
20244
20413
  */
20245
20414
 
20246
- // ngCsp is not implemented as a proper directive any more, because we need it be processed while we bootstrap
20247
- // the system (before $parse is instantiated), for this reason we just have a csp() fn that looks for ng-csp attribute
20248
- // anywhere in the current doc
20415
+ // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
20416
+ // bootstrap the system (before $parse is instantiated), for this reason we just have
20417
+ // the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
20249
20418
 
20250
20419
  /**
20251
20420
  * @ngdoc directive
@@ -20290,6 +20459,7 @@ forEach(
20290
20459
  var directiveName = directiveNormalize('ng-' + name);
20291
20460
  ngEventDirectives[directiveName] = ['$parse', function($parse) {
20292
20461
  return {
20462
+ restrict: 'A',
20293
20463
  compile: function($element, attr) {
20294
20464
  var fn = $parse(attr[directiveName]);
20295
20465
  return function ngEventHandler(scope, element) {
@@ -20772,6 +20942,7 @@ forEach(
20772
20942
  */
20773
20943
  var ngIfDirective = ['$animate', function($animate) {
20774
20944
  return {
20945
+ multiElement: true,
20775
20946
  transclude: 'element',
20776
20947
  priority: 600,
20777
20948
  terminal: true,
@@ -21505,6 +21676,13 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
21505
21676
  * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
21506
21677
  * will be associated by item identity in the array.
21507
21678
  *
21679
+ * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
21680
+ * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
21681
+ * when a filter is active on the repeater, but the filtered result set is empty.
21682
+ *
21683
+ * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
21684
+ * the items have been processed through the filter.
21685
+ *
21508
21686
  * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
21509
21687
  * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
21510
21688
  * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
@@ -21537,9 +21715,12 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
21537
21715
  I have {{friends.length}} friends. They are:
21538
21716
  <input type="search" ng-model="q" placeholder="filter friends..." />
21539
21717
  <ul class="example-animate-container">
21540
- <li class="animate-repeat" ng-repeat="friend in friends | filter:q">
21718
+ <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
21541
21719
  [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
21542
21720
  </li>
21721
+ <li class="animate-repeat" ng-if="results.length == 0">
21722
+ <strong>No results found...</strong>
21723
+ </li>
21543
21724
  </ul>
21544
21725
  </div>
21545
21726
  </file>
@@ -21607,14 +21788,16 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
21607
21788
  var NG_REMOVED = '$$NG_REMOVED';
21608
21789
  var ngRepeatMinErr = minErr('ngRepeat');
21609
21790
  return {
21791
+ restrict: 'A',
21792
+ multiElement: true,
21610
21793
  transclude: 'element',
21611
21794
  priority: 1000,
21612
21795
  terminal: true,
21613
21796
  $$tlb: true,
21614
21797
  link: function($scope, $element, $attr, ctrl, $transclude){
21615
21798
  var expression = $attr.ngRepeat;
21616
- var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
21617
- trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
21799
+ 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*$/),
21800
+ trackByExp, trackByExpGetter, aliasAs, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
21618
21801
  lhs, rhs, valueIdentifier, keyIdentifier,
21619
21802
  hashFnLocals = {$id: hashKey};
21620
21803
 
@@ -21625,7 +21808,8 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
21625
21808
 
21626
21809
  lhs = match[1];
21627
21810
  rhs = match[2];
21628
- trackByExp = match[3];
21811
+ aliasAs = match[3];
21812
+ trackByExp = match[4];
21629
21813
 
21630
21814
  if (trackByExp) {
21631
21815
  trackByExpGetter = $parse(trackByExp);
@@ -21677,6 +21861,10 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
21677
21861
  nextBlockOrder = [],
21678
21862
  elementsToRemove;
21679
21863
 
21864
+ if (aliasAs) {
21865
+ $scope[aliasAs] = collection;
21866
+ }
21867
+
21680
21868
  var updateScope = function(scope, index) {
21681
21869
  scope[valueIdentifier] = value;
21682
21870
  if (keyIdentifier) scope[keyIdentifier] = key;
@@ -21952,10 +22140,14 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
21952
22140
  </example>
21953
22141
  */
21954
22142
  var ngShowDirective = ['$animate', function($animate) {
21955
- return function(scope, element, attr) {
21956
- scope.$watch(attr.ngShow, function ngShowWatchAction(value){
21957
- $animate[value ? 'removeClass' : 'addClass'](element, 'ng-hide');
21958
- });
22143
+ return {
22144
+ restrict: 'A',
22145
+ multiElement: true,
22146
+ link: function(scope, element, attr) {
22147
+ scope.$watch(attr.ngShow, function ngShowWatchAction(value){
22148
+ $animate[value ? 'removeClass' : 'addClass'](element, 'ng-hide');
22149
+ });
22150
+ }
21959
22151
  };
21960
22152
  }];
21961
22153
 
@@ -22103,10 +22295,14 @@ var ngShowDirective = ['$animate', function($animate) {
22103
22295
  </example>
22104
22296
  */
22105
22297
  var ngHideDirective = ['$animate', function($animate) {
22106
- return function(scope, element, attr) {
22107
- scope.$watch(attr.ngHide, function ngHideWatchAction(value){
22108
- $animate[value ? 'addClass' : 'removeClass'](element, 'ng-hide');
22109
- });
22298
+ return {
22299
+ restrict: 'A',
22300
+ multiElement: true,
22301
+ link: function(scope, element, attr) {
22302
+ scope.$watch(attr.ngHide, function ngHideWatchAction(value){
22303
+ $animate[value ? 'addClass' : 'removeClass'](element, 'ng-hide');
22304
+ });
22305
+ }
22110
22306
  };
22111
22307
  }];
22112
22308
 
@@ -22317,7 +22513,7 @@ var ngSwitchDirective = ['$animate', function($animate) {
22317
22513
  previousElements.length = 0;
22318
22514
 
22319
22515
  for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
22320
- var selected = selectedElements[i];
22516
+ var selected = getBlockElements(selectedElements[i].clone);
22321
22517
  selectedScopes[i].$destroy();
22322
22518
  previousElements[i] = selected;
22323
22519
  $animate.leave(selected, function() {
@@ -22331,12 +22527,13 @@ var ngSwitchDirective = ['$animate', function($animate) {
22331
22527
  if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
22332
22528
  scope.$eval(attr.change);
22333
22529
  forEach(selectedTranscludes, function(selectedTransclude) {
22334
- var selectedScope = scope.$new();
22335
- selectedScopes.push(selectedScope);
22336
- selectedTransclude.transclude(selectedScope, function(caseElement) {
22530
+ selectedTransclude.transclude(function(caseElement, selectedScope) {
22531
+ selectedScopes.push(selectedScope);
22337
22532
  var anchor = selectedTransclude.element;
22533
+ caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
22534
+ var block = { clone: caseElement };
22338
22535
 
22339
- selectedElements.push(caseElement);
22536
+ selectedElements.push(block);
22340
22537
  $animate.enter(caseElement, anchor.parent(), anchor);
22341
22538
  });
22342
22539
  });
@@ -22348,8 +22545,9 @@ var ngSwitchDirective = ['$animate', function($animate) {
22348
22545
 
22349
22546
  var ngSwitchWhenDirective = ngDirective({
22350
22547
  transclude: 'element',
22351
- priority: 800,
22548
+ priority: 1200,
22352
22549
  require: '^ngSwitch',
22550
+ multiElement: true,
22353
22551
  link: function(scope, element, attrs, ctrl, $transclude) {
22354
22552
  ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
22355
22553
  ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
@@ -22358,8 +22556,9 @@ var ngSwitchWhenDirective = ngDirective({
22358
22556
 
22359
22557
  var ngSwitchDefaultDirective = ngDirective({
22360
22558
  transclude: 'element',
22361
- priority: 800,
22559
+ priority: 1200,
22362
22560
  require: '^ngSwitch',
22561
+ multiElement: true,
22363
22562
  link: function(scope, element, attr, ctrl, $transclude) {
22364
22563
  ctrl.cases['?'] = (ctrl.cases['?'] || []);
22365
22564
  ctrl.cases['?'].push({ transclude: $transclude, element: element });
@@ -22369,7 +22568,7 @@ var ngSwitchDefaultDirective = ngDirective({
22369
22568
  /**
22370
22569
  * @ngdoc directive
22371
22570
  * @name ngTransclude
22372
- * @restrict AC
22571
+ * @restrict EAC
22373
22572
  *
22374
22573
  * @description
22375
22574
  * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
@@ -22390,7 +22589,7 @@ var ngSwitchDefaultDirective = ngDirective({
22390
22589
  scope: { title:'@' },
22391
22590
  template: '<div style="border: 1px solid black;">' +
22392
22591
  '<div style="background-color: gray">{{title}}</div>' +
22393
- '<div ng-transclude></div>' +
22592
+ '<ng-transclude></ng-transclude>' +
22394
22593
  '</div>'
22395
22594
  };
22396
22595
  })
@@ -22421,6 +22620,7 @@ var ngSwitchDefaultDirective = ngDirective({
22421
22620
  *
22422
22621
  */
22423
22622
  var ngTranscludeDirective = ngDirective({
22623
+ restrict: 'EAC',
22424
22624
  link: function($scope, $element, $attrs, controller, $transclude) {
22425
22625
  if (!$transclude) {
22426
22626
  throw minErr('ngTransclude')('orphan',
@@ -22621,7 +22821,11 @@ var ngOptionsMinErr = minErr('ngOptions');
22621
22821
  </example>
22622
22822
  */
22623
22823
 
22624
- var ngOptionsDirective = valueFn({ terminal: true });
22824
+ var ngOptionsDirective = valueFn({
22825
+ restrict: 'A',
22826
+ terminal: true
22827
+ });
22828
+
22625
22829
  // jshint maxlen: false
22626
22830
  var selectDirective = ['$compile', '$parse', function($compile, $parse) {
22627
22831
  //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
@@ -23032,6 +23236,12 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
23032
23236
  // lastElement.prop('selected') provided by jQuery has side-effects
23033
23237
  if (existingOption.selected !== option.selected) {
23034
23238
  lastElement.prop('selected', (existingOption.selected = option.selected));
23239
+ if (msie) {
23240
+ // See #7692
23241
+ // The selected item wouldn't visually update on IE without this.
23242
+ // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well
23243
+ lastElement.prop('selected', existingOption.selected);
23244
+ }
23035
23245
  }
23036
23246
  } else {
23037
23247
  // grow elements