angularjs-rails 1.4.7 → 1.4.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.4.7
2
+ * @license AngularJS v1.4.8
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.7
2
+ * @license AngularJS v1.4.8
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.7/' +
60
+ message += '\nhttp://errors.angularjs.org/1.4.8/' +
61
61
  (module ? module + '/' : '') + code;
62
62
 
63
63
  for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
@@ -267,20 +267,24 @@ msie = document.documentMode;
267
267
  * String ...)
268
268
  */
269
269
  function isArrayLike(obj) {
270
- if (obj == null || isWindow(obj)) {
271
- return false;
272
- }
270
+
271
+ // `null`, `undefined` and `window` are not array-like
272
+ if (obj == null || isWindow(obj)) return false;
273
+
274
+ // arrays, strings and jQuery/jqLite objects are array like
275
+ // * jqLite is either the jQuery or jqLite constructor function
276
+ // * we have to check the existance of jqLite first as this method is called
277
+ // via the forEach method when constructing the jqLite object in the first place
278
+ if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
273
279
 
274
280
  // Support: iOS 8.2 (not reproducible in simulator)
275
281
  // "length" in obj used to prevent JIT error (gh-11508)
276
282
  var length = "length" in Object(obj) && obj.length;
277
283
 
278
- if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
279
- return true;
280
- }
281
-
282
- return isString(obj) || isArray(obj) || length === 0 ||
283
- typeof length === 'number' && length > 0 && (length - 1) in obj;
284
+ // NodeList objects (with `item` method) and
285
+ // other objects with suitable length characteristics are array-like
286
+ return isNumber(length) &&
287
+ (length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
284
288
  }
285
289
 
286
290
  /**
@@ -425,6 +429,10 @@ function baseExtend(dst, objs, deep) {
425
429
  dst[key] = new Date(src.valueOf());
426
430
  } else if (isRegExp(src)) {
427
431
  dst[key] = new RegExp(src);
432
+ } else if (src.nodeName) {
433
+ dst[key] = src.cloneNode(true);
434
+ } else if (isElement(src)) {
435
+ dst[key] = src.clone();
428
436
  } else {
429
437
  if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
430
438
  baseExtend(dst[key], [src], true);
@@ -540,7 +548,7 @@ identity.$inject = [];
540
548
  function valueFn(value) {return function() {return value;};}
541
549
 
542
550
  function hasCustomToString(obj) {
543
- return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
551
+ return isFunction(obj.toString) && obj.toString !== toString;
544
552
  }
545
553
 
546
554
 
@@ -739,9 +747,9 @@ function isPromiseLike(obj) {
739
747
  }
740
748
 
741
749
 
742
- var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
750
+ var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
743
751
  function isTypedArray(value) {
744
- return TYPED_ARRAY_REGEXP.test(toString.call(value));
752
+ return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
745
753
  }
746
754
 
747
755
 
@@ -863,100 +871,111 @@ function arrayRemove(array, value) {
863
871
  </file>
864
872
  </example>
865
873
  */
