angularjs-rails 1.2.25 → 1.2.26

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.3.0-rc.3
2
+ * @license AngularJS v1.3.0-rc.5
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.0-rc.3
2
+ * @license AngularJS v1.3.0-rc.5
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.0-rc.3
2
+ * @license AngularJS v1.3.0-rc.5
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -72,7 +72,7 @@ function minErr(module, ErrorConstructor) {
72
72
  return match;
73
73
  });
74
74
 
75
- message = message + '\nhttp://errors.angularjs.org/1.3.0-rc.3/' +
75
+ message = message + '\nhttp://errors.angularjs.org/1.3.0-rc.5/' +
76
76
  (module ? module + '/' : '') + code;
77
77
  for (i = 2; i < arguments.length; i++) {
78
78
  message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.0-rc.3
2
+ * @license AngularJS v1.3.0-rc.5
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.0-rc.3
2
+ * @license AngularJS v1.3.0-rc.5
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -53,9 +53,10 @@ angular.mock.$Browser = function() {
53
53
  self.onUrlChange = function(listener) {
54
54
  self.pollFns.push(
55
55
  function() {
56
- if (self.$$lastUrl != self.$$url) {
56
+ if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) {
57
57
  self.$$lastUrl = self.$$url;
58
- listener(self.$$url);
58
+ self.$$lastState = self.$$state;
59
+ listener(self.$$url, self.$$state);
59
60
  }
60
61
  }
61
62
  );
@@ -151,15 +152,24 @@ angular.mock.$Browser.prototype = {
151
152
  return pollFn;
152
153
  },
153
154
 
154
- url: function(url, replace) {
155
+ url: function(url, replace, state) {
156
+ if (angular.isUndefined(state)) {
157
+ state = null;
158
+ }
155
159
  if (url) {
156
160
  this.$$url = url;
161
+ // Native pushState serializes & copies the object; simulate it.
162
+ this.$$state = angular.copy(state);
157
163
  return this;
158
164
  }
159
165
 
160
166
  return this.$$url;
161
167
  },
162
168
 
169
+ state: function() {
170
+ return this.$$state;
171
+ },
172
+
163
173
  cookies: function(name, value) {
164
174
  if (name) {
165
175
  if (angular.isUndefined(value)) {
@@ -806,6 +816,7 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
806
816
  animate.queue.push({
807
817
  event : method,
808
818
  element : arguments[0],
819
+ options : arguments[arguments.length-1],
809
820
  args : arguments
810
821
  });
811
822
  return $delegate[method].apply($delegate, arguments);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.0-rc.3
2
+ * @license AngularJS v1.3.0-rc.5
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.0-rc.3
2
+ * @license AngularJS v1.3.0-rc.5
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -380,6 +380,10 @@ function $RouteProvider(){
380
380
  * defined in `resolve` route property. Once all of the dependencies are resolved
381
381
  * `$routeChangeSuccess` is fired.
382
382
  *
383
+ * The route change (and the `$location` change that triggered it) can be prevented
384
+ * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
385
+ * for more details about event object.
386
+ *
383
387
  * @param {Object} angularEvent Synthetic event object.
384
388
  * @param {Route} next Future route information.
385
389
  * @param {Route} current Current route information.
@@ -424,6 +428,8 @@ function $RouteProvider(){
424
428
  */
425
429
 
426
430
  var forceReload = false,
431
+ preparedRoute,
432
+ preparedRouteIsUpdateOnly,
427
433
  $route = {
428
434
  routes: routes,
429
435
 
@@ -440,7 +446,11 @@ function $RouteProvider(){
440
446
  */
441
447
  reload: function() {
442
448
  forceReload = true;
443
- $rootScope.$evalAsync(updateRoute);
449
+ $rootScope.$evalAsync(function() {
450
+ // Don't support cancellation of a reload for now...
451
+ prepareRoute();
452
+ commitRoute();
453
+ });
444
454
  },
445
455
 
446
456
  /**
@@ -474,7 +484,8 @@ function $RouteProvider(){
474
484
  }
475
485
  };
476
486
 
477
- $rootScope.$on('$locationChangeSuccess', updateRoute);
487
+ $rootScope.$on('$locationChangeStart', prepareRoute);
488
+ $rootScope.$on('$locationChangeSuccess', commitRoute);
478
489
 
479
490
  return $route;
480
491
 
@@ -512,36 +523,50 @@ function $RouteProvider(){
512
523
  return params;
513
524
  }
514
525
 
515
- function updateRoute() {
516
- var next = parseRoute(),
517
- last = $route.current;
518
-
519
- if (next && last && next.$$route === last.$$route
520
- && angular.equals(next.pathParams, last.pathParams)
521
- && !next.reloadOnSearch && !forceReload) {
522
- last.params = next.params;
523
- angular.copy(last.params, $routeParams);
524
- $rootScope.$broadcast('$routeUpdate', last);
525
- } else if (next || last) {
526
+ function prepareRoute($locationEvent) {
527
+ var lastRoute = $route.current;
528
+
529
+ preparedRoute = parseRoute();
530
+ preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
531
+ && angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
532
+ && !preparedRoute.reloadOnSearch && !forceReload;
533
+
534
+ if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
535
+ if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
536
+ if ($locationEvent) {
537
+ $locationEvent.preventDefault();
538
+ }
539
+ }
540
+ }
541
+ }
542
+
543
+ function commitRoute() {
544
+ var lastRoute = $route.current;
545
+ var nextRoute = preparedRoute;
546
+
547
+ if (preparedRouteIsUpdateOnly) {
548
+ lastRoute.params = nextRoute.params;
549
+ angular.copy(lastRoute.params, $routeParams);
550
+ $rootScope.$broadcast('$routeUpdate', lastRoute);
551
+ } else if (nextRoute || lastRoute) {
526
552
  forceReload = false;
527
- $rootScope.$broadcast('$routeChangeStart', next, last);
528
- $route.current = next;
529
- if (next) {
530
- if (next.redirectTo) {
531
- if (angular.isString(next.redirectTo)) {
532
- $location.path(interpolate(next.redirectTo, next.params)).search(next.params)
553
+ $route.current = nextRoute;
554
+ if (nextRoute) {
555
+ if (nextRoute.redirectTo) {
556
+ if (angular.isString(nextRoute.redirectTo)) {
557
+ $location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
533
558
  .replace();
534
559
  } else {
535
- $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
560
+ $location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
536
561
  .replace();
537
562
  }
538
563
  }
539
564
  }
540
565
 
541
- $q.when(next).
566
+ $q.when(nextRoute).
542
567
  then(function() {
543
- if (next) {
544
- var locals = angular.extend({}, next.resolve),
568
+ if (nextRoute) {
569
+ var locals = angular.extend({}, nextRoute.resolve),
545
570
  template, templateUrl;
546
571
 
547
572
  angular.forEach(locals, function(value, key) {
@@ -549,17 +574,17 @@ function $RouteProvider(){
549
574
  $injector.get(value) : $injector.invoke(value, null, null, key);
550
575
  });
551
576
 
552
- if (angular.isDefined(template = next.template)) {
577
+ if (angular.isDefined(template = nextRoute.template)) {
553
578
  if (angular.isFunction(template)) {
554
- template = template(next.params);
579
+ template = template(nextRoute.params);
555
580
  }
556
- } else if (angular.isDefined(templateUrl = next.templateUrl)) {
581
+ } else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
557
582
  if (angular.isFunction(templateUrl)) {
558
- templateUrl = templateUrl(next.params);
583
+ templateUrl = templateUrl(nextRoute.params);
559
584
  }
560
585
  templateUrl = $sce.getTrustedResourceUrl(templateUrl);
561
586
  if (angular.isDefined(templateUrl)) {
562
- next.loadedTemplateUrl = templateUrl;
587
+ nextRoute.loadedTemplateUrl = templateUrl;
563
588
  template = $templateRequest(templateUrl);
564
589
  }
565
590
  }
@@ -571,16 +596,16 @@ function $RouteProvider(){
571
596
  }).
572
597
  // after route change
573
598
  then(function(locals) {
574
- if (next == $route.current) {
575
- if (next) {
576
- next.locals = locals;
577
- angular.copy(next.params, $routeParams);
599
+ if (nextRoute == $route.current) {
600
+ if (nextRoute) {
601
+ nextRoute.locals = locals;
602
+ angular.copy(nextRoute.params, $routeParams);
578
603
  }
579
- $rootScope.$broadcast('$routeChangeSuccess', next, last);
604
+ $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
580
605
  }
581
606
  }, function(error) {
582
- if (next == $route.current) {
583
- $rootScope.$broadcast('$routeChangeError', next, last, error);
607
+ if (nextRoute == $route.current) {
608
+ $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
584
609
  }
585
610
  });
586
611
  }
@@ -854,7 +879,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
854
879
  link: function(scope, $element, attr, ctrl, $transclude) {
855
880
  var currentScope,
856
881
  currentElement,
857
- previousElement,
882
+ previousLeaveAnimation,
858
883
  autoScrollExp = attr.autoscroll,
859
884
  onloadExp = attr.onload || '';
860
885
 
@@ -862,19 +887,20 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
862
887
  update();
863
888
 
864
889
  function cleanupLastView() {
865
- if(previousElement) {
866
- previousElement.remove();
867
- previousElement = null;
890
+ if(previousLeaveAnimation) {
891
+ $animate.cancel(previousLeaveAnimation);
892
+ previousLeaveAnimation = null;
868
893
  }
894
+
869
895
  if(currentScope) {
870
896
  currentScope.$destroy();
871
897
  currentScope = null;
872
898
  }
873
899
  if(currentElement) {
874
- $animate.leave(currentElement).then(function() {
875
- previousElement = null;
900
+ previousLeaveAnimation = $animate.leave(currentElement);
901
+ previousLeaveAnimation.then(function() {
902
+ previousLeaveAnimation = null;
876
903
  });
877
- previousElement = currentElement;
878
904
  currentElement = null;
879
905
  }
880
906
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.0-rc.3
2
+ * @license AngularJS v1.3.0-rc.5
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -9190,7 +9190,7 @@ return jQuery;
9190
9190
  }));
9191
9191
 
9192
9192
  /**
9193
- * @license AngularJS v1.3.0-rc.3
9193
+ * @license AngularJS v1.3.0-rc.5
9194
9194
  * (c) 2010-2014 Google, Inc. http://angularjs.org
9195
9195
  * License: MIT
9196
9196
  */
@@ -9263,7 +9263,7 @@ function minErr(module, ErrorConstructor) {
9263
9263
  return match;
9264
9264
  });
9265
9265
 
9266
- message = message + '\nhttp://errors.angularjs.org/1.3.0-rc.3/' +
9266
+ message = message + '\nhttp://errors.angularjs.org/1.3.0-rc.5/' +
9267
9267
  (module ? module + '/' : '') + code;
9268
9268
  for (i = 2; i < arguments.length; i++) {
9269
9269
  message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
@@ -9279,6 +9279,7 @@ function minErr(module, ErrorConstructor) {
9279
9279
  jqLite: true,
9280
9280
  jQuery: true,
9281
9281
  slice: true,
9282
+ splice: true,
9282
9283
  push: true,
9283
9284
  toString: true,
9284
9285
  ngMinErr: true,
@@ -9355,6 +9356,12 @@ function minErr(module, ErrorConstructor) {
9355
9356
  getBlockNodes: true,
9356
9357
  hasOwnProperty: true,
9357
9358
  createMap: true,
9359
+
9360
+ NODE_TYPE_ELEMENT: true,
9361
+ NODE_TYPE_TEXT: true,
9362
+ NODE_TYPE_COMMENT: true,
9363
+ NODE_TYPE_DOCUMENT: true,
9364
+ NODE_TYPE_DOCUMENT_FRAGMENT: true,
9358
9365
  */
9359
9366
 
9360
9367
  ////////////////////////////////////
@@ -9434,6 +9441,7 @@ var /** holds major version number for IE or NaN for real browsers */
9434
9441
  jqLite, // delay binding since jQuery could be loaded after us.
9435
9442
  jQuery, // delay binding
9436
9443
  slice = [].slice,
9444
+ splice = [].splice,
9437
9445
  push = [].push,
9438
9446
  toString = Object.prototype.toString,
9439
9447
  ngMinErr = minErr('ng'),
@@ -9444,13 +9452,10 @@ var /** holds major version number for IE or NaN for real browsers */
9444
9452
  uid = 0;
9445
9453
 
9446
9454
  /**
9447
- * IE 11 changed the format of the UserAgent string.
9448
- * See http://msdn.microsoft.com/en-us/library/ms537503.aspx
9455
+ * documentMode is an IE-only property
9456
+ * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
9449
9457
  */
9450
- msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
9451
- if (isNaN(msie)) {
9452
- msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
9453
- }
9458
+ msie = document.documentMode;
9454
9459
 
9455
9460
 
9456
9461
  /**
@@ -9466,7 +9471,7 @@ function isArrayLike(obj) {
9466
9471
 
9467
9472
  var length = obj.length;
9468
9473
 
9469
- if (obj.nodeType === 1 && length) {
9474
+ if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
9470
9475
  return true;
9471
9476
  }
9472
9477
 
@@ -10302,11 +10307,9 @@ function startingTag(element) {
10302
10307
  // are not allowed to have children. So we just ignore it.
10303
10308
  element.empty();
10304
10309
  } catch(e) {}
10305
- // As Per DOM Standards
10306
- var TEXT_NODE = 3;
10307
10310
  var elemHtml = jqLite('<div>').append(element).html();
10308
10311
  try {
10309
- return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
10312
+ return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
10310
10313
  elemHtml.
10311
10314
  match(/^(<[^>]+>)/)[1].
10312
10315
  replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
@@ -10885,6 +10888,12 @@ function createMap() {
10885
10888
  return Object.create(null);
10886
10889
  }
10887
10890
 
10891
+ var NODE_TYPE_ELEMENT = 1;
10892
+ var NODE_TYPE_TEXT = 3;
10893
+ var NODE_TYPE_COMMENT = 8;
10894
+ var NODE_TYPE_DOCUMENT = 9;
10895
+ var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
10896
+
10888
10897
  /**
10889
10898
  * @ngdoc type
10890
10899
  * @name angular.Module
@@ -11304,11 +11313,11 @@ function setupModuleLoader(window) {
11304
11313
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
11305
11314
  */
11306
11315
  var version = {
11307
- full: '1.3.0-rc.3', // all of these placeholder strings will be replaced by grunt's
11316
+ full: '1.3.0-rc.5', // all of these placeholder strings will be replaced by grunt's
11308
11317
  major: 1, // package task
11309
11318
  minor: 3,
11310
11319
  dot: 0,
11311
- codeName: 'aggressive-pacifism'
11320
+ codeName: 'impossible-choreography'
11312
11321
  };
11313
11322
 
11314
11323
 
@@ -11342,8 +11351,7 @@ function publishExternalAPI(angular){
11342
11351
  'getTestability': getTestability,
11343
11352
  '$$minErr': minErr,
11344
11353
  '$$csp': csp,
11345
- 'reloadWithDebugInfo': reloadWithDebugInfo,
11346
- '$$hasClass': jqLiteHasClass
11354
+ 'reloadWithDebugInfo': reloadWithDebugInfo
11347
11355
  });
11348
11356
 
11349
11357
  angularModule = setupModuleLoader(window);
@@ -11489,7 +11497,7 @@ function publishExternalAPI(angular){
11489
11497
  * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
11490
11498
  * - [`clone()`](http://api.jquery.com/clone/)
11491
11499
  * - [`contents()`](http://api.jquery.com/contents/)
11492
- * - [`css()`](http://api.jquery.com/css/)
11500
+ * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyles()`
11493
11501
  * - [`data()`](http://api.jquery.com/data/)
11494
11502
  * - [`detach()`](http://api.jquery.com/detach/)
11495
11503
  * - [`empty()`](http://api.jquery.com/empty/)
@@ -11611,7 +11619,7 @@ function jqLiteAcceptsData(node) {
11611
11619
  // The window object can accept data but has no nodeType
11612
11620
  // Otherwise we are only interested in elements (1) and documents (9)
11613
11621
  var nodeType = node.nodeType;
11614
- return nodeType === 1 || !nodeType || nodeType === 9;
11622
+ return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
11615
11623
  }
11616
11624
 
11617
11625
  function jqLiteBuildFragment(html, context) {
@@ -11864,7 +11872,7 @@ function jqLiteController(element, name) {
11864
11872
  function jqLiteInheritedData(element, name, value) {
11865
11873
  // if element is the document object work with the html element instead
11866
11874
  // this makes $(document).scope() possible
11867
- if(element.nodeType == 9) {
11875
+ if(element.nodeType == NODE_TYPE_DOCUMENT) {
11868
11876
  element = element.documentElement;
11869
11877
  }
11870
11878
  var names = isArray(name) ? name : [name];
@@ -11877,7 +11885,7 @@ function jqLiteInheritedData(element, name, value) {
11877
11885
  // If dealing with a document fragment node with a host element, and no parent, use the host
11878
11886
  // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
11879
11887
  // to lookup parent controllers.
11880
- element = element.parentNode || (element.nodeType === 11 && element.host);
11888
+ element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
11881
11889
  }
11882
11890
  }
11883
11891
 
@@ -12055,7 +12063,7 @@ forEach({
12055
12063
  function getText(element, value) {
12056
12064
  if (isUndefined(value)) {
12057
12065
  var nodeType = element.nodeType;
12058
- return (nodeType === 1 || nodeType === 3) ? element.textContent : '';
12066
+ return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
12059
12067
  }
12060
12068
  element.textContent = value;
12061
12069
  }
@@ -12277,7 +12285,7 @@ forEach({
12277
12285
  children: function(element) {
12278
12286
  var children = [];
12279
12287
  forEach(element.childNodes, function(element){
12280
- if (element.nodeType === 1)
12288
+ if (element.nodeType === NODE_TYPE_ELEMENT)
12281
12289
  children.push(element);
12282
12290
  });
12283
12291
  return children;
@@ -12289,7 +12297,7 @@ forEach({
12289
12297
 
12290
12298
  append: function(element, node) {
12291
12299
  var nodeType = element.nodeType;
12292
- if (nodeType !== 1 && nodeType !== 11) return;
12300
+ if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
12293
12301
 
12294
12302
  node = new JQLite(node);
12295
12303
 
@@ -12300,7 +12308,7 @@ forEach({
12300
12308
  },
12301
12309
 
12302
12310
  prepend: function(element, node) {
12303
- if (element.nodeType === 1) {
12311
+ if (element.nodeType === NODE_TYPE_ELEMENT) {
12304
12312
  var index = element.firstChild;
12305
12313
  forEach(new JQLite(node), function(child){
12306
12314
  element.insertBefore(child, index);
@@ -12351,7 +12359,7 @@ forEach({
12351
12359
 
12352
12360
  parent: function(element) {
12353
12361
  var parent = element.parentNode;
12354
- return parent && parent.nodeType !== 11 ? parent : null;
12362
+ return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
12355
12363
  },
12356
12364
 
12357
12365
  next: function(element) {
@@ -12510,13 +12518,13 @@ HashMap.prototype = {
12510
12518
  * @kind function
12511
12519
  *
12512
12520
  * @description
12513
- * Creates an injector function that can be used for retrieving services as well as for
12521
+ * Creates an injector object that can be used for retrieving services as well as for
12514
12522
  * dependency injection (see {@link guide/di dependency injection}).
12515
12523
  *
12516
12524
 
12517
12525
  * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
12518
12526
  * {@link angular.module}. The `ng` module must be explicitly added.
12519
- * @returns {function()} Injector function. See {@link auto.$injector $injector}.
12527
+ * @returns {function()} Injector object. See {@link auto.$injector $injector}.
12520
12528
  *
12521
12529
  * @example
12522
12530
  * Typical usage
@@ -12623,7 +12631,6 @@ function annotate(fn, strictDi, name) {
12623
12631
  /**
12624
12632
  * @ngdoc service
12625
12633
  * @name $injector
12626
- * @kind function
12627
12634
  *
12628
12635
  * @description
12629
12636
  *
@@ -12638,7 +12645,7 @@ function annotate(fn, strictDi, name) {
12638
12645
  * expect($injector.get('$injector')).toBe($injector);
12639
12646
  * expect($injector.invoke(function($injector) {
12640
12647
  * return $injector;
12641
- * }).toBe($injector);
12648
+ * })).toBe($injector);
12642
12649
  * ```
12643
12650
  *
12644
12651
  * # Injection Function Annotation
@@ -12705,8 +12712,8 @@ function annotate(fn, strictDi, name) {
12705
12712
  * @description
12706
12713
  * Allows the user to query if the particular service exists.
12707
12714
  *
12708
- * @param {string} Name of the service to query.
12709
- * @returns {boolean} returns true if injector has given service.
12715
+ * @param {string} name Name of the service to query.
12716
+ * @returns {boolean} `true` if injector has given service.
12710
12717
  */
12711
12718
 
12712
12719
  /**
@@ -13166,7 +13173,21 @@ function createInjector(modulesToLoad, strictDi) {
13166
13173
  return providerCache[name + providerSuffix] = provider_;
13167
13174
  }
13168
13175
 
13169
- function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
13176
+ function enforceReturnValue(name, factory) {
13177
+ return function enforcedReturnValue() {
13178
+ var result = instanceInjector.invoke(factory);
13179
+ if (isUndefined(result)) {
13180
+ throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
13181
+ }
13182
+ return result;
13183
+ };
13184
+ }
13185
+
13186
+ function factory(name, factoryFn, enforce) {
13187
+ return provider(name, {
13188
+ $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
13189
+ });
13190
+ }
13170
13191
 
13171
13192
  function service(name, constructor) {
13172
13193
  return factory(name, ['$injector', function($injector) {
@@ -13174,7 +13195,7 @@ function createInjector(modulesToLoad, strictDi) {
13174
13195
  }]);
13175
13196
  }
13176
13197
 
13177
- function value(name, val) { return factory(name, valueFn(val)); }
13198
+ function value(name, val) { return factory(name, valueFn(val), false); }
13178
13199
 
13179
13200
  function constant(name, value) {
13180
13201
  assertNotHasOwnProperty(name, 'constant');
@@ -13425,7 +13446,10 @@ function $AnchorScrollProvider() {
13425
13446
  // (no url change, no $location.hash() change), browser native does scroll
13426
13447
  if (autoScrollingEnabled) {
13427
13448
  $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
13428
- function autoScrollWatchAction() {
13449
+ function autoScrollWatchAction(newVal, oldVal) {
13450
+ // skip the initial scroll if $location.hash is empty
13451
+ if (newVal === oldVal && newVal === '') return;
13452
+
13429
13453
  $rootScope.$evalAsync(scroll);
13430
13454
  });
13431
13455
  }
@@ -13515,9 +13539,57 @@ var $AnimateProvider = ['$provide', function($provide) {
13515
13539
  return this.$$classNameFilter;
13516
13540
  };
13517
13541
 
13518
- this.$get = ['$$q', '$$asyncCallback', function($$q, $$asyncCallback) {
13542
+ this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) {
13519
13543
 
13520
13544
  var currentDefer;
13545
+
13546
+ function runAnimationPostDigest(fn) {
13547
+ var cancelFn, defer = $$q.defer();
13548
+ defer.promise.$$cancelFn = function ngAnimateMaybeCancel() {
13549
+ cancelFn && cancelFn();
13550
+ };
13551
+
13552
+ $rootScope.$$postDigest(function ngAnimatePostDigest() {
13553
+ cancelFn = fn(function ngAnimateNotifyComplete() {
13554
+ defer.resolve();
13555
+ });
13556
+ });
13557
+
13558
+ return defer.promise;
13559
+ }
13560
+
13561
+ function resolveElementClasses(element, cache) {
13562
+ var toAdd = [], toRemove = [];
13563
+
13564
+ var hasClasses = createMap();
13565
+ forEach((element.attr('class') || '').split(/\s+/), function(className) {
13566
+ hasClasses[className] = true;
13567
+ });
13568
+
13569
+ forEach(cache.classes, function(status, className) {
13570
+ var hasClass = hasClasses[className];
13571
+
13572
+ // If the most recent class manipulation (via $animate) was to remove the class, and the
13573
+ // element currently has the class, the class is scheduled for removal. Otherwise, if
13574
+ // the most recent class manipulation (via $animate) was to add the class, and the
13575
+ // element does not currently have the class, the class is scheduled to be added.
13576
+ if (status === false && hasClass) {
13577
+ toRemove.push(className);
13578
+ } else if (status === true && !hasClass) {
13579
+ toAdd.push(className);
13580
+ }
13581
+ });
13582
+
13583
+ return (toAdd.length + toRemove.length) > 0 && [toAdd.length && toAdd, toRemove.length && toRemove];
13584
+ }
13585
+
13586
+ function cachedClassManipulation(cache, classes, op) {
13587
+ for (var i=0, ii = classes.length; i < ii; ++i) {
13588
+ var className = classes[i];
13589
+ cache[className] = op;
13590
+ }
13591
+ }
13592
+
13521
13593
  function asyncPromise() {
13522
13594
  // only serve one instance of a promise in order to save CPU cycles
13523
13595
  if (!currentDefer) {
@@ -13621,13 +13693,17 @@ var $AnimateProvider = ['$provide', function($provide) {
13621
13693
  * @return {Promise} the animation callback promise
13622
13694
  */
13623
13695
  addClass : function(element, className) {
13696
+ return this.setClass(element, className, []);
13697
+ },
13698
+
13699
+ $$addClassImmediately : function addClassImmediately(element, className) {
13700
+ element = jqLite(element);
13624
13701
  className = !isString(className)
13625
13702
  ? (isArray(className) ? className.join(' ') : '')
13626
13703
  : className;
13627
13704
  forEach(element, function (element) {
13628
13705
  jqLiteAddClass(element, className);
13629
13706
  });
13630
- return asyncPromise();
13631
13707
  },
13632
13708
 
13633
13709
  /**
@@ -13643,6 +13719,11 @@ var $AnimateProvider = ['$provide', function($provide) {
13643
13719
  * @return {Promise} the animation callback promise
13644
13720
  */
13645
13721
  removeClass : function(element, className) {
13722
+ return this.setClass(element, [], className);
13723
+ },
13724
+
13725
+ $$removeClassImmediately : function removeClassImmediately(element, className) {
13726
+ element = jqLite(element);
13646
13727
  className = !isString(className)
13647
13728
  ? (isArray(className) ? className.join(' ') : '')
13648
13729
  : className;
@@ -13665,10 +13746,53 @@ var $AnimateProvider = ['$provide', function($provide) {
13665
13746
  * @param {string} remove the CSS class which will be removed from the element
13666
13747
  * @return {Promise} the animation callback promise
13667
13748
  */
13668
- setClass : function(element, add, remove) {
13669
- this.addClass(element, add);
13670
- this.removeClass(element, remove);
13671
- return asyncPromise();
13749
+ setClass : function(element, add, remove, runSynchronously) {
13750
+ var self = this;
13751
+ var STORAGE_KEY = '$$animateClasses';
13752
+ var createdCache = false;
13753
+ element = jqLite(element);
13754
+
13755
+ if (runSynchronously) {
13756
+ // TODO(@caitp/@matsko): Remove undocumented `runSynchronously` parameter, and always
13757
+ // perform DOM manipulation asynchronously or in postDigest.
13758
+ self.$$addClassImmediately(element, add);
13759
+ self.$$removeClassImmediately(element, remove);
13760
+ return asyncPromise();
13761
+ }
13762
+
13763
+ var cache = element.data(STORAGE_KEY);
13764
+ if (!cache) {
13765
+ cache = {
13766
+ classes: {}
13767
+ };
13768
+ createdCache = true;
13769
+ }
13770
+
13771
+ var classes = cache.classes;
13772
+
13773
+ add = isArray(add) ? add : add.split(' ');
13774
+ remove = isArray(remove) ? remove : remove.split(' ');
13775
+ cachedClassManipulation(classes, add, true);
13776
+ cachedClassManipulation(classes, remove, false);
13777
+
13778
+ if (createdCache) {
13779
+ cache.promise = runAnimationPostDigest(function(done) {
13780
+ var cache = element.data(STORAGE_KEY);
13781
+ element.removeData(STORAGE_KEY);
13782
+
13783
+ var classes = cache && resolveElementClasses(element, cache);
13784
+
13785
+ if (classes) {
13786
+ if (classes[0]) self.$$addClassImmediately(element, classes[0]);
13787
+ if (classes[1]) self.$$removeClassImmediately(element, classes[1]);
13788
+ }
13789
+
13790
+ done();
13791
+ });
13792
+ element.data(STORAGE_KEY, cache);
13793
+ }
13794
+
13795
+ return cache.promise;
13672
13796
  },
13673
13797
 
13674
13798
  enabled : noop,
@@ -13687,6 +13811,8 @@ function $$AsyncCallbackProvider(){
13687
13811
  }];
13688
13812
  }
13689
13813
 
13814
+ /* global stripHash: true */
13815
+
13690
13816
  /**
13691
13817
  * ! This is a private undocumented service !
13692
13818
  *
@@ -13810,8 +13936,9 @@ function Browser(window, document, $log, $sniffer) {
13810
13936
  //////////////////////////////////////////////////////////////
13811
13937
 
13812
13938
  var lastBrowserUrl = location.href,
13939
+ lastHistoryState = history.state,
13813
13940
  baseElement = document.find('base'),
13814
- newLocation = null;
13941
+ reloadLocation = null;
13815
13942
 
13816
13943
  /**
13817
13944
  * @name $browser#url
@@ -13830,26 +13957,42 @@ function Browser(window, document, $log, $sniffer) {
13830
13957
  * {@link ng.$location $location service} to change url.
13831
13958
  *
13832
13959
  * @param {string} url New url (when used as setter)
13833
- * @param {boolean=} replace Should new url replace current history record ?
13960
+ * @param {boolean=} replace Should new url replace current history record?
13961
+ * @param {object=} state object to use with pushState/replaceState
13834
13962
  */
13835
- self.url = function(url, replace) {
13963
+ self.url = function(url, replace, state) {
13964
+ // In modern browsers `history.state` is `null` by default; treating it separately
13965
+ // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
13966
+ // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
13967
+ if (isUndefined(state)) {
13968
+ state = null;
13969
+ }
13970
+
13836
13971
  // Android Browser BFCache causes location, history reference to become stale.
13837
13972
  if (location !== window.location) location = window.location;
13838
13973
  if (history !== window.history) history = window.history;
13839
13974
 
13840
13975
  // setter
13841
13976
  if (url) {
13842
- if (lastBrowserUrl == url) return;
13977
+ // Don't change anything if previous and current URLs and states match. This also prevents
13978
+ // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
13979
+ // See https://github.com/angular/angular.js/commit/ffb2701
13980
+ if (lastBrowserUrl === url && (!$sniffer.history || history.state === state)) {
13981
+ return;
13982
+ }
13983
+ var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
13843
13984
  lastBrowserUrl = url;
13844
- if ($sniffer.history) {
13845
- if (replace) history.replaceState(null, '', url);
13846
- else {
13847
- history.pushState(null, '', url);
13848
- // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462
13849
- baseElement.attr('href', baseElement.attr('href'));
13850
- }
13985
+ // Don't use history API if only the hash changed
13986
+ // due to a bug in IE10/IE11 which leads
13987
+ // to not firing a `hashchange` nor `popstate` event
13988
+ // in some cases (see #9143).
13989
+ if ($sniffer.history && (!sameBase || history.state !== state)) {
13990
+ history[replace ? 'replaceState' : 'pushState'](state, '', url);
13991
+ lastHistoryState = history.state;
13851
13992
  } else {
13852
- newLocation = url;
13993
+ if (!sameBase) {
13994
+ reloadLocation = url;
13995
+ }
13853
13996
  if (replace) {
13854
13997
  location.replace(url);
13855
13998
  } else {
@@ -13859,23 +14002,38 @@ function Browser(window, document, $log, $sniffer) {
13859
14002
  return self;
13860
14003
  // getter
13861
14004
  } else {
13862
- // - newLocation is a workaround for an IE7-9 issue with location.replace and location.href
13863
- // methods not updating location.href synchronously.
14005
+ // - reloadLocation is needed as browsers don't allow to read out
14006
+ // the new location.href if a reload happened.
13864
14007
  // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
13865
- return newLocation || location.href.replace(/%27/g,"'");
14008
+ return reloadLocation || location.href.replace(/%27/g,"'");
13866
14009
  }
13867
14010
  };
13868
14011
 
14012
+ /**
14013
+ * @name $browser#state
14014
+ *
14015
+ * @description
14016
+ * This method is a getter.
14017
+ *
14018
+ * Return history.state or null if history.state is undefined.
14019
+ *
14020
+ * @returns {object} state
14021
+ */
14022
+ self.state = function() {
14023
+ return isUndefined(history.state) ? null : history.state;
14024
+ };
14025
+
13869
14026
  var urlChangeListeners = [],
13870
14027
  urlChangeInit = false;
13871
14028
 
13872
14029
  function fireUrlChange() {
13873
- newLocation = null;
13874
- if (lastBrowserUrl == self.url()) return;
14030
+ if (lastBrowserUrl === self.url() && lastHistoryState === history.state) {
14031
+ return;
14032
+ }
13875
14033
 
13876
14034
  lastBrowserUrl = self.url();
13877
14035
  forEach(urlChangeListeners, function(listener) {
13878
- listener(self.url());
14036
+ listener(self.url(), history.state);
13879
14037
  });
13880
14038
  }
13881
14039
 
@@ -13910,9 +14068,7 @@ function Browser(window, document, $log, $sniffer) {
13910
14068
  // html5 history api - popstate event
13911
14069
  if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange);
13912
14070
  // hashchange event
13913
- if ($sniffer.hashchange) jqLite(window).on('hashchange', fireUrlChange);
13914
- // polling
13915
- else self.addPollFn(fireUrlChange);
14071
+ jqLite(window).on('hashchange', fireUrlChange);
13916
14072
 
13917
14073
  urlChangeInit = true;
13918
14074
  }
@@ -14687,8 +14843,11 @@ function $TemplateCacheProvider() {
14687
14843
  * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
14688
14844
  * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
14689
14845
  * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
14846
+ * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
14690
14847
  * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
14691
14848
  * `null` to the `link` fn if not found.
14849
+ * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
14850
+ * `null` to the `link` fn if not found.
14692
14851
  *
14693
14852
  *
14694
14853
  * #### `controllerAs`
@@ -14766,22 +14925,18 @@ function $TemplateCacheProvider() {
14766
14925
  * (because SVG doesn't work with custom elements in the DOM tree).
14767
14926
  *
14768
14927
  * #### `transclude`
14769
- * compile the content of the element and make it available to the directive.
14770
- * Typically used with {@link ng.directive:ngTransclude
14771
- * ngTransclude}. The advantage of transclusion is that the linking function receives a
14772
- * transclusion function which is pre-bound to the correct scope. In a typical setup the widget
14773
- * creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
14774
- * scope. This makes it possible for the widget to have private state, and the transclusion to
14775
- * be bound to the parent (pre-`isolate`) scope.
14928
+ * Extract the contents of the element where the directive appears and make it available to the directive.
14929
+ * The contents are compiled and provided to the directive as a **transclusion function**. See the
14930
+ * {@link $compile#transclusion Transclusion} section below.
14776
14931
  *
14777
- * * `true` - transclude the content of the directive.
14778
- * * `'element'` - transclude the whole element including any directives defined at lower priority.
14932
+ * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
14933
+ * directive's element or the entire element:
14934
+ *
14935
+ * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
14936
+ * * `'element'` - transclude the whole of the directive's element including any directives on this
14937
+ * element that defined at a lower priority than this directive. When used, the `template`
14938
+ * property is ignored.
14779
14939
  *
14780
- * <div class="alert alert-warning">
14781
- * **Note:** When testing an element transclude directive you must not place the directive at the root of the
14782
- * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
14783
- * Testing Transclusion Directives}.
14784
- * </div>
14785
14940
  *
14786
14941
  * #### `compile`
14787
14942
  *
@@ -14879,7 +15034,121 @@ function $TemplateCacheProvider() {
14879
15034
  * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
14880
15035
  * for their async templates to be resolved.
14881
15036
  *
14882
- * <a name="Attributes"></a>
15037
+ *
15038
+ * ### Transclusion
15039
+ *
15040
+ * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
15041
+ * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
15042
+ * scope from where they were taken.
15043
+ *
15044
+ * Transclusion is used (often with {@link ngTransclude}) to insert the
15045
+ * original contents of a directive's element into a specified place in the template of the directive.
15046
+ * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
15047
+ * content has access to the properties on the scope from which it was taken, even if the directive
15048
+ * has isolated scope.
15049
+ * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
15050
+ *
15051
+ * This makes it possible for the widget to have private state for its template, while the transcluded
15052
+ * content has access to its originating scope.
15053
+ *
15054
+ * <div class="alert alert-warning">
15055
+ * **Note:** When testing an element transclude directive you must not place the directive at the root of the
15056
+ * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
15057
+ * Testing Transclusion Directives}.
15058
+ * </div>
15059
+ *
15060
+ * #### Transclusion Functions
15061
+ *
15062
+ * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
15063
+ * function** to the directive's `link` function and `controller`. This transclusion function is a special
15064
+ * **linking function** that will return the compiled contents linked to a new transclusion scope.
15065
+ *
15066
+ * <div class="alert alert-info">
15067
+ * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
15068
+ * ngTransclude will deal with it for us.
15069
+ * </div>
15070
+ *
15071
+ * If you want to manually control the insertion and removal of the transcluded content in your directive
15072
+ * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
15073
+ * object that contains the compiled DOM, which is linked to the correct transclusion scope.
15074
+ *
15075
+ * When you call a transclusion function you can pass in a **clone attach function**. This function is accepts
15076
+ * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
15077
+ * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
15078
+ *
15079
+ * <div class="alert alert-info">
15080
+ * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
15081
+ * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
15082
+ * </div>
15083
+ *
15084
+ * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
15085
+ * attach function**:
15086
+ *
15087
+ * ```js
15088
+ * var transcludedContent, transclusionScope;
15089
+ *
15090
+ * $transclude(function(clone, scope) {
15091
+ * element.append(clone);
15092
+ * transcludedContent = clone;
15093
+ * transclusionScope = scope;
15094
+ * });
15095
+ * ```
15096
+ *
15097
+ * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
15098
+ * associated transclusion scope:
15099
+ *
15100
+ * ```js
15101
+ * transcludedContent.remove();
15102
+ * transclusionScope.$destroy();
15103
+ * ```
15104
+ *
15105
+ * <div class="alert alert-info">
15106
+ * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
15107
+ * (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it),
15108
+ * then you are also responsible for calling `$destroy` on the transclusion scope.
15109
+ * </div>
15110
+ *
15111
+ * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
15112
+ * automatically destroy their transluded clones as necessary so you do not need to worry about this if
15113
+ * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
15114
+ *
15115
+ *
15116
+ * #### Transclusion Scopes
15117
+ *
15118
+ * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
15119
+ * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
15120
+ * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
15121
+ * was taken.
15122
+ *
15123
+ * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
15124
+ * like this:
15125
+ *
15126
+ * ```html
15127
+ * <div ng-app>
15128
+ * <div isolate>
15129
+ * <div transclusion>
15130
+ * </div>
15131
+ * </div>
15132
+ * </div>
15133
+ * ```
15134
+ *
15135
+ * The `$parent` scope hierarchy will look like this:
15136
+ *
15137
+ * ```
15138
+ * - $rootScope
15139
+ * - isolate
15140
+ * - transclusion
15141
+ * ```
15142
+ *
15143
+ * but the scopes will inherit prototypically from different scopes to their `$parent`.
15144
+ *
15145
+ * ```
15146
+ * - $rootScope
15147
+ * - transclusion
15148
+ * - isolate
15149
+ * ```
15150
+ *
15151
+ *
14883
15152
  * ### Attributes
14884
15153
  *
14885
15154
  * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
@@ -14917,7 +15186,7 @@ function $TemplateCacheProvider() {
14917
15186
  * }
14918
15187
  * ```
14919
15188
  *
14920
- * Below is an example using `$compileProvider`.
15189
+ * ## Example
14921
15190
  *
14922
15191
  * <div class="alert alert-warning">
14923
15192
  * **Note**: Typically directives are registered with `module.directive`. The example below is
@@ -15042,7 +15311,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15042
15311
  Suffix = 'Directive',
15043
15312
  COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,
15044
15313
  CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/,
15045
- ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset');
15314
+ ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
15315
+ REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
15046
15316
 
15047
15317
  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
15048
15318
  // The assumption is that future DOM event attribute names will begin with
@@ -15347,10 +15617,44 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15347
15617
 
15348
15618
  nodeName = nodeName_(this.$$element);
15349
15619
 
15350
- // sanitize a[href] and img[src] values
15351
15620
  if ((nodeName === 'a' && key === 'href') ||
15352
15621
  (nodeName === 'img' && key === 'src')) {
15622
+ // sanitize a[href] and img[src] values
15353
15623
  this[key] = value = $$sanitizeUri(value, key === 'src');
15624
+ } else if (nodeName === 'img' && key === 'srcset') {
15625
+ // sanitize img[srcset] values
15626
+ var result = "";
15627
+
15628
+ // first check if there are spaces because it's not the same pattern
15629
+ var trimmedSrcset = trim(value);
15630
+ // ( 999x ,| 999w ,| ,|, )
15631
+ var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
15632
+ var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
15633
+
15634
+ // split srcset into tuple of uri and descriptor except for the last item
15635
+ var rawUris = trimmedSrcset.split(pattern);
15636
+
15637
+ // for each tuples
15638
+ var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
15639
+ for (var i=0; i<nbrUrisWith2parts; i++) {
15640
+ var innerIdx = i*2;
15641
+ // sanitize the uri
15642
+ result += $$sanitizeUri(trim( rawUris[innerIdx]), true);
15643
+ // add the descriptor
15644
+ result += ( " " + trim(rawUris[innerIdx+1]));
15645
+ }
15646
+
15647
+ // split the last item into uri and descriptor
15648
+ var lastTuple = trim(rawUris[i*2]).split(/\s/);
15649
+
15650
+ // sanitize the last uri
15651
+ result += $$sanitizeUri(trim(lastTuple[0]), true);
15652
+
15653
+ // and add the last descriptor if any
15654
+ if( lastTuple.length === 2) {
15655
+ result += (" " + trim(lastTuple[1]));
15656
+ }
15657
+ this[key] = value = result;
15354
15658
  }
15355
15659
 
15356
15660
  if (writeAttr !== false) {
@@ -15388,12 +15692,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15388
15692
  * @param {string} key Normalized key. (ie ngAttribute) .
15389
15693
  * @param {function(interpolatedValue)} fn Function that will be called whenever
15390
15694
  the interpolated value of the attribute changes.
15391
- * See the {@link guide/directive#Attributes Directives} guide for more info.
15695
+ * See {@link ng.$compile#attributes $compile} for more info.
15392
15696
  * @returns {function()} Returns a deregistration function for this observer.
15393
15697
  */
15394
15698
  $observe: function(key, fn) {
15395
15699
  var attrs = this,
15396
- $$observers = (attrs.$$observers || (attrs.$$observers = {})),
15700
+ $$observers = (attrs.$$observers || (attrs.$$observers = Object.create(null))),
15397
15701
  listeners = ($$observers[key] || ($$observers[key] = []));
15398
15702
 
15399
15703
  listeners.push(fn);
@@ -15469,7 +15773,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15469
15773
  // We can not compile top level text elements since text nodes can be merged and we will
15470
15774
  // not be able to attach scope data to them, so we will wrap them in <span>
15471
15775
  forEach($compileNodes, function(node, index){
15472
- if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
15776
+ if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
15473
15777
  $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
15474
15778
  }
15475
15779
  });
@@ -15478,27 +15782,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15478
15782
  maxPriority, ignoreDirective, previousCompileContext);
15479
15783
  compile.$$addScopeClass($compileNodes);
15480
15784
  var namespace = null;
15481
- var namespaceAdaptedCompileNodes = $compileNodes;
15482
- var lastCompileNode;
15483
15785
  return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn, futureParentElement){
15484
15786
  assertArg(scope, 'scope');
15485
15787
  if (!namespace) {
15486
15788
  namespace = detectNamespaceForChildElements(futureParentElement);
15487
15789
  }
15488
- if (namespace !== 'html' && $compileNodes[0] !== lastCompileNode) {
15489
- namespaceAdaptedCompileNodes = jqLite(
15790
+ var $linkNode;
15791
+ if (namespace !== 'html') {
15792
+ // When using a directive with replace:true and templateUrl the $compileNodes
15793
+ // (or a child element inside of them)
15794
+ // might change, so we need to recreate the namespace adapted compileNodes
15795
+ // for call to the link function.
15796
+ // Note: This will already clone the nodes...
15797
+ $linkNode = jqLite(
15490
15798
  wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
15491
15799
  );
15800
+ } else if (cloneConnectFn) {
15801
+ // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
15802
+ // and sometimes changes the structure of the DOM.
15803
+ $linkNode = JQLitePrototype.clone.call($compileNodes);
15804
+ } else {
15805
+ $linkNode = $compileNodes;
15492
15806
  }
15493
- // When using a directive with replace:true and templateUrl the $compileNodes
15494
- // might change, so we need to recreate the namespace adapted compileNodes.
15495
- lastCompileNode = $compileNodes[0];
15496
-
15497
- // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
15498
- // and sometimes changes the structure of the DOM.
15499
- var $linkNode = cloneConnectFn
15500
- ? JQLitePrototype.clone.call(namespaceAdaptedCompileNodes) // IMPORTANT!!!
15501
- : namespaceAdaptedCompileNodes;
15502
15807
 
15503
15808
  if (transcludeControllers) {
15504
15809
  for (var controllerName in transcludeControllers) {
@@ -15641,20 +15946,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15641
15946
 
15642
15947
  function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
15643
15948
 
15644
- var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement) {
15645
- var scopeCreated = false;
15949
+ var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
15646
15950
 
15647
15951
  if (!transcludedScope) {
15648
- transcludedScope = scope.$new();
15952
+ transcludedScope = scope.$new(false, containingScope);
15649
15953
  transcludedScope.$$transcluded = true;
15650
- scopeCreated = true;
15651
15954
  }
15652
15955
 
15653
- var clone = transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn, futureParentElement);
15654
- if (scopeCreated && !elementTransclusion) {
15655
- clone.on('$destroy', function() { transcludedScope.$destroy(); });
15656
- }
15657
- return clone;
15956
+ return transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn, futureParentElement);
15658
15957
  };
15659
15958
 
15660
15959
  return boundTranscludeFn;
@@ -15677,7 +15976,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15677
15976
  className;
15678
15977
 
15679
15978
  switch(nodeType) {
15680
- case 1: /* Element */
15979
+ case NODE_TYPE_ELEMENT: /* Element */
15681
15980
  // use the node name: <directive>
15682
15981
  addDirective(directives,
15683
15982
  directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
@@ -15689,37 +15988,35 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15689
15988
  var attrEndName = false;
15690
15989
 
15691
15990
  attr = nAttrs[j];
15692
- if (!msie || msie >= 8 || attr.specified) {
15693
- name = attr.name;
15694
- value = trim(attr.value);
15695
-
15696
- // support ngAttr attribute binding
15697
- ngAttrName = directiveNormalize(name);
15698
- if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
15699
- name = snake_case(ngAttrName.substr(6), '-');
15700
- }
15991
+ name = attr.name;
15992
+ value = trim(attr.value);
15701
15993
 
15702
- var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
15703
- if (directiveIsMultiElement(directiveNName)) {
15704
- if (ngAttrName === directiveNName + 'Start') {
15705
- attrStartName = name;
15706
- attrEndName = name.substr(0, name.length - 5) + 'end';
15707
- name = name.substr(0, name.length - 6);
15708
- }
15709
- }
15994
+ // support ngAttr attribute binding
15995
+ ngAttrName = directiveNormalize(name);
15996
+ if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
15997
+ name = snake_case(ngAttrName.substr(6), '-');
15998
+ }
15710
15999
 
15711
- nName = directiveNormalize(name.toLowerCase());
15712
- attrsMap[nName] = name;
15713
- if (isNgAttr || !attrs.hasOwnProperty(nName)) {
15714
- attrs[nName] = value;
15715
- if (getBooleanAttrName(node, nName)) {
15716
- attrs[nName] = true; // presence means true
15717
- }
16000
+ var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
16001
+ if (directiveIsMultiElement(directiveNName)) {
16002
+ if (ngAttrName === directiveNName + 'Start') {
16003
+ attrStartName = name;
16004
+ attrEndName = name.substr(0, name.length - 5) + 'end';
16005
+ name = name.substr(0, name.length - 6);
15718
16006
  }
15719
- addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
15720
- addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
15721
- attrEndName);
15722
16007
  }
16008
+
16009
+ nName = directiveNormalize(name.toLowerCase());
16010
+ attrsMap[nName] = name;
16011
+ if (isNgAttr || !attrs.hasOwnProperty(nName)) {
16012
+ attrs[nName] = value;
16013
+ if (getBooleanAttrName(node, nName)) {
16014
+ attrs[nName] = true; // presence means true
16015
+ }
16016
+ }
16017
+ addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
16018
+ addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
16019
+ attrEndName);
15723
16020
  }
15724
16021
 
15725
16022
  // use class as directive
@@ -15734,10 +16031,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15734
16031
  }
15735
16032
  }
15736
16033
  break;
15737
- case 3: /* Text Node */
16034
+ case NODE_TYPE_TEXT: /* Text Node */
15738
16035
  addTextInterpolateDirective(directives, node.nodeValue);
15739
16036
  break;
15740
- case 8: /* Comment */
16037
+ case NODE_TYPE_COMMENT: /* Comment */
15741
16038
  try {
15742
16039
  match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
15743
16040
  if (match) {
@@ -15777,7 +16074,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15777
16074
  "Unterminated attribute, found '{0}' but no matching '{1}' found.",
15778
16075
  attrStart, attrEnd);
15779
16076
  }
15780
- if (node.nodeType == 1 /** Element **/) {
16077
+ if (node.nodeType == NODE_TYPE_ELEMENT) {
15781
16078
  if (node.hasAttribute(attrStart)) depth++;
15782
16079
  if (node.hasAttribute(attrEnd)) depth--;
15783
16080
  }
@@ -15956,11 +16253,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15956
16253
  if (jqLiteIsTextNode(directiveValue)) {
15957
16254
  $template = [];
15958
16255
  } else {
15959
- $template = jqLite(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
16256
+ $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
15960
16257
  }
15961
16258
  compileNode = $template[0];
15962
16259
 
15963
- if ($template.length != 1 || compileNode.nodeType !== 1) {
16260
+ if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
15964
16261
  throw $compileMinErr('tplrt',
15965
16262
  "Template for directive '{0}' must have exactly one root element. {1}",
15966
16263
  directiveName, '');
@@ -16064,14 +16361,26 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16064
16361
 
16065
16362
  function getControllers(directiveName, require, $element, elementControllers) {
16066
16363
  var value, retrievalMethod = 'data', optional = false;
16364
+ var $searchElement = $element;
16365
+ var match;
16067
16366
  if (isString(require)) {
16068
- while((value = require.charAt(0)) == '^' || value == '?') {
16069
- require = require.substr(1);
16070
- if (value == '^') {
16071
- retrievalMethod = 'inheritedData';
16072
- }
16073
- optional = optional || value == '?';
16367
+ match = require.match(REQUIRE_PREFIX_REGEXP);
16368
+ require = require.substring(match[0].length);
16369
+
16370
+ if (match[3]) {
16371
+ if (match[1]) match[3] = null;
16372
+ else match[1] = match[3];
16074
16373
  }
16374
+ if (match[1] === '^') {
16375
+ retrievalMethod = 'inheritedData';
16376
+ } else if (match[1] === '^^') {
16377
+ retrievalMethod = 'inheritedData';
16378
+ $searchElement = $element.parent();
16379
+ }
16380
+ if (match[2] === '?') {
16381
+ optional = true;
16382
+ }
16383
+
16075
16384
  value = null;
16076
16385
 
16077
16386
  if (elementControllers && retrievalMethod === 'data') {
@@ -16079,7 +16388,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16079
16388
  value = value.instance;
16080
16389
  }
16081
16390
  }
16082
- value = value || $element[retrievalMethod]('$' + require + 'Controller');
16391
+ value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
16083
16392
 
16084
16393
  if (!value && !optional) {
16085
16394
  throw $compileMinErr('ctreq',
@@ -16285,7 +16594,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16285
16594
  if (!futureParentElement) {
16286
16595
  futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
16287
16596
  }
16288
- return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement);
16597
+ return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
16289
16598
  }
16290
16599
  }
16291
16600
  }
@@ -16426,11 +16735,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16426
16735
  if (jqLiteIsTextNode(content)) {
16427
16736
  $template = [];
16428
16737
  } else {
16429
- $template = jqLite(wrapTemplate(templateNamespace, trim(content)));
16738
+ $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
16430
16739
  }
16431
16740
  compileNode = $template[0];
16432
16741
 
16433
- if ($template.length != 1 || compileNode.nodeType !== 1) {
16742
+ if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
16434
16743
  throw $compileMinErr('tplrt',
16435
16744
  "Template for directive '{0}' must have exactly one root element. {1}",
16436
16745
  origAsyncDirective.name, templateUrl);
@@ -16469,6 +16778,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16469
16778
  boundTranscludeFn = linkQueue.shift(),
16470
16779
  linkNode = $compileNode[0];
16471
16780
 
16781
+ if (scope.$$destroyed) continue;
16782
+
16472
16783
  if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
16473
16784
  var oldClasses = beforeTemplateLinkNode.className;
16474
16785
 
@@ -16495,6 +16806,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16495
16806
 
16496
16807
  return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
16497
16808
  var childBoundTranscludeFn = boundTranscludeFn;
16809
+ if (scope.$$destroyed) return;
16498
16810
  if (linkQueue) {
16499
16811
  linkQueue.push(scope);
16500
16812
  linkQueue.push(node);
@@ -16611,6 +16923,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16611
16923
  "ng- versions (such as ng-click instead of onclick) instead.");
16612
16924
  }
16613
16925
 
16926
+ // If the attribute was removed, then we are done
16927
+ if (!attr[name]) {
16928
+ return;
16929
+ }
16930
+
16614
16931
  // we need to interpolate again, in case the attribute value has been updated
16615
16932
  // (e.g. by another directive's compile function)
16616
16933
  interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name),
@@ -16838,6 +17155,23 @@ function tokenDifference(str1, str2) {
16838
17155
  return values;
16839
17156
  }
16840
17157
 
17158
+ function removeComments(jqNodes) {
17159
+ jqNodes = jqLite(jqNodes);
17160
+ var i = jqNodes.length;
17161
+
17162
+ if (i <= 1) {
17163
+ return jqNodes;
17164
+ }
17165
+
17166
+ while (i--) {
17167
+ var node = jqNodes[i];
17168
+ if (node.nodeType === NODE_TYPE_COMMENT) {
17169
+ splice.call(jqNodes, i, 1);
17170
+ }
17171
+ }
17172
+ return jqNodes;
17173
+ }
17174
+
16841
17175
  /**
16842
17176
  * @ngdoc provider
16843
17177
  * @name $controllerProvider
@@ -17141,7 +17475,8 @@ function $HttpProvider() {
17141
17475
  var JSON_START = /^\s*(\[|\{[^\{])/,
17142
17476
  JSON_END = /[\}\]]\s*$/,
17143
17477
  PROTECTION_PREFIX = /^\)\]\}',?\n/,
17144
- CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'};
17478
+ APPLICATION_JSON = 'application/json',
17479
+ CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
17145
17480
 
17146
17481
  /**
17147
17482
  * @ngdoc property
@@ -17166,12 +17501,15 @@ function $HttpProvider() {
17166
17501
  **/
17167
17502
  var defaults = this.defaults = {
17168
17503
  // transform incoming response data
17169
- transformResponse: [function(data) {
17504
+ transformResponse: [function defaultHttpResponseTransform(data, headers) {
17170
17505
  if (isString(data)) {
17171
17506
  // strip json vulnerability protection prefix
17172
17507
  data = data.replace(PROTECTION_PREFIX, '');
17173
- if (JSON_START.test(data) && JSON_END.test(data))
17508
+ var contentType = headers('Content-Type');
17509
+ if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0) ||
17510
+ (JSON_START.test(data) && JSON_END.test(data))) {
17174
17511
  data = fromJson(data);
17512
+ }
17175
17513
  }
17176
17514
  return data;
17177
17515
  }],
@@ -18129,18 +18467,8 @@ function $HttpProvider() {
18129
18467
  }];
18130
18468
  }
18131
18469
 
18132
- function createXhr(method) {
18133
- //if IE and the method is not RFC2616 compliant, or if XMLHttpRequest
18134
- //is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest
18135
- //if it is available
18136
- if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) ||
18137
- !window.XMLHttpRequest)) {
18138
- return new window.ActiveXObject("Microsoft.XMLHTTP");
18139
- } else if (window.XMLHttpRequest) {
18140
- return new window.XMLHttpRequest();
18141
- }
18142
-
18143
- throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
18470
+ function createXhr() {
18471
+ return new window.XMLHttpRequest();
18144
18472
  }
18145
18473
 
18146
18474
  /**
@@ -18166,11 +18494,8 @@ function $HttpBackendProvider() {
18166
18494
  }
18167
18495
 
18168
18496
  function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
18169
- var ABORTED = -1;
18170
-
18171
18497
  // TODO(vojta): fix the signature
18172
18498
  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
18173
- var status;
18174
18499
  $browser.$$incOutstandingRequestCount();
18175
18500
  url = url || $browser.url();
18176
18501
 
@@ -18188,7 +18513,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
18188
18513
  });
18189
18514
  } else {
18190
18515
 
18191
- var xhr = createXhr(method);
18516
+ var xhr = createXhr();
18192
18517
 
18193
18518
  xhr.open(method, url, true);
18194
18519
  forEach(headers, function(value, key) {
@@ -18197,44 +18522,39 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
18197
18522
  }
18198
18523
  });
18199
18524
 
18200
- // In IE6 and 7, this might be called synchronously when xhr.send below is called and the
18201
- // response is in the cache. the promise api will ensure that to the app code the api is
18202
- // always async
18203
- xhr.onreadystatechange = function() {
18204
- // onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by
18205
- // xhrs that are resolved while the app is in the background (see #5426).
18206
- // since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
18207
- // continuing
18208
- //
18209
- // we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and
18210
- // Safari respectively.
18211
- if (xhr && xhr.readyState == 4) {
18212
- var responseHeaders = null,
18213
- response = null,
18214
- statusText = '';
18215
-
18216
- if(status !== ABORTED) {
18217
- responseHeaders = xhr.getAllResponseHeaders();
18218
-
18219
- // responseText is the old-school way of retrieving response (supported by IE8 & 9)
18220
- // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
18221
- response = ('response' in xhr) ? xhr.response : xhr.responseText;
18222
- }
18525
+ xhr.onload = function requestLoaded() {
18526
+ var statusText = xhr.statusText || '';
18223
18527
 
18224
- // Accessing statusText on an aborted xhr object will
18225
- // throw an 'c00c023f error' in IE9 and lower, don't touch it.
18226
- if (!(status === ABORTED && msie < 10)) {
18227
- statusText = xhr.statusText;
18228
- }
18528
+ // responseText is the old-school way of retrieving response (supported by IE8 & 9)
18529
+ // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
18530
+ var response = ('response' in xhr) ? xhr.response : xhr.responseText;
18531
+
18532
+ // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
18533
+ var status = xhr.status === 1223 ? 204 : xhr.status;
18229
18534
 
18230
- completeRequest(callback,
18231
- status || xhr.status,
18232
- response,
18233
- responseHeaders,
18234
- statusText);
18535
+ // fix status code when it is 0 (0 status is undocumented).
18536
+ // Occurs when accessing file resources or on Android 4.1 stock browser
18537
+ // while retrieving files from application cache.
18538
+ if (status === 0) {
18539
+ status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
18235
18540
  }
18541
+
18542
+ completeRequest(callback,
18543
+ status,
18544
+ response,
18545
+ xhr.getAllResponseHeaders(),
18546
+ statusText);
18547
+ };
18548
+
18549
+ var requestError = function () {
18550
+ // The response is always empty
18551
+ // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
18552
+ completeRequest(callback, -1, null, null, '');
18236
18553
  };
18237
18554
 
18555
+ xhr.onerror = requestError;
18556
+ xhr.onabort = requestError;
18557
+
18238
18558
  if (withCredentials) {
18239
18559
  xhr.withCredentials = true;
18240
18560
  }
@@ -18267,7 +18587,6 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
18267
18587
 
18268
18588
 
18269
18589
  function timeoutRequest() {
18270
- status = ABORTED;
18271
18590
  jsonpDone && jsonpDone();
18272
18591
  xhr && xhr.abort();
18273
18592
  }
@@ -18277,17 +18596,6 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
18277
18596
  timeoutId && $browserDefer.cancel(timeoutId);
18278
18597
  jsonpDone = xhr = null;
18279
18598
 
18280
- // fix status code when it is 0 (0 status is undocumented).
18281
- // Occurs when accessing file resources or on Android 4.1 stock browser
18282
- // while retrieving files from application cache.
18283
- if (status === 0) {
18284
- status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
18285
- }
18286
-
18287
- // normalize IE bug (http://bugs.jquery.com/ticket/1450)
18288
- status = status === 1223 ? 204 : status;
18289
- statusText = statusText || '';
18290
-
18291
18599
  callback(status, response, headersString, statusText);
18292
18600
  $browser.$$completeOutstandingRequest(noop);
18293
18601
  }
@@ -19239,9 +19547,7 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) {
19239
19547
  }
19240
19548
 
19241
19549
 
19242
- LocationHashbangInHtml5Url.prototype =
19243
- LocationHashbangUrl.prototype =
19244
- LocationHtml5Url.prototype = {
19550
+ var locationPrototype = {
19245
19551
 
19246
19552
  /**
19247
19553
  * Are we in html5 mode?
@@ -19250,7 +19556,7 @@ LocationHashbangInHtml5Url.prototype =
19250
19556
  $$html5: false,
19251
19557
 
19252
19558
  /**
19253
- * Has any change been replacing ?
19559
+ * Has any change been replacing?
19254
19560
  * @private
19255
19561
  */
19256
19562
  $$replace: false,
@@ -19352,7 +19658,7 @@ LocationHashbangInHtml5Url.prototype =
19352
19658
  * @return {string} path
19353
19659
  */
19354
19660
  path: locationGetterSetter('$$path', function(path) {
19355
- path = path ? path.toString() : '';
19661
+ path = path !== null ? path.toString() : '';
19356
19662
  return path.charAt(0) == '/' ? path : '/' + path;
19357
19663
  }),
19358
19664
 
@@ -19449,7 +19755,7 @@ LocationHashbangInHtml5Url.prototype =
19449
19755
  * @return {string} hash
19450
19756
  */
19451
19757
  hash: locationGetterSetter('$$hash', function(hash) {
19452
- return hash ? hash.toString() : '';
19758
+ return hash !== null ? hash.toString() : '';
19453
19759
  }),
19454
19760
 
19455
19761
  /**
@@ -19466,6 +19772,46 @@ LocationHashbangInHtml5Url.prototype =
19466
19772
  }
19467
19773
  };
19468
19774
 
19775
+ forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function (Location) {
19776
+ Location.prototype = Object.create(locationPrototype);
19777
+
19778
+ /**
19779
+ * @ngdoc method
19780
+ * @name $location#state
19781
+ *
19782
+ * @description
19783
+ * This method is getter / setter.
19784
+ *
19785
+ * Return the history state object when called without any parameter.
19786
+ *
19787
+ * Change the history state object when called with one parameter and return `$location`.
19788
+ * The state object is later passed to `pushState` or `replaceState`.
19789
+ *
19790
+ * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
19791
+ * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
19792
+ * older browsers (like IE9 or Android < 4.0), don't use this method.
19793
+ *
19794
+ * @param {object=} state State object for pushState or replaceState
19795
+ * @return {object} state
19796
+ */
19797
+ Location.prototype.state = function(state) {
19798
+ if (!arguments.length)
19799
+ return this.$$state;
19800
+
19801
+ if (Location !== LocationHtml5Url || !this.$$html5) {
19802
+ throw $locationMinErr('nostate', 'History API state support is available only ' +
19803
+ 'in HTML5 mode and only in browsers supporting HTML5 History API');
19804
+ }
19805
+ // The user might modify `stateObject` after invoking `$location.state(stateObject)`
19806
+ // but we're changing the $$state reference to $browser.state() during the $digest
19807
+ // so the modification window is narrow.
19808
+ this.$$state = isUndefined(state) ? null : state;
19809
+
19810
+ return this;
19811
+ };
19812
+ });
19813
+
19814
+
19469
19815
  function locationGetter(property) {
19470
19816
  return function() {
19471
19817
  return this[property];
@@ -19522,7 +19868,8 @@ function $LocationProvider(){
19522
19868
  var hashPrefix = '',
19523
19869
  html5Mode = {
19524
19870
  enabled: false,
19525
- requireBase: true
19871
+ requireBase: true,
19872
+ rewriteLinks: true
19526
19873
  };
19527
19874
 
19528
19875
  /**
@@ -19546,15 +19893,17 @@ function $LocationProvider(){
19546
19893
  * @name $locationProvider#html5Mode
19547
19894
  * @description
19548
19895
  * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
19549
- * If object, sets `enabled` and `requireBase` to respective values.
19550
- * - **enabled** – `{boolean}` – Sets `html5Mode.enabled`. If true, will rely on
19551
- * `history.pushState` to change urls where supported. Will fall back to hash-prefixed paths
19552
- * in browsers that do not support `pushState`.
19553
- * - **requireBase** - `{boolean}` - Sets `html5Mode.requireBase` (default: `true`). When
19554
- * html5Mode is enabled, specifies whether or not a <base> tag is required to be present. If
19555
- * `enabled` and `requireBase` are true, and a base tag is not present, an error will be
19556
- * thrown when `$location` is injected. See the
19557
- * {@link guide/$location $location guide for more information}
19896
+ * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
19897
+ * properties:
19898
+ * - **enabled** – `{boolean}` (default: false) If true, will rely on `history.pushState` to
19899
+ * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
19900
+ * support `pushState`.
19901
+ * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
19902
+ * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
19903
+ * true, and a base tag is not present, an error will be thrown when `$location` is injected.
19904
+ * See the {@link guide/$location $location guide for more information}
19905
+ * - **rewriteLinks** - `{boolean}` - (default: `false`) When html5Mode is enabled, disables
19906
+ * url rewriting for relative linksTurns off url rewriting for relative links.
19558
19907
  *
19559
19908
  * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
19560
19909
  */
@@ -19563,12 +19912,19 @@ function $LocationProvider(){
19563
19912
  html5Mode.enabled = mode;
19564
19913
  return this;
19565
19914
  } else if (isObject(mode)) {
19566
- html5Mode.enabled = isBoolean(mode.enabled) ?
19567
- mode.enabled :
19568
- html5Mode.enabled;
19569
- html5Mode.requireBase = isBoolean(mode.requireBase) ?
19570
- mode.requireBase :
19571
- html5Mode.requireBase;
19915
+
19916
+ if (isBoolean(mode.enabled)) {
19917
+ html5Mode.enabled = mode.enabled;
19918
+ }
19919
+
19920
+ if (isBoolean(mode.requireBase)) {
19921
+ html5Mode.requireBase = mode.requireBase;
19922
+ }
19923
+
19924
+ if (isBoolean(mode.rewriteLinks)) {
19925
+ html5Mode.rewriteLinks = mode.rewriteLinks;
19926
+ }
19927
+
19572
19928
  return this;
19573
19929
  } else {
19574
19930
  return html5Mode;
@@ -19580,14 +19936,21 @@ function $LocationProvider(){
19580
19936
  * @name $location#$locationChangeStart
19581
19937
  * @eventType broadcast on root scope
19582
19938
  * @description
19583
- * Broadcasted before a URL will change. This change can be prevented by calling
19939
+ * Broadcasted before a URL will change.
19940
+ *
19941
+ * This change can be prevented by calling
19584
19942
  * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
19585
19943
  * details about event object. Upon successful change
19586
19944
  * {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired.
19587
19945
  *
19946
+ * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
19947
+ * the browser supports the HTML5 History API.
19948
+ *
19588
19949
  * @param {Object} angularEvent Synthetic event object.
19589
19950
  * @param {string} newUrl New URL
19590
19951
  * @param {string=} oldUrl URL that was before it was changed.
19952
+ * @param {string=} newState New history state object
19953
+ * @param {string=} oldState History state object that was before it was changed.
19591
19954
  */
19592
19955
 
19593
19956
  /**
@@ -19597,9 +19960,14 @@ function $LocationProvider(){
19597
19960
  * @description
19598
19961
  * Broadcasted after a URL was changed.
19599
19962
  *
19963
+ * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
19964
+ * the browser supports the HTML5 History API.
19965
+ *
19600
19966
  * @param {Object} angularEvent Synthetic event object.
19601
19967
  * @param {string} newUrl New URL
19602
19968
  * @param {string=} oldUrl URL that was before it was changed.
19969
+ * @param {string=} newState New history state object
19970
+ * @param {string=} oldState History state object that was before it was changed.
19603
19971
  */
19604
19972
 
19605
19973
  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
@@ -19624,13 +19992,34 @@ function $LocationProvider(){
19624
19992
  $location = new LocationMode(appBase, '#' + hashPrefix);
19625
19993
  $location.$$parseLinkUrl(initialUrl, initialUrl);
19626
19994
 
19995
+ $location.$$state = $browser.state();
19996
+
19627
19997
  var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
19628
19998
 
19999
+ function setBrowserUrlWithFallback(url, replace, state) {
20000
+ var oldUrl = $location.url();
20001
+ var oldState = $location.$$state;
20002
+ try {
20003
+ $browser.url(url, replace, state);
20004
+
20005
+ // Make sure $location.state() returns referentially identical (not just deeply equal)
20006
+ // state object; this makes possible quick checking if the state changed in the digest
20007
+ // loop. Checking deep equality would be too expensive.
20008
+ $location.$$state = $browser.state();
20009
+ } catch (e) {
20010
+ // Restore old values if pushState fails
20011
+ $location.url(oldUrl);
20012
+ $location.$$state = oldState;
20013
+
20014
+ throw e;
20015
+ }
20016
+ }
20017
+
19629
20018
  $rootElement.on('click', function(event) {
19630
20019
  // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
19631
20020
  // currently we open nice url link and redirect then
19632
20021
 
19633
- if (event.ctrlKey || event.metaKey || event.which == 2) return;
20022
+ if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.which == 2) return;
19634
20023
 
19635
20024
  var elm = jqLite(event.target);
19636
20025
 
@@ -19656,6 +20045,9 @@ function $LocationProvider(){
19656
20045
 
19657
20046
  if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
19658
20047
  if ($location.$$parseLinkUrl(absHref, relHref)) {
20048
+ // We do a preventDefault for all urls that are part of the angular application,
20049
+ // in html5mode and also without, so that we are able to abort navigation without
20050
+ // getting double entries in the location history.
19659
20051
  event.preventDefault();
19660
20052
  // update location manually
19661
20053
  if ($location.absUrl() != $browser.url()) {
@@ -19673,52 +20065,63 @@ function $LocationProvider(){
19673
20065
  $browser.url($location.absUrl(), true);
19674
20066
  }
19675
20067
 
19676
- // update $location when $browser url changes
19677
- $browser.onUrlChange(function(newUrl) {
19678
- if ($location.absUrl() != newUrl) {
19679
- $rootScope.$evalAsync(function() {
19680
- var oldUrl = $location.absUrl();
20068
+ var initializing = true;
19681
20069
 
19682
- $location.$$parse(newUrl);
19683
- if ($rootScope.$broadcast('$locationChangeStart', newUrl,
19684
- oldUrl).defaultPrevented) {
19685
- $location.$$parse(oldUrl);
19686
- $browser.url(oldUrl);
19687
- } else {
19688
- afterLocationChange(oldUrl);
19689
- }
19690
- });
19691
- if (!$rootScope.$$phase) $rootScope.$digest();
19692
- }
20070
+ // update $location when $browser url changes
20071
+ $browser.onUrlChange(function(newUrl, newState) {
20072
+ $rootScope.$evalAsync(function() {
20073
+ var oldUrl = $location.absUrl();
20074
+ var oldState = $location.$$state;
20075
+
20076
+ $location.$$parse(newUrl);
20077
+ $location.$$state = newState;
20078
+ if ($rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
20079
+ newState, oldState).defaultPrevented) {
20080
+ $location.$$parse(oldUrl);
20081
+ $location.$$state = oldState;
20082
+ setBrowserUrlWithFallback(oldUrl, false, oldState);
20083
+ } else {
20084
+ initializing = false;
20085
+ afterLocationChange(oldUrl, oldState);
20086
+ }
20087
+ });
20088
+ if (!$rootScope.$$phase) $rootScope.$digest();
19693
20089
  });
19694
20090
 
19695
20091
  // update browser
19696
- var changeCounter = 0;
19697
20092
  $rootScope.$watch(function $locationWatch() {
19698
20093
  var oldUrl = $browser.url();
20094
+ var oldState = $browser.state();
19699
20095
  var currentReplace = $location.$$replace;
19700
20096
 
19701
- if (!changeCounter || oldUrl != $location.absUrl()) {
19702
- changeCounter++;
20097
+ if (initializing || oldUrl !== $location.absUrl() ||
20098
+ ($location.$$html5 && $sniffer.history && oldState !== $location.$$state)) {
20099
+ initializing = false;
20100
+
19703
20101
  $rootScope.$evalAsync(function() {
19704
- if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
19705
- defaultPrevented) {
20102
+ if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl,
20103
+ $location.$$state, oldState).defaultPrevented) {
19706
20104
  $location.$$parse(oldUrl);
20105
+ $location.$$state = oldState;
19707
20106
  } else {
19708
- $browser.url($location.absUrl(), currentReplace);
19709
- afterLocationChange(oldUrl);
20107
+ setBrowserUrlWithFallback($location.absUrl(), currentReplace,
20108
+ oldState === $location.$$state ? null : $location.$$state);
20109
+ afterLocationChange(oldUrl, oldState);
19710
20110
  }
19711
20111
  });
19712
20112
  }
20113
+
19713
20114
  $location.$$replace = false;
19714
20115
 
19715
- return changeCounter;
20116
+ // we don't need to return anything because $evalAsync will make the digest loop dirty when
20117
+ // there is a change
19716
20118
  });
19717
20119
 
19718
20120
  return $location;
19719
20121
 
19720
- function afterLocationChange(oldUrl) {
19721
- $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl);
20122
+ function afterLocationChange(oldUrl, oldState) {
20123
+ $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
20124
+ $location.$$state, oldState);
19722
20125
  }
19723
20126
  }];
19724
20127
  }
@@ -19977,6 +20380,11 @@ forEach({
19977
20380
  CONSTANTS[name] = constantGetter;
19978
20381
  });
19979
20382
 
20383
+ //Not quite a constant, but can be lex/parsed the same
20384
+ CONSTANTS['this'] = function(self) { return self; };
20385
+ CONSTANTS['this'].sharedGetter = true;
20386
+
20387
+
19980
20388
  //Operators - will be wrapped by binaryFn/unaryFn/assignment/filter
19981
20389
  var OPERATORS = extend(createMap(), {
19982
20390
  /* jshint bitwise : false */
@@ -21840,14 +22248,11 @@ function $RootScopeProvider(){
21840
22248
  this.$$phase = this.$parent = this.$$watchers =
21841
22249
  this.$$nextSibling = this.$$prevSibling =
21842
22250
  this.$$childHead = this.$$childTail = null;
21843
- this['this'] = this.$root = this;
22251
+ this.$root = this;
21844
22252
  this.$$destroyed = false;
21845
- this.$$asyncQueue = [];
21846
- this.$$postDigestQueue = [];
21847
22253
  this.$$listeners = {};
21848
22254
  this.$$listenerCount = {};
21849
22255
  this.$$isolateBindings = null;
21850
- this.$$applyAsyncQueue = [];
21851
22256
  }
21852
22257
 
21853
22258
  /**
@@ -21896,18 +22301,23 @@ function $RootScopeProvider(){
21896
22301
  * When creating widgets, it is useful for the widget to not accidentally read parent
21897
22302
  * state.
21898
22303
  *
22304
+ * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
22305
+ * of the newly created scope. Defaults to `this` scope if not provided.
22306
+ * This is used when creating a transclude scope to correctly place it
22307
+ * in the scope hierarchy while maintaining the correct prototypical
22308
+ * inheritance.
22309
+ *
21899
22310
  * @returns {Object} The newly created child scope.
21900
22311
  *
21901
22312
  */
21902
- $new: function(isolate) {
22313
+ $new: function(isolate, parent) {
21903
22314
  var child;
21904
22315
 
22316
+ parent = parent || this;
22317
+
21905
22318
  if (isolate) {
21906
22319
  child = new Scope();
21907
22320
  child.$root = this.$root;
21908
- // ensure that there is just one async queue per $rootScope and its children
21909
- child.$$asyncQueue = this.$$asyncQueue;
21910
- child.$$postDigestQueue = this.$$postDigestQueue;
21911
22321
  } else {
21912
22322
  // Only create a child scope class if somebody asks for one,
21913
22323
  // but cache it to allow the VM to optimize lookups.
@@ -21924,16 +22334,27 @@ function $RootScopeProvider(){
21924
22334
  }
21925
22335
  child = new this.$$ChildScope();
21926
22336
  }
21927
- child['this'] = child;
21928
- child.$parent = this;
21929
- child.$$prevSibling = this.$$childTail;
21930
- if (this.$$childHead) {
21931
- this.$$childTail.$$nextSibling = child;
21932
- this.$$childTail = child;
22337
+ child.$parent = parent;
22338
+ child.$$prevSibling = parent.$$childTail;
22339
+ if (parent.$$childHead) {
22340
+ parent.$$childTail.$$nextSibling = child;
22341
+ parent.$$childTail = child;
21933
22342
  } else {
21934
- this.$$childHead = this.$$childTail = child;
22343
+ parent.$$childHead = parent.$$childTail = child;
21935
22344
  }
22345
+
22346
+ // When the new scope is not isolated or we inherit from `this`, and
22347
+ // the parent scope is destroyed, the property `$$destroyed` is inherited
22348
+ // prototypically. In all other cases, this property needs to be set
22349
+ // when the parent scope is destroyed.
22350
+ // The listener needs to be added after the parent is set
22351
+ if (isolate || parent != this) child.$on('$destroy', destroyChild);
22352
+
21936
22353
  return child;
22354
+
22355
+ function destroyChild() {
22356
+ child.$$destroyed = true;
22357
+ }
21937
22358
  },
21938
22359
 
21939
22360
  /**
@@ -22409,8 +22830,6 @@ function $RootScopeProvider(){
22409
22830
  $digest: function() {
22410
22831
  var watch, value, last,
22411
22832
  watchers,
22412
- asyncQueue = this.$$asyncQueue,
22413
- postDigestQueue = this.$$postDigestQueue,
22414
22833
  length,
22415
22834
  dirty, ttl = TTL,
22416
22835
  next, current, target = this,
@@ -22575,6 +22994,10 @@ function $RootScopeProvider(){
22575
22994
  if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
22576
22995
  if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
22577
22996
 
22997
+ // Disable listeners, watchers and apply/digest methods
22998
+ this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
22999
+ this.$on = this.$watch = this.$watchGroup = function() { return noop; };
23000
+ this.$$listeners = {};
22578
23001
 
22579
23002
  // All of the code below is bogus code that works around V8's memory leak via optimized code
22580
23003
  // and inline caches.
@@ -22585,15 +23008,7 @@ function $RootScopeProvider(){
22585
23008
  // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
22586
23009
 
22587
23010
  this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
22588
- this.$$childTail = this.$root = null;
22589
-
22590
- // don't reset these to null in case some async task tries to register a listener/watch/task
22591
- this.$$listeners = {};
22592
- this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = [];
22593
-
22594
- // prevent NPEs since these methods have references to properties we nulled out
22595
- this.$destroy = this.$digest = this.$apply = noop;
22596
- this.$on = this.$watch = this.$watchGroup = function() { return noop; };
23011
+ this.$$childTail = this.$root = this.$$watchers = null;
22597
23012
  },
22598
23013
 
22599
23014
  /**
@@ -22660,19 +23075,19 @@ function $RootScopeProvider(){
22660
23075
  $evalAsync: function(expr) {
22661
23076
  // if we are outside of an $digest loop and this is the first time we are scheduling async
22662
23077
  // task also schedule async auto-flush
22663
- if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
23078
+ if (!$rootScope.$$phase && !asyncQueue.length) {
22664
23079
  $browser.defer(function() {
22665
- if ($rootScope.$$asyncQueue.length) {
23080
+ if (asyncQueue.length) {
22666
23081
  $rootScope.$digest();
22667
23082
  }
22668
23083
  });
22669
23084
  }
22670
23085
 
22671
- this.$$asyncQueue.push({scope: this, expression: expr});
23086
+ asyncQueue.push({scope: this, expression: expr});
22672
23087
  },
22673
23088
 
22674
23089
  $$postDigest : function(fn) {
22675
- this.$$postDigestQueue.push(fn);
23090
+ postDigestQueue.push(fn);
22676
23091
  },
22677
23092
 
22678
23093
  /**
@@ -22756,7 +23171,7 @@ function $RootScopeProvider(){
22756
23171
  */
22757
23172
  $applyAsync: function(expr) {
22758
23173
  var scope = this;
22759
- expr && $rootScope.$$applyAsyncQueue.push($applyAsyncExpression);
23174
+ expr && applyAsyncQueue.push($applyAsyncExpression);
22760
23175
  scheduleApplyAsync();
22761
23176
 
22762
23177
  function $applyAsyncExpression() {
@@ -22965,6 +23380,11 @@ function $RootScopeProvider(){
22965
23380
 
22966
23381
  var $rootScope = new Scope();
22967
23382
 
23383
+ //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
23384
+ var asyncQueue = $rootScope.$$asyncQueue = [];
23385
+ var postDigestQueue = $rootScope.$$postDigestQueue = [];
23386
+ var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
23387
+
22968
23388
  return $rootScope;
22969
23389
 
22970
23390
 
@@ -22998,10 +23418,9 @@ function $RootScopeProvider(){
22998
23418
  function initWatchVal() {}
22999
23419
 
23000
23420
  function flushApplyAsync() {
23001
- var queue = $rootScope.$$applyAsyncQueue;
23002
- while (queue.length) {
23421
+ while (applyAsyncQueue.length) {
23003
23422
  try {
23004
- queue.shift()();
23423
+ applyAsyncQueue.shift()();
23005
23424
  } catch(e) {
23006
23425
  $exceptionHandler(e);
23007
23426
  }
@@ -23080,12 +23499,9 @@ function $$SanitizeUriProvider() {
23080
23499
  return function sanitizeUri(uri, isImage) {
23081
23500
  var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
23082
23501
  var normalizedVal;
23083
- // NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
23084
- if (!msie || msie >= 8 ) {
23085
- normalizedVal = urlResolve(uri).href;
23086
- if (normalizedVal !== '' && !normalizedVal.match(regex)) {
23087
- return 'unsafe:'+normalizedVal;
23088
- }
23502
+ normalizedVal = urlResolve(uri).href;
23503
+ if (normalizedVal !== '' && !normalizedVal.match(regex)) {
23504
+ return 'unsafe:'+normalizedVal;
23089
23505
  }
23090
23506
  return uri;
23091
23507
  };
@@ -24157,7 +24573,6 @@ function $SceProvider() {
24157
24573
  * @requires $document
24158
24574
  *
24159
24575
  * @property {boolean} history Does the browser support html5 history api ?
24160
- * @property {boolean} hashchange Does the browser support hashchange event ?
24161
24576
  * @property {boolean} transitions Does the browser support CSS transition events ?
24162
24577
  * @property {boolean} animations Does the browser support CSS animation events ?
24163
24578
  *
@@ -24214,9 +24629,6 @@ function $SnifferProvider() {
24214
24629
  // jshint -W018
24215
24630
  history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
24216
24631
  // jshint +W018
24217
- hashchange: 'onhashchange' in $window &&
24218
- // IE8 compatible mode lies
24219
- (!documentMode || documentMode > 7),
24220
24632
  hasEvent: function(event) {
24221
24633
  // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
24222
24634
  // it. In particular the event is not fired when backspace or delete key are pressed or
@@ -25686,7 +26098,7 @@ function limitToFilter(){
25686
26098
  * correctly, make sure they are actually being saved as numbers and not strings.
25687
26099
  *
25688
26100
  * @param {Array} array The array to sort.
25689
- * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
26101
+ * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
25690
26102
  * used by the comparator to determine the order of elements.
25691
26103
  *
25692
26104
  * Can be one of:
@@ -25699,10 +26111,13 @@ function limitToFilter(){
25699
26111
  * is interpreted as a property name to be used in comparisons (for example `"special name"`
25700
26112
  * to sort object by the value of their `special name` property). An expression can be
25701
26113
  * optionally prefixed with `+` or `-` to control ascending or descending sort order
25702
- * (for example, `+name` or `-name`).
26114
+ * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
26115
+ * element itself is used to compare where sorting.
25703
26116
  * - `Array`: An array of function or string predicates. The first predicate in the array
25704
26117
  * is used for sorting, but when two items are equivalent, the next predicate is used.
25705
26118
  *
26119
+ * If the predicate is missing or empty then it defaults to `'+'`.
26120
+ *
25706
26121
  * @param {boolean=} reverse Reverse the order of the array.
25707
26122
  * @returns {Array} Sorted copy of the source array.
25708
26123
  *
@@ -25791,8 +26206,8 @@ orderByFilter.$inject = ['$parse'];
25791
26206
  function orderByFilter($parse){
25792
26207
  return function(array, sortPredicate, reverseOrder) {
25793
26208
  if (!(isArrayLike(array))) return array;
25794
- if (!sortPredicate) return array;
25795
26209
  sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
26210
+ if (sortPredicate.length === 0) { sortPredicate = ['+']; }
25796
26211
  sortPredicate = sortPredicate.map(function(predicate){
25797
26212
  var descending = false, get = predicate || identity;
25798
26213
  if (isString(predicate)) {
@@ -25800,6 +26215,12 @@ function orderByFilter($parse){
25800
26215
  descending = predicate.charAt(0) == '-';
25801
26216
  predicate = predicate.substring(1);
25802
26217
  }
26218
+ if ( predicate === '' ) {
26219
+ // Effectively no predicate was passed so we compare identity
26220
+ return reverseComparator(function(a,b) {
26221
+ return compare(a, b);
26222
+ }, descending);
26223
+ }
25803
26224
  get = $parse(predicate);
25804
26225
  if (get.constant) {
25805
26226
  var key = get();
@@ -25875,22 +26296,6 @@ function ngDirective(directive) {
25875
26296
  var htmlAnchorDirective = valueFn({
25876
26297
  restrict: 'E',
25877
26298
  compile: function(element, attr) {
25878
-
25879
- if (msie <= 8) {
25880
-
25881
- // turn <a href ng-click="..">link</a> into a stylable link in IE
25882
- // but only if it doesn't have name attribute, in which case it's an anchor
25883
- if (!attr.href && !attr.name) {
25884
- attr.$set('href', '');
25885
- }
25886
-
25887
- // add a comment node to anchors to workaround IE bug that causes element content to be reset
25888
- // to new attribute content if attribute is updated with value containing @ and element also
25889
- // contains value with @
25890
- // see issue #1949
25891
- element.append(document.createComment('IE fix'));
25892
- }
25893
-
25894
26299
  if (!attr.href && !attr.xlinkHref && !attr.name) {
25895
26300
  return function(scope, element) {
25896
26301
  // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
@@ -26338,11 +26743,9 @@ var nullFormCtrl = {
26338
26743
  $$renameControl: nullFormRenameControl,
26339
26744
  $removeControl: noop,
26340
26745
  $setValidity: noop,
26341
- $$setPending: noop,
26342
26746
  $setDirty: noop,
26343
26747
  $setPristine: noop,
26344
- $setSubmitted: noop,
26345
- $$clearControlValidity: noop
26748
+ $setSubmitted: noop
26346
26749
  },
26347
26750
  SUBMITTED_CLASS = 'ng-submitted';
26348
26751
 
@@ -26407,9 +26810,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
26407
26810
 
26408
26811
  parentForm.$addControl(form);
26409
26812
 
26410
- // Setup initial state of the control
26411
- element.addClass(PRISTINE_CLASS);
26412
-
26413
26813
  /**
26414
26814
  * @ngdoc method
26415
26815
  * @name form.FormController#$rollbackViewValue
@@ -26782,10 +27182,14 @@ var formDirectiveFactory = function(isNgForm) {
26782
27182
  name: 'form',
26783
27183
  restrict: isNgForm ? 'EAC' : 'E',
26784
27184
  controller: FormController,
26785
- compile: function() {
27185
+ compile: function ngFormCompile(formElement) {
27186
+ // Setup initial state of the control
27187
+ formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
27188
+
26786
27189
  return {
26787
- pre: function(scope, formElement, attr, controller) {
26788
- if (!attr.action) {
27190
+ pre: function ngFormPreLink(scope, formElement, attr, controller) {
27191
+ // if `action` attr is not present on the form, prevent the default action (submission)
27192
+ if (!('action' in attr)) {
26789
27193
  // we can't use jq events because if a form is destroyed during submission the default
26790
27194
  // action is not prevented. see #1238
26791
27195
  //
@@ -27939,16 +28343,15 @@ function createDateInputType(type, regexp, parseDate, format) {
27939
28343
  badInputChecker(scope, element, attr, ctrl);
27940
28344
  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
27941
28345
  var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
28346
+ var previousDate;
27942
28347
 
27943
28348
  ctrl.$$parserName = type;
27944
28349
  ctrl.$parsers.push(function(value) {
27945
28350
  if (ctrl.$isEmpty(value)) return null;
27946
28351
  if (regexp.test(value)) {
27947
- var previousDate = ctrl.$modelValue;
27948
- if (previousDate && timezone === 'UTC') {
27949
- var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
27950
- previousDate = new Date(previousDate.getTime() + timezoneOffset);
27951
- }
28352
+ // Note: We cannot read ctrl.$modelValue, as there might be a different
28353
+ // parser/formatter in the processing chain so that the model
28354
+ // contains some different data format!
27952
28355
  var parsedDate = parseDate(value, previousDate);
27953
28356
  if (timezone === 'UTC') {
27954
28357
  parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
@@ -27959,8 +28362,18 @@ function createDateInputType(type, regexp, parseDate, format) {
27959
28362
  });
27960
28363
 
27961
28364
  ctrl.$formatters.push(function(value) {
27962
- if (isDate(value)) {
28365
+ if (!ctrl.$isEmpty(value)) {
28366
+ if (!isDate(value)) {
28367
+ throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
28368
+ }
28369
+ previousDate = value;
28370
+ if (previousDate && timezone === 'UTC') {
28371
+ var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
28372
+ previousDate = new Date(previousDate.getTime() + timezoneOffset);
28373
+ }
27963
28374
  return $filter('date')(value, format, timezone);
28375
+ } else {
28376
+ previousDate = null;
27964
28377
  }
27965
28378
  return '';
27966
28379
  });
@@ -27986,6 +28399,11 @@ function createDateInputType(type, regexp, parseDate, format) {
27986
28399
  ctrl.$validate();
27987
28400
  });
27988
28401
  }
28402
+ // Override the standard $isEmpty to detect invalid dates as well
28403
+ ctrl.$isEmpty = function(value) {
28404
+ // Invalid Date: getTime() returns NaN
28405
+ return !value || (value.getTime && value.getTime() !== value.getTime());
28406
+ };
27989
28407
 
27990
28408
  function parseObservedDateValue(val) {
27991
28409
  return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
@@ -28300,10 +28718,12 @@ var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
28300
28718
  return {
28301
28719
  restrict: 'E',
28302
28720
  require: ['?ngModel'],
28303
- link: function(scope, element, attr, ctrls) {
28304
- if (ctrls[0]) {
28305
- (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
28306
- $browser, $filter, $parse);
28721
+ link: {
28722
+ pre: function(scope, element, attr, ctrls) {
28723
+ if (ctrls[0]) {
28724
+ (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
28725
+ $browser, $filter, $parse);
28726
+ }
28307
28727
  }
28308
28728
  }
28309
28729
  };
@@ -28604,11 +29024,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
28604
29024
  var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
28605
29025
  currentValidationRunId = 0;
28606
29026
 
28607
- // Setup initial state of the control
28608
- $element
28609
- .addClass(PRISTINE_CLASS)
28610
- .addClass(UNTOUCHED_CLASS);
28611
-
28612
29027
  /**
28613
29028
  * @ngdoc method
28614
29029
  * @name ngModel.NgModelController#$setValidity
@@ -28901,14 +29316,17 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
28901
29316
  };
28902
29317
 
28903
29318
  this.$$parseAndValidate = function() {
28904
- var parserValid = true,
28905
- viewValue = ctrl.$$lastCommittedViewValue,
28906
- modelValue = viewValue;
28907
- for(var i = 0; i < ctrl.$parsers.length; i++) {
28908
- modelValue = ctrl.$parsers[i](modelValue);
28909
- if (isUndefined(modelValue)) {
28910
- parserValid = false;
28911
- break;
29319
+ var viewValue = ctrl.$$lastCommittedViewValue;
29320
+ var modelValue = viewValue;
29321
+ var parserValid = isUndefined(modelValue) ? undefined : true;
29322
+
29323
+ if (parserValid) {
29324
+ for(var i = 0; i < ctrl.$parsers.length; i++) {
29325
+ modelValue = ctrl.$parsers[i](modelValue);
29326
+ if (isUndefined(modelValue)) {
29327
+ parserValid = false;
29328
+ break;
29329
+ }
28912
29330
  }
28913
29331
  }
28914
29332
  if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
@@ -29225,42 +29643,51 @@ var ngModelDirective = function() {
29225
29643
  restrict: 'A',
29226
29644
  require: ['ngModel', '^?form', '^?ngModelOptions'],
29227
29645
  controller: NgModelController,
29228
- link: {
29229
- pre: function(scope, element, attr, ctrls) {
29230
- var modelCtrl = ctrls[0],
29231
- formCtrl = ctrls[1] || nullFormCtrl;
29646
+ // Prelink needs to run before any input directive
29647
+ // so that we can set the NgModelOptions in NgModelController
29648
+ // before anyone else uses it.
29649
+ priority: 1,
29650
+ compile: function ngModelCompile(element) {
29651
+ // Setup initial state of the control
29652
+ element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
29232
29653
 
29233
- modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
29654
+ return {
29655
+ pre: function ngModelPreLink(scope, element, attr, ctrls) {
29656
+ var modelCtrl = ctrls[0],
29657
+ formCtrl = ctrls[1] || nullFormCtrl;
29234
29658
 
29235
- // notify others, especially parent forms
29236
- formCtrl.$addControl(modelCtrl);
29659
+ modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
29237
29660
 
29238
- attr.$observe('name', function(newValue) {
29239
- if (modelCtrl.$name !== newValue) {
29240
- formCtrl.$$renameControl(modelCtrl, newValue);
29241
- }
29242
- });
29661
+ // notify others, especially parent forms
29662
+ formCtrl.$addControl(modelCtrl);
29243
29663
 
29244
- scope.$on('$destroy', function() {
29245
- formCtrl.$removeControl(modelCtrl);
29246
- });
29247
- },
29248
- post: function(scope, element, attr, ctrls) {
29249
- var modelCtrl = ctrls[0];
29250
- if (modelCtrl.$options && modelCtrl.$options.updateOn) {
29251
- element.on(modelCtrl.$options.updateOn, function(ev) {
29252
- modelCtrl.$$debounceViewValueCommit(ev && ev.type);
29664
+ attr.$observe('name', function(newValue) {
29665
+ if (modelCtrl.$name !== newValue) {
29666
+ formCtrl.$$renameControl(modelCtrl, newValue);
29667
+ }
29253
29668
  });
29254
- }
29255
29669
 
29256
- element.on('blur', function(ev) {
29257
- if (modelCtrl.$touched) return;
29670
+ scope.$on('$destroy', function() {
29671
+ formCtrl.$removeControl(modelCtrl);
29672
+ });
29673
+ },
29674
+ post: function ngModelPostLink(scope, element, attr, ctrls) {
29675
+ var modelCtrl = ctrls[0];
29676
+ if (modelCtrl.$options && modelCtrl.$options.updateOn) {
29677
+ element.on(modelCtrl.$options.updateOn, function(ev) {
29678
+ modelCtrl.$$debounceViewValueCommit(ev && ev.type);
29679
+ });
29680
+ }
29681
+
29682
+ element.on('blur', function(ev) {
29683
+ if (modelCtrl.$touched) return;
29258
29684
 
29259
- scope.$apply(function() {
29260
- modelCtrl.$setTouched();
29685
+ scope.$apply(function() {
29686
+ modelCtrl.$setTouched();
29687
+ });
29261
29688
  });
29262
- });
29263
- }
29689
+ }
29690
+ };
29264
29691
  }
29265
29692
  };
29266
29693
  };
@@ -29815,8 +30242,9 @@ function addSetValidityMethod(context) {
29815
30242
  parentForm = context.parentForm,
29816
30243
  $animate = context.$animate;
29817
30244
 
30245
+ classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
30246
+
29818
30247
  ctrl.$setValidity = setValidity;
29819
- toggleValidationCss('', true);
29820
30248
 
29821
30249
  function setValidity(validationErrorKey, state, options) {
29822
30250
  if (state === undefined) {
@@ -29966,11 +30394,9 @@ var ngBindDirective = ['$compile', function($compile) {
29966
30394
  $compile.$$addBindingClass(templateElement);
29967
30395
  return function ngBindLink(scope, element, attr) {
29968
30396
  $compile.$$addBindingInfo(element, attr.ngBind);
30397
+ element = element[0];
29969
30398
  scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
29970
- // We are purposefully using == here rather than === because we want to
29971
- // catch when value is "null or undefined"
29972
- // jshint -W041
29973
- element.text(value == undefined ? '' : value);
30399
+ element.textContent = value === undefined ? '' : value;
29974
30400
  });
29975
30401
  };
29976
30402
  }
@@ -30036,8 +30462,9 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate
30036
30462
  return function ngBindTemplateLink(scope, element, attr) {
30037
30463
  var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
30038
30464
  $compile.$$addBindingInfo(element, interpolateFn.expressions);
30465
+ element = element[0];
30039
30466
  attr.$observe('ngBindTemplate', function(value) {
30040
- element.text(value);
30467
+ element.textContent = value === undefined ? '' : value;
30041
30468
  });
30042
30469
  };
30043
30470
  }
@@ -30054,7 +30481,10 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate
30054
30481
  * element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link
30055
30482
  * ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize`
30056
30483
  * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
30057
- * core Angular.) You may also bypass sanitization for values you know are safe. To do so, bind to
30484
+ * core Angular). In order to use {@link ngSanitize} in your module's dependencies, you need to
30485
+ * include "angular-sanitize.js" in your application.
30486
+ *
30487
+ * You may also bypass sanitization for values you know are safe. To do so, bind to
30058
30488
  * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
30059
30489
  * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}.
30060
30490
  *
@@ -30816,7 +31246,125 @@ var ngControllerDirective = [function() {
30816
31246
  ...
30817
31247
  </html>
30818
31248
  ```
30819
- */
31249
+ * @example
31250
+ // Note: the suffix `.csp` in the example name triggers
31251
+ // csp mode in our http server!
31252
+ <example name="example.csp" module="cspExample" ng-csp="true">
31253
+ <file name="index.html">
31254
+ <div ng-controller="MainController as ctrl">
31255
+ <div>
31256
+ <button ng-click="ctrl.inc()" id="inc">Increment</button>
31257
+ <span id="counter">
31258
+ {{ctrl.counter}}
31259
+ </span>
31260
+ </div>
31261
+
31262
+ <div>
31263
+ <button ng-click="ctrl.evil()" id="evil">Evil</button>
31264
+ <span id="evilError">
31265
+ {{ctrl.evilError}}
31266
+ </span>
31267
+ </div>
31268
+ </div>
31269
+ </file>
31270
+ <file name="script.js">
31271
+ angular.module('cspExample', [])
31272
+ .controller('MainController', function() {
31273
+ this.counter = 0;
31274
+ this.inc = function() {
31275
+ this.counter++;
31276
+ };
31277
+ this.evil = function() {
31278
+ // jshint evil:true
31279
+ try {
31280
+ eval('1+2');
31281
+ } catch (e) {
31282
+ this.evilError = e.message;
31283
+ }
31284
+ };
31285
+ });
31286
+ </file>
31287
+ <file name="protractor.js" type="protractor">
31288
+ var util, webdriver;
31289
+
31290
+ var incBtn = element(by.id('inc'));
31291
+ var counter = element(by.id('counter'));
31292
+ var evilBtn = element(by.id('evil'));
31293
+ var evilError = element(by.id('evilError'));
31294
+
31295
+ function getAndClearSevereErrors() {
31296
+ return browser.manage().logs().get('browser').then(function(browserLog) {
31297
+ return browserLog.filter(function(logEntry) {
31298
+ return logEntry.level.value > webdriver.logging.Level.WARNING.value;
31299
+ });
31300
+ });
31301
+ }
31302
+
31303
+ function clearErrors() {
31304
+ getAndClearSevereErrors();
31305
+ }
31306
+
31307
+ function expectNoErrors() {
31308
+ getAndClearSevereErrors().then(function(filteredLog) {
31309
+ expect(filteredLog.length).toEqual(0);
31310
+ if (filteredLog.length) {
31311
+ console.log('browser console errors: ' + util.inspect(filteredLog));
31312
+ }
31313
+ });
31314
+ }
31315
+
31316
+ function expectError(regex) {
31317
+ getAndClearSevereErrors().then(function(filteredLog) {
31318
+ var found = false;
31319
+ filteredLog.forEach(function(log) {
31320
+ if (log.message.match(regex)) {
31321
+ found = true;
31322
+ }
31323
+ });
31324
+ if (!found) {
31325
+ throw new Error('expected an error that matches ' + regex);
31326
+ }
31327
+ });
31328
+ }
31329
+
31330
+ beforeEach(function() {
31331
+ util = require('util');
31332
+ webdriver = require('protractor/node_modules/selenium-webdriver');
31333
+ });
31334
+
31335
+ // For now, we only test on Chrome,
31336
+ // as Safari does not load the page with Protractor's injected scripts,
31337
+ // and Firefox webdriver always disables content security policy (#6358)
31338
+ if (browser.params.browser !== 'chrome') {
31339
+ return;
31340
+ }
31341
+
31342
+ it('should not report errors when the page is loaded', function() {
31343
+ // clear errors so we are not dependent on previous tests
31344
+ clearErrors();
31345
+ // Need to reload the page as the page is already loaded when
31346
+ // we come here
31347
+ browser.driver.getCurrentUrl().then(function(url) {
31348
+ browser.get(url);
31349
+ });
31350
+ expectNoErrors();
31351
+ });
31352
+
31353
+ it('should evaluate expressions', function() {
31354
+ expect(counter.getText()).toEqual('0');
31355
+ incBtn.click();
31356
+ expect(counter.getText()).toEqual('1');
31357
+ expectNoErrors();
31358
+ });
31359
+
31360
+ it('should throw and report an error when using "eval"', function() {
31361
+ evilBtn.click();
31362
+ expect(evilError.getText()).toMatch(/Content Security Policy/);
31363
+ expectError(/Content Security Policy/);
31364
+ });
31365
+ </file>
31366
+ </example>
31367
+ */
30820
31368
 
30821
31369
  // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
30822
31370
  // bootstrap the system (before $parse is instantiated), for this reason we just have
@@ -31323,7 +31871,7 @@ forEach(
31323
31871
  * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
31324
31872
  * is created when the element is restored. The scope created within `ngIf` inherits from
31325
31873
  * its parent scope using
31326
- * [prototypal inheritance](https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance).
31874
+ * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
31327
31875
  * An important implication of this is if `ngModel` is used within `ngIf` to bind to
31328
31876
  * a javascript primitive defined in the parent scope. In this case any modifications made to the
31329
31877
  * variable within the child scope will override (hide) the value in the parent scope.
@@ -31337,8 +31885,8 @@ forEach(
31337
31885
  * and `leave` effects.
31338
31886
  *
31339
31887
  * @animations
31340
- * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container
31341
- * leave - happens just before the ngIf contents are removed from the DOM
31888
+ * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
31889
+ * leave - happens just before the `ngIf` contents are removed from the DOM
31342
31890
  *
31343
31891
  * @element ANY
31344
31892
  * @scope
@@ -32478,6 +33026,8 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
32478
33026
  };
32479
33027
  }];
32480
33028
 
33029
+ var NG_HIDE_CLASS = 'ng-hide';
33030
+ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
32481
33031
  /**
32482
33032
  * @ngdoc directive
32483
33033
  * @name ngShow
@@ -32639,7 +33189,11 @@ var ngShowDirective = ['$animate', function($animate) {
32639
33189
  multiElement: true,
32640
33190
  link: function(scope, element, attr) {
32641
33191
  scope.$watch(attr.ngShow, function ngShowWatchAction(value){
32642
- $animate[value ? 'removeClass' : 'addClass'](element, 'ng-hide');
33192
+ // we're adding a temporary, animation-specific class for ng-hide since this way
33193
+ // we can control when the element is actually displayed on screen without having
33194
+ // to have a global/greedy CSS selector that breaks when other animations are run.
33195
+ // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
33196
+ $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, NG_HIDE_IN_PROGRESS_CLASS);
32643
33197
  });
32644
33198
  }
32645
33199
  };
@@ -32794,7 +33348,9 @@ var ngHideDirective = ['$animate', function($animate) {
32794
33348
  multiElement: true,
32795
33349
  link: function(scope, element, attr) {
32796
33350
  scope.$watch(attr.ngHide, function ngHideWatchAction(value){
32797
- $animate[value ? 'addClass' : 'removeClass'](element, 'ng-hide');
33351
+ // The comment inside of the ngShowDirective explains why we add and
33352
+ // remove a temporary class for the show/hide animation
33353
+ $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, NG_HIDE_IN_PROGRESS_CLASS);
32798
33354
  });
32799
33355
  }
32800
33356
  };
@@ -33216,6 +33772,12 @@ var ngOptionsMinErr = minErr('ngOptions');
33216
33772
  * be bound to string values at present.
33217
33773
  * </div>
33218
33774
  *
33775
+ * <div class="alert alert-info">
33776
+ * **Note:** Using `select as` will bind the result of the `select as` expression to the model, but
33777
+ * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
33778
+ * or property name (for object data sources) of the value within the collection.
33779
+ * </div>
33780
+ *
33219
33781
  * @param {string} ngModel Assignable angular expression to data-bind to.
33220
33782
  * @param {string=} name Property name of the form under which the control is published.
33221
33783
  * @param {string=} required The control is considered valid only if value is entered.
@@ -33250,7 +33812,25 @@ var ngOptionsMinErr = minErr('ngOptions');
33250
33812
  * DOM element.
33251
33813
  * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
33252
33814
  * used to identify the objects in the array. The `trackexpr` will most likely refer to the
33253
- * `value` variable (e.g. `value.propertyName`).
33815
+ * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
33816
+ * even when the options are recreated (e.g. reloaded from the server).
33817
+
33818
+ * <div class="alert alert-info">
33819
+ * **Note:** Using `select as` together with `trackexpr` is not possible (and will throw).
33820
+ * Reasoning:
33821
+ * - Example: <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
33822
+ * values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItemß'}}],
33823
+ * $scope.selected = {name: 'aSubItem'};
33824
+ * - track by is always applied to `value`, with purpose to preserve the selection,
33825
+ * (to `item` in this case)
33826
+ * - to calculate whether an item is selected we do the following:
33827
+ * 1. apply `track by` to the values in the array, e.g.
33828
+ * In the example: [1,2]
33829
+ * 2. apply `track by` to the already selected value in `ngModel`:
33830
+ * In the example: this is not possible, as `track by` refers to `item.id`, but the selected
33831
+ * value from `ngModel` is `{name: aSubItem}`.
33832
+ *
33833
+ * </div>
33254
33834
  *
33255
33835
  * @example
33256
33836
  <example module="selectExample">
@@ -33507,6 +34087,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
33507
34087
 
33508
34088
  var displayFn = $parse(match[2] || match[1]),
33509
34089
  valueName = match[4] || match[6],
34090
+ selectAs = / as /.test(match[0]) && match[1],
34091
+ selectAsFn = selectAs ? $parse(selectAs) : null,
33510
34092
  keyName = match[5],
33511
34093
  groupByFn = $parse(match[3] || ''),
33512
34094
  valueFn = $parse(match[2] ? match[1] : valueName),
@@ -33517,7 +34099,16 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
33517
34099
  // We try to reuse these if possible
33518
34100
  // - optionGroupsCache[0] is the options with no option group
33519
34101
  // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
33520
- optionGroupsCache = [[{element: selectElement, label:''}]];
34102
+ optionGroupsCache = [[{element: selectElement, label:''}]],
34103
+ //re-usable object to represent option's locals
34104
+ locals = {};
34105
+
34106
+ if (trackFn && selectAsFn) {
34107
+ throw ngOptionsMinErr('trkslct',
34108
+ "Comprehension expression cannot contain both selectAs '{0}' " +
34109
+ "and trackBy '{1}' expressions.",
34110
+ selectAs, track);
34111
+ }
33521
34112
 
33522
34113
  if (nullOption) {
33523
34114
  // compile the element since there might be bindings in it
@@ -33535,103 +34126,110 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
33535
34126
  // clear contents, we'll add what's needed based on the model
33536
34127
  selectElement.empty();
33537
34128
 
33538
- selectElement.on('change', function() {
34129
+ selectElement.on('change', selectionChanged);
34130
+
34131
+ ctrl.$render = render;
34132
+
34133
+ scope.$watchCollection(valuesFn, scheduleRendering);
34134
+ scope.$watchCollection(getLabels, scheduleRendering);
34135
+
34136
+ if (multiple) {
34137
+ scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
34138
+ }
34139
+
34140
+ // ------------------------------------------------------------------ //
34141
+
34142
+ function callExpression(exprFn, key, value) {
34143
+ locals[valueName] = value;
34144
+ if (keyName) locals[keyName] = key;
34145
+ return exprFn(scope, locals);
34146
+ }
34147
+
34148
+ function selectionChanged() {
33539
34149
  scope.$apply(function() {
33540
34150
  var optionGroup,
33541
34151
  collection = valuesFn(scope) || [],
33542
- locals = {},
33543
34152
  key, value, optionElement, index, groupIndex, length, groupLength, trackIndex;
33544
-
34153
+ var viewValue;
33545
34154
  if (multiple) {
33546
- value = [];
33547
- for (groupIndex = 0, groupLength = optionGroupsCache.length;
33548
- groupIndex < groupLength;
33549
- groupIndex++) {
33550
- // list of options for that group. (first item has the parent)
33551
- optionGroup = optionGroupsCache[groupIndex];
33552
-
33553
- for(index = 1, length = optionGroup.length; index < length; index++) {
33554
- if ((optionElement = optionGroup[index].element)[0].selected) {
33555
- key = optionElement.val();
33556
- if (keyName) locals[keyName] = key;
33557
- if (trackFn) {
33558
- for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
33559
- locals[valueName] = collection[trackIndex];
33560
- if (trackFn(scope, locals) == key) break;
33561
- }
33562
- } else {
33563
- locals[valueName] = collection[key];
33564
- }
33565
- value.push(valueFn(scope, locals));
33566
- }
33567
- }
33568
- }
34155
+ viewValue = [];
34156
+ forEach(selectElement.val(), function(selectedKey) {
34157
+ viewValue.push(getViewValue(selectedKey, collection[selectedKey]));
34158
+ });
33569
34159
  } else {
33570
- key = selectElement.val();
33571
- if (key == '?') {
33572
- value = undefined;
33573
- } else if (key === ''){
33574
- value = null;
33575
- } else {
33576
- if (trackFn) {
33577
- for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
33578
- locals[valueName] = collection[trackIndex];
33579
- if (trackFn(scope, locals) == key) {
33580
- value = valueFn(scope, locals);
33581
- break;
33582
- }
33583
- }
33584
- } else {
33585
- locals[valueName] = collection[key];
33586
- if (keyName) locals[keyName] = key;
33587
- value = valueFn(scope, locals);
33588
- }
33589
- }
34160
+ var selectedKey = selectElement.val();
34161
+ viewValue = getViewValue(selectedKey, collection[selectedKey]);
33590
34162
  }
33591
- ctrl.$setViewValue(value);
34163
+ ctrl.$setViewValue(viewValue);
33592
34164
  render();
33593
34165
  });
33594
- });
34166
+ }
33595
34167
 
33596
- ctrl.$render = render;
34168
+ function getViewValue(key, value) {
34169
+ if (key === '?') {
34170
+ return undefined;
34171
+ } else if (key === '') {
34172
+ return null;
34173
+ } else {
34174
+ var viewValueFn = selectAsFn ? selectAsFn : valueFn;
34175
+ return callExpression(viewValueFn, key, value);
34176
+ }
34177
+ }
33597
34178
 
33598
- scope.$watchCollection(valuesFn, scheduleRendering);
33599
- scope.$watchCollection(function () {
33600
- var locals = {},
33601
- values = valuesFn(scope);
33602
- if (values) {
33603
- var toDisplay = new Array(values.length);
34179
+ function getLabels() {
34180
+ var values = valuesFn(scope);
34181
+ var toDisplay;
34182
+ if (values && isArray(values)) {
34183
+ toDisplay = new Array(values.length);
33604
34184
  for (var i = 0, ii = values.length; i < ii; i++) {
33605
- locals[valueName] = values[i];
33606
- toDisplay[i] = displayFn(scope, locals);
34185
+ toDisplay[i] = callExpression(displayFn, i, values[i]);
33607
34186
  }
33608
34187
  return toDisplay;
34188
+ } else if (values) {
34189
+ // TODO: Add a test for this case
34190
+ toDisplay = {};
34191
+ for (var prop in values) {
34192
+ if (values.hasOwnProperty(prop)) {
34193
+ toDisplay[prop] = callExpression(displayFn, prop, values[prop]);
34194
+ }
34195
+ }
33609
34196
  }
33610
- }, scheduleRendering);
33611
-
33612
- if (multiple) {
33613
- scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
34197
+ return toDisplay;
33614
34198
  }
33615
34199
 
33616
-
33617
- function getSelectedSet() {
33618
- var selectedSet = false;
34200
+ function createIsSelectedFn(viewValue) {
34201
+ var selectedSet;
33619
34202
  if (multiple) {
33620
- var modelValue = ctrl.$modelValue;
33621
- if (trackFn && isArray(modelValue)) {
34203
+ if (!selectAs && trackFn && isArray(viewValue)) {
34204
+
33622
34205
  selectedSet = new HashMap([]);
33623
- var locals = {};
33624
- for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
33625
- locals[valueName] = modelValue[trackIndex];
33626
- selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
34206
+ for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) {
34207
+ // tracking by key
34208
+ selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true);
33627
34209
  }
33628
34210
  } else {
33629
- selectedSet = new HashMap(modelValue);
34211
+ selectedSet = new HashMap(viewValue);
33630
34212
  }
34213
+ } else if (!selectAsFn && trackFn) {
34214
+ viewValue = callExpression(trackFn, null, viewValue);
33631
34215
  }
33632
- return selectedSet;
33633
- }
34216
+ return function isSelected(key, value) {
34217
+ var compareValueFn;
34218
+ if (selectAsFn) {
34219
+ compareValueFn = selectAsFn;
34220
+ } else if (trackFn) {
34221
+ compareValueFn = trackFn;
34222
+ } else {
34223
+ compareValueFn = valueFn;
34224
+ }
33634
34225
 
34226
+ if (multiple) {
34227
+ return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value)));
34228
+ } else {
34229
+ return viewValue == callExpression(compareValueFn, key, value);
34230
+ }
34231
+ };
34232
+ }
33635
34233
 
33636
34234
  function scheduleRendering() {
33637
34235
  if (!renderScheduled) {
@@ -33640,78 +34238,64 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
33640
34238
  }
33641
34239
  }
33642
34240
 
33643
-
33644
34241
  function render() {
33645
34242
  renderScheduled = false;
33646
34243
 
33647
- // Temporary location for the option groups before we render them
34244
+ // Temporary location for the option groups before we render them
33648
34245
  var optionGroups = {'':[]},
33649
34246
  optionGroupNames = [''],
33650
34247
  optionGroupName,
33651
34248
  optionGroup,
33652
34249
  option,
33653
34250
  existingParent, existingOptions, existingOption,
33654
- modelValue = ctrl.$modelValue,
34251
+ viewValue = ctrl.$viewValue,
33655
34252
  values = valuesFn(scope) || [],
33656
34253
  keys = keyName ? sortedKeys(values) : values,
33657
34254
  key,
34255
+ value,
33658
34256
  groupLength, length,
33659
34257
  groupIndex, index,
33660
- locals = {},
33661
34258
  selected,
33662
- selectedSet = getSelectedSet(),
34259
+ isSelected = createIsSelectedFn(viewValue),
34260
+ anySelected = false,
33663
34261
  lastElement,
33664
34262
  element,
33665
34263
  label;
33666
34264
 
33667
-
33668
34265
  // We now build up the list of options we need (we merge later)
33669
34266
  for (index = 0; length = keys.length, index < length; index++) {
33670
-
33671
34267
  key = index;
33672
34268
  if (keyName) {
33673
34269
  key = keys[index];
33674
34270
  if ( key.charAt(0) === '$' ) continue;
33675
- locals[keyName] = key;
33676
34271
  }
34272
+ value = values[key];
33677
34273
 
33678
- locals[valueName] = values[key];
33679
-
33680
- optionGroupName = groupByFn(scope, locals) || '';
34274
+ optionGroupName = callExpression(groupByFn, key, value) || '';
33681
34275
  if (!(optionGroup = optionGroups[optionGroupName])) {
33682
34276
  optionGroup = optionGroups[optionGroupName] = [];
33683
34277
  optionGroupNames.push(optionGroupName);
33684
34278
  }
33685
- if (multiple) {
33686
- selected = isDefined(
33687
- selectedSet.remove(trackFn ? trackFn(scope, locals) : valueFn(scope, locals))
33688
- );
33689
- } else {
33690
- if (trackFn) {
33691
- var modelCast = {};
33692
- modelCast[valueName] = modelValue;
33693
- selected = trackFn(scope, modelCast) === trackFn(scope, locals);
33694
- } else {
33695
- selected = modelValue === valueFn(scope, locals);
33696
- }
33697
- selectedSet = selectedSet || selected; // see if at least one item is selected
33698
- }
33699
- label = displayFn(scope, locals); // what will be seen by the user
34279
+
34280
+ selected = isSelected(key, value);
34281
+ anySelected = anySelected || selected;
34282
+
34283
+ label = callExpression(displayFn, key, value); // what will be seen by the user
33700
34284
 
33701
34285
  // doing displayFn(scope, locals) || '' overwrites zero values
33702
34286
  label = isDefined(label) ? label : '';
33703
34287
  optionGroup.push({
33704
34288
  // either the index into array or key from object
33705
- id: trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index),
34289
+ id: (keyName ? keys[index] : index),
33706
34290
  label: label,
33707
34291
  selected: selected // determine if we should be selected
33708
34292
  });
33709
34293
  }
33710
34294
  if (!multiple) {
33711
- if (nullOption || modelValue === null) {
34295
+ if (nullOption || viewValue === null) {
33712
34296
  // insert null option if we have a placeholder, or the model is null
33713
- optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
33714
- } else if (!selectedSet) {
34297
+ optionGroups[''].unshift({id:'', label:'', selected:!anySelected});
34298
+ } else if (!anySelected) {
33715
34299
  // option could not be found, we have to insert the undefined item
33716
34300
  optionGroups[''].unshift({id:'?', label:'', selected:true});
33717
34301
  }
@@ -33792,6 +34376,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
33792
34376
  id: option.id,
33793
34377
  selected: option.selected
33794
34378
  });
34379
+ selectCtrl.addOption(option.label, element);
33795
34380
  if (lastElement) {
33796
34381
  lastElement.after(element);
33797
34382
  } else {
@@ -33803,7 +34388,9 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
33803
34388
  // remove any excessive OPTIONs in a group
33804
34389
  index++; // increment since the existingOptions[0] is parent element not OPTION
33805
34390
  while(existingOptions.length > index) {
33806
- existingOptions.pop().element.remove();
34391
+ option = existingOptions.pop();
34392
+ selectCtrl.removeOption(option.label);
34393
+ option.element.remove();
33807
34394
  }
33808
34395
  }
33809
34396
  // remove any excessive OPTGROUPs from select
@@ -33839,11 +34426,7 @@ var optionDirective = ['$interpolate', function($interpolate) {
33839
34426
  selectCtrl = parent.data(selectCtrlName) ||
33840
34427
  parent.parent().data(selectCtrlName); // in case we are in optgroup
33841
34428
 
33842
- if (selectCtrl && selectCtrl.databound) {
33843
- // For some reason Opera defaults to true and if not overridden this messes up the repeater.
33844
- // We don't want the view to drive the initialization of the model anyway.
33845
- element.prop('selected', false);
33846
- } else {
34429
+ if (!selectCtrl || !selectCtrl.databound) {
33847
34430
  selectCtrl = nullSelectCtrl;
33848
34431
  }
33849
34432
 
@@ -34197,7 +34780,11 @@ _jQuery.fn.bindings = function(windowJquery, bindExp) {
34197
34780
  };
34198
34781
 
34199
34782
  (function() {
34200
- var msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1], 10);
34783
+ /**
34784
+ * documentMode is an IE-only property
34785
+ * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
34786
+ */
34787
+ var msie = document.documentMode;
34201
34788
 
34202
34789
  /**
34203
34790
  * Triggers a browser event. Attempts to choose the right event if one is
@@ -34248,97 +34835,71 @@ _jQuery.fn.bindings = function(windowJquery, bindExp) {
34248
34835
  return keys.indexOf(key) !== -1;
34249
34836
  }
34250
34837
 
34251
- if (msie < 9) {
34252
- if (inputType == 'radio' || inputType == 'checkbox') {
34253
- element.checked = !element.checked;
34838
+ var evnt;
34839
+ if(/transitionend/.test(eventType)) {
34840
+ if(window.WebKitTransitionEvent) {
34841
+ evnt = new WebKitTransitionEvent(eventType, eventData);
34842
+ evnt.initEvent(eventType, false, true);
34254
34843
  }
34255
-
34256
- // WTF!!! Error: Unspecified error.
34257
- // Don't know why, but some elements when detached seem to be in inconsistent state and
34258
- // calling .fireEvent() on them will result in very unhelpful error (Error: Unspecified error)
34259
- // forcing the browser to compute the element position (by reading its CSS)
34260
- // puts the element in consistent state.
34261
- element.style.posLeft;
34262
-
34263
- // TODO(vojta): create event objects with pressed keys to get it working on IE<9
34264
- var ret = element.fireEvent('on' + eventType);
34265
- if (inputType == 'submit') {
34266
- while(element) {
34267
- if (element.nodeName.toLowerCase() == 'form') {
34268
- element.fireEvent('onsubmit');
34269
- break;
34270
- }
34271
- element = element.parentNode;
34272
- }
34273
- }
34274
- return ret;
34275
- } else {
34276
- var evnt;
34277
- if(/transitionend/.test(eventType)) {
34278
- if(window.WebKitTransitionEvent) {
34279
- evnt = new WebKitTransitionEvent(eventType, eventData);
34280
- evnt.initEvent(eventType, false, true);
34844
+ else {
34845
+ try {
34846
+ evnt = new TransitionEvent(eventType, eventData);
34281
34847
  }
34282
- else {
34283
- try {
34284
- evnt = new TransitionEvent(eventType, eventData);
34285
- }
34286
- catch(e) {
34287
- evnt = document.createEvent('TransitionEvent');
34288
- evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
34289
- }
34848
+ catch(e) {
34849
+ evnt = document.createEvent('TransitionEvent');
34850
+ evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
34290
34851
  }
34291
34852
  }
34292
- else if(/animationend/.test(eventType)) {
34293
- if(window.WebKitAnimationEvent) {
34294
- evnt = new WebKitAnimationEvent(eventType, eventData);
34295
- evnt.initEvent(eventType, false, true);
34853
+ }
34854
+ else if(/animationend/.test(eventType)) {
34855
+ if(window.WebKitAnimationEvent) {
34856
+ evnt = new WebKitAnimationEvent(eventType, eventData);
34857
+ evnt.initEvent(eventType, false, true);
34858
+ }
34859
+ else {
34860
+ try {
34861
+ evnt = new AnimationEvent(eventType, eventData);
34296
34862
  }
34297
- else {
34298
- try {
34299
- evnt = new AnimationEvent(eventType, eventData);
34300
- }
34301
- catch(e) {
34302
- evnt = document.createEvent('AnimationEvent');
34303
- evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
34304
- }
34863
+ catch(e) {
34864
+ evnt = document.createEvent('AnimationEvent');
34865
+ evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
34305
34866
  }
34306
34867
  }
34307
- else {
34308
- evnt = document.createEvent('MouseEvents');
34309
- x = x || 0;
34310
- y = y || 0;
34311
- evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
34312
- pressed('alt'), pressed('shift'), pressed('meta'), 0, element);
34313
- }
34314
-
34315
- /* we're unable to change the timeStamp value directly so this
34316
- * is only here to allow for testing where the timeStamp value is
34317
- * read */
34318
- evnt.$manualTimeStamp = eventData.timeStamp;
34319
-
34320
- if(!evnt) return;
34321
-
34322
- var originalPreventDefault = evnt.preventDefault,
34323
- appWindow = element.ownerDocument.defaultView,
34324
- fakeProcessDefault = true,
34325
- finalProcessDefault,
34326
- angular = appWindow.angular || {};
34327
-
34328
- // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
34329
- angular['ff-684208-preventDefault'] = false;
34330
- evnt.preventDefault = function() {
34331
- fakeProcessDefault = false;
34332
- return originalPreventDefault.apply(evnt, arguments);
34333
- };
34868
+ }
34869
+ else {
34870
+ evnt = document.createEvent('MouseEvents');
34871
+ x = x || 0;
34872
+ y = y || 0;
34873
+ evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
34874
+ pressed('alt'), pressed('shift'), pressed('meta'), 0, element);
34875
+ }
34334
34876
 
34335
- element.dispatchEvent(evnt);
34336
- finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
34877
+ /* we're unable to change the timeStamp value directly so this
34878
+ * is only here to allow for testing where the timeStamp value is
34879
+ * read */
34880
+ evnt.$manualTimeStamp = eventData.timeStamp;
34337
34881
 
34338
- delete angular['ff-684208-preventDefault'];
34882
+ if(!evnt) return;
34339
34883
 
34340
- return finalProcessDefault;
34341
- }
34884
+ var originalPreventDefault = evnt.preventDefault,
34885
+ appWindow = element.ownerDocument.defaultView,
34886
+ fakeProcessDefault = true,
34887
+ finalProcessDefault,
34888
+ angular = appWindow.angular || {};
34889
+
34890
+ // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
34891
+ angular['ff-684208-preventDefault'] = false;
34892
+ evnt.preventDefault = function() {
34893
+ fakeProcessDefault = false;
34894
+ return originalPreventDefault.apply(evnt, arguments);
34895
+ };
34896
+
34897
+ element.dispatchEvent(evnt);
34898
+ finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
34899
+
34900
+ delete angular['ff-684208-preventDefault'];
34901
+
34902
+ return finalProcessDefault;
34342
34903
  };
34343
34904
  }());
34344
34905
 
@@ -36079,5 +36640,5 @@ if (config.autotest) {
36079
36640
  })(window, document);
36080
36641
 
36081
36642
 
36082
- !window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],\n.ng-cloak, .x-ng-cloak,\n.ng-hide:not(.ng-animate) {\n display: none !important;\n}\n\nng\\:form {\n display: block;\n}\n</style>');
36643
+ !window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],\n.ng-cloak, .x-ng-cloak,\n.ng-hide:not(.ng-hide-animate) {\n display: none !important;\n}\n\nng\\:form {\n display: block;\n}\n</style>');
36083
36644
  !window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n/* CSS Document */\n\n/** Structure */\nbody {\n font-family: Arial, sans-serif;\n margin: 0;\n font-size: 14px;\n}\n\n#system-error {\n font-size: 1.5em;\n text-align: center;\n}\n\n#json, #xml {\n display: none;\n}\n\n#header {\n position: fixed;\n width: 100%;\n}\n\n#specs {\n padding-top: 50px;\n}\n\n#header .angular {\n font-family: Courier New, monospace;\n font-weight: bold;\n}\n\n#header h1 {\n font-weight: normal;\n float: left;\n font-size: 30px;\n line-height: 30px;\n margin: 0;\n padding: 10px 10px;\n height: 30px;\n}\n\n#application h2,\n#specs h2 {\n margin: 0;\n padding: 0.5em;\n font-size: 1.1em;\n}\n\n#status-legend {\n margin-top: 10px;\n margin-right: 10px;\n}\n\n#header,\n#application,\n.test-info,\n.test-actions li {\n overflow: hidden;\n}\n\n#application {\n margin: 10px;\n}\n\n#application iframe {\n width: 100%;\n height: 758px;\n}\n\n#application .popout {\n float: right;\n}\n\n#application iframe {\n border: none;\n}\n\n.tests li,\n.test-actions li,\n.test-it li,\n.test-it ol,\n.status-display {\n list-style-type: none;\n}\n\n.tests,\n.test-it ol,\n.status-display {\n margin: 0;\n padding: 0;\n}\n\n.test-info {\n margin-left: 1em;\n margin-top: 0.5em;\n border-radius: 8px 0 0 8px;\n -webkit-border-radius: 8px 0 0 8px;\n -moz-border-radius: 8px 0 0 8px;\n cursor: pointer;\n}\n\n.test-info:hover .test-name {\n text-decoration: underline;\n}\n\n.test-info .closed:before {\n content: \'\\25b8\\00A0\';\n}\n\n.test-info .open:before {\n content: \'\\25be\\00A0\';\n font-weight: bold;\n}\n\n.test-it ol {\n margin-left: 2.5em;\n}\n\n.status-display,\n.status-display li {\n float: right;\n}\n\n.status-display li {\n padding: 5px 10px;\n}\n\n.timer-result,\n.test-title {\n display: inline-block;\n margin: 0;\n padding: 4px;\n}\n\n.test-actions .test-title,\n.test-actions .test-result {\n display: table-cell;\n padding-left: 0.5em;\n padding-right: 0.5em;\n}\n\n.test-actions {\n display: table;\n}\n\n.test-actions li {\n display: table-row;\n}\n\n.timer-result {\n width: 4em;\n padding: 0 10px;\n text-align: right;\n font-family: monospace;\n}\n\n.test-it pre,\n.test-actions pre {\n clear: left;\n color: black;\n margin-left: 6em;\n}\n\n.test-describe {\n padding-bottom: 0.5em;\n}\n\n.test-describe .test-describe {\n margin: 5px 5px 10px 2em;\n}\n\n.test-actions .status-pending .test-title:before {\n content: \'\\00bb\\00A0\';\n}\n\n.scrollpane {\n max-height: 20em;\n overflow: auto;\n}\n\n/** Colors */\n\n#header {\n background-color: #F2C200;\n}\n\n#specs h2 {\n border-top: 2px solid #BABAD1;\n}\n\n#specs h2,\n#application h2 {\n background-color: #efefef;\n}\n\n#application {\n border: 1px solid #BABAD1;\n}\n\n.test-describe .test-describe {\n border-left: 1px solid #BABAD1;\n border-right: 1px solid #BABAD1;\n border-bottom: 1px solid #BABAD1;\n}\n\n.status-display {\n border: 1px solid #777;\n}\n\n.status-display .status-pending,\n.status-pending .test-info {\n background-color: #F9EEBC;\n}\n\n.status-display .status-success,\n.status-success .test-info {\n background-color: #B1D7A1;\n}\n\n.status-display .status-failure,\n.status-failure .test-info {\n background-color: #FF8286;\n}\n\n.status-display .status-error,\n.status-error .test-info {\n background-color: black;\n color: white;\n}\n\n.test-actions .status-success .test-title {\n color: #30B30A;\n}\n\n.test-actions .status-failure .test-title {\n color: #DF0000;\n}\n\n.test-actions .status-error .test-title {\n color: black;\n}\n\n.test-actions .timer-result {\n color: #888;\n}\n</style>');