angularjs-rails 1.0.6.2 → 1.0.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.1.4
2
+ * @license AngularJS v1.1.5
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -87,6 +87,27 @@ function noConflict() {
87
87
  return a;
88
88
  }
89
89
 
90
+ /**
91
+ * @private
92
+ * @param {*} obj
93
+ * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
94
+ */
95
+ function isArrayLike(obj) {
96
+ if (!obj || (typeof obj.length !== 'number')) return false;
97
+
98
+ // We have on object which has length property. Should we treat it as array?
99
+ if (typeof obj.hasOwnProperty != 'function' &&
100
+ typeof obj.constructor != 'function') {
101
+ // This is here for IE8: it is a bogus object treat it as array;
102
+ return true;
103
+ } else {
104
+ return obj instanceof JQLite || // JQLite
105
+ (jQuery && obj instanceof jQuery) || // jQuery
106
+ toString.call(obj) !== '[object Object]' || // some browser native object
107
+ typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
108
+ }
109
+ }
110
+
90
111
  /**
91
112
  * @ngdoc function
92
113
  * @name angular.forEach
@@ -114,30 +135,6 @@ function noConflict() {
114
135
  * @param {Object=} context Object to become context (`this`) for the iterator function.
115
136
  * @returns {Object|Array} Reference to `obj`.
116
137
  */