866
- function copy(source, destination, stackSource, stackDest) {
867
- if (isWindow(source) || isScope(source)) {
868
- throw ngMinErr('cpws',
869
- "Can't copy! Making copies of Window or Scope instances is not supported.");
870
- }
871
- if (isTypedArray(destination)) {
872
- throw ngMinErr('cpta',
873
- "Can't copy! TypedArray destination cannot be mutated.");
874
- }
874
+ function copy(source, destination) {
875
+ var stackSource = [];
876
+ var stackDest = [];
875
877
 
876
- if (!destination) {
877
- destination = source;
878
- if (isObject(source)) {
879
- var index;
880
- if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
881
- return stackDest[index];
882
- }
883
-
884
- // TypedArray, Date and RegExp have specific copy functionality and must be
885
- // pushed onto the stack before returning.
886
- // Array and other objects create the base object and recurse to copy child
887
- // objects. The array/object will be pushed onto the stack when recursed.
888
- if (isArray(source)) {
889
- return copy(source, [], stackSource, stackDest);
890
- } else if (isTypedArray(source)) {
891
- destination = new source.constructor(source);
892
- } else if (isDate(source)) {
893
- destination = new Date(source.getTime());
894
- } else if (isRegExp(source)) {
895
- destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
896
- destination.lastIndex = source.lastIndex;
897
- } else if (isFunction(source.cloneNode)) {
898
- destination = source.cloneNode(true);
899
- } else {
900
- var emptyObject = Object.create(getPrototypeOf(source));
901
- return copy(source, emptyObject, stackSource, stackDest);
902
- }
878
+ if (destination) {
879
+ if (isTypedArray(destination)) {
880
+ throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
881
+ }
882
+ if (source === destination) {
883
+ throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
884
+ }
903
885
 
904
- if (stackDest) {
905
- stackSource.push(source);
906
- stackDest.push(destination);
907
- }
886
+ // Empty the destination object
887
+ if (isArray(destination)) {
888
+ destination.length = 0;
889
+ } else {
890
+ forEach(destination, function(value, key) {
891
+ if (key !== '$$hashKey') {
892
+ delete destination[key];
893
+ }
894
+ });
908
895
  }
909
- } else {
910
- if (source === destination) throw ngMinErr('cpi',
911
- "Can't copy! Source and destination are identical.");
912
896
 
913
- stackSource = stackSource || [];
914
- stackDest = stackDest || [];
897
+ stackSource.push(source);
898
+ stackDest.push(destination);
899
+ return copyRecurse(source, destination);
900
+ }
915
901
 
916
- if (isObject(source)) {
917
- stackSource.push(source);
918
- stackDest.push(destination);
919
- }
902
+ return copyElement(source);
920
903
 
904
+ function copyRecurse(source, destination) {
905
+ var h = destination.$$hashKey;
921
906
  var result, key;
922
907
  if (isArray(source)) {
923
- destination.length = 0;
924
- for (var i = 0; i < source.length; i++) {
925
- destination.push(copy(source[i], null, stackSource, stackDest));
908
+ for (var i = 0, ii = source.length; i < ii; i++) {
909
+ destination.push(copyElement(source[i]));
926
910
  }
927
- } else {
928
- var h = destination.$$hashKey;
929
- if (isArray(destination)) {
930
- destination.length = 0;
931
- } else {
932
- forEach(destination, function(value, key) {
933
- delete destination[key];
934
- });
935
- }
936
- if (isBlankObject(source)) {
937
- // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
938
- for (key in source) {
939
- destination[key] = copy(source[key], null, stackSource, stackDest);
940
- }
941
- } else if (source && typeof source.hasOwnProperty === 'function') {
942
- // Slow path, which must rely on hasOwnProperty
943
- for (key in source) {
944
- if (source.hasOwnProperty(key)) {
945
- destination[key] = copy(source[key], null, stackSource, stackDest);
946
- }
911
+ } else if (isBlankObject(source)) {
912
+ // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
913
+ for (key in source) {
914
+ destination[key] = copyElement(source[key]);
915
+ }
916
+ } else if (source && typeof source.hasOwnProperty === 'function') {
917
+ // Slow path, which must rely on hasOwnProperty
918
+ for (key in source) {
919
+ if (source.hasOwnProperty(key)) {
920
+ destination[key] = copyElement(source[key]);
947
921
  }
948
- } else {
949
- // Slowest path --- hasOwnProperty can't be called as a method
950
- for (key in source) {
951
- if (hasOwnProperty.call(source, key)) {
952
- destination[key] = copy(source[key], null, stackSource, stackDest);
953
- }
922
+ }
923
+ } else {
924
+ // Slowest path --- hasOwnProperty can't be called as a method
925
+ for (key in source) {
926
+ if (hasOwnProperty.call(source, key)) {
927
+ destination[key] = copyElement(source[key]);
954
928
  }
955
929
  }
956
- setHashKey(destination,h);
957
930
  }
931
+ setHashKey(destination, h);
932
+ return destination;
933
+ }
934
+
935
+ function copyElement(source) {
936
+ // Simple values
937
+ if (!isObject(source)) {
938
+ return source;
939
+ }
940
+
941
+ // Already copied values
942
+ var index = stackSource.indexOf(source);
943
+ if (index !== -1) {
944
+ return stackDest[index];
945
+ }
946
+
947
+ if (isWindow(source) || isScope(source)) {
948
+ throw ngMinErr('cpws',
949
+ "Can't copy! Making copies of Window or Scope instances is not supported.");
950
+ }
951
+
952
+ var needsRecurse = false;
953
+ var destination;
954
+
955
+ if (isArray(source)) {
956
+ destination = [];
957
+ needsRecurse = true;
958
+ } else if (isTypedArray(source)) {
959
+ destination = new source.constructor(source);
960
+ } else if (isDate(source)) {
961
+ destination = new Date(source.getTime());
962
+ } else if (isRegExp(source)) {
963
+ destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
964
+ destination.lastIndex = source.lastIndex;
965
+ } else if (isFunction(source.cloneNode)) {
966
+ destination = source.cloneNode(true);
967
+ } else {
968
+ destination = Object.create(getPrototypeOf(source));
969
+ needsRecurse = true;
970
+ }
971
+
972
+ stackSource.push(source);
973
+ stackDest.push(destination);
974
+
975
+ return needsRecurse
976
+ ? copyRecurse(source, destination)
977
+ : destination;
958
978
  }
959
- return destination;
960
979
  }
961
980
 
962
981
  /**
@@ -2080,7 +2099,7 @@ function setupModuleLoader(window) {
2080
2099
  * @param {string} name constant name
2081
2100
  * @param {*} object Constant value.
2082
2101
  * @description
2083
- * Because the constant are fixed, they get applied before other provide methods.
2102
+ * Because the constants are fixed, they get applied before other provide methods.
2084
2103
  * See {@link auto.$provide#constant $provide.constant()}.
2085
2104
  */
2086
2105
  constant: invokeLater('$provide', 'constant', 'unshift'),
@@ -2379,11 +2398,11 @@ function toDebugString(obj) {
2379
2398
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2380
2399
  */
2381
2400
  var version = {
2382
- full: '1.4.7', // all of these placeholder strings will be replaced by grunt's
2401
+ full: '1.4.8', // all of these placeholder strings will be replaced by grunt's
2383
2402
  major: 1, // package task
2384
2403
  minor: 4,
2385
- dot: 7,
2386
- codeName: 'dark-luminescence'
2404
+ dot: 8,
2405
+ codeName: 'ice-manipulation'
2387
2406
  };
2388
2407
 
2389
2408
 
@@ -2765,6 +2784,14 @@ function jqLiteParseHTML(html, context) {
2765
2784
  return [];
2766
2785
  }
2767
2786
 
2787
+
2788
+ // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2789
+ var jqLiteContains = Node.prototype.contains || function(arg) {
2790
+ // jshint bitwise: false
2791
+ return !!(this.compareDocumentPosition(arg) & 16);
2792
+ // jshint bitwise: true
2793
+ };
2794
+
2768
2795
  /////////////////////////////////////////////
2769
2796
  function JQLite(element) {
2770
2797
  if (element instanceof JQLite) {
@@ -2823,17 +2850,23 @@ function jqLiteOff(element, type, fn, unsupported) {
2823
2850
  delete events[type];
2824
2851
  }
2825
2852
  } else {
2826
- forEach(type.split(' '), function(type) {
2853
+
2854
+ var removeHandler = function(type) {
2855
+ var listenerFns = events[type];
2827
2856
  if (isDefined(fn)) {
2828
- var listenerFns = events[type];
2829
2857
  arrayRemove(listenerFns || [], fn);
2830
- if (listenerFns && listenerFns.length > 0) {
2831
- return;
2832
- }
2833
2858
  }
2859
+ if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2860
+ removeEventListenerFn(element, type, handle);
2861
+ delete events[type];
2862
+ }
2863
+ };
2834
2864
 
2835
- removeEventListenerFn(element, type, handle);
2836
- delete events[type];
2865
+ forEach(type.split(' '), function(type) {
2866
+ removeHandler(type);
2867
+ if (MOUSE_EVENT_MAP[type]) {
2868
+ removeHandler(MOUSE_EVENT_MAP[type]);
2869
+ }
2837
2870
  });
2838
2871
  }
2839
2872
  }
@@ -3288,6 +3321,9 @@ function createEventHandler(element, events) {
3288
3321
  return event.immediatePropagationStopped === true;
3289
3322
  };
3290
3323
 
3324
+ // Some events have special handlers that wrap the real handler
3325
+ var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3326
+
3291
3327
  // Copy event handlers in case event handlers array is modified during execution.
3292
3328
  if ((eventFnsLength > 1)) {
3293
3329
  eventFns = shallowCopy(eventFns);
@@ -3295,7 +3331,7 @@ function createEventHandler(element, events) {
3295
3331
 
3296
3332
  for (var i = 0; i < eventFnsLength; i++) {
3297
3333
  if (!event.isImmediatePropagationStopped()) {
3298
- eventFns[i].call(element, event);
3334
+ handlerWrapper(element, event, eventFns[i]);
3299
3335
  }
3300
3336
  }
3301
3337
  };
@@ -3306,6 +3342,22 @@ function createEventHandler(element, events) {
3306
3342
  return eventHandler;
3307
3343
  }
3308
3344
 
3345
+ function defaultHandlerWrapper(element, event, handler) {
3346
+ handler.call(element, event);
3347
+ }
3348
+
3349
+ function specialMouseHandlerWrapper(target, event, handler) {
3350
+ // Refer to jQuery's implementation of mouseenter & mouseleave
3351
+ // Read about mouseenter and mouseleave:
3352
+ // http://www.quirksmode.org/js/events_mouse.html#link8
3353
+ var related = event.relatedTarget;
3354
+ // For mousenter/leave call the handler if related is outside the target.
3355
+ // NB: No relatedTarget if the mouse left/entered the browser window
3356
+ if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3357
+ handler.call(target, event);
3358
+ }
3359
+ }
3360
+
3309
3361
  //////////////////////////////////////////
3310
3362
  // Functions iterating traversal.
3311
3363
  // These functions chain results into a single
@@ -3334,35 +3386,28 @@ forEach({
3334
3386
  var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3335
3387
  var i = types.length;
3336
3388
 
3337
- while (i--) {
3338
- type = types[i];
3389
+ var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3339
3390
  var eventFns = events[type];
3340
3391
 
3341
3392
  if (!eventFns) {
3342
- events[type] = [];
3343
-
3344
- if (type === 'mouseenter' || type === 'mouseleave') {
3345
- // Refer to jQuery's implementation of mouseenter & mouseleave
3346
- // Read about mouseenter and mouseleave:
3347
- // http://www.quirksmode.org/js/events_mouse.html#link8
3348
-
3349
- jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
3350
- var target = this, related = event.relatedTarget;
3351
- // For mousenter/leave call the handler if related is outside the target.
3352
- // NB: No relatedTarget if the mouse left/entered the browser window
3353
- if (!related || (related !== target && !target.contains(related))) {
3354
- handle(event, type);
3355
- }
3356
- });
3357
-
3358
- } else {
3359
- if (type !== '$destroy') {
3360
- addEventListenerFn(element, type, handle);
3361
- }
3393
+ eventFns = events[type] = [];
3394
+ eventFns.specialHandlerWrapper = specialHandlerWrapper;
3395
+ if (type !== '$destroy' && !noEventListener) {
3396
+ addEventListenerFn(element, type, handle);
3362
3397
  }
3363
- eventFns = events[type];
3364
3398
  }
3399
+
3365
3400
  eventFns.push(fn);
3401
+ };
3402
+
3403
+ while (i--) {
3404
+ type = types[i];
3405
+ if (MOUSE_EVENT_MAP[type]) {
3406
+ addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3407
+ addHandler(type, undefined, true);
3408
+ } else {
3409
+ addHandler(type);
3410
+ }
3366
3411
  }
3367
3412
  },
3368
3413
 
@@ -4543,7 +4588,7 @@ function $AnchorScrollProvider() {
4543
4588
  * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4544
4589
  * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4545
4590
  * in the
4546
- * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4591
+ * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4547
4592
  *
4548
4593
  * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4549
4594
  * match any anchor whenever it changes. This can be disabled by calling
@@ -5058,7 +5103,7 @@ var $AnimateProvider = ['$provide', function($provide) {
5058
5103
  * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5059
5104
  * to ensure that animation runs with the triggered DOM operation.
5060
5105
  *
5061
- * By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't
5106
+ * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5062
5107
  * included and only when it is active then the animation hooks that `$animate` triggers will be
5063
5108
  * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5064
5109
  * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
@@ -5910,9 +5955,9 @@ function $CacheFactoryProvider() {
5910
5955
 
5911
5956
  var size = 0,
5912
5957
  stats = extend({}, options, {id: cacheId}),
5913
- data = {},
5958
+ data = createMap(),
5914
5959
  capacity = (options && options.capacity) || Number.MAX_VALUE,
5915
- lruHash = {},
5960
+ lruHash = createMap(),
5916
5961
  freshEnd = null,
5917
5962
  staleEnd = null;
5918
5963
 
@@ -6040,6 +6085,8 @@ function $CacheFactoryProvider() {
6040
6085
  delete lruHash[key];
6041
6086
  }
6042
6087
 
6088
+ if (!(key in data)) return;
6089
+
6043
6090
  delete data[key];
6044
6091
  size--;
6045
6092
  },
@@ -6054,9 +6101,9 @@ function $CacheFactoryProvider() {
6054
6101
  * Clears the cache object of any entries.
6055
6102
  */
6056
6103
  removeAll: function() {
6057
- data = {};
6104
+ data = createMap();
6058
6105
  size = 0;
6059
- lruHash = {};
6106
+ lruHash = createMap();
6060
6107
  freshEnd = staleEnd = null;
6061
6108
  },
6062
6109
 
@@ -7457,6 +7504,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7457
7504
  return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7458
7505
  },
7459
7506
  NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7507
+ var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7460
7508
 
7461
7509
  compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7462
7510
  var bindings = $element.data('$binding') || [];
@@ -7509,6 +7557,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7509
7557
  return function publicLinkFn(scope, cloneConnectFn, options) {
7510
7558
  assertArg(scope, 'scope');
7511
7559
 
7560
+ if (previousCompileContext && previousCompileContext.needsNewScope) {
7561
+ // A parent directive did a replace and a directive on this element asked
7562
+ // for transclusion, which caused us to lose a layer of element on which
7563
+ // we could hold the new transclusion scope, so we will create it manually
7564
+ // here.
7565
+ scope = scope.$parent.$new();
7566
+ }
7567
+
7512
7568
  options = options || {};
7513
7569
  var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7514
7570
  transcludeControllers = options.transcludeControllers,
@@ -7654,11 +7710,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7654
7710
  if (nodeLinkFn.scope) {
7655
7711
  childScope = scope.$new();
7656
7712
  compile.$$addScopeInfo(jqLite(node), childScope);
7657
- var destroyBindings = nodeLinkFn.$$destroyBindings;
7658
- if (destroyBindings) {
7659
- nodeLinkFn.$$destroyBindings = null;
7660
- childScope.$on('$destroyed', destroyBindings);
7661
- }
7662
7713
  } else {
7663
7714
  childScope = scope;
7664
7715
  }
@@ -7677,8 +7728,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7677
7728
  childBoundTranscludeFn = null;
7678
7729
  }
7679
7730
 
7680
- nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn,
7681
- nodeLinkFn);
7731
+ nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7682
7732
 
7683
7733
  } else if (childLinkFn) {
7684
7734
  childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
@@ -7747,13 +7797,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7747
7797
  });
7748
7798
  }
7749
7799
 
7750
- var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
7751
- if (directiveIsMultiElement(directiveNName)) {
7752
- if (ngAttrName === directiveNName + 'Start') {
7753
- attrStartName = name;
7754
- attrEndName = name.substr(0, name.length - 5) + 'end';
7755
- name = name.substr(0, name.length - 6);
7756
- }
7800
+ var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
7801
+ if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
7802
+ attrStartName = name;
7803
+ attrEndName = name.substr(0, name.length - 5) + 'end';
7804
+ name = name.substr(0, name.length - 6);
7757
7805
  }
7758
7806
 
7759
7807
  nName = directiveNormalize(name.toLowerCase());
@@ -7992,7 +8040,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7992
8040
  } else {
7993
8041
  $template = jqLite(jqLiteClone(compileNode)).contents();
7994
8042
  $compileNode.empty(); // clear contents
7995
- childTranscludeFn = compile($template, transcludeFn);
8043
+ childTranscludeFn = compile($template, transcludeFn, undefined,
8044
+ undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
7996
8045
  }
7997
8046
  }
7998
8047
 
@@ -8034,8 +8083,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8034
8083
  var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8035
8084
  var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8036
8085
 
8037
- if (newIsolateScopeDirective) {
8038
- markDirectivesAsIsolate(templateDirectives);
8086
+ if (newIsolateScopeDirective || newScopeDirective) {
8087
+ // The original directive caused the current element to be replaced but this element
8088
+ // also needs to have a new scope, so we need to tell the template directives
8089
+ // that they would need to get their scope from further up, if they require transclusion
8090
+ markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8039
8091
  }
8040
8092
  directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8041
8093
  mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
@@ -8188,10 +8240,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8188
8240
  return elementControllers;
8189
8241
  }
8190
8242
 
8191
- function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn,
8192
- thisLinkFn) {
8193
- var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
8194
- attrs;
8243
+ function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8244
+ var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8245
+ attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8195
8246
 
8196
8247
  if (compileNode === linkNode) {
8197
8248
  attrs = templateAttrs;
@@ -8201,8 +8252,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8201
8252
  attrs = new Attributes($element, templateAttrs);
8202
8253
  }
8203
8254
 
8255
+ controllerScope = scope;
8204
8256
  if (newIsolateScopeDirective) {
8205
8257
  isolateScope = scope.$new(true);
8258
+ } else if (newScopeDirective) {
8259
+ controllerScope = scope.$parent;
8206
8260
  }
8207
8261
 
8208
8262
  if (boundTranscludeFn) {
@@ -8223,42 +8277,34 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8223
8277
  compile.$$addScopeClass($element, true);
8224
8278
  isolateScope.$$isolateBindings =
8225
8279
  newIsolateScopeDirective.$$isolateBindings;
8226
- initializeDirectiveBindings(scope, attrs, isolateScope,
8227
- isolateScope.$$isolateBindings,
8228
- newIsolateScopeDirective, isolateScope);
8280
+ removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8281
+ isolateScope.$$isolateBindings,
8282
+ newIsolateScopeDirective);
8283
+ if (removeScopeBindingWatches) {
8284
+ isolateScope.$on('$destroy', removeScopeBindingWatches);
8285
+ }
8229
8286
  }
8230
- if (elementControllers) {
8231
- // Initialize bindToController bindings for new/isolate scopes
8232
- var scopeDirective = newIsolateScopeDirective || newScopeDirective;
8233
- var bindings;
8234
- var controllerForBindings;
8235
- if (scopeDirective && elementControllers[scopeDirective.name]) {
8236
- bindings = scopeDirective.$$bindings.bindToController;
8237
- controller = elementControllers[scopeDirective.name];
8238
-
8239
- if (controller && controller.identifier && bindings) {
8240
- controllerForBindings = controller;
8241
- thisLinkFn.$$destroyBindings =
8242
- initializeDirectiveBindings(scope, attrs, controller.instance,
8243
- bindings, scopeDirective);
8244
- }
8287
+
8288
+ // Initialize bindToController bindings
8289
+ for (var name in elementControllers) {
8290
+ var controllerDirective = controllerDirectives[name];
8291
+ var controller = elementControllers[name];
8292
+ var bindings = controllerDirective.$$bindings.bindToController;
8293
+
8294
+ if (controller.identifier && bindings) {
8295
+ removeControllerBindingWatches =
8296
+ initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8245
8297
  }
8246
- for (i in elementControllers) {
8247
- controller = elementControllers[i];
8248
- var controllerResult = controller();
8249
-
8250
- if (controllerResult !== controller.instance) {
8251
- // If the controller constructor has a return value, overwrite the instance
8252
- // from setupControllers and update the element data
8253
- controller.instance = controllerResult;
8254
- $element.data('$' + i + 'Controller', controllerResult);
8255
- if (controller === controllerForBindings) {
8256
- // Remove and re-install bindToController bindings
8257
- thisLinkFn.$$destroyBindings();
8258
- thisLinkFn.$$destroyBindings =
8259
- initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
8260
- }
8261
- }
8298
+
8299
+ var controllerResult = controller();
8300
+ if (controllerResult !== controller.instance) {
8301
+ // If the controller constructor has a return value, overwrite the instance
8302
+ // from setupControllers
8303
+ controller.instance = controllerResult;
8304
+ $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8305
+ removeControllerBindingWatches && removeControllerBindingWatches();
8306
+ removeControllerBindingWatches =
8307
+ initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8262
8308
  }
8263
8309
  }
8264
8310
 
@@ -8318,10 +8364,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8318
8364
  }
8319
8365
  }
8320
8366
 
8321
- function markDirectivesAsIsolate(directives) {
8322
- // mark all directives as needing isolate scope.
8367
+ // Depending upon the context in which a directive finds itself it might need to have a new isolated
8368
+ // or child scope created. For instance:
8369
+ // * if the directive has been pulled into a template because another directive with a higher priority
8370
+ // asked for element transclusion
8371
+ // * if the directive itself asks for transclusion but it is at the root of a template and the original
8372
+ // element was replaced. See https://github.com/angular/angular.js/issues/12936
8373
+ function markDirectiveScope(directives, isolateScope, newScope) {
8323
8374
  for (var j = 0, jj = directives.length; j < jj; j++) {
8324
- directives[j] = inherit(directives[j], {$$isolateScope: true});
8375
+ directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8325
8376
  }
8326
8377
  }
8327
8378
 
@@ -8468,7 +8519,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8468
8519
  var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8469
8520
 
8470
8521
  if (isObject(origAsyncDirective.scope)) {
8471
- markDirectivesAsIsolate(templateDirectives);
8522
+ // the original directive that caused the template to be loaded async required
8523
+ // an isolate scope
8524
+ markDirectiveScope(templateDirectives, true);
8472
8525
  }
8473
8526
  directives = templateDirectives.concat(directives);
8474
8527
  mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
@@ -8517,7 +8570,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8517
8570
  childBoundTranscludeFn = boundTranscludeFn;
8518
8571
  }
8519
8572
  afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8520
- childBoundTranscludeFn, afterTemplateNodeLinkFn);
8573
+ childBoundTranscludeFn);
8521
8574
  }
8522
8575
  linkQueue = null;
8523
8576
  });
@@ -8534,8 +8587,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8534
8587
  if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8535
8588
  childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8536
8589
  }
