angularjs-rails 1.4.4 → 1.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.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
  };