117
-
118
-
119
- /**
120
- * @private
121
- * @param {*} obj
122
- * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
123
- */
124
- function isArrayLike(obj) {
125
- if (!obj || (typeof obj.length !== 'number')) return false;
126
-
127
- // We have on object which has length property. Should we treat it as array?
128
- if (typeof obj.hasOwnProperty != 'function' &&
129
- typeof obj.constructor != 'function') {
130
- // This is here for IE8: it is a bogus object treat it as array;
131
- return true;
132
- } else {
133
- return obj instanceof JQLite || // JQLite
134
- (jQuery && obj instanceof jQuery) || // jQuery
135
- toString.call(obj) !== '[object Object]' || // some browser native object
136
- typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
137
- }
138
- }
139
-
140
-
141
138
  function forEach(obj, iterator, context) {
142
139
  var key;
143
140
  if (obj) {
@@ -221,6 +218,21 @@ function nextUid() {
221
218
  return uid.join('');
222
219
  }
223
220
 
221
+
222
+ /**
223
+ * Set or clear the hashkey for an object.
224
+ * @param obj object
225
+ * @param h the hashkey (!truthy to delete the hashkey)
226
+ */
227
+ function setHashKey(obj, h) {
228
+ if (h) {
229
+ obj.$$hashKey = h;
230
+ }
231
+ else {
232
+ delete obj.$$hashKey;
233
+ }
234
+ }
235
+
224
236
  /**
225
237
  * @ngdoc function
226
238
  * @name angular.extend
@@ -232,8 +244,10 @@ function nextUid() {
232
244
  *
233
245
  * @param {Object} dst Destination object.
234
246
  * @param {...Object} src Source object(s).
247
+ * @returns {Object} Reference to `dst`.
235
248
  */
236
249
  function extend(dst) {
250
+ var h = dst.$$hashKey;
237
251
  forEach(arguments, function(obj){
238
252
  if (obj !== dst) {
239
253
  forEach(obj, function(value, key){
@@ -241,6 +255,8 @@ function extend(dst) {
241
255
  });
242
256
  }
243
257
  });
258
+
259
+ setHashKey(dst,h);
244
260
  return dst;
245
261
  }
246
262
 
@@ -600,12 +616,14 @@ function copy(source, destination){
600
616
  destination.push(copy(source[i]));
601
617
  }
602
618
  } else {
619
+ var h = destination.$$hashKey;
603
620
  forEach(destination, function(value, key){
604
621
  delete destination[key];
605
622
  });
606
623
  for ( var key in source) {
607
624
  destination[key] = copy(source[key]);
608
625
  }
626
+ setHashKey(destination,h);
609
627
  }
610
628
  }
611
629
  return destination;
@@ -645,7 +663,7 @@ function shallowCopy(src, dst) {
645
663
  * During a property comparison, properties of `function` type and properties with names
646
664
  * that begin with `$` are ignored.
647
665
  *
648
- * Scope and DOMWindow objects are being compared only be identify (`===`).
666
+ * Scope and DOMWindow objects are being compared only by identify (`===`).
649
667
  *
650
668
  * @param {*} o1 Object or value to compare.
651
669
  * @param {*} o2 Object or value to compare.
@@ -705,7 +723,7 @@ function sliceArgs(args, startIndex) {
705
723
  *
706
724
  * @description
707
725
  * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
708
- * `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
726
+ * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
709
727
  * known as [function currying](http://en.wikipedia.org/wiki/Currying).
710
728
  *
711
729
  * @param {Object} self Context which `fn` should be evaluated in.
@@ -898,7 +916,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
898
916
  *
899
917
  * @description
900
918
  *
901
- * Use this directive to auto-bootstrap on application. Only
919
+ * Use this directive to auto-bootstrap an application. Only
902
920
  * one directive can be used per HTML document. The directive
903
921
  * designates the root of the application and is typically placed
904
922
  * at the root of the page.
@@ -981,12 +999,13 @@ function bootstrap(element, modules) {
981
999
  }]);
982
1000
  modules.unshift('ng');
983
1001
  var injector = createInjector(modules);
984
- injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
985
- function(scope, element, compile, injector) {
1002
+ injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animator',
1003
+ function(scope, element, compile, injector, animator) {
986
1004
  scope.$apply(function() {
987
1005
  element.data('$injector', injector);
988
1006
  compile(element)(scope);
989
1007
  });
1008
+ animator.enabled(true);
990
1009
  }]
991
1010
  );
992
1011
  return injector;
@@ -1037,7 +1056,7 @@ function bindJQuery() {
1037
1056
  }
1038
1057
 
1039
1058
  /**
1040
- * throw error of the argument is falsy.
1059
+ * throw error if the argument is falsy.
1041
1060
  */
1042
1061
  function assertArg(arg, name, reason) {
1043
1062
  if (!arg) {
@@ -1345,11 +1364,11 @@ function setupModuleLoader(window) {
1345
1364
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
1346
1365
  */
1347
1366
  var version = {
1348
- full: '1.1.4', // all of these placeholder strings will be replaced by grunt's
1367
+ full: '1.1.5', // all of these placeholder strings will be replaced by grunt's
1349
1368
  major: 1, // package task
1350
1369
  minor: 1,
1351
- dot: 4,
1352
- codeName: 'quantum-manipulation'
1370
+ dot: 5,
1371
+ codeName: 'triangle-squarification'
1353
1372
  };
1354
1373
 
1355
1374
 
@@ -1413,6 +1432,7 @@ function publishExternalAPI(angular){
1413
1432
  ngController: ngControllerDirective,
1414
1433
  ngForm: ngFormDirective,
1415
1434
  ngHide: ngHideDirective,
1435
+ ngIf: ngIfDirective,
1416
1436
  ngInclude: ngIncludeDirective,
1417
1437
  ngInit: ngInitDirective,
1418
1438
  ngNonBindable: ngNonBindableDirective,
@@ -1497,18 +1517,18 @@ function publishExternalAPI(angular){
1497
1517
  * - [after()](http://api.jquery.com/after/)
1498
1518
  * - [append()](http://api.jquery.com/append/)
1499
1519
  * - [attr()](http://api.jquery.com/attr/)
1500
- * - [bind()](http://api.jquery.com/bind/)
1501
- * - [children()](http://api.jquery.com/children/)
1520
+ * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
1521
+ * - [children()](http://api.jquery.com/children/) - Does not support selectors
1502
1522
  * - [clone()](http://api.jquery.com/clone/)
1503
1523
  * - [contents()](http://api.jquery.com/contents/)
1504
1524
  * - [css()](http://api.jquery.com/css/)
1505
1525
  * - [data()](http://api.jquery.com/data/)
1506
1526
  * - [eq()](http://api.jquery.com/eq/)
1507
- * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name.
1527
+ * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
1508
1528
  * - [hasClass()](http://api.jquery.com/hasClass/)
1509
1529
  * - [html()](http://api.jquery.com/html/)
1510
- * - [next()](http://api.jquery.com/next/)
1511
- * - [parent()](http://api.jquery.com/parent/)
1530
+ * - [next()](http://api.jquery.com/next/) - Does not support selectors
1531
+ * - [parent()](http://api.jquery.com/parent/) - Does not support selectors
1512
1532
  * - [prepend()](http://api.jquery.com/prepend/)
1513
1533
  * - [prop()](http://api.jquery.com/prop/)
1514
1534
  * - [ready()](http://api.jquery.com/ready/)
@@ -1519,8 +1539,8 @@ function publishExternalAPI(angular){
1519
1539
  * - [replaceWith()](http://api.jquery.com/replaceWith/)
1520
1540
  * - [text()](http://api.jquery.com/text/)
1521
1541
  * - [toggleClass()](http://api.jquery.com/toggleClass/)
1522
- * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
1523
- * - [unbind()](http://api.jquery.com/unbind/)
1542
+ * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
1543
+ * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
1524
1544
  * - [val()](http://api.jquery.com/val/)
1525
1545
  * - [wrap()](http://api.jquery.com/wrap/)
1526
1546
  *
@@ -2025,7 +2045,7 @@ function createEventHandler(element, events) {
2025
2045
  }
2026
2046
 
2027
2047
  event.isDefaultPrevented = function() {
2028
- return event.defaultPrevented;
2048
+ return event.defaultPrevented || event.returnValue == false;
2029
2049
  };
2030
2050
 
2031
2051
  forEach(events[type || event.type], function(fn) {
@@ -2072,23 +2092,43 @@ forEach({
2072
2092
 
2073
2093
  if (!eventFns) {
2074
2094
  if (type == 'mouseenter' || type == 'mouseleave') {
2075
- var counter = 0;
2095
+ var contains = document.body.contains || document.body.compareDocumentPosition ?
2096
+ function( a, b ) {
2097
+ var adown = a.nodeType === 9 ? a.documentElement : a,
2098
+ bup = b && b.parentNode;
2099
+ return a === bup || !!( bup && bup.nodeType === 1 && (
2100
+ adown.contains ?
2101
+ adown.contains( bup ) :
2102
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
2103
+ ));
2104
+ } :
2105
+ function( a, b ) {
2106
+ if ( b ) {
2107
+ while ( (b = b.parentNode) ) {
2108
+ if ( b === a ) {
2109
+ return true;
2110
+ }
2111
+ }
2112
+ }
2113
+ return false;
2114
+ };
2076
2115
 
2077
- events.mouseenter = [];
2078
- events.mouseleave = [];
2116
+ events[type] = [];
2117
+
2118
+ // Refer to jQuery's implementation of mouseenter & mouseleave
2119
+ // Read about mouseenter and mouseleave:
2120
+ // http://www.quirksmode.org/js/events_mouse.html#link8
2121
+ var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}
2122
+ bindFn(element, eventmap[type], function(event) {
2123
+ var ret, target = this, related = event.relatedTarget;
2124
+ // For mousenter/leave call the handler if related is outside the target.
2125
+ // NB: No relatedTarget if the mouse left/entered the browser window
2126
+ if ( !related || (related !== target && !contains(target, related)) ){
2127
+ handle(event, type);
2128
+ }
2079
2129
 
2080
- bindFn(element, 'mouseover', function(event) {
2081
- counter++;
2082
- if (counter == 1) {
2083
- handle(event, 'mouseenter');
2084
- }
2085
- });
2086
- bindFn(element, 'mouseout', function(event) {
2087
- counter --;
2088
- if (counter == 0) {
2089
- handle(event, 'mouseleave');
2090
- }
2091
2130
  });
2131
+
2092
2132
  } else {
2093
2133
  addEventListenerFn(element, type, handle);
2094
2134
  events[type] = [];
@@ -2208,9 +2248,10 @@ forEach({
2208
2248
 
2209
2249
  triggerHandler: function(element, eventName) {
2210
2250
  var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName];
2251
+ var event;
2211
2252
 
2212
2253
  forEach(eventFns, function(fn) {
2213
- fn.call(element, null);
2254
+ fn.call(element, {preventDefault: noop});
2214
2255
  });
2215
2256
  }
2216
2257
  }, function(fn, name){
@@ -2361,7 +2402,7 @@ function annotate(fn) {
2361
2402
  }
2362
2403
  } else if (isArray(fn)) {
2363
2404
  last = fn.length - 1;
2364
- assertArgFn(fn[last], 'fn')
2405
+ assertArgFn(fn[last], 'fn');
2365
2406
  $inject = fn.slice(0, last);
2366
2407
  } else {
2367
2408
  assertArgFn(fn, 'fn', true);
@@ -2395,7 +2436,7 @@ function annotate(fn) {
2395
2436
  * # Injection Function Annotation
2396
2437
  *
2397
2438
  * JavaScript does not have annotations, and annotations are needed for dependency injection. The
2398
- * following ways are all valid way of annotating function with injection arguments and are equivalent.
2439
+ * following are all valid ways of annotating function with injection arguments and are equivalent.
2399
2440
  *
2400
2441
  * <pre>
2401
2442
  * // inferred (only works if code not minified/obfuscated)
@@ -2450,6 +2491,18 @@ function annotate(fn) {
2450
2491
  * @returns {*} the value returned by the invoked `fn` function.
2451
2492
  */
2452
2493
 
2494
+ /**
2495
+ * @ngdoc method
2496
+ * @name AUTO.$injector#has
2497
+ * @methodOf AUTO.$injector
2498
+ *
2499
+ * @description
2500
+ * Allows the user to query if the particular service exist.
2501
+ *
2502
+ * @param {string} Name of the service to query.
2503
+ * @returns {boolean} returns true if injector has given service.
2504
+ */
2505
+
2453
2506
  /**
2454
2507
  * @ngdoc method
2455
2508
  * @name AUTO.$injector#instantiate
@@ -2524,7 +2577,7 @@ function annotate(fn) {
2524
2577
  * // ...
2525
2578
  * };
2526
2579
  * tmpFn.$inject = ['$compile', '$rootScope'];
2527
- * injector.invoke(tempFn);
2580
+ * injector.invoke(tmpFn);
2528
2581
  *
2529
2582
  * // To better support inline function the inline annotation is supported
2530
2583
  * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
@@ -2577,7 +2630,7 @@ function annotate(fn) {
2577
2630
  *
2578
2631
  * beforeEach(module(function($provide) {
2579
2632
  * $provide.provider('greet', GreetProvider);
2580
- * });
2633
+ * }));
2581
2634
  *
2582
2635
  * it('should greet', inject(function(greet) {
2583
2636
  * expect(greet('angular')).toEqual('Hello angular!');
@@ -2590,9 +2643,7 @@ function annotate(fn) {
2590
2643
  * inject(function(greet) {
2591
2644
  * expect(greet('angular')).toEqual('Ahoj angular!');
2592
2645
  * });
2593
- * )};
2594
- *
2595
- * });
2646
+ * });
2596
2647
  * </pre>
2597
2648
  */
2598
2649
 
@@ -2686,7 +2737,7 @@ function annotate(fn) {
2686
2737
  *
2687
2738
  * @param {string} name The name of the service to decorate.
2688
2739
  * @param {function()} decorator This function will be invoked when the service needs to be
2689
- * instanciated. The function is called using the {@link AUTO.$injector#invoke
2740
+ * instantiated. The function is called using the {@link AUTO.$injector#invoke
2690
2741
  * injector.invoke} method and is therefore fully injectable. Local injection arguments:
2691
2742
  *
2692
2743
  * * `$delegate` - The original service instance, which can be monkey patched, configured,
@@ -2885,6 +2936,8 @@ function createInjector(modulesToLoad) {
2885
2936
  var Constructor = function() {},
2886
2937
  instance, returnedValue;
2887
2938
 
2939
+ // Check if Type is annotated and use just the given function at n-1 as parameter
2940
+ // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
2888
2941
  Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
2889
2942
  instance = new Constructor();
2890
2943
  returnedValue = invoke(Type, instance, locals);
@@ -2896,7 +2949,10 @@ function createInjector(modulesToLoad) {
2896
2949
  invoke: invoke,
2897
2950
  instantiate: instantiate,
2898
2951
  get: getService,
2899
- annotate: annotate
2952
+ annotate: annotate,
2953
+ has: function(name) {
2954
+ return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
2955
+ }
2900
2956
  };
2901
2957
  }
2902
2958
  }
@@ -3022,18 +3078,14 @@ function $AnimationProvider($provide) {
3022
3078
  */
3023
3079
  return function $animation(name) {
3024
3080
  if (name) {
3025
- try {
3026
- return $injector.get(camelCase(name) + suffix);
3027
- } catch (e) {
3028
- //TODO(misko): this is a hack! we should have a better way to test if the injector has a given key.
3029
- // The issue is that the animations are optional, and if not present they should be silently ignored.
3030
- // The proper way to fix this is to add API onto the injector so that we can ask to see if a given
3031
- // animation is supported.
3081
+ var animationName = camelCase(name) + suffix;
3082
+ if ($injector.has(animationName)) {
3083
+ return $injector.get(animationName);
3032
3084
  }
3033
3085
  }
3034
- }
3086
+ };
3035
3087
  }];
3036
- };
3088
+ }
3037
3089
 
3038
3090
  // NOTE: this is a pseudo directive.
3039
3091
 
@@ -3043,18 +3095,21 @@ function $AnimationProvider($provide) {
3043
3095
  *
3044
3096
  * @description
3045
3097
  * The `ngAnimate` directive works as an attribute that is attached alongside pre-existing directives.
3046
- * It effects how the directive will perform DOM manipulation. This allows for complex animations to take place while
3047
- * without burduning the directive which uses the animation with animation details. The built dn directives
3098
+ * It effects how the directive will perform DOM manipulation. This allows for complex animations to take place
3099
+ * without burdening the directive which uses the animation with animation details. The built in directives
3048
3100
  * `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView` already accept `ngAnimate` directive.
3049
3101
  * Custom directives can take advantage of animation through {@link ng.$animator $animator service}.
3050
3102
  *
3051
3103
  * Below is a more detailed breakdown of the supported callback events provided by pre-exisitng ng directives:
3052
3104
  *
3053
- * * {@link ng.directive:ngRepeat#animations ngRepeat} — enter, leave and move
3054
- * * {@link ng.directive:ngView#animations ngView} — enter and leave
3055
- * * {@link ng.directive:ngInclude#animations ngInclude} enter and leave
3056
- * * {@link ng.directive:ngSwitch#animations ngSwitch} enter and leave
3057
- * * {@link ng.directive:ngShow#animations ngShow & ngHide} - show and hide respectively
3105
+ * | Directive | Supported Animations |
3106
+ * |========================================================== |====================================================|
3107
+ * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move |
3108
+ * | {@link ng.directive:ngView#animations ngView} | enter and leave |
3109
+ * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
3110
+ * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
3111
+ * | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
3112
+ * | {@link ng.directive:ngShow#animations ngShow & ngHide} | show and hide |
3058
3113
  *
3059
3114
  * You can find out more information about animations upon visiting each directive page.
3060
3115
  *
@@ -3075,23 +3130,26 @@ function $AnimationProvider($provide) {
3075
3130
  *
3076
3131
  * The `event1` and `event2` attributes refer to the animation events specific to the directive that has been assigned.
3077
3132
  *
3133
+ * Keep in mind that if an animation is running, no child element of such animation can also be animated.
3134
+ *
3078
3135
  * <h2>CSS-defined Animations</h2>
3079
- * By default, ngAnimate attaches two CSS3 classes per animation event to the DOM element to achieve the animation.
3080
- * This is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions.
3081
- * All that is required is the following CSS code:
3136
+ * By default, ngAnimate attaches two CSS classes per animation event to the DOM element to achieve the animation.
3137
+ * It is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions as
3138
+ * well as CSS animations.
3139
+ *
3140
+ * The following code below demonstrates how to perform animations using **CSS transitions** with ngAnimate:
3082
3141
  *
3083
3142
  * <pre>
3084
3143
  * <style type="text/css">
3085
3144
  * /&#42;
3086
- * The animate-enter prefix is the event name that you
3145
+ * The animate-enter CSS class is the event name that you
3087
3146
  * have provided within the ngAnimate attribute.
3088
3147
  * &#42;/
3089
- * .animate-enter-setup {
3148
+ * .animate-enter {
3090
3149
  * -webkit-transition: 1s linear all; /&#42; Safari/Chrome &#42;/
3091
3150
  * -moz-transition: 1s linear all; /&#42; Firefox &#42;/
3092
- * -ms-transition: 1s linear all; /&#42; IE10 &#42;/
3093
3151
  * -o-transition: 1s linear all; /&#42; Opera &#42;/
3094
- * transition: 1s linear all; /&#42; Future Browsers &#42;/
3152
+ * transition: 1s linear all; /&#42; IE10+ and Future Browsers &#42;/
3095
3153
  *
3096
3154
  * /&#42; The animation preparation code &#42;/
3097
3155
  * opacity: 0;
@@ -3102,7 +3160,7 @@ function $AnimationProvider($provide) {
3102
3160
  * classes together to avoid any CSS-specificity
3103
3161
  * conflicts
3104
3162
  * &#42;/
3105
- * .animate-enter-setup.animate-enter-start {
3163
+ * .animate-enter.animate-enter-active {
3106
3164
  * /&#42; The animation code itself &#42;/
3107
3165
  * opacity: 1;
3108
3166
  * }
@@ -3111,16 +3169,49 @@ function $AnimationProvider($provide) {
3111
3169
  * <div ng-directive ng-animate="{enter: 'animate-enter'}"></div>
3112
3170
  * </pre>
3113
3171
  *
3114
- * Upon DOM mutation, the setup class is added first, then the browser is allowed to reflow the content and then,
3115
- * the start class is added to trigger the animation. The ngAnimate directive will automatically extract the duration
3172
+ * The following code below demonstrates how to perform animations using **CSS animations** with ngAnimate:
3173
+ *
3174
+ * <pre>
3175
+ * <style type="text/css">
3176
+ * .animate-enter {
3177
+ * -webkit-animation: enter_sequence 1s linear; /&#42; Safari/Chrome &#42;/
3178
+ * -moz-animation: enter_sequence 1s linear; /&#42; Firefox &#42;/
3179
+ * -o-animation: enter_sequence 1s linear; /&#42; Opera &#42;/
3180
+ * animation: enter_sequence 1s linear; /&#42; IE10+ and Future Browsers &#42;/
3181
+ * }
3182
+ * &#64-webkit-keyframes enter_sequence {
3183
+ * from { opacity:0; }
3184
+ * to { opacity:1; }
3185
+ * }
3186
+ * &#64-moz-keyframes enter_sequence {
3187
+ * from { opacity:0; }
3188
+ * to { opacity:1; }
3189
+ * }
3190
+ * &#64-o-keyframes enter_sequence {
3191
+ * from { opacity:0; }
3192
+ * to { opacity:1; }
3193
+ * }
3194
+ * &#64keyframes enter_sequence {
3195
+ * from { opacity:0; }
3196
+ * to { opacity:1; }
3197
+ * }
3198
+ * </style>
3199
+ *
3200
+ * <div ng-directive ng-animate="{enter: 'animate-enter'}"></div>
3201
+ * </pre>
3202
+ *
3203
+ * ngAnimate will first examine any CSS animation code and then fallback to using CSS transitions.
3204
+ *
3205
+ * Upon DOM mutation, the event class is added first, then the browser is allowed to reflow the content and then,
3206
+ * the active class is added to trigger the animation. The ngAnimate directive will automatically extract the duration
3116
3207
  * of the animation to determine when the animation ends. Once the animation is over then both CSS classes will be
3117
- * removed from the DOM. If a browser does not support CSS transitions then the animation will start and end
3208
+ * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
3118
3209
  * immediately resulting in a DOM element that is at it's final state. This final state is when the DOM element
3119
- * has no CSS animation classes surrounding it.
3210
+ * has no CSS transition/animation classes surrounding it.
3120
3211
  *
3121
3212
  * <h2>JavaScript-defined Animations</h2>
3122
- * In the event that you do not want to use CSS3 animations or if you wish to offer animations to browsers that do not
3123
- * yet support them, then you can make use of JavaScript animations defined inside ngModule.
3213
+ * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations to browsers that do not
3214
+ * yet support them, then you can make use of JavaScript animations defined inside of your AngularJS module.
3124
3215
  *
3125
3216
  * <pre>
3126
3217
  * var ngModule = angular.module('YourApp', []);
@@ -3147,8 +3238,8 @@ function $AnimationProvider($provide) {
3147
3238
  *
3148
3239
  * As you can see, the JavaScript code follows a similar template to the CSS3 animations. Once defined, the animation
3149
3240
  * can be used in the same way with the ngAnimate attribute. Keep in mind that, when using JavaScript-enabled
3150
- * animations, ngAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're using
3151
- * JavaScript animations) to animated the element, but it will not attempt to find any CSS3 transition duration value.
3241
+ * animations, ngAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're not using
3242
+ * CSS animations) to animated the element, but it will not attempt to find any CSS3 transition or animation duration/delay values.
3152
3243
  * It will instead close off the animation once the provided done function is executed. So it's important that you
3153
3244
  * make sure your animations remember to fire off the done function once the animations are complete.
3154
3245
  *
@@ -3156,151 +3247,214 @@ function $AnimationProvider($provide) {
3156
3247
  *
3157
3248
  */
3158
3249
 
3159
- /**
3160
- * @ngdoc function
3161
- * @name ng.$animator
3162
- *
3163
- * @description
3164
- * The $animator service provides the DOM manipulation API which is decorated with animations.
3165
- *
3166
- * @param {Scope} scope the scope for the ng-animate.
3167
- * @param {Attributes} attr the attributes object which contains the ngAnimate key / value pair. (The attributes are
3168
- * passed into the linking function of the directive using the `$animator`.)
3169
- * @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods.
3170
- */
3171
3250
  var $AnimatorProvider = function() {
3172
- this.$get = ['$animation', '$window', '$sniffer', function($animation, $window, $sniffer) {
3173
- return function(scope, attrs) {
3174
- var ngAnimateAttr = attrs.ngAnimate;
3175
- var animator = {};
3251
+ var NG_ANIMATE_CONTROLLER = '$ngAnimateController';
3252
+ var rootAnimateController = {running:true};
3176
3253
 
3177
- /**
3178
- * @ngdoc function
3179
- * @name ng.animator#enter
3180
- * @methodOf ng.$animator
3181
- * @function
3182
- *
3183
- * @description
3184
- * Injects the element object into the DOM (inside of the parent element) and then runs the enter animation.
3185
- *
3186
- * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
3187
- * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
3188
- * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation
3189
- */
3190
- animator.enter = animateActionFactory('enter', insert, noop);
3191
-
3192
- /**
3193
- * @ngdoc function
3194
- * @name ng.animator#leave
3195
- * @methodOf ng.$animator
3196
- * @function
3197
- *
3198
- * @description
3199
- * Runs the leave animation operation and, upon completion, removes the element from the DOM.
3200
- *
3201
- * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
3202
- * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation
3203
- */
3204
- animator.leave = animateActionFactory('leave', noop, remove);
3205
-
3206
- /**
3207
- * @ngdoc function
3208
- * @name ng.animator#move
3209
- * @methodOf ng.$animator
3210
- * @function
3211
- *
3212
- * @description
3213
- * Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or
3214
- * add the element directly after the after element if present. Then the move animation will be run.
3215
- *
3216
- * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
3217
- * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
3218
- * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation
3219
- */
3220
- animator.move = animateActionFactory('move', move, noop);
3254
+ this.$get = ['$animation', '$window', '$sniffer', '$rootElement', '$rootScope',
3255
+ function($animation, $window, $sniffer, $rootElement, $rootScope) {
3256
+ $rootElement.data(NG_ANIMATE_CONTROLLER, rootAnimateController);
3221
3257
 
3222
- /**
3223
- * @ngdoc function
3224
- * @name ng.animator#show
3225
- * @methodOf ng.$animator
3226
- * @function
3227
- *
3228
- * @description
3229
- * Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after.
3230
- *
3231
- * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
3232
- */
3233
- animator.show = animateActionFactory('show', show, noop);
3234
-
3235
- /**
3236
- * @ngdoc function
3237
- * @name ng.animator#hide
3238
- * @methodOf ng.$animator
3239
- *
3240
- * @description
3241
- * Starts the hide animation first and sets the CSS `display` property to `none` upon completion.
3242
- *
3243
- * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
3244
- */
3245
- animator.hide = animateActionFactory('hide', noop, hide);
3246
- return animator;
3258
+ /**
3259
+ * @ngdoc function
3260
+ * @name ng.$animator
3261
+ * @function
3262
+ *
3263
+ * @description
3264
+ * The $animator.create service provides the DOM manipulation API which is decorated with animations.
3265
+ *
3266
+ * @param {Scope} scope the scope for the ng-animate.
3267
+ * @param {Attributes} attr the attributes object which contains the ngAnimate key / value pair. (The attributes are
3268
+ * passed into the linking function of the directive using the `$animator`.)
3269
+ * @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods.
3270
+ */
3271
+ var AnimatorService = function(scope, attrs) {
3272
+ var animator = {};
3273
+
3274
+ /**
3275
+ * @ngdoc function
3276
+ * @name ng.animator#enter
3277
+ * @methodOf ng.$animator
3278
+ * @function
3279
+ *
3280
+ * @description
3281
+ * Injects the element object into the DOM (inside of the parent element) and then runs the enter animation.
3282
+ *
3283
+ * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
3284
+ * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
3285
+ * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation
3286
+ */
3287
+ animator.enter = animateActionFactory('enter', insert, noop);
3288
+
3289
+ /**
3290
+ * @ngdoc function
3291
+ * @name ng.animator#leave
3292
+ * @methodOf ng.$animator
3293
+ * @function
3294
+ *
3295
+ * @description
3296
+ * Runs the leave animation operation and, upon completion, removes the element from the DOM.
3297
+ *
3298
+ * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
3299
+ * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation
3300
+ */
3301
+ animator.leave = animateActionFactory('leave', noop, remove);
3302
+
3303
+ /**
3304
+ * @ngdoc function
3305
+ * @name ng.animator#move
3306
+ * @methodOf ng.$animator
3307
+ * @function
3308
+ *
3309
+ * @description
3310
+ * Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or
3311
+ * add the element directly after the after element if present. Then the move animation will be run.
3312
+ *
3313
+ * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
3314
+ * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
3315
+ * @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation
3316
+ */
3317
+ animator.move = animateActionFactory('move', move, noop);
3318
+
3319
+ /**
3320
+ * @ngdoc function
3321
+ * @name ng.animator#show
3322
+ * @methodOf ng.$animator
3323
+ * @function
3324
+ *
3325
+ * @description
3326
+ * Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after.
3327
+ *
3328
+ * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
3329
+ */
3330
+ animator.show = animateActionFactory('show', show, noop);
3331
+
3332
+ /**
3333
+ * @ngdoc function
3334
+ * @name ng.animator#hide
3335
+ * @methodOf ng.$animator
3336
+ *
3337
+ * @description
3338
+ * Starts the hide animation first and sets the CSS `display` property to `none` upon completion.
3339
+ *
3340
+ * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
3341
+ */
3342
+ animator.hide = animateActionFactory('hide', noop, hide);
3247
3343
 
3248
- function animateActionFactory(type, beforeFn, afterFn) {
3249
- var ngAnimateValue = ngAnimateAttr && scope.$eval(ngAnimateAttr);
3250
- var className = ngAnimateAttr
3251
- ? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type
3252
- : '';
3253
- var animationPolyfill = $animation(className);
3344
+ /**
3345
+ * @ngdoc function
3346
+ * @name ng.animator#animate
3347
+ * @methodOf ng.$animator
3348
+ *
3349
+ * @description
3350
+ * Triggers a custom animation event to be executed on the given element
3351
+ *
3352
+ * @param {jQuery/jqLite element} element that will be animated
3353
+ */
3354
+ animator.animate = function(event, element) {
3355
+ animateActionFactory(event, noop, noop)(element);
3356
+ }
3357
+ return animator;
3358
+
3359
+ function animateActionFactory(type, beforeFn, afterFn) {
3360
+ return function(element, parent, after) {
3361
+ var ngAnimateValue = scope.$eval(attrs.ngAnimate);
3362
+ var className = ngAnimateValue
3363
+ ? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type
3364
+ : '';
3365
+ var animationPolyfill = $animation(className);
3366
+ var polyfillSetup = animationPolyfill && animationPolyfill.setup;
3367
+ var polyfillStart = animationPolyfill && animationPolyfill.start;
3368
+ var polyfillCancel = animationPolyfill && animationPolyfill.cancel;
3369
+
3370
+ if (!className) {
3371
+ beforeFn(element, parent, after);
3372
+ afterFn(element, parent, after);
3373
+ } else {
3374
+ var activeClassName = className + '-active';
3254
3375
 
3255
- var polyfillSetup = animationPolyfill && animationPolyfill.setup;
3256
- var polyfillStart = animationPolyfill && animationPolyfill.start;
3376
+ if (!parent) {
3377
+ parent = after ? after.parent() : element.parent();
3378
+ }
3379
+ if ((!$sniffer.transitions && !polyfillSetup && !polyfillStart) ||
3380
+ (parent.inheritedData(NG_ANIMATE_CONTROLLER) || noop).running) {
3381
+ beforeFn(element, parent, after);
3382
+ afterFn(element, parent, after);
3383
+ return;
3384
+ }
3257
3385
 
3258
- if (!className) {
3259
- return function(element, parent, after) {
3260
- beforeFn(element, parent, after);
3261
- afterFn(element, parent, after);
3262
- }
3263
- } else {
3264
- var setupClass = className + '-setup';
3265
- var startClass = className + '-start';
3386
+ var animationData = element.data(NG_ANIMATE_CONTROLLER) || {};
3387
+ if(animationData.running) {
3388
+ (polyfillCancel || noop)(element);
3389
+ animationData.done();
3390
+ }
3266
3391
 
3267
- return function(element, parent, after) {
3268
- if (!$sniffer.supportsTransitions && !polyfillSetup && !polyfillStart) {
3392
+ element.data(NG_ANIMATE_CONTROLLER, {running:true, done:done});
3393
+ element.addClass(className);
3269
3394
  beforeFn(element, parent, after);
3270
- afterFn(element, parent, after);
3271
- return;
3272
- }
3395
+ if (element.length == 0) return done();
3273
3396
 
3274
- element.addClass(setupClass);
3275
- beforeFn(element, parent, after);
3276
- if (element.length == 0) return done();
3397
+ var memento = (polyfillSetup || noop)(element);
3277
3398
 
3278
- var memento = (polyfillSetup || noop)(element);
3399
+ // $window.setTimeout(beginAnimation, 0); this was causing the element not to animate
3400
+ // keep at 1 for animation dom rerender
3401
+ $window.setTimeout(beginAnimation, 1);
3402
+ }
3279
3403
 
3280
- // $window.setTimeout(beginAnimation, 0); this was causing the element not to animate
3281
- // keep at 1 for animation dom rerender
3282
- $window.setTimeout(beginAnimation, 1);
3404
+ function parseMaxTime(str) {
3405
+ var total = 0, values = isString(str) ? str.split(/\s*,\s*/) : [];
3406
+ forEach(values, function(value) {
3407
+ total = Math.max(parseFloat(value) || 0, total);
3408
+ });
3409
+ return total;
3410
+ }
3283
3411
 
3284
3412
  function beginAnimation() {
3285
- element.addClass(startClass);
3413
+ element.addClass(activeClassName);
3286
3414
  if (polyfillStart) {
3287
3415
  polyfillStart(element, done, memento);
3288
3416
  } else if (isFunction($window.getComputedStyle)) {
3417
+ //one day all browsers will have these properties
3418
+ var w3cAnimationProp = 'animation';
3419
+ var w3cTransitionProp = 'transition';
3420
+
3421
+ //but some still use vendor-prefixed styles
3422
+ var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
3289
3423
  var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
3290
- var w3cTransitionProp = 'transition'; //one day all browsers will have this
3291
3424
 
3292
- var durationKey = 'Duration';
3293
- var duration = 0;
3425
+ var durationKey = 'Duration',
3426
+ delayKey = 'Delay',
3427
+ animationIterationCountKey = 'IterationCount',
3428
+ duration = 0;
3429
+
3294
3430
  //we want all the styles defined before and after
3431
+ var ELEMENT_NODE = 1;
3295
3432
  forEach(element, function(element) {
3296
- var globalStyles = $window.getComputedStyle(element) || {};
3297
- duration = Math.max(
3298
- parseFloat(globalStyles[w3cTransitionProp + durationKey]) ||
3299
- parseFloat(globalStyles[vendorTransitionProp + durationKey]) ||
3300
- 0,
3301
- duration);
3302
- });
3433
+ if (element.nodeType == ELEMENT_NODE) {
3434
+ var w3cProp = w3cTransitionProp,
3435
+ vendorProp = vendorTransitionProp,
3436
+ iterations = 1,
3437
+ elementStyles = $window.getComputedStyle(element) || {};
3438
+
3439
+ //use CSS Animations over CSS Transitions
3440
+ if(parseFloat(elementStyles[w3cAnimationProp + durationKey]) > 0 ||
3441
+ parseFloat(elementStyles[vendorAnimationProp + durationKey]) > 0) {
3442
+ w3cProp = w3cAnimationProp;
3443
+ vendorProp = vendorAnimationProp;
3444
+ iterations = Math.max(parseInt(elementStyles[w3cProp + animationIterationCountKey]) || 0,
3445
+ parseInt(elementStyles[vendorProp + animationIterationCountKey]) || 0,
3446
+ iterations);
3447
+ }
3303
3448
 
3449
+ var parsedDelay = Math.max(parseMaxTime(elementStyles[w3cProp + delayKey]),
3450
+ parseMaxTime(elementStyles[vendorProp + delayKey]));
3451
+
3452
+ var parsedDuration = Math.max(parseMaxTime(elementStyles[w3cProp + durationKey]),
3453
+ parseMaxTime(elementStyles[vendorProp + durationKey]));
3454
+
3455
+ duration = Math.max(parsedDelay + (iterations * parsedDuration), duration);
3456
+ }
3457
+ });
3304
3458
  $window.setTimeout(done, duration * 1000);
3305
3459
  } else {
3306
3460
  done();
@@ -3308,40 +3462,65 @@ var $AnimatorProvider = function() {
3308
3462
  }
3309
3463
 
3310
3464
  function done() {
3311
- afterFn(element, parent, after);
3312
- element.removeClass(setupClass);
3313
- element.removeClass(startClass);
3465
+ if(!done.run) {
3466
+ done.run = true;
3467
+ afterFn(element, parent, after);
3468
+ element.removeClass(className);
3469
+ element.removeClass(activeClassName);
3470
+ element.removeData(NG_ANIMATE_CONTROLLER);
3471
+ }
3314
3472
  }
3473
+ };
3474
+ }
3475
+
3476
+ function show(element) {
3477
+ element.css('display', '');
3478
+ }
3479
+
3480
+ function hide(element) {
3481
+ element.css('display', 'none');
3482
+ }
3483
+
3484
+ function insert(element, parent, after) {
3485
+ if (after) {
3486
+ after.after(element);
3487
+ } else {
3488
+ parent.append(element);
3315
3489
  }
3316
3490
  }
3317
- }
3318
- }
3319
-
3320
- function show(element) {
3321
- element.css('display', '');
3322
- }
3323
-
3324
- function hide(element) {
3325
- element.css('display', 'none');
3326
- }
3491
+
3492
+ function remove(element) {
3493
+ element.remove();
3494
+ }
3495
+
3496
+ function move(element, parent, after) {
3497
+ // Do not remove element before insert. Removing will cause data associated with the
3498
+ // element to be dropped. Insert will implicitly do the remove.
3499
+ insert(element, parent, after);
3500
+ }
3501
+ };
3327
3502
 
3328
- function insert(element, parent, after) {
3329
- if (after) {
3330
- after.after(element);
3331
- } else {
3332
- parent.append(element);
3503
+ /**
3504
+ * @ngdoc function
3505
+ * @name ng.animator#enabled
3506
+ * @methodOf ng.$animator
3507
+ * @function
3508
+ *
3509
+ * @param {Boolean=} If provided then set the animation on or off.
3510
+ * @return {Boolean} Current animation state.
3511
+ *
3512
+ * @description
3513
+ * Globally enables/disables animations.
3514
+ *
3515
+ */
3516
+ AnimatorService.enabled = function(value) {
3517
+ if (arguments.length) {
3518
+ rootAnimateController.running = !value;
3333
3519
  }
3334
- }
3335
-
3336
- function remove(element) {
3337
- element.remove();
3338
- }
3520
+ return !rootAnimateController.running;
3521
+ };
3339
3522
 
3340
- function move(element, parent, after) {
3341
- // Do not remove element before insert. Removing will cause data associated with the
3342
- // element to be dropped. Insert will implicitly do the remove.
3343
- insert(element, parent, after);
3344
- }
3523
+ return AnimatorService;
3345
3524
  }];
3346
3525
  };
3347
3526
 
@@ -3642,7 +3821,13 @@ function Browser(window, document, $log, $sniffer) {
3642
3821
  cookie = cookieArray[i];
3643
3822
  index = cookie.indexOf('=');
3644
3823
  if (index > 0) { //ignore nameless cookies
3645
- lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1));
3824
+ var name = unescape(cookie.substring(0, index));
3825
+ // the first value that is seen for a cookie is the most
3826
+ // specific one. values for the same cookie name that
3827
+ // follow are for less specific paths.
3828
+ if (lastCookies[name] === undefined) {
3829
+ lastCookies[name] = unescape(cookie.substring(index + 1));
3830
+ }
3646
3831
  }
3647
3832
  }
3648
3833
  }
@@ -4455,9 +4640,9 @@ function $CompileProvider($provide) {
4455
4640
 
4456
4641
 
4457
4642
  /**
4458
- * Once the directives have been collected their compile functions is executed. This method
4643
+ * Once the directives have been collected, their compile functions are executed. This method
4459
4644
  * is responsible for inlining directive templates as well as terminating the application
4460
- * of the directives if the terminal directive has been reached..
4645
+ * of the directives if the terminal directive has been reached.
4461
4646
  *
4462
4647
  * @param {Array} directives Array of collected directives to execute their compile function.
4463
4648
  * this needs to be pre-sorted by priority order.
@@ -4465,11 +4650,11 @@ function $CompileProvider($provide) {
4465
4650
  * @param {Object} templateAttrs The shared attribute function
4466
4651
  * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
4467
4652
  * scope argument is auto-generated to the new child of the transcluded parent scope.
4468
- * @param {DOMElement} $rootElement If we are working on the root of the compile tree then this
4469
- * argument has the root jqLite array so that we can replace widgets on it.
4653
+ * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
4654
+ * argument has the root jqLite array so that we can replace nodes on it.
4470
4655
  * @returns linkFn
4471
4656
  */
4472
- function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) {
4657
+ function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
4473
4658
  var terminalPriority = -Number.MAX_VALUE,
4474
4659
  preLinkFns = [],
4475
4660
  postLinkFns = [],
@@ -4523,7 +4708,7 @@ function $CompileProvider($provide) {
4523
4708
  $compileNode = templateAttrs.$$element =
4524
4709
  jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
4525
4710
  compileNode = $compileNode[0];
4526
- replaceWith($rootElement, jqLite($template[0]), compileNode);
4711
+ replaceWith(jqCollection, jqLite($template[0]), compileNode);
4527
4712
  childTranscludeFn = compile($template, transcludeFn, terminalPriority);
4528
4713
  } else {
4529
4714
  $template = jqLite(JQLiteClone(compileNode)).contents();
@@ -4552,7 +4737,7 @@ function $CompileProvider($provide) {
4552
4737
  throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
4553
4738
  }
4554
4739
 
4555
- replaceWith($rootElement, $compileNode, compileNode);
4740
+ replaceWith(jqCollection, $compileNode, compileNode);
4556
4741
 
4557
4742
  var newTemplateAttrs = {$attr: {}};
4558
4743
 
@@ -4580,7 +4765,7 @@ function $CompileProvider($provide) {
4580
4765
  assertNoDuplicate('template', templateDirective, directive, $compileNode);
4581
4766
  templateDirective = directive;
4582
4767
  nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
4583
- nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace,
4768
+ nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
4584
4769
  childTranscludeFn);
4585
4770
  ii = directives.length;
4586
4771
  } else if (directive.compile) {
@@ -4721,7 +4906,7 @@ function $CompileProvider($provide) {
4721
4906
  parentGet = $parse(attrs[attrName]);
4722
4907
  scope[scopeName] = function(locals) {
4723
4908
  return parentGet(parentScope, locals);
4724
- }
4909
+ };
4725
4910
  break;
4726
4911
  }
4727
4912
 
@@ -5120,7 +5305,8 @@ function directiveLinkingFn(
5120
5305
  * {@link ng.$controllerProvider#register register} method.
5121
5306
  */
5122
5307
  function $ControllerProvider() {
5123
- var controllers = {};
5308
+ var controllers = {},
5309
+ CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
5124
5310
 
5125
5311
 
5126
5312
  /**
@@ -5165,17 +5351,32 @@ function $ControllerProvider() {
5165
5351
  * a service, so that one can override this service with {@link https://gist.github.com/1649788
5166
5352
  * BC version}.
5167
5353
  */
5168
- return function(constructor, locals) {
5169
- if(isString(constructor)) {
5170
- var name = constructor;
5171
- constructor = controllers.hasOwnProperty(name)
5172
- ? controllers[name]
5173
- : getter(locals.$scope, name, true) || getter($window, name, true);
5354
+ return function(expression, locals) {
5355
+ var instance, match, constructor, identifier;
5356
+
5357
+ if(isString(expression)) {
5358
+ match = expression.match(CNTRL_REG),
5359
+ constructor = match[1],
5360
+ identifier = match[3];
5361
+ expression = controllers.hasOwnProperty(constructor)
5362
+ ? controllers[constructor]
5363
+ : getter(locals.$scope, constructor, true) || getter($window, constructor, true);
5364
+
5365
+ assertArgFn(expression, constructor, true);
5366
+ }
5174
5367
 
5175
- assertArgFn(constructor, name, true);
5368
+ instance = $injector.instantiate(expression, locals);
5369
+
5370
+ if (identifier) {
5371
+ if (typeof locals.$scope !== 'object') {
5372
+ throw new Error('Can not export controller as "' + identifier + '". ' +
5373
+ 'No scope object provided!');
5374
+ }
5375
+
5376
+ locals.$scope[identifier] = instance;
5176
5377
  }
5177
5378
 
5178
- return $injector.instantiate(constructor, locals);
5379
+ return instance;
5179
5380
  };
5180
5381
  }];
5181
5382
  }
@@ -5214,7 +5415,7 @@ function $DocumentProvider(){
5214
5415
  *
5215
5416
  */
5216
5417
  function $ExceptionHandlerProvider() {
5217
- this.$get = ['$log', function($log){
5418
+ this.$get = ['$log', function($log) {
5218
5419
  return function(exception, cause) {
5219
5420
  $log.error.apply($log, arguments);
5220
5421
  };
@@ -5408,9 +5609,8 @@ function $InterpolateProvider() {
5408
5609
  }];
5409
5610
  }
5410
5611
 
5411
- var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
5412
- PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,
5413
- HASH_MATCH = PATH_MATCH,
5612
+ var SERVER_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
5613
+ PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
5414
5614
  DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
5415
5615
 
5416
5616
 
@@ -5431,30 +5631,23 @@ function encodePath(path) {
5431
5631
  return segments.join('/');
5432
5632
  }
5433
5633
 
5434
- function stripHash(url) {
5435
- return url.split('#')[0];
5436
- }
5437
-
5438
-
5439
5634
  function matchUrl(url, obj) {
5440
- var match = URL_MATCH.exec(url);
5635
+ var match = SERVER_MATCH.exec(url);
5441
5636
 
5442
- match = {
5443
- protocol: match[1],
5444
- host: match[3],
5445
- port: int(match[5]) || DEFAULT_PORTS[match[1]] || null,
5446
- path: match[6] || '/',
5447
- search: match[8],
5448
- hash: match[10]
5449
- };
5637
+ obj.$$protocol = match[1];
5638
+ obj.$$host = match[3];
5639
+ obj.$$port = int(match[5]) || DEFAULT_PORTS[match[1]] || null;
5640
+ }
5450
5641
 
5451
- if (obj) {
5452
- obj.$$protocol = match.protocol;
5453
- obj.$$host = match.host;
5454
- obj.$$port = match.port;
5455
- }
5642
+ function matchAppUrl(url, obj) {
5643
+ var match = PATH_MATCH.exec(url);
5456
5644
 
5457
- return match;
5645
+ obj.$$path = decodeURIComponent(match[1]);
5646
+ obj.$$search = parseKeyValue(match[3]);
5647
+ obj.$$hash = decodeURIComponent(match[5] || '');
5648
+
5649
+ // make sure path starts with '/';
5650
+ if (obj.$$path && obj.$$path.charAt(0) != '/') obj.$$path = '/' + obj.$$path;
5458
5651
  }
5459
5652
 
5460
5653
 
@@ -5462,77 +5655,62 @@ function composeProtocolHostPort(protocol, host, port) {
5462
5655
  return protocol + '://' + host + (port == DEFAULT_PORTS[protocol] ? '' : ':' + port);
5463
5656
  }
5464
5657
 
5465
-
5466
- function pathPrefixFromBase(basePath) {
5467
- return basePath.substr(0, basePath.lastIndexOf('/'));
5658
+ /**
5659
+ *
5660
+ * @param {string} begin
5661
+ * @param {string} whole
5662
+ * @param {string} otherwise
5663
+ * @returns {string} returns text from whole after begin or otherwise if it does not begin with expected string.
5664
+ */
5665
+ function beginsWith(begin, whole, otherwise) {
5666
+ return whole.indexOf(begin) == 0 ? whole.substr(begin.length) : otherwise;
5468
5667
  }
5469
5668
 
5470
5669
 
5471
- function convertToHtml5Url(url, basePath, hashPrefix) {
5472
- var match = matchUrl(url);
5473
-
5474
- // already html5 url
5475
- if (decodeURIComponent(match.path) != basePath || isUndefined(match.hash) ||
5476
- match.hash.indexOf(hashPrefix) !== 0) {
5477
- return url;
5478
- // convert hashbang url -> html5 url
5479
- } else {
5480
- return composeProtocolHostPort(match.protocol, match.host, match.port) +
5481
- pathPrefixFromBase(basePath) + match.hash.substr(hashPrefix.length);
5482
- }
5670
+ function stripHash(url) {
5671
+ var index = url.indexOf('#');
5672
+ return index == -1 ? url : url.substr(0, index);
5483
5673
  }
5484
5674
 
5485
5675
 
5486
- function convertToHashbangUrl(url, basePath, hashPrefix) {
5487
- var match = matchUrl(url);
5488
-
5489
- // already hashbang url
5490
- if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
5491
- match.hash.indexOf(hashPrefix) === 0) {
5492
- return url;
5493
- // convert html5 url -> hashbang url
5494
- } else {
5495
- var search = match.search && '?' + match.search || '',
5496
- hash = match.hash && '#' + match.hash || '',
5497
- pathPrefix = pathPrefixFromBase(basePath),
5498
- path = match.path.substr(pathPrefix.length);
5499
-
5500
- if (match.path.indexOf(pathPrefix) !== 0) {
5501
- throw Error('Invalid url "' + url + '", missing path prefix "' + pathPrefix + '" !');
5502
- }
5676
+ function stripFile(url) {
5677
+ return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
5678
+ }
5503
5679
 
5504
- return composeProtocolHostPort(match.protocol, match.host, match.port) + basePath +
5505
- '#' + hashPrefix + path + search + hash;
5506
- }
5680
+ /* return the server only */
5681
+ function serverBase(url) {
5682
+ return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
5507
5683
  }
5508
5684
 
5509
5685
 
5510
5686
  /**
5511
- * LocationUrl represents an url
5687
+ * LocationHtml5Url represents an url
5512
5688
  * This object is exposed as $location service when HTML5 mode is enabled and supported
5513
5689
  *
5514
5690
  * @constructor
5515
- * @param {string} url HTML5 url
5516
- * @param {string} pathPrefix
5691
+ * @param {string} appBase application base URL
5692
+ * @param {string} basePrefix url path prefix
5517
5693
  */
5518
- function LocationUrl(url, pathPrefix, appBaseUrl) {
5519
- pathPrefix = pathPrefix || '';
5520
-
5694
+ function LocationHtml5Url(appBase, basePrefix) {
5695
+ basePrefix = basePrefix || '';
5696
+ var appBaseNoFile = stripFile(appBase);
5521
5697
  /**
5522
5698
  * Parse given html5 (regular) url string into properties
5523
5699
  * @param {string} newAbsoluteUrl HTML5 url
5524
5700
  * @private
5525
5701
  */
5526
- this.$$parse = function(newAbsoluteUrl) {
5527
- var match = matchUrl(newAbsoluteUrl, this);
5528
-
5529
- if (match.path.indexOf(pathPrefix) !== 0) {
5530
- throw Error('Invalid url "' + newAbsoluteUrl + '", missing path prefix "' + pathPrefix + '" !');
5702
+ this.$$parse = function(url) {
5703
+ var parsed = {}
5704
+ matchUrl(url, parsed);
5705
+ var pathUrl = beginsWith(appBaseNoFile, url);
5706
+ if (!isString(pathUrl)) {
5707
+ throw Error('Invalid url "' + url + '", missing path prefix "' + appBaseNoFile + '".');
5708
+ }
5709
+ matchAppUrl(pathUrl, parsed);
5710
+ extend(this, parsed);
5711
+ if (!this.$$path) {
5712
+ this.$$path = '/';
5531
5713
  }
5532
-
5533
- this.$$path = decodeURIComponent(match.path.substr(pathPrefix.length));
5534
- this.$$search = parseKeyValue(match.search);
5535
- this.$$hash = match.hash && decodeURIComponent(match.hash) || '';
5536
5714
 
5537
5715
  this.$$compose();
5538
5716
  };
@@ -5546,19 +5724,25 @@ function LocationUrl(url, pathPrefix, appBaseUrl) {
5546
5724
  hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
5547
5725
 
5548
5726
  this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
5549
- this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) +
5550
- pathPrefix + this.$$url;
5727
+ this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
5551
5728
  };
5552
5729
 
5730
+ this.$$rewrite = function(url) {
5731
+ var appUrl, prevAppUrl;
5553
5732
 
5554
- this.$$rewriteAppUrl = function(absoluteLinkUrl) {
5555
- if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
5556
- return absoluteLinkUrl;
5733
+ if ( (appUrl = beginsWith(appBase, url)) !== undefined ) {
5734
+ prevAppUrl = appUrl;
5735
+ if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) {
5736
+ return appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
5737
+ } else {
5738
+ return appBase + prevAppUrl;
5739
+ }
5740
+ } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) {
5741
+ return appBaseNoFile + appUrl;
5742
+ } else if (appBaseNoFile == url + '/') {
5743
+ return appBaseNoFile;
5557
5744
  }
5558
- }
5559
-
5560
-
5561
- this.$$parse(url);
5745
+ }
5562
5746
  }
5563
5747
 
5564
5748
 
@@ -5567,11 +5751,11 @@ function LocationUrl(url, pathPrefix, appBaseUrl) {
5567
5751
  * This object is exposed as $location service when html5 history api is disabled or not supported
5568
5752
  *
5569
5753
  * @constructor
5570
- * @param {string} url Legacy url
5571
- * @param {string} hashPrefix Prefix for hash part (containing path and search)
5754
+ * @param {string} appBase application base URL
5755
+ * @param {string} hashPrefix hashbang prefix
5572
5756
  */
5573
- function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
5574
- var basePath;
5757
+ function LocationHashbangUrl(appBase, hashPrefix) {
5758
+ var appBaseNoFile = stripFile(appBase);
5575
5759
 
5576
5760
  /**
5577
5761
  * Parse given hashbang url into properties
@@ -5579,24 +5763,16 @@ function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
5579
5763
  * @private
5580
5764
  */
5581
5765
  this.$$parse = function(url) {
5582
- var match = matchUrl(url, this);
5583
-
5584
-
5585
- if (match.hash && match.hash.indexOf(hashPrefix) !== 0) {
5586
- throw Error('Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '" !');
5766
+ matchUrl(url, this);
5767
+ var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
5768
+ if (!isString(withoutBaseUrl)) {
5769
+ throw new Error('Invalid url "' + url + '", does not start with "' + appBase + '".');
5587
5770
  }
5588
-
5589
- basePath = match.path + (match.search ? '?' + match.search : '');
5590
- match = HASH_MATCH.exec((match.hash || '').substr(hashPrefix.length));
5591
- if (match[1]) {
5592
- this.$$path = (match[1].charAt(0) == '/' ? '' : '/') + decodeURIComponent(match[1]);
5593
- } else {
5594
- this.$$path = '';
5771
+ var withoutHashUrl = withoutBaseUrl.charAt(0) == '#' ? beginsWith(hashPrefix, withoutBaseUrl) : withoutBaseUrl;
5772
+ if (!isString(withoutHashUrl)) {
5773
+ throw new Error('Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '".');
5595
5774
  }
5596
-
5597
- this.$$search = parseKeyValue(match[3]);
5598
- this.$$hash = match[5] && decodeURIComponent(match[5]) || '';
5599
-
5775
+ matchAppUrl(withoutHashUrl, this);
5600
5776
  this.$$compose();
5601
5777
  };
5602
5778
 
@@ -5609,22 +5785,48 @@ function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
5609
5785
  hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
5610
5786
 
5611
5787
  this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
5612
- this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) +
5613
- basePath + (this.$$url ? '#' + hashPrefix + this.$$url : '');
5788
+ this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
5614
5789
  };
5615
5790
 
5616
- this.$$rewriteAppUrl = function(absoluteLinkUrl) {
5617
- if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
5618
- return absoluteLinkUrl;
5791
+ this.$$rewrite = function(url) {
5792
+ if(stripHash(appBase) == stripHash(url)) {
5793
+ return url;
5619
5794
  }
5620
5795
  }
5796
+ }
5797
+
5798
+
5799
+ /**
5800
+ * LocationHashbangUrl represents url
5801
+ * This object is exposed as $location service when html5 history api is enabled but the browser
5802
+ * does not support it.
5803
+ *
5804
+ * @constructor
5805
+ * @param {string} appBase application base URL
5806
+ * @param {string} hashPrefix hashbang prefix
5807
+ */
5808
+ function LocationHashbangInHtml5Url(appBase, hashPrefix) {
5809
+ LocationHashbangUrl.apply(this, arguments);
5621
5810
 
5811
+ var appBaseNoFile = stripFile(appBase);
5622
5812
 
5623
- this.$$parse(url);
5813
+ this.$$rewrite = function(url) {
5814
+ var appUrl;
5815
+
5816
+ if ( appBase == stripHash(url) ) {
5817
+ return url;
5818
+ } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) {
5819
+ return appBase + hashPrefix + appUrl;
5820
+ } else if ( appBaseNoFile === url + '/') {
5821
+ return appBaseNoFile;
5822
+ }
5823
+ }
5624
5824
  }
5625
5825
 
5626
5826
 
5627
- LocationUrl.prototype = {
5827
+ LocationHashbangInHtml5Url.prototype =
5828
+ LocationHashbangUrl.prototype =
5829
+ LocationHtml5Url.prototype = {
5628
5830
 
5629
5831
  /**
5630
5832
  * Has any change been replacing ?
@@ -5806,21 +6008,6 @@ LocationUrl.prototype = {
5806
6008
  }
5807
6009
  };
5808
6010
 
5809
- LocationHashbangUrl.prototype = inherit(LocationUrl.prototype);
5810
-
5811
- function LocationHashbangInHtml5Url(url, hashPrefix, appBaseUrl, baseExtra) {
5812
- LocationHashbangUrl.apply(this, arguments);
5813
-
5814
-
5815
- this.$$rewriteAppUrl = function(absoluteLinkUrl) {
5816
- if (absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
5817
- return appBaseUrl + baseExtra + '#' + hashPrefix + absoluteLinkUrl.substr(appBaseUrl.length);
5818
- }
5819
- }
5820
- }
5821
-
5822
- LocationHashbangInHtml5Url.prototype = inherit(LocationHashbangUrl.prototype);
5823
-
5824
6011
  function locationGetter(property) {
5825
6012
  return function() {
5826
6013
  return this[property];
@@ -5917,37 +6104,20 @@ function $LocationProvider(){
5917
6104
  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
5918
6105
  function( $rootScope, $browser, $sniffer, $rootElement) {
5919
6106
  var $location,
5920
- basePath,
5921
- pathPrefix,
5922
- initUrl = $browser.url(),
5923
- initUrlParts = matchUrl(initUrl),
5924
- appBaseUrl;
6107
+ LocationMode,
6108
+ baseHref = $browser.baseHref(),
6109
+ initialUrl = $browser.url(),
6110
+ appBase;
5925
6111
 
5926
6112
  if (html5Mode) {
5927
- basePath = $browser.baseHref() || '/';
5928
- pathPrefix = pathPrefixFromBase(basePath);
5929
- appBaseUrl =
5930
- composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
5931
- pathPrefix + '/';
5932
-
5933
- if ($sniffer.history) {
5934
- $location = new LocationUrl(
5935
- convertToHtml5Url(initUrl, basePath, hashPrefix),
5936
- pathPrefix, appBaseUrl);
5937
- } else {
5938
- $location = new LocationHashbangInHtml5Url(
5939
- convertToHashbangUrl(initUrl, basePath, hashPrefix),
5940
- hashPrefix, appBaseUrl, basePath.substr(pathPrefix.length + 1));
5941
- }
6113
+ appBase = baseHref ? serverBase(initialUrl) + baseHref : initialUrl;
6114
+ LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
5942
6115
  } else {
5943
- appBaseUrl =
5944
- composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
5945
- (initUrlParts.path || '') +
5946
- (initUrlParts.search ? ('?' + initUrlParts.search) : '') +
5947
- '#' + hashPrefix + '/';
5948
-
5949
- $location = new LocationHashbangUrl(initUrl, hashPrefix, appBaseUrl);
6116
+ appBase = stripHash(initialUrl);
6117
+ LocationMode = LocationHashbangUrl;
5950
6118
  }
6119
+ $location = new LocationMode(appBase, '#' + hashPrefix);
6120
+ $location.$$parse($location.$$rewrite(initialUrl));
5951
6121
 
5952
6122
  $rootElement.bind('click', function(event) {
5953
6123
  // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
@@ -5963,28 +6133,34 @@ function $LocationProvider(){
5963
6133
  if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
5964
6134
  }
5965
6135
 
5966
- var absHref = elm.prop('href'),
5967
- rewrittenUrl = $location.$$rewriteAppUrl(absHref);
6136
+ var absHref = elm.prop('href');
6137
+ var rewrittenUrl = $location.$$rewrite(absHref);
5968
6138
 
5969
- if (absHref && !elm.attr('target') && rewrittenUrl) {
5970
- // update location manually
5971
- $location.$$parse(rewrittenUrl);
5972
- $rootScope.$apply();
6139
+ if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
5973
6140
  event.preventDefault();
5974
- // hack to work around FF6 bug 684208 when scenario runner clicks on links
5975
- window.angular['ff-684208-preventDefault'] = true;
6141
+ if (rewrittenUrl != $browser.url()) {
6142
+ // update location manually
6143
+ $location.$$parse(rewrittenUrl);
6144
+ $rootScope.$apply();
6145
+ // hack to work around FF6 bug 684208 when scenario runner clicks on links
6146
+ window.angular['ff-684208-preventDefault'] = true;
6147
+ }
5976
6148
  }
5977
6149
  });
5978
6150
 
5979
6151
 
5980
6152
  // rewrite hashbang url <> html5 url
5981
- if ($location.absUrl() != initUrl) {
6153
+ if ($location.absUrl() != initialUrl) {
5982
6154
  $browser.url($location.absUrl(), true);
5983
6155
  }
5984
6156
 
5985
6157
  // update $location when $browser url changes
5986
6158
  $browser.onUrlChange(function(newUrl) {
5987
6159
  if ($location.absUrl() != newUrl) {
6160
+ if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) {
6161
+ $browser.url($location.absUrl());
6162
+ return;
6163
+ }
5988
6164
  $rootScope.$evalAsync(function() {
5989
6165
  var oldUrl = $location.absUrl();
5990
6166
 
@@ -6241,7 +6417,7 @@ function lex(text, csp){
6241
6417
  (token=tokens[tokens.length-1])) {
6242
6418
  token.json = token.text.indexOf('.') == -1;
6243
6419
  }
6244
- } else if (is('(){}[].,;:')) {
6420
+ } else if (is('(){}[].,;:?')) {
6245
6421
  tokens.push({
6246
6422
  index:index,
6247
6423
  text:ch,
@@ -6345,10 +6521,10 @@ function lex(text, csp){
6345
6521
  function readIdent() {
6346
6522
  var ident = "",
6347
6523
  start = index,
6348
- lastDot, peekIndex, methodName;
6524
+ lastDot, peekIndex, methodName, ch;
6349
6525
 
6350
6526
  while (index < text.length) {
6351
- var ch = text.charAt(index);
6527
+ ch = text.charAt(index);
6352
6528
  if (ch == '.' || isIdent(ch) || isNumber(ch)) {
6353
6529
  if (ch == '.') lastDot = index;
6354
6530
  ident += ch;
@@ -6362,7 +6538,7 @@ function lex(text, csp){
6362
6538
  if (lastDot) {
6363
6539
  peekIndex = index;
6364
6540
  while(peekIndex < text.length) {
6365
- var ch = text.charAt(peekIndex);
6541
+ ch = text.charAt(peekIndex);
6366
6542
  if (ch == '(') {
6367
6543
  methodName = ident.substr(lastDot - start + 1);
6368
6544
  ident = ident.substr(0, lastDot - start);
@@ -6542,6 +6718,14 @@ function parser(text, json, $filter, csp){
6542
6718
  });
6543
6719
  }
6544
6720
 
6721
+ function ternaryFn(left, middle, right){
6722
+ return extend(function(self, locals){
6723
+ return left(self, locals) ? middle(self, locals) : right(self, locals);
6724
+ }, {
6725
+ constant: left.constant && middle.constant && right.constant
6726
+ });
6727
+ }
6728
+
6545
6729
  function binaryFn(left, fn, right) {
6546
6730
  return extend(function(self, locals) {
6547
6731
  return fn(self, locals, left, right);
@@ -6612,7 +6796,7 @@ function parser(text, json, $filter, csp){
6612
6796
  }
6613
6797
 
6614
6798
  function _assignment() {
6615
- var left = logicalOR();
6799
+ var left = ternary();
6616
6800
  var right;
6617
6801
  var token;
6618
6802
  if ((token = expect('='))) {
@@ -6620,15 +6804,33 @@ function parser(text, json, $filter, csp){
6620
6804
  throwError("implies assignment but [" +
6621
6805
  text.substring(0, token.index) + "] can not be assigned to", token);
6622
6806
  }
6623
- right = logicalOR();
6624
- return function(self, locals){
6625
- return left.assign(self, right(self, locals), locals);
6807
+ right = ternary();
6808
+ return function(scope, locals){
6809
+ return left.assign(scope, right(scope, locals), locals);
6626
6810
  };
6627
6811
  } else {
6628
6812
  return left;
6629
6813
  }
6630
6814
  }
6631
6815
 
6816
+ function ternary() {
6817
+ var left = logicalOR();
6818
+ var middle;
6819
+ var token;
6820
+ if((token = expect('?'))){
6821
+ middle = ternary();
6822
+ if((token = expect(':'))){
6823
+ return ternaryFn(left, middle, ternary());
6824
+ }
6825
+ else {
6826
+ throwError('expected :', token);
6827
+ }
6828
+ }
6829
+ else {
6830
+ return left;
6831
+ }
6832
+ }
6833
+
6632
6834
  function logicalOR() {
6633
6835
  var left = logicalAND();
6634
6836
  var token;
@@ -6742,12 +6944,12 @@ function parser(text, json, $filter, csp){
6742
6944
  var field = expect().text;
6743
6945
  var getter = getterFn(field, csp);
6744
6946
  return extend(
6745
- function(self, locals) {
6746
- return getter(object(self, locals), locals);
6947
+ function(scope, locals, self) {
6948
+ return getter(self || object(scope, locals), locals);
6747
6949
  },
6748
6950
  {
6749
- assign:function(self, value, locals) {
6750
- return setter(object(self, locals), field, value);
6951
+ assign:function(scope, value, locals) {
6952
+ return setter(object(scope, locals), field, value);
6751
6953
  }
6752
6954
  }
6753
6955
  );
@@ -6788,14 +6990,14 @@ function parser(text, json, $filter, csp){
6788
6990
  } while (expect(','));
6789
6991
  }
6790
6992
  consume(')');
6791
- return function(self, locals){
6993
+ return function(scope, locals){
6792
6994
  var args = [],
6793
- context = contextGetter ? contextGetter(self, locals) : self;
6995
+ context = contextGetter ? contextGetter(scope, locals) : scope;
6794
6996
 
6795
6997
  for ( var i = 0; i < argsFn.length; i++) {
6796
- args.push(argsFn[i](self, locals));
6998
+ args.push(argsFn[i](scope, locals));
6797
6999
  }
6798
- var fnPtr = fn(self, locals) || noop;
7000
+ var fnPtr = fn(scope, locals, context) || noop;
6799
7001
  // IE stupidity!
6800
7002
  return fnPtr.apply
6801
7003
  ? fnPtr.apply(context, args)
@@ -6849,8 +7051,7 @@ function parser(text, json, $filter, csp){
6849
7051
  var object = {};
6850
7052
  for ( var i = 0; i < keyValues.length; i++) {
6851
7053
  var keyValue = keyValues[i];
6852
- var value = keyValue.value(self, locals);
6853
- object[keyValue.key] = value;
7054
+ object[keyValue.key] = keyValue.value(self, locals);
6854
7055
  }
6855
7056
  return object;
6856
7057
  }, {
@@ -6975,7 +7176,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4) {
6975
7176
  }
6976
7177
  return pathVal;
6977
7178
  };
6978
- };
7179
+ }
6979
7180
 
6980
7181
  function getterFn(path, csp) {
6981
7182
  if (getterFnCache.hasOwnProperty(path)) {
@@ -6990,7 +7191,7 @@ function getterFn(path, csp) {
6990
7191
  fn = (pathKeysLength < 6)
6991
7192
  ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4])
6992
7193
  : function(scope, locals) {
6993
- var i = 0, val
7194
+ var i = 0, val;
6994
7195
  do {
6995
7196
  val = cspSafeGetterFn(
6996
7197
  pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++]
@@ -7178,6 +7379,11 @@ function $ParseProvider() {
7178
7379
  * This method *returns a new promise* which is resolved or rejected via the return value of the
7179
7380
  * `successCallback` or `errorCallback`.
7180
7381
  *
7382
+ * - `always(callback)` – allows you to observe either the fulfillment or rejection of a promise,
7383
+ * but to do so without modifying the final value. This is useful to release resources or do some
7384
+ * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
7385
+ * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
7386
+ * more information.
7181
7387
  *
7182
7388
  * # Chaining promises
7183
7389
  *
@@ -7208,7 +7414,7 @@ function $ParseProvider() {
7208
7414
  * models and avoiding unnecessary browser repaints, which would result in flickering UI.
7209
7415
  * - $q promises are recognized by the templating engine in angular, which means that in templates
7210
7416
  * you can treat promises attached to a scope as if they were the resulting values.
7211
- * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains
7417
+ * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
7212
7418
  * all the important functionality needed for common async tasks.
7213
7419
  *
7214
7420
  * # Testing
@@ -7323,6 +7529,42 @@ function qFactory(nextTick, exceptionHandler) {
7323
7529
  }
7324
7530
 
7325
7531
  return result.promise;
7532
+ },
7533
+ always: function(callback) {
7534
+
7535
+ function makePromise(value, resolved) {
7536
+ var result = defer();
7537
+ if (resolved) {
7538
+ result.resolve(value);
7539
+ } else {
7540
+ result.reject(value);
7541
+ }
7542
+ return result.promise;
7543
+ }
7544
+
7545
+ function handleCallback(value, isResolved) {
7546
+ var callbackOutput = null;
7547
+ try {
7548
+ callbackOutput = (callback ||defaultCallback)();
7549
+ } catch(e) {
7550
+ return makePromise(e, false);
7551
+ }
7552
+ if (callbackOutput && callbackOutput.then) {
7553
+ return callbackOutput.then(function() {
7554
+ return makePromise(value, isResolved);
7555
+ }, function(error) {
7556
+ return makePromise(error, false);
7557
+ });
7558
+ } else {
7559
+ return makePromise(value, isResolved);
7560
+ }
7561
+ }
7562
+
7563
+ return this.then(function(value) {
7564
+ return handleCallback(value, true);
7565
+ }, function(error) {
7566
+ return handleCallback(error, false);
7567
+ });
7326
7568
  }
7327
7569
  }
7328
7570
  };
@@ -7403,10 +7645,7 @@ function qFactory(nextTick, exceptionHandler) {
7403
7645
  * the promise comes from a source that can't be trusted.
7404
7646
  *
7405
7647
  * @param {*} value Value or a promise
7406
- * @returns {Promise} Returns a single promise that will be resolved with an array of values,
7407
- * each value corresponding to the promise at the same index in the `promises` array. If any of
7408
- * the promises is resolved with a rejection, this resulting promise will be resolved with the
7409
- * same rejection.
7648
+ * @returns {Promise} Returns a promise of the passed value or promise
7410
7649
  */
7411
7650
  var when = function(value, callback, errback) {
7412
7651
  var result = defer(),
@@ -7545,6 +7784,8 @@ function $RouteProvider(){
7545
7784
  * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly
7546
7785
  * created scope or the name of a {@link angular.Module#controller registered controller}
7547
7786
  * if passed as a string.
7787
+ * - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
7788
+ * published to scope under the `controllerAs` name.
7548
7789
  * - `template` – `{string=|function()=}` – html template as a string or function that returns
7549
7790
  * an html template as a string which should be used by {@link ng.directive:ngView ngView} or
7550
7791
  * {@link ng.directive:ngInclude ngInclude} directives.
@@ -8039,22 +8280,22 @@ function $RouteParamsProvider() {
8039
8280
  /**
8040
8281
  * DESIGN NOTES
8041
8282
  *
8042
- * The design decisions behind the scope ware heavily favored for speed and memory consumption.
8283
+ * The design decisions behind the scope are heavily favored for speed and memory consumption.
8043
8284
  *
8044
8285
  * The typical use of scope is to watch the expressions, which most of the time return the same
8045
8286
  * value as last time so we optimize the operation.
8046
8287
  *
8047
- * Closures construction is expensive from speed as well as memory:
8048
- * - no closures, instead ups prototypical inheritance for API
8288
+ * Closures construction is expensive in terms of speed as well as memory:
8289
+ * - No closures, instead use prototypical inheritance for API
8049
8290
  * - Internal state needs to be stored on scope directly, which means that private state is
8050
8291
  * exposed as $$____ properties
8051
8292
  *
8052
8293
  * Loop operations are optimized by using while(count--) { ... }
8053
8294
  * - this means that in order to keep the same order of execution as addition we have to add
8054
- * items to the array at the begging (shift) instead of at the end (push)
8295
+ * items to the array at the beginning (shift) instead of at the end (push)
8055
8296
  *
8056
8297
  * Child scopes are created and removed often
8057
- * - Using array would be slow since inserts in meddle are expensive so we use linked list
8298
+ * - Using an array would be slow since inserts in middle are expensive so we use linked list
8058
8299
  *
8059
8300
  * There are few watches then a lot of observers. This is why you don't want the observer to be
8060
8301
  * implemented in the same way as watch. Watch requires return of initialization function which
@@ -8076,7 +8317,7 @@ function $RouteParamsProvider() {
8076
8317
  * @methodOf ng.$rootScopeProvider
8077
8318
  * @description
8078
8319
  *
8079
- * Sets the number of digest iteration the scope should attempt to execute before giving up and
8320
+ * Sets the number of digest iterations the scope should attempt to execute before giving up and
8080
8321
  * assuming that the model is unstable.
8081
8322
  *
8082
8323
  * The current default is 10 iterations.
@@ -8412,7 +8653,7 @@ function $RootScopeProvider(){
8412
8653
  oldValue = newValue;
8413
8654
  changeDetected++;
8414
8655
  }
8415
- } else if (isArray(newValue)) {
8656
+ } else if (isArrayLike(newValue)) {
8416
8657
  if (oldValue !== internalArray) {
8417
8658
  // we are transitioning from something which was not an array into array.
8418
8659
  oldValue = internalArray;
@@ -8486,7 +8727,7 @@ function $RootScopeProvider(){
8486
8727
  * @function
8487
8728
  *
8488
8729
  * @description
8489
- * Process all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
8730
+ * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
8490
8731
  * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the
8491
8732
  * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are
8492
8733
  * firing. This means that it is possible to get into an infinite loop. This function will throw
@@ -8829,7 +9070,7 @@ function $RootScopeProvider(){
8829
9070
  * Afterwards, the event traverses upwards toward the root scope and calls all registered
8830
9071
  * listeners along the way. The event will stop propagating if one of the listeners cancels it.
8831
9072
  *
8832
- * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
9073
+ * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
8833
9074
  * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
8834
9075
  *
8835
9076
  * @param {string} name Event name to emit.
@@ -8898,7 +9139,7 @@ function $RootScopeProvider(){
8898
9139
  * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
8899
9140
  * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
8900
9141
  *
8901
- * @param {string} name Event name to emit.
9142
+ * @param {string} name Event name to broadcast.
8902
9143
  * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
8903
9144
  * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
8904
9145
  */
@@ -8992,7 +9233,8 @@ function $RootScopeProvider(){
8992
9233
  *
8993
9234
  * @property {boolean} history Does the browser support html5 history api ?
8994
9235
  * @property {boolean} hashchange Does the browser support hashchange event ?
8995
- * @property {boolean} supportsTransitions Does the browser support CSS transition events ?
9236
+ * @property {boolean} transitions Does the browser support CSS transition events ?
9237
+ * @property {boolean} animations Does the browser support CSS animation events ?
8996
9238
  *
8997
9239
  * @description
8998
9240
  * This is very simple implementation of testing browser's features.
@@ -9006,6 +9248,7 @@ function $SnifferProvider() {
9006
9248
  vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
9007
9249
  bodyStyle = document.body && document.body.style,
9008
9250
  transitions = false,
9251
+ animations = false,
9009
9252
  match;
9010
9253
 
9011
9254
  if (bodyStyle) {
@@ -9016,7 +9259,8 @@ function $SnifferProvider() {
9016
9259
  break;
9017
9260
  }
9018
9261
  }
9019
- transitions = !!(vendorPrefix + 'Transition' in bodyStyle);
9262
+ transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
9263
+ animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
9020
9264
  }
9021
9265
 
9022
9266
 
@@ -9044,7 +9288,8 @@ function $SnifferProvider() {
9044
9288
  },
9045
9289
  csp: document.securityPolicy ? document.securityPolicy.isActive : false,
9046
9290
  vendorPrefix: vendorPrefix,
9047
- supportsTransitions : transitions
9291
+ transitions : transitions,
9292
+ animations : animations
9048
9293
  };
9049
9294
  }];
9050
9295
  }
@@ -9065,10 +9310,23 @@ function $SnifferProvider() {
9065
9310
  * @example
9066
9311
  <doc:example>
9067
9312
  <doc:source>
9068
- <input ng-init="$window = $service('$window'); greeting='Hello World!'" type="text" ng-model="greeting" />
9069
- <button ng-click="$window.alert(greeting)">ALERT</button>
9313
+ <script>
9314
+ function Ctrl($scope, $window) {
9315
+ $scope.$window = $window;
9316
+ $scope.greeting = 'Hello, World!';
9317
+ }
9318
+ </script>
9319
+ <div ng-controller="Ctrl">
9320
+ <input type="text" ng-model="greeting" />
9321
+ <button ng-click="$window.alert(greeting)">ALERT</button>
9322
+ </div>
9070
9323
  </doc:source>
9071
9324
  <doc:scenario>
9325
+ it('should display the greeting in the input box', function() {
9326
+ input('greeting').enter('Hello, E2E Tests');
9327
+ // If we click the button it will block the test runner
9328
+ // element(':button').click();
9329
+ });
9072
9330
  </doc:scenario>
9073
9331
  </doc:example>
9074
9332
  */
@@ -9128,7 +9386,7 @@ function isSameDomain(requestUrl, locationUrl) {
9128
9386
  relativeProtocol: match[2] === undefined || match[2] === ''
9129
9387
  };
9130
9388
 
9131
- match = URL_MATCH.exec(locationUrl);
9389
+ match = SERVER_MATCH.exec(locationUrl);
9132
9390
  var domain2 = {
9133
9391
  protocol: match[1],
9134
9392
  host: match[3],
@@ -9199,7 +9457,8 @@ function isSuccess(status) {
9199
9457
  function $HttpProvider() {
9200
9458
  var JSON_START = /^\s*(\[|\{[^\{])/,
9201
9459
  JSON_END = /[\}\]]\s*$/,
9202
- PROTECTION_PREFIX = /^\)\]\}',?\n/;
9460
+ PROTECTION_PREFIX = /^\)\]\}',?\n/,
9461
+ CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'};
9203
9462
 
9204
9463
  var defaults = this.defaults = {
9205
9464
  // transform incoming response data
@@ -9223,8 +9482,9 @@ function $HttpProvider() {
9223
9482
  common: {
9224
9483
  'Accept': 'application/json, text/plain, */*'
9225
9484
  },
9226
- post: {'Content-Type': 'application/json;charset=utf-8'},
9227
- put: {'Content-Type': 'application/json;charset=utf-8'}
9485
+ post: CONTENT_TYPE_APPLICATION_JSON,
9486
+ put: CONTENT_TYPE_APPLICATION_JSON,
9487
+ patch: CONTENT_TYPE_APPLICATION_JSON
9228
9488
  },
9229
9489
 
9230
9490
  xsrfCookieName: 'XSRF-TOKEN',
@@ -9292,7 +9552,7 @@ function $HttpProvider() {
9292
9552
  *
9293
9553
  * @description
9294
9554
  * The `$http` service is a core Angular service that facilitates communication with the remote
9295
- * HTTP servers via browser's {@link https://developer.mozilla.org/en/xmlhttprequest
9555
+ * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
9296
9556
  * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
9297
9557
  *
9298
9558
  * For unit testing applications that use `$http` service, see
@@ -9302,13 +9562,13 @@ function $HttpProvider() {
9302
9562
  * $resource} service.
9303
9563
  *
9304
9564
  * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9305
- * the $q service. While for simple usage patters this doesn't matter much, for advanced usage,
9306
- * it is important to familiarize yourself with these apis and guarantees they provide.
9565
+ * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9566
+ * it is important to familiarize yourself with these APIs and the guarantees they provide.
9307
9567
  *
9308
9568
  *
9309
9569
  * # General usage
9310
9570
  * The `$http` service is a function which takes a single argument — a configuration object —
9311
- * that is used to generate an http request and returns a {@link ng.$q promise}
9571
+ * that is used to generate an HTTP request and returns a {@link ng.$q promise}
9312
9572
  * with two $http specific methods: `success` and `error`.
9313
9573
  *
9314
9574
  * <pre>
@@ -9323,21 +9583,21 @@ function $HttpProvider() {
9323
9583
  * });
9324
9584
  * </pre>
9325
9585
  *
9326
- * Since the returned value of calling the $http function is a Promise object, you can also use
9586
+ * Since the returned value of calling the $http function is a `promise`, you can also use
9327
9587
  * the `then` method to register callbacks, and these callbacks will receive a single argument –
9328
- * an object representing the response. See the api signature and type info below for more
9588
+ * an object representing the response. See the API signature and type info below for more
9329
9589
  * details.
9330
9590
  *
9331
- * A response status code that falls in the [200, 300) range is considered a success status and
9591
+ * A response status code between 200 and 299 is considered a success status and
9332
9592
  * will result in the success callback being called. Note that if the response is a redirect,
9333
9593
  * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9334
9594
  * called for such responses.
9335
9595
  *
9336
9596
  * # Shortcut methods
9337
9597
  *
9338
- * Since all invocation of the $http service require definition of the http method and url and
9339
- * POST and PUT requests require response body/data to be provided as well, shortcut methods
9340
- * were created to simplify using the api:
9598
+ * Since all invocations of the $http service require passing in an HTTP method and URL, and
9599
+ * POST/PUT requests require request data to be provided as well, shortcut methods
9600
+ * were created:
9341
9601
  *
9342
9602
  * <pre>
9343
9603
  * $http.get('/someUrl').success(successCallback);
@@ -9356,24 +9616,24 @@ function $HttpProvider() {
9356
9616
  *
9357
9617
  * # Setting HTTP Headers
9358
9618
  *
9359
- * The $http service will automatically add certain http headers to all requests. These defaults
9619
+ * The $http service will automatically add certain HTTP headers to all requests. These defaults
9360
9620
  * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
9361
9621
  * object, which currently contains this default configuration:
9362
9622
  *
9363
9623
  * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
9364
9624
  * - `Accept: application/json, text/plain, * / *`
9365
- * - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests)
9625
+ * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
9366
9626
  * - `Content-Type: application/json`
9367
- * - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests)
9627
+ * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
9368
9628
  * - `Content-Type: application/json`
9369
9629
  *
9370
- * To add or overwrite these defaults, simply add or remove a property from this configuration
9630
+ * To add or overwrite these defaults, simply add or remove a property from these configuration
9371
9631
  * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
9372
- * with name equal to the lower-cased http method name, e.g.
9632
+ * with the lowercased HTTP method name as the key, e.g.
9373
9633
  * `$httpProvider.defaults.headers.get['My-Header']='value'`.
9374
9634
  *
9375
- * Additionally, the defaults can be set at runtime via the `$http.defaults` object in a similar
9376
- * fashion as described above.
9635
+ * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
9636
+ * fashion.
9377
9637
  *
9378
9638
  *
9379
9639
  * # Transforming Requests and Responses
@@ -9383,54 +9643,54 @@ function $HttpProvider() {
9383
9643
  *
9384
9644
  * Request transformations:
9385
9645
  *
9386
- * - if the `data` property of the request config object contains an object, serialize it into
9646
+ * - If the `data` property of the request configuration object contains an object, serialize it into
9387
9647
  * JSON format.
9388
9648
  *
9389
9649
  * Response transformations:
9390
9650
  *
9391
- * - if XSRF prefix is detected, strip it (see Security Considerations section below)
9392
- * - if json response is detected, deserialize it using a JSON parser
9651
+ * - If XSRF prefix is detected, strip it (see Security Considerations section below).
9652
+ * - If JSON response is detected, deserialize it using a JSON parser.
9393
9653
  *
9394
9654
  * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
9395
- * `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`. These properties are by default an
9655
+ * `$httpProvider.defaults.transformResponse` properties. These properties are by default an
9396
9656
  * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
9397
9657
  * transformation chain. You can also decide to completely override any default transformations by assigning your
9398
9658
  * transformation functions to these properties directly without the array wrapper.
9399
9659
  *
9400
9660
  * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
9401
- * `transformResponse` properties of the config object passed into `$http`.
9661
+ * `transformResponse` properties of the configuration object passed into `$http`.
9402
9662
  *
9403
9663
  *
9404
9664
  * # Caching
9405
9665
  *
9406
- * To enable caching set the configuration property `cache` to `true`. When the cache is
9666
+ * To enable caching, set the configuration property `cache` to `true`. When the cache is
9407
9667
  * enabled, `$http` stores the response from the server in local cache. Next time the
9408
9668
  * response is served from the cache without sending a request to the server.
9409
9669
  *
9410
9670
  * Note that even if the response is served from cache, delivery of the data is asynchronous in
9411
9671
  * the same way that real requests are.
9412
9672
  *
9413
- * If there are multiple GET requests for the same url that should be cached using the same
9673
+ * If there are multiple GET requests for the same URL that should be cached using the same
9414
9674
  * cache, but the cache is not populated yet, only one request to the server will be made and
9415
- * the remaining requests will be fulfilled using the response for the first request.
9675
+ * the remaining requests will be fulfilled using the response from the first request.
9416
9676
  *
9417
9677
  * A custom default cache built with $cacheFactory can be provided in $http.defaults.cache.
9418
9678
  * To skip it, set configuration property `cache` to `false`.
9419
- *
9679
+ *
9420
9680
  *
9421
9681
  * # Interceptors
9422
9682
  *
9423
9683
  * Before you start creating interceptors, be sure to understand the
9424
9684
  * {@link ng.$q $q and deferred/promise APIs}.
9425
9685
  *
9426
- * For purposes of global error handling, authentication or any kind of synchronous or
9686
+ * For purposes of global error handling, authentication, or any kind of synchronous or
9427
9687
  * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
9428
9688
  * able to intercept requests before they are handed to the server and
9429
- * responses before they are handed over to the application code that
9689
+ * responses before they are handed over to the application code that
9430
9690
  * initiated these requests. The interceptors leverage the {@link ng.$q
9431
- * promise APIs} to fulfil this need for both synchronous and asynchronous pre-processing.
9691
+ * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
9432
9692
  *
9433
- * The interceptors are service factories that are registered with the $httpProvider by
9693
+ * The interceptors are service factories that are registered with the `$httpProvider` by
9434
9694
  * adding them to the `$httpProvider.interceptors` array. The factory is called and
9435
9695
  * injected with dependencies (if specified) and returns the interceptor.
9436
9696
  *
@@ -9550,7 +9810,7 @@ function $HttpProvider() {
9550
9810
  * When designing web applications, consider security threats from:
9551
9811
  *
9552
9812
  * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
9553
- * JSON Vulnerability}
9813
+ * JSON vulnerability}
9554
9814
  * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
9555
9815
  *
9556
9816
  * Both server and the client must cooperate in order to eliminate these threats. Angular comes
@@ -9560,8 +9820,8 @@ function $HttpProvider() {
9560
9820
  * ## JSON Vulnerability Protection
9561
9821
  *
9562
9822
  * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
9563
- * JSON Vulnerability} allows third party web-site to turn your JSON resource URL into
9564
- * {@link http://en.wikipedia.org/wiki/JSON#JSONP JSONP} request under some conditions. To
9823
+ * JSON vulnerability} allows third party website to turn your JSON resource URL into
9824
+ * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
9565
9825
  * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
9566
9826
  * Angular will automatically strip the prefix before processing it as JSON.
9567
9827
  *
@@ -9582,7 +9842,7 @@ function $HttpProvider() {
9582
9842
  * ## Cross Site Request Forgery (XSRF) Protection
9583
9843
  *
9584
9844
  * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
9585
- * an unauthorized site can gain your user's private data. Angular provides following mechanism
9845
+ * an unauthorized site can gain your user's private data. Angular provides a mechanism
9586
9846
  * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
9587
9847
  * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
9588
9848
  * JavaScript that runs on your domain could read the cookie, your server can be assured that
@@ -9590,12 +9850,12 @@ function $HttpProvider() {
9590
9850
  * cross-domain requests.
9591
9851
  *
9592
9852
  * To take advantage of this, your server needs to set a token in a JavaScript readable session
9593
- * cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
9853
+ * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
9594
9854
  * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
9595
- * that only JavaScript running on your domain could have read the token. The token must be
9596
- * unique for each user and must be verifiable by the server (to prevent the JavaScript making
9855
+ * that only JavaScript running on your domain could have sent the request. The token must be
9856
+ * unique for each user and must be verifiable by the server (to prevent the JavaScript from making
9597
9857
  * up its own tokens). We recommend that the token is a digest of your site's authentication
9598
- * cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}.
9858
+ * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security.
9599
9859
  *
9600
9860
  * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
9601
9861
  * properties of either $httpProvider.defaults, or the per-request config object.
@@ -9622,7 +9882,8 @@ function $HttpProvider() {
9622
9882
  * GET request, otherwise if a cache instance built with
9623
9883
  * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
9624
9884
  * caching.
9625
- * - **timeout** – `{number}` – timeout in milliseconds.
9885
+ * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
9886
+ * that should abort the request when resolved.
9626
9887
  * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
9627
9888
  * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
9628
9889
  * requests with credentials} for more information.
@@ -9775,7 +10036,7 @@ function $HttpProvider() {
9775
10036
  var rejectFn = chain.shift();
9776
10037
 
9777
10038
  promise = promise.then(thenFn, rejectFn);
9778
- };
10039
+ }
9779
10040
 
9780
10041
  promise.success = function(fn) {
9781
10042
  promise.then(function(response) {
@@ -9812,7 +10073,7 @@ function $HttpProvider() {
9812
10073
  * @methodOf ng.$http
9813
10074
  *
9814
10075
  * @description
9815
- * Shortcut method to perform `GET` request
10076
+ * Shortcut method to perform `GET` request.
9816
10077
  *
9817
10078
  * @param {string} url Relative or absolute URL specifying the destination of the request
9818
10079
  * @param {Object=} config Optional configuration object
@@ -9825,7 +10086,7 @@ function $HttpProvider() {
9825
10086
  * @methodOf ng.$http
9826
10087
  *
9827
10088
  * @description
9828
- * Shortcut method to perform `DELETE` request
10089
+ * Shortcut method to perform `DELETE` request.
9829
10090
  *
9830
10091
  * @param {string} url Relative or absolute URL specifying the destination of the request
9831
10092
  * @param {Object=} config Optional configuration object
@@ -9838,7 +10099,7 @@ function $HttpProvider() {
9838
10099
  * @methodOf ng.$http
9839
10100
  *
9840
10101
  * @description
9841
- * Shortcut method to perform `HEAD` request
10102
+ * Shortcut method to perform `HEAD` request.
9842
10103
  *
9843
10104
  * @param {string} url Relative or absolute URL specifying the destination of the request
9844
10105
  * @param {Object=} config Optional configuration object
@@ -9851,7 +10112,7 @@ function $HttpProvider() {
9851
10112
  * @methodOf ng.$http
9852
10113
  *
9853
10114
  * @description
9854
- * Shortcut method to perform `JSONP` request
10115
+ * Shortcut method to perform `JSONP` request.
9855
10116
  *
9856
10117
  * @param {string} url Relative or absolute URL specifying the destination of the request.
9857
10118
  * Should contain `JSON_CALLBACK` string.
@@ -9866,7 +10127,7 @@ function $HttpProvider() {
9866
10127
  * @methodOf ng.$http
9867
10128
  *
9868
10129
  * @description
9869
- * Shortcut method to perform `POST` request
10130
+ * Shortcut method to perform `POST` request.
9870
10131
  *
9871
10132
  * @param {string} url Relative or absolute URL specifying the destination of the request
9872
10133
  * @param {*} data Request content
@@ -9880,7 +10141,7 @@ function $HttpProvider() {
9880
10141
  * @methodOf ng.$http
9881
10142
  *
9882
10143
  * @description
9883
- * Shortcut method to perform `PUT` request
10144
+ * Shortcut method to perform `PUT` request.
9884
10145
  *
9885
10146
  * @param {string} url Relative or absolute URL specifying the destination of the request
9886
10147
  * @param {*} data Request content
@@ -9932,7 +10193,7 @@ function $HttpProvider() {
9932
10193
 
9933
10194
 
9934
10195
  /**
9935
- * Makes the request
10196
+ * Makes the request.
9936
10197
  *
9937
10198
  * !!! ACCESSES CLOSURE VARS:
9938
10199
  * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
@@ -9949,8 +10210,8 @@ function $HttpProvider() {
9949
10210
 
9950
10211
 
9951
10212
  if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
9952
- cache = isObject(config.cache) ? config.cache
9953
- : isObject(defaults.cache) ? defaults.cache
10213
+ cache = isObject(config.cache) ? config.cache
10214
+ : isObject(defaults.cache) ? defaults.cache
9954
10215
  : defaultCache;
9955
10216
  }
9956
10217
 
@@ -10001,7 +10262,7 @@ function $HttpProvider() {
10001
10262
  }
10002
10263
 
10003
10264
  resolvePromise(response, status, headersString);
10004
- $rootScope.$apply();
10265
+ if (!$rootScope.$$phase) $rootScope.$apply();
10005
10266
  }
10006
10267
 
10007
10268
 
@@ -10085,6 +10346,7 @@ function $HttpBackendProvider() {
10085
10346
  function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
10086
10347
  // TODO(vojta): fix the signature
10087
10348
  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10349
+ var status;
10088
10350
  $browser.$$incOutstandingRequestCount();
10089
10351
  url = url || $browser.url();
10090
10352
 
@@ -10094,12 +10356,12 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
10094
10356
  callbacks[callbackId].data = data;
10095
10357
  };
10096
10358
 
10097
- jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10359
+ var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10098
10360
  function() {
10099
10361
  if (callbacks[callbackId].data) {
10100
10362
  completeRequest(callback, 200, callbacks[callbackId].data);
10101
10363
  } else {
10102
- completeRequest(callback, -2);
10364
+ completeRequest(callback, status || -2);
10103
10365
  }
10104
10366
  delete callbacks[callbackId];
10105
10367
  });
@@ -10110,8 +10372,6 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
10110
10372
  if (value) xhr.setRequestHeader(key, value);
10111
10373
  });
10112
10374
 
10113
- var status;
10114
-
10115
10375
  // In IE6 and 7, this might be called synchronously when xhr.send below is called and the
10116
10376
  // response is in the cache. the promise api will ensure that to the app code the api is
10117
10377
  // always async
@@ -10157,19 +10417,28 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
10157
10417
  }
10158
10418
 
10159
10419
  xhr.send(post || '');
10420
+ }
10160
10421
 
10161
- if (timeout > 0) {
10162
- $browserDefer(function() {
10163
- status = -1;
10164
- xhr.abort();
10165
- }, timeout);
10166
- }
10422
+ if (timeout > 0) {
10423
+ var timeoutId = $browserDefer(timeoutRequest, timeout);
10424
+ } else if (timeout && timeout.then) {
10425
+ timeout.then(timeoutRequest);
10167
10426
  }
10168
10427
 
10169
10428
 
10429
+ function timeoutRequest() {
10430
+ status = -1;
10431
+ jsonpDone && jsonpDone();
10432
+ xhr && xhr.abort();
10433
+ }
10434
+
10170
10435
  function completeRequest(callback, status, response, headersString) {
10171
10436
  // URL_MATCH is defined in src/service/location.js
10172
- var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1];
10437
+ var protocol = (url.match(SERVER_MATCH) || ['', locationProtocol])[1];
10438
+
10439
+ // cancel timeout and subsequent timeout promise resolution
10440
+ timeoutId && $browserDefer.cancel(timeoutId);
10441
+ jsonpDone = xhr = null;
10173
10442
 
10174
10443
  // fix status code for file protocol (it's always 0)
10175
10444
  status = (protocol == 'file') ? (response ? 200 : 404) : status;
@@ -10204,6 +10473,7 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
10204
10473
  }
10205
10474
 
10206
10475
  rawDocument.body.appendChild(script);
10476
+ return doneWrapper;
10207
10477
  }
10208
10478
  }
10209
10479
 
@@ -10294,17 +10564,17 @@ function $TimeoutProvider() {
10294
10564
  * block and delegates any exceptions to
10295
10565
  * {@link ng.$exceptionHandler $exceptionHandler} service.
10296
10566
  *
10297
- * The return value of registering a timeout function is a promise which will be resolved when
10567
+ * The return value of registering a timeout function is a promise, which will be resolved when
10298
10568
  * the timeout is reached and the timeout function is executed.
10299
10569
  *
10300
- * To cancel a the timeout request, call `$timeout.cancel(promise)`.
10570
+ * To cancel a timeout request, call `$timeout.cancel(promise)`.
10301
10571
  *
10302
10572
  * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
10303
10573
  * synchronously flush the queue of deferred functions.
10304
10574
  *
10305
- * @param {function()} fn A function, who's execution should be delayed.
10575
+ * @param {function()} fn A function, whose execution should be delayed.
10306
10576
  * @param {number=} [delay=0] Delay in milliseconds.
10307
- * @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise
10577
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
10308
10578
  * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
10309
10579
  * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
10310
10580
  * promise will be resolved with is the return value of the `fn` function.
@@ -10344,7 +10614,7 @@ function $TimeoutProvider() {
10344
10614
  * @methodOf ng.$timeout
10345
10615
  *
10346
10616
  * @description
10347
- * Cancels a task associated with the `promise`. As a result of this the promise will be
10617
+ * Cancels a task associated with the `promise`. As a result of this, the promise will be
10348
10618
  * resolved with a rejection.
10349
10619
  *
10350
10620
  * @param {Promise=} promise Promise returned by the `$timeout` function.
@@ -10432,7 +10702,7 @@ function $TimeoutProvider() {
10432
10702
  *
10433
10703
  * The general syntax in templates is as follows:
10434
10704
  *
10435
- * {{ expression | [ filter_name ] }}
10705
+ * {{ expression [| filter_name[:parameter_value] ... ] }}
10436
10706
  *
10437
10707
  * @param {String} name Name of the filter function to retrieve
10438
10708
  * @return {Function} the filter function
@@ -10534,7 +10804,7 @@ function $FilterProvider($provide) {
10534
10804
  <hr>
10535
10805
  Any: <input ng-model="search.$"> <br>
10536
10806
  Name only <input ng-model="search.name"><br>
10537
- Phone only <input ng-model="search.phone"å><br>
10807
+ Phone only <input ng-model="search.phone"><br>
10538
10808
  Equality <input type="checkbox" ng-model="strict"><br>
10539
10809
  <table id="searchObjResults">
10540
10810
  <tr><th>Name</th><th>Phone</th></tr>
@@ -10866,6 +11136,7 @@ function padNumber(num, digits, trim) {
10866
11136
 
10867
11137
 
10868
11138
  function dateGetter(name, size, offset, trim) {
11139
+ offset = offset || 0;
10869
11140
  return function(date) {
10870
11141
  var value = date['get' + name]();
10871
11142
  if (offset > 0 || value > -offset)
@@ -10980,7 +11251,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
10980
11251
  * (e.g. `"h o''clock"`).
10981
11252
  *
10982
11253
  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
10983
- * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
11254
+ * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
10984
11255
  * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
10985
11256
  * specified in the string input, the time is considered to be in the local timezone.
10986
11257
  * @param {string=} format Formatting rules (see Description). If not specified,
@@ -11029,7 +11300,11 @@ function dateFilter($locale) {
11029
11300
  tzMin = int(match[9] + match[11]);
11030
11301
  }
11031
11302
  dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
11032
- timeSetter.call(date, int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
11303
+ var h = int(match[4]||0) - tzHour;
11304
+ var m = int(match[5]||0) - tzMin
11305
+ var s = int(match[6]||0);
11306
+ var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000);
11307
+ timeSetter.call(date, h, m, s, ms);
11033
11308
  return date;
11034
11309
  }
11035
11310
  return string;
@@ -11534,6 +11809,31 @@ var htmlAnchorDirective = valueFn({
11534
11809
  * @param {template} ngSrc any string which can contain `{{}}` markup.
11535
11810
  */
11536
11811
 
11812
+ /**
11813
+ * @ngdoc directive
11814
+ * @name ng.directive:ngSrcset
11815
+ * @restrict A
11816
+ *
11817
+ * @description
11818
+ * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
11819
+ * work right: The browser will fetch from the URL with the literal
11820
+ * text `{{hash}}` until Angular replaces the expression inside
11821
+ * `{{hash}}`. The `ngSrcset` directive solves this problem.
11822
+ *
11823
+ * The buggy way to write it:
11824
+ * <pre>
11825
+ * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
11826
+ * </pre>
11827
+ *
11828
+ * The correct way to write it:
11829
+ * <pre>
11830
+ * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
11831
+ * </pre>
11832
+ *
11833
+ * @element IMG
11834
+ * @param {template} ngSrcset any string which can contain `{{}}` markup.
11835
+ */
11836
+
11537
11837
  /**
11538
11838
  * @ngdoc directive
11539
11839
  * @name ng.directive:ngDisabled
@@ -11754,8 +12054,8 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) {
11754
12054
  });
11755
12055
 
11756
12056
 
11757
- // ng-src, ng-href are interpolated
11758
- forEach(['src', 'href'], function(attrName) {
12057
+ // ng-src, ng-srcset, ng-href are interpolated
12058
+ forEach(['src', 'srcset', 'href'], function(attrName) {
11759
12059
  var normalized = directiveNormalize('ng-' + attrName);
11760
12060
  ngAttributeAliasDirectives[normalized] = function() {
11761
12061
  return {
@@ -12187,8 +12487,8 @@ var inputType = {
12187
12487
  *
12188
12488
  * @param {string} ngModel Assignable angular expression to data-bind to.
12189
12489
  * @param {string=} name Property name of the form under which the control is published.
12190
- * @param {string=} min Sets the `min` validation error key if the value entered is less then `min`.
12191
- * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`.
12490
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
12491
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
12192
12492
  * @param {string=} required Sets `required` validation error key if the value is not entered.
12193
12493
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
12194
12494
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -12507,6 +12807,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
12507
12807
  } else {
12508
12808
  var timeout;
12509
12809
 
12810
+ var deferListener = function() {
12811
+ if (!timeout) {
12812
+ timeout = $browser.defer(function() {
12813
+ listener();
12814
+ timeout = null;
12815
+ });
12816
+ }
12817
+ };
12818
+
12510
12819
  element.bind('keydown', function(event) {
12511
12820
  var key = event.keyCode;
12512
12821
 
@@ -12514,16 +12823,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
12514
12823
  // command modifiers arrows
12515
12824
  if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
12516
12825
 
12517
- if (!timeout) {
12518
- timeout = $browser.defer(function() {
12519
- listener();
12520
- timeout = null;
12521
- });
12522
- }
12826
+ deferListener();
12523
12827
  });
12524
12828
 
12525
12829
  // if user paste into input using mouse, we need "change" event to catch it
12526
12830
  element.bind('change', listener);
12831
+
12832
+ // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
12833
+ if ($sniffer.hasEvent('paste')) {
12834
+ element.bind('paste cut', deferListener);
12835
+ }
12527
12836
  }
12528
12837
 
12529
12838
 
@@ -12533,7 +12842,8 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
12533
12842
 
12534
12843
  // pattern validator
12535
12844
  var pattern = attr.ngPattern,
12536
- patternValidator;
12845
+ patternValidator,
12846
+ match;
12537
12847
 
12538
12848
  var validate = function(regexp, value) {
12539
12849
  if (isEmpty(value) || regexp.test(value)) {
@@ -12546,8 +12856,9 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
12546
12856
  };
12547
12857
 
12548
12858
  if (pattern) {
12549
- if (pattern.match(/^\/(.*)\/$/)) {
12550
- pattern = new RegExp(pattern.substr(1, pattern.length - 2));
12859
+ match = pattern.match(/^\/(.*)\/([gim]*)$/);
12860
+ if (match) {
12861
+ pattern = new RegExp(match[1], match[2]);
12551
12862
  patternValidator = function(value) {
12552
12863
  return validate(pattern, value)
12553
12864
  };
@@ -12822,7 +13133,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
12822
13133
  <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
12823
13134
  <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
12824
13135
  <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
12825
- <tt>myForm.userName.$error = {{myForm.lastName.$error}}</tt><br>
13136
+ <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
12826
13137
  <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
12827
13138
  <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
12828
13139
  <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
@@ -13101,7 +13412,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
13101
13412
  * For example {@link ng.directive:input input} or
13102
13413
  * {@link ng.directive:select select} directives call it.
13103
13414
  *
13104
- * It internally calls all `formatters` and if resulted value is valid, updates the model and
13415
+ * It internally calls all `parsers` and if resulted value is valid, updates the model and
13105
13416
  * calls all registered change listeners.
13106
13417
  *
13107
13418
  * @param {string} value Value from the view.
@@ -13407,7 +13718,7 @@ var ngValueDirective = function() {
13407
13718
  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
13408
13719
  * `{{ expression }}` which is similar but less verbose.
13409
13720
  *
13410
- * Once scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
13721
+ * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
13411
13722
  * it's desirable to put bindings into template that is momentarily displayed by the browser in its
13412
13723
  * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
13413
13724
  * bindings invisible to the user while the page is loading.
@@ -13548,9 +13859,9 @@ function classDirective(name, selector) {
13548
13859
 
13549
13860
  if (name !== 'ngClass') {
13550
13861
  scope.$watch('$index', function($index, old$index) {
13551
- var mod = $index % 2;
13552
- if (mod !== old$index % 2) {
13553
- if (mod == selector) {
13862
+ var mod = $index & 1;
13863
+ if (mod !== old$index & 1) {
13864
+ if (mod === selector) {
13554
13865
  addClass(scope.$eval(attr[name]));
13555
13866
  } else {
13556
13867
  removeClass(scope.$eval(attr[name]));
@@ -13562,12 +13873,12 @@ function classDirective(name, selector) {
13562
13873
 
13563
13874
  function ngClassWatchAction(newVal) {
13564
13875
  if (selector === true || scope.$index % 2 === selector) {
13565
- if (oldVal && (newVal !== oldVal)) {
13876
+ if (oldVal && !equals(newVal,oldVal)) {
13566
13877
  removeClass(oldVal);
13567
13878
  }
13568
13879
  addClass(newVal);
13569
13880
  }
13570
- oldVal = newVal;
13881
+ oldVal = copy(newVal);
13571
13882
  }
13572
13883
 
13573
13884
 
@@ -13693,7 +14004,7 @@ var ngClassOddDirective = classDirective('Odd', 0);
13693
14004
  * @name ng.directive:ngClassEven
13694
14005
  *
13695
14006
  * @description
13696
- * The `ngClassOdd` and `ngClassEven` works exactly as
14007
+ * The `ngClassOdd` and `ngClassEven` directives work exactly as
13697
14008
  * {@link ng.directive:ngClass ngClass}, except it works in
13698
14009
  * conjunction with `ngRepeat` and takes affect only on odd (even) rows.
13699
14010
  *
@@ -13810,14 +14121,14 @@ var ngCloakDirective = ngDirective({
13810
14121
  * * Controller — The `ngController` directive specifies a Controller class; the class has
13811
14122
  * methods that typically express the business logic behind the application.
13812
14123
  *
13813
- * Note that an alternative way to define controllers is via the `{@link ng.$route}`
13814
- * service.
14124
+ * Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
13815
14125
  *
13816
14126
  * @element ANY
13817
14127
  * @scope
13818
14128
  * @param {expression} ngController Name of a globally accessible constructor function or an
13819
14129
  * {@link guide/expression expression} that on the current scope evaluates to a
13820
- * constructor function.
14130
+ * constructor function. The controller instance can further be published into the scope
14131
+ * by adding `as localName` the controller name attribute.
13821
14132
  *
13822
14133
  * @example
13823
14134
  * Here is a simple form for editing user contact information. Adding, removing, clearing, and
@@ -13825,8 +14136,75 @@ var ngCloakDirective = ngDirective({
13825
14136
  * easily be called from the angular markup. Notice that the scope becomes the `this` for the
13826
14137
  * controller's instance. This allows for easy access to the view data from the controller. Also
13827
14138
  * notice that any changes to the data are automatically reflected in the View without the need
13828
- * for a manual update.
14139
+ * for a manual update. The example is included in two different declaration styles based on
14140
+ * your style preferences.
13829
14141
  <doc:example>
14142
+ <doc:source>
14143
+ <script>
14144
+ function SettingsController() {
14145
+ this.name = "John Smith";
14146
+ this.contacts = [
14147
+ {type: 'phone', value: '408 555 1212'},
14148
+ {type: 'email', value: 'john.smith@example.org'} ];
14149
+ };
14150
+
14151
+ SettingsController.prototype.greet = function() {
14152
+ alert(this.name);
14153
+ };
14154
+
14155
+ SettingsController.prototype.addContact = function() {
14156
+ this.contacts.push({type: 'email', value: 'yourname@example.org'});
14157
+ };
14158
+
14159
+ SettingsController.prototype.removeContact = function(contactToRemove) {
14160
+ var index = this.contacts.indexOf(contactToRemove);
14161
+ this.contacts.splice(index, 1);
14162
+ };
14163
+
14164
+ SettingsController.prototype.clearContact = function(contact) {
14165
+ contact.type = 'phone';
14166
+ contact.value = '';
14167
+ };
14168
+ </script>
14169
+ <div ng-controller="SettingsController as settings">
14170
+ Name: <input type="text" ng-model="settings.name"/>
14171
+ [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
14172
+ Contact:
14173
+ <ul>
14174
+ <li ng-repeat="contact in settings.contacts">
14175
+ <select ng-model="contact.type">
14176
+ <option>phone</option>
14177
+ <option>email</option>
14178
+ </select>
14179
+ <input type="text" ng-model="contact.value"/>
14180
+ [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
14181
+ | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
14182
+ </li>
14183
+ <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
14184
+ </ul>
14185
+ </div>
14186
+ </doc:source>
14187
+ <doc:scenario>
14188
+ it('should check controller', function() {
14189
+ expect(element('.doc-example-live div>:input').val()).toBe('John Smith');
14190
+ expect(element('.doc-example-live li:nth-child(1) input').val())
14191
+ .toBe('408 555 1212');
14192
+ expect(element('.doc-example-live li:nth-child(2) input').val())
14193
+ .toBe('john.smith@example.org');
14194
+
14195
+ element('.doc-example-live li:first a:contains("clear")').click();
14196
+ expect(element('.doc-example-live li:first input').val()).toBe('');
14197
+
14198
+ element('.doc-example-live li:last a:contains("add")').click();
14199
+ expect(element('.doc-example-live li:nth-child(3) input').val())
14200
+ .toBe('yourname@example.org');
14201
+ });
14202
+ </doc:scenario>
14203
+ </doc:example>
14204
+
14205
+
14206
+
14207
+ <doc:example>
13830
14208
  <doc:source>
13831
14209
  <script>
13832
14210
  function SettingsController($scope) {
@@ -13889,6 +14267,7 @@ var ngCloakDirective = ngDirective({
13889
14267
  });
13890
14268
  </doc:scenario>
13891
14269
  </doc:example>
14270
+
13892
14271
  */
13893
14272
  var ngControllerDirective = [function() {
13894
14273
  return {
@@ -13902,16 +14281,32 @@ var ngControllerDirective = [function() {
13902
14281
  * @name ng.directive:ngCsp
13903
14282
  * @priority 1000
13904
14283
  *
14284
+ * @element html
13905
14285
  * @description
13906
14286
  * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
13907
- * This directive should be used on the root element of the application (typically the `<html>`
13908
- * element or other element with the {@link ng.directive:ngApp ngApp}
13909
- * directive).
13910
- *
13911
- * If enabled the performance of template expression evaluator will suffer slightly, so don't enable
13912
- * this mode unless you need it.
13913
- *
13914
- * @element html
14287
+ *
14288
+ * This is necessary when developing things like Google Chrome Extensions.
14289
+ *
14290
+ * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
14291
+ * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
14292
+ * any of these restrictions.
14293
+ *
14294
+ * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
14295
+ * it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
14296
+ * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
14297
+ * be raised.
14298
+ *
14299
+ * In order to use this feature put `ngCsp` directive on the root element of the application.
14300
+ *
14301
+ * @example
14302
+ * This example shows how to apply the `ngCsp` directive to the `html` tag.
14303
+ <pre>
14304
+ <!doctype html>
14305
+ <html ng-app ng-csp>
14306
+ ...
14307
+ ...
14308
+ </html>
14309
+ </pre>
13915
14310
  */
13916
14311
 
13917
14312
  var ngCspDirective = ['$sniffer', function($sniffer) {
@@ -14192,6 +14587,114 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
14192
14587
  });
14193
14588
  });
14194
14589
 
14590
+ /**
14591
+ * @ngdoc directive
14592
+ * @name ng.directive:ngIf
14593
+ * @restrict A
14594
+ *
14595
+ * @description
14596
+ * The `ngIf` directive removes and recreates a portion of the DOM tree (HTML)
14597
+ * conditionally based on **"falsy"** and **"truthy"** values, respectively, evaluated within
14598
+ * an {expression}. In other words, if the expression assigned to **ngIf evaluates to a false
14599
+ * value** then **the element is removed from the DOM** and **if true** then **a clone of the
14600
+ * element is reinserted into the DOM**.
14601
+ *
14602
+ * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
14603
+ * element in the DOM rather than changing its visibility via the `display` css property. A common
14604
+ * case when this difference is significant is when using css selectors that rely on an element's
14605
+ * position within the DOM (HTML), such as the `:first-child` or `:last-child` pseudo-classes.
14606
+ *
14607
+ * Note that **when an element is removed using ngIf its scope is destroyed** and **a new scope
14608
+ * is created when the element is restored**. The scope created within `ngIf` inherits from
14609
+ * its parent scope using
14610
+ * {@link https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance prototypal inheritance}.
14611
+ * An important implication of this is if `ngModel` is used within `ngIf` to bind to
14612
+ * a javascript primitive defined in the parent scope. In this case any modifications made to the
14613
+ * variable within the child scope will override (hide) the value in the parent scope.
14614
+ *
14615
+ * Also, `ngIf` recreates elements using their compiled state. An example scenario of this behavior
14616
+ * is if an element's class attribute is directly modified after it's compiled, using something like
14617
+ * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
14618
+ * the added class will be lost because the original compiled state is used to regenerate the element.
14619
+ *
14620
+ * Additionally, you can provide animations via the ngAnimate attribute to animate the **enter**
14621
+ * and **leave** effects.
14622
+ *
14623
+ * @animations
14624
+ * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container
14625
+ * leave - happens just before the ngIf contents are removed from the DOM
14626
+ *
14627
+ * @element ANY
14628
+ * @scope
14629
+ * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
14630
+ * the element is removed from the DOM tree (HTML).
14631
+ *
14632
+ * @example
14633
+ <example animations="true">
14634
+ <file name="index.html">
14635
+ Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
14636
+ Show when checked:
14637
+ <span ng-if="checked" ng-animate="'example'">
14638
+ I'm removed when the checkbox is unchecked.
14639
+ </span>
14640
+ </file>
14641
+ <file name="animations.css">
14642
+ .example-leave, .example-enter {
14643
+ -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
14644
+ -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
14645
+ -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
14646
+ -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
14647
+ transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
14648
+ }
14649
+
14650
+ .example-enter {
14651
+ opacity:0;
14652
+ }
14653
+ .example-enter.example-enter-active {
14654
+ opacity:1;
14655
+ }
14656
+
14657
+ .example-leave {
14658
+ opacity:1;
14659
+ }
14660
+ .example-leave.example-leave-active {
14661
+ opacity:0;
14662
+ }
14663
+ </file>
14664
+ </example>
14665
+ */
14666
+ var ngIfDirective = ['$animator', function($animator) {
14667
+ return {
14668
+ transclude: 'element',
14669
+ priority: 1000,
14670
+ terminal: true,
14671
+ restrict: 'A',
14672
+ compile: function (element, attr, transclude) {
14673
+ return function ($scope, $element, $attr) {
14674
+ var animate = $animator($scope, $attr);
14675
+ var childElement, childScope;
14676
+ $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
14677
+ if (childElement) {
14678
+ animate.leave(childElement);
14679
+ childElement = undefined;
14680
+ }
14681
+ if (childScope) {
14682
+ childScope.$destroy();
14683
+ childScope = undefined;
14684
+ }
14685
+ if (toBoolean(value)) {
14686
+ childScope = $scope.$new();
14687
+ transclude(childScope, function (clone) {
14688
+ childElement = clone;
14689
+ animate.enter(clone, $element.parent(), $element);
14690
+ });
14691
+ }
14692
+ });
14693
+ }
14694
+ }
14695
+ }
14696
+ }];
14697
+
14195
14698
  /**
14196
14699
  * @ngdoc directive
14197
14700
  * @name ng.directive:ngInclude
@@ -14225,7 +14728,7 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
14225
14728
  * - Otherwise enable scrolling only if the expression evaluates to truthy value.
14226
14729
  *
14227
14730
  * @example
14228
- <example>
14731
+ <example animations="true">
14229
14732
  <file name="index.html">
14230
14733
  <div ng-controller="Ctrl">
14231
14734
  <select ng-model="template" ng-options="t.name for t in templates">
@@ -14253,8 +14756,8 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
14253
14756
  <div>Content of template2.html</div>
14254
14757
  </file>
14255
14758
  <file name="animations.css">
14256
- .example-leave-setup,
14257
- .example-enter-setup {
14759
+ .example-leave,
14760
+ .example-enter {
14258
14761
  -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
14259
14762
  -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
14260
14763
  -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
@@ -14273,17 +14776,17 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
14273
14776
  padding:10px;
14274
14777
  }
14275
14778
 
14276
- .example-enter-setup {
14779
+ .example-enter {
14277
14780
  top:-50px;
14278
14781
  }
14279
- .example-enter-setup.example-enter-start {
14782
+ .example-enter.example-enter-active {
14280
14783
  top:0;
14281
14784
  }
14282
14785
 
14283
- .example-leave-setup {
14786
+ .example-leave {
14284
14787
  top:0;
14285
14788
  }
14286
- .example-leave-setup.example-leave-start {
14789
+ .example-leave.example-leave-active {
14287
14790
  top:50px;
14288
14791
  }
14289
14792
  </file>
@@ -14306,6 +14809,16 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
14306
14809
  */
14307
14810
 
14308
14811
 
14812
+ /**
14813
+ * @ngdoc event
14814
+ * @name ng.directive:ngInclude#$includeContentRequested
14815
+ * @eventOf ng.directive:ngInclude
14816
+ * @eventType emit on the scope ngInclude was declared in
14817
+ * @description
14818
+ * Emitted every time the ngInclude content is requested.
14819
+ */
14820
+
14821
+
14309
14822
  /**
14310
14823
  * @ngdoc event
14311
14824
  * @name ng.directive:ngInclude#$includeContentLoaded
@@ -14362,6 +14875,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
14362
14875
  }).error(function() {
14363
14876
  if (thisChangeId === changeCounter) clearContent();
14364
14877
  });
14878
+ scope.$emit('$includeContentRequested');
14365
14879
  } else {
14366
14880
  clearContent();
14367
14881
  }
@@ -14633,7 +15147,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
14633
15147
  if (!isNaN(value)) {
14634
15148
  //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
14635
15149
  //check it against pluralization rules in $locale service
14636
- if (!whens[value]) value = $locale.pluralCat(value - offset);
15150
+ if (!(value in whens)) value = $locale.pluralCat(value - offset);
14637
15151
  return whensExpFns[value](scope, element, true);
14638
15152
  } else {
14639
15153
  return '';
@@ -14733,9 +15247,9 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
14733
15247
  </div>
14734
15248
  </file>
14735
15249
  <file name="animations.css">
14736
- .example-repeat-enter-setup,
14737
- .example-repeat-leave-setup,
14738
- .example-repeat-move-setup {
15250
+ .example-repeat-enter,
15251
+ .example-repeat-leave,
15252
+ .example-repeat-move {
14739
15253
  -webkit-transition:all linear 0.5s;
14740
15254
  -moz-transition:all linear 0.5s;
14741
15255
  -ms-transition:all linear 0.5s;
@@ -14743,26 +15257,26 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
14743
15257
  transition:all linear 0.5s;
14744
15258
  }
14745
15259
 
14746
- .example-repeat-enter-setup {
15260
+ .example-repeat-enter {
14747
15261
  line-height:0;
14748
15262
  opacity:0;
14749
15263
  }
14750
- .example-repeat-enter-setup.example-repeat-enter-start {
15264
+ .example-repeat-enter.example-repeat-enter-active {
14751
15265
  line-height:20px;
14752
15266
  opacity:1;
14753
15267
  }
14754
15268
 
14755
- .example-repeat-leave-setup {
15269
+ .example-repeat-leave {
14756
15270
  opacity:1;
14757
15271
  line-height:20px;
14758
15272
  }
14759
- .example-repeat-leave-setup.example-repeat-leave-start {
15273
+ .example-repeat-leave.example-repeat-leave-active {
14760
15274
  opacity:0;
14761
15275
  line-height:0;
14762
15276
  }
14763
15277
 
14764
- .example-repeat-move-setup { }
14765
- .example-repeat-move-setup.example-repeat-move-start { }
15278
+ .example-repeat-move { }
15279
+ .example-repeat-move.example-repeat-move-active { }
14766
15280
  </file>
14767
15281
  <file name="scenario.js">
14768
15282
  it('should render initial data set', function() {
@@ -14798,7 +15312,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
14798
15312
  var animate = $animator($scope, $attr);
14799
15313
  var expression = $attr.ngRepeat;
14800
15314
  var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
14801
- trackByExp, hashExpFn, trackByIdFn, lhs, rhs, valueIdentifier, keyIdentifier,
15315
+ trackByExp, trackByExpGetter, trackByIdFn, lhs, rhs, valueIdentifier, keyIdentifier,
14802
15316
  hashFnLocals = {$id: hashKey};
14803
15317
 
14804
15318
  if (!match) {
@@ -14811,13 +15325,13 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
14811
15325
  trackByExp = match[4];
14812
15326
 
14813
15327
  if (trackByExp) {
14814
- hashExpFn = $parse(trackByExp);
15328
+ trackByExpGetter = $parse(trackByExp);
14815
15329
  trackByIdFn = function(key, value, index) {
14816
15330
  // assign key, value, and $index to the locals so that they can be used in hash functions
14817
15331
  if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
14818
15332
  hashFnLocals[valueIdentifier] = value;
14819
15333
  hashFnLocals.$index = index;
14820
- return hashExpFn($scope, hashFnLocals);
15334
+ return trackByExpGetter($scope, hashFnLocals);
14821
15335
  };
14822
15336
  } else {
14823
15337
  trackByIdFn = function(key, value) {
@@ -14857,7 +15371,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
14857
15371
  nextBlockOrder = [];
14858
15372
 
14859
15373
 
14860
- if (isArray(collection)) {
15374
+ if (isArrayLike(collection)) {
14861
15375
  collectionKeys = collection;
14862
15376
  } else {
14863
15377
  // if object, extract keys, sort them and use to determine order of iteration over obj props
@@ -14878,7 +15392,8 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
14878
15392
  key = (collection === collectionKeys) ? index : collectionKeys[index];
14879
15393
  value = collection[key];
14880
15394
  trackById = trackByIdFn(key, value, index);
14881
- if((block = lastBlockMap[trackById])) {
15395
+ if(lastBlockMap.hasOwnProperty(trackById)) {
15396
+ block = lastBlockMap[trackById]
14882
15397
  delete lastBlockMap[trackById];
14883
15398
  nextBlockMap[trackById] = block;
14884
15399
  nextBlockOrder[index] = block;
@@ -14888,10 +15403,12 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
14888
15403
  if (block && block.element) lastBlockMap[block.id] = block;
14889
15404
  });
14890
15405
  // This is a duplicate and we need to throw an error
14891
- throw new Error('Duplicates in a repeater are not allowed. Repeater: ' + expression);
15406
+ throw new Error('Duplicates in a repeater are not allowed. Repeater: ' + expression +
15407
+ ' key: ' + trackById);
14892
15408
  } else {
14893
15409
  // new never before seen block
14894
15410
  nextBlockOrder[index] = { id: trackById };
15411
+ nextBlockMap[trackById] = false;
14895
15412
  }
14896
15413
  }
14897
15414
 
@@ -15003,7 +15520,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
15003
15520
  </div>
15004
15521
  </file>
15005
15522
  <file name="animations.css">
15006
- .example-show-setup, .example-hide-setup {
15523
+ .example-show, .example-hide {
15007
15524
  -webkit-transition:all linear 0.5s;
15008
15525
  -moz-transition:all linear 0.5s;
15009
15526
  -ms-transition:all linear 0.5s;
@@ -15011,12 +15528,12 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
15011
15528
  transition:all linear 0.5s;
15012
15529
  }
15013
15530
 
15014
- .example-show-setup {
15531
+ .example-show {
15015
15532
  line-height:0;
15016
15533
  opacity:0;
15017
15534
  padding:0 10px;
15018
15535
  }
15019
- .example-show-start.example-show-start {
15536
+ .example-show-active.example-show-active {
15020
15537
  line-height:20px;
15021
15538
  opacity:1;
15022
15539
  padding:10px;
@@ -15024,14 +15541,14 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
15024
15541
  background:white;
15025
15542
  }
15026
15543
 
15027
- .example-hide-setup {
15544
+ .example-hide {
15028
15545
  line-height:20px;
15029
15546
  opacity:1;
15030
15547
  padding:10px;
15031
15548
  border:1px solid black;
15032
15549
  background:white;
15033
15550
  }
15034
- .example-hide-start.example-hide-start {
15551
+ .example-hide-active.example-hide-active {
15035
15552
  line-height:0;
15036
15553
  opacity:0;
15037
15554
  padding:0 10px;
@@ -15112,7 +15629,7 @@ var ngShowDirective = ['$animator', function($animator) {
15112
15629
  </div>
15113
15630
  </file>
15114
15631
  <file name="animations.css">
15115
- .example-show-setup, .example-hide-setup {
15632
+ .example-show, .example-hide {
15116
15633
  -webkit-transition:all linear 0.5s;
15117
15634
  -moz-transition:all linear 0.5s;
15118
15635
  -ms-transition:all linear 0.5s;
@@ -15120,12 +15637,12 @@ var ngShowDirective = ['$animator', function($animator) {
15120
15637
  transition:all linear 0.5s;
15121
15638
  }
15122
15639
 
15123
- .example-show-setup {
15640
+ .example-show {
15124
15641
  line-height:0;
15125
15642
  opacity:0;
15126
15643
  padding:0 10px;
15127
15644
  }
15128
- .example-show-start.example-show-start {
15645
+ .example-show.example-show-active {
15129
15646
  line-height:20px;
15130
15647
  opacity:1;
15131
15648
  padding:10px;
@@ -15133,14 +15650,14 @@ var ngShowDirective = ['$animator', function($animator) {
15133
15650
  background:white;
15134
15651
  }
15135
15652
 
15136
- .example-hide-setup {
15653
+ .example-hide {
15137
15654
  line-height:20px;
15138
15655
  opacity:1;
15139
15656
  padding:10px;
15140
15657
  border:1px solid black;
15141
15658
  background:white;
15142
15659
  }
15143
- .example-hide-start.example-hide-start {
15660
+ .example-hide.example-hide-active {
15144
15661
  line-height:0;
15145
15662
  opacity:0;
15146
15663
  padding:0 10px;
@@ -15292,7 +15809,7 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
15292
15809
  }
15293
15810
  </file>
15294
15811
  <file name="animations.css">
15295
- .example-leave-setup, .example-enter-setup {
15812
+ .example-leave, .example-enter {
15296
15813
  -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
15297
15814
  -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
15298
15815
  -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
@@ -15311,17 +15828,17 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
15311
15828
  padding:10px;
15312
15829
  }
15313
15830
 
15314
- .example-enter-setup {
15831
+ .example-enter {
15315
15832
  top:-50px;
15316
15833
  }
15317
- .example-enter-start.example-enter-start {
15834
+ .example-enter.example-enter-active {
15318
15835
  top:0;
15319
15836
  }
15320
15837
 
15321
- .example-leave-setup {
15838
+ .example-leave {
15322
15839
  top:0;
15323
15840
  }
15324
- .example-leave-start.example-leave-start {
15841
+ .example-leave.example-leave-active {
15325
15842
  top:50px;
15326
15843
  }
15327
15844
  </file>
@@ -15487,7 +16004,7 @@ var ngTranscludeDirective = ngDirective({
15487
16004
  * @example
15488
16005
  <example module="ngView" animations="true">
15489
16006
  <file name="index.html">
15490
- <div ng-controller="MainCntl">
16007
+ <div ng-controller="MainCntl as main">
15491
16008
  Choose:
15492
16009
  <a href="Book/Moby">Moby</a> |
15493
16010
  <a href="Book/Moby/ch/1">Moby: Ch1</a> |
@@ -15501,31 +16018,31 @@ var ngTranscludeDirective = ngDirective({
15501
16018
  ng-animate="{enter: 'example-enter', leave: 'example-leave'}"></div>
15502
16019
  <hr />
15503
16020
 
15504
- <pre>$location.path() = {{$location.path()}}</pre>
15505
- <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
15506
- <pre>$route.current.params = {{$route.current.params}}</pre>
15507
- <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
15508
- <pre>$routeParams = {{$routeParams}}</pre>
16021
+ <pre>$location.path() = {{main.$location.path()}}</pre>
16022
+ <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
16023
+ <pre>$route.current.params = {{main.$route.current.params}}</pre>
16024
+ <pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
16025
+ <pre>$routeParams = {{main.$routeParams}}</pre>
15509
16026
  </div>
15510
16027
  </file>
15511
16028
 
15512
16029
  <file name="book.html">
15513
16030
  <div>
15514
- controller: {{name}}<br />
15515
- Book Id: {{params.bookId}}<br />
16031
+ controller: {{book.name}}<br />
16032
+ Book Id: {{book.params.bookId}}<br />
15516
16033
  </div>
15517
16034
  </file>
15518
16035
 
15519
16036
  <file name="chapter.html">
15520
16037
  <div>
15521
- controller: {{name}}<br />
15522
- Book Id: {{params.bookId}}<br />
15523
- Chapter Id: {{params.chapterId}}
16038
+ controller: {{chapter.name}}<br />
16039
+ Book Id: {{chapter.params.bookId}}<br />
16040
+ Chapter Id: {{chapter.params.chapterId}}
15524
16041
  </div>
15525
16042
  </file>
15526
16043
 
15527
16044
  <file name="animations.css">
15528
- .example-leave-setup, .example-enter-setup {
16045
+ .example-leave, .example-enter {
15529
16046
  -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
15530
16047
  -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
15531
16048
  -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
@@ -15551,15 +16068,15 @@ var ngTranscludeDirective = ngDirective({
15551
16068
  padding:10px;
15552
16069
  }
15553
16070
 
15554
- .example-enter-setup {
16071
+ .example-enter {
15555
16072
  left:100%;
15556
16073
  }
15557
- .example-enter-setup.example-enter-start {
16074
+ .example-enter.example-enter-active {
15558
16075
  left:0;
15559
16076
  }
15560
16077
 
15561
- .example-leave-setup { }
15562
- .example-leave-setup.example-leave-start {
16078
+ .example-leave { }
16079
+ .example-leave.example-leave-active {
15563
16080
  left:-100%;
15564
16081
  }
15565
16082
  </file>
@@ -15568,31 +16085,33 @@ var ngTranscludeDirective = ngDirective({
15568
16085
  angular.module('ngView', [], function($routeProvider, $locationProvider) {
15569
16086
  $routeProvider.when('/Book/:bookId', {
15570
16087
  templateUrl: 'book.html',
15571
- controller: BookCntl
16088
+ controller: BookCntl,
16089
+ controllerAs: 'book'
15572
16090
  });
15573
16091
  $routeProvider.when('/Book/:bookId/ch/:chapterId', {
15574
16092
  templateUrl: 'chapter.html',
15575
- controller: ChapterCntl
16093
+ controller: ChapterCntl,
16094
+ controllerAs: 'chapter'
15576
16095
  });
15577
16096
 
15578
16097
  // configure html5 to get links working on jsfiddle
15579
16098
  $locationProvider.html5Mode(true);
15580
16099
  });
15581
16100
 
15582
- function MainCntl($scope, $route, $routeParams, $location) {
15583
- $scope.$route = $route;
15584
- $scope.$location = $location;
15585
- $scope.$routeParams = $routeParams;
16101
+ function MainCntl($route, $routeParams, $location) {
16102
+ this.$route = $route;
16103
+ this.$location = $location;
16104
+ this.$routeParams = $routeParams;
15586
16105
  }
15587
16106
 
15588
- function BookCntl($scope, $routeParams) {
15589
- $scope.name = "BookCntl";
15590
- $scope.params = $routeParams;
16107
+ function BookCntl($routeParams) {
16108
+ this.name = "BookCntl";
16109
+ this.params = $routeParams;
15591
16110
  }
15592
16111
 
15593
- function ChapterCntl($scope, $routeParams) {
15594
- $scope.name = "ChapterCntl";
15595
- $scope.params = $routeParams;
16112
+ function ChapterCntl($routeParams) {
16113
+ this.name = "ChapterCntl";
16114
+ this.params = $routeParams;
15596
16115
  }
15597
16116
  </file>
15598
16117
 
@@ -15656,9 +16175,10 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
15656
16175
 
15657
16176
  if (template) {
15658
16177
  clearContent();
15659
- animate.enter(jqLite('<div></div>').html(template).contents(), element);
16178
+ var enterElements = jqLite('<div></div>').html(template).contents();
16179
+ animate.enter(enterElements, element);
15660
16180
 
15661
- var link = $compile(element.contents()),
16181
+ var link = $compile(enterElements),
15662
16182
  current = $route.current,
15663
16183
  controller;
15664
16184
 
@@ -15666,6 +16186,9 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
15666
16186
  if (current.controller) {
15667
16187
  locals.$scope = lastScope;
15668
16188
  controller = $controller(current.controller, locals);
16189
+ if (current.controllerAs) {
16190
+ lastScope[current.controllerAs] = controller;
16191
+ }
15669
16192
  element.children().data('$ngControllerController', controller);
15670
16193
  }
15671
16194
 
@@ -15755,7 +16278,8 @@ var scriptDirective = ['$templateCache', function($templateCache) {
15755
16278
  * `select` model to be bound to a non-string value. This is because an option element can currently
15756
16279
  * be bound to string values only.
15757
16280
  *
15758
- * @param {string} name assignable expression to data-bind to.
16281
+ * @param {string} ngModel Assignable angular expression to data-bind to.
16282
+ * @param {string=} name Property name of the form under which the control is published.
15759
16283
  * @param {string=} required The control is considered valid only if value is entered.
15760
16284
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
15761
16285
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -15766,7 +16290,7 @@ var scriptDirective = ['$templateCache', function($templateCache) {
15766
16290
  * * `label` **`for`** `value` **`in`** `array`
15767
16291
  * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
15768
16292
  * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
15769
- * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array`
16293
+ * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
15770
16294
  * * for object data sources:
15771
16295
  * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
15772
16296
  * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
@@ -15786,6 +16310,9 @@ var scriptDirective = ['$templateCache', function($templateCache) {
15786
16310
  * element. If not specified, `select` expression will default to `value`.
15787
16311
  * * `group`: The result of this expression will be used to group options using the `<optgroup>`
15788
16312
  * DOM element.
16313
+ * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
16314
+ * used to identify the objects in the array. The `trackexpr` will most likely refer to the
16315
+ * `value` variable (e.g. `value.propertyName`).
15789
16316
  *
15790
16317
  * @example
15791
16318
  <doc:example>
@@ -15850,8 +16377,8 @@ var scriptDirective = ['$templateCache', function($templateCache) {
15850
16377
 
15851
16378
  var ngOptionsDirective = valueFn({ terminal: true });
15852
16379
  var selectDirective = ['$compile', '$parse', function($compile, $parse) {
15853
- //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
15854
- var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,
16380
+ //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000007777000000000000000000088888
16381
+ var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/,
15855
16382
  nullModelCtrl = {$setViewValue: noop};
15856
16383
 
15857
16384
  return {
@@ -16025,7 +16552,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
16025
16552
 
16026
16553
  if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) {
16027
16554
  throw Error(
16028
- "Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
16555
+ "Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_ (track by _expr_)?'" +
16029
16556
  " but got '" + optionsExp + "'.");
16030
16557
  }
16031
16558
 
@@ -16035,6 +16562,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
16035
16562
  groupByFn = $parse(match[3] || ''),
16036
16563
  valueFn = $parse(match[2] ? match[1] : valueName),
16037
16564
  valuesFn = $parse(match[7]),
16565
+ track = match[8],
16566
+ trackFn = track ? $parse(match[8]) : null,
16038
16567
  // This is an array of array of existing option groups in DOM. We try to reuse these if possible
16039
16568
  // optionGroupsCache[0] is the options with no option group
16040
16569
  // optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
@@ -16075,7 +16604,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
16075
16604
  if ((optionElement = optionGroup[index].element)[0].selected) {
16076
16605
  key = optionElement.val();
16077
16606
  if (keyName) locals[keyName] = key;
16078
- locals[valueName] = collection[key];
16607
+ if (trackFn) {
16608
+ for (var trackIndex = 0; trackIndex < collection.length; trackIndex++) {
16609
+ locals[valueName] = collection[trackIndex];
16610
+ if (trackFn(scope, locals) == key) break;
16611
+ }
16612
+ } else {
16613
+ locals[valueName] = collection[key];
16614
+ }
16079
16615
  value.push(valueFn(scope, locals));
16080
16616
  }
16081
16617
  }
@@ -16087,9 +16623,19 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
16087
16623
  } else if (key == ''){
16088
16624
  value = null;
16089
16625
  } else {
16090
- locals[valueName] = collection[key];
16091
- if (keyName) locals[keyName] = key;
16092
- value = valueFn(scope, locals);
16626
+ if (trackFn) {
16627
+ for (var trackIndex = 0; trackIndex < collection.length; trackIndex++) {
16628
+ locals[valueName] = collection[trackIndex];
16629
+ if (trackFn(scope, locals) == key) {
16630
+ value = valueFn(scope, locals);
16631
+ break;
16632
+ }
16633
+ }
16634
+ } else {
16635
+ locals[valueName] = collection[key];
16636
+ if (keyName) locals[keyName] = key;
16637
+ value = valueFn(scope, locals);
16638
+ }
16093
16639
  }
16094
16640
  }
16095
16641
  ctrl.$setViewValue(value);
@@ -16121,11 +16667,15 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
16121
16667
  label;
16122
16668
 
16123
16669
  if (multiple) {
16124
- selectedSet = new HashMap(modelValue);
16125
- } else if (modelValue === null || nullOption) {
16126
- // if we are not multiselect, and we are null then we have to add the nullOption
16127
- optionGroups[''].push({selected:modelValue === null, id:'', label:''});
16128
- selectedSet = true;
16670
+ if (trackFn && isArray(modelValue)) {
16671
+ selectedSet = new HashMap([]);
16672
+ for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
16673
+ locals[valueName] = modelValue[trackIndex];
16674
+ selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
16675
+ }
16676
+ } else {
16677
+ selectedSet = new HashMap(modelValue);
16678
+ }
16129
16679
  }
16130
16680
 
16131
16681
  // We now build up the list of options we need (we merge later)
@@ -16137,22 +16687,33 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
16137
16687
  optionGroupNames.push(optionGroupName);
16138
16688
  }
16139
16689
  if (multiple) {
16140
- selected = selectedSet.remove(valueFn(scope, locals)) != undefined;
16690
+ selected = selectedSet.remove(trackFn ? trackFn(scope, locals) : valueFn(scope, locals)) != undefined;
16141
16691
  } else {
16142
- selected = modelValue === valueFn(scope, locals);
16692
+ if (trackFn) {
16693
+ var modelCast = {};
16694
+ modelCast[valueName] = modelValue;
16695
+ selected = trackFn(scope, modelCast) === trackFn(scope, locals);
16696
+ } else {
16697
+ selected = modelValue === valueFn(scope, locals);
16698
+ }
16143
16699
  selectedSet = selectedSet || selected; // see if at least one item is selected
16144
16700
  }
16145
16701
  label = displayFn(scope, locals); // what will be seen by the user
16146
16702
  label = label === undefined ? '' : label; // doing displayFn(scope, locals) || '' overwrites zero values
16147
16703
  optionGroup.push({
16148
- id: keyName ? keys[index] : index, // either the index into array or key from object
16704
+ id: trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index), // either the index into array or key from object
16149
16705
  label: label,
16150
16706
  selected: selected // determine if we should be selected
16151
16707
  });
16152
16708
  }
16153
- if (!multiple && !selectedSet) {
16154
- // nothing was selected, we have to insert the undefined item
16155
- optionGroups[''].unshift({id:'?', label:'', selected:true});
16709
+ if (!multiple) {
16710
+ if (nullOption || modelValue === null) {
16711
+ // insert null option if we have a placeholder, or the model is null
16712
+ optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
16713
+ } else if (!selectedSet) {
16714
+ // option could not be found, we have to insert the undefined item
16715
+ optionGroups[''].unshift({id:'?', label:'', selected:true});
16716
+ }
16156
16717
  }
16157
16718
 
16158
16719
  // Now we need to update the list of DOM nodes to match the optionGroups we computed above
@@ -16196,7 +16757,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
16196
16757
  if (existingOption.id !== option.id) {
16197
16758
  lastElement.val(existingOption.id = option.id);
16198
16759
  }
16199
- if (existingOption.element.selected !== option.selected) {
16760
+ // lastElement.prop('selected') provided by jQuery has side-effects
16761
+ if (lastElement[0].selected !== option.selected) {
16200
16762
  lastElement.prop('selected', (existingOption.selected = option.selected));
16201
16763
  }
16202
16764
  } else {