8537
- afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn,
8538
- afterTemplateNodeLinkFn);
8590
+ afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8539
8591
  }
8540
8592
  };
8541
8593
  }
@@ -8747,7 +8799,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8747
8799
  // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8748
8800
  // data here because there's no public interface in jQuery to do that and copying over
8749
8801
  // event listeners (which is the main use of private data) wouldn't work anyway.
8750
- jqLite(newNode).data(jqLite(firstElementToRemove).data());
8802
+ jqLite.data(newNode, jqLite.data(firstElementToRemove));
8751
8803
 
8752
8804
  // Remove data of the replaced element. We cannot just call .remove()
8753
8805
  // on the element it since that would deallocate scope that is needed
@@ -8795,9 +8847,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8795
8847
 
8796
8848
  // Set up $watches for isolate scope and controller bindings. This process
8797
8849
  // only occurs for isolate scopes and new scopes with controllerAs.
8798
- function initializeDirectiveBindings(scope, attrs, destination, bindings,
8799
- directive, newScope) {
8800
- var onNewScopeDestroyed;
8850
+ function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
8851
+ var removeWatchCollection = [];
8801
8852
  forEach(bindings, function(definition, scopeName) {
8802
8853
  var attrName = definition.attrName,
8803
8854
  optional = definition.optional,
@@ -8859,14 +8910,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8859
8910
  return lastValue = parentValue;
8860
8911
  };
8861
8912
  parentValueWatch.$stateful = true;
8862
- var unwatch;
8913
+ var removeWatch;
8863
8914
  if (definition.collection) {
8864
- unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
8915
+ removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
8865
8916
  } else {
8866
- unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
8917
+ removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
8867
8918
  }
8868
- onNewScopeDestroyed = (onNewScopeDestroyed || []);
8869
- onNewScopeDestroyed.push(unwatch);
8919
+ removeWatchCollection.push(removeWatch);
8870
8920
  break;
8871
8921
 
8872
8922
  case '&':
@@ -8882,16 +8932,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
8882
8932
  break;
8883
8933
  }
8884
8934
  });
