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
  */
@@ -25,19 +25,28 @@
25
25
  * the need to interact with the low level {@link ng.$http $http} service.
26
26
  *
27
27
  * # Installation
28
- * To use $resource make sure you have included the `angular-resource.js` that comes in Angular
29
- * package. You also can find this stuff in {@link http://code.angularjs.org/ code.angularjs.org}.
28
+ * To use $resource make sure you have included the `angular-resource.js` that comes in Angular
29
+ * package. You can also find this file on Google CDN, bower as well as at
30
+ * {@link http://code.angularjs.org/ code.angularjs.org}.
31
+ *
30
32
  * Finally load the module in your application:
31
33
  *
32
34
  * angular.module('app', ['ngResource']);
33
35
  *
34
- * and you ready to get started!
36
+ * and you are ready to get started!
35
37
  *
36
38
  * @param {string} url A parametrized URL template with parameters prefixed by `:` as in
37
39
  * `/user/:username`. If you are using a URL with a port number (e.g.
38
40
  * `http://example.com:8080/api`), you'll need to escape the colon character before the port
39
41
  * number, like this: `$resource('http://example.com\\:8080/api')`.
40
42
  *
43
+ * If you are using a url with a suffix, just add the suffix, like this:
44
+ * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')
45
+ * or even `$resource('http://example.com/resource/:resource_id.:format')`
46
+ * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
47
+ * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
48
+ * can escape it with `/\.`.
49
+ *
41
50
  * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
42
51
  * `actions` methods. If any of the parameter value is a function, it will be executed every time
43
52
  * when a param value needs to be obtained for a request (unless the param was overridden).
@@ -82,7 +91,8 @@
82
91
  * GET request, otherwise if a cache instance built with
83
92
  * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
84
93
  * caching.
85
- * - **`timeout`** – `{number}` – timeout in milliseconds.
94
+ * - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
95
+ * should abort the request when resolved.
86
96
  * - **`withCredentials`** - `{boolean}` - whether to to set the `withCredentials` flag on the
87
97
  * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
88
98
  * requests with credentials} for more information.
@@ -321,7 +331,7 @@ angular.module('ngResource', ['ng']).
321
331
  }
322
332
 
323
333
  function Route(template, defaults) {
324
- this.template = template = template + '#';
334
+ this.template = template;
325
335
  this.defaults = defaults || {};
326
336
  this.urlParams = {};
327
337
  }
@@ -359,8 +369,14 @@ angular.module('ngResource', ['ng']).
359
369
  }
360
370
  });
361
371
 
362
- // set the url
363
- config.url = url.replace(/\/?#$/, '').replace(/\/*$/, '');
372
+ // strip trailing slashes and set the url
373
+ url = url.replace(/\/+$/, '');
374
+ // then replace collapse `/.` if found in the last URL path segment before the query
375
+ // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
376
+ url = url.replace(/\/\.(?=\w+($|\?))/, '.');
377
+ // replace escaped `/\.` with `/.`
378
+ config.url = url.replace(/\/\\\./, '/.');
379
+
364
380
 
365
381
  // set params - delegate param encoding to $http
366
382
  forEach(params, function(value, key){
@@ -383,7 +399,7 @@ angular.module('ngResource', ['ng']).
383
399
  actionParams = extend({}, paramDefaults, actionParams);
384
400
  forEach(actionParams, function(value, key){
385
401
  if (isFunction(value)) { value = value(); }
386
- ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
402
+ ids[key] = value && value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
387
403
  });
388
404
  return ids;
389
405
  }
@@ -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
  */
@@ -9441,7 +9441,7 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
9441
9441
  })( window );
9442
9442
 
9443
9443
  /**
9444
- * @license AngularJS v1.1.4
9444
+ * @license AngularJS v1.1.5
9445
9445
  * (c) 2010-2012 Google, Inc. http://angularjs.org
9446
9446
  * License: MIT
9447
9447
  */
@@ -9529,6 +9529,27 @@ function noConflict() {
9529
9529
  return a;
9530
9530
  }
9531
9531
 
9532
+ /**
9533
+ * @private
9534
+ * @param {*} obj
9535
+ * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
9536
+ */
9537
+ function isArrayLike(obj) {
9538
+ if (!obj || (typeof obj.length !== 'number')) return false;
9539
+
9540
+ // We have on object which has length property. Should we treat it as array?
9541
+ if (typeof obj.hasOwnProperty != 'function' &&
9542
+ typeof obj.constructor != 'function') {
9543
+ // This is here for IE8: it is a bogus object treat it as array;
9544
+ return true;
9545
+ } else {
9546
+ return obj instanceof JQLite || // JQLite
9547
+ (jQuery && obj instanceof jQuery) || // jQuery
9548
+ toString.call(obj) !== '[object Object]' || // some browser native object
9549
+ typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
9550
+ }
9551
+ }
9552
+
9532
9553
  /**
9533
9554
  * @ngdoc function
9534
9555
  * @name angular.forEach
@@ -9556,30 +9577,6 @@ function noConflict() {
9556
9577
  * @param {Object=} context Object to become context (`this`) for the iterator function.
9557
9578
  * @returns {Object|Array} Reference to `obj`.
9558
9579
  */
