angularjs-rails 1.4.4 → 1.4.7

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.4.4
2
+ * @license AngularJS v1.4.7
3
3
  * (c) 2010-2015 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.4.4
2
+ * @license AngularJS v1.4.7
3
3
  * (c) 2010-2015 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -57,7 +57,7 @@ function minErr(module, ErrorConstructor) {
57
57
  return match;
58
58
  });
59
59
 
60
- message += '\nhttp://errors.angularjs.org/1.4.4/' +
60
+ message += '\nhttp://errors.angularjs.org/1.4.7/' +
61
61
  (module ? module + '/' : '') + code;
62
62
 
63
63
  for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
@@ -894,6 +894,8 @@ function copy(source, destination, stackSource, stackDest) {
894
894
  } else if (isRegExp(source)) {
895
895
  destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
896
896
  destination.lastIndex = source.lastIndex;
897
+ } else if (isFunction(source.cloneNode)) {
898
+ destination = source.cloneNode(true);
897
899
  } else {
898
900
  var emptyObject = Object.create(getPrototypeOf(source));
899
901
  return copy(source, emptyObject, stackSource, stackDest);
@@ -1044,7 +1046,7 @@ function equals(o1, o2) {
1044
1046
  for (key in o2) {
1045
1047
  if (!(key in keySet) &&
1046
1048
  key.charAt(0) !== '$' &&
1047
- o2[key] !== undefined &&
1049
+ isDefined(o2[key]) &&
1048
1050
  !isFunction(o2[key])) return false;
1049
1051
  }
1050
1052
  return true;
@@ -1740,10 +1742,9 @@ function bindJQuery() {
1740
1742
 
1741
1743
  // bind to jQuery if present;
1742
1744
  var jqName = jq();
1743
- jQuery = window.jQuery; // use default jQuery.
1744
- if (isDefined(jqName)) { // `ngJq` present
1745
- jQuery = jqName === null ? undefined : window[jqName]; // if empty; use jqLite. if not empty, use jQuery specified by `ngJq`.
1746
- }
1745
+ jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
1746
+ !jqName ? undefined : // use jqLite
1747
+ window[jqName]; // use jQuery specified by `ngJq`
1747
1748
 
1748
1749
  // Use jQuery if it exists with proper functionality, otherwise default to us.
1749
1750
  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
@@ -1848,22 +1849,24 @@ function getter(obj, path, bindFnToScope) {
1848
1849
  /**
1849
1850
  * Return the DOM siblings between the first and last node in the given array.
1850
1851
  * @param {Array} array like object
1851
- * @returns {jqLite} jqLite collection containing the nodes
1852
+ * @returns {Array} the inputted object or a jqLite collection containing the nodes
1852
1853
  */
1853
1854
  function getBlockNodes(nodes) {
1854
- // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
1855
- // collection, otherwise update the original collection.
1855
+ // TODO(perf): update `nodes` instead of creating a new object?
1856
1856
  var node = nodes[0];
1857
1857
  var endNode = nodes[nodes.length - 1];
1858
- var blockNodes = [node];
1858
+ var blockNodes;
1859
1859
 
1860
- do {
1861
- node = node.nextSibling;
1862
- if (!node) break;
1863
- blockNodes.push(node);
1864
- } while (node !== endNode);
1860
+ for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1861
+ if (blockNodes || nodes[i] !== node) {
1862
+ if (!blockNodes) {
1863
+ blockNodes = jqLite(slice.call(nodes, 0, i));
1864
+ }
1865
+ blockNodes.push(node);
1866
+ }
1867
+ }
1865
1868
 
1866
- return jqLite(blockNodes);
1869
+ return blockNodes || nodes;
1867
1870
  }
1868
1871
 
1869
1872
 
@@ -2247,7 +2250,7 @@ function serializeObject(obj) {
2247
2250
  val = toJsonReplacer(key, val);
2248
2251
  if (isObject(val)) {
2249
2252
 
2250
- if (seen.indexOf(val) >= 0) return '<<already seen>>';
2253
+ if (seen.indexOf(val) >= 0) return '...';
2251
2254
 
2252
2255
  seen.push(val);
2253
2256
  }
@@ -2258,7 +2261,7 @@ function serializeObject(obj) {
2258
2261
  function toDebugString(obj) {
2259
2262
  if (typeof obj === 'function') {
2260
2263
  return obj.toString().replace(/ \{[\s\S]*$/, '');
2261
- } else if (typeof obj === 'undefined') {
2264
+ } else if (isUndefined(obj)) {
2262
2265
  return 'undefined';
2263
2266
  } else if (typeof obj !== 'string') {
2264
2267
  return serializeObject(obj);
@@ -2338,6 +2341,7 @@ function toDebugString(obj) {
2338
2341
  $HttpParamSerializerProvider,
2339
2342
  $HttpParamSerializerJQLikeProvider,
2340
2343
  $HttpBackendProvider,
2344
+ $xhrFactoryProvider,
2341
2345
  $LocationProvider,
2342
2346
  $LogProvider,
2343
2347
  $ParseProvider,
@@ -2364,8 +2368,9 @@ function toDebugString(obj) {
2364
2368
  * @name angular.version
2365
2369
  * @module ng
2366
2370
  * @description
2367
- * An object that contains information about the current AngularJS version. This object has the
2368
- * following properties:
2371
+ * An object that contains information about the current AngularJS version.
2372
+ *
2373
+ * This object has the following properties:
2369
2374
  *
2370
2375
  * - `full` – `{string}` – Full version string, such as "0.9.18".
2371
2376
  * - `major` – `{number}` – Major version number, such as "0".
@@ -2374,11 +2379,11 @@ function toDebugString(obj) {
2374
2379
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2375
2380
  */
2376
2381
  var version = {
2377
- full: '1.4.4', // all of these placeholder strings will be replaced by grunt's
2382
+ full: '1.4.7', // all of these placeholder strings will be replaced by grunt's
2378
2383
  major: 1, // package task
2379
2384
  minor: 4,
2380
- dot: 4,
2381
- codeName: 'pylon-requirement'
2385
+ dot: 7,
2386
+ codeName: 'dark-luminescence'
2382
2387
  };
2383
2388
 
2384
2389
 
@@ -2495,6 +2500,7 @@ function publishExternalAPI(angular) {
2495
2500
  $httpParamSerializer: $HttpParamSerializerProvider,
2496
2501
  $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2497
2502
  $httpBackend: $HttpBackendProvider,
2503
+ $xhrFactory: $xhrFactoryProvider,
2498
2504
  $location: $LocationProvider,
2499
2505
  $log: $LogProvider,
2500
2506
  $parse: $ParseProvider,
@@ -2583,7 +2589,7 @@ function publishExternalAPI(angular) {
2583
2589
  * - [`html()`](http://api.jquery.com/html/)
2584
2590
  * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2585
2591
  * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2586
- * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
2592
+ * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2587
2593
  * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2588
2594
  * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2589
2595
  * - [`prepend()`](http://api.jquery.com/prepend/)
@@ -2597,7 +2603,7 @@ function publishExternalAPI(angular) {
2597
2603
  * - [`text()`](http://api.jquery.com/text/)
2598
2604
  * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2599
2605
  * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2600
- * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
2606
+ * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2601
2607
  * - [`val()`](http://api.jquery.com/val/)
2602
2608
  * - [`wrap()`](http://api.jquery.com/wrap/)
2603
2609
  *
@@ -2669,10 +2675,10 @@ function camelCase(name) {
2669
2675
  replace(MOZ_HACK_REGEXP, 'Moz$1');
2670
2676
  }
2671
2677
 
2672
- var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
2678
+ var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2673
2679
  var HTML_REGEXP = /<|&#?\w+;/;
2674
- var TAG_NAME_REGEXP = /<([\w:]+)/;
2675
- var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
2680
+ var TAG_NAME_REGEXP = /<([\w:-]+)/;
2681
+ var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2676
2682
 
2677
2683
  var wrapMap = {
2678
2684
  'option': [1, '<select multiple="multiple">', '</select>'],
@@ -2968,7 +2974,7 @@ function jqLiteInheritedData(element, name, value) {
2968
2974
 
2969
2975
  while (element) {
2970
2976
  for (var i = 0, ii = names.length; i < ii; i++) {
2971
- if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2977
+ if (isDefined(value = jqLite.data(element, names[i]))) return value;
2972
2978
  }
2973
2979
 
2974
2980
  // If dealing with a document fragment node with a host element, and no parent, use the host
@@ -3074,9 +3080,8 @@ function getBooleanAttrName(element, name) {
3074
3080
  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3075
3081
  }
3076
3082
 
3077
- function getAliasedAttrName(element, name) {
3078
- var nodeName = element.nodeName;
3079
- return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
3083
+ function getAliasedAttrName(name) {
3084
+ return ALIASED_ATTR[name];
3080
3085
  }
3081
3086
 
3082
3087
  forEach({
@@ -3213,7 +3218,7 @@ forEach({
3213
3218
  // in a way that survives minification.
3214
3219
  // jqLiteEmpty takes no arguments but is a setter.
3215
3220
  if (fn !== jqLiteEmpty &&
3216
- (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
3221
+ (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3217
3222
  if (isObject(arg1)) {
3218
3223
 
3219
3224
  // we are a write, but the object properties are the key/values
@@ -3234,7 +3239,7 @@ forEach({
3234
3239
  // TODO: do we still need this?
3235
3240
  var value = fn.$dv;
3236
3241
  // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3237
- var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
3242
+ var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3238
3243
  for (var j = 0; j < jj; j++) {
3239
3244
  var nodeValue = fn(this[j], arg1, arg2);
3240
3245
  value = value ? value + nodeValue : nodeValue;
@@ -4873,61 +4878,66 @@ var $$CoreAnimateQueueProvider = function() {
4873
4878
  }
4874
4879
  };
4875
4880
 
4876
- function addRemoveClassesPostDigest(element, add, remove) {
4877
- var classVal, data = postDigestQueue.get(element);
4878
4881
 
4879
- if (!data) {
4880
- postDigestQueue.put(element, data = {});
4881
- postDigestElements.push(element);
4882
+ function updateData(data, classes, value) {
4883
+ var changed = false;
4884
+ if (classes) {
4885
+ classes = isString(classes) ? classes.split(' ') :
4886
+ isArray(classes) ? classes : [];
4887
+ forEach(classes, function(className) {
4888
+ if (className) {
4889
+ changed = true;
4890
+ data[className] = value;
4891
+ }
4892
+ });
4882
4893
  }
4883
-
4884
- var updateData = function(classes, value) {
4885
- var changed = false;
4886
- if (classes) {
4887
- classes = isString(classes) ? classes.split(' ') :
4888
- isArray(classes) ? classes : [];
4889
- forEach(classes, function(className) {
4890
- if (className) {
4891
- changed = true;
4892
- data[className] = value;
4894
+ return changed;
4895
+ }
4896
+
4897
+ function handleCSSClassChanges() {
4898
+ forEach(postDigestElements, function(element) {
4899
+ var data = postDigestQueue.get(element);
4900
+ if (data) {
4901
+ var existing = splitClasses(element.attr('class'));
4902
+ var toAdd = '';
4903
+ var toRemove = '';
4904
+ forEach(data, function(status, className) {
4905
+ var hasClass = !!existing[className];
4906
+ if (status !== hasClass) {
4907
+ if (status) {
4908
+ toAdd += (toAdd.length ? ' ' : '') + className;
4909
+ } else {
4910
+ toRemove += (toRemove.length ? ' ' : '') + className;
4911
+ }
4893
4912
  }
4894
4913
  });
4914
+
4915
+ forEach(element, function(elm) {
4916
+ toAdd && jqLiteAddClass(elm, toAdd);
4917
+ toRemove && jqLiteRemoveClass(elm, toRemove);
4918
+ });
4919
+ postDigestQueue.remove(element);
4895
4920
  }
4896
- return changed;
4897
- };
4921
+ });
4922
+ postDigestElements.length = 0;
4923
+ }
4898
4924
 
4899
- var classesAdded = updateData(add, true);
4900
- var classesRemoved = updateData(remove, false);
4901
- if ((!classesAdded && !classesRemoved) || postDigestElements.length > 1) return;
4902
-
4903
- $rootScope.$$postDigest(function() {
4904
- forEach(postDigestElements, function(element) {
4905
- var data = postDigestQueue.get(element);
4906
- if (data) {
4907
- var existing = splitClasses(element.attr('class'));
4908
- var toAdd = '';
4909
- var toRemove = '';
4910
- forEach(data, function(status, className) {
4911
- var hasClass = !!existing[className];
4912
- if (status !== hasClass) {
4913
- if (status) {
4914
- toAdd += (toAdd.length ? ' ' : '') + className;
4915
- } else {
4916
- toRemove += (toRemove.length ? ' ' : '') + className;
4917
- }
4918
- }
4919
- });
4920
4925
 
4921
- forEach(element, function(elm) {
4922
- toAdd && jqLiteAddClass(elm, toAdd);
4923
- toRemove && jqLiteRemoveClass(elm, toRemove);
4924
- });
4925
- postDigestQueue.remove(element);
4926
- }
4927
- });
4926
+ function addRemoveClassesPostDigest(element, add, remove) {
4927
+ var data = postDigestQueue.get(element) || {};
4928
4928
 
4929
- postDigestElements.length = 0;
4930
- });
4929
+ var classesAdded = updateData(data, add, true);
4930
+ var classesRemoved = updateData(data, remove, false);
4931
+
4932
+ if (classesAdded || classesRemoved) {
4933
+
4934
+ postDigestQueue.put(element, data);
4935
+ postDigestElements.push(element);
4936
+
4937
+ if (postDigestElements.length === 1) {
4938
+ $rootScope.$$postDigest(handleCSSClassChanges);
4939
+ }
4940
+ }
4931
4941
  }
4932
4942
  }];
4933
4943
  };
@@ -5392,14 +5402,21 @@ var $CoreAnimateCssProvider = function() {
5392
5402
  return this.getPromise().then(f1,f2);
5393
5403
  },
5394
5404
  'catch': function(f1) {
5395
- return this.getPromise().catch(f1);
5405
+ return this.getPromise()['catch'](f1);
5396
5406
  },
5397
5407
  'finally': function(f1) {
5398
- return this.getPromise().finally(f1);
5408
+ return this.getPromise()['finally'](f1);
5399
5409
  }
5400
5410
  };
5401
5411
 
5402
5412
  return function(element, options) {
5413
+ // there is no point in applying the styles since
5414
+ // there is no animation that goes on at all in
5415
+ // this version of $animateCss.
5416
+ if (options.cleanupStyles) {
5417
+ options.from = options.to = null;
5418
+ }
5419
+
5403
5420
  if (options.from) {
5404
5421
  element.css(options.from);
5405
5422
  options.from = null;
@@ -5528,7 +5545,7 @@ function Browser(window, document, $log, $sniffer) {
5528
5545
  var cachedState, lastHistoryState,
5529
5546
  lastBrowserUrl = location.href,
5530
5547
  baseElement = document.find('base'),
5531
- reloadLocation = null;
5548
+ pendingLocation = null;
5532
5549
 
5533
5550
  cacheState();
5534
5551
  lastHistoryState = cachedState;
@@ -5588,8 +5605,8 @@ function Browser(window, document, $log, $sniffer) {
5588
5605
  // Do the assignment again so that those two variables are referentially identical.
5589
5606
  lastHistoryState = cachedState;
5590
5607
  } else {
5591
- if (!sameBase || reloadLocation) {
5592
- reloadLocation = url;
5608
+ if (!sameBase || pendingLocation) {
5609
+ pendingLocation = url;
5593
5610
  }
5594
5611
  if (replace) {
5595
5612
  location.replace(url);
@@ -5598,14 +5615,18 @@ function Browser(window, document, $log, $sniffer) {
5598
5615
  } else {
5599
5616
  location.hash = getHash(url);
5600
5617
  }
5618
+ if (location.href !== url) {
5619
+ pendingLocation = url;
5620
+ }
5601
5621
  }
5602
5622
  return self;
5603
5623
  // getter
5604
5624
  } else {
5605
- // - reloadLocation is needed as browsers don't allow to read out
5606
- // the new location.href if a reload happened.
5625
+ // - pendingLocation is needed as browsers don't allow to read out
5626
+ // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5627
+ // https://openradar.appspot.com/22186109).
5607
5628
  // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5608
- return reloadLocation || location.href.replace(/%27/g,"'");
5629
+ return pendingLocation || location.href.replace(/%27/g,"'");
5609
5630
  }
5610
5631
  };
5611
5632
 
@@ -5627,6 +5648,7 @@ function Browser(window, document, $log, $sniffer) {
5627
5648
  urlChangeInit = false;
5628
5649
 
5629
5650
  function cacheStateAndFireUrlChange() {
5651
+ pendingLocation = null;
5630
5652
  cacheState();
5631
5653
  fireUrlChange();
5632
5654
  }
@@ -5862,10 +5884,10 @@ function $BrowserProvider() {
5862
5884
  $scope.keys = [];
5863
5885
  $scope.cache = $cacheFactory('cacheId');
5864
5886
  $scope.put = function(key, value) {
5865
- if ($scope.cache.get(key) === undefined) {
5887
+ if (angular.isUndefined($scope.cache.get(key))) {
5866
5888
  $scope.keys.push(key);
5867
5889
  }
5868
- $scope.cache.put(key, value === undefined ? null : value);
5890
+ $scope.cache.put(key, angular.isUndefined(value) ? null : value);
5869
5891
  };
5870
5892
  }]);
5871
5893
  </file>
@@ -6341,18 +6363,24 @@ function $TemplateCacheProvider() {
6341
6363
  * and other directives used in the directive's template will also be excluded from execution.
6342
6364
  *
6343
6365
  * #### `scope`
6344
- * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
6345
- * same element request a new scope, only one new scope is created. The new scope rule does not
6346
- * apply for the root of the template since the root of the template always gets a new scope.
6366
+ * The scope property can be `true`, an object or a falsy value:
6367
+ *
6368
+ * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6369
+ *
6370
+ * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6371
+ * the directive's element. If multiple directives on the same element request a new scope,
6372
+ * only one new scope is created. The new scope rule does not apply for the root of the template
6373
+ * since the root of the template always gets a new scope.
6347
6374
  *
6348
- * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
6349
- * normal scope in that it does not prototypically inherit from the parent scope. This is useful
6350
- * when creating reusable components, which should not accidentally read or modify data in the
6351
- * parent scope.
6375
+ * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6376
+ * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6377
+ * scope. This is useful when creating reusable components, which should not accidentally read or modify
6378
+ * data in the parent scope.
6352
6379
  *
6353
- * The 'isolate' scope takes an object hash which defines a set of local scope properties
6354
- * derived from the parent scope. These local properties are useful for aliasing values for
6355
- * templates. Locals definition is a hash of local scope property to its source:
6380
+ * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6381
+ * directive's element. These local properties are useful for aliasing values for templates. The keys in
6382
+ * the object hash map to the name of the property on the isolate scope; the values define how the property
6383
+ * is bound to the parent scope, via matching attributes on the directive's element:
6356
6384
  *
6357
6385
  * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6358
6386
  * always a string since DOM attributes are strings. If no `attr` name is specified then the
@@ -6385,6 +6413,20 @@ function $TemplateCacheProvider() {
6385
6413
  * For example, if the expression is `increment(amount)` then we can specify the amount value
6386
6414
  * by calling the `localFn` as `localFn({amount: 22})`.
6387
6415
  *
6416
+ * In general it's possible to apply more than one directive to one element, but there might be limitations
6417
+ * depending on the type of scope required by the directives. The following points will help explain these limitations.
6418
+ * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6419
+ *
6420
+ * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6421
+ * * **child scope** + **no scope** => Both directives will share one single child scope
6422
+ * * **child scope** + **child scope** => Both directives will share one single child scope
6423
+ * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
6424
+ * its parent's scope
6425
+ * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6426
+ * be applied to the same element.
6427
+ * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
6428
+ * cannot be applied to the same element.
6429
+ *
6388
6430
  *
6389
6431
  * #### `bindToController`
6390
6432
  * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
@@ -6393,7 +6435,7 @@ function $TemplateCacheProvider() {
6393
6435
  *
6394
6436
  * #### `controller`
6395
6437
  * Controller constructor function. The controller is instantiated before the
6396
- * pre-linking phase and it is shared with other directives (see
6438
+ * pre-linking phase and can be accessed by other directives (see
6397
6439
  * `require` attribute). This allows the directives to communicate with each other and augment
6398
6440
  * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6399
6441
  *
@@ -6433,9 +6475,10 @@ function $TemplateCacheProvider() {
6433
6475
  *
6434
6476
  * #### `controllerAs`
6435
6477
  * Identifier name for a reference to the controller in the directive's scope.
6436
- * This allows the controller to be referenced from the directive template. The directive
6437
- * needs to define a scope for this configuration to be used. Useful in the case when
6438
- * directive is used as component.
6478
+ * This allows the controller to be referenced from the directive template. This is especially
6479
+ * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6480
+ * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6481
+ * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6439
6482
  *
6440
6483
  *
6441
6484
  * #### `restrict`
@@ -7272,7 +7315,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7272
7315
 
7273
7316
  var node = this.$$element[0],
7274
7317
  booleanKey = getBooleanAttrName(node, key),
7275
- aliasedKey = getAliasedAttrName(node, key),
7318
+ aliasedKey = getAliasedAttrName(key),
7276
7319
  observer = key,
7277
7320
  nodeName;
7278
7321
 
@@ -7339,7 +7382,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7339
7382
  }
7340
7383
 
7341
7384
  if (writeAttr !== false) {
7342
- if (value === null || value === undefined) {
7385
+ if (value === null || isUndefined(value)) {
7343
7386
  this.$$element.removeAttr(attrName);
7344
7387
  } else {
7345
7388
  this.$$element.attr(attrName, value);
@@ -8305,7 +8348,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8305
8348
  i = 0, ii = directives.length; i < ii; i++) {
8306
8349
  try {
8307
8350
  directive = directives[i];
8308
- if ((maxPriority === undefined || maxPriority > directive.priority) &&
8351
+ if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8309
8352
  directive.restrict.indexOf(location) != -1) {
8310
8353
  if (startAttrName) {
8311
8354
  directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
@@ -8601,7 +8644,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8601
8644
  compile: function() {
8602
8645
  return {
8603
8646
  pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8604
- var $$observers = (attr.$$observers || (attr.$$observers = {}));
8647
+ var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8605
8648
 
8606
8649
  if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8607
8650
  throw $compileMinErr('nodomevents',
@@ -9653,28 +9696,18 @@ function $HttpProvider() {
9653
9696
  *
9654
9697
  *
9655
9698
  * ## General usage
9656
- * The `$http` service is a function which takes a single argument — a configuration object —
9699
+ * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object}
9657
9700
  * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
9658
9701
  *
9659
9702
  * ```js
9660
- * // Simple GET request example :
9661
- * $http.get('/someUrl').
9662
- * then(function(response) {
9663
- * // this callback will be called asynchronously
9664
- * // when the response is available
9665
- * }, function(response) {
9666
- * // called asynchronously if an error occurs
9667
- * // or server returns response with an error status.
9668
- * });
9669
- * ```
9670
- *
9671
- * ```js
9672
- * // Simple POST request example (passing data) :
9673
- * $http.post('/someUrl', {msg:'hello word!'}).
9674
- * then(function(response) {
9703
+ * // Simple GET request example:
9704
+ * $http({
9705
+ * method: 'GET',
9706
+ * url: '/someUrl'
9707
+ * }).then(function successCallback(response) {
9675
9708
  * // this callback will be called asynchronously
9676
9709
  * // when the response is available
9677
- * }, function(response) {
9710
+ * }, function errorCallback(response) {
9678
9711
  * // called asynchronously if an error occurs
9679
9712
  * // or server returns response with an error status.
9680
9713
  * });
@@ -9694,25 +9727,16 @@ function $HttpProvider() {
9694
9727
  * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9695
9728
  * called for such responses.
9696
9729
  *
9697
- * ## Writing Unit Tests that use $http
9698
- * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9699
- * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9700
- * request using trained responses.
9701
- *
9702
- * ```
9703
- * $httpBackend.expectGET(...);
9704
- * $http.get(...);
9705
- * $httpBackend.flush();
9706
- * ```
9707
9730
  *
9708
9731
  * ## Shortcut methods
9709
9732
  *
9710
9733
  * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9711
- * request data must be passed in for POST/PUT requests.
9734
+ * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9735
+ * last argument.
9712
9736
  *
9713
9737
  * ```js
9714
- * $http.get('/someUrl').then(successCallback);
9715
- * $http.post('/someUrl', data).then(successCallback);
9738
+ * $http.get('/someUrl', config).then(successCallback, errorCallback);
9739
+ * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9716
9740
  * ```
9717
9741
  *
9718
9742
  * Complete list of shortcut methods:
@@ -9726,6 +9750,17 @@ function $HttpProvider() {
9726
9750
  * - {@link ng.$http#patch $http.patch}
9727
9751
  *
9728
9752
  *
9753
+ * ## Writing Unit Tests that use $http
9754
+ * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9755
+ * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9756
+ * request using trained responses.
9757
+ *
9758
+ * ```
9759
+ * $httpBackend.expectGET(...);
9760
+ * $http.get(...);
9761
+ * $httpBackend.flush();
9762
+ * ```
9763
+ *
9729
9764
  * ## Deprecation Notice
9730
9765
  * <div class="alert alert-danger">
9731
9766
  * The `$http` legacy promise methods `success` and `error` have been deprecated.
@@ -9883,7 +9918,7 @@ function $HttpProvider() {
9883
9918
  *
9884
9919
  * There are two kinds of interceptors (and two kinds of rejection interceptors):
9885
9920
  *
9886
- * * `request`: interceptors get called with a http `config` object. The function is free to
9921
+ * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
9887
9922
  * modify the `config` object or create a new one. The function needs to return the `config`
9888
9923
  * object directly, or a promise containing the `config` or a new `config` object.
9889
9924
  * * `requestError`: interceptor gets called when a previous interceptor threw an error or
@@ -10517,8 +10552,8 @@ function $HttpProvider() {
10517
10552
  * Resolves the raw $http promise.
10518
10553
  */
10519
10554
  function resolvePromise(response, status, headers, statusText) {
10520
- // normalize internal statuses to 0
10521
- status = Math.max(status, 0);
10555
+ //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10556
+ status = status >= -1 ? status : 0;
10522
10557
 
10523
10558
  (isSuccess(status) ? deferred.resolve : deferred.reject)({
10524
10559
  data: response,
@@ -10549,8 +10584,33 @@ function $HttpProvider() {
10549
10584
  }];
10550
10585
  }
10551
10586
 
10552
- function createXhr() {
10553
- return new window.XMLHttpRequest();
10587
+ /**
10588
+ * @ngdoc service
10589
+ * @name $xhrFactory
10590
+ *
10591
+ * @description
10592
+ * Factory function used to create XMLHttpRequest objects.
10593
+ *
10594
+ * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10595
+ *
10596
+ * ```
10597
+ * angular.module('myApp', [])
10598
+ * .factory('$xhrFactory', function() {
10599
+ * return function createXhr(method, url) {
10600
+ * return new window.XMLHttpRequest({mozSystem: true});
10601
+ * };
10602
+ * });
10603
+ * ```
10604
+ *
10605
+ * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10606
+ * @param {string} url URL of the request.
10607
+ */
10608
+ function $xhrFactoryProvider() {
10609
+ this.$get = function() {
10610
+ return function createXhr() {
10611
+ return new window.XMLHttpRequest();
10612
+ };
10613
+ };
10554
10614
  }
10555
10615
 
10556
10616
  /**
@@ -10558,6 +10618,7 @@ function createXhr() {
10558
10618
  * @name $httpBackend
10559
10619
  * @requires $window
10560
10620
  * @requires $document
10621
+ * @requires $xhrFactory
10561
10622
  *
10562
10623
  * @description
10563
10624
  * HTTP backend used by the {@link ng.$http service} that delegates to
@@ -10570,8 +10631,8 @@ function createXhr() {
10570
10631
  * $httpBackend} which can be trained with responses.
10571
10632
  */
10572
10633
  function $HttpBackendProvider() {
10573
- this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
10574
- return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
10634
+ this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10635
+ return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10575
10636
  }];
10576
10637
  }
10577
10638
 
@@ -10595,7 +10656,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
10595
10656
  });
10596
10657
  } else {
10597
10658
 
10598
- var xhr = createXhr();
10659
+ var xhr = createXhr(method, url);
10599
10660
 
10600
10661
  xhr.open(method, url, true);
10601
10662
  forEach(headers, function(value, key) {
@@ -10658,7 +10719,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
10658
10719
  }
10659
10720
  }
10660
10721
 
10661
- xhr.send(post);
10722
+ xhr.send(isUndefined(post) ? null : post);
10662
10723
  }
10663
10724
 
10664
10725
  if (timeout > 0) {
@@ -10675,7 +10736,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
10675
10736
 
10676
10737
  function completeRequest(callback, status, response, headersString, statusText) {
10677
10738
  // cancel timeout and subsequent timeout promise resolution
10678
- if (timeoutId !== undefined) {
10739
+ if (isDefined(timeoutId)) {
10679
10740
  $browserDefer.cancel(timeoutId);
10680
10741
  }
10681
10742
  jsonpDone = xhr = null;
@@ -10861,7 +10922,7 @@ function $InterpolateProvider() {
10861
10922
  * ```js
10862
10923
  * var $interpolate = ...; // injected
10863
10924
  * var exp = $interpolate('Hello {{name | uppercase}}!');
10864
- * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
10925
+ * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
10865
10926
  * ```
10866
10927
  *
10867
10928
  * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
@@ -11414,14 +11475,14 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11414
11475
  var appUrl, prevAppUrl;
11415
11476
  var rewrittenUrl;
11416
11477
 
11417
- if ((appUrl = beginsWith(appBase, url)) !== undefined) {
11478
+ if (isDefined(appUrl = beginsWith(appBase, url))) {
11418
11479
  prevAppUrl = appUrl;
11419
- if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {
11480
+ if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11420
11481
  rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11421
11482
  } else {
11422
11483
  rewrittenUrl = appBase + prevAppUrl;
11423
11484
  }
11424
- } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {
11485
+ } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11425
11486
  rewrittenUrl = appBaseNoFile + appUrl;
11426
11487
  } else if (appBaseNoFile == url + '/') {
11427
11488
  rewrittenUrl = appBaseNoFile;
@@ -12474,6 +12535,25 @@ function ensureSafeMemberName(name, fullExpression) {
12474
12535
  return name;
12475
12536
  }
12476
12537
 
12538
+ function getStringValue(name, fullExpression) {
12539
+ // From the JavaScript docs:
12540
+ // Property names must be strings. This means that non-string objects cannot be used
12541
+ // as keys in an object. Any non-string object, including a number, is typecasted
12542
+ // into a string via the toString method.
12543
+ //
12544
+ // So, to ensure that we are checking the same `name` that JavaScript would use,
12545
+ // we cast it to a string, if possible.
12546
+ // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12547
+ // this is, this will handle objects that misbehave.
12548
+ name = name + '';
12549
+ if (!isString(name)) {
12550
+ throw $parseMinErr('iseccst',
12551
+ 'Cannot convert object to primitive value! '
12552
+ + 'Expression: {0}', fullExpression);
12553
+ }
12554
+ return name;
12555
+ }
12556
+
12477
12557
  function ensureSafeObject(obj, fullExpression) {
12478
12558
  // nifty check if obj is Function that is fast and works across iframes and other contexts
12479
12559
  if (obj) {
@@ -12519,6 +12599,16 @@ function ensureSafeFunction(obj, fullExpression) {
12519
12599
  }
12520
12600
  }
12521
12601
 
12602
+ function ensureSafeAssignContext(obj, fullExpression) {
12603
+ if (obj) {
12604
+ if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12605
+ obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12606
+ throw $parseMinErr('isecaf',
12607
+ 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12608
+ }
12609
+ }
12610
+ }
12611
+
12522
12612
  var OPERATORS = createMap();
12523
12613
  forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12524
12614
  var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
@@ -13200,6 +13290,7 @@ ASTCompiler.prototype = {
13200
13290
  this.state.computing = 'assign';
13201
13291
  var result = this.nextId();
13202
13292
  this.recurse(assignable, result);
13293
+ this.return_(result);
13203
13294
  extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13204
13295
  }
13205
13296
  var toWatch = getInputs(ast.body);
@@ -13232,6 +13323,8 @@ ASTCompiler.prototype = {
13232
13323
  'ensureSafeMemberName',
13233
13324
  'ensureSafeObject',
13234
13325
  'ensureSafeFunction',
13326
+ 'getStringValue',
13327
+ 'ensureSafeAssignContext',
13235
13328
  'ifDefined',
13236
13329
  'plus',
13237
13330
  'text',
@@ -13240,6 +13333,8 @@ ASTCompiler.prototype = {
13240
13333
  ensureSafeMemberName,
13241
13334
  ensureSafeObject,
13242
13335
  ensureSafeFunction,
13336
+ getStringValue,
13337
+ ensureSafeAssignContext,
13243
13338
  ifDefined,
13244
13339
  plusFn,
13245
13340
  expression);
@@ -13383,6 +13478,7 @@ ASTCompiler.prototype = {
13383
13478
  if (ast.computed) {
13384
13479
  right = self.nextId();
13385
13480
  self.recurse(ast.property, right);
13481
+ self.getStringValue(right);
13386
13482
  self.addEnsureSafeMemberName(right);
13387
13483
  if (create && create !== 1) {
13388
13484
  self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
@@ -13466,6 +13562,7 @@ ASTCompiler.prototype = {
13466
13562
  self.if_(self.notNull(left.context), function() {
13467
13563
  self.recurse(ast.right, right);
13468
13564
  self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13565
+ self.addEnsureSafeAssignContext(left.context);
13469
13566
  expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13470
13567
  self.assign(intoId, expression);
13471
13568
  recursionFn(intoId || expression);
@@ -13591,6 +13688,10 @@ ASTCompiler.prototype = {
13591
13688
  this.current().body.push(this.ensureSafeFunction(item), ';');
13592
13689
  },
13593
13690
 
13691
+ addEnsureSafeAssignContext: function(item) {
13692
+ this.current().body.push(this.ensureSafeAssignContext(item), ';');
13693
+ },
13694
+
13594
13695
  ensureSafeObject: function(item) {
13595
13696
  return 'ensureSafeObject(' + item + ',text)';
13596
13697
  },
@@ -13603,6 +13704,14 @@ ASTCompiler.prototype = {
13603
13704
  return 'ensureSafeFunction(' + item + ',text)';
13604
13705
  },
13605
13706
 
13707
+ getStringValue: function(item) {
13708
+ this.assign(item, 'getStringValue(' + item + ',text)');
13709
+ },
13710
+
13711
+ ensureSafeAssignContext: function(item) {
13712
+ return 'ensureSafeAssignContext(' + item + ',text)';
13713
+ },
13714
+
13606
13715
  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13607
13716
  var self = this;
13608
13717
  return function() {
@@ -13780,6 +13889,7 @@ ASTInterpreter.prototype = {
13780
13889
  var lhs = left(scope, locals, assign, inputs);
13781
13890
  var rhs = right(scope, locals, assign, inputs);
13782
13891
  ensureSafeObject(lhs.value, self.expression);
13892
+ ensureSafeAssignContext(lhs.context);
13783
13893
  lhs.context[lhs.name] = rhs;
13784
13894
  return context ? {value: rhs} : rhs;
13785
13895
  };
@@ -13977,6 +14087,7 @@ ASTInterpreter.prototype = {
13977
14087
  var value;
13978
14088
  if (lhs != null) {
13979
14089
  rhs = right(scope, locals, assign, inputs);
14090
+ rhs = getStringValue(rhs);
13980
14091
  ensureSafeMemberName(rhs, expression);
13981
14092
  if (create && create !== 1 && lhs && !(lhs[rhs])) {
13982
14093
  lhs[rhs] = {};
@@ -14917,7 +15028,7 @@ function $$RAFProvider() { //rAF
14917
15028
  $window.webkitCancelRequestAnimationFrame;
14918
15029
 
14919
15030
  var rafSupported = !!requestAnimationFrame;
14920
- var rafFn = rafSupported
15031
+ var raf = rafSupported
14921
15032
  ? function(fn) {
14922
15033
  var id = requestAnimationFrame(fn);
14923
15034
  return function() {
@@ -14931,47 +15042,9 @@ function $$RAFProvider() { //rAF
14931
15042
  };
14932
15043
  };
14933
15044
 
14934
- queueFn.supported = rafSupported;
14935
-
14936
- var cancelLastRAF;
14937
- var taskCount = 0;
14938
- var taskQueue = [];
14939
- return queueFn;
14940
-
14941
- function flush() {
14942
- for (var i = 0; i < taskQueue.length; i++) {
14943
- var task = taskQueue[i];
14944
- if (task) {
14945
- taskQueue[i] = null;
14946
- task();
14947
- }
14948
- }
14949
- taskCount = taskQueue.length = 0;
14950
- }
14951
-
14952
- function queueFn(asyncFn) {
14953
- var index = taskQueue.length;
15045
+ raf.supported = rafSupported;
14954
15046
 
14955
- taskCount++;
14956
- taskQueue.push(asyncFn);
14957
-
14958
- if (index === 0) {
14959
- cancelLastRAF = rafFn(flush);
14960
- }
14961
-
14962
- return function cancelQueueFn() {
14963
- if (index >= 0) {
14964
- taskQueue[index] = null;
14965
- index = null;
14966
-
14967
- if (--taskCount === 0 && cancelLastRAF) {
14968
- cancelLastRAF();
14969
- cancelLastRAF = null;
14970
- taskQueue.length = 0;
14971
- }
14972
- }
14973
- };
14974
- }
15047
+ return raf;
14975
15048
  }];
14976
15049
  }
14977
15050
 
@@ -15228,10 +15301,10 @@ function $RootScopeProvider() {
15228
15301
  * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15229
15302
  *
15230
15303
  * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15231
- * $digest()} and should return the value that will be watched. (Since
15232
- * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
15233
- * `watchExpression` can execute multiple times per
15234
- * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
15304
+ * $digest()} and should return the value that will be watched. (`watchExpression` should not change
15305
+ * its value when executed multiple times with the same input because it may be executed multiple
15306
+ * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15307
+ * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15235
15308
  * - The `listener` is called only when the value from the current `watchExpression` and the
15236
15309
  * previous call to `watchExpression` are not equal (with the exception of the initial run,
15237
15310
  * see below). Inequality is determined according to reference inequality,
@@ -15580,7 +15653,7 @@ function $RootScopeProvider() {
15580
15653
  // copy the items to oldValue and look for changes.
15581
15654
  newLength = 0;
15582
15655
  for (key in newValue) {
15583
- if (newValue.hasOwnProperty(key)) {
15656
+ if (hasOwnProperty.call(newValue, key)) {
15584
15657
  newLength++;
15585
15658
  newItem = newValue[key];
15586
15659
  oldItem = oldValue[key];
@@ -15602,7 +15675,7 @@ function $RootScopeProvider() {
15602
15675
  // we used to have more keys, need to find them and destroy them.
15603
15676
  changeDetected++;
15604
15677
  for (key in oldValue) {
15605
- if (!newValue.hasOwnProperty(key)) {
15678
+ if (!hasOwnProperty.call(newValue, key)) {
15606
15679
  oldLength--;
15607
15680
  delete oldValue[key];
15608
15681
  }
@@ -16687,7 +16760,7 @@ function $SceDelegateProvider() {
16687
16760
  'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16688
16761
  type, trustedValue);
16689
16762
  }
16690
- if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
16763
+ if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
16691
16764
  return trustedValue;
16692
16765
  }
16693
16766
  // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
@@ -16742,7 +16815,7 @@ function $SceDelegateProvider() {
16742
16815
  * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
16743
16816
  */
16744
16817
  function getTrusted(type, maybeTrusted) {
16745
- if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
16818
+ if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
16746
16819
  return maybeTrusted;
16747
16820
  }
16748
16821
  var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
@@ -18003,7 +18076,7 @@ function $$CookieReader($document) {
18003
18076
  // the first value that is seen for a cookie is the most
18004
18077
  // specific one. values for the same cookie name that
18005
18078
  // follow are for less specific paths.
18006
- if (lastCookies[name] === undefined) {
18079
+ if (isUndefined(lastCookies[name])) {
18007
18080
  lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18008
18081
  }
18009
18082
  }
@@ -18651,6 +18724,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18651
18724
  if (fractionSize > 0 && number < 1) {
18652
18725
  formatedText = number.toFixed(fractionSize);
18653
18726
  number = parseFloat(formatedText);
18727
+ formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
18654
18728
  }
18655
18729
  }
18656
18730
 
@@ -19949,6 +20023,7 @@ function nullFormRenameControl(control, name) {
19949
20023
  * @property {boolean} $dirty True if user has already interacted with the form.
19950
20024
  * @property {boolean} $valid True if all of the containing forms and controls are valid.
19951
20025
  * @property {boolean} $invalid True if at least one containing control or form is invalid.
20026
+ * @property {boolean} $pending True if at least one containing control or form is pending.
19952
20027
  * @property {boolean} $submitted True if user has submitted the form even if its invalid.
19953
20028
  *
19954
20029
  * @property {Object} $error Is an object hash, containing references to controls or
@@ -19988,8 +20063,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
19988
20063
  var form = this,
19989
20064
  controls = [];
19990
20065
 
19991
- var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;
19992
-
19993
20066
  // init state
19994
20067
  form.$error = {};
19995
20068
  form.$$success = {};
@@ -20000,8 +20073,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20000
20073
  form.$valid = true;
20001
20074
  form.$invalid = false;
20002
20075
  form.$submitted = false;
20003
-
20004
- parentForm.$addControl(form);
20076
+ form.$$parentForm = nullFormCtrl;
20005
20077
 
20006
20078
  /**
20007
20079
  * @ngdoc method
@@ -20040,11 +20112,23 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20040
20112
  /**
20041
20113
  * @ngdoc method
20042
20114
  * @name form.FormController#$addControl
20115
+ * @param {object} control control object, either a {@link form.FormController} or an
20116
+ * {@link ngModel.NgModelController}
20043
20117
  *
20044
20118
  * @description
20045
- * Register a control with the form.
20119
+ * Register a control with the form. Input elements using ngModelController do this automatically
20120
+ * when they are linked.
20046
20121
  *
20047
- * Input elements using ngModelController do this automatically when they are linked.
20122
+ * Note that the current state of the control will not be reflected on the new parent form. This
20123
+ * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20124
+ * state.
20125
+ *
20126
+ * However, if the method is used programmatically, for example by adding dynamically created controls,
20127
+ * or controls that have been previously removed without destroying their corresponding DOM element,
20128
+ * it's the developers responsiblity to make sure the current state propagates to the parent form.
20129
+ *
20130
+ * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20131
+ * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20048
20132
  */
20049
20133
  form.$addControl = function(control) {
20050
20134
  // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
@@ -20055,6 +20139,8 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20055
20139
  if (control.$name) {
20056
20140
  form[control.$name] = control;
20057
20141
  }
20142
+
20143
+ control.$$parentForm = form;
20058
20144
  };
20059
20145
 
20060
20146
  // Private API: rename a form control
@@ -20071,11 +20157,18 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20071
20157
  /**
20072
20158
  * @ngdoc method
20073
20159
  * @name form.FormController#$removeControl
20160
+ * @param {object} control control object, either a {@link form.FormController} or an
20161
+ * {@link ngModel.NgModelController}
20074
20162
  *
20075
20163
  * @description
20076
20164
  * Deregister a control from the form.
20077
20165
  *
20078
20166
  * Input elements using ngModelController do this automatically when they are destroyed.
20167
+ *
20168
+ * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20169
+ * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20170
+ * different from case to case. For example, removing the only `$dirty` control from a form may or
20171
+ * may not mean that the form is still `$dirty`.
20079
20172
  */
20080
20173
  form.$removeControl = function(control) {
20081
20174
  if (control.$name && form[control.$name] === control) {
@@ -20092,6 +20185,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20092
20185
  });
20093
20186
 
20094
20187
  arrayRemove(controls, control);
20188
+ control.$$parentForm = nullFormCtrl;
20095
20189
  };
20096
20190
 
20097
20191
 
@@ -20128,7 +20222,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20128
20222
  delete object[property];
20129
20223
  }
20130
20224
  },
20131
- parentForm: parentForm,
20132
20225
  $animate: $animate
20133
20226
  });
20134
20227
 
@@ -20147,7 +20240,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20147
20240
  $animate.addClass(element, DIRTY_CLASS);
20148
20241
  form.$dirty = true;
20149
20242
  form.$pristine = false;
20150
- parentForm.$setDirty();
20243
+ form.$$parentForm.$setDirty();
20151
20244
  };
20152
20245
 
20153
20246
  /**
@@ -20203,7 +20296,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20203
20296
  form.$setSubmitted = function() {
20204
20297
  $animate.addClass(element, SUBMITTED_CLASS);
20205
20298
  form.$submitted = true;
20206
- parentForm.$setSubmitted();
20299
+ form.$$parentForm.$setSubmitted();
20207
20300
  };
20208
20301
  }
20209
20302
 
@@ -20253,6 +20346,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20253
20346
  * # CSS classes
20254
20347
  * - `ng-valid` is set if the form is valid.
20255
20348
  * - `ng-invalid` is set if the form is invalid.
20349
+ * - `ng-pending` is set if the form is pending.
20256
20350
  * - `ng-pristine` is set if the form is pristine.
20257
20351
  * - `ng-dirty` is set if the form is dirty.
20258
20352
  * - `ng-submitted` is set if the form was submitted.
@@ -20328,7 +20422,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
20328
20422
  </script>
20329
20423
  <style>
20330
20424
  .my-form {
20331
- -webkit-transition:all linear 0.5s;
20332
20425
  transition:all linear 0.5s;
20333
20426
  background: transparent;
20334
20427
  }
@@ -20377,6 +20470,7 @@ var formDirectiveFactory = function(isNgForm) {
20377
20470
  var formDirective = {
20378
20471
  name: 'form',
20379
20472
  restrict: isNgForm ? 'EAC' : 'E',
20473
+ require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20380
20474
  controller: FormController,
20381
20475
  compile: function ngFormCompile(formElement, attr) {
20382
20476
  // Setup initial state of the control
@@ -20385,7 +20479,9 @@ var formDirectiveFactory = function(isNgForm) {
20385
20479
  var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20386
20480
 
20387
20481
  return {
20388
- pre: function ngFormPreLink(scope, formElement, attr, controller) {
20482
+ pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20483
+ var controller = ctrls[0];
20484
+
20389
20485
  // if `action` attr is not present on the form, prevent the default action (submission)
20390
20486
  if (!('action' in attr)) {
20391
20487
  // we can't use jq events because if a form is destroyed during submission the default
@@ -20414,7 +20510,9 @@ var formDirectiveFactory = function(isNgForm) {
20414
20510
  });
20415
20511
  }
20416
20512
 
20417
- var parentFormCtrl = controller.$$parentForm;
20513
+ var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20514
+ parentFormCtrl.$addControl(controller);
20515
+
20418
20516
  var setter = nameAttr ? getSetter(controller.$name) : noop;
20419
20517
 
20420
20518
  if (nameAttr) {
@@ -20422,13 +20520,13 @@ var formDirectiveFactory = function(isNgForm) {
20422
20520
  attr.$observe(nameAttr, function(newValue) {
20423
20521
  if (controller.$name === newValue) return;
20424
20522
  setter(scope, undefined);
20425
- parentFormCtrl.$$renameControl(controller, newValue);
20523
+ controller.$$parentForm.$$renameControl(controller, newValue);
20426
20524
  setter = getSetter(controller.$name);
20427
20525
  setter(scope, controller);
20428
20526
  });
20429
20527
  }
20430
20528
  formElement.on('$destroy', function() {
20431
- parentFormCtrl.$removeControl(controller);
20529
+ controller.$$parentForm.$removeControl(controller);
20432
20530
  setter(scope, undefined);
20433
20531
  extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20434
20532
  });
@@ -20590,9 +20688,17 @@ var inputType = {
20590
20688
  * @param {string} ngModel Assignable angular expression to data-bind to.
20591
20689
  * @param {string=} name Property name of the form under which the control is published.
20592
20690
  * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20593
- * valid ISO date string (yyyy-MM-dd).
20691
+ * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20692
+ * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
20693
+ * constraint validation.
20594
20694
  * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20595
- * a valid ISO date string (yyyy-MM-dd).
20695
+ * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20696
+ * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
20697
+ * constraint validation.
20698
+ * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
20699
+ * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20700
+ * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
20701
+ * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20596
20702
  * @param {string=} required Sets `required` validation error key if the value is not entered.
20597
20703
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20598
20704
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -20684,10 +20790,18 @@ var inputType = {
20684
20790
  *
20685
20791
  * @param {string} ngModel Assignable angular expression to data-bind to.
20686
20792
  * @param {string=} name Property name of the form under which the control is published.
20687
- * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20688
- * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
20689
- * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20690
- * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
20793
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20794
+ * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20795
+ * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20796
+ * Note that `min` will also add native HTML5 constraint validation.
20797
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20798
+ * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20799
+ * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20800
+ * Note that `max` will also add native HTML5 constraint validation.
20801
+ * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
20802
+ * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20803
+ * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
20804
+ * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20691
20805
  * @param {string=} required Sets `required` validation error key if the value is not entered.
20692
20806
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20693
20807
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -20780,10 +20894,18 @@ var inputType = {
20780
20894
  *
20781
20895
  * @param {string} ngModel Assignable angular expression to data-bind to.
20782
20896
  * @param {string=} name Property name of the form under which the control is published.
20783
- * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20784
- * valid ISO time format (HH:mm:ss).
20785
- * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a
20786
- * valid ISO time format (HH:mm:ss).
20897
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20898
+ * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
20899
+ * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
20900
+ * native HTML5 constraint validation.
20901
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20902
+ * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
20903
+ * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
20904
+ * native HTML5 constraint validation.
20905
+ * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
20906
+ * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20907
+ * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
20908
+ * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20787
20909
  * @param {string=} required Sets `required` validation error key if the value is not entered.
20788
20910
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20789
20911
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -20875,10 +20997,18 @@ var inputType = {
20875
20997
  *
20876
20998
  * @param {string} ngModel Assignable angular expression to data-bind to.
20877
20999
  * @param {string=} name Property name of the form under which the control is published.
20878
- * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20879
- * valid ISO week format (yyyy-W##).
20880
- * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20881
- * a valid ISO week format (yyyy-W##).
21000
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21001
+ * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21002
+ * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21003
+ * native HTML5 constraint validation.
21004
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21005
+ * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21006
+ * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21007
+ * native HTML5 constraint validation.
21008
+ * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21009
+ * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21010
+ * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21011
+ * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20882
21012
  * @param {string=} required Sets `required` validation error key if the value is not entered.
20883
21013
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20884
21014
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -20972,10 +21102,19 @@ var inputType = {
20972
21102
  *
20973
21103
  * @param {string} ngModel Assignable angular expression to data-bind to.
20974
21104
  * @param {string=} name Property name of the form under which the control is published.
20975
- * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
20976
- * a valid ISO month format (yyyy-MM).
20977
- * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must
20978
- * be a valid ISO month format (yyyy-MM).
21105
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21106
+ * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21107
+ * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21108
+ * native HTML5 constraint validation.
21109
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21110
+ * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21111
+ * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21112
+ * native HTML5 constraint validation.
21113
+ * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21114
+ * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21115
+ * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21116
+ * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21117
+
20979
21118
  * @param {string=} required Sets `required` validation error key if the value is not entered.
20980
21119
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20981
21120
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -21737,7 +21876,7 @@ function createDateInputType(type, regexp, parseDate, format) {
21737
21876
  }
21738
21877
 
21739
21878
  function parseObservedDateValue(val) {
21740
- return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
21879
+ return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
21741
21880
  }
21742
21881
  };
21743
21882
  }
@@ -22232,7 +22371,7 @@ var ngBindDirective = ['$compile', function($compile) {
22232
22371
  $compile.$$addBindingInfo(element, attr.ngBind);
22233
22372
  element = element[0];
22234
22373
  scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22235
- element.textContent = value === undefined ? '' : value;
22374
+ element.textContent = isUndefined(value) ? '' : value;
22236
22375
  });
22237
22376
  };
22238
22377
  }
@@ -22300,7 +22439,7 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate
22300
22439
  $compile.$$addBindingInfo(element, interpolateFn.expressions);
22301
22440
  element = element[0];
22302
22441
  attr.$observe('ngBindTemplate', function(value) {
22303
- element.textContent = value === undefined ? '' : value;
22442
+ element.textContent = isUndefined(value) ? '' : value;
22304
22443
  });
22305
22444
  };
22306
22445
  }
@@ -22717,7 +22856,6 @@ function classDirective(name, selector) {
22717
22856
  </file>
22718
22857
  <file name="style.css">
22719
22858
  .base-class {
22720
- -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22721
22859
  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22722
22860
  }
22723
22861
 
@@ -23892,7 +24030,6 @@ forEach(
23892
24030
  }
23893
24031
 
23894
24032
  .animate-if.ng-enter, .animate-if.ng-leave {
23895
- -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23896
24033
  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23897
24034
  }
23898
24035
 
@@ -24041,7 +24178,6 @@ var ngIfDirective = ['$animate', function($animate) {
24041
24178
  }
24042
24179
 
24043
24180
  .slide-animate.ng-enter, .slide-animate.ng-leave {
24044
- -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24045
24181
  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24046
24182
 
24047
24183
  position:absolute;
@@ -24260,16 +24396,18 @@ var ngIncludeFillContentDirective = ['$compile',
24260
24396
  * current scope.
24261
24397
  *
24262
24398
  * <div class="alert alert-danger">
24263
- * The only appropriate use of `ngInit` is for aliasing special properties of
24264
- * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
24265
- * should use {@link guide/controller controllers} rather than `ngInit`
24266
- * to initialize values on a scope.
24399
+ * This directive can be abused to add unnecessary amounts of logic into your templates.
24400
+ * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24401
+ * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24402
+ * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24403
+ * rather than `ngInit` to initialize values on a scope.
24267
24404
  * </div>
24405
+ *
24268
24406
  * <div class="alert alert-warning">
24269
- * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
24270
- * sure you have parenthesis for correct precedence:
24407
+ * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24408
+ * sure you have parentheses to ensure correct operator precedence:
24271
24409
  * <pre class="prettyprint">
24272
- * `<div ng-init="test1 = (data | orderBy:'name')"></div>`
24410
+ * `<div ng-init="test1 = ($index | toString)"></div>`
24273
24411
  * </pre>
24274
24412
  * </div>
24275
24413
  *
@@ -24382,7 +24520,7 @@ var ngInitDirective = ngDirective({
24382
24520
  * </file>
24383
24521
  * </example>
24384
24522
  *
24385
- * ### Example - splitting on whitespace
24523
+ * ### Example - splitting on newline
24386
24524
  * <example name="ngList-directive-newlines">
24387
24525
  * <file name="index.html">
24388
24526
  * <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
@@ -24467,7 +24605,9 @@ var ngModelMinErr = minErr('ngModel');
24467
24605
  * @ngdoc type
24468
24606
  * @name ngModel.NgModelController
24469
24607
  *
24470
- * @property {string} $viewValue Actual string value in the view.
24608
+ * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
24609
+ * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
24610
+ * is set.
24471
24611
  * @property {*} $modelValue The value in the model that the control is bound to.
24472
24612
  * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24473
24613
  the control reads value from the DOM. The functions are called in array order, each passing
@@ -24681,7 +24821,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
24681
24821
  this.$$success = {}; // keep valid keys here
24682
24822
  this.$pending = undefined; // keep pending keys here
24683
24823
  this.$name = $interpolate($attr.name || '', false)($scope);
24684
-
24824
+ this.$$parentForm = nullFormCtrl;
24685
24825
 
24686
24826
  var parsedNgModel = $parse($attr.ngModel),
24687
24827
  parsedNgModelAssign = parsedNgModel.assign,
@@ -24761,8 +24901,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
24761
24901
  return isUndefined(value) || value === '' || value === null || value !== value;
24762
24902
  };
24763
24903
 
24764
- var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
24765
- currentValidationRunId = 0;
24904
+ var currentValidationRunId = 0;
24766
24905
 
24767
24906
  /**
24768
24907
  * @ngdoc method
@@ -24795,7 +24934,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
24795
24934
  unset: function(object, property) {
24796
24935
  delete object[property];
24797
24936
  },
24798
- parentForm: parentForm,
24799
24937
  $animate: $animate
24800
24938
  });
24801
24939
 
@@ -24833,7 +24971,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
24833
24971
  ctrl.$pristine = false;
24834
24972
  $animate.removeClass($element, PRISTINE_CLASS);
24835
24973
  $animate.addClass($element, DIRTY_CLASS);
24836
- parentForm.$setDirty();
24974
+ ctrl.$$parentForm.$setDirty();
24837
24975
  };
24838
24976
 
24839
24977
  /**
@@ -25003,7 +25141,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
25003
25141
 
25004
25142
  function processParseErrors() {
25005
25143
  var errorKey = ctrl.$$parserName || 'parse';
25006
- if (parserValid === undefined) {
25144
+ if (isUndefined(parserValid)) {
25007
25145
  setValidity(errorKey, null);
25008
25146
  } else {
25009
25147
  if (!parserValid) {
@@ -25173,37 +25311,47 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
25173
25311
  * @description
25174
25312
  * Update the view value.
25175
25313
  *
25176
- * This method should be called when an input directive want to change the view value; typically,
25177
- * this is done from within a DOM event handler.
25178
- *
25179
- * For example {@link ng.directive:input input} calls it when the value of the input changes and
25180
- * {@link ng.directive:select select} calls it when an option is selected.
25181
- *
25182
- * If the new `value` is an object (rather than a string or a number), we should make a copy of the
25183
- * object before passing it to `$setViewValue`. This is because `ngModel` does not perform a deep
25184
- * watch of objects, it only looks for a change of identity. If you only change the property of
25185
- * the object then ngModel will not realise that the object has changed and will not invoke the
25186
- * `$parsers` and `$validators` pipelines.
25187
- *
25188
- * For this reason, you should not change properties of the copy once it has been passed to
25189
- * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly.
25314
+ * This method should be called when a control wants to change the view value; typically,
25315
+ * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25316
+ * directive calls it when the value of the input changes and {@link ng.directive:select select}
25317
+ * calls it when an option is selected.
25190
25318
  *
25191
- * When this method is called, the new `value` will be staged for committing through the `$parsers`
25319
+ * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25192
25320
  * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25193
25321
  * value sent directly for processing, finally to be applied to `$modelValue` and then the
25194
- * **expression** specified in the `ng-model` attribute.
25195
- *
25196
- * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
25322
+ * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25323
+ * in the `$viewChangeListeners` list, are called.
25197
25324
  *
25198
25325
  * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25199
25326
  * and the `default` trigger is not listed, all those actions will remain pending until one of the
25200
25327
  * `updateOn` events is triggered on the DOM element.
25201
25328
  * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25202
25329
  * directive is used with a custom debounce for this particular event.
25330
+ * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25331
+ * is specified, once the timer runs out.
25332
+ *
25333
+ * When used with standard inputs, the view value will always be a string (which is in some cases
25334
+ * parsed into another type, such as a `Date` object for `input[date]`.)
25335
+ * However, custom controls might also pass objects to this method. In this case, we should make
25336
+ * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25337
+ * perform a deep watch of objects, it only looks for a change of identity. If you only change
25338
+ * the property of the object then ngModel will not realise that the object has changed and
25339
+ * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25340
+ * not change properties of the copy once it has been passed to `$setViewValue`.
25341
+ * Otherwise you may cause the model value on the scope to change incorrectly.
25342
+ *
25343
+ * <div class="alert alert-info">
25344
+ * In any case, the value passed to the method should always reflect the current value
25345
+ * of the control. For example, if you are calling `$setViewValue` for an input element,
25346
+ * you should pass the input DOM value. Otherwise, the control and the scope model become
25347
+ * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25348
+ * the control's DOM value in any way. If we want to change the control's DOM value
25349
+ * programmatically, we should update the `ngModel` scope expression. Its new value will be
25350
+ * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25351
+ * to update the DOM, and finally call `$validate` on it.
25352
+ * </div>
25203
25353
  *
25204
- * Note that calling this function does not trigger a `$digest`.
25205
- *
25206
- * @param {string} value Value from the view.
25354
+ * @param {*} value value from the view.
25207
25355
  * @param {string} trigger Event that triggered the update.
25208
25356
  */
25209
25357
  this.$setViewValue = function(value, trigger) {
@@ -25380,7 +25528,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
25380
25528
  </script>
25381
25529
  <style>
25382
25530
  .my-input {
25383
- -webkit-transition:all linear 0.5s;
25384
25531
  transition:all linear 0.5s;
25385
25532
  background: transparent;
25386
25533
  }
@@ -25467,7 +25614,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
25467
25614
  return {
25468
25615
  pre: function ngModelPreLink(scope, element, attr, ctrls) {
25469
25616
  var modelCtrl = ctrls[0],
25470
- formCtrl = ctrls[1] || nullFormCtrl;
25617
+ formCtrl = ctrls[1] || modelCtrl.$$parentForm;
25471
25618
 
25472
25619
  modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25473
25620
 
@@ -25476,12 +25623,12 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
25476
25623
 
25477
25624
  attr.$observe('name', function(newValue) {
25478
25625
  if (modelCtrl.$name !== newValue) {
25479
- formCtrl.$$renameControl(modelCtrl, newValue);
25626
+ modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
25480
25627
  }
25481
25628
  });
25482
25629
 
25483
25630
  scope.$on('$destroy', function() {
25484
- formCtrl.$removeControl(modelCtrl);
25631
+ modelCtrl.$$parentForm.$removeControl(modelCtrl);
25485
25632
  });
25486
25633
  },
25487
25634
  post: function ngModelPostLink(scope, element, attr, ctrls) {
@@ -25676,7 +25823,7 @@ var ngModelOptionsDirective = function() {
25676
25823
  var that = this;
25677
25824
  this.$options = copy($scope.$eval($attrs.ngModelOptions));
25678
25825
  // Allow adding/overriding bound events
25679
- if (this.$options.updateOn !== undefined) {
25826
+ if (isDefined(this.$options.updateOn)) {
25680
25827
  this.$options.updateOnDefault = false;
25681
25828
  // extract "default" pseudo-event from list of events that can trigger a model update
25682
25829
  this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
@@ -25699,7 +25846,6 @@ function addSetValidityMethod(context) {
25699
25846
  classCache = {},
25700
25847
  set = context.set,
25701
25848
  unset = context.unset,
25702
- parentForm = context.parentForm,
25703
25849
  $animate = context.$animate;
25704
25850
 
25705
25851
  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
@@ -25707,7 +25853,7 @@ function addSetValidityMethod(context) {
25707
25853
  ctrl.$setValidity = setValidity;
25708
25854
 
25709
25855
  function setValidity(validationErrorKey, state, controller) {
25710
- if (state === undefined) {
25856
+ if (isUndefined(state)) {
25711
25857
  createAndSet('$pending', validationErrorKey, controller);
25712
25858
  } else {
25713
25859
  unsetAndCleanup('$pending', validationErrorKey, controller);
@@ -25751,7 +25897,7 @@ function addSetValidityMethod(context) {
25751
25897
  }
25752
25898
 
25753
25899
  toggleValidationCss(validationErrorKey, combinedState);
25754
- parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
25900
+ ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
25755
25901
  }
25756
25902
 
25757
25903
  function createAndSet(name, value, controller) {
@@ -26412,11 +26558,16 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26412
26558
  function updateOptionElement(option, element) {
26413
26559
  option.element = element;
26414
26560
  element.disabled = option.disabled;
26415
- if (option.value !== element.value) element.value = option.selectValue;
26561
+ // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26562
+ // selects in certain circumstances when multiple selects are next to each other and display
26563
+ // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
26564
+ // See https://github.com/angular/angular.js/issues/11314 for more info.
26565
+ // This is unfortunately untestable with unit / e2e tests
26416
26566
  if (option.label !== element.label) {
26417
26567
  element.label = option.label;
26418
26568
  element.textContent = option.label;
26419
26569
  }
26570
+ if (option.value !== element.value) element.value = option.selectValue;
26420
26571
  }
26421
26572
 
26422
26573
  function addOrReuseElement(parent, current, type, templateElement) {
@@ -26457,7 +26608,10 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26457
26608
  if (emptyOption_ || unknownOption_) {
26458
26609
  while (current &&
26459
26610
  (current === emptyOption_ ||
26460
- current === unknownOption_)) {
26611
+ current === unknownOption_ ||
26612
+ emptyOption_ && emptyOption_.nodeType === NODE_TYPE_COMMENT)) {
26613
+ // Empty options might have directives that transclude
26614
+ // and insert comments (e.g. ngIf)
26461
26615
  current = current.nextSibling;
26462
26616
  }
26463
26617
  }
@@ -26819,8 +26973,10 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
26819
26973
  * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
26820
26974
  * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
26821
26975
  *
26822
- * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
26823
- * This may be useful when, for instance, nesting ngRepeats.
26976
+ * <div class="alert alert-info">
26977
+ * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
26978
+ * This may be useful when, for instance, nesting ngRepeats.
26979
+ * </div>
26824
26980
  *
26825
26981
  *
26826
26982
  * # Iterating over object properties
@@ -27054,7 +27210,6 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
27054
27210
  .animate-repeat.ng-move,
27055
27211
  .animate-repeat.ng-enter,
27056
27212
  .animate-repeat.ng-leave {
27057
- -webkit-transition:all linear 0.5s;
27058
27213
  transition:all linear 0.5s;
27059
27214
  }
27060
27215
 
@@ -27226,7 +27381,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27226
27381
  // if object, extract keys, in enumeration order, unsorted
27227
27382
  collectionKeys = [];
27228
27383
  for (var itemKey in collection) {
27229
- if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
27384
+ if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27230
27385
  collectionKeys.push(itemKey);
27231
27386
  }
27232
27387
  }
@@ -27451,9 +27606,7 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27451
27606
  background: white;
27452
27607
  }
27453
27608
 
27454
- .animate-show.ng-hide-add.ng-hide-add-active,
27455
- .animate-show.ng-hide-remove.ng-hide-remove-active {
27456
- -webkit-transition: all linear 0.5s;
27609
+ .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
27457
27610
  transition: all linear 0.5s;
27458
27611
  }
27459
27612
 
@@ -27610,7 +27763,6 @@ var ngShowDirective = ['$animate', function($animate) {
27610
27763
  </file>
27611
27764
  <file name="animations.css">
27612
27765
  .animate-hide {
27613
- -webkit-transition: all linear 0.5s;
27614
27766
  transition: all linear 0.5s;
27615
27767
  line-height: 20px;
27616
27768
  opacity: 1;
@@ -27809,7 +27961,6 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
27809
27961
  }
27810
27962
 
27811
27963
  .animate-switch.ng-animate {
27812
- -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
27813
27964
  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
27814
27965
 
27815
27966
  position:absolute;
@@ -28150,31 +28301,162 @@ var SelectController =
28150
28301
  * @description
28151
28302
  * HTML `SELECT` element with angular data-binding.
28152
28303
  *
28153
- * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28154
- * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits such as reducing
28155
- * memory and increasing speed by not creating a new scope for each repeated instance, as well as providing
28156
- * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28157
- * comprehension expression.
28304
+ * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28305
+ * between the scope and the `<select>` control (including setting default values).
28306
+ * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28307
+ * {@link ngOptions `ngOptions`} directives.
28158
28308
  *
28159
- * When an item in the `<select>` menu is selected, the array element or object property
28160
- * represented by the selected option will be bound to the model identified by the `ngModel`
28161
- * directive.
28309
+ * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28310
+ * to the model identified by the `ngModel` directive. With static or repeated options, this is
28311
+ * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28312
+ * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28162
28313
  *
28163
- * If the viewValue contains a value that doesn't match any of the options then the control
28164
- * will automatically add an "unknown" option, which it then removes when this is resolved.
28314
+ * <div class="alert alert-warning">
28315
+ * Note that the value of a `select` directive used without `ngOptions` is always a string.
28316
+ * When the model needs to be bound to a non-string value, you must either explictly convert it
28317
+ * using a directive (see example below) or use `ngOptions` to specify the set of options.
28318
+ * This is because an option element can only be bound to string values at present.
28319
+ * </div>
28320
+ *
28321
+ * If the viewValue of `ngModel` does not match any of the options, then the control
28322
+ * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28165
28323
  *
28166
28324
  * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28167
28325
  * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28168
28326
  * option. See example below for demonstration.
28169
28327
  *
28170
28328
  * <div class="alert alert-info">
28171
- * The value of a `select` directive used without `ngOptions` is always a string.
28172
- * When the model needs to be bound to a non-string value, you must either explictly convert it
28173
- * using a directive (see example below) or use `ngOptions` to specify the set of options.
28174
- * This is because an option element can only be bound to string values at present.
28329
+ * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28330
+ * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
28331
+ * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28332
+ * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28333
+ * a new scope for each repeated instance.
28334
+ * </div>
28335
+ *
28336
+ *
28337
+ * @param {string} ngModel Assignable angular expression to data-bind to.
28338
+ * @param {string=} name Property name of the form under which the control is published.
28339
+ * @param {string=} required Sets `required` validation error key if the value is not entered.
28340
+ * @param {string=} ngRequired Adds required attribute and required validation constraint to
28341
+ * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28342
+ * when you want to data-bind to the required attribute.
28343
+ * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28344
+ * interaction with the select element.
28345
+ * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28346
+ * set on the model on selection. See {@link ngOptions `ngOptions`}.
28347
+ *
28348
+ * @example
28349
+ * ### Simple `select` elements with static options
28350
+ *
28351
+ * <example name="static-select" module="staticSelect">
28352
+ * <file name="index.html">
28353
+ * <div ng-controller="ExampleController">
28354
+ * <form name="myForm">
28355
+ * <label for="singleSelect"> Single select: </label><br>
28356
+ * <select name="singleSelect" ng-model="data.singleSelect">
28357
+ * <option value="option-1">Option 1</option>
28358
+ * <option value="option-2">Option 2</option>
28359
+ * </select><br>
28360
+ *
28361
+ * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28362
+ * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28363
+ * <option value="">---Please select---</option> <!-- not selected / blank option -->
28364
+ * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28365
+ * <option value="option-2">Option 2</option>
28366
+ * </select><br>
28367
+ * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28368
+ * <tt>singleSelect = {{data.singleSelect}}</tt>
28369
+ *
28370
+ * <hr>
28371
+ * <label for="multipleSelect"> Multiple select: </label><br>
28372
+ * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28373
+ * <option value="option-1">Option 1</option>
28374
+ * <option value="option-2">Option 2</option>
28375
+ * <option value="option-3">Option 3</option>
28376
+ * </select><br>
28377
+ * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28378
+ * </form>
28379
+ * </div>
28380
+ * </file>
28381
+ * <file name="app.js">
28382
+ * angular.module('staticSelect', [])
28383
+ * .controller('ExampleController', ['$scope', function($scope) {
28384
+ * $scope.data = {
28385
+ * singleSelect: null,
28386
+ * multipleSelect: [],
28387
+ * option1: 'option-1',
28388
+ * };
28389
+ *
28390
+ * $scope.forceUnknownOption = function() {
28391
+ * $scope.data.singleSelect = 'nonsense';
28392
+ * };
28393
+ * }]);
28394
+ * </file>
28395
+ *</example>
28396
+ *
28397
+ * ### Using `ngRepeat` to generate `select` options
28398
+ * <example name="ngrepeat-select" module="ngrepeatSelect">
28399
+ * <file name="index.html">
28400
+ * <div ng-controller="ExampleController">
28401
+ * <form name="myForm">
28402
+ * <label for="repeatSelect"> Repeat select: </label>
28403
+ * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28404
+ * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28405
+ * </select>
28406
+ * </form>
28407
+ * <hr>
28408
+ * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28175
28409
  * </div>
28410
+ * </file>
28411
+ * <file name="app.js">
28412
+ * angular.module('ngrepeatSelect', [])
28413
+ * .controller('ExampleController', ['$scope', function($scope) {
28414
+ * $scope.data = {
28415
+ * repeatSelect: null,
28416
+ * availableOptions: [
28417
+ * {id: '1', name: 'Option A'},
28418
+ * {id: '2', name: 'Option B'},
28419
+ * {id: '3', name: 'Option C'}
28420
+ * ],
28421
+ * };
28422
+ * }]);
28423
+ * </file>
28424
+ *</example>
28176
28425
  *
28177
- * ### Example (binding `select` to a non-string value)
28426
+ *
28427
+ * ### Using `select` with `ngOptions` and setting a default value
28428
+ * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28429
+ *
28430
+ * <example name="select-with-default-values" module="defaultValueSelect">
28431
+ * <file name="index.html">
28432
+ * <div ng-controller="ExampleController">
28433
+ * <form name="myForm">
28434
+ * <label for="mySelect">Make a choice:</label>
28435
+ * <select name="mySelect" id="mySelect"
28436
+ * ng-options="option.name for option in data.availableOptions track by option.id"
28437
+ * ng-model="data.selectedOption"></select>
28438
+ * </form>
28439
+ * <hr>
28440
+ * <tt>option = {{data.selectedOption}}</tt><br/>
28441
+ * </div>
28442
+ * </file>
28443
+ * <file name="app.js">
28444
+ * angular.module('defaultValueSelect', [])
28445
+ * .controller('ExampleController', ['$scope', function($scope) {
28446
+ * $scope.data = {
28447
+ * availableOptions: [
28448
+ * {id: '1', name: 'Option A'},
28449
+ * {id: '2', name: 'Option B'},
28450
+ * {id: '3', name: 'Option C'}
28451
+ * ],
28452
+ * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28453
+ * };
28454
+ * }]);
28455
+ * </file>
28456
+ *</example>
28457
+ *
28458
+ *
28459
+ * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28178
28460
  *
28179
28461
  * <example name="select-with-non-string-options" module="nonStringSelect">
28180
28462
  * <file name="index.html">
@@ -28312,9 +28594,12 @@ var optionDirective = ['$interpolate', function($interpolate) {
28312
28594
  priority: 100,
28313
28595
  compile: function(element, attr) {
28314
28596
 
28315
- // If the value attribute is not defined then we fall back to the
28316
- // text content of the option element, which may be interpolated
28317
- if (isUndefined(attr.value)) {
28597
+ if (isDefined(attr.value)) {
28598
+ // If the value attribute is defined, check if it contains an interpolation
28599
+ var valueInterpolated = $interpolate(attr.value, true);
28600
+ } else {
28601
+ // If the value attribute is not defined then we fall back to the
28602
+ // text content of the option element, which may be interpolated
28318
28603
  var interpolateFn = $interpolate(element.text(), true);
28319
28604
  if (!interpolateFn) {
28320
28605
  attr.$set('value', element.text());
@@ -28330,24 +28615,38 @@ var optionDirective = ['$interpolate', function($interpolate) {
28330
28615
  selectCtrl = parent.data(selectCtrlName) ||
28331
28616
  parent.parent().data(selectCtrlName); // in case we are in optgroup
28332
28617
 
28618
+ function addOption(optionValue) {
28619
+ selectCtrl.addOption(optionValue, element);
28620
+ selectCtrl.ngModelCtrl.$render();
28621
+ chromeHack(element);
28622
+ }
28623
+
28333
28624
  // Only update trigger option updates if this is an option within a `select`
28334
28625
  // that also has `ngModel` attached
28335
28626
  if (selectCtrl && selectCtrl.ngModelCtrl) {
28336
28627
 
28337
- if (interpolateFn) {
28628
+ if (valueInterpolated) {
28629
+ // The value attribute is interpolated
28630
+ var oldVal;
28631
+ attr.$observe('value', function valueAttributeObserveAction(newVal) {
28632
+ if (isDefined(oldVal)) {
28633
+ selectCtrl.removeOption(oldVal);
28634
+ }
28635
+ oldVal = newVal;
28636
+ addOption(newVal);
28637
+ });
28638
+ } else if (interpolateFn) {
28639
+ // The text content is interpolated
28338
28640
  scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
28339
28641
  attr.$set('value', newVal);
28340
28642
  if (oldVal !== newVal) {
28341
28643
  selectCtrl.removeOption(oldVal);
28342
28644
  }
28343
- selectCtrl.addOption(newVal, element);
28344
- selectCtrl.ngModelCtrl.$render();
28345
- chromeHack(element);
28645
+ addOption(newVal);
28346
28646
  });
28347
28647
  } else {
28348
- selectCtrl.addOption(attr.value, element);
28349
- selectCtrl.ngModelCtrl.$render();
28350
- chromeHack(element);
28648
+ // The value attribute is static
28649
+ addOption(attr.value);
28351
28650
  }
28352
28651
 
28353
28652
  element.on('$destroy', function() {
@@ -28408,8 +28707,9 @@ var patternDirective = function() {
28408
28707
  ctrl.$validate();
28409
28708
  });
28410
28709
 
28411
- ctrl.$validators.pattern = function(value) {
28412
- return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
28710
+ ctrl.$validators.pattern = function(modelValue, viewValue) {
28711
+ // HTML5 pattern constraint validates the input value, so we validate the viewValue
28712
+ return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
28413
28713
  };
28414
28714
  }
28415
28715
  };