8885
- var destroyBindings = onNewScopeDestroyed ? function destroyBindings() {
8886
- for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) {
8887
- onNewScopeDestroyed[i]();
8935
+
8936
+ return removeWatchCollection.length && function removeWatches() {
8937
+ for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
8938
+ removeWatchCollection[i]();
8888
8939
  }
8889
- } : noop;
8890
- if (newScope && destroyBindings !== noop) {
8891
- newScope.$on('$destroy', destroyBindings);
8892
- return noop;
8893
- }
8894
- return destroyBindings;
8940
+ };
8895
8941
  }
8896
8942
  }];
8897
8943
  }
@@ -9616,9 +9662,9 @@ function $HttpProvider() {
9616
9662
  * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9617
9663
  * This should be used to make sure that applications work without these methods.
9618
9664
  *
9619
- * Defaults to false. If no value is specified, returns the current configured value.
9665
+ * Defaults to true. If no value is specified, returns the current configured value.
9620
9666
  *
9621
- * @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods.
9667
+ * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9622
9668
  *
9623
9669
  * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9624
9670
  * otherwise, returns the current configured value.
@@ -10270,11 +10316,8 @@ function $HttpProvider() {
10270
10316
  function transformResponse(response) {
10271
10317
  // make a copy since the response must be cacheable
10272
10318
  var resp = extend({}, response);
10273
- if (!response.data) {
10274
- resp.data = response.data;
10275
- } else {
10276
- resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
10277
- }
10319
+ resp.data = transformData(response.data, response.headers, response.status,
10320
+ config.transformResponse);
10278
10321
  return (isSuccess(response.status))
10279
10322
  ? resp
10280
10323
  : $q.reject(resp);
@@ -11908,9 +11951,9 @@ var locationPrototype = {
11908
11951
  * @description
11909
11952
  * This method is getter / setter.
11910
11953
  *
11911
- * Return hash fragment when called without any parameter.
11954
+ * Returns the hash fragment when called without any parameters.
11912
11955
  *
11913
- * Change hash fragment when called with parameter and return `$location`.
11956
+ * Changes the hash fragment when called with a parameter and returns `$location`.
11914
11957
  *
11915
11958
  *
11916
11959
  * ```js
@@ -11931,8 +11974,8 @@ var locationPrototype = {
11931
11974
  * @name $location#replace
11932
11975
  *
11933
11976
  * @description
11934
- * If called, all changes to $location during current `$digest` will be replacing current history
11935
- * record, instead of adding new one.
11977
+ * If called, all changes to $location during the current `$digest` will replace the current history
11978
+ * record, instead of adding a new one.
11936
11979
  */
11937
11980
  replace: function() {
11938
11981
  this.$$replace = true;
@@ -12252,7 +12295,7 @@ function $LocationProvider() {
12252
12295
  var oldUrl = $location.absUrl();
12253
12296
  var oldState = $location.$$state;
12254
12297
  var defaultPrevented;
12255
-
12298
+ newUrl = trimEmptyHash(newUrl);
12256
12299
  $location.$$parse(newUrl);
12257
12300
  $location.$$state = newState;
12258
12301
 
@@ -14393,13 +14436,14 @@ function $ParseProvider() {
14393
14436
  function addInterceptor(parsedExpression, interceptorFn) {
14394
14437
  if (!interceptorFn) return parsedExpression;
14395
14438
  var watchDelegate = parsedExpression.$$watchDelegate;
14439
+ var useInputs = false;
14396
14440
 
14397
14441
  var regularWatch =
14398
14442
  watchDelegate !== oneTimeLiteralWatchDelegate &&
14399
14443
  watchDelegate !== oneTimeWatchDelegate;
14400
14444
 
14401
14445
  var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14402
- var value = parsedExpression(scope, locals, assign, inputs);
14446
+ var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14403
14447
  return interceptorFn(value, scope, locals);
14404
14448
  } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14405
14449
  var value = parsedExpression(scope, locals, assign, inputs);
@@ -14417,6 +14461,7 @@ function $ParseProvider() {
14417
14461
  // If there is an interceptor, but no watchDelegate then treat the interceptor like
14418
14462
  // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14419
14463
  fn.$$watchDelegate = inputsWatchDelegate;
14464
+ useInputs = !parsedExpression.inputs;
14420
14465
  fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14421
14466
  }
14422
14467
 
@@ -14478,6 +14523,8 @@ function $ParseProvider() {
14478
14523
  *
14479
14524
  * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14480
14525
  *
14526
+ * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14527
+ *
14481
14528
  * However, the more traditional CommonJS-style usage is still available, and documented below.
14482
14529
  *
14483
14530
  * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
@@ -15062,15 +15109,15 @@ function $$RAFProvider() { //rAF
15062
15109
  * exposed as $$____ properties
15063
15110
  *
15064
15111
  * Loop operations are optimized by using while(count--) { ... }
15065
- * - this means that in order to keep the same order of execution as addition we have to add
15112
+ * - This means that in order to keep the same order of execution as addition we have to add
15066
15113
  * items to the array at the beginning (unshift) instead of at the end (push)
15067
15114
  *
15068
15115
  * Child scopes are created and removed often
15069
- * - Using an array would be slow since inserts in middle are expensive so we use linked list
15116
+ * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15070
15117
  *
15071
- * There are few watches then a lot of observers. This is why you don't want the observer to be
15072
- * implemented in the same way as watch. Watch requires return of initialization function which
15073
- * are expensive to construct.
15118
+ * There are fewer watches than observers. This is why you don't want the observer to be implemented
15119
+ * in the same way as watch. Watch requires return of the initialization function which is expensive
15120
+ * to construct.
15074
15121
  */
15075
15122
 
15076
15123
 
@@ -15112,7 +15159,7 @@ function $$RAFProvider() { //rAF
15112
15159
  * Every application has a single root {@link ng.$rootScope.Scope scope}.
15113
15160
  * All other scopes are descendant scopes of the root scope. Scopes provide separation
15114
15161
  * between the model and the view, via a mechanism for watching the model for changes.
15115
- * They also provide an event emission/broadcast and subscription facility. See the
15162
+ * They also provide event emission/broadcast and subscription facility. See the
15116
15163
  * {@link guide/scope developer guide on scopes}.
15117
15164
  */
15118
15165
  function $RootScopeProvider() {
@@ -15149,6 +15196,29 @@ function $RootScopeProvider() {
15149
15196
  $event.currentScope.$$destroyed = true;
15150
15197
  }
15151
15198
 
15199
+ function cleanUpScope($scope) {
15200
+
15201
+ if (msie === 9) {
15202
+ // There is a memory leak in IE9 if all child scopes are not disconnected
15203
+ // completely when a scope is destroyed. So this code will recurse up through
15204
+ // all this scopes children
15205
+ //
15206
+ // See issue https://github.com/angular/angular.js/issues/10706
15207
+ $scope.$$childHead && cleanUpScope($scope.$$childHead);
15208
+ $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15209
+ }
15210
+
15211
+ // The code below works around IE9 and V8's memory leaks
15212
+ //
15213
+ // See:
15214
+ // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15215
+ // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15216
+ // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15217
+
15218
+ $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15219
+ $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15220
+ }
15221
+
15152
15222
  /**
15153
15223
  * @ngdoc type
15154
15224
  * @name $rootScope.Scope
@@ -15945,16 +16015,9 @@ function $RootScopeProvider() {
15945
16015
  this.$on = this.$watch = this.$watchGroup = function() { return noop; };
15946
16016
  this.$$listeners = {};
15947
16017
 
15948
- // All of the code below is bogus code that works around V8's memory leak via optimized code
15949
- // and inline caches.
15950
- //
15951
- // see:
15952
- // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15953
- // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15954
- // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15955
-
15956
- this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
15957
- this.$$childTail = this.$root = this.$$watchers = null;
16018
+ // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16019
+ this.$$nextSibling = null;
16020
+ cleanUpScope(this);
15958
16021
  },
15959
16022
 
15960
16023
  /**
@@ -16950,7 +17013,7 @@ function $SceDelegateProvider() {
16950
17013
  * By default, Angular only loads templates from the same domain and protocol as the application
16951
17014
  * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
16952
17015
  * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
16953
- * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17016
+ * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
16954
17017
  * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
16955
17018
  *
16956
17019
  * *Please note*:
@@ -19201,7 +19264,7 @@ function limitToFilter() {
19201
19264
  if (!isArray(input) && !isString(input)) return input;
19202
19265
 
19203
19266
  begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19204
- begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
19267
+ begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19205
19268
 
19206
19269
  if (limit >= 0) {
19207
19270
  return input.slice(begin, begin + limit);
@@ -20561,7 +20624,8 @@ var ngFormDirective = formDirectiveFactory(true);
20561
20624
 
20562
20625
  // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20563
20626
  var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
20564
- var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
20627
+ // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
20628
+ var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
20565
20629
  var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
20566
20630
  var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20567
20631
  var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
@@ -24127,7 +24191,13 @@ var ngIfDirective = ['$animate', function($animate) {
24127
24191
  * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24128
24192
  * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24129
24193
  * @param {string=} onload Expression to evaluate when a new partial is loaded.
24130
- *
24194
+ * <div class="alert alert-warning">
24195
+ * **Note:** When using onload on SVG elements in IE11, the browser will try to call
24196
+ * a function with the name on the window element, which will usually throw a
24197
+ * "function is undefined" error. To fix this, you can instead use `data-onload` or a
24198
+ * different form that {@link guide/directive#normalization matches} `onload`.
24199
+ * </div>
24200
+ *
24131
24201
  * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24132
24202
  * $anchorScroll} to scroll the viewport after the content is loaded.
24133
24203
  *
@@ -25720,12 +25790,13 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25720
25790
  </label><br />
25721
25791
  </form>
25722
25792
  <pre>user.name = <span ng-bind="user.name"></span></pre>
25793
+ <pre>user.data = <span ng-bind="user.data"></span></pre>
25723
25794
  </div>
25724
25795
  </file>
25725
25796
  <file name="app.js">
25726
25797
  angular.module('optionsExample', [])
25727
25798
  .controller('ExampleController', ['$scope', function($scope) {
25728
- $scope.user = { name: 'say', data: '' };
25799
+ $scope.user = { name: 'John', data: '' };
25729
25800
 
25730
25801
  $scope.cancel = function(e) {
25731
25802
  if (e.keyCode == 27) {
@@ -25740,20 +25811,20 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25740
25811
  var other = element(by.model('user.data'));
25741
25812
 
25742
25813
  it('should allow custom events', function() {
25743
- input.sendKeys(' hello');
25814
+ input.sendKeys(' Doe');
25744
25815
  input.click();
25745
- expect(model.getText()).toEqual('say');
25816
+ expect(model.getText()).toEqual('John');
25746
25817
  other.click();
25747
- expect(model.getText()).toEqual('say hello');
25818
+ expect(model.getText()).toEqual('John Doe');
25748
25819
  });
25749
25820
 
25750
25821
  it('should $rollbackViewValue when model changes', function() {
25751
- input.sendKeys(' hello');
25752
- expect(input.getAttribute('value')).toEqual('say hello');
25822
+ input.sendKeys(' Doe');
25823
+ expect(input.getAttribute('value')).toEqual('John Doe');
25753
25824
  input.sendKeys(protractor.Key.ESCAPE);
25754
- expect(input.getAttribute('value')).toEqual('say');
25825
+ expect(input.getAttribute('value')).toEqual('John');
25755
25826
  other.click();
25756
- expect(model.getText()).toEqual('say');
25827
+ expect(model.getText()).toEqual('John');
25757
25828
  });
25758
25829
  </file>
25759
25830
  </example>
@@ -25779,7 +25850,7 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25779
25850
  <file name="app.js">
25780
25851
  angular.module('optionsExample', [])
25781
25852
  .controller('ExampleController', ['$scope', function($scope) {
25782
- $scope.user = { name: 'say' };
25853
+ $scope.user = { name: 'Igor' };
25783
25854
  }]);
25784
25855
  </file>
25785
25856
  </example>
@@ -26012,19 +26083,27 @@ var ngOptionsMinErr = minErr('ngOptions');
26012
26083
  *
26013
26084
  * ## Complex Models (objects or collections)
26014
26085
  *
26015
- * **Note:** By default, `ngModel` watches the model by reference, not value. This is important when
26016
- * binding any input directive to a model that is an object or a collection.
26086
+ * By default, `ngModel` watches the model by reference, not value. This is important to know when
26087
+ * binding the select to a model that is an object or a collection.
26017
26088
  *
26018
- * Since this is a common situation for `ngOptions` the directive additionally watches the model using
26019
- * `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in
26020
- * the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual
26021
- * object/collection has not changed identity but only a property on the object or an item in the collection
26022
- * changes.
26089
+ * One issue occurs if you want to preselect an option. For example, if you set
26090
+ * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26091
+ * because the objects are not identical. So by default, you should always reference the item in your collection
26092
+ * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26023
26093
  *
26024
- * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26025
- * if the model is an array). This means that changing a property deeper inside the object/collection that the
26026
- * first level will not trigger a re-rendering.
26094
+ * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26095
+ * of the item not by reference, but by the result of the `track by` expression. For example, if your
26096
+ * collection items have an id property, you would `track by item.id`.
26097
+ *
26098
+ * A different issue with objects or collections is that ngModel won't detect if an object property or
26099
+ * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26100
+ * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26101
+ * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26102
+ * has not changed identity, but only a property on the object or an item in the collection changes.
26027
26103
  *
26104
+ * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26105
+ * if the model is an array). This means that changing a property deeper than the first level inside the
26106
+ * object/collection will not trigger a re-rendering.
26028
26107
  *
26029
26108
  * ## `select` **`as`**
26030
26109
  *
@@ -26037,17 +26116,13 @@ var ngOptionsMinErr = minErr('ngOptions');
26037
26116
  * ### `select` **`as`** and **`track by`**
26038
26117
  *
26039
26118
  * <div class="alert alert-warning">
26040
- * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
26119
+ * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26041
26120
  * </div>
26042
26121
  *
26043
- * Consider the following example:
26044
- *
26045
- * ```html
26046
- * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"></select>
26047
- * ```
26122
+ * Given this array of items on the $scope:
26048
26123
  *
26049
26124
  * ```js
26050
- * $scope.values = [{
26125
+ * $scope.items = [{
26051
26126
  * id: 1,
26052
26127
  * label: 'aLabel',
26053
26128
  * subItem: { name: 'aSubItem' }
@@ -26056,20 +26131,33 @@ var ngOptionsMinErr = minErr('ngOptions');
26056
26131
  * label: 'bLabel',
26057
26132
  * subItem: { name: 'bSubItem' }
26058
26133
  * }];
26134
+ * ```
26135
+ *
26136
+ * This will work:
26059
26137
  *
26060
- * $scope.selected = { name: 'aSubItem' };
26138
+ * ```html
26139
+ * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26140
+ * ```
26141
+ * ```js
26142
+ * $scope.selected = $scope.items[0];
26061
26143
  * ```
26062
26144
  *
26063
- * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
26064
- * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
26065
- * following:
26145
+ * but this will not work:
26066
26146
  *
26067
- * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
26068
- * 2. Apply **`track by`** to the already selected value in `ngModel`.
26069
- * In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
26070
- * value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
26071
- * a wrong object, the selected element can't be found, `<select>` is always reset to the "not
26072
- * selected" option.
26147
+ * ```html
26148
+ * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26149
+ * ```
26150
+ * ```js
26151
+ * $scope.selected = $scope.items[0].subItem;
26152
+ * ```
26153
+ *
26154
+ * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26155
+ * `items` array. Because the selected option has been set programmatically in the controller, the
26156
+ * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26157
+ * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26158
+ * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26159
+ * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26160
+ * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26073
26161
  *
26074
26162
  *
26075
26163
  * @param {string} ngModel Assignable angular expression to data-bind to.
@@ -26371,11 +26459,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26371
26459
  var optionTemplate = document.createElement('option'),
26372
26460
  optGroupTemplate = document.createElement('optgroup');
26373
26461
 
26374
- return {
26375
- restrict: 'A',
26376
- terminal: true,
26377
- require: ['select', '?ngModel'],
26378
- link: function(scope, selectElement, attr, ctrls) {
26462
+
26463
+ function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26379
26464
 
26380
26465
  // if ngModel is not defined, we don't need to do anything
26381
26466
  var ngModelCtrl = ctrls[1];
@@ -26430,7 +26515,6 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26430
26515
  unknownOption.remove();
26431
26516
  };
26432
26517
 
26433
-
26434
26518
  // Update the controller methods for multiple selectable options
26435
26519
  if (!multiple) {
26436
26520
 
@@ -26605,13 +26689,15 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26605
26689
  var emptyOption_ = emptyOption && emptyOption[0];
26606
26690
  var unknownOption_ = unknownOption && unknownOption[0];
26607
26691
 
26692
+ // We cannot rely on the extracted empty option being the same as the compiled empty option,
26693
+ // because the compiled empty option might have been replaced by a comment because
26694
+ // it had an "element" transclusion directive on it (such as ngIf)
26608
26695
  if (emptyOption_ || unknownOption_) {
26609
26696
  while (current &&
26610
26697
  (current === emptyOption_ ||
26611
26698
  current === unknownOption_ ||
26612
- emptyOption_ && emptyOption_.nodeType === NODE_TYPE_COMMENT)) {
26613
- // Empty options might have directives that transclude
26614
- // and insert comments (e.g. ngIf)
26699
+ current.nodeType === NODE_TYPE_COMMENT ||
26700
+ current.value === '')) {
26615
26701
  current = current.nextSibling;
26616
26702
  }
26617
26703
  }
@@ -26708,7 +26794,20 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26708
26794
  }
26709
26795
 
26710
26796
  }
26797
+ }
26711
26798
 
