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.
- checksums.yaml +4 -4
- data/lib/angularjs-rails/version.rb +2 -2
- data/vendor/assets/javascripts/angular-animate.js +1 -1
- data/vendor/assets/javascripts/angular-cookies.js +1 -1
- data/vendor/assets/javascripts/angular-loader.js +2 -2
- data/vendor/assets/javascripts/angular-mocks.js +1 -1
- data/vendor/assets/javascripts/angular-resource.js +1 -1
- data/vendor/assets/javascripts/angular-route.js +1 -4
- data/vendor/assets/javascripts/angular-sanitize.js +1 -1
- data/vendor/assets/javascripts/angular-scenario.js +30 -24
- data/vendor/assets/javascripts/angular-touch.js +1 -1
- data/vendor/assets/javascripts/angular.js +30 -24
- data/vendor/assets/javascripts/unstable/angular-animate.js +109 -46
- data/vendor/assets/javascripts/unstable/angular-aria.js +1 -1
- data/vendor/assets/javascripts/unstable/angular-cookies.js +1 -1
- data/vendor/assets/javascripts/unstable/angular-loader.js +2 -2
- data/vendor/assets/javascripts/unstable/angular-messages.js +1 -1
- data/vendor/assets/javascripts/unstable/angular-mocks.js +15 -4
- data/vendor/assets/javascripts/unstable/angular-resource.js +1 -1
- data/vendor/assets/javascripts/unstable/angular-route.js +69 -43
- data/vendor/assets/javascripts/unstable/angular-sanitize.js +1 -1
- data/vendor/assets/javascripts/unstable/angular-scenario.js +1152 -591
- data/vendor/assets/javascripts/unstable/angular-touch.js +1 -1
- data/vendor/assets/javascripts/unstable/angular.js +1092 -509
- metadata +14 -14
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.3.0-rc.
|
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.
|
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.
|
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
|
56
|
+
if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) {
|
57
57
|
self.$$lastUrl = self.$$url;
|
58
|
-
|
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.
|
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(
|
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('$
|
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
|
516
|
-
var
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
&& angular.equals(
|
521
|
-
&& !
|
522
|
-
|
523
|
-
|
524
|
-
$rootScope.$broadcast('$
|
525
|
-
|
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
|
-
$
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
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(
|
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(
|
566
|
+
$q.when(nextRoute).
|
542
567
|
then(function() {
|
543
|
-
if (
|
544
|
-
var locals = angular.extend({},
|
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 =
|
577
|
+
if (angular.isDefined(template = nextRoute.template)) {
|
553
578
|
if (angular.isFunction(template)) {
|
554
|
-
template = template(
|
579
|
+
template = template(nextRoute.params);
|
555
580
|
}
|
556
|
-
} else if (angular.isDefined(templateUrl =
|
581
|
+
} else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
|
557
582
|
if (angular.isFunction(templateUrl)) {
|
558
|
-
templateUrl = templateUrl(
|
583
|
+
templateUrl = templateUrl(nextRoute.params);
|
559
584
|
}
|
560
585
|
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
|
561
586
|
if (angular.isDefined(templateUrl)) {
|
562
|
-
|
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 (
|
575
|
-
if (
|
576
|
-
|
577
|
-
angular.copy(
|
599
|
+
if (nextRoute == $route.current) {
|
600
|
+
if (nextRoute) {
|
601
|
+
nextRoute.locals = locals;
|
602
|
+
angular.copy(nextRoute.params, $routeParams);
|
578
603
|
}
|
579
|
-
$rootScope.$broadcast('$routeChangeSuccess',
|
604
|
+
$rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
|
580
605
|
}
|
581
606
|
}, function(error) {
|
582
|
-
if (
|
583
|
-
$rootScope.$broadcast('$routeChangeError',
|
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
|
-
|
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(
|
866
|
-
|
867
|
-
|
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)
|
875
|
-
|
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
|
}
|
@@ -9190,7 +9190,7 @@ return jQuery;
|
|
9190
9190
|
}));
|
9191
9191
|
|
9192
9192
|
/**
|
9193
|
-
* @license AngularJS v1.3.0-rc.
|
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.
|
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
|
-
*
|
9448
|
-
*
|
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 =
|
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 ===
|
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 ===
|
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.
|
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: '
|
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 ===
|
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 ==
|
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 ===
|
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 ===
|
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 ===
|
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 !==
|
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 ===
|
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 !==
|
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
|
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
|
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}
|
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
|
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
|
13670
|
-
|
13671
|
-
|
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
|
-
|
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
|
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
|
-
|
13845
|
-
|
13846
|
-
|
13847
|
-
|
13848
|
-
|
13849
|
-
|
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
|
-
|
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
|
-
// -
|
13863
|
-
//
|
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
|
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
|
-
|
13874
|
-
|
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
|
-
|
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
|
-
*
|
14770
|
-
*
|
14771
|
-
*
|
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
|
-
*
|
14778
|
-
*
|
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
|
-
*
|
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
|
-
*
|
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
|
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 ==
|
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
|
-
|
15489
|
-
|
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
|
-
|
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
|
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
|
-
|
15693
|
-
|
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
|
-
|
15703
|
-
|
15704
|
-
|
15705
|
-
|
15706
|
-
|
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
|
-
|
15712
|
-
|
15713
|
-
if (
|
15714
|
-
|
15715
|
-
|
15716
|
-
|
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
|
16034
|
+
case NODE_TYPE_TEXT: /* Text Node */
|
15738
16035
|
addTextInterpolateDirective(directives, node.nodeValue);
|
15739
16036
|
break;
|
15740
|
-
case
|
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 ==
|
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 =
|
16256
|
+
$template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
|
15960
16257
|
}
|
15961
16258
|
compileNode = $template[0];
|
15962
16259
|
|
15963
|
-
if ($template.length != 1 || compileNode.nodeType !==
|
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
|
-
|
16069
|
-
|
16070
|
-
|
16071
|
-
|
16072
|
-
|
16073
|
-
|
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 || $
|
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 =
|
16738
|
+
$template = removeComments(wrapTemplate(templateNamespace, trim(content)));
|
16430
16739
|
}
|
16431
16740
|
compileNode = $template[0];
|
16432
16741
|
|
16433
|
-
if ($template.length != 1 || compileNode.nodeType !==
|
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
|
-
|
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
|
-
|
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(
|
18133
|
-
|
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(
|
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
|
-
|
18201
|
-
|
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
|
-
|
18225
|
-
|
18226
|
-
|
18227
|
-
|
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
|
-
|
18231
|
-
|
18232
|
-
|
18233
|
-
|
18234
|
-
|
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
|
-
|
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 `
|
19550
|
-
*
|
19551
|
-
*
|
19552
|
-
* in browsers that do not
|
19553
|
-
*
|
19554
|
-
*
|
19555
|
-
*
|
19556
|
-
* thrown when `$location` is injected.
|
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
|
-
|
19567
|
-
|
19568
|
-
|
19569
|
-
|
19570
|
-
|
19571
|
-
|
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.
|
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
|
-
|
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
|
-
|
19683
|
-
|
19684
|
-
|
19685
|
-
|
19686
|
-
|
19687
|
-
|
19688
|
-
|
19689
|
-
|
19690
|
-
|
19691
|
-
|
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 (
|
19702
|
-
|
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
|
-
|
19709
|
-
|
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
|
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
|
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
|
21928
|
-
child
|
21929
|
-
|
21930
|
-
|
21931
|
-
|
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
|
-
|
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 &&
|
23078
|
+
if (!$rootScope.$$phase && !asyncQueue.length) {
|
22664
23079
|
$browser.defer(function() {
|
22665
|
-
if (
|
23080
|
+
if (asyncQueue.length) {
|
22666
23081
|
$rootScope.$digest();
|
22667
23082
|
}
|
22668
23083
|
});
|
22669
23084
|
}
|
22670
23085
|
|
22671
|
-
|
23086
|
+
asyncQueue.push({scope: this, expression: expr});
|
22672
23087
|
},
|
22673
23088
|
|
22674
23089
|
$$postDigest : function(fn) {
|
22675
|
-
|
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 &&
|
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
|
-
|
23002
|
-
while (queue.length) {
|
23421
|
+
while (applyAsyncQueue.length) {
|
23003
23422
|
try {
|
23004
|
-
|
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
|
-
|
23084
|
-
if (
|
23085
|
-
normalizedVal
|
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)
|
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
|
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
|
-
|
27948
|
-
|
27949
|
-
|
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 (
|
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:
|
28304
|
-
|
28305
|
-
|
28306
|
-
|
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
|
28905
|
-
|
28906
|
-
|
28907
|
-
|
28908
|
-
|
28909
|
-
|
28910
|
-
|
28911
|
-
|
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
|
-
|
29229
|
-
|
29230
|
-
|
29231
|
-
|
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
|
-
|
29654
|
+
return {
|
29655
|
+
pre: function ngModelPreLink(scope, element, attr, ctrls) {
|
29656
|
+
var modelCtrl = ctrls[0],
|
29657
|
+
formCtrl = ctrls[1] || nullFormCtrl;
|
29234
29658
|
|
29235
|
-
|
29236
|
-
formCtrl.$addControl(modelCtrl);
|
29659
|
+
modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
|
29237
29660
|
|
29238
|
-
|
29239
|
-
|
29240
|
-
formCtrl.$$renameControl(modelCtrl, newValue);
|
29241
|
-
}
|
29242
|
-
});
|
29661
|
+
// notify others, especially parent forms
|
29662
|
+
formCtrl.$addControl(modelCtrl);
|
29243
29663
|
|
29244
|
-
|
29245
|
-
|
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
|
-
|
29257
|
-
|
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
|
-
|
29260
|
-
|
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
|
-
|
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.
|
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.
|
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/
|
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
|
-
|
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
|
-
|
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',
|
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
|
-
|
33547
|
-
|
33548
|
-
|
33549
|
-
|
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
|
-
|
33571
|
-
|
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(
|
34163
|
+
ctrl.$setViewValue(viewValue);
|
33592
34164
|
render();
|
33593
34165
|
});
|
33594
|
-
}
|
34166
|
+
}
|
33595
34167
|
|
33596
|
-
|
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
|
-
|
33599
|
-
|
33600
|
-
var
|
33601
|
-
|
33602
|
-
|
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
|
-
|
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
|
-
|
33611
|
-
|
33612
|
-
if (multiple) {
|
33613
|
-
scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
|
34197
|
+
return toDisplay;
|
33614
34198
|
}
|
33615
34199
|
|
33616
|
-
|
33617
|
-
|
33618
|
-
var selectedSet = false;
|
34200
|
+
function createIsSelectedFn(viewValue) {
|
34201
|
+
var selectedSet;
|
33619
34202
|
if (multiple) {
|
33620
|
-
|
33621
|
-
|
34203
|
+
if (!selectAs && trackFn && isArray(viewValue)) {
|
34204
|
+
|
33622
34205
|
selectedSet = new HashMap([]);
|
33623
|
-
var
|
33624
|
-
|
33625
|
-
|
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(
|
34211
|
+
selectedSet = new HashMap(viewValue);
|
33630
34212
|
}
|
34213
|
+
} else if (!selectAsFn && trackFn) {
|
34214
|
+
viewValue = callExpression(trackFn, null, viewValue);
|
33631
34215
|
}
|
33632
|
-
return
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
33686
|
-
|
33687
|
-
|
33688
|
-
|
33689
|
-
|
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:
|
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 ||
|
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:!
|
33714
|
-
} else if (!
|
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()
|
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
|
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
|
-
|
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
|
-
|
34252
|
-
|
34253
|
-
|
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
|
-
|
34257
|
-
|
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
|
-
|
34283
|
-
|
34284
|
-
|
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
|
-
|
34293
|
-
|
34294
|
-
|
34295
|
-
|
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
|
-
|
34298
|
-
|
34299
|
-
|
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
|
-
|
34308
|
-
|
34309
|
-
|
34310
|
-
|
34311
|
-
|
34312
|
-
|
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
|
-
|
34336
|
-
|
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
|
-
|
34882
|
+
if(!evnt) return;
|
34339
34883
|
|
34340
|
-
|
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>');
|