9559
-
9560
-
9561
- /**
9562
- * @private
9563
- * @param {*} obj
9564
- * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
9565
- */
9566
- function isArrayLike(obj) {
9567
- if (!obj || (typeof obj.length !== 'number')) return false;
9568
-
9569
- // We have on object which has length property. Should we treat it as array?
9570
- if (typeof obj.hasOwnProperty != 'function' &&
9571
- typeof obj.constructor != 'function') {
9572
- // This is here for IE8: it is a bogus object treat it as array;
9573
- return true;
9574
- } else {
9575
- return obj instanceof JQLite || // JQLite
9576
- (jQuery && obj instanceof jQuery) || // jQuery
9577
- toString.call(obj) !== '[object Object]' || // some browser native object
9578
- typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
9579
- }
9580
- }
9581
-
9582
-
9583
9580
  function forEach(obj, iterator, context) {
9584
9581
  var key;
9585
9582
  if (obj) {
@@ -9663,6 +9660,21 @@ function nextUid() {
9663
9660
  return uid.join('');
9664
9661
  }
9665
9662
 
9663
+
9664
+ /**
9665
+ * Set or clear the hashkey for an object.
9666
+ * @param obj object
9667
+ * @param h the hashkey (!truthy to delete the hashkey)
9668
+ */
9669
+ function setHashKey(obj, h) {
9670
+ if (h) {
9671
+ obj.$$hashKey = h;
9672
+ }
9673
+ else {
9674
+ delete obj.$$hashKey;
9675
+ }
9676
+ }
9677
+
9666
9678
  /**
9667
9679
  * @ngdoc function
9668
9680
  * @name angular.extend
@@ -9674,8 +9686,10 @@ function nextUid() {
9674
9686
  *
9675
9687
  * @param {Object} dst Destination object.
9676
9688
  * @param {...Object} src Source object(s).
9689
+ * @returns {Object} Reference to `dst`.
9677
9690
  */
9678
9691
  function extend(dst) {
9692
+ var h = dst.$$hashKey;
9679
9693
  forEach(arguments, function(obj){
9680
9694
  if (obj !== dst) {
9681
9695
  forEach(obj, function(value, key){
@@ -9683,6 +9697,8 @@ function extend(dst) {
9683
9697
  });
9684
9698
  }
9685
9699
  });
9700
+
9701
+ setHashKey(dst,h);
9686
9702
  return dst;
9687
9703
  }
9688
9704
 
@@ -10042,12 +10058,14 @@ function copy(source, destination){
10042
10058
  destination.push(copy(source[i]));
10043
10059
  }
10044
10060
  } else {
10061
+ var h = destination.$$hashKey;
10045
10062
  forEach(destination, function(value, key){
10046
10063
  delete destination[key];
10047
10064
  });
10048
10065
  for ( var key in source) {
10049
10066
  destination[key] = copy(source[key]);
10050
10067
  }
10068
+ setHashKey(destination,h);
10051
10069
  }
10052
10070
  }
10053
10071
  return destination;
@@ -10087,7 +10105,7 @@ function shallowCopy(src, dst) {
10087
10105
  * During a property comparison, properties of `function` type and properties with names
10088
10106
  * that begin with `$` are ignored.
10089
10107
  *
10090
- * Scope and DOMWindow objects are being compared only be identify (`===`).
10108
+ * Scope and DOMWindow objects are being compared only by identify (`===`).
10091
10109
  *
10092
10110
  * @param {*} o1 Object or value to compare.
10093
10111
  * @param {*} o2 Object or value to compare.
@@ -10147,7 +10165,7 @@ function sliceArgs(args, startIndex) {
10147
10165
  *
10148
10166
  * @description
10149
10167
  * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
10150
- * `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
10168
+ * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
10151
10169
  * known as [function currying](http://en.wikipedia.org/wiki/Currying).
10152
10170
  *
10153
10171
  * @param {Object} self Context which `fn` should be evaluated in.
@@ -10340,7 +10358,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
10340
10358
  *
10341
10359
  * @description
10342
10360
  *
10343
- * Use this directive to auto-bootstrap on application. Only
10361
+ * Use this directive to auto-bootstrap an application. Only
10344
10362
  * one directive can be used per HTML document. The directive
10345
10363
  * designates the root of the application and is typically placed
10346
10364
  * at the root of the page.
@@ -10423,12 +10441,13 @@ function bootstrap(element, modules) {
10423
10441
  }]);
10424
10442
  modules.unshift('ng');
10425
10443
  var injector = createInjector(modules);
10426
- injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
10427
- function(scope, element, compile, injector) {
10444
+ injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animator',
10445
+ function(scope, element, compile, injector, animator) {
10428
10446
  scope.$apply(function() {
10429
10447
  element.data('$injector', injector);
10430
10448
  compile(element)(scope);
10431
10449
  });
10450
+ animator.enabled(true);
10432
10451
  }]
10433
10452
  );
10434
10453
  return injector;
@@ -10479,7 +10498,7 @@ function bindJQuery() {
10479
10498
  }
10480
10499
 
10481
10500
  /**
10482
- * throw error of the argument is falsy.
10501
+ * throw error if the argument is falsy.
10483
10502
  */
10484
10503
  function assertArg(arg, name, reason) {
10485
10504
  if (!arg) {
@@ -10787,11 +10806,11 @@ function setupModuleLoader(window) {
10787
10806
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
10788
10807
  */
10789
10808
  var version = {
10790
- full: '1.1.4', // all of these placeholder strings will be replaced by grunt's
10809
+ full: '1.1.5', // all of these placeholder strings will be replaced by grunt's
10791
10810
  major: 1, // package task
10792
10811
  minor: 1,
10793
- dot: 4,
10794
- codeName: 'quantum-manipulation'
10812
+ dot: 5,
10813
+ codeName: 'triangle-squarification'
10795
10814
  };
10796
10815
 
10797
10816
 
@@ -10855,6 +10874,7 @@ function publishExternalAPI(angular){
10855
10874
  ngController: ngControllerDirective,
10856
10875
  ngForm: ngFormDirective,
10857
10876
  ngHide: ngHideDirective,
10877
+ ngIf: ngIfDirective,
10858
10878
  ngInclude: ngIncludeDirective,
10859
10879
  ngInit: ngInitDirective,
10860
10880
  ngNonBindable: ngNonBindableDirective,
@@ -10939,18 +10959,18 @@ function publishExternalAPI(angular){
10939
10959
  * - [after()](http://api.jquery.com/after/)
10940
10960
  * - [append()](http://api.jquery.com/append/)
10941
10961
  * - [attr()](http://api.jquery.com/attr/)
10942
- * - [bind()](http://api.jquery.com/bind/)
10943
- * - [children()](http://api.jquery.com/children/)
10962
+ * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
10963
+ * - [children()](http://api.jquery.com/children/) - Does not support selectors
10944
10964
  * - [clone()](http://api.jquery.com/clone/)
10945
10965
  * - [contents()](http://api.jquery.com/contents/)
10946
10966
  * - [css()](http://api.jquery.com/css/)
10947
10967
  * - [data()](http://api.jquery.com/data/)
10948
10968
  * - [eq()](http://api.jquery.com/eq/)
10949
- * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name.
10969
+ * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
10950
10970
  * - [hasClass()](http://api.jquery.com/hasClass/)
10951
10971
  * - [html()](http://api.jquery.com/html/)
10952
- * - [next()](http://api.jquery.com/next/)
10953
- * - [parent()](http://api.jquery.com/parent/)
10972
+ * - [next()](http://api.jquery.com/next/) - Does not support selectors
10973
+ * - [parent()](http://api.jquery.com/parent/) - Does not support selectors
10954
10974
  * - [prepend()](http://api.jquery.com/prepend/)
10955
10975
  * - [prop()](http://api.jquery.com/prop/)
10956
10976
  * - [ready()](http://api.jquery.com/ready/)
@@ -10961,8 +10981,8 @@ function publishExternalAPI(angular){
10961
10981
  * - [replaceWith()](http://api.jquery.com/replaceWith/)
10962
10982
  * - [text()](http://api.jquery.com/text/)
10963
10983
  * - [toggleClass()](http://api.jquery.com/toggleClass/)
10964
- * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
10965
- * - [unbind()](http://api.jquery.com/unbind/)
10984
+ * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
10985
+ * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
10966
10986
  * - [val()](http://api.jquery.com/val/)
10967
10987
  * - [wrap()](http://api.jquery.com/wrap/)
10968
10988
  *
@@ -11467,7 +11487,7 @@ function createEventHandler(element, events) {
11467
11487
  }
11468
11488
 
11469
11489
  event.isDefaultPrevented = function() {
11470
- return event.defaultPrevented;
11490
+ return event.defaultPrevented || event.returnValue == false;
11471
11491
  };
11472
11492
 
11473
11493
  forEach(events[type || event.type], function(fn) {
@@ -11514,23 +11534,43 @@ forEach({
11514
11534
 
11515
11535
  if (!eventFns) {
11516
11536
  if (type == 'mouseenter' || type == 'mouseleave') {
11517
- var counter = 0;
11537
+ var contains = document.body.contains || document.body.compareDocumentPosition ?
11538
+ function( a, b ) {
11539
+ var adown = a.nodeType === 9 ? a.documentElement : a,
11540
+ bup = b && b.parentNode;
11541
+ return a === bup || !!( bup && bup.nodeType === 1 && (
11542
+ adown.contains ?
11543
+ adown.contains( bup ) :
11544
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
11545
+ ));
11546
+ } :
11547
+ function( a, b ) {
11548
+ if ( b ) {
11549
+ while ( (b = b.parentNode) ) {
11550
+ if ( b === a ) {
11551
+ return true;
11552
+ }
11553
+ }
11554
+ }
11555
+ return false;
11556
+ };
11518
11557
 
11519
- events.mouseenter = [];
11520
- events.mouseleave = [];
11558
+ events[type] = [];
11559
+
11560
+ // Refer to jQuery's implementation of mouseenter & mouseleave
11561
+ // Read about mouseenter and mouseleave:
11562
+ // http://www.quirksmode.org/js/events_mouse.html#link8
11563
+ var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}
11564
+ bindFn(element, eventmap[type], function(event) {
11565
+ var ret, target = this, related = event.relatedTarget;
11566
+ // For mousenter/leave call the handler if related is outside the target.
11567
+ // NB: No relatedTarget if the mouse left/entered the browser window
11568
+ if ( !related || (related !== target && !contains(target, related)) ){
11569
+ handle(event, type);
11570
+ }
11521
11571
 
11522
- bindFn(element, 'mouseover', function(event) {
11523
- counter++;
11524
- if (counter == 1) {
11525
- handle(event, 'mouseenter');
11526
- }
11527
- });
11528
- bindFn(element, 'mouseout', function(event) {
11529
- counter --;
11530
- if (counter == 0) {
11531
- handle(event, 'mouseleave');
11532
- }
11533
11572
  });
11573
+
11534
11574
  } else {
11535
11575
  addEventListenerFn(element, type, handle);
11536
11576
  events[type] = [];
@@ -11650,9 +11690,10 @@ forEach({
11650
11690
 
11651
11691
  triggerHandler: function(element, eventName) {
11652
11692
  var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName];
11693
+ var event;
11653
11694
 
11654
11695
  forEach(eventFns, function(fn) {
11655
- fn.call(element, null);
11696
+ fn.call(element, {preventDefault: noop});
11656
11697
  });
11657
11698
  }
11658
11699
  }, function(fn, name){
@@ -11803,7 +11844,7 @@ function annotate(fn) {
11803
11844
  }
11804
11845
  } else if (isArray(fn)) {
11805
11846
  last = fn.length - 1;
11806
- assertArgFn(fn[last], 'fn')
11847
+ assertArgFn(fn[last], 'fn');
11807
11848
  $inject = fn.slice(0, last);
11808
11849
  } else {
11809
11850
  assertArgFn(fn, 'fn', true);
@@ -11837,7 +11878,7 @@ function annotate(fn) {
11837
11878
  * # Injection Function Annotation
11838
11879
  *
11839
11880
  * JavaScript does not have annotations, and annotations are needed for dependency injection. The
11840
- * following ways are all valid way of annotating function with injection arguments and are equivalent.
11881
+ * following are all valid ways of annotating function with injection arguments and are equivalent.
11841
11882
  *
11842
11883
  * <pre>
11843
11884
  * // inferred (only works if code not minified/obfuscated)
@@ -11892,6 +11933,18 @@ function annotate(fn) {
11892
11933
  * @returns {*} the value returned by the invoked `fn` function.
11893
11934
  */
11894
11935
 
11936
+ /**
11937
+ * @ngdoc method
11938
+ * @name AUTO.$injector#has
11939
+ * @methodOf AUTO.$injector
11940
+ *
11941
+ * @description
11942
+ * Allows the user to query if the particular service exist.
11943
+ *
11944
+ * @param {string} Name of the service to query.
11945
+ * @returns {boolean} returns true if injector has given service.
11946
+ */
11947
+
11895
11948
  /**
11896
11949
  * @ngdoc method
11897
11950
  * @name AUTO.$injector#instantiate
@@ -11966,7 +12019,7 @@ function annotate(fn) {
11966
12019
  * // ...
11967
12020
  * };
11968
12021
  * tmpFn.$inject = ['$compile', '$rootScope'];
11969
- * injector.invoke(tempFn);
12022
+ * injector.invoke(tmpFn);
11970
12023
  *
11971
12024
  * // To better support inline function the inline annotation is supported
11972
12025
  * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
@@ -12019,7 +12072,7 @@ function annotate(fn) {
12019
12072
  *
12020
12073
  * beforeEach(module(function($provide) {
12021
12074
  * $provide.provider('greet', GreetProvider);
12022
- * });
12075
+ * }));
12023
12076
  *
12024
12077
  * it('should greet', inject(function(greet) {
12025
12078
  * expect(greet('angular')).toEqual('Hello angular!');
@@ -12032,9 +12085,7 @@ function annotate(fn) {
12032
12085
  * inject(function(greet) {
12033
12086
  * expect(greet('angular')).toEqual('Ahoj angular!');
12034
12087
  * });
12035
- * )};
12036
- *
12037
- * });
12088
+ * });
12038
12089
  * </pre>
12039
12090
  */
12040
12091
 
@@ -12128,7 +12179,7 @@ function annotate(fn) {
12128
12179
  *
12129
12180
  * @param {string} name The name of the service to decorate.
12130
12181
  * @param {function()} decorator This function will be invoked when the service needs to be
12131
- * instanciated. The function is called using the {@link AUTO.$injector#invoke
12182
+ * instantiated. The function is called using the {@link AUTO.$injector#invoke
12132
12183
  * injector.invoke} method and is therefore fully injectable. Local injection arguments:
12133
12184
  *
12134
12185
  * * `$delegate` - The original service instance, which can be monkey patched, configured,
@@ -12327,6 +12378,8 @@ function createInjector(modulesToLoad) {
12327
12378
  var Constructor = function() {},
12328
12379
  instance, returnedValue;
12329
12380
 
12381
+ // Check if Type is annotated and use just the given function at n-1 as parameter
12382
+ // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
12330
12383
  Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
12331
12384
  instance = new Constructor();
12332
12385
  returnedValue = invoke(Type, instance, locals);
@@ -12338,7 +12391,10 @@ function createInjector(modulesToLoad) {
12338
12391
  invoke: invoke,
12339
12392
  instantiate: instantiate,
12340
12393
  get: getService,
12341
- annotate: annotate
12394
+ annotate: annotate,
12395
+ has: function(name) {
12396
+ return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
12397
+ }
12342
12398
  };
12343
12399
  }
12344
12400
  }
@@ -12464,18 +12520,14 @@ function $AnimationProvider($provide) {
12464
12520
  */
12465
12521
  return function $animation(name) {
12466
12522
  if (name) {
12467
- try {
12468
- return $injector.get(camelCase(name) + suffix);
12469
- } catch (e) {
12470
- //TODO(misko): this is a hack! we should have a better way to test if the injector has a given key.
12471
- // The issue is that the animations are optional, and if not present they should be silently ignored.
12472
- // The proper way to fix this is to add API onto the injector so that we can ask to see if a given
12473
- // animation is supported.
12523
+ var animationName = camelCase(name) + suffix;
12524
+ if ($injector.has(animationName)) {
12525
+ return $injector.get(animationName);
12474
12526
  }
12475
12527
  }
12476
- }
12528
+ };
12477
12529
  }];
12478
- };
12530
+ }
12479
12531
 
12480
12532
  // NOTE: this is a pseudo directive.
12481
12533
 
@@ -12485,18 +12537,21 @@ function $AnimationProvider($provide) {
12485
12537
  *
12486
12538
  * @description
12487
12539
  * The `ngAnimate` directive works as an attribute that is attached alongside pre-existing directives.
12488
- * It effects how the directive will perform DOM manipulation. This allows for complex animations to take place while
12489
- * without burduning the directive which uses the animation with animation details. The built dn directives
12540
+ * It effects how the directive will perform DOM manipulation. This allows for complex animations to take place
12541
+ * without burdening the directive which uses the animation with animation details. The built in directives
12490
12542
  * `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView` already accept `ngAnimate` directive.
12491
12543
  * Custom directives can take advantage of animation through {@link ng.$animator $animator service}.
12492
12544
  *
12493
12545
  * Below is a more detailed breakdown of the supported callback events provided by pre-exisitng ng directives:
12494
12546
  *
12495
- * * {@link ng.directive:ngRepeat#animations ngRepeat} — enter, leave and move
12496
- * * {@link ng.directive:ngView#animations ngView} — enter and leave
12497
- * * {@link ng.directive:ngInclude#animations ngInclude} enter and leave
12498
- * * {@link ng.directive:ngSwitch#animations ngSwitch} enter and leave
12499
- * * {@link ng.directive:ngShow#animations ngShow & ngHide} - show and hide respectively
12547
+ * | Directive | Supported Animations |
12548
+ * |========================================================== |====================================================|
12549
+ * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move |
12550
+ * | {@link ng.directive:ngView#animations ngView} | enter and leave |
12551
+ * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
12552
+ * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
12553
+ * | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
12554
+ * | {@link ng.directive:ngShow#animations ngShow & ngHide} | show and hide |
12500
12555
  *
12501
12556
  * You can find out more information about animations upon visiting each directive page.
12502
12557
  *
@@ -12517,23 +12572,26 @@ function $AnimationProvider($provide) {
12517
12572
  *
12518
12573
  * The `event1` and `event2` attributes refer to the animation events specific to the directive that has been assigned.
12519
12574
  *
12575
+ * Keep in mind that if an animation is running, no child element of such animation can also be animated.
12576
+ *
12520
12577
  * <h2>CSS-defined Animations</h2>
12521
- * By default, ngAnimate attaches two CSS3 classes per animation event to the DOM element to achieve the animation.
12522
- * This is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions.
12523
- * All that is required is the following CSS code:
12578
+ * By default, ngAnimate attaches two CSS classes per animation event to the DOM element to achieve the animation.
12579
+ * It is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions as
12580
+ * well as CSS animations.
12581
+ *
12582
+ * The following code below demonstrates how to perform animations using **CSS transitions** with ngAnimate:
12524
12583
  *
12525
12584
  * <pre>
12526
12585
  * <style type="text/css">
12527
12586
  * /&#42;
12528
- * The animate-enter prefix is the event name that you
12587
+ * The animate-enter CSS class is the event name that you
12529
12588
  * have provided within the ngAnimate attribute.
12530
12589
  * &#42;/
12531
- * .animate-enter-setup {
12590
+ * .animate-enter {
12532
12591
  * -webkit-transition: 1s linear all; /&#42; Safari/Chrome &#42;/
12533
12592
  * -moz-transition: 1s linear all; /&#42; Firefox &#42;/
12534
- * -ms-transition: 1s linear all; /&#42; IE10 &#42;/
12535
12593
  * -o-transition: 1s linear all; /&#42; Opera &#42;/
12536
- * transition: 1s linear all; /&#42; Future Browsers &#42;/
12594
+ * transition: 1s linear all; /&#42; IE10+ and Future Browsers &#42;/
12537
12595
  *
12538
12596
  * /&#42; The animation preparation code &#42;/
12539
12597
  * opacity: 0;
@@ -12544,7 +12602,7 @@ function $AnimationProvider($provide) {
12544
12602
  * classes together to avoid any CSS-specificity
12545
12603
  * conflicts
12546
12604
  * &#42;/
12547
- * .animate-enter-setup.animate-enter-start {
12605
+ * .animate-enter.animate-enter-active {
12548
12606
  * /&#42; The animation code itself &#42;/
12549
12607
  * opacity: 1;
12550
12608
  * }
@@ -12553,16 +12611,49 @@ function $AnimationProvider($provide) {
12553
12611
  * <div ng-directive ng-animate="{enter: 'animate-enter'}"></div>
12554
12612
  * </pre>
12555
12613
  *
12556
- * Upon DOM mutation, the setup class is added first, then the browser is allowed to reflow the content and then,
12557
- * the start class is added to trigger the animation. The ngAnimate directive will automatically extract the duration
12614
+ * The following code below demonstrates how to perform animations using **CSS animations** with ngAnimate:
12615
+ *
12616
+ * <pre>
12617
+ * <style type="text/css">
12618
+ * .animate-enter {
12619
+ * -webkit-animation: enter_sequence 1s linear; /&#42; Safari/Chrome &#42;/
12620
+ * -moz-animation: enter_sequence 1s linear; /&#42; Firefox &#42;/
12621
+ * -o-animation: enter_sequence 1s linear; /&#42; Opera &#42;/
12622
+ * animation: enter_sequence 1s linear; /&#42; IE10+ and Future Browsers &#42;/
12623
+ * }
12624
+ * &#64-webkit-keyframes enter_sequence {
12625
+ * from { opacity:0; }
12626
+ * to { opacity:1; }
12627
+ * }
12628
+ * &#64-moz-keyframes enter_sequence {
12629
+ * from { opacity:0; }
12630
+ * to { opacity:1; }
12631
+ * }
12632
+ * &#64-o-keyframes enter_sequence {
12633
+ * from { opacity:0; }
12634
+ * to { opacity:1; }
12635
+ * }
12636
+ * &#64keyframes enter_sequence {
12637
+ * from { opacity:0; }
12638
+ * to { opacity:1; }
12639
+ * }
12640
+ * </style>
12641
+ *
12642
+ * <div ng-directive ng-animate="{enter: 'animate-enter'}"></div>
12643
+ * </pre>
12644
+ *
12645
+ * ngAnimate will first examine any CSS animation code and then fallback to using CSS transitions.
12646
+ *
12647
+ * Upon DOM mutation, the event class is added first, then the browser is allowed to reflow the content and then,
12648
+ * the active class is added to trigger the animation. The ngAnimate directive will automatically extract the duration
12558
12649
  * of the animation to determine when the animation ends. Once the animation is over then both CSS classes will be
12559
- * removed from the DOM. If a browser does not support CSS transitions then the animation will start and end
12650
+ * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
12560
12651
  * immediately resulting in a DOM element that is at it's final state. This final state is when the DOM element
12561
- * has no CSS animation classes surrounding it.
12652
+ * has no CSS transition/animation classes surrounding it.
12562
12653
  *
12563
12654
  * <h2>JavaScript-defined Animations</h2>
12564
- * In the event that you do not want to use CSS3 animations or if you wish to offer animations to browsers that do not
12565
- * yet support them, then you can make use of JavaScript animations defined inside ngModule.
12655
+ * 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
12656
+ * yet support them, then you can make use of JavaScript animations defined inside of your AngularJS module.
12566
12657
  *
12567
12658
  * <pre>
12568
12659
  * var ngModule = angular.module('YourApp', []);
@@ -12589,8 +12680,8 @@ function $AnimationProvider($provide) {
12589
12680
  *
12590
12681
  * As you can see, the JavaScript code follows a similar template to the CSS3 animations. Once defined, the animation
12591
12682
  * can be used in the same way with the ngAnimate attribute. Keep in mind that, when using JavaScript-enabled
12592
- * animations, ngAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're using
12593
- * JavaScript animations) to animated the element, but it will not attempt to find any CSS3 transition duration value.
12683
+ * animations, ngAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're not using
12684
+ * CSS animations) to animated the element, but it will not attempt to find any CSS3 transition or animation duration/delay values.
12594
12685
  * It will instead close off the animation once the provided done function is executed. So it's important that you
12595
12686
  * make sure your animations remember to fire off the done function once the animations are complete.
12596
12687
  *
@@ -12598,151 +12689,214 @@ function $AnimationProvider($provide) {
12598
12689
  *
12599
12690
  */
12600
12691
 
12601
- /**
12602
- * @ngdoc function
12603
- * @name ng.$animator
12604
- *
12605
- * @description
12606
- * The $animator service provides the DOM manipulation API which is decorated with animations.
12607
- *
12608
- * @param {Scope} scope the scope for the ng-animate.
12609
- * @param {Attributes} attr the attributes object which contains the ngAnimate key / value pair. (The attributes are
12610
- * passed into the linking function of the directive using the `$animator`.)
12611
- * @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods.
12612
- */
12613
12692
  var $AnimatorProvider = function() {
12614
- this.$get = ['$animation', '$window', '$sniffer', function($animation, $window, $sniffer) {
12615
- return function(scope, attrs) {
12616
- var ngAnimateAttr = attrs.ngAnimate;
12617
- var animator = {};
12618
-
12619
- /**
12620
- * @ngdoc function
12621
- * @name ng.animator#enter
12622
- * @methodOf ng.$animator
12623
- * @function
12624
- *
12625
- * @description
12626
- * Injects the element object into the DOM (inside of the parent element) and then runs the enter animation.
12627
- *
12628
- * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
12629
- * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
12630
- * @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
12631
- */
12632
- animator.enter = animateActionFactory('enter', insert, noop);
12693
+ var NG_ANIMATE_CONTROLLER = '$ngAnimateController';
12694
+ var rootAnimateController = {running:true};
12633
12695
 
12634
- /**
12635
- * @ngdoc function
12636
- * @name ng.animator#leave
12637
- * @methodOf ng.$animator
12638
- * @function
12639
- *
12640
- * @description
12641
- * Runs the leave animation operation and, upon completion, removes the element from the DOM.
12642
- *
12643
- * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
12644
- * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation
12645
- */
12646
- animator.leave = animateActionFactory('leave', noop, remove);
12696
+ this.$get = ['$animation', '$window', '$sniffer', '$rootElement', '$rootScope',
12697
+ function($animation, $window, $sniffer, $rootElement, $rootScope) {
12698
+ $rootElement.data(NG_ANIMATE_CONTROLLER, rootAnimateController);
12647
12699
 
12648
- /**
12649
- * @ngdoc function
12650
- * @name ng.animator#move
12651
- * @methodOf ng.$animator
12652
- * @function
12653
- *
12654
- * @description
12655
- * Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or
12656
- * add the element directly after the after element if present. Then the move animation will be run.
12657
- *
12658
- * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
12659
- * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
12660
- * @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
12661
- */
12662
- animator.move = animateActionFactory('move', move, noop);
12663
-
12664
- /**
12665
- * @ngdoc function
12666
- * @name ng.animator#show
12667
- * @methodOf ng.$animator
12668
- * @function
12669
- *
12670
- * @description
12671
- * Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after.
12672
- *
12673
- * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
12674
- */
12675
- animator.show = animateActionFactory('show', show, noop);
12676
-
12677
- /**
12678
- * @ngdoc function
12679
- * @name ng.animator#hide
12680
- * @methodOf ng.$animator
12681
- *
12682
- * @description
12683
- * Starts the hide animation first and sets the CSS `display` property to `none` upon completion.
12684
- *
12685
- * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
12686
- */
12687
- animator.hide = animateActionFactory('hide', noop, hide);
12688
- return animator;
12700
+ /**
12701
+ * @ngdoc function
12702
+ * @name ng.$animator
12703
+ * @function
12704
+ *
12705
+ * @description
12706
+ * The $animator.create service provides the DOM manipulation API which is decorated with animations.
12707
+ *
12708
+ * @param {Scope} scope the scope for the ng-animate.
12709
+ * @param {Attributes} attr the attributes object which contains the ngAnimate key / value pair. (The attributes are
12710
+ * passed into the linking function of the directive using the `$animator`.)
12711
+ * @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods.
12712
+ */
12713
+ var AnimatorService = function(scope, attrs) {
12714
+ var animator = {};
12715
+
12716
+ /**
12717
+ * @ngdoc function
12718
+ * @name ng.animator#enter
12719
+ * @methodOf ng.$animator
12720
+ * @function
12721
+ *
12722
+ * @description
12723
+ * Injects the element object into the DOM (inside of the parent element) and then runs the enter animation.
12724
+ *
12725
+ * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
12726
+ * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
12727
+ * @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
12728
+ */
12729
+ animator.enter = animateActionFactory('enter', insert, noop);
12730
+
12731
+ /**
12732
+ * @ngdoc function
12733
+ * @name ng.animator#leave
12734
+ * @methodOf ng.$animator
12735
+ * @function
12736
+ *
12737
+ * @description
12738
+ * Runs the leave animation operation and, upon completion, removes the element from the DOM.
12739
+ *
12740
+ * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
12741
+ * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation
12742
+ */
12743
+ animator.leave = animateActionFactory('leave', noop, remove);
12744
+
12745
+ /**
12746
+ * @ngdoc function
12747
+ * @name ng.animator#move
12748
+ * @methodOf ng.$animator
12749
+ * @function
12750
+ *
12751
+ * @description
12752
+ * Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or
12753
+ * add the element directly after the after element if present. Then the move animation will be run.
12754
+ *
12755
+ * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
12756
+ * @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
12757
+ * @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
12758
+ */
12759
+ animator.move = animateActionFactory('move', move, noop);
12760
+
12761
+ /**
12762
+ * @ngdoc function
12763
+ * @name ng.animator#show
12764
+ * @methodOf ng.$animator
12765
+ * @function
12766
+ *
12767
+ * @description
12768
+ * Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after.
12769
+ *
12770
+ * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
12771
+ */
12772
+ animator.show = animateActionFactory('show', show, noop);
12773
+
12774
+ /**
12775
+ * @ngdoc function
12776
+ * @name ng.animator#hide
12777
+ * @methodOf ng.$animator
12778
+ *
12779
+ * @description
12780
+ * Starts the hide animation first and sets the CSS `display` property to `none` upon completion.
12781
+ *
12782
+ * @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
12783
+ */
12784
+ animator.hide = animateActionFactory('hide', noop, hide);
12689
12785
 
12690
- function animateActionFactory(type, beforeFn, afterFn) {
12691
- var ngAnimateValue = ngAnimateAttr && scope.$eval(ngAnimateAttr);
12692
- var className = ngAnimateAttr
12693
- ? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type
12694
- : '';
12695
- var animationPolyfill = $animation(className);
12786
+ /**
12787
+ * @ngdoc function
12788
+ * @name ng.animator#animate
12789
+ * @methodOf ng.$animator
12790
+ *
12791
+ * @description
12792
+ * Triggers a custom animation event to be executed on the given element
12793
+ *
12794
+ * @param {jQuery/jqLite element} element that will be animated
12795
+ */
12796
+ animator.animate = function(event, element) {
12797
+ animateActionFactory(event, noop, noop)(element);
12798
+ }
12799
+ return animator;
12800
+
12801
+ function animateActionFactory(type, beforeFn, afterFn) {
12802
+ return function(element, parent, after) {
12803
+ var ngAnimateValue = scope.$eval(attrs.ngAnimate);
12804
+ var className = ngAnimateValue
12805
+ ? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type
12806
+ : '';
12807
+ var animationPolyfill = $animation(className);
12808
+ var polyfillSetup = animationPolyfill && animationPolyfill.setup;
12809
+ var polyfillStart = animationPolyfill && animationPolyfill.start;
12810
+ var polyfillCancel = animationPolyfill && animationPolyfill.cancel;
12811
+
12812
+ if (!className) {
12813
+ beforeFn(element, parent, after);
12814
+ afterFn(element, parent, after);
12815
+ } else {
12816
+ var activeClassName = className + '-active';
12696
12817
 
12697
- var polyfillSetup = animationPolyfill && animationPolyfill.setup;
12698
- var polyfillStart = animationPolyfill && animationPolyfill.start;
12818
+ if (!parent) {
12819
+ parent = after ? after.parent() : element.parent();
12820
+ }
12821
+ if ((!$sniffer.transitions && !polyfillSetup && !polyfillStart) ||
12822
+ (parent.inheritedData(NG_ANIMATE_CONTROLLER) || noop).running) {
12823
+ beforeFn(element, parent, after);
12824
+ afterFn(element, parent, after);
12825
+ return;
12826
+ }
12699
12827
 
12700
- if (!className) {
12701
- return function(element, parent, after) {
12702
- beforeFn(element, parent, after);
12703
- afterFn(element, parent, after);
12704
- }
12705
- } else {
12706
- var setupClass = className + '-setup';
12707
- var startClass = className + '-start';
12828
+ var animationData = element.data(NG_ANIMATE_CONTROLLER) || {};
12829
+ if(animationData.running) {
12830
+ (polyfillCancel || noop)(element);
12831
+ animationData.done();
12832
+ }
12708
12833
 
12709
- return function(element, parent, after) {
12710
- if (!$sniffer.supportsTransitions && !polyfillSetup && !polyfillStart) {
12834
+ element.data(NG_ANIMATE_CONTROLLER, {running:true, done:done});
12835
+ element.addClass(className);
12711
12836
  beforeFn(element, parent, after);
12712
- afterFn(element, parent, after);
12713
- return;
12714
- }
12837
+ if (element.length == 0) return done();
12715
12838
 
12716
- element.addClass(setupClass);
12717
- beforeFn(element, parent, after);
12718
- if (element.length == 0) return done();
12839
+ var memento = (polyfillSetup || noop)(element);
12719
12840
 
12720
- var memento = (polyfillSetup || noop)(element);
12841
+ // $window.setTimeout(beginAnimation, 0); this was causing the element not to animate
12842
+ // keep at 1 for animation dom rerender
12843
+ $window.setTimeout(beginAnimation, 1);
12844
+ }
12721
12845
 
12722
- // $window.setTimeout(beginAnimation, 0); this was causing the element not to animate
12723
- // keep at 1 for animation dom rerender
12724
- $window.setTimeout(beginAnimation, 1);
12846
+ function parseMaxTime(str) {
12847
+ var total = 0, values = isString(str) ? str.split(/\s*,\s*/) : [];
12848
+ forEach(values, function(value) {
12849
+ total = Math.max(parseFloat(value) || 0, total);
12850
+ });
12851
+ return total;
12852
+ }
12725
12853
 
12726
12854
  function beginAnimation() {
12727
- element.addClass(startClass);
12855
+ element.addClass(activeClassName);
12728
12856
  if (polyfillStart) {
12729
12857
  polyfillStart(element, done, memento);
12730
12858
  } else if (isFunction($window.getComputedStyle)) {
12859
+ //one day all browsers will have these properties
12860
+ var w3cAnimationProp = 'animation';
12861
+ var w3cTransitionProp = 'transition';
12862
+
12863
+ //but some still use vendor-prefixed styles
12864
+ var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
12731
12865
  var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
12732
- var w3cTransitionProp = 'transition'; //one day all browsers will have this
12733
12866
 
12734
- var durationKey = 'Duration';
12735
- var duration = 0;
12867
+ var durationKey = 'Duration',
12868
+ delayKey = 'Delay',
12869
+ animationIterationCountKey = 'IterationCount',
12870
+ duration = 0;
12871
+
12736
12872
  //we want all the styles defined before and after
12873
+ var ELEMENT_NODE = 1;
12737
12874
  forEach(element, function(element) {
12738
- var globalStyles = $window.getComputedStyle(element) || {};
12739
- duration = Math.max(
12740
- parseFloat(globalStyles[w3cTransitionProp + durationKey]) ||
12741
- parseFloat(globalStyles[vendorTransitionProp + durationKey]) ||
12742
- 0,
12743
- duration);
12744
- });
12875
+ if (element.nodeType == ELEMENT_NODE) {
12876
+ var w3cProp = w3cTransitionProp,
12877
+ vendorProp = vendorTransitionProp,
12878
+ iterations = 1,
12879
+ elementStyles = $window.getComputedStyle(element) || {};
12880
+
12881
+ //use CSS Animations over CSS Transitions
12882
+ if(parseFloat(elementStyles[w3cAnimationProp + durationKey]) > 0 ||
12883
+ parseFloat(elementStyles[vendorAnimationProp + durationKey]) > 0) {
12884
+ w3cProp = w3cAnimationProp;
12885
+ vendorProp = vendorAnimationProp;
12886
+ iterations = Math.max(parseInt(elementStyles[w3cProp + animationIterationCountKey]) || 0,
12887
+ parseInt(elementStyles[vendorProp + animationIterationCountKey]) || 0,
12888
+ iterations);
12889
+ }
12890
+
12891
+ var parsedDelay = Math.max(parseMaxTime(elementStyles[w3cProp + delayKey]),
12892
+ parseMaxTime(elementStyles[vendorProp + delayKey]));
12745
12893
 
12894
+ var parsedDuration = Math.max(parseMaxTime(elementStyles[w3cProp + durationKey]),
12895
+ parseMaxTime(elementStyles[vendorProp + durationKey]));
12896
+
12897
+ duration = Math.max(parsedDelay + (iterations * parsedDuration), duration);
12898
+ }
12899
+ });
12746
12900
  $window.setTimeout(done, duration * 1000);
12747
12901
  } else {
12748
12902
  done();
@@ -12750,40 +12904,65 @@ var $AnimatorProvider = function() {
12750
12904
  }
12751
12905
 
12752
12906
  function done() {
12753
- afterFn(element, parent, after);
12754
- element.removeClass(setupClass);
12755
- element.removeClass(startClass);
12907
+ if(!done.run) {
12908
+ done.run = true;
12909
+ afterFn(element, parent, after);
12910
+ element.removeClass(className);
12911
+ element.removeClass(activeClassName);
12912
+ element.removeData(NG_ANIMATE_CONTROLLER);
12913
+ }
12756
12914
  }
12915
+ };
12916
+ }
12917
+
12918
+ function show(element) {
12919
+ element.css('display', '');
12920
+ }
12921
+
12922
+ function hide(element) {
12923
+ element.css('display', 'none');
12924
+ }
12925
+
12926
+ function insert(element, parent, after) {
12927
+ if (after) {
12928
+ after.after(element);
12929
+ } else {
12930
+ parent.append(element);
12757
12931
  }
12758
12932
  }
12759
- }
12760
- }
12761
-
12762
- function show(element) {
12763
- element.css('display', '');
12764
- }
12765
-
12766
- function hide(element) {
12767
- element.css('display', 'none');
12768
- }
12933
+
12934
+ function remove(element) {
12935
+ element.remove();
12936
+ }
12937
+
12938
+ function move(element, parent, after) {
12939
+ // Do not remove element before insert. Removing will cause data associated with the
12940
+ // element to be dropped. Insert will implicitly do the remove.
12941
+ insert(element, parent, after);
12942
+ }
12943
+ };
12769
12944
 
12770
- function insert(element, parent, after) {
12771
- if (after) {
12772
- after.after(element);
12773
- } else {
12774
- parent.append(element);
12945
+ /**
12946
+ * @ngdoc function
12947
+ * @name ng.animator#enabled
12948
+ * @methodOf ng.$animator
12949
+ * @function
12950
+ *
12951
+ * @param {Boolean=} If provided then set the animation on or off.
12952
+ * @return {Boolean} Current animation state.
12953
+ *
12954
+ * @description
12955
+ * Globally enables/disables animations.
12956
+ *
12957
+ */
12958
+ AnimatorService.enabled = function(value) {
12959
+ if (arguments.length) {
12960
+ rootAnimateController.running = !value;
12775
12961
  }
12776
- }
12777
-
12778
- function remove(element) {
12779
- element.remove();
12780
- }
12962
+ return !rootAnimateController.running;
12963
+ };
12781
12964
 
12782
- function move(element, parent, after) {
12783
- // Do not remove element before insert. Removing will cause data associated with the
12784
- // element to be dropped. Insert will implicitly do the remove.
12785
- insert(element, parent, after);
12786
- }
12965
+ return AnimatorService;
12787
12966
  }];
12788
12967
  };
12789
12968
 
@@ -13084,7 +13263,13 @@ function Browser(window, document, $log, $sniffer) {
13084
13263
  cookie = cookieArray[i];
13085
13264
  index = cookie.indexOf('=');
13086
13265
  if (index > 0) { //ignore nameless cookies
13087
- lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1));
13266
+ var name = unescape(cookie.substring(0, index));
13267
+ // the first value that is seen for a cookie is the most
13268
+ // specific one. values for the same cookie name that
13269
+ // follow are for less specific paths.
13270
+ if (lastCookies[name] === undefined) {
13271
+ lastCookies[name] = unescape(cookie.substring(index + 1));
13272
+ }
13088
13273
  }
13089
13274
  }
13090
13275
  }
@@ -13897,9 +14082,9 @@ function $CompileProvider($provide) {
13897
14082
 
13898
14083
 
13899
14084
  /**
13900
- * Once the directives have been collected their compile functions is executed. This method
14085
+ * Once the directives have been collected, their compile functions are executed. This method
13901
14086
  * is responsible for inlining directive templates as well as terminating the application
13902
- * of the directives if the terminal directive has been reached..
14087
+ * of the directives if the terminal directive has been reached.
13903
14088
  *
13904
14089
  * @param {Array} directives Array of collected directives to execute their compile function.
13905
14090
  * this needs to be pre-sorted by priority order.
@@ -13907,11 +14092,11 @@ function $CompileProvider($provide) {
13907
14092
  * @param {Object} templateAttrs The shared attribute function
13908
14093
  * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
13909
14094
  * scope argument is auto-generated to the new child of the transcluded parent scope.
13910
- * @param {DOMElement} $rootElement If we are working on the root of the compile tree then this
13911
- * argument has the root jqLite array so that we can replace widgets on it.
14095
+ * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
14096
+ * argument has the root jqLite array so that we can replace nodes on it.
13912
14097
  * @returns linkFn
13913
14098
  */
13914
- function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) {
14099
+ function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
13915
14100
  var terminalPriority = -Number.MAX_VALUE,
13916
14101
  preLinkFns = [],
13917
14102
  postLinkFns = [],
@@ -13965,7 +14150,7 @@ function $CompileProvider($provide) {
13965
14150
  $compileNode = templateAttrs.$$element =
13966
14151
  jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
13967
14152
  compileNode = $compileNode[0];
13968
- replaceWith($rootElement, jqLite($template[0]), compileNode);
14153
+ replaceWith(jqCollection, jqLite($template[0]), compileNode);
13969
14154
  childTranscludeFn = compile($template, transcludeFn, terminalPriority);
13970
14155
  } else {
13971
14156
  $template = jqLite(JQLiteClone(compileNode)).contents();
@@ -13994,7 +14179,7 @@ function $CompileProvider($provide) {
13994
14179
  throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
13995
14180
  }
13996
14181
 
13997
- replaceWith($rootElement, $compileNode, compileNode);
14182
+ replaceWith(jqCollection, $compileNode, compileNode);
13998
14183
 
13999
14184
  var newTemplateAttrs = {$attr: {}};
14000
14185
 
@@ -14022,7 +14207,7 @@ function $CompileProvider($provide) {
14022
14207
  assertNoDuplicate('template', templateDirective, directive, $compileNode);
14023
14208
  templateDirective = directive;
14024
14209
  nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
14025
- nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace,
14210
+ nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
14026
14211
  childTranscludeFn);
14027
14212
  ii = directives.length;
14028
14213
  } else if (directive.compile) {
@@ -14163,7 +14348,7 @@ function $CompileProvider($provide) {
14163
14348
  parentGet = $parse(attrs[attrName]);
14164
14349
  scope[scopeName] = function(locals) {
14165
14350
  return parentGet(parentScope, locals);
14166
- }
14351
+ };
14167
14352
  break;
14168
14353
  }
14169
14354
 
@@ -14562,7 +14747,8 @@ function directiveLinkingFn(
14562
14747
  * {@link ng.$controllerProvider#register register} method.
14563
14748
  */
14564
14749
  function $ControllerProvider() {
14565
- var controllers = {};
14750
+ var controllers = {},
14751
+ CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
14566
14752
 
14567
14753
 
14568
14754
  /**
@@ -14607,17 +14793,32 @@ function $ControllerProvider() {
14607
14793
  * a service, so that one can override this service with {@link https://gist.github.com/1649788
14608
14794
  * BC version}.
14609
14795
  */
14610
- return function(constructor, locals) {
14611
- if(isString(constructor)) {
14612
- var name = constructor;
14613
- constructor = controllers.hasOwnProperty(name)
14614
- ? controllers[name]
14615
- : getter(locals.$scope, name, true) || getter($window, name, true);
14616
-
14617
- assertArgFn(constructor, name, true);
14796
+ return function(expression, locals) {
14797
+ var instance, match, constructor, identifier;
14798
+
14799
+ if(isString(expression)) {
14800
+ match = expression.match(CNTRL_REG),
14801
+ constructor = match[1],
14802
+ identifier = match[3];
14803
+ expression = controllers.hasOwnProperty(constructor)
14804
+ ? controllers[constructor]
14805
+ : getter(locals.$scope, constructor, true) || getter($window, constructor, true);
14806
+
14807
+ assertArgFn(expression, constructor, true);
14808
+ }
14809
+
14810
+ instance = $injector.instantiate(expression, locals);
14811
+
14812
+ if (identifier) {
14813
+ if (typeof locals.$scope !== 'object') {
14814
+ throw new Error('Can not export controller as "' + identifier + '". ' +
14815
+ 'No scope object provided!');
14816
+ }
14817
+
14818
+ locals.$scope[identifier] = instance;
14618
14819
  }
14619
14820
 
14620
- return $injector.instantiate(constructor, locals);
14821
+ return instance;
14621
14822
  };
14622
14823
  }];
14623
14824
  }
@@ -14656,7 +14857,7 @@ function $DocumentProvider(){
14656
14857
  *
14657
14858
  */
14658
14859
  function $ExceptionHandlerProvider() {
14659
- this.$get = ['$log', function($log){
14860
+ this.$get = ['$log', function($log) {
14660
14861
  return function(exception, cause) {
14661
14862
  $log.error.apply($log, arguments);
14662
14863
  };
@@ -14850,9 +15051,8 @@ function $InterpolateProvider() {
14850
15051
  }];
14851
15052
  }
14852
15053
 
14853
- var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
14854
- PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,
14855
- HASH_MATCH = PATH_MATCH,
15054
+ var SERVER_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
15055
+ PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
14856
15056
  DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
14857
15057
 
14858
15058
 
@@ -14873,30 +15073,23 @@ function encodePath(path) {
14873
15073
  return segments.join('/');
14874
15074
  }
14875
15075
 
14876
- function stripHash(url) {
14877
- return url.split('#')[0];
14878
- }
14879
-
14880
-
14881
15076
  function matchUrl(url, obj) {
14882
- var match = URL_MATCH.exec(url);
15077
+ var match = SERVER_MATCH.exec(url);
14883
15078
 
14884
- match = {
14885
- protocol: match[1],
14886
- host: match[3],
14887
- port: int(match[5]) || DEFAULT_PORTS[match[1]] || null,
14888
- path: match[6] || '/',
14889
- search: match[8],
14890
- hash: match[10]
14891
- };
15079
+ obj.$$protocol = match[1];
15080
+ obj.$$host = match[3];
15081
+ obj.$$port = int(match[5]) || DEFAULT_PORTS[match[1]] || null;
15082
+ }
14892
15083
 
14893
- if (obj) {
14894
- obj.$$protocol = match.protocol;
14895
- obj.$$host = match.host;
14896
- obj.$$port = match.port;
14897
- }
15084
+ function matchAppUrl(url, obj) {
15085
+ var match = PATH_MATCH.exec(url);
15086
+
15087
+ obj.$$path = decodeURIComponent(match[1]);
15088
+ obj.$$search = parseKeyValue(match[3]);
15089
+ obj.$$hash = decodeURIComponent(match[5] || '');
14898
15090
 
14899
- return match;
15091
+ // make sure path starts with '/';
15092
+ if (obj.$$path && obj.$$path.charAt(0) != '/') obj.$$path = '/' + obj.$$path;
14900
15093
  }
14901
15094
 
14902
15095
 
@@ -14904,77 +15097,62 @@ function composeProtocolHostPort(protocol, host, port) {
14904
15097
  return protocol + '://' + host + (port == DEFAULT_PORTS[protocol] ? '' : ':' + port);
14905
15098
  }
14906
15099
 
14907
-
14908
- function pathPrefixFromBase(basePath) {
14909
- return basePath.substr(0, basePath.lastIndexOf('/'));
15100
+ /**
15101
+ *
15102
+ * @param {string} begin
15103
+ * @param {string} whole
15104
+ * @param {string} otherwise
15105
+ * @returns {string} returns text from whole after begin or otherwise if it does not begin with expected string.
15106
+ */
15107
+ function beginsWith(begin, whole, otherwise) {
15108
+ return whole.indexOf(begin) == 0 ? whole.substr(begin.length) : otherwise;
14910
15109
  }
14911
15110
 
14912
15111
 
14913
- function convertToHtml5Url(url, basePath, hashPrefix) {
14914
- var match = matchUrl(url);
14915
-
14916
- // already html5 url
14917
- if (decodeURIComponent(match.path) != basePath || isUndefined(match.hash) ||
14918
- match.hash.indexOf(hashPrefix) !== 0) {
14919
- return url;
14920
- // convert hashbang url -> html5 url
14921
- } else {
14922
- return composeProtocolHostPort(match.protocol, match.host, match.port) +
14923
- pathPrefixFromBase(basePath) + match.hash.substr(hashPrefix.length);
14924
- }
15112
+ function stripHash(url) {
15113
+ var index = url.indexOf('#');
15114
+ return index == -1 ? url : url.substr(0, index);
14925
15115
  }
14926
15116
 
14927
15117
 
14928
- function convertToHashbangUrl(url, basePath, hashPrefix) {
14929
- var match = matchUrl(url);
14930
-
14931
- // already hashbang url
14932
- if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
14933
- match.hash.indexOf(hashPrefix) === 0) {
14934
- return url;
14935
- // convert html5 url -> hashbang url
14936
- } else {
14937
- var search = match.search && '?' + match.search || '',
14938
- hash = match.hash && '#' + match.hash || '',
14939
- pathPrefix = pathPrefixFromBase(basePath),
14940
- path = match.path.substr(pathPrefix.length);
14941
-
14942
- if (match.path.indexOf(pathPrefix) !== 0) {
14943
- throw Error('Invalid url "' + url + '", missing path prefix "' + pathPrefix + '" !');
14944
- }
15118
+ function stripFile(url) {
15119
+ return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
15120
+ }
14945
15121
 
14946
- return composeProtocolHostPort(match.protocol, match.host, match.port) + basePath +
14947
- '#' + hashPrefix + path + search + hash;
14948
- }
15122
+ /* return the server only */
15123
+ function serverBase(url) {
15124
+ return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
14949
15125
  }
14950
15126
 
14951
15127
 
14952
15128
  /**
14953
- * LocationUrl represents an url
15129
+ * LocationHtml5Url represents an url
14954
15130
  * This object is exposed as $location service when HTML5 mode is enabled and supported
14955
15131
  *
14956
15132
  * @constructor
14957
- * @param {string} url HTML5 url
14958
- * @param {string} pathPrefix
15133
+ * @param {string} appBase application base URL
15134
+ * @param {string} basePrefix url path prefix
14959
15135
  */
14960
- function LocationUrl(url, pathPrefix, appBaseUrl) {
14961
- pathPrefix = pathPrefix || '';
14962
-
15136
+ function LocationHtml5Url(appBase, basePrefix) {
15137
+ basePrefix = basePrefix || '';
15138
+ var appBaseNoFile = stripFile(appBase);
14963
15139
  /**
14964
15140
  * Parse given html5 (regular) url string into properties
14965
15141
  * @param {string} newAbsoluteUrl HTML5 url
14966
15142
  * @private
14967
15143
  */
14968
- this.$$parse = function(newAbsoluteUrl) {
14969
- var match = matchUrl(newAbsoluteUrl, this);
14970
-
14971
- if (match.path.indexOf(pathPrefix) !== 0) {
14972
- throw Error('Invalid url "' + newAbsoluteUrl + '", missing path prefix "' + pathPrefix + '" !');
15144
+ this.$$parse = function(url) {
15145
+ var parsed = {}
15146
+ matchUrl(url, parsed);
15147
+ var pathUrl = beginsWith(appBaseNoFile, url);
15148
+ if (!isString(pathUrl)) {
15149
+ throw Error('Invalid url "' + url + '", missing path prefix "' + appBaseNoFile + '".');
15150
+ }
15151
+ matchAppUrl(pathUrl, parsed);
15152
+ extend(this, parsed);
15153
+ if (!this.$$path) {
15154
+ this.$$path = '/';
14973
15155
  }
14974
-
14975
- this.$$path = decodeURIComponent(match.path.substr(pathPrefix.length));
14976
- this.$$search = parseKeyValue(match.search);
14977
- this.$$hash = match.hash && decodeURIComponent(match.hash) || '';
14978
15156
 
14979
15157
  this.$$compose();
14980
15158
  };
@@ -14988,19 +15166,25 @@ function LocationUrl(url, pathPrefix, appBaseUrl) {
14988
15166
  hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
14989
15167
 
14990
15168
  this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
14991
- this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) +
14992
- pathPrefix + this.$$url;
15169
+ this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
14993
15170
  };
14994
15171
 
15172
+ this.$$rewrite = function(url) {
15173
+ var appUrl, prevAppUrl;
14995
15174
 
14996
- this.$$rewriteAppUrl = function(absoluteLinkUrl) {
14997
- if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
14998
- return absoluteLinkUrl;
15175
+ if ( (appUrl = beginsWith(appBase, url)) !== undefined ) {
15176
+ prevAppUrl = appUrl;
15177
+ if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) {
15178
+ return appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
15179
+ } else {
15180
+ return appBase + prevAppUrl;
15181
+ }
15182
+ } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) {
15183
+ return appBaseNoFile + appUrl;
15184
+ } else if (appBaseNoFile == url + '/') {
15185
+ return appBaseNoFile;
14999
15186
  }
15000
15187
  }
15001
-
15002
-
15003
- this.$$parse(url);
15004
15188
  }
15005
15189
 
15006
15190
 
@@ -15009,11 +15193,11 @@ function LocationUrl(url, pathPrefix, appBaseUrl) {
15009
15193
  * This object is exposed as $location service when html5 history api is disabled or not supported
15010
15194
  *
15011
15195
  * @constructor
15012
- * @param {string} url Legacy url
15013
- * @param {string} hashPrefix Prefix for hash part (containing path and search)
15196
+ * @param {string} appBase application base URL
15197
+ * @param {string} hashPrefix hashbang prefix
15014
15198
  */
15015
- function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
15016
- var basePath;
15199
+ function LocationHashbangUrl(appBase, hashPrefix) {
15200
+ var appBaseNoFile = stripFile(appBase);
15017
15201
 
15018
15202
  /**
15019
15203
  * Parse given hashbang url into properties
@@ -15021,24 +15205,16 @@ function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
15021
15205
  * @private
15022
15206
  */
15023
15207
  this.$$parse = function(url) {
15024
- var match = matchUrl(url, this);
15025
-
15026
-
15027
- if (match.hash && match.hash.indexOf(hashPrefix) !== 0) {
15028
- throw Error('Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '" !');
15208
+ matchUrl(url, this);
15209
+ var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
15210
+ if (!isString(withoutBaseUrl)) {
15211
+ throw new Error('Invalid url "' + url + '", does not start with "' + appBase + '".');
15029
15212
  }
15030
-
15031
- basePath = match.path + (match.search ? '?' + match.search : '');
15032
- match = HASH_MATCH.exec((match.hash || '').substr(hashPrefix.length));
15033
- if (match[1]) {
15034
- this.$$path = (match[1].charAt(0) == '/' ? '' : '/') + decodeURIComponent(match[1]);
15035
- } else {
15036
- this.$$path = '';
15213
+ var withoutHashUrl = withoutBaseUrl.charAt(0) == '#' ? beginsWith(hashPrefix, withoutBaseUrl) : withoutBaseUrl;
15214
+ if (!isString(withoutHashUrl)) {
15215
+ throw new Error('Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '".');
15037
15216
  }
15038
-
15039
- this.$$search = parseKeyValue(match[3]);
15040
- this.$$hash = match[5] && decodeURIComponent(match[5]) || '';
15041
-
15217
+ matchAppUrl(withoutHashUrl, this);
15042
15218
  this.$$compose();
15043
15219
  };
15044
15220
 
@@ -15051,22 +15227,48 @@ function LocationHashbangUrl(url, hashPrefix, appBaseUrl) {
15051
15227
  hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
15052
15228
 
15053
15229
  this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
15054
- this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) +
15055
- basePath + (this.$$url ? '#' + hashPrefix + this.$$url : '');
15230
+ this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
15056
15231
  };
15057
15232
 
15058
- this.$$rewriteAppUrl = function(absoluteLinkUrl) {
15059
- if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
15060
- return absoluteLinkUrl;
15233
+ this.$$rewrite = function(url) {
15234
+ if(stripHash(appBase) == stripHash(url)) {
15235
+ return url;
15061
15236
  }
15062
15237
  }
15238
+ }
15239
+
15240
+
15241
+ /**
15242
+ * LocationHashbangUrl represents url
15243
+ * This object is exposed as $location service when html5 history api is enabled but the browser
15244
+ * does not support it.
15245
+ *
15246
+ * @constructor
15247
+ * @param {string} appBase application base URL
15248
+ * @param {string} hashPrefix hashbang prefix
15249
+ */
15250
+ function LocationHashbangInHtml5Url(appBase, hashPrefix) {
15251
+ LocationHashbangUrl.apply(this, arguments);
15063
15252
 
15253
+ var appBaseNoFile = stripFile(appBase);
15064
15254
 
15065
- this.$$parse(url);
15255
+ this.$$rewrite = function(url) {
15256
+ var appUrl;
15257
+
15258
+ if ( appBase == stripHash(url) ) {
15259
+ return url;
15260
+ } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) {
15261
+ return appBase + hashPrefix + appUrl;
15262
+ } else if ( appBaseNoFile === url + '/') {
15263
+ return appBaseNoFile;
15264
+ }
15265
+ }
15066
15266
  }
15067
15267
 
15068
15268
 
15069
- LocationUrl.prototype = {
15269
+ LocationHashbangInHtml5Url.prototype =
15270
+ LocationHashbangUrl.prototype =
15271
+ LocationHtml5Url.prototype = {
15070
15272
 
15071
15273
  /**
15072
15274
  * Has any change been replacing ?
@@ -15248,21 +15450,6 @@ LocationUrl.prototype = {
15248
15450
  }
15249
15451
  };
15250
15452
 
15251
- LocationHashbangUrl.prototype = inherit(LocationUrl.prototype);
15252
-
15253
- function LocationHashbangInHtml5Url(url, hashPrefix, appBaseUrl, baseExtra) {
15254
- LocationHashbangUrl.apply(this, arguments);
15255
-
15256
-
15257
- this.$$rewriteAppUrl = function(absoluteLinkUrl) {
15258
- if (absoluteLinkUrl.indexOf(appBaseUrl) == 0) {
15259
- return appBaseUrl + baseExtra + '#' + hashPrefix + absoluteLinkUrl.substr(appBaseUrl.length);
15260
- }
15261
- }
15262
- }
15263
-
15264
- LocationHashbangInHtml5Url.prototype = inherit(LocationHashbangUrl.prototype);
15265
-
15266
15453
  function locationGetter(property) {
15267
15454
  return function() {
15268
15455
  return this[property];
@@ -15359,37 +15546,20 @@ function $LocationProvider(){
15359
15546
  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
15360
15547
  function( $rootScope, $browser, $sniffer, $rootElement) {
15361
15548
  var $location,
15362
- basePath,
15363
- pathPrefix,
15364
- initUrl = $browser.url(),
15365
- initUrlParts = matchUrl(initUrl),
15366
- appBaseUrl;
15549
+ LocationMode,
15550
+ baseHref = $browser.baseHref(),
15551
+ initialUrl = $browser.url(),
15552
+ appBase;
15367
15553
 
15368
15554
  if (html5Mode) {
15369
- basePath = $browser.baseHref() || '/';
15370
- pathPrefix = pathPrefixFromBase(basePath);
15371
- appBaseUrl =
15372
- composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
15373
- pathPrefix + '/';
15374
-
15375
- if ($sniffer.history) {
15376
- $location = new LocationUrl(
15377
- convertToHtml5Url(initUrl, basePath, hashPrefix),
15378
- pathPrefix, appBaseUrl);
15379
- } else {
15380
- $location = new LocationHashbangInHtml5Url(
15381
- convertToHashbangUrl(initUrl, basePath, hashPrefix),
15382
- hashPrefix, appBaseUrl, basePath.substr(pathPrefix.length + 1));
15383
- }
15555
+ appBase = baseHref ? serverBase(initialUrl) + baseHref : initialUrl;
15556
+ LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
15384
15557
  } else {
15385
- appBaseUrl =
15386
- composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) +
15387
- (initUrlParts.path || '') +
15388
- (initUrlParts.search ? ('?' + initUrlParts.search) : '') +
15389
- '#' + hashPrefix + '/';
15390
-
15391
- $location = new LocationHashbangUrl(initUrl, hashPrefix, appBaseUrl);
15558
+ appBase = stripHash(initialUrl);
15559
+ LocationMode = LocationHashbangUrl;
15392
15560
  }
15561
+ $location = new LocationMode(appBase, '#' + hashPrefix);
15562
+ $location.$$parse($location.$$rewrite(initialUrl));
15393
15563
 
15394
15564
  $rootElement.bind('click', function(event) {
15395
15565
  // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
@@ -15405,28 +15575,34 @@ function $LocationProvider(){
15405
15575
  if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
15406
15576
  }
15407
15577
 
15408
- var absHref = elm.prop('href'),
15409
- rewrittenUrl = $location.$$rewriteAppUrl(absHref);
15578
+ var absHref = elm.prop('href');
15579
+ var rewrittenUrl = $location.$$rewrite(absHref);
15410
15580
 
15411
- if (absHref && !elm.attr('target') && rewrittenUrl) {
15412
- // update location manually
15413
- $location.$$parse(rewrittenUrl);
15414
- $rootScope.$apply();
15581
+ if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
15415
15582
  event.preventDefault();
15416
- // hack to work around FF6 bug 684208 when scenario runner clicks on links
15417
- window.angular['ff-684208-preventDefault'] = true;
15583
+ if (rewrittenUrl != $browser.url()) {
15584
+ // update location manually
15585
+ $location.$$parse(rewrittenUrl);
15586
+ $rootScope.$apply();
15587
+ // hack to work around FF6 bug 684208 when scenario runner clicks on links
15588
+ window.angular['ff-684208-preventDefault'] = true;
15589
+ }
15418
15590
  }
15419
15591
  });
15420
15592
 
15421
15593
 
15422
15594
  // rewrite hashbang url <> html5 url
15423
- if ($location.absUrl() != initUrl) {
15595
+ if ($location.absUrl() != initialUrl) {
15424
15596
  $browser.url($location.absUrl(), true);
15425
15597
  }
15426
15598
 
15427
15599
  // update $location when $browser url changes
15428
15600
  $browser.onUrlChange(function(newUrl) {
15429
15601
  if ($location.absUrl() != newUrl) {
15602
+ if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) {
15603
+ $browser.url($location.absUrl());
15604
+ return;
15605
+ }
15430
15606
  $rootScope.$evalAsync(function() {
15431
15607
  var oldUrl = $location.absUrl();
15432
15608
 
@@ -15683,7 +15859,7 @@ function lex(text, csp){
15683
15859
  (token=tokens[tokens.length-1])) {
15684
15860
  token.json = token.text.indexOf('.') == -1;
15685
15861
  }
15686
- } else if (is('(){}[].,;:')) {
15862
+ } else if (is('(){}[].,;:?')) {
15687
15863
  tokens.push({
15688
15864
  index:index,
15689
15865
  text:ch,
@@ -15787,10 +15963,10 @@ function lex(text, csp){
15787
15963
  function readIdent() {
15788
15964
  var ident = "",
15789
15965
  start = index,
15790
- lastDot, peekIndex, methodName;
15966
+ lastDot, peekIndex, methodName, ch;
15791
15967
 
15792
15968
  while (index < text.length) {
15793
- var ch = text.charAt(index);
15969
+ ch = text.charAt(index);
15794
15970
  if (ch == '.' || isIdent(ch) || isNumber(ch)) {
15795
15971
  if (ch == '.') lastDot = index;
15796
15972
  ident += ch;
@@ -15804,7 +15980,7 @@ function lex(text, csp){
15804
15980
  if (lastDot) {
15805
15981
  peekIndex = index;
15806
15982
  while(peekIndex < text.length) {
15807
- var ch = text.charAt(peekIndex);
15983
+ ch = text.charAt(peekIndex);
15808
15984
  if (ch == '(') {
15809
15985
  methodName = ident.substr(lastDot - start + 1);
15810
15986
  ident = ident.substr(0, lastDot - start);
@@ -15984,6 +16160,14 @@ function parser(text, json, $filter, csp){
15984
16160
  });
15985
16161
  }
15986
16162
 
16163
+ function ternaryFn(left, middle, right){
16164
+ return extend(function(self, locals){
16165
+ return left(self, locals) ? middle(self, locals) : right(self, locals);
16166
+ }, {
16167
+ constant: left.constant && middle.constant && right.constant
16168
+ });
16169
+ }
16170
+
15987
16171
  function binaryFn(left, fn, right) {
15988
16172
  return extend(function(self, locals) {
15989
16173
  return fn(self, locals, left, right);
@@ -16054,7 +16238,7 @@ function parser(text, json, $filter, csp){
16054
16238
  }
16055
16239
 
16056
16240
  function _assignment() {
16057
- var left = logicalOR();
16241
+ var left = ternary();
16058
16242
  var right;
16059
16243
  var token;
16060
16244
  if ((token = expect('='))) {
@@ -16062,15 +16246,33 @@ function parser(text, json, $filter, csp){
16062
16246
  throwError("implies assignment but [" +
16063
16247
  text.substring(0, token.index) + "] can not be assigned to", token);
16064
16248
  }
16065
- right = logicalOR();
16066
- return function(self, locals){
16067
- return left.assign(self, right(self, locals), locals);
16249
+ right = ternary();
16250
+ return function(scope, locals){
16251
+ return left.assign(scope, right(scope, locals), locals);
16068
16252
  };
16069
16253
  } else {
16070
16254
  return left;
16071
16255
  }
16072
16256
  }
16073
16257
 
16258
+ function ternary() {
16259
+ var left = logicalOR();
16260
+ var middle;
16261
+ var token;
16262
+ if((token = expect('?'))){
16263
+ middle = ternary();
16264
+ if((token = expect(':'))){
16265
+ return ternaryFn(left, middle, ternary());
16266
+ }
16267
+ else {
16268
+ throwError('expected :', token);
16269
+ }
16270
+ }
16271
+ else {
16272
+ return left;
16273
+ }
16274
+ }
16275
+
16074
16276
  function logicalOR() {
16075
16277
  var left = logicalAND();
16076
16278
  var token;
@@ -16184,12 +16386,12 @@ function parser(text, json, $filter, csp){
16184
16386
  var field = expect().text;
16185
16387
  var getter = getterFn(field, csp);
16186
16388
  return extend(
16187
- function(self, locals) {
16188
- return getter(object(self, locals), locals);
16389
+ function(scope, locals, self) {
16390
+ return getter(self || object(scope, locals), locals);
16189
16391
  },
16190
16392
  {
16191
- assign:function(self, value, locals) {
16192
- return setter(object(self, locals), field, value);
16393
+ assign:function(scope, value, locals) {
16394
+ return setter(object(scope, locals), field, value);
16193
16395
  }
16194
16396
  }
16195
16397
  );
@@ -16230,14 +16432,14 @@ function parser(text, json, $filter, csp){
16230
16432
  } while (expect(','));
16231
16433
  }
16232
16434
  consume(')');
16233
- return function(self, locals){
16435
+ return function(scope, locals){
16234
16436
  var args = [],
16235
- context = contextGetter ? contextGetter(self, locals) : self;
16437
+ context = contextGetter ? contextGetter(scope, locals) : scope;
16236
16438
 
16237
16439
  for ( var i = 0; i < argsFn.length; i++) {
16238
- args.push(argsFn[i](self, locals));
16440
+ args.push(argsFn[i](scope, locals));
16239
16441
  }
16240
- var fnPtr = fn(self, locals) || noop;
16442
+ var fnPtr = fn(scope, locals, context) || noop;
16241
16443
  // IE stupidity!
16242
16444
  return fnPtr.apply
16243
16445
  ? fnPtr.apply(context, args)
@@ -16291,8 +16493,7 @@ function parser(text, json, $filter, csp){
16291
16493
  var object = {};
16292
16494
  for ( var i = 0; i < keyValues.length; i++) {
16293
16495
  var keyValue = keyValues[i];
16294
- var value = keyValue.value(self, locals);
16295
- object[keyValue.key] = value;
16496
+ object[keyValue.key] = keyValue.value(self, locals);
16296
16497
  }
16297
16498
  return object;
16298
16499
  }, {
@@ -16417,7 +16618,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4) {
16417
16618
  }
16418
16619
  return pathVal;
16419
16620
  };
16420
- };
16621
+ }
16421
16622
 
16422
16623
  function getterFn(path, csp) {
16423
16624
  if (getterFnCache.hasOwnProperty(path)) {
@@ -16432,7 +16633,7 @@ function getterFn(path, csp) {
16432
16633
  fn = (pathKeysLength < 6)
16433
16634
  ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4])
16434
16635
  : function(scope, locals) {
16435
- var i = 0, val
16636
+ var i = 0, val;
16436
16637
  do {
16437
16638
  val = cspSafeGetterFn(
16438
16639
  pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++]
@@ -16620,6 +16821,11 @@ function $ParseProvider() {
16620
16821
  * This method *returns a new promise* which is resolved or rejected via the return value of the
16621
16822
  * `successCallback` or `errorCallback`.
16622
16823
  *
16824
+ * - `always(callback)` – allows you to observe either the fulfillment or rejection of a promise,
16825
+ * but to do so without modifying the final value. This is useful to release resources or do some
16826
+ * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
16827
+ * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
16828
+ * more information.
16623
16829
  *
16624
16830
  * # Chaining promises
16625
16831
  *
@@ -16650,7 +16856,7 @@ function $ParseProvider() {
16650
16856
  * models and avoiding unnecessary browser repaints, which would result in flickering UI.
16651
16857
  * - $q promises are recognized by the templating engine in angular, which means that in templates
16652
16858
  * you can treat promises attached to a scope as if they were the resulting values.
16653
- * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains
16859
+ * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
16654
16860
  * all the important functionality needed for common async tasks.
16655
16861
  *
16656
16862
  * # Testing
@@ -16765,6 +16971,42 @@ function qFactory(nextTick, exceptionHandler) {
16765
16971
  }
16766
16972
 
16767
16973
  return result.promise;
16974
+ },
16975
+ always: function(callback) {
16976
+
16977
+ function makePromise(value, resolved) {
16978
+ var result = defer();
16979
+ if (resolved) {
16980
+ result.resolve(value);
16981
+ } else {
16982
+ result.reject(value);
16983
+ }
16984
+ return result.promise;
16985
+ }
16986
+
16987
+ function handleCallback(value, isResolved) {
16988
+ var callbackOutput = null;
16989
+ try {
16990
+ callbackOutput = (callback ||defaultCallback)();
16991
+ } catch(e) {
16992
+ return makePromise(e, false);
16993
+ }
16994
+ if (callbackOutput && callbackOutput.then) {
16995
+ return callbackOutput.then(function() {
16996
+ return makePromise(value, isResolved);
16997
+ }, function(error) {
16998
+ return makePromise(error, false);
16999
+ });
17000
+ } else {
17001
+ return makePromise(value, isResolved);
17002
+ }
17003
+ }
17004
+
17005
+ return this.then(function(value) {
17006
+ return handleCallback(value, true);
17007
+ }, function(error) {
17008
+ return handleCallback(error, false);
17009
+ });
16768
17010
  }
16769
17011
  }
16770
17012
  };
@@ -16845,10 +17087,7 @@ function qFactory(nextTick, exceptionHandler) {
16845
17087
  * the promise comes from a source that can't be trusted.
16846
17088
  *
16847
17089
  * @param {*} value Value or a promise
16848
- * @returns {Promise} Returns a single promise that will be resolved with an array of values,
16849
- * each value corresponding to the promise at the same index in the `promises` array. If any of
16850
- * the promises is resolved with a rejection, this resulting promise will be resolved with the
16851
- * same rejection.
17090
+ * @returns {Promise} Returns a promise of the passed value or promise
16852
17091
  */
16853
17092
  var when = function(value, callback, errback) {
16854
17093
  var result = defer(),
@@ -16987,6 +17226,8 @@ function $RouteProvider(){
16987
17226
  * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly
16988
17227
  * created scope or the name of a {@link angular.Module#controller registered controller}
16989
17228
  * if passed as a string.
17229
+ * - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
17230
+ * published to scope under the `controllerAs` name.
16990
17231
  * - `template` – `{string=|function()=}` – html template as a string or function that returns
16991
17232
  * an html template as a string which should be used by {@link ng.directive:ngView ngView} or
16992
17233
  * {@link ng.directive:ngInclude ngInclude} directives.
@@ -17481,22 +17722,22 @@ function $RouteParamsProvider() {
17481
17722
  /**
17482
17723
  * DESIGN NOTES
17483
17724
  *
17484
- * The design decisions behind the scope ware heavily favored for speed and memory consumption.
17725
+ * The design decisions behind the scope are heavily favored for speed and memory consumption.
17485
17726
  *
17486
17727
  * The typical use of scope is to watch the expressions, which most of the time return the same
17487
17728
  * value as last time so we optimize the operation.
17488
17729
  *
17489
- * Closures construction is expensive from speed as well as memory:
17490
- * - no closures, instead ups prototypical inheritance for API
17730
+ * Closures construction is expensive in terms of speed as well as memory:
17731
+ * - No closures, instead use prototypical inheritance for API
17491
17732
  * - Internal state needs to be stored on scope directly, which means that private state is
17492
17733
  * exposed as $$____ properties
17493
17734
  *
17494
17735
  * Loop operations are optimized by using while(count--) { ... }
17495
17736
  * - this means that in order to keep the same order of execution as addition we have to add
17496
- * items to the array at the begging (shift) instead of at the end (push)
17737
+ * items to the array at the beginning (shift) instead of at the end (push)
17497
17738
  *
17498
17739
  * Child scopes are created and removed often
17499
- * - Using array would be slow since inserts in meddle are expensive so we use linked list
17740
+ * - Using an array would be slow since inserts in middle are expensive so we use linked list
17500
17741
  *
17501
17742
  * There are few watches then a lot of observers. This is why you don't want the observer to be
17502
17743
  * implemented in the same way as watch. Watch requires return of initialization function which
@@ -17518,7 +17759,7 @@ function $RouteParamsProvider() {
17518
17759
  * @methodOf ng.$rootScopeProvider
17519
17760
  * @description
17520
17761
  *
17521
- * Sets the number of digest iteration the scope should attempt to execute before giving up and
17762
+ * Sets the number of digest iterations the scope should attempt to execute before giving up and
17522
17763
  * assuming that the model is unstable.
17523
17764
  *
17524
17765
  * The current default is 10 iterations.
@@ -17854,7 +18095,7 @@ function $RootScopeProvider(){
17854
18095
  oldValue = newValue;
17855
18096
  changeDetected++;
17856
18097
  }
17857
- } else if (isArray(newValue)) {
18098
+ } else if (isArrayLike(newValue)) {
17858
18099
  if (oldValue !== internalArray) {
17859
18100
  // we are transitioning from something which was not an array into array.
17860
18101
  oldValue = internalArray;
@@ -17928,7 +18169,7 @@ function $RootScopeProvider(){
17928
18169
  * @function
17929
18170
  *
17930
18171
  * @description
17931
- * Process all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
18172
+ * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
17932
18173
  * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the
17933
18174
  * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are
17934
18175
  * firing. This means that it is possible to get into an infinite loop. This function will throw
@@ -18271,7 +18512,7 @@ function $RootScopeProvider(){
18271
18512
  * Afterwards, the event traverses upwards toward the root scope and calls all registered
18272
18513
  * listeners along the way. The event will stop propagating if one of the listeners cancels it.
18273
18514
  *
18274
- * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
18515
+ * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
18275
18516
  * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
18276
18517
  *
18277
18518
  * @param {string} name Event name to emit.
@@ -18340,7 +18581,7 @@ function $RootScopeProvider(){
18340
18581
  * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
18341
18582
  * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
18342
18583
  *
18343
- * @param {string} name Event name to emit.
18584
+ * @param {string} name Event name to broadcast.
18344
18585
  * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
18345
18586
  * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
18346
18587
  */
@@ -18434,7 +18675,8 @@ function $RootScopeProvider(){
18434
18675
  *
18435
18676
  * @property {boolean} history Does the browser support html5 history api ?
18436
18677
  * @property {boolean} hashchange Does the browser support hashchange event ?
18437
- * @property {boolean} supportsTransitions Does the browser support CSS transition events ?
18678
+ * @property {boolean} transitions Does the browser support CSS transition events ?
18679
+ * @property {boolean} animations Does the browser support CSS animation events ?
18438
18680
  *
18439
18681
  * @description
18440
18682
  * This is very simple implementation of testing browser's features.
@@ -18448,6 +18690,7 @@ function $SnifferProvider() {
18448
18690
  vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
18449
18691
  bodyStyle = document.body && document.body.style,
18450
18692
  transitions = false,
18693
+ animations = false,
18451
18694
  match;
18452
18695
 
18453
18696
  if (bodyStyle) {
@@ -18458,7 +18701,8 @@ function $SnifferProvider() {
18458
18701
  break;
18459
18702
  }
18460
18703
  }
18461
- transitions = !!(vendorPrefix + 'Transition' in bodyStyle);
18704
+ transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
18705
+ animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
18462
18706
  }
18463
18707
 
18464
18708
 
@@ -18486,7 +18730,8 @@ function $SnifferProvider() {
18486
18730
  },
18487
18731
  csp: document.securityPolicy ? document.securityPolicy.isActive : false,
18488
18732
  vendorPrefix: vendorPrefix,
18489
- supportsTransitions : transitions
18733
+ transitions : transitions,
18734
+ animations : animations
18490
18735
  };
18491
18736
  }];
18492
18737
  }
@@ -18507,10 +18752,23 @@ function $SnifferProvider() {
18507
18752
  * @example
18508
18753
  <doc:example>
18509
18754
  <doc:source>
18510
- <input ng-init="$window = $service('$window'); greeting='Hello World!'" type="text" ng-model="greeting" />
18511
- <button ng-click="$window.alert(greeting)">ALERT</button>
18755
+ <script>
18756
+ function Ctrl($scope, $window) {
18757
+ $scope.$window = $window;
18758
+ $scope.greeting = 'Hello, World!';
18759
+ }
18760
+ </script>
18761
+ <div ng-controller="Ctrl">
18762
+ <input type="text" ng-model="greeting" />
18763
+ <button ng-click="$window.alert(greeting)">ALERT</button>
18764
+ </div>
18512
18765
  </doc:source>
18513
18766
  <doc:scenario>
18767
+ it('should display the greeting in the input box', function() {
18768
+ input('greeting').enter('Hello, E2E Tests');
18769
+ // If we click the button it will block the test runner
18770
+ // element(':button').click();
18771
+ });
18514
18772
  </doc:scenario>
18515
18773
  </doc:example>
18516
18774
  */
@@ -18570,7 +18828,7 @@ function isSameDomain(requestUrl, locationUrl) {
18570
18828
  relativeProtocol: match[2] === undefined || match[2] === ''
18571
18829
  };
18572
18830
 
18573
- match = URL_MATCH.exec(locationUrl);
18831
+ match = SERVER_MATCH.exec(locationUrl);
18574
18832
  var domain2 = {
18575
18833
  protocol: match[1],
18576
18834
  host: match[3],
@@ -18641,7 +18899,8 @@ function isSuccess(status) {
18641
18899
  function $HttpProvider() {
18642
18900
  var JSON_START = /^\s*(\[|\{[^\{])/,
18643
18901
  JSON_END = /[\}\]]\s*$/,
18644
- PROTECTION_PREFIX = /^\)\]\}',?\n/;
18902
+ PROTECTION_PREFIX = /^\)\]\}',?\n/,
18903
+ CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'};
18645
18904
 
18646
18905
  var defaults = this.defaults = {
18647
18906
  // transform incoming response data
@@ -18665,8 +18924,9 @@ function $HttpProvider() {
18665
18924
  common: {
18666
18925
  'Accept': 'application/json, text/plain, */*'
18667
18926
  },
18668
- post: {'Content-Type': 'application/json;charset=utf-8'},
18669
- put: {'Content-Type': 'application/json;charset=utf-8'}
18927
+ post: CONTENT_TYPE_APPLICATION_JSON,
18928
+ put: CONTENT_TYPE_APPLICATION_JSON,
18929
+ patch: CONTENT_TYPE_APPLICATION_JSON
18670
18930
  },
18671
18931
 
18672
18932
  xsrfCookieName: 'XSRF-TOKEN',
@@ -18734,7 +18994,7 @@ function $HttpProvider() {
18734
18994
  *
18735
18995
  * @description
18736
18996
  * The `$http` service is a core Angular service that facilitates communication with the remote
18737
- * HTTP servers via browser's {@link https://developer.mozilla.org/en/xmlhttprequest
18997
+ * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
18738
18998
  * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
18739
18999
  *
18740
19000
  * For unit testing applications that use `$http` service, see
@@ -18744,13 +19004,13 @@ function $HttpProvider() {
18744
19004
  * $resource} service.
18745
19005
  *
18746
19006
  * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
18747
- * the $q service. While for simple usage patters this doesn't matter much, for advanced usage,
18748
- * it is important to familiarize yourself with these apis and guarantees they provide.
19007
+ * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
19008
+ * it is important to familiarize yourself with these APIs and the guarantees they provide.
18749
19009
  *
18750
19010
  *
18751
19011
  * # General usage
18752
19012
  * The `$http` service is a function which takes a single argument — a configuration object —
18753
- * that is used to generate an http request and returns a {@link ng.$q promise}
19013
+ * that is used to generate an HTTP request and returns a {@link ng.$q promise}
18754
19014
  * with two $http specific methods: `success` and `error`.
18755
19015
  *
18756
19016
  * <pre>
@@ -18765,21 +19025,21 @@ function $HttpProvider() {
18765
19025
  * });
18766
19026
  * </pre>
18767
19027
  *
18768
- * Since the returned value of calling the $http function is a Promise object, you can also use
19028
+ * Since the returned value of calling the $http function is a `promise`, you can also use
18769
19029
  * the `then` method to register callbacks, and these callbacks will receive a single argument –
18770
- * an object representing the response. See the api signature and type info below for more
19030
+ * an object representing the response. See the API signature and type info below for more
18771
19031
  * details.
18772
19032
  *
18773
- * A response status code that falls in the [200, 300) range is considered a success status and
19033
+ * A response status code between 200 and 299 is considered a success status and
18774
19034
  * will result in the success callback being called. Note that if the response is a redirect,
18775
19035
  * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
18776
19036
  * called for such responses.
18777
19037
  *
18778
19038
  * # Shortcut methods
18779
19039
  *
18780
- * Since all invocation of the $http service require definition of the http method and url and
18781
- * POST and PUT requests require response body/data to be provided as well, shortcut methods
18782
- * were created to simplify using the api:
19040
+ * Since all invocations of the $http service require passing in an HTTP method and URL, and
19041
+ * POST/PUT requests require request data to be provided as well, shortcut methods
19042
+ * were created:
18783
19043
  *
18784
19044
  * <pre>
18785
19045
  * $http.get('/someUrl').success(successCallback);
@@ -18798,24 +19058,24 @@ function $HttpProvider() {
18798
19058
  *
18799
19059
  * # Setting HTTP Headers
18800
19060
  *
18801
- * The $http service will automatically add certain http headers to all requests. These defaults
19061
+ * The $http service will automatically add certain HTTP headers to all requests. These defaults
18802
19062
  * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
18803
19063
  * object, which currently contains this default configuration:
18804
19064
  *
18805
19065
  * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
18806
19066
  * - `Accept: application/json, text/plain, * / *`
18807
- * - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests)
19067
+ * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
18808
19068
  * - `Content-Type: application/json`
18809
- * - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests)
19069
+ * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
18810
19070
  * - `Content-Type: application/json`
18811
19071
  *
18812
- * To add or overwrite these defaults, simply add or remove a property from this configuration
19072
+ * To add or overwrite these defaults, simply add or remove a property from these configuration
18813
19073
  * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
18814
- * with name equal to the lower-cased http method name, e.g.
19074
+ * with the lowercased HTTP method name as the key, e.g.
18815
19075
  * `$httpProvider.defaults.headers.get['My-Header']='value'`.
18816
19076
  *
18817
- * Additionally, the defaults can be set at runtime via the `$http.defaults` object in a similar
18818
- * fashion as described above.
19077
+ * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
19078
+ * fashion.
18819
19079
  *
18820
19080
  *
18821
19081
  * # Transforming Requests and Responses
@@ -18825,54 +19085,54 @@ function $HttpProvider() {
18825
19085
  *
18826
19086
  * Request transformations:
18827
19087
  *
18828
- * - if the `data` property of the request config object contains an object, serialize it into
19088
+ * - If the `data` property of the request configuration object contains an object, serialize it into
18829
19089
  * JSON format.
18830
19090
  *
18831
19091
  * Response transformations:
18832
19092
  *
18833
- * - if XSRF prefix is detected, strip it (see Security Considerations section below)
18834
- * - if json response is detected, deserialize it using a JSON parser
19093
+ * - If XSRF prefix is detected, strip it (see Security Considerations section below).
19094
+ * - If JSON response is detected, deserialize it using a JSON parser.
18835
19095
  *
18836
19096
  * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
18837
- * `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`. These properties are by default an
19097
+ * `$httpProvider.defaults.transformResponse` properties. These properties are by default an
18838
19098
  * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
18839
19099
  * transformation chain. You can also decide to completely override any default transformations by assigning your
18840
19100
  * transformation functions to these properties directly without the array wrapper.
18841
19101
  *
18842
19102
  * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
18843
- * `transformResponse` properties of the config object passed into `$http`.
19103
+ * `transformResponse` properties of the configuration object passed into `$http`.
18844
19104
  *
18845
19105
  *
18846
19106
  * # Caching
18847
19107
  *
18848
- * To enable caching set the configuration property `cache` to `true`. When the cache is
19108
+ * To enable caching, set the configuration property `cache` to `true`. When the cache is
18849
19109
  * enabled, `$http` stores the response from the server in local cache. Next time the
18850
19110
  * response is served from the cache without sending a request to the server.
18851
19111
  *
18852
19112
  * Note that even if the response is served from cache, delivery of the data is asynchronous in
18853
19113
  * the same way that real requests are.
18854
19114
  *
18855
- * If there are multiple GET requests for the same url that should be cached using the same
19115
+ * If there are multiple GET requests for the same URL that should be cached using the same
18856
19116
  * cache, but the cache is not populated yet, only one request to the server will be made and
18857
- * the remaining requests will be fulfilled using the response for the first request.
19117
+ * the remaining requests will be fulfilled using the response from the first request.
18858
19118
  *
18859
19119
  * A custom default cache built with $cacheFactory can be provided in $http.defaults.cache.
18860
19120
  * To skip it, set configuration property `cache` to `false`.
18861
- *
19121
+ *
18862
19122
  *
18863
19123
  * # Interceptors
18864
19124
  *
18865
19125
  * Before you start creating interceptors, be sure to understand the
18866
19126
  * {@link ng.$q $q and deferred/promise APIs}.
18867
19127
  *
18868
- * For purposes of global error handling, authentication or any kind of synchronous or
19128
+ * For purposes of global error handling, authentication, or any kind of synchronous or
18869
19129
  * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
18870
19130
  * able to intercept requests before they are handed to the server and
18871
- * responses before they are handed over to the application code that
19131
+ * responses before they are handed over to the application code that
18872
19132
  * initiated these requests. The interceptors leverage the {@link ng.$q
18873
- * promise APIs} to fulfil this need for both synchronous and asynchronous pre-processing.
19133
+ * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
18874
19134
  *
18875
- * The interceptors are service factories that are registered with the $httpProvider by
19135
+ * The interceptors are service factories that are registered with the `$httpProvider` by
18876
19136
  * adding them to the `$httpProvider.interceptors` array. The factory is called and
18877
19137
  * injected with dependencies (if specified) and returns the interceptor.
18878
19138
  *
@@ -18992,7 +19252,7 @@ function $HttpProvider() {
18992
19252
  * When designing web applications, consider security threats from:
18993
19253
  *
18994
19254
  * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
18995
- * JSON Vulnerability}
19255
+ * JSON vulnerability}
18996
19256
  * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
18997
19257
  *
18998
19258
  * Both server and the client must cooperate in order to eliminate these threats. Angular comes
@@ -19002,8 +19262,8 @@ function $HttpProvider() {
19002
19262
  * ## JSON Vulnerability Protection
19003
19263
  *
19004
19264
  * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
19005
- * JSON Vulnerability} allows third party web-site to turn your JSON resource URL into
19006
- * {@link http://en.wikipedia.org/wiki/JSON#JSONP JSONP} request under some conditions. To
19265
+ * JSON vulnerability} allows third party website to turn your JSON resource URL into
19266
+ * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
19007
19267
  * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
19008
19268
  * Angular will automatically strip the prefix before processing it as JSON.
19009
19269
  *
@@ -19024,7 +19284,7 @@ function $HttpProvider() {
19024
19284
  * ## Cross Site Request Forgery (XSRF) Protection
19025
19285
  *
19026
19286
  * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
19027
- * an unauthorized site can gain your user's private data. Angular provides following mechanism
19287
+ * an unauthorized site can gain your user's private data. Angular provides a mechanism
19028
19288
  * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
19029
19289
  * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
19030
19290
  * JavaScript that runs on your domain could read the cookie, your server can be assured that
@@ -19032,12 +19292,12 @@ function $HttpProvider() {
19032
19292
  * cross-domain requests.
19033
19293
  *
19034
19294
  * To take advantage of this, your server needs to set a token in a JavaScript readable session
19035
- * cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
19295
+ * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
19036
19296
  * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
19037
- * that only JavaScript running on your domain could have read the token. The token must be
19038
- * unique for each user and must be verifiable by the server (to prevent the JavaScript making
19297
+ * that only JavaScript running on your domain could have sent the request. The token must be
19298
+ * unique for each user and must be verifiable by the server (to prevent the JavaScript from making
19039
19299
  * up its own tokens). We recommend that the token is a digest of your site's authentication
19040
- * cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}.
19300
+ * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security.
19041
19301
  *
19042
19302
  * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
19043
19303
  * properties of either $httpProvider.defaults, or the per-request config object.
@@ -19064,7 +19324,8 @@ function $HttpProvider() {
19064
19324
  * GET request, otherwise if a cache instance built with
19065
19325
  * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
19066
19326
  * caching.
19067
- * - **timeout** – `{number}` – timeout in milliseconds.
19327
+ * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
19328
+ * that should abort the request when resolved.
19068
19329
  * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
19069
19330
  * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
19070
19331
  * requests with credentials} for more information.
@@ -19217,7 +19478,7 @@ function $HttpProvider() {
19217
19478
  var rejectFn = chain.shift();
19218
19479
 
19219
19480
  promise = promise.then(thenFn, rejectFn);
19220
- };
19481
+ }
19221
19482
 
19222
19483
  promise.success = function(fn) {
19223
19484
  promise.then(function(response) {
@@ -19254,7 +19515,7 @@ function $HttpProvider() {
19254
19515
  * @methodOf ng.$http
19255
19516
  *
19256
19517
  * @description
19257
- * Shortcut method to perform `GET` request
19518
+ * Shortcut method to perform `GET` request.
19258
19519
  *
19259
19520
  * @param {string} url Relative or absolute URL specifying the destination of the request
19260
19521
  * @param {Object=} config Optional configuration object
@@ -19267,7 +19528,7 @@ function $HttpProvider() {
19267
19528
  * @methodOf ng.$http
19268
19529
  *
19269
19530
  * @description
19270
- * Shortcut method to perform `DELETE` request
19531
+ * Shortcut method to perform `DELETE` request.
19271
19532
  *
19272
19533
  * @param {string} url Relative or absolute URL specifying the destination of the request
19273
19534
  * @param {Object=} config Optional configuration object
@@ -19280,7 +19541,7 @@ function $HttpProvider() {
19280
19541
  * @methodOf ng.$http
19281
19542
  *
19282
19543
  * @description
19283
- * Shortcut method to perform `HEAD` request
19544
+ * Shortcut method to perform `HEAD` request.
19284
19545
  *
19285
19546
  * @param {string} url Relative or absolute URL specifying the destination of the request
19286
19547
  * @param {Object=} config Optional configuration object
@@ -19293,7 +19554,7 @@ function $HttpProvider() {
19293
19554
  * @methodOf ng.$http
19294
19555
  *
19295
19556
  * @description
19296
- * Shortcut method to perform `JSONP` request
19557
+ * Shortcut method to perform `JSONP` request.
19297
19558
  *
19298
19559
  * @param {string} url Relative or absolute URL specifying the destination of the request.
19299
19560
  * Should contain `JSON_CALLBACK` string.
@@ -19308,7 +19569,7 @@ function $HttpProvider() {
19308
19569
  * @methodOf ng.$http
19309
19570
  *
19310
19571
  * @description
19311
- * Shortcut method to perform `POST` request
19572
+ * Shortcut method to perform `POST` request.
19312
19573
  *
19313
19574
  * @param {string} url Relative or absolute URL specifying the destination of the request
19314
19575
  * @param {*} data Request content
@@ -19322,7 +19583,7 @@ function $HttpProvider() {
19322
19583
  * @methodOf ng.$http
19323
19584
  *
19324
19585
  * @description
19325
- * Shortcut method to perform `PUT` request
19586
+ * Shortcut method to perform `PUT` request.
19326
19587
  *
19327
19588
  * @param {string} url Relative or absolute URL specifying the destination of the request
19328
19589
  * @param {*} data Request content
@@ -19374,7 +19635,7 @@ function $HttpProvider() {
19374
19635
 
19375
19636
 
19376
19637
  /**
19377
- * Makes the request
19638
+ * Makes the request.
19378
19639
  *
19379
19640
  * !!! ACCESSES CLOSURE VARS:
19380
19641
  * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
@@ -19391,8 +19652,8 @@ function $HttpProvider() {
19391
19652
 
19392
19653
 
19393
19654
  if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
19394
- cache = isObject(config.cache) ? config.cache
19395
- : isObject(defaults.cache) ? defaults.cache
19655
+ cache = isObject(config.cache) ? config.cache
19656
+ : isObject(defaults.cache) ? defaults.cache
19396
19657
  : defaultCache;
19397
19658
  }
19398
19659
 
@@ -19443,7 +19704,7 @@ function $HttpProvider() {
19443
19704
  }
19444
19705
 
19445
19706
  resolvePromise(response, status, headersString);
19446
- $rootScope.$apply();
19707
+ if (!$rootScope.$$phase) $rootScope.$apply();
19447
19708
  }
19448
19709
 
19449
19710
 
@@ -19527,6 +19788,7 @@ function $HttpBackendProvider() {
19527
19788
  function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
19528
19789
  // TODO(vojta): fix the signature
19529
19790
  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
19791
+ var status;
19530
19792
  $browser.$$incOutstandingRequestCount();
19531
19793
  url = url || $browser.url();
19532
19794
 
@@ -19536,12 +19798,12 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
19536
19798
  callbacks[callbackId].data = data;
19537
19799
  };
19538
19800
 
19539
- jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
19801
+ var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
19540
19802
  function() {
19541
19803
  if (callbacks[callbackId].data) {
19542
19804
  completeRequest(callback, 200, callbacks[callbackId].data);
19543
19805
  } else {
19544
- completeRequest(callback, -2);
19806
+ completeRequest(callback, status || -2);
19545
19807
  }
19546
19808
  delete callbacks[callbackId];
19547
19809
  });
@@ -19552,8 +19814,6 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
19552
19814
  if (value) xhr.setRequestHeader(key, value);
19553
19815
  });
19554
19816
 
19555
- var status;
19556
-
19557
19817
  // In IE6 and 7, this might be called synchronously when xhr.send below is called and the
19558
19818
  // response is in the cache. the promise api will ensure that to the app code the api is
19559
19819
  // always async
@@ -19599,19 +19859,28 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
19599
19859
  }
19600
19860
 
19601
19861
  xhr.send(post || '');
19862
+ }
19602
19863
 
19603
- if (timeout > 0) {
19604
- $browserDefer(function() {
19605
- status = -1;
19606
- xhr.abort();
19607
- }, timeout);
19608
- }
19864
+ if (timeout > 0) {
19865
+ var timeoutId = $browserDefer(timeoutRequest, timeout);
19866
+ } else if (timeout && timeout.then) {
19867
+ timeout.then(timeoutRequest);
19609
19868
  }
19610
19869
 
19611
19870
 
19871
+ function timeoutRequest() {
19872
+ status = -1;
19873
+ jsonpDone && jsonpDone();
19874
+ xhr && xhr.abort();
19875
+ }
19876
+
19612
19877
  function completeRequest(callback, status, response, headersString) {
19613
19878
  // URL_MATCH is defined in src/service/location.js
19614
- var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1];
19879
+ var protocol = (url.match(SERVER_MATCH) || ['', locationProtocol])[1];
19880
+
19881
+ // cancel timeout and subsequent timeout promise resolution
19882
+ timeoutId && $browserDefer.cancel(timeoutId);
19883
+ jsonpDone = xhr = null;
19615
19884
 
19616
19885
  // fix status code for file protocol (it's always 0)
19617
19886
  status = (protocol == 'file') ? (response ? 200 : 404) : status;
@@ -19646,6 +19915,7 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
19646
19915
  }
19647
19916
 
19648
19917
  rawDocument.body.appendChild(script);
19918
+ return doneWrapper;
19649
19919
  }
19650
19920
  }
19651
19921
 
@@ -19736,17 +20006,17 @@ function $TimeoutProvider() {
19736
20006
  * block and delegates any exceptions to
19737
20007
  * {@link ng.$exceptionHandler $exceptionHandler} service.
19738
20008
  *
19739
- * The return value of registering a timeout function is a promise which will be resolved when
20009
+ * The return value of registering a timeout function is a promise, which will be resolved when
19740
20010
  * the timeout is reached and the timeout function is executed.
19741
20011
  *
19742
- * To cancel a the timeout request, call `$timeout.cancel(promise)`.
20012
+ * To cancel a timeout request, call `$timeout.cancel(promise)`.
19743
20013
  *
19744
20014
  * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
19745
20015
  * synchronously flush the queue of deferred functions.
19746
20016
  *
19747
- * @param {function()} fn A function, who's execution should be delayed.
20017
+ * @param {function()} fn A function, whose execution should be delayed.
19748
20018
  * @param {number=} [delay=0] Delay in milliseconds.
19749
- * @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise
20019
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
19750
20020
  * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
19751
20021
  * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
19752
20022
  * promise will be resolved with is the return value of the `fn` function.
@@ -19786,7 +20056,7 @@ function $TimeoutProvider() {
19786
20056
  * @methodOf ng.$timeout
19787
20057
  *
19788
20058
  * @description
19789
- * Cancels a task associated with the `promise`. As a result of this the promise will be
20059
+ * Cancels a task associated with the `promise`. As a result of this, the promise will be
19790
20060
  * resolved with a rejection.
19791
20061
  *
19792
20062
  * @param {Promise=} promise Promise returned by the `$timeout` function.
@@ -19874,7 +20144,7 @@ function $TimeoutProvider() {
19874
20144
  *
19875
20145
  * The general syntax in templates is as follows:
19876
20146
  *
19877
- * {{ expression | [ filter_name ] }}
20147
+ * {{ expression [| filter_name[:parameter_value] ... ] }}
19878
20148
  *
19879
20149
  * @param {String} name Name of the filter function to retrieve
19880
20150
  * @return {Function} the filter function
@@ -19976,7 +20246,7 @@ function $FilterProvider($provide) {
19976
20246
  <hr>
19977
20247
  Any: <input ng-model="search.$"> <br>
19978
20248
  Name only <input ng-model="search.name"><br>
19979
- Phone only <input ng-model="search.phone"å><br>
20249
+ Phone only <input ng-model="search.phone"><br>
19980
20250
  Equality <input type="checkbox" ng-model="strict"><br>
19981
20251
  <table id="searchObjResults">
19982
20252
  <tr><th>Name</th><th>Phone</th></tr>
@@ -20308,6 +20578,7 @@ function padNumber(num, digits, trim) {
20308
20578
 
20309
20579
 
20310
20580
  function dateGetter(name, size, offset, trim) {
20581
+ offset = offset || 0;
20311
20582
  return function(date) {
20312
20583
  var value = date['get' + name]();
20313
20584
  if (offset > 0 || value > -offset)
@@ -20422,7 +20693,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
20422
20693
  * (e.g. `"h o''clock"`).
20423
20694
  *
20424
20695
  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
20425
- * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
20696
+ * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
20426
20697
  * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
20427
20698
  * specified in the string input, the time is considered to be in the local timezone.
20428
20699
  * @param {string=} format Formatting rules (see Description). If not specified,
@@ -20471,7 +20742,11 @@ function dateFilter($locale) {
20471
20742
  tzMin = int(match[9] + match[11]);
20472
20743
  }
20473
20744
  dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
20474
- timeSetter.call(date, int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
20745
+ var h = int(match[4]||0) - tzHour;
20746
+ var m = int(match[5]||0) - tzMin
20747
+ var s = int(match[6]||0);
20748
+ var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000);
20749
+ timeSetter.call(date, h, m, s, ms);
20475
20750
  return date;
20476
20751
  }
20477
20752
  return string;
@@ -20953,27 +21228,52 @@ var htmlAnchorDirective = valueFn({
20953
21228
 
20954
21229
  /**
20955
21230
  * @ngdoc directive
20956
- * @name ng.directive:ngSrc
21231
+ * @name ng.directive:ngSrc
21232
+ * @restrict A
21233
+ *
21234
+ * @description
21235
+ * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
21236
+ * work right: The browser will fetch from the URL with the literal
21237
+ * text `{{hash}}` until Angular replaces the expression inside
21238
+ * `{{hash}}`. The `ngSrc` directive solves this problem.
21239
+ *
21240
+ * The buggy way to write it:
21241
+ * <pre>
21242
+ * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
21243
+ * </pre>
21244
+ *
21245
+ * The correct way to write it:
21246
+ * <pre>
21247
+ * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
21248
+ * </pre>
21249
+ *
21250
+ * @element IMG
21251
+ * @param {template} ngSrc any string which can contain `{{}}` markup.
21252
+ */
21253
+
21254
+ /**
21255
+ * @ngdoc directive
21256
+ * @name ng.directive:ngSrcset
20957
21257
  * @restrict A
20958
21258
  *
20959
21259
  * @description
20960
- * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
21260
+ * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
20961
21261
  * work right: The browser will fetch from the URL with the literal
20962
21262
  * text `{{hash}}` until Angular replaces the expression inside
20963
- * `{{hash}}`. The `ngSrc` directive solves this problem.
21263
+ * `{{hash}}`. The `ngSrcset` directive solves this problem.
20964
21264
  *
20965
21265
  * The buggy way to write it:
20966
21266
  * <pre>
20967
- * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
21267
+ * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
20968
21268
  * </pre>
20969
21269
  *
20970
21270
  * The correct way to write it:
20971
21271
  * <pre>
20972
- * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
21272
+ * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
20973
21273
  * </pre>
20974
21274
  *
20975
21275
  * @element IMG
20976
- * @param {template} ngSrc any string which can contain `{{}}` markup.
21276
+ * @param {template} ngSrcset any string which can contain `{{}}` markup.
20977
21277
  */
20978
21278
 
20979
21279
  /**
@@ -21196,8 +21496,8 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) {
21196
21496
  });
21197
21497
 
21198
21498
 
21199
- // ng-src, ng-href are interpolated
21200
- forEach(['src', 'href'], function(attrName) {
21499
+ // ng-src, ng-srcset, ng-href are interpolated
21500
+ forEach(['src', 'srcset', 'href'], function(attrName) {
21201
21501
  var normalized = directiveNormalize('ng-' + attrName);
21202
21502
  ngAttributeAliasDirectives[normalized] = function() {
21203
21503
  return {
@@ -21629,8 +21929,8 @@ var inputType = {
21629
21929
  *
21630
21930
  * @param {string} ngModel Assignable angular expression to data-bind to.
21631
21931
  * @param {string=} name Property name of the form under which the control is published.
21632
- * @param {string=} min Sets the `min` validation error key if the value entered is less then `min`.
21633
- * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`.
21932
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21933
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21634
21934
  * @param {string=} required Sets `required` validation error key if the value is not entered.
21635
21935
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21636
21936
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -21949,6 +22249,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21949
22249
  } else {
21950
22250
  var timeout;
21951
22251
 
22252
+ var deferListener = function() {
22253
+ if (!timeout) {
22254
+ timeout = $browser.defer(function() {
22255
+ listener();
22256
+ timeout = null;
22257
+ });
22258
+ }
22259
+ };
22260
+
21952
22261
  element.bind('keydown', function(event) {
21953
22262
  var key = event.keyCode;
21954
22263
 
@@ -21956,16 +22265,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21956
22265
  // command modifiers arrows
21957
22266
  if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21958
22267
 
21959
- if (!timeout) {
21960
- timeout = $browser.defer(function() {
21961
- listener();
21962
- timeout = null;
21963
- });
21964
- }
22268
+ deferListener();
21965
22269
  });
21966
22270
 
21967
22271
  // if user paste into input using mouse, we need "change" event to catch it
21968
22272
  element.bind('change', listener);
22273
+
22274
+ // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
22275
+ if ($sniffer.hasEvent('paste')) {
22276
+ element.bind('paste cut', deferListener);
22277
+ }
21969
22278
  }
21970
22279
 
21971
22280
 
@@ -21975,7 +22284,8 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21975
22284
 
21976
22285
  // pattern validator
21977
22286
  var pattern = attr.ngPattern,
21978
- patternValidator;
22287
+ patternValidator,
22288
+ match;
21979
22289
 
21980
22290
  var validate = function(regexp, value) {
21981
22291
  if (isEmpty(value) || regexp.test(value)) {
@@ -21988,8 +22298,9 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21988
22298
  };
21989
22299
 
21990
22300
  if (pattern) {
21991
- if (pattern.match(/^\/(.*)\/$/)) {
21992
- pattern = new RegExp(pattern.substr(1, pattern.length - 2));
22301
+ match = pattern.match(/^\/(.*)\/([gim]*)$/);
22302
+ if (match) {
22303
+ pattern = new RegExp(match[1], match[2]);
21993
22304
  patternValidator = function(value) {
21994
22305
  return validate(pattern, value)
21995
22306
  };
@@ -22264,7 +22575,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
22264
22575
  <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
22265
22576
  <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
22266
22577
  <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
22267
- <tt>myForm.userName.$error = {{myForm.lastName.$error}}</tt><br>
22578
+ <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
22268
22579
  <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
22269
22580
  <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
22270
22581
  <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
@@ -22543,7 +22854,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
22543
22854
  * For example {@link ng.directive:input input} or
22544
22855
  * {@link ng.directive:select select} directives call it.
22545
22856
  *
22546
- * It internally calls all `formatters` and if resulted value is valid, updates the model and
22857
+ * It internally calls all `parsers` and if resulted value is valid, updates the model and
22547
22858
  * calls all registered change listeners.
22548
22859
  *
22549
22860
  * @param {string} value Value from the view.
@@ -22849,7 +23160,7 @@ var ngValueDirective = function() {
22849
23160
  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22850
23161
  * `{{ expression }}` which is similar but less verbose.
22851
23162
  *
22852
- * Once scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
23163
+ * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
22853
23164
  * it's desirable to put bindings into template that is momentarily displayed by the browser in its
22854
23165
  * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
22855
23166
  * bindings invisible to the user while the page is loading.
@@ -22990,9 +23301,9 @@ function classDirective(name, selector) {
22990
23301
 
22991
23302
  if (name !== 'ngClass') {
22992
23303
  scope.$watch('$index', function($index, old$index) {
22993
- var mod = $index % 2;
22994
- if (mod !== old$index % 2) {
22995
- if (mod == selector) {
23304
+ var mod = $index & 1;
23305
+ if (mod !== old$index & 1) {
23306
+ if (mod === selector) {
22996
23307
  addClass(scope.$eval(attr[name]));
22997
23308
  } else {
22998
23309
  removeClass(scope.$eval(attr[name]));
@@ -23004,12 +23315,12 @@ function classDirective(name, selector) {
23004
23315
 
23005
23316
  function ngClassWatchAction(newVal) {
23006
23317
  if (selector === true || scope.$index % 2 === selector) {
23007
- if (oldVal && (newVal !== oldVal)) {
23318
+ if (oldVal && !equals(newVal,oldVal)) {
23008
23319
  removeClass(oldVal);
23009
23320
  }
23010
23321
  addClass(newVal);
23011
23322
  }
23012
- oldVal = newVal;
23323
+ oldVal = copy(newVal);
23013
23324
  }
23014
23325
 
23015
23326
 
@@ -23135,7 +23446,7 @@ var ngClassOddDirective = classDirective('Odd', 0);
23135
23446
  * @name ng.directive:ngClassEven
23136
23447
  *
23137
23448
  * @description
23138
- * The `ngClassOdd` and `ngClassEven` works exactly as
23449
+ * The `ngClassOdd` and `ngClassEven` directives work exactly as
23139
23450
  * {@link ng.directive:ngClass ngClass}, except it works in
23140
23451
  * conjunction with `ngRepeat` and takes affect only on odd (even) rows.
23141
23452
  *
@@ -23252,14 +23563,14 @@ var ngCloakDirective = ngDirective({
23252
23563
  * * Controller — The `ngController` directive specifies a Controller class; the class has
23253
23564
  * methods that typically express the business logic behind the application.
23254
23565
  *
23255
- * Note that an alternative way to define controllers is via the `{@link ng.$route}`
23256
- * service.
23566
+ * Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
23257
23567
  *
23258
23568
  * @element ANY
23259
23569
  * @scope
23260
23570
  * @param {expression} ngController Name of a globally accessible constructor function or an
23261
23571
  * {@link guide/expression expression} that on the current scope evaluates to a
23262
- * constructor function.
23572
+ * constructor function. The controller instance can further be published into the scope
23573
+ * by adding `as localName` the controller name attribute.
23263
23574
  *
23264
23575
  * @example
23265
23576
  * Here is a simple form for editing user contact information. Adding, removing, clearing, and
@@ -23267,8 +23578,75 @@ var ngCloakDirective = ngDirective({
23267
23578
  * easily be called from the angular markup. Notice that the scope becomes the `this` for the
23268
23579
  * controller's instance. This allows for easy access to the view data from the controller. Also
23269
23580
  * notice that any changes to the data are automatically reflected in the View without the need
23270
- * for a manual update.
23581
+ * for a manual update. The example is included in two different declaration styles based on
23582
+ * your style preferences.
23271
23583
  <doc:example>
23584
+ <doc:source>
23585
+ <script>
23586
+ function SettingsController() {
23587
+ this.name = "John Smith";
23588
+ this.contacts = [
23589
+ {type: 'phone', value: '408 555 1212'},
23590
+ {type: 'email', value: 'john.smith@example.org'} ];
23591
+ };
23592
+
23593
+ SettingsController.prototype.greet = function() {
23594
+ alert(this.name);
23595
+ };
23596
+
23597
+ SettingsController.prototype.addContact = function() {
23598
+ this.contacts.push({type: 'email', value: 'yourname@example.org'});
23599
+ };
23600
+
23601
+ SettingsController.prototype.removeContact = function(contactToRemove) {
23602
+ var index = this.contacts.indexOf(contactToRemove);
23603
+ this.contacts.splice(index, 1);
23604
+ };
23605
+
23606
+ SettingsController.prototype.clearContact = function(contact) {
23607
+ contact.type = 'phone';
23608
+ contact.value = '';
23609
+ };
23610
+ </script>
23611
+ <div ng-controller="SettingsController as settings">
23612
+ Name: <input type="text" ng-model="settings.name"/>
23613
+ [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
23614
+ Contact:
23615
+ <ul>
23616
+ <li ng-repeat="contact in settings.contacts">
23617
+ <select ng-model="contact.type">
23618
+ <option>phone</option>
23619
+ <option>email</option>
23620
+ </select>
23621
+ <input type="text" ng-model="contact.value"/>
23622
+ [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
23623
+ | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
23624
+ </li>
23625
+ <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
23626
+ </ul>
23627
+ </div>
23628
+ </doc:source>
23629
+ <doc:scenario>
23630
+ it('should check controller', function() {
23631
+ expect(element('.doc-example-live div>:input').val()).toBe('John Smith');
23632
+ expect(element('.doc-example-live li:nth-child(1) input').val())
23633
+ .toBe('408 555 1212');
23634
+ expect(element('.doc-example-live li:nth-child(2) input').val())
23635
+ .toBe('john.smith@example.org');
23636
+
23637
+ element('.doc-example-live li:first a:contains("clear")').click();
23638
+ expect(element('.doc-example-live li:first input').val()).toBe('');
23639
+
23640
+ element('.doc-example-live li:last a:contains("add")').click();
23641
+ expect(element('.doc-example-live li:nth-child(3) input').val())
23642
+ .toBe('yourname@example.org');
23643
+ });
23644
+ </doc:scenario>
23645
+ </doc:example>
23646
+
23647
+
23648
+
23649
+ <doc:example>
23272
23650
  <doc:source>
23273
23651
  <script>
23274
23652
  function SettingsController($scope) {
@@ -23331,6 +23709,7 @@ var ngCloakDirective = ngDirective({
23331
23709
  });
23332
23710
  </doc:scenario>
23333
23711
  </doc:example>
23712
+
23334
23713
  */
23335
23714
  var ngControllerDirective = [function() {
23336
23715
  return {
@@ -23344,16 +23723,32 @@ var ngControllerDirective = [function() {
23344
23723
  * @name ng.directive:ngCsp
23345
23724
  * @priority 1000
23346
23725
  *
23726
+ * @element html
23347
23727
  * @description
23348
23728
  * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
23349
- * This directive should be used on the root element of the application (typically the `<html>`
23350
- * element or other element with the {@link ng.directive:ngApp ngApp}
23351
- * directive).
23352
- *
23353
- * If enabled the performance of template expression evaluator will suffer slightly, so don't enable
23354
- * this mode unless you need it.
23355
- *
23356
- * @element html
23729
+ *
23730
+ * This is necessary when developing things like Google Chrome Extensions.
23731
+ *
23732
+ * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
23733
+ * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
23734
+ * any of these restrictions.
23735
+ *
23736
+ * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
23737
+ * it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
23738
+ * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
23739
+ * be raised.
23740
+ *
23741
+ * In order to use this feature put `ngCsp` directive on the root element of the application.
23742
+ *
23743
+ * @example
23744
+ * This example shows how to apply the `ngCsp` directive to the `html` tag.
23745
+ <pre>
23746
+ <!doctype html>
23747
+ <html ng-app ng-csp>
23748
+ ...
23749
+ ...
23750
+ </html>
23751
+ </pre>
23357
23752
  */
23358
23753
 
23359
23754
  var ngCspDirective = ['$sniffer', function($sniffer) {
@@ -23634,6 +24029,114 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
23634
24029
  });
23635
24030
  });
23636
24031
 
24032
+ /**
24033
+ * @ngdoc directive
24034
+ * @name ng.directive:ngIf
24035
+ * @restrict A
24036
+ *
24037
+ * @description
24038
+ * The `ngIf` directive removes and recreates a portion of the DOM tree (HTML)
24039
+ * conditionally based on **"falsy"** and **"truthy"** values, respectively, evaluated within
24040
+ * an {expression}. In other words, if the expression assigned to **ngIf evaluates to a false
24041
+ * value** then **the element is removed from the DOM** and **if true** then **a clone of the
24042
+ * element is reinserted into the DOM**.
24043
+ *
24044
+ * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24045
+ * element in the DOM rather than changing its visibility via the `display` css property. A common
24046
+ * case when this difference is significant is when using css selectors that rely on an element's
24047
+ * position within the DOM (HTML), such as the `:first-child` or `:last-child` pseudo-classes.
24048
+ *
24049
+ * Note that **when an element is removed using ngIf its scope is destroyed** and **a new scope
24050
+ * is created when the element is restored**. The scope created within `ngIf` inherits from
24051
+ * its parent scope using
24052
+ * {@link https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance prototypal inheritance}.
24053
+ * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24054
+ * a javascript primitive defined in the parent scope. In this case any modifications made to the
24055
+ * variable within the child scope will override (hide) the value in the parent scope.
24056
+ *
24057
+ * Also, `ngIf` recreates elements using their compiled state. An example scenario of this behavior
24058
+ * is if an element's class attribute is directly modified after it's compiled, using something like
24059
+ * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24060
+ * the added class will be lost because the original compiled state is used to regenerate the element.
24061
+ *
24062
+ * Additionally, you can provide animations via the ngAnimate attribute to animate the **enter**
24063
+ * and **leave** effects.
24064
+ *
24065
+ * @animations
24066
+ * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container
24067
+ * leave - happens just before the ngIf contents are removed from the DOM
24068
+ *
24069
+ * @element ANY
24070
+ * @scope
24071
+ * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24072
+ * the element is removed from the DOM tree (HTML).
24073
+ *
24074
+ * @example
24075
+ <example animations="true">
24076
+ <file name="index.html">
24077
+ Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
24078
+ Show when checked:
24079
+ <span ng-if="checked" ng-animate="'example'">
24080
+ I'm removed when the checkbox is unchecked.
24081
+ </span>
24082
+ </file>
24083
+ <file name="animations.css">
24084
+ .example-leave, .example-enter {
24085
+ -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24086
+ -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24087
+ -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24088
+ -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24089
+ transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24090
+ }
24091
+
24092
+ .example-enter {
24093
+ opacity:0;
24094
+ }
24095
+ .example-enter.example-enter-active {
24096
+ opacity:1;
24097
+ }
24098
+
24099
+ .example-leave {
24100
+ opacity:1;
24101
+ }
24102
+ .example-leave.example-leave-active {
24103
+ opacity:0;
24104
+ }
24105
+ </file>
24106
+ </example>
24107
+ */
24108
+ var ngIfDirective = ['$animator', function($animator) {
24109
+ return {
24110
+ transclude: 'element',
24111
+ priority: 1000,
24112
+ terminal: true,
24113
+ restrict: 'A',
24114
+ compile: function (element, attr, transclude) {
24115
+ return function ($scope, $element, $attr) {
24116
+ var animate = $animator($scope, $attr);
24117
+ var childElement, childScope;
24118
+ $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24119
+ if (childElement) {
24120
+ animate.leave(childElement);
24121
+ childElement = undefined;
24122
+ }
24123
+ if (childScope) {
24124
+ childScope.$destroy();
24125
+ childScope = undefined;
24126
+ }
24127
+ if (toBoolean(value)) {
24128
+ childScope = $scope.$new();
24129
+ transclude(childScope, function (clone) {
24130
+ childElement = clone;
24131
+ animate.enter(clone, $element.parent(), $element);
24132
+ });
24133
+ }
24134
+ });
24135
+ }
24136
+ }
24137
+ }
24138
+ }];
24139
+
23637
24140
  /**
23638
24141
  * @ngdoc directive
23639
24142
  * @name ng.directive:ngInclude
@@ -23667,7 +24170,7 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
23667
24170
  * - Otherwise enable scrolling only if the expression evaluates to truthy value.
23668
24171
  *
23669
24172
  * @example
23670
- <example>
24173
+ <example animations="true">
23671
24174
  <file name="index.html">
23672
24175
  <div ng-controller="Ctrl">
23673
24176
  <select ng-model="template" ng-options="t.name for t in templates">
@@ -23695,8 +24198,8 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
23695
24198
  <div>Content of template2.html</div>
23696
24199
  </file>
23697
24200
  <file name="animations.css">
23698
- .example-leave-setup,
23699
- .example-enter-setup {
24201
+ .example-leave,
24202
+ .example-enter {
23700
24203
  -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23701
24204
  -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23702
24205
  -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
@@ -23715,17 +24218,17 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
23715
24218
  padding:10px;
23716
24219
  }
23717
24220
 
23718
- .example-enter-setup {
24221
+ .example-enter {
23719
24222
  top:-50px;
23720
24223
  }
23721
- .example-enter-setup.example-enter-start {
24224
+ .example-enter.example-enter-active {
23722
24225
  top:0;
23723
24226
  }
23724
24227
 
23725
- .example-leave-setup {
24228
+ .example-leave {
23726
24229
  top:0;
23727
24230
  }
23728
- .example-leave-setup.example-leave-start {
24231
+ .example-leave.example-leave-active {
23729
24232
  top:50px;
23730
24233
  }
23731
24234
  </file>
@@ -23748,6 +24251,16 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
23748
24251
  */
23749
24252
 
23750
24253
 
24254
+ /**
24255
+ * @ngdoc event
24256
+ * @name ng.directive:ngInclude#$includeContentRequested
24257
+ * @eventOf ng.directive:ngInclude
24258
+ * @eventType emit on the scope ngInclude was declared in
24259
+ * @description
24260
+ * Emitted every time the ngInclude content is requested.
24261
+ */
24262
+
24263
+
23751
24264
  /**
23752
24265
  * @ngdoc event
23753
24266
  * @name ng.directive:ngInclude#$includeContentLoaded
@@ -23804,6 +24317,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
23804
24317
  }).error(function() {
23805
24318
  if (thisChangeId === changeCounter) clearContent();
23806
24319
  });
24320
+ scope.$emit('$includeContentRequested');
23807
24321
  } else {
23808
24322
  clearContent();
23809
24323
  }
@@ -24075,7 +24589,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
24075
24589
  if (!isNaN(value)) {
24076
24590
  //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
24077
24591
  //check it against pluralization rules in $locale service
24078
- if (!whens[value]) value = $locale.pluralCat(value - offset);
24592
+ if (!(value in whens)) value = $locale.pluralCat(value - offset);
24079
24593
  return whensExpFns[value](scope, element, true);
24080
24594
  } else {
24081
24595
  return '';
@@ -24175,9 +24689,9 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
24175
24689
  </div>
24176
24690
  </file>
24177
24691
  <file name="animations.css">
24178
- .example-repeat-enter-setup,
24179
- .example-repeat-leave-setup,
24180
- .example-repeat-move-setup {
24692
+ .example-repeat-enter,
24693
+ .example-repeat-leave,
24694
+ .example-repeat-move {
24181
24695
  -webkit-transition:all linear 0.5s;
24182
24696
  -moz-transition:all linear 0.5s;
24183
24697
  -ms-transition:all linear 0.5s;
@@ -24185,26 +24699,26 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
24185
24699
  transition:all linear 0.5s;
24186
24700
  }
24187
24701
 
24188
- .example-repeat-enter-setup {
24702
+ .example-repeat-enter {
24189
24703
  line-height:0;
24190
24704
  opacity:0;
24191
24705
  }
24192
- .example-repeat-enter-setup.example-repeat-enter-start {
24706
+ .example-repeat-enter.example-repeat-enter-active {
24193
24707
  line-height:20px;
24194
24708
  opacity:1;
24195
24709
  }
24196
24710
 
24197
- .example-repeat-leave-setup {
24711
+ .example-repeat-leave {
24198
24712
  opacity:1;
24199
24713
  line-height:20px;
24200
24714
  }
24201
- .example-repeat-leave-setup.example-repeat-leave-start {
24715
+ .example-repeat-leave.example-repeat-leave-active {
24202
24716
  opacity:0;
24203
24717
  line-height:0;
24204
24718
  }
24205
24719
 
24206
- .example-repeat-move-setup { }
24207
- .example-repeat-move-setup.example-repeat-move-start { }
24720
+ .example-repeat-move { }
24721
+ .example-repeat-move.example-repeat-move-active { }
24208
24722
  </file>
24209
24723
  <file name="scenario.js">
24210
24724
  it('should render initial data set', function() {
@@ -24240,7 +24754,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
24240
24754
  var animate = $animator($scope, $attr);
24241
24755
  var expression = $attr.ngRepeat;
24242
24756
  var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
24243
- trackByExp, hashExpFn, trackByIdFn, lhs, rhs, valueIdentifier, keyIdentifier,
24757
+ trackByExp, trackByExpGetter, trackByIdFn, lhs, rhs, valueIdentifier, keyIdentifier,
24244
24758
  hashFnLocals = {$id: hashKey};
24245
24759
 
24246
24760
  if (!match) {
@@ -24253,13 +24767,13 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
24253
24767
  trackByExp = match[4];
24254
24768
 
24255
24769
  if (trackByExp) {
24256
- hashExpFn = $parse(trackByExp);
24770
+ trackByExpGetter = $parse(trackByExp);
24257
24771
  trackByIdFn = function(key, value, index) {
24258
24772
  // assign key, value, and $index to the locals so that they can be used in hash functions
24259
24773
  if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
24260
24774
  hashFnLocals[valueIdentifier] = value;
24261
24775
  hashFnLocals.$index = index;
24262
- return hashExpFn($scope, hashFnLocals);
24776
+ return trackByExpGetter($scope, hashFnLocals);
24263
24777
  };
24264
24778
  } else {
24265
24779
  trackByIdFn = function(key, value) {
@@ -24299,7 +24813,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
24299
24813
  nextBlockOrder = [];
24300
24814
 
24301
24815
 
24302
- if (isArray(collection)) {
24816
+ if (isArrayLike(collection)) {
24303
24817
  collectionKeys = collection;
24304
24818
  } else {
24305
24819
  // if object, extract keys, sort them and use to determine order of iteration over obj props
@@ -24320,7 +24834,8 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
24320
24834
  key = (collection === collectionKeys) ? index : collectionKeys[index];
24321
24835
  value = collection[key];
24322
24836
  trackById = trackByIdFn(key, value, index);
24323
- if((block = lastBlockMap[trackById])) {
24837
+ if(lastBlockMap.hasOwnProperty(trackById)) {
24838
+ block = lastBlockMap[trackById]
24324
24839
  delete lastBlockMap[trackById];
24325
24840
  nextBlockMap[trackById] = block;
24326
24841
  nextBlockOrder[index] = block;
@@ -24330,10 +24845,12 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
24330
24845
  if (block && block.element) lastBlockMap[block.id] = block;
24331
24846
  });
24332
24847
  // This is a duplicate and we need to throw an error
24333
- throw new Error('Duplicates in a repeater are not allowed. Repeater: ' + expression);
24848
+ throw new Error('Duplicates in a repeater are not allowed. Repeater: ' + expression +
24849
+ ' key: ' + trackById);
24334
24850
  } else {
24335
24851
  // new never before seen block
24336
24852
  nextBlockOrder[index] = { id: trackById };
24853
+ nextBlockMap[trackById] = false;
24337
24854
  }
24338
24855
  }
24339
24856
 
@@ -24445,7 +24962,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
24445
24962
  </div>
24446
24963
  </file>
24447
24964
  <file name="animations.css">
24448
- .example-show-setup, .example-hide-setup {
24965
+ .example-show, .example-hide {
24449
24966
  -webkit-transition:all linear 0.5s;
24450
24967
  -moz-transition:all linear 0.5s;
24451
24968
  -ms-transition:all linear 0.5s;
@@ -24453,12 +24970,12 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
24453
24970
  transition:all linear 0.5s;
24454
24971
  }
24455
24972
 
24456
- .example-show-setup {
24973
+ .example-show {
24457
24974
  line-height:0;
24458
24975
  opacity:0;
24459
24976
  padding:0 10px;
24460
24977
  }
24461
- .example-show-start.example-show-start {
24978
+ .example-show-active.example-show-active {
24462
24979
  line-height:20px;
24463
24980
  opacity:1;
24464
24981
  padding:10px;
@@ -24466,14 +24983,14 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
24466
24983
  background:white;
24467
24984
  }
24468
24985
 
24469
- .example-hide-setup {
24986
+ .example-hide {
24470
24987
  line-height:20px;
24471
24988
  opacity:1;
24472
24989
  padding:10px;
24473
24990
  border:1px solid black;
24474
24991
  background:white;
24475
24992
  }
24476
- .example-hide-start.example-hide-start {
24993
+ .example-hide-active.example-hide-active {
24477
24994
  line-height:0;
24478
24995
  opacity:0;
24479
24996
  padding:0 10px;
@@ -24554,7 +25071,7 @@ var ngShowDirective = ['$animator', function($animator) {
24554
25071
  </div>
24555
25072
  </file>
24556
25073
  <file name="animations.css">
24557
- .example-show-setup, .example-hide-setup {
25074
+ .example-show, .example-hide {
24558
25075
  -webkit-transition:all linear 0.5s;
24559
25076
  -moz-transition:all linear 0.5s;
24560
25077
  -ms-transition:all linear 0.5s;
@@ -24562,12 +25079,12 @@ var ngShowDirective = ['$animator', function($animator) {
24562
25079
  transition:all linear 0.5s;
24563
25080
  }
24564
25081
 
24565
- .example-show-setup {
25082
+ .example-show {
24566
25083
  line-height:0;
24567
25084
  opacity:0;
24568
25085
  padding:0 10px;
24569
25086
  }
24570
- .example-show-start.example-show-start {
25087
+ .example-show.example-show-active {
24571
25088
  line-height:20px;
24572
25089
  opacity:1;
24573
25090
  padding:10px;
@@ -24575,14 +25092,14 @@ var ngShowDirective = ['$animator', function($animator) {
24575
25092
  background:white;
24576
25093
  }
24577
25094
 
24578
- .example-hide-setup {
25095
+ .example-hide {
24579
25096
  line-height:20px;
24580
25097
  opacity:1;
24581
25098
  padding:10px;
24582
25099
  border:1px solid black;
24583
25100
  background:white;
24584
25101
  }
24585
- .example-hide-start.example-hide-start {
25102
+ .example-hide.example-hide-active {
24586
25103
  line-height:0;
24587
25104
  opacity:0;
24588
25105
  padding:0 10px;
@@ -24734,7 +25251,7 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
24734
25251
  }
24735
25252
  </file>
24736
25253
  <file name="animations.css">
24737
- .example-leave-setup, .example-enter-setup {
25254
+ .example-leave, .example-enter {
24738
25255
  -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24739
25256
  -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24740
25257
  -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
@@ -24753,17 +25270,17 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
24753
25270
  padding:10px;
24754
25271
  }
24755
25272
 
24756
- .example-enter-setup {
25273
+ .example-enter {
24757
25274
  top:-50px;
24758
25275
  }
24759
- .example-enter-start.example-enter-start {
25276
+ .example-enter.example-enter-active {
24760
25277
  top:0;
24761
25278
  }
24762
25279
 
24763
- .example-leave-setup {
25280
+ .example-leave {
24764
25281
  top:0;
24765
25282
  }
24766
- .example-leave-start.example-leave-start {
25283
+ .example-leave.example-leave-active {
24767
25284
  top:50px;
24768
25285
  }
24769
25286
  </file>
@@ -24929,7 +25446,7 @@ var ngTranscludeDirective = ngDirective({
24929
25446
  * @example
24930
25447
  <example module="ngView" animations="true">
24931
25448
  <file name="index.html">
24932
- <div ng-controller="MainCntl">
25449
+ <div ng-controller="MainCntl as main">
24933
25450
  Choose:
24934
25451
  <a href="Book/Moby">Moby</a> |
24935
25452
  <a href="Book/Moby/ch/1">Moby: Ch1</a> |
@@ -24943,31 +25460,31 @@ var ngTranscludeDirective = ngDirective({
24943
25460
  ng-animate="{enter: 'example-enter', leave: 'example-leave'}"></div>
24944
25461
  <hr />
24945
25462
 
24946
- <pre>$location.path() = {{$location.path()}}</pre>
24947
- <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
24948
- <pre>$route.current.params = {{$route.current.params}}</pre>
24949
- <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
24950
- <pre>$routeParams = {{$routeParams}}</pre>
25463
+ <pre>$location.path() = {{main.$location.path()}}</pre>
25464
+ <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
25465
+ <pre>$route.current.params = {{main.$route.current.params}}</pre>
25466
+ <pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
25467
+ <pre>$routeParams = {{main.$routeParams}}</pre>
24951
25468
  </div>
24952
25469
  </file>
24953
25470
 
24954
25471
  <file name="book.html">
24955
25472
  <div>
24956
- controller: {{name}}<br />
24957
- Book Id: {{params.bookId}}<br />
25473
+ controller: {{book.name}}<br />
25474
+ Book Id: {{book.params.bookId}}<br />
24958
25475
  </div>
24959
25476
  </file>
24960
25477
 
24961
25478
  <file name="chapter.html">
24962
25479
  <div>
24963
- controller: {{name}}<br />
24964
- Book Id: {{params.bookId}}<br />
24965
- Chapter Id: {{params.chapterId}}
25480
+ controller: {{chapter.name}}<br />
25481
+ Book Id: {{chapter.params.bookId}}<br />
25482
+ Chapter Id: {{chapter.params.chapterId}}
24966
25483
  </div>
24967
25484
  </file>
24968
25485
 
24969
25486
  <file name="animations.css">
24970
- .example-leave-setup, .example-enter-setup {
25487
+ .example-leave, .example-enter {
24971
25488
  -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
24972
25489
  -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
24973
25490
  -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
@@ -24993,15 +25510,15 @@ var ngTranscludeDirective = ngDirective({
24993
25510
  padding:10px;
24994
25511
  }
24995
25512
 
24996
- .example-enter-setup {
25513
+ .example-enter {
24997
25514
  left:100%;
24998
25515
  }
24999
- .example-enter-setup.example-enter-start {
25516
+ .example-enter.example-enter-active {
25000
25517
  left:0;
25001
25518
  }
25002
25519
 
25003
- .example-leave-setup { }
25004
- .example-leave-setup.example-leave-start {
25520
+ .example-leave { }
25521
+ .example-leave.example-leave-active {
25005
25522
  left:-100%;
25006
25523
  }
25007
25524
  </file>
@@ -25010,31 +25527,33 @@ var ngTranscludeDirective = ngDirective({
25010
25527
  angular.module('ngView', [], function($routeProvider, $locationProvider) {
25011
25528
  $routeProvider.when('/Book/:bookId', {
25012
25529
  templateUrl: 'book.html',
25013
- controller: BookCntl
25530
+ controller: BookCntl,
25531
+ controllerAs: 'book'
25014
25532
  });
25015
25533
  $routeProvider.when('/Book/:bookId/ch/:chapterId', {
25016
25534
  templateUrl: 'chapter.html',
25017
- controller: ChapterCntl
25535
+ controller: ChapterCntl,
25536
+ controllerAs: 'chapter'
25018
25537
  });
25019
25538
 
25020
25539
  // configure html5 to get links working on jsfiddle
25021
25540
  $locationProvider.html5Mode(true);
25022
25541
  });
25023
25542
 
25024
- function MainCntl($scope, $route, $routeParams, $location) {
25025
- $scope.$route = $route;
25026
- $scope.$location = $location;
25027
- $scope.$routeParams = $routeParams;
25543
+ function MainCntl($route, $routeParams, $location) {
25544
+ this.$route = $route;
25545
+ this.$location = $location;
25546
+ this.$routeParams = $routeParams;
25028
25547
  }
25029
25548
 
25030
- function BookCntl($scope, $routeParams) {
25031
- $scope.name = "BookCntl";
25032
- $scope.params = $routeParams;
25549
+ function BookCntl($routeParams) {
25550
+ this.name = "BookCntl";
25551
+ this.params = $routeParams;
25033
25552
  }
25034
25553
 
25035
- function ChapterCntl($scope, $routeParams) {
25036
- $scope.name = "ChapterCntl";
25037
- $scope.params = $routeParams;
25554
+ function ChapterCntl($routeParams) {
25555
+ this.name = "ChapterCntl";
25556
+ this.params = $routeParams;
25038
25557
  }
25039
25558
  </file>
25040
25559
 
@@ -25098,9 +25617,10 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
25098
25617
 
25099
25618
  if (template) {
25100
25619
  clearContent();
25101
- animate.enter(jqLite('<div></div>').html(template).contents(), element);
25620
+ var enterElements = jqLite('<div></div>').html(template).contents();
25621
+ animate.enter(enterElements, element);
25102
25622
 
25103
- var link = $compile(element.contents()),
25623
+ var link = $compile(enterElements),
25104
25624
  current = $route.current,
25105
25625
  controller;
25106
25626
 
@@ -25108,6 +25628,9 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
25108
25628
  if (current.controller) {
25109
25629
  locals.$scope = lastScope;
25110
25630
  controller = $controller(current.controller, locals);
25631
+ if (current.controllerAs) {
25632
+ lastScope[current.controllerAs] = controller;
25633
+ }
25111
25634
  element.children().data('$ngControllerController', controller);
25112
25635
  }
25113
25636
 
@@ -25197,7 +25720,8 @@ var scriptDirective = ['$templateCache', function($templateCache) {
25197
25720
  * `select` model to be bound to a non-string value. This is because an option element can currently
25198
25721
  * be bound to string values only.
25199
25722
  *
25200
- * @param {string} name assignable expression to data-bind to.
25723
+ * @param {string} ngModel Assignable angular expression to data-bind to.
25724
+ * @param {string=} name Property name of the form under which the control is published.
25201
25725
  * @param {string=} required The control is considered valid only if value is entered.
25202
25726
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25203
25727
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -25208,7 +25732,7 @@ var scriptDirective = ['$templateCache', function($templateCache) {
25208
25732
  * * `label` **`for`** `value` **`in`** `array`
25209
25733
  * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
25210
25734
  * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
25211
- * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array`
25735
+ * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
25212
25736
  * * for object data sources:
25213
25737
  * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25214
25738
  * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
@@ -25228,6 +25752,9 @@ var scriptDirective = ['$templateCache', function($templateCache) {
25228
25752
  * element. If not specified, `select` expression will default to `value`.
25229
25753
  * * `group`: The result of this expression will be used to group options using the `<optgroup>`
25230
25754
  * DOM element.
25755
+ * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
25756
+ * used to identify the objects in the array. The `trackexpr` will most likely refer to the
25757
+ * `value` variable (e.g. `value.propertyName`).
25231
25758
  *
25232
25759
  * @example
25233
25760
  <doc:example>
@@ -25292,8 +25819,8 @@ var scriptDirective = ['$templateCache', function($templateCache) {
25292
25819
 
25293
25820
  var ngOptionsDirective = valueFn({ terminal: true });
25294
25821
  var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25295
- //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
25296
- 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+(.*)$/,
25822
+ //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000007777000000000000000000088888
25823
+ 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+(.*?))?$/,
25297
25824
  nullModelCtrl = {$setViewValue: noop};
25298
25825
 
25299
25826
  return {
@@ -25467,7 +25994,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25467
25994
 
25468
25995
  if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) {
25469
25996
  throw Error(
25470
- "Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
25997
+ "Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_ (track by _expr_)?'" +
25471
25998
  " but got '" + optionsExp + "'.");
25472
25999
  }
25473
26000
 
@@ -25477,6 +26004,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25477
26004
  groupByFn = $parse(match[3] || ''),
25478
26005
  valueFn = $parse(match[2] ? match[1] : valueName),
25479
26006
  valuesFn = $parse(match[7]),
26007
+ track = match[8],
26008
+ trackFn = track ? $parse(match[8]) : null,
25480
26009
  // This is an array of array of existing option groups in DOM. We try to reuse these if possible
25481
26010
  // optionGroupsCache[0] is the options with no option group
25482
26011
  // optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
@@ -25517,7 +26046,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25517
26046
  if ((optionElement = optionGroup[index].element)[0].selected) {
25518
26047
  key = optionElement.val();
25519
26048
  if (keyName) locals[keyName] = key;
25520
- locals[valueName] = collection[key];
26049
+ if (trackFn) {
26050
+ for (var trackIndex = 0; trackIndex < collection.length; trackIndex++) {
26051
+ locals[valueName] = collection[trackIndex];
26052
+ if (trackFn(scope, locals) == key) break;
26053
+ }
26054
+ } else {
26055
+ locals[valueName] = collection[key];
26056
+ }
25521
26057
  value.push(valueFn(scope, locals));
25522
26058
  }
25523
26059
  }
@@ -25529,9 +26065,19 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25529
26065
  } else if (key == ''){
25530
26066
  value = null;
25531
26067
  } else {
25532
- locals[valueName] = collection[key];
25533
- if (keyName) locals[keyName] = key;
25534
- value = valueFn(scope, locals);
26068
+ if (trackFn) {
26069
+ for (var trackIndex = 0; trackIndex < collection.length; trackIndex++) {
26070
+ locals[valueName] = collection[trackIndex];
26071
+ if (trackFn(scope, locals) == key) {
26072
+ value = valueFn(scope, locals);
26073
+ break;
26074
+ }
26075
+ }
26076
+ } else {
26077
+ locals[valueName] = collection[key];
26078
+ if (keyName) locals[keyName] = key;
26079
+ value = valueFn(scope, locals);
26080
+ }
25535
26081
  }
25536
26082
  }
25537
26083
  ctrl.$setViewValue(value);
@@ -25563,11 +26109,15 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25563
26109
  label;
25564
26110
 
25565
26111
  if (multiple) {
25566
- selectedSet = new HashMap(modelValue);
25567
- } else if (modelValue === null || nullOption) {
25568
- // if we are not multiselect, and we are null then we have to add the nullOption
25569
- optionGroups[''].push({selected:modelValue === null, id:'', label:''});
25570
- selectedSet = true;
26112
+ if (trackFn && isArray(modelValue)) {
26113
+ selectedSet = new HashMap([]);
26114
+ for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
26115
+ locals[valueName] = modelValue[trackIndex];
26116
+ selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
26117
+ }
26118
+ } else {
26119
+ selectedSet = new HashMap(modelValue);
26120
+ }
25571
26121
  }
25572
26122
 
25573
26123
  // We now build up the list of options we need (we merge later)
@@ -25579,22 +26129,33 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25579
26129
  optionGroupNames.push(optionGroupName);
25580
26130
  }
25581
26131
  if (multiple) {
25582
- selected = selectedSet.remove(valueFn(scope, locals)) != undefined;
26132
+ selected = selectedSet.remove(trackFn ? trackFn(scope, locals) : valueFn(scope, locals)) != undefined;
25583
26133
  } else {
25584
- selected = modelValue === valueFn(scope, locals);
26134
+ if (trackFn) {
26135
+ var modelCast = {};
26136
+ modelCast[valueName] = modelValue;
26137
+ selected = trackFn(scope, modelCast) === trackFn(scope, locals);
26138
+ } else {
26139
+ selected = modelValue === valueFn(scope, locals);
26140
+ }
25585
26141
  selectedSet = selectedSet || selected; // see if at least one item is selected
25586
26142
  }
25587
26143
  label = displayFn(scope, locals); // what will be seen by the user
25588
26144
  label = label === undefined ? '' : label; // doing displayFn(scope, locals) || '' overwrites zero values
25589
26145
  optionGroup.push({
25590
- id: keyName ? keys[index] : index, // either the index into array or key from object
26146
+ id: trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index), // either the index into array or key from object
25591
26147
  label: label,
25592
26148
  selected: selected // determine if we should be selected
25593
26149
  });
25594
26150
  }
25595
- if (!multiple && !selectedSet) {
25596
- // nothing was selected, we have to insert the undefined item
25597
- optionGroups[''].unshift({id:'?', label:'', selected:true});
26151
+ if (!multiple) {
26152
+ if (nullOption || modelValue === null) {
26153
+ // insert null option if we have a placeholder, or the model is null
26154
+ optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
26155
+ } else if (!selectedSet) {
26156
+ // option could not be found, we have to insert the undefined item
26157
+ optionGroups[''].unshift({id:'?', label:'', selected:true});
26158
+ }
25598
26159
  }
25599
26160
 
25600
26161
  // Now we need to update the list of DOM nodes to match the optionGroups we computed above
@@ -25638,7 +26199,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25638
26199
  if (existingOption.id !== option.id) {
25639
26200
  lastElement.val(existingOption.id = option.id);
25640
26201
  }
25641
- if (existingOption.element.selected !== option.selected) {
26202
+ // lastElement.prop('selected') provided by jQuery has side-effects
26203
+ if (lastElement[0].selected !== option.selected) {
25642
26204
  lastElement.prop('selected', (existingOption.selected = option.selected));
25643
26205
  }
25644
26206
  } else {
@@ -25964,102 +26526,6 @@ function callerFile(offset) {
25964
26526
  };
25965
26527
  }
25966
26528
 
25967
- /**
25968
- * Triggers a browser event. Attempts to choose the right event if one is
25969
- * not specified.
25970
- *
25971
- * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement
25972
- * @param {string} type Optional event type.
25973
- * @param {Array.<string>=} keys Optional list of pressed keys
25974
- * (valid values: 'alt', 'meta', 'shift', 'ctrl')
25975
- * @param {number} x Optional x-coordinate for mouse/touch events.
25976
- * @param {number} y Optional y-coordinate for mouse/touch events.
25977
- */
25978
- function browserTrigger(element, type, keys, x, y) {
25979
- if (element && !element.nodeName) element = element[0];
25980
- if (!element) return;
25981
- if (!type) {
25982
- type = {
25983
- 'text': 'change',
25984
- 'textarea': 'change',
25985
- 'hidden': 'change',
25986
- 'password': 'change',
25987
- 'button': 'click',
25988
- 'submit': 'click',
25989
- 'reset': 'click',
25990
- 'image': 'click',
25991
- 'checkbox': 'click',
25992
- 'radio': 'click',
25993
- 'select-one': 'change',
25994
- 'select-multiple': 'change'
25995
- }[lowercase(element.type)] || 'click';
25996
- }
25997
- if (lowercase(nodeName_(element)) == 'option') {
25998
- element.parentNode.value = element.value;
25999
- element = element.parentNode;
26000
- type = 'change';
26001
- }
26002
-
26003
- keys = keys || [];
26004
- function pressed(key) {
26005
- return indexOf(keys, key) !== -1;
26006
- }
26007
-
26008
- if (msie < 9) {
26009
- switch(element.type) {
26010
- case 'radio':
26011
- case 'checkbox':
26012
- element.checked = !element.checked;
26013
- break;
26014
- }
26015
- // WTF!!! Error: Unspecified error.
26016
- // Don't know why, but some elements when detached seem to be in inconsistent state and
26017
- // calling .fireEvent() on them will result in very unhelpful error (Error: Unspecified error)
26018
- // forcing the browser to compute the element position (by reading its CSS)
26019
- // puts the element in consistent state.
26020
- element.style.posLeft;
26021
-
26022
- // TODO(vojta): create event objects with pressed keys to get it working on IE<9
26023
- var ret = element.fireEvent('on' + type);
26024
- if (lowercase(element.type) == 'submit') {
26025
- while(element) {
26026
- if (lowercase(element.nodeName) == 'form') {
26027
- element.fireEvent('onsubmit');
26028
- break;
26029
- }
26030
- element = element.parentNode;
26031
- }
26032
- }
26033
- return ret;
26034
- } else {
26035
- var evnt = document.createEvent('MouseEvents'),
26036
- originalPreventDefault = evnt.preventDefault,
26037
- iframe = _jQuery('#application iframe')[0],
26038
- appWindow = iframe ? iframe.contentWindow : window,
26039
- fakeProcessDefault = true,
26040
- finalProcessDefault,
26041
- angular = appWindow.angular || {};
26042
-
26043
- // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
26044
- angular['ff-684208-preventDefault'] = false;
26045
- evnt.preventDefault = function() {
26046
- fakeProcessDefault = false;
26047
- return originalPreventDefault.apply(evnt, arguments);
26048
- };
26049
-
26050
- x = x || 0;
26051
- y = y || 0;
26052
- evnt.initMouseEvent(type, true, true, window, 0, x, y, x, y, pressed('ctrl'), pressed('alt'),
26053
- pressed('shift'), pressed('meta'), 0, element);
26054
-
26055
- element.dispatchEvent(evnt);
26056
- finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
26057
-
26058
- delete angular['ff-684208-preventDefault'];
26059
-
26060
- return finalProcessDefault;
26061
- }
26062
- }
26063
26529
 
26064
26530
  /**
26065
26531
  * Don't use the jQuery trigger method since it works incorrectly.
@@ -26073,7 +26539,7 @@ function browserTrigger(element, type, keys, x, y) {
26073
26539
  (function(fn){
26074
26540
  var parentTrigger = fn.trigger;
26075
26541
  fn.trigger = function(type) {
26076
- if (/(click|change|keydown|blur|input)/.test(type)) {
26542
+ if (/(click|change|keydown|blur|input|mousedown|mouseup)/.test(type)) {
26077
26543
  var processDefaults = [];
26078
26544
  this.each(function(index, node) {
26079
26545
  processDefaults.push(browserTrigger(node, type));
@@ -26162,6 +26628,121 @@ _jQuery.fn.bindings = function(windowJquery, bindExp) {
26162
26628
  return result;
26163
26629
  };
26164
26630
 
26631
+ (function() {
26632
+ var msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1], 10);
26633
+
26634
+ function indexOf(array, obj) {
26635
+ if (array.indexOf) return array.indexOf(obj);
26636
+
26637
+ for ( var i = 0; i < array.length; i++) {
26638
+ if (obj === array[i]) return i;
26639
+ }
26640
+ return -1;
26641
+ }
26642
+
26643
+
26644
+
26645
+ /**
26646
+ * Triggers a browser event. Attempts to choose the right event if one is
26647
+ * not specified.
26648
+ *
26649
+ * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement
26650
+ * @param {string} eventType Optional event type.
26651
+ * @param {Array.<string>=} keys Optional list of pressed keys
26652
+ * (valid values: 'alt', 'meta', 'shift', 'ctrl')
26653
+ * @param {number} x Optional x-coordinate for mouse/touch events.
26654
+ * @param {number} y Optional y-coordinate for mouse/touch events.
26655
+ */
26656
+ window.browserTrigger = function browserTrigger(element, eventType, keys, x, y) {
26657
+ if (element && !element.nodeName) element = element[0];
26658
+ if (!element) return;
26659
+
26660
+ var inputType = (element.type) ? element.type.toLowerCase() : null,
26661
+ nodeName = element.nodeName.toLowerCase();
26662
+
26663
+ if (!eventType) {
26664
+ eventType = {
26665
+ 'text': 'change',
26666
+ 'textarea': 'change',
26667
+ 'hidden': 'change',
26668
+ 'password': 'change',
26669
+ 'button': 'click',
26670
+ 'submit': 'click',
26671
+ 'reset': 'click',
26672
+ 'image': 'click',
26673
+ 'checkbox': 'click',
26674
+ 'radio': 'click',
26675
+ 'select-one': 'change',
26676
+ 'select-multiple': 'change',
26677
+ '_default_': 'click'
26678
+ }[inputType || '_default_'];
26679
+ }
26680
+
26681
+ if (nodeName == 'option') {
26682
+ element.parentNode.value = element.value;
26683
+ element = element.parentNode;
26684
+ eventType = 'change';
26685
+ }
26686
+
26687
+ keys = keys || [];
26688
+ function pressed(key) {
26689
+ return indexOf(keys, key) !== -1;
26690
+ }
26691
+
26692
+ if (msie < 9) {
26693
+ if (inputType == 'radio' || inputType == 'checkbox') {
26694
+ element.checked = !element.checked;
26695
+ }
26696
+
26697
+ // WTF!!! Error: Unspecified error.
26698
+ // Don't know why, but some elements when detached seem to be in inconsistent state and
26699
+ // calling .fireEvent() on them will result in very unhelpful error (Error: Unspecified error)
26700
+ // forcing the browser to compute the element position (by reading its CSS)
26701
+ // puts the element in consistent state.
26702
+ element.style.posLeft;
26703
+
26704
+ // TODO(vojta): create event objects with pressed keys to get it working on IE<9
26705
+ var ret = element.fireEvent('on' + eventType);
26706
+ if (inputType == 'submit') {
26707
+ while(element) {
26708
+ if (element.nodeName.toLowerCase() == 'form') {
26709
+ element.fireEvent('onsubmit');
26710
+ break;
26711
+ }
26712
+ element = element.parentNode;
26713
+ }
26714
+ }
26715
+ return ret;
26716
+ } else {
26717
+ var evnt = document.createEvent('MouseEvents'),
26718
+ originalPreventDefault = evnt.preventDefault,
26719
+ appWindow = element.ownerDocument.defaultView,
26720
+ fakeProcessDefault = true,
26721
+ finalProcessDefault,
26722
+ angular = appWindow.angular || {};
26723
+
26724
+ // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
26725
+ angular['ff-684208-preventDefault'] = false;
26726
+ evnt.preventDefault = function() {
26727
+ fakeProcessDefault = false;
26728
+ return originalPreventDefault.apply(evnt, arguments);
26729
+ };
26730
+
26731
+ x = x || 0;
26732
+ y = y || 0;
26733
+ evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'), pressed('alt'),
26734
+ pressed('shift'), pressed('meta'), 0, element);
26735
+
26736
+ element.dispatchEvent(evnt);
26737
+ finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
26738
+
26739
+ delete angular['ff-684208-preventDefault'];
26740
+
26741
+ return finalProcessDefault;
26742
+ }
26743
+ }
26744
+ }());
26745
+
26165
26746
  /**
26166
26747
  * Represents the application currently being tested and abstracts usage
26167
26748
  * of iframes or separate windows.
@@ -26225,8 +26806,6 @@ angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorF
26225
26806
  self.context.find('#test-frames').append('<iframe>');
26226
26807
  frame = self.getFrame_();
26227
26808
 
26228
- frame[0].contentWindow.name = "NG_DEFER_BOOTSTRAP!";
26229
-
26230
26809
  frame.load(function() {
26231
26810
  frame.unbind();
26232
26811
  try {
@@ -26239,7 +26818,8 @@ angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorF
26239
26818
  // we don't need that for our tests, but it should be done
26240
26819
  $window.angular.resumeBootstrap([['$provide', function($provide) {
26241
26820
  $provide.decorator('$sniffer', function($delegate) {
26242
- $delegate.supportsTransitions = false;
26821
+ $delegate.transitions = false;
26822
+ $delegate.animations = false;
26243
26823
  return $delegate;
26244
26824
  });
26245
26825
  }]]);
@@ -26250,6 +26830,9 @@ angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorF
26250
26830
  errorFn(e);
26251
26831
  }
26252
26832
  }).attr('src', url);
26833
+
26834
+ // for IE compatibility set the name *after* setting the frame url
26835
+ frame[0].contentWindow.name = "NG_DEFER_BOOTSTRAP!";
26253
26836
  }
26254
26837
  self.context.find('> h2 a').attr('href', url).text(url);
26255
26838
  };
@@ -27452,6 +28035,8 @@ angular.scenario.dsl('select', function() {
27452
28035
  * element(selector, label).count() get the number of elements that match selector
27453
28036
  * element(selector, label).click() clicks an element
27454
28037
  * element(selector, label).mouseover() mouseover an element
28038
+ * element(selector, label).mousedown() mousedown an element
28039
+ * element(selector, label).mouseup() mouseup an element
27455
28040
  * element(selector, label).query(fn) executes fn(selectedElements, done)
27456
28041
  * element(selector, label).{method}() gets the value (as defined by jQuery, ex. val)
27457
28042
  * element(selector, label).{method}(value) sets the value (as defined by jQuery, ex. val)
@@ -27516,6 +28101,22 @@ angular.scenario.dsl('element', function() {
27516
28101
  });
27517
28102
  };
27518
28103
 
28104
+ chain.mousedown = function() {
28105
+ return this.addFutureAction("element '" + this.label + "' mousedown", function($window, $document, done) {
28106
+ var elements = $document.elements();
28107
+ elements.trigger('mousedown');
28108
+ done();
28109
+ });
28110
+ };
28111
+
28112
+ chain.mouseup = function() {
28113
+ return this.addFutureAction("element '" + this.label + "' mouseup", function($window, $document, done) {
28114
+ var elements = $document.elements();
28115
+ elements.trigger('mouseup');
28116
+ done();
28117
+ });
28118
+ };
28119
+
27519
28120
  chain.query = function(fn) {
27520
28121
  return this.addFutureAction('element ' + this.label + ' custom query', function($window, $document, done) {
27521
28122
  fn.call(this, $document.elements(), done);