26799
+ return {
26800
+ restrict: 'A',
26801
+ terminal: true,
26802
+ require: ['select', '?ngModel'],
26803
+ link: {
26804
+ pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
26805
+ // Deactivate the SelectController.register method to prevent
26806
+ // option directives from accidentally registering themselves
26807
+ // (and unwanted $destroy handlers etc.)
26808
+ ctrls[0].registerOption = noop;
26809
+ },
26810
+ post: ngOptionsPostLink
26712
26811
  }
26713
26812
  };
26714
26813
  }];
@@ -26995,7 +27094,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
26995
27094
  * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
26996
27095
  * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
26997
27096
  * keys in the order in which they were defined, although there are exceptions when keys are deleted
26998
- * and reinstated. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues
27097
+ * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
26999
27098
  *
27000
27099
  * If this is not desired, the recommended workaround is to convert your object into an array
27001
27100
  * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
@@ -27005,15 +27104,21 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
27005
27104
  *
27006
27105
  * # Tracking and Duplicates
27007
27106
  *
27008
- * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
27107
+ * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27108
+ * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27009
27109
  *
27010
27110
  * * When an item is added, a new instance of the template is added to the DOM.
27011
27111
  * * When an item is removed, its template instance is removed from the DOM.
27012
27112
  * * When items are reordered, their respective templates are reordered in the DOM.
