angularjs-rails 1.0.6.2 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.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);