angularjs-rails 1.2.20 → 1.2.21

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,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