27013
27113
  *
27014
- * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
27015
- * there are duplicates, it is not possible to maintain a one-to-one mapping between collection
27016
- * items and DOM elements.
27114
+ * To minimize creation of DOM elements, `ngRepeat` uses a function
27115
+ * to "keep track" of all items in the collection and their corresponding DOM elements.
27116
+ * For example, if an item is added to the collection, ngRepeat will know that all other items
27117
+ * already have DOM elements, and will not re-render them.
27118
+ *
27119
+ * The default tracking function (which tracks items by their identity) does not allow
27120
+ * duplicate items in arrays. This is because when there are duplicates, it is not possible
27121
+ * to maintain a one-to-one mapping between collection items and DOM elements.
27017
27122
  *
27018
27123
  * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27019
27124
  * with your own using the `track by` expression.
@@ -27026,7 +27131,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
27026
27131
  * </div>
27027
27132
  * ```
27028
27133
  *
27029
- * You may use arbitrary expressions in `track by`, including references to custom functions
27134
+ * You may also use arbitrary expressions in `track by`, including references to custom functions
27030
27135
  * on the scope:
27031
27136
  * ```html
27032
27137
  * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
@@ -27034,10 +27139,14 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
27034
27139
  * </div>
27035
27140
  * ```
27036
27141
  *
27037
- * If you are working with objects that have an identifier property, you can track
27142
+ * <div class="alert alert-success">
27143
+ * If you are working with objects that have an identifier property, you should track
27038
27144
  * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27039
27145
  * will not have to rebuild the DOM elements for items it has already rendered, even if the
27040
- * JavaScript objects in the collection have been substituted for new ones:
27146
+ * JavaScript objects in the collection have been substituted for new ones. For large collections,
27147
+ * this signifincantly improves rendering performance. If you don't have a unique identifier,
27148
+ * `track by $index` can also provide a performance boost.
27149
+ * </div>
27041
27150
  * ```html
27042
27151
  * <div ng-repeat="model in collection track by model.id">
27043
27152
  * {{model.name}}
@@ -28195,6 +28304,15 @@ var scriptDirective = ['$templateCache', function($templateCache) {
28195
28304
 
28196
28305
  var noopNgModelController = { $setViewValue: noop, $render: noop };
28197
28306
 
28307
+ function chromeHack(optionElement) {
28308
+ // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28309
+ // Adding an <option selected="selected"> element to a <select required="required"> should
28310
+ // automatically select the new element
28311
+ if (optionElement[0].hasAttribute('selected')) {
28312
+ optionElement[0].selected = true;
28313
+ }
28314
+ }
28315
+
28198
28316
  /**
28199
28317
  * @ngdoc type
28200
28318
  * @name select.SelectController
@@ -28270,6 +28388,8 @@ var SelectController =
28270
28388
  }
28271
28389
  var count = optionsMap.get(value) || 0;
28272
28390
  optionsMap.put(value, count + 1);
28391
+ self.ngModelCtrl.$render();
28392
+ chromeHack(element);
28273
28393
  };
28274
28394
 
28275
28395
  // Tell the select control that an option, with the given value, has been removed
@@ -28291,6 +28411,39 @@ var SelectController =
28291
28411
  self.hasOption = function(value) {
28292
28412
  return !!optionsMap.get(value);
28293
28413
  };
28414
+
28415
+
28416
+ self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28417
+
28418
+ if (interpolateValueFn) {
28419
+ // The value attribute is interpolated
28420
+ var oldVal;
28421
+ optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28422
+ if (isDefined(oldVal)) {
28423
+ self.removeOption(oldVal);
28424
+ }
28425
+ oldVal = newVal;
28426
+ self.addOption(newVal, optionElement);
28427
+ });
28428
+ } else if (interpolateTextFn) {
28429
+ // The text content is interpolated
28430
+ optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28431
+ optionAttrs.$set('value', newVal);
28432
+ if (oldVal !== newVal) {
28433
+ self.removeOption(oldVal);
28434
+ }
28435
+ self.addOption(newVal, optionElement);
28436
+ });
28437
+ } else {
28438
+ // The value attribute is static
28439
+ self.addOption(optionAttrs.value, optionElement);
28440
+ }
28441
+
28442
+ optionElement.on('$destroy', function() {
28443
+ self.removeOption(optionAttrs.value);
28444
+ self.ngModelCtrl.$render();
28445
+ });
28446
+ };
28294
28447
  }];
28295
28448
 
28296
28449
  /**
@@ -28336,6 +28489,8 @@ var SelectController =
28336
28489
  *
28337
28490
  * @param {string} ngModel Assignable angular expression to data-bind to.
28338
28491
  * @param {string=} name Property name of the form under which the control is published.
28492
+ * @param {string=} multiple Allows multiple options to be selected. The selected values will be
28493
+ * bound to the model as an array.
28339
28494
  * @param {string=} required Sets `required` validation error key if the value is not entered.
28340
28495
  * @param {string=} ngRequired Adds required attribute and required validation constraint to
28341
28496
  * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
@@ -28501,7 +28656,13 @@ var selectDirective = function() {
28501
28656
  restrict: 'E',
28502
28657
  require: ['select', '?ngModel'],
28503
28658
  controller: SelectController,
28504
- link: function(scope, element, attr, ctrls) {
28659
+ priority: 1,
28660
+ link: {
28661
+ pre: selectPreLink
28662
+ }
28663
+ };
28664
+
28665
+ function selectPreLink(scope, element, attr, ctrls) {
28505
28666
 
28506
28667
  // if ngModel is not defined, we don't need to do anything
28507
28668
  var ngModelCtrl = ctrls[1];
@@ -28571,7 +28732,6 @@ var selectDirective = function() {
28571
28732
 
28572
28733
  }
28573
28734
  }
28574
- };
28575
28735
  };
28576
28736
 
28577
28737
 
@@ -28579,16 +28739,6 @@ var selectDirective = function() {
28579
28739
  // of dynamically created (and destroyed) option elements to their containing select
28580
28740
  // directive via its controller.
28581
28741
  var optionDirective = ['$interpolate', function($interpolate) {
28582
-
28583
- function chromeHack(optionElement) {
28584
- // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28585
- // Adding an <option selected="selected"> element to a <select required="required"> should
28586
- // automatically select the new element
28587
- if (optionElement[0].hasAttribute('selected')) {
28588
- optionElement[0].selected = true;
28589
- }
28590
- }
28591
-
28592
28742
  return {
28593
28743
  restrict: 'E',
28594
28744
  priority: 100,
@@ -28596,12 +28746,12 @@ var optionDirective = ['$interpolate', function($interpolate) {
28596
28746
 
28597
28747
  if (isDefined(attr.value)) {
28598
28748
  // If the value attribute is defined, check if it contains an interpolation
28599
- var valueInterpolated = $interpolate(attr.value, true);
28749
+ var interpolateValueFn = $interpolate(attr.value, true);
28600
28750
  } else {
28601
28751
  // If the value attribute is not defined then we fall back to the
28602
28752
  // text content of the option element, which may be interpolated
28603
- var interpolateFn = $interpolate(element.text(), true);
28604
- if (!interpolateFn) {
28753
+ var interpolateTextFn = $interpolate(element.text(), true);
28754
+ if (!interpolateTextFn) {
28605
28755
  attr.$set('value', element.text());
28606
28756
  }
28607
28757
  }
@@ -28615,44 +28765,8 @@ var optionDirective = ['$interpolate', function($interpolate) {
28615
28765
  selectCtrl = parent.data(selectCtrlName) ||
28616
28766
  parent.parent().data(selectCtrlName); // in case we are in optgroup
28617
28767
 
28618
- function addOption(optionValue) {
28619
- selectCtrl.addOption(optionValue, element);
28620
- selectCtrl.ngModelCtrl.$render();
28621
- chromeHack(element);
28622
- }
28623
-
28624
- // Only update trigger option updates if this is an option within a `select`
28625
- // that also has `ngModel` attached
28626
- if (selectCtrl && selectCtrl.ngModelCtrl) {
28627
-
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
28640
- scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
28641
- attr.$set('value', newVal);
28642
- if (oldVal !== newVal) {
28643
- selectCtrl.removeOption(oldVal);
28644
- }
28645
- addOption(newVal);
28646
- });
28647
- } else {
28648
- // The value attribute is static
28649
- addOption(attr.value);
28650
- }
28651
-
28652
- element.on('$destroy', function() {
28653
- selectCtrl.removeOption(attr.value);
28654
- selectCtrl.ngModelCtrl.$render();
28655
- });
28768
+ if (selectCtrl) {
28769
+ selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
28656
28770
  }
28657
28771
  };
28658
28772
  }