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
|
*/
|
@@ -71,7 +71,7 @@ function minErr(module, ErrorConstructor) {
|
|
71
71
|
return match;
|
72
72
|
});
|
73
73
|
|
74
|
-
message = message + '\nhttp://errors.angularjs.org/1.3.0-rc.
|
74
|
+
message = message + '\nhttp://errors.angularjs.org/1.3.0-rc.5/' +
|
75
75
|
(module ? module + '/' : '') + code;
|
76
76
|
for (i = 2; i < arguments.length; i++) {
|
77
77
|
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
|
@@ -87,6 +87,7 @@ function minErr(module, ErrorConstructor) {
|
|
87
87
|
jqLite: true,
|
88
88
|
jQuery: true,
|
89
89
|
slice: true,
|
90
|
+
splice: true,
|
90
91
|
push: true,
|
91
92
|
toString: true,
|
92
93
|
ngMinErr: true,
|
@@ -163,6 +164,12 @@ function minErr(module, ErrorConstructor) {
|
|
163
164
|
getBlockNodes: true,
|
164
165
|
hasOwnProperty: true,
|
165
166
|
createMap: true,
|
167
|
+
|
168
|
+
NODE_TYPE_ELEMENT: true,
|
169
|
+
NODE_TYPE_TEXT: true,
|
170
|
+
NODE_TYPE_COMMENT: true,
|
171
|
+
NODE_TYPE_DOCUMENT: true,
|
172
|
+
NODE_TYPE_DOCUMENT_FRAGMENT: true,
|
166
173
|
*/
|
167
174
|
|
168
175
|
////////////////////////////////////
|
@@ -242,6 +249,7 @@ var /** holds major version number for IE or NaN for real browsers */
|
|
242
249
|
jqLite, // delay binding since jQuery could be loaded after us.
|
243
250
|
jQuery, // delay binding
|
244
251
|
slice = [].slice,
|
252
|
+
splice = [].splice,
|
245
253
|
push = [].push,
|
246
254
|
toString = Object.prototype.toString,
|
247
255
|
ngMinErr = minErr('ng'),
|
@@ -252,13 +260,10 @@ var /** holds major version number for IE or NaN for real browsers */
|
|
252
260
|
uid = 0;
|
253
261
|
|
254
262
|
/**
|
255
|
-
*
|
256
|
-
*
|
263
|
+
* documentMode is an IE-only property
|
264
|
+
* http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
|
257
265
|
*/
|
258
|
-
msie =
|
259
|
-
if (isNaN(msie)) {
|
260
|
-
msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
|
261
|
-
}
|
266
|
+
msie = document.documentMode;
|
262
267
|
|
263
268
|
|
264
269
|
/**
|
@@ -274,7 +279,7 @@ function isArrayLike(obj) {
|
|
274
279
|
|
275
280
|
var length = obj.length;
|
276
281
|
|
277
|
-
if (obj.nodeType ===
|
282
|
+
if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
|
278
283
|
return true;
|
279
284
|
}
|
280
285
|
|
@@ -1110,11 +1115,9 @@ function startingTag(element) {
|
|
1110
1115
|
// are not allowed to have children. So we just ignore it.
|
1111
1116
|
element.empty();
|
1112
1117
|
} catch(e) {}
|
1113
|
-
// As Per DOM Standards
|
1114
|
-
var TEXT_NODE = 3;
|
1115
1118
|
var elemHtml = jqLite('<div>').append(element).html();
|
1116
1119
|
try {
|
1117
|
-
return element[0].nodeType ===
|
1120
|
+
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
|
1118
1121
|
elemHtml.
|
1119
1122
|
match(/^(<[^>]+>)/)[1].
|
1120
1123
|
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
|
@@ -1693,6 +1696,12 @@ function createMap() {
|
|
1693
1696
|
return Object.create(null);
|
1694
1697
|
}
|
1695
1698
|
|
1699
|
+
var NODE_TYPE_ELEMENT = 1;
|
1700
|
+
var NODE_TYPE_TEXT = 3;
|
1701
|
+
var NODE_TYPE_COMMENT = 8;
|
1702
|
+
var NODE_TYPE_DOCUMENT = 9;
|
1703
|
+
var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
|
1704
|
+
|
1696
1705
|
/**
|
1697
1706
|
* @ngdoc type
|
1698
1707
|
* @name angular.Module
|
@@ -2112,11 +2121,11 @@ function setupModuleLoader(window) {
|
|
2112
2121
|
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
|
2113
2122
|
*/
|
2114
2123
|
var version = {
|
2115
|
-
full: '1.3.0-rc.
|
2124
|
+
full: '1.3.0-rc.5', // all of these placeholder strings will be replaced by grunt's
|
2116
2125
|
major: 1, // package task
|
2117
2126
|
minor: 3,
|
2118
2127
|
dot: 0,
|
2119
|
-
codeName: '
|
2128
|
+
codeName: 'impossible-choreography'
|
2120
2129
|
};
|
2121
2130
|
|
2122
2131
|
|
@@ -2150,8 +2159,7 @@ function publishExternalAPI(angular){
|
|
2150
2159
|
'getTestability': getTestability,
|
2151
2160
|
'$$minErr': minErr,
|
2152
2161
|
'$$csp': csp,
|
2153
|
-
'reloadWithDebugInfo': reloadWithDebugInfo
|
2154
|
-
'$$hasClass': jqLiteHasClass
|
2162
|
+
'reloadWithDebugInfo': reloadWithDebugInfo
|
2155
2163
|
});
|
2156
2164
|
|
2157
2165
|
angularModule = setupModuleLoader(window);
|
@@ -2297,7 +2305,7 @@ function publishExternalAPI(angular){
|
|
2297
2305
|
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
|
2298
2306
|
* - [`clone()`](http://api.jquery.com/clone/)
|
2299
2307
|
* - [`contents()`](http://api.jquery.com/contents/)
|
2300
|
-
* - [`css()`](http://api.jquery.com/css/)
|
2308
|
+
* - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyles()`
|
2301
2309
|
* - [`data()`](http://api.jquery.com/data/)
|
2302
2310
|
* - [`detach()`](http://api.jquery.com/detach/)
|
2303
2311
|
* - [`empty()`](http://api.jquery.com/empty/)
|
@@ -2419,7 +2427,7 @@ function jqLiteAcceptsData(node) {
|
|
2419
2427
|
// The window object can accept data but has no nodeType
|
2420
2428
|
// Otherwise we are only interested in elements (1) and documents (9)
|
2421
2429
|
var nodeType = node.nodeType;
|
2422
|
-
return nodeType ===
|
2430
|
+
return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
|
2423
2431
|
}
|
2424
2432
|
|
2425
2433
|
function jqLiteBuildFragment(html, context) {
|
@@ -2672,7 +2680,7 @@ function jqLiteController(element, name) {
|
|
2672
2680
|
function jqLiteInheritedData(element, name, value) {
|
2673
2681
|
// if element is the document object work with the html element instead
|
2674
2682
|
// this makes $(document).scope() possible
|
2675
|
-
if(element.nodeType ==
|
2683
|
+
if(element.nodeType == NODE_TYPE_DOCUMENT) {
|
2676
2684
|
element = element.documentElement;
|
2677
2685
|
}
|
2678
2686
|
var names = isArray(name) ? name : [name];
|
@@ -2685,7 +2693,7 @@ function jqLiteInheritedData(element, name, value) {
|
|
2685
2693
|
// If dealing with a document fragment node with a host element, and no parent, use the host
|
2686
2694
|
// element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
|
2687
2695
|
// to lookup parent controllers.
|
2688
|
-
element = element.parentNode || (element.nodeType ===
|
2696
|
+
element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
|
2689
2697
|
}
|
2690
2698
|
}
|
2691
2699
|
|
@@ -2863,7 +2871,7 @@ forEach({
|
|
2863
2871
|
function getText(element, value) {
|
2864
2872
|
if (isUndefined(value)) {
|
2865
2873
|
var nodeType = element.nodeType;
|
2866
|
-
return (nodeType ===
|
2874
|
+
return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
|
2867
2875
|
}
|
2868
2876
|
element.textContent = value;
|
2869
2877
|
}
|
@@ -3085,7 +3093,7 @@ forEach({
|
|
3085
3093
|
children: function(element) {
|
3086
3094
|
var children = [];
|
3087
3095
|
forEach(element.childNodes, function(element){
|
3088
|
-
if (element.nodeType ===
|
3096
|
+
if (element.nodeType === NODE_TYPE_ELEMENT)
|
3089
3097
|
children.push(element);
|
3090
3098
|
});
|
3091
3099
|
return children;
|
@@ -3097,7 +3105,7 @@ forEach({
|
|
3097
3105
|
|
3098
3106
|
append: function(element, node) {
|
3099
3107
|
var nodeType = element.nodeType;
|
3100
|
-
if (nodeType !==
|
3108
|
+
if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
|
3101
3109
|
|
3102
3110
|
node = new JQLite(node);
|
3103
3111
|
|
@@ -3108,7 +3116,7 @@ forEach({
|
|
3108
3116
|
},
|
3109
3117
|
|
3110
3118
|
prepend: function(element, node) {
|
3111
|
-
if (element.nodeType ===
|
3119
|
+
if (element.nodeType === NODE_TYPE_ELEMENT) {
|
3112
3120
|
var index = element.firstChild;
|
3113
3121
|
forEach(new JQLite(node), function(child){
|
3114
3122
|
element.insertBefore(child, index);
|
@@ -3159,7 +3167,7 @@ forEach({
|
|
3159
3167
|
|
3160
3168
|
parent: function(element) {
|
3161
3169
|
var parent = element.parentNode;
|
3162
|
-
return parent && parent.nodeType !==
|
3170
|
+
return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
|
3163
3171
|
},
|
3164
3172
|
|
3165
3173
|
next: function(element) {
|
@@ -3318,13 +3326,13 @@ HashMap.prototype = {
|
|
3318
3326
|
* @kind function
|
3319
3327
|
*
|
3320
3328
|
* @description
|
3321
|
-
* Creates an injector
|
3329
|
+
* Creates an injector object that can be used for retrieving services as well as for
|
3322
3330
|
* dependency injection (see {@link guide/di dependency injection}).
|
3323
3331
|
*
|
3324
3332
|
|
3325
3333
|
* @param {Array.<string|Function>} modules A list of module functions or their aliases. See
|
3326
3334
|
* {@link angular.module}. The `ng` module must be explicitly added.
|
3327
|
-
* @returns {function()} Injector
|
3335
|
+
* @returns {function()} Injector object. See {@link auto.$injector $injector}.
|
3328
3336
|
*
|
3329
3337
|
* @example
|
3330
3338
|
* Typical usage
|
@@ -3431,7 +3439,6 @@ function annotate(fn, strictDi, name) {
|
|
3431
3439
|
/**
|
3432
3440
|
* @ngdoc service
|
3433
3441
|
* @name $injector
|
3434
|
-
* @kind function
|
3435
3442
|
*
|
3436
3443
|
* @description
|
3437
3444
|
*
|
@@ -3446,7 +3453,7 @@ function annotate(fn, strictDi, name) {
|
|
3446
3453
|
* expect($injector.get('$injector')).toBe($injector);
|
3447
3454
|
* expect($injector.invoke(function($injector) {
|
3448
3455
|
* return $injector;
|
3449
|
-
* }).toBe($injector);
|
3456
|
+
* })).toBe($injector);
|
3450
3457
|
* ```
|
3451
3458
|
*
|
3452
3459
|
* # Injection Function Annotation
|
@@ -3513,8 +3520,8 @@ function annotate(fn, strictDi, name) {
|
|
3513
3520
|
* @description
|
3514
3521
|
* Allows the user to query if the particular service exists.
|
3515
3522
|
*
|
3516
|
-
* @param {string} Name of the service to query.
|
3517
|
-
* @returns {boolean}
|
3523
|
+
* @param {string} name Name of the service to query.
|
3524
|
+
* @returns {boolean} `true` if injector has given service.
|
3518
3525
|
*/
|
3519
3526
|
|
3520
3527
|
/**
|
@@ -3974,7 +3981,21 @@ function createInjector(modulesToLoad, strictDi) {
|
|
3974
3981
|
return providerCache[name + providerSuffix] = provider_;
|
3975
3982
|
}
|
3976
3983
|
|
3977
|
-
function
|
3984
|
+
function enforceReturnValue(name, factory) {
|
3985
|
+
return function enforcedReturnValue() {
|
3986
|
+
var result = instanceInjector.invoke(factory);
|
3987
|
+
if (isUndefined(result)) {
|
3988
|
+
throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
|
3989
|
+
}
|
3990
|
+
return result;
|
3991
|
+
};
|
3992
|
+
}
|
3993
|
+
|
3994
|
+
function factory(name, factoryFn, enforce) {
|
3995
|
+
return provider(name, {
|
3996
|
+
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
|
3997
|
+
});
|
3998
|
+
}
|
3978
3999
|
|
3979
4000
|
function service(name, constructor) {
|
3980
4001
|
return factory(name, ['$injector', function($injector) {
|
@@ -3982,7 +4003,7 @@ function createInjector(modulesToLoad, strictDi) {
|
|
3982
4003
|
}]);
|
3983
4004
|
}
|
3984
4005
|
|
3985
|
-
function value(name, val) { return factory(name, valueFn(val)); }
|
4006
|
+
function value(name, val) { return factory(name, valueFn(val), false); }
|
3986
4007
|
|
3987
4008
|
function constant(name, value) {
|
3988
4009
|
assertNotHasOwnProperty(name, 'constant');
|
@@ -4233,7 +4254,10 @@ function $AnchorScrollProvider() {
|
|
4233
4254
|
// (no url change, no $location.hash() change), browser native does scroll
|
4234
4255
|
if (autoScrollingEnabled) {
|
4235
4256
|
$rootScope.$watch(function autoScrollWatch() {return $location.hash();},
|
4236
|
-
function autoScrollWatchAction() {
|
4257
|
+
function autoScrollWatchAction(newVal, oldVal) {
|
4258
|
+
// skip the initial scroll if $location.hash is empty
|
4259
|
+
if (newVal === oldVal && newVal === '') return;
|
4260
|
+
|
4237
4261
|
$rootScope.$evalAsync(scroll);
|
4238
4262
|
});
|
4239
4263
|
}
|
@@ -4323,9 +4347,57 @@ var $AnimateProvider = ['$provide', function($provide) {
|
|
4323
4347
|
return this.$$classNameFilter;
|
4324
4348
|
};
|
4325
4349
|
|
4326
|
-
this.$get = ['$$q', '$$asyncCallback', function($$q, $$asyncCallback) {
|
4350
|
+
this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) {
|
4327
4351
|
|
4328
4352
|
var currentDefer;
|
4353
|
+
|
4354
|
+
function runAnimationPostDigest(fn) {
|
4355
|
+
var cancelFn, defer = $$q.defer();
|
4356
|
+
defer.promise.$$cancelFn = function ngAnimateMaybeCancel() {
|
4357
|
+
cancelFn && cancelFn();
|
4358
|
+
};
|
4359
|
+
|
4360
|
+
$rootScope.$$postDigest(function ngAnimatePostDigest() {
|
4361
|
+
cancelFn = fn(function ngAnimateNotifyComplete() {
|
4362
|
+
defer.resolve();
|
4363
|
+
});
|
4364
|
+
});
|
4365
|
+
|
4366
|
+
return defer.promise;
|
4367
|
+
}
|
4368
|
+
|
4369
|
+
function resolveElementClasses(element, cache) {
|
4370
|
+
var toAdd = [], toRemove = [];
|
4371
|
+
|
4372
|
+
var hasClasses = createMap();
|
4373
|
+
forEach((element.attr('class') || '').split(/\s+/), function(className) {
|
4374
|
+
hasClasses[className] = true;
|
4375
|
+
});
|
4376
|
+
|
4377
|
+
forEach(cache.classes, function(status, className) {
|
4378
|
+
var hasClass = hasClasses[className];
|
4379
|
+
|
4380
|
+
// If the most recent class manipulation (via $animate) was to remove the class, and the
|
4381
|
+
// element currently has the class, the class is scheduled for removal. Otherwise, if
|
4382
|
+
// the most recent class manipulation (via $animate) was to add the class, and the
|
4383
|
+
// element does not currently have the class, the class is scheduled to be added.
|
4384
|
+
if (status === false && hasClass) {
|
4385
|
+
toRemove.push(className);
|
4386
|
+
} else if (status === true && !hasClass) {
|
4387
|
+
toAdd.push(className);
|
4388
|
+
}
|
4389
|
+
});
|
4390
|
+
|
4391
|
+
return (toAdd.length + toRemove.length) > 0 && [toAdd.length && toAdd, toRemove.length && toRemove];
|
4392
|
+
}
|
4393
|
+
|
4394
|
+
function cachedClassManipulation(cache, classes, op) {
|
4395
|
+
for (var i=0, ii = classes.length; i < ii; ++i) {
|
4396
|
+
var className = classes[i];
|
4397
|
+
cache[className] = op;
|
4398
|
+
}
|
4399
|
+
}
|
4400
|
+
|
4329
4401
|
function asyncPromise() {
|
4330
4402
|
// only serve one instance of a promise in order to save CPU cycles
|
4331
4403
|
if (!currentDefer) {
|
@@ -4429,13 +4501,17 @@ var $AnimateProvider = ['$provide', function($provide) {
|
|
4429
4501
|
* @return {Promise} the animation callback promise
|
4430
4502
|
*/
|
4431
4503
|
addClass : function(element, className) {
|
4504
|
+
return this.setClass(element, className, []);
|
4505
|
+
},
|
4506
|
+
|
4507
|
+
$$addClassImmediately : function addClassImmediately(element, className) {
|
4508
|
+
element = jqLite(element);
|
4432
4509
|
className = !isString(className)
|
4433
4510
|
? (isArray(className) ? className.join(' ') : '')
|
4434
4511
|
: className;
|
4435
4512
|
forEach(element, function (element) {
|
4436
4513
|
jqLiteAddClass(element, className);
|
4437
4514
|
});
|
4438
|
-
return asyncPromise();
|
4439
4515
|
},
|
4440
4516
|
|
4441
4517
|
/**
|
@@ -4451,6 +4527,11 @@ var $AnimateProvider = ['$provide', function($provide) {
|
|
4451
4527
|
* @return {Promise} the animation callback promise
|
4452
4528
|
*/
|
4453
4529
|
removeClass : function(element, className) {
|
4530
|
+
return this.setClass(element, [], className);
|
4531
|
+
},
|
4532
|
+
|
4533
|
+
$$removeClassImmediately : function removeClassImmediately(element, className) {
|
4534
|
+
element = jqLite(element);
|
4454
4535
|
className = !isString(className)
|
4455
4536
|
? (isArray(className) ? className.join(' ') : '')
|
4456
4537
|
: className;
|
@@ -4473,10 +4554,53 @@ var $AnimateProvider = ['$provide', function($provide) {
|
|
4473
4554
|
* @param {string} remove the CSS class which will be removed from the element
|
4474
4555
|
* @return {Promise} the animation callback promise
|
4475
4556
|
*/
|
4476
|
-
setClass : function(element, add, remove) {
|
4477
|
-
this
|
4478
|
-
|
4479
|
-
|
4557
|
+
setClass : function(element, add, remove, runSynchronously) {
|
4558
|
+
var self = this;
|
4559
|
+
var STORAGE_KEY = '$$animateClasses';
|
4560
|
+
var createdCache = false;
|
4561
|
+
element = jqLite(element);
|
4562
|
+
|
4563
|
+
if (runSynchronously) {
|
4564
|
+
// TODO(@caitp/@matsko): Remove undocumented `runSynchronously` parameter, and always
|
4565
|
+
// perform DOM manipulation asynchronously or in postDigest.
|
4566
|
+
self.$$addClassImmediately(element, add);
|
4567
|
+
self.$$removeClassImmediately(element, remove);
|
4568
|
+
return asyncPromise();
|
4569
|
+
}
|
4570
|
+
|
4571
|
+
var cache = element.data(STORAGE_KEY);
|
4572
|
+
if (!cache) {
|
4573
|
+
cache = {
|
4574
|
+
classes: {}
|
4575
|
+
};
|
4576
|
+
createdCache = true;
|
4577
|
+
}
|
4578
|
+
|
4579
|
+
var classes = cache.classes;
|
4580
|
+
|
4581
|
+
add = isArray(add) ? add : add.split(' ');
|
4582
|
+
remove = isArray(remove) ? remove : remove.split(' ');
|
4583
|
+
cachedClassManipulation(classes, add, true);
|
4584
|
+
cachedClassManipulation(classes, remove, false);
|
4585
|
+
|
4586
|
+
if (createdCache) {
|
4587
|
+
cache.promise = runAnimationPostDigest(function(done) {
|
4588
|
+
var cache = element.data(STORAGE_KEY);
|
4589
|
+
element.removeData(STORAGE_KEY);
|
4590
|
+
|
4591
|
+
var classes = cache && resolveElementClasses(element, cache);
|
4592
|
+
|
4593
|
+
if (classes) {
|
4594
|
+
if (classes[0]) self.$$addClassImmediately(element, classes[0]);
|
4595
|
+
if (classes[1]) self.$$removeClassImmediately(element, classes[1]);
|
4596
|
+
}
|
4597
|
+
|
4598
|
+
done();
|
4599
|
+
});
|
4600
|
+
element.data(STORAGE_KEY, cache);
|
4601
|
+
}
|
4602
|
+
|
4603
|
+
return cache.promise;
|
4480
4604
|
},
|
4481
4605
|
|
4482
4606
|
enabled : noop,
|
@@ -4495,6 +4619,8 @@ function $$AsyncCallbackProvider(){
|
|
4495
4619
|
}];
|
4496
4620
|
}
|
4497
4621
|
|
4622
|
+
/* global stripHash: true */
|
4623
|
+
|
4498
4624
|
/**
|
4499
4625
|
* ! This is a private undocumented service !
|
4500
4626
|
*
|
@@ -4618,8 +4744,9 @@ function Browser(window, document, $log, $sniffer) {
|
|
4618
4744
|
//////////////////////////////////////////////////////////////
|
4619
4745
|
|
4620
4746
|
var lastBrowserUrl = location.href,
|
4747
|
+
lastHistoryState = history.state,
|
4621
4748
|
baseElement = document.find('base'),
|
4622
|
-
|
4749
|
+
reloadLocation = null;
|
4623
4750
|
|
4624
4751
|
/**
|
4625
4752
|
* @name $browser#url
|
@@ -4638,26 +4765,42 @@ function Browser(window, document, $log, $sniffer) {
|
|
4638
4765
|
* {@link ng.$location $location service} to change url.
|
4639
4766
|
*
|
4640
4767
|
* @param {string} url New url (when used as setter)
|
4641
|
-
* @param {boolean=} replace Should new url replace current history record
|
4768
|
+
* @param {boolean=} replace Should new url replace current history record?
|
4769
|
+
* @param {object=} state object to use with pushState/replaceState
|
4642
4770
|
*/
|
4643
|
-
self.url = function(url, replace) {
|
4771
|
+
self.url = function(url, replace, state) {
|
4772
|
+
// In modern browsers `history.state` is `null` by default; treating it separately
|
4773
|
+
// from `undefined` would cause `$browser.url('/foo')` to change `history.state`
|
4774
|
+
// to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
|
4775
|
+
if (isUndefined(state)) {
|
4776
|
+
state = null;
|
4777
|
+
}
|
4778
|
+
|
4644
4779
|
// Android Browser BFCache causes location, history reference to become stale.
|
4645
4780
|
if (location !== window.location) location = window.location;
|
4646
4781
|
if (history !== window.history) history = window.history;
|
4647
4782
|
|
4648
4783
|
// setter
|
4649
4784
|
if (url) {
|
4650
|
-
if
|
4785
|
+
// Don't change anything if previous and current URLs and states match. This also prevents
|
4786
|
+
// IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
|
4787
|
+
// See https://github.com/angular/angular.js/commit/ffb2701
|
4788
|
+
if (lastBrowserUrl === url && (!$sniffer.history || history.state === state)) {
|
4789
|
+
return;
|
4790
|
+
}
|
4791
|
+
var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
|
4651
4792
|
lastBrowserUrl = url;
|
4652
|
-
|
4653
|
-
|
4654
|
-
|
4655
|
-
|
4656
|
-
|
4657
|
-
|
4658
|
-
|
4793
|
+
// Don't use history API if only the hash changed
|
4794
|
+
// due to a bug in IE10/IE11 which leads
|
4795
|
+
// to not firing a `hashchange` nor `popstate` event
|
4796
|
+
// in some cases (see #9143).
|
4797
|
+
if ($sniffer.history && (!sameBase || history.state !== state)) {
|
4798
|
+
history[replace ? 'replaceState' : 'pushState'](state, '', url);
|
4799
|
+
lastHistoryState = history.state;
|
4659
4800
|
} else {
|
4660
|
-
|
4801
|
+
if (!sameBase) {
|
4802
|
+
reloadLocation = url;
|
4803
|
+
}
|
4661
4804
|
if (replace) {
|
4662
4805
|
location.replace(url);
|
4663
4806
|
} else {
|
@@ -4667,23 +4810,38 @@ function Browser(window, document, $log, $sniffer) {
|
|
4667
4810
|
return self;
|
4668
4811
|
// getter
|
4669
4812
|
} else {
|
4670
|
-
// -
|
4671
|
-
//
|
4813
|
+
// - reloadLocation is needed as browsers don't allow to read out
|
4814
|
+
// the new location.href if a reload happened.
|
4672
4815
|
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
|
4673
|
-
return
|
4816
|
+
return reloadLocation || location.href.replace(/%27/g,"'");
|
4674
4817
|
}
|
4675
4818
|
};
|
4676
4819
|
|
4820
|
+
/**
|
4821
|
+
* @name $browser#state
|
4822
|
+
*
|
4823
|
+
* @description
|
4824
|
+
* This method is a getter.
|
4825
|
+
*
|
4826
|
+
* Return history.state or null if history.state is undefined.
|
4827
|
+
*
|
4828
|
+
* @returns {object} state
|
4829
|
+
*/
|
4830
|
+
self.state = function() {
|
4831
|
+
return isUndefined(history.state) ? null : history.state;
|
4832
|
+
};
|
4833
|
+
|
4677
4834
|
var urlChangeListeners = [],
|
4678
4835
|
urlChangeInit = false;
|
4679
4836
|
|
4680
4837
|
function fireUrlChange() {
|
4681
|
-
|
4682
|
-
|
4838
|
+
if (lastBrowserUrl === self.url() && lastHistoryState === history.state) {
|
4839
|
+
return;
|
4840
|
+
}
|
4683
4841
|
|
4684
4842
|
lastBrowserUrl = self.url();
|
4685
4843
|
forEach(urlChangeListeners, function(listener) {
|
4686
|
-
listener(self.url());
|
4844
|
+
listener(self.url(), history.state);
|
4687
4845
|
});
|
4688
4846
|
}
|
4689
4847
|
|
@@ -4718,9 +4876,7 @@ function Browser(window, document, $log, $sniffer) {
|
|
4718
4876
|
// html5 history api - popstate event
|
4719
4877
|
if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange);
|
4720
4878
|
// hashchange event
|
4721
|
-
|
4722
|
-
// polling
|
4723
|
-
else self.addPollFn(fireUrlChange);
|
4879
|
+
jqLite(window).on('hashchange', fireUrlChange);
|
4724
4880
|
|
4725
4881
|
urlChangeInit = true;
|
4726
4882
|
}
|
@@ -5495,8 +5651,11 @@ function $TemplateCacheProvider() {
|
|
5495
5651
|
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
|
5496
5652
|
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
|
5497
5653
|
* * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
|
5654
|
+
* * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
|
5498
5655
|
* * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
|
5499
5656
|
* `null` to the `link` fn if not found.
|
5657
|
+
* * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
|
5658
|
+
* `null` to the `link` fn if not found.
|
5500
5659
|
*
|
5501
5660
|
*
|
5502
5661
|
* #### `controllerAs`
|
@@ -5574,22 +5733,18 @@ function $TemplateCacheProvider() {
|
|
5574
5733
|
* (because SVG doesn't work with custom elements in the DOM tree).
|
5575
5734
|
*
|
5576
5735
|
* #### `transclude`
|
5577
|
-
*
|
5578
|
-
*
|
5579
|
-
*
|
5580
|
-
* transclusion function which is pre-bound to the correct scope. In a typical setup the widget
|
5581
|
-
* creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
|
5582
|
-
* scope. This makes it possible for the widget to have private state, and the transclusion to
|
5583
|
-
* be bound to the parent (pre-`isolate`) scope.
|
5736
|
+
* Extract the contents of the element where the directive appears and make it available to the directive.
|
5737
|
+
* The contents are compiled and provided to the directive as a **transclusion function**. See the
|
5738
|
+
* {@link $compile#transclusion Transclusion} section below.
|
5584
5739
|
*
|
5585
|
-
*
|
5586
|
-
*
|
5740
|
+
* There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
|
5741
|
+
* directive's element or the entire element:
|
5742
|
+
*
|
5743
|
+
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
|
5744
|
+
* * `'element'` - transclude the whole of the directive's element including any directives on this
|
5745
|
+
* element that defined at a lower priority than this directive. When used, the `template`
|
5746
|
+
* property is ignored.
|
5587
5747
|
*
|
5588
|
-
* <div class="alert alert-warning">
|
5589
|
-
* **Note:** When testing an element transclude directive you must not place the directive at the root of the
|
5590
|
-
* DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
|
5591
|
-
* Testing Transclusion Directives}.
|
5592
|
-
* </div>
|
5593
5748
|
*
|
5594
5749
|
* #### `compile`
|
5595
5750
|
*
|
@@ -5687,7 +5842,121 @@ function $TemplateCacheProvider() {
|
|
5687
5842
|
* It is safe to do DOM transformation in the post-linking function on elements that are not waiting
|
5688
5843
|
* for their async templates to be resolved.
|
5689
5844
|
*
|
5690
|
-
*
|
5845
|
+
*
|
5846
|
+
* ### Transclusion
|
5847
|
+
*
|
5848
|
+
* Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
|
5849
|
+
* copying them to another part of the DOM, while maintaining their connection to the original AngularJS
|
5850
|
+
* scope from where they were taken.
|
5851
|
+
*
|
5852
|
+
* Transclusion is used (often with {@link ngTransclude}) to insert the
|
5853
|
+
* original contents of a directive's element into a specified place in the template of the directive.
|
5854
|
+
* The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
|
5855
|
+
* content has access to the properties on the scope from which it was taken, even if the directive
|
5856
|
+
* has isolated scope.
|
5857
|
+
* See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
|
5858
|
+
*
|
5859
|
+
* This makes it possible for the widget to have private state for its template, while the transcluded
|
5860
|
+
* content has access to its originating scope.
|
5861
|
+
*
|
5862
|
+
* <div class="alert alert-warning">
|
5863
|
+
* **Note:** When testing an element transclude directive you must not place the directive at the root of the
|
5864
|
+
* DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
|
5865
|
+
* Testing Transclusion Directives}.
|
5866
|
+
* </div>
|
5867
|
+
*
|
5868
|
+
* #### Transclusion Functions
|
5869
|
+
*
|
5870
|
+
* When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
|
5871
|
+
* function** to the directive's `link` function and `controller`. This transclusion function is a special
|
5872
|
+
* **linking function** that will return the compiled contents linked to a new transclusion scope.
|
5873
|
+
*
|
5874
|
+
* <div class="alert alert-info">
|
5875
|
+
* If you are just using {@link ngTransclude} then you don't need to worry about this function, since
|
5876
|
+
* ngTransclude will deal with it for us.
|
5877
|
+
* </div>
|
5878
|
+
*
|
5879
|
+
* If you want to manually control the insertion and removal of the transcluded content in your directive
|
5880
|
+
* then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
|
5881
|
+
* object that contains the compiled DOM, which is linked to the correct transclusion scope.
|
5882
|
+
*
|
5883
|
+
* When you call a transclusion function you can pass in a **clone attach function**. This function is accepts
|
5884
|
+
* two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
|
5885
|
+
* content and the `scope` is the newly created transclusion scope, to which the clone is bound.
|
5886
|
+
*
|
5887
|
+
* <div class="alert alert-info">
|
5888
|
+
* **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
|
5889
|
+
* since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
|
5890
|
+
* </div>
|
5891
|
+
*
|
5892
|
+
* It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
|
5893
|
+
* attach function**:
|
5894
|
+
*
|
5895
|
+
* ```js
|
5896
|
+
* var transcludedContent, transclusionScope;
|
5897
|
+
*
|
5898
|
+
* $transclude(function(clone, scope) {
|
5899
|
+
* element.append(clone);
|
5900
|
+
* transcludedContent = clone;
|
5901
|
+
* transclusionScope = scope;
|
5902
|
+
* });
|
5903
|
+
* ```
|
5904
|
+
*
|
5905
|
+
* Later, if you want to remove the transcluded content from your DOM then you should also destroy the
|
5906
|
+
* associated transclusion scope:
|
5907
|
+
*
|
5908
|
+
* ```js
|
5909
|
+
* transcludedContent.remove();
|
5910
|
+
* transclusionScope.$destroy();
|
5911
|
+
* ```
|
5912
|
+
*
|
5913
|
+
* <div class="alert alert-info">
|
5914
|
+
* **Best Practice**: if you intend to add and remove transcluded content manually in your directive
|
5915
|
+
* (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it),
|
5916
|
+
* then you are also responsible for calling `$destroy` on the transclusion scope.
|
5917
|
+
* </div>
|
5918
|
+
*
|
5919
|
+
* The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
|
5920
|
+
* automatically destroy their transluded clones as necessary so you do not need to worry about this if
|
5921
|
+
* you are simply using {@link ngTransclude} to inject the transclusion into your directive.
|
5922
|
+
*
|
5923
|
+
*
|
5924
|
+
* #### Transclusion Scopes
|
5925
|
+
*
|
5926
|
+
* When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
|
5927
|
+
* scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
|
5928
|
+
* when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
|
5929
|
+
* was taken.
|
5930
|
+
*
|
5931
|
+
* For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
|
5932
|
+
* like this:
|
5933
|
+
*
|
5934
|
+
* ```html
|
5935
|
+
* <div ng-app>
|
5936
|
+
* <div isolate>
|
5937
|
+
* <div transclusion>
|
5938
|
+
* </div>
|
5939
|
+
* </div>
|
5940
|
+
* </div>
|
5941
|
+
* ```
|
5942
|
+
*
|
5943
|
+
* The `$parent` scope hierarchy will look like this:
|
5944
|
+
*
|
5945
|
+
* ```
|
5946
|
+
* - $rootScope
|
5947
|
+
* - isolate
|
5948
|
+
* - transclusion
|
5949
|
+
* ```
|
5950
|
+
*
|
5951
|
+
* but the scopes will inherit prototypically from different scopes to their `$parent`.
|
5952
|
+
*
|
5953
|
+
* ```
|
5954
|
+
* - $rootScope
|
5955
|
+
* - transclusion
|
5956
|
+
* - isolate
|
5957
|
+
* ```
|
5958
|
+
*
|
5959
|
+
*
|
5691
5960
|
* ### Attributes
|
5692
5961
|
*
|
5693
5962
|
* The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
|
@@ -5725,7 +5994,7 @@ function $TemplateCacheProvider() {
|
|
5725
5994
|
* }
|
5726
5995
|
* ```
|
5727
5996
|
*
|
5728
|
-
*
|
5997
|
+
* ## Example
|
5729
5998
|
*
|
5730
5999
|
* <div class="alert alert-warning">
|
5731
6000
|
* **Note**: Typically directives are registered with `module.directive`. The example below is
|
@@ -5850,7 +6119,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
5850
6119
|
Suffix = 'Directive',
|
5851
6120
|
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,
|
5852
6121
|
CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/,
|
5853
|
-
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset')
|
6122
|
+
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
|
6123
|
+
REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
|
5854
6124
|
|
5855
6125
|
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
5856
6126
|
// The assumption is that future DOM event attribute names will begin with
|
@@ -6155,10 +6425,44 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6155
6425
|
|
6156
6426
|
nodeName = nodeName_(this.$$element);
|
6157
6427
|
|
6158
|
-
// sanitize a[href] and img[src] values
|
6159
6428
|
if ((nodeName === 'a' && key === 'href') ||
|
6160
6429
|
(nodeName === 'img' && key === 'src')) {
|
6430
|
+
// sanitize a[href] and img[src] values
|
6161
6431
|
this[key] = value = $$sanitizeUri(value, key === 'src');
|
6432
|
+
} else if (nodeName === 'img' && key === 'srcset') {
|
6433
|
+
// sanitize img[srcset] values
|
6434
|
+
var result = "";
|
6435
|
+
|
6436
|
+
// first check if there are spaces because it's not the same pattern
|
6437
|
+
var trimmedSrcset = trim(value);
|
6438
|
+
// ( 999x ,| 999w ,| ,|, )
|
6439
|
+
var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
|
6440
|
+
var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
|
6441
|
+
|
6442
|
+
// split srcset into tuple of uri and descriptor except for the last item
|
6443
|
+
var rawUris = trimmedSrcset.split(pattern);
|
6444
|
+
|
6445
|
+
// for each tuples
|
6446
|
+
var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
|
6447
|
+
for (var i=0; i<nbrUrisWith2parts; i++) {
|
6448
|
+
var innerIdx = i*2;
|
6449
|
+
// sanitize the uri
|
6450
|
+
result += $$sanitizeUri(trim( rawUris[innerIdx]), true);
|
6451
|
+
// add the descriptor
|
6452
|
+
result += ( " " + trim(rawUris[innerIdx+1]));
|
6453
|
+
}
|
6454
|
+
|
6455
|
+
// split the last item into uri and descriptor
|
6456
|
+
var lastTuple = trim(rawUris[i*2]).split(/\s/);
|
6457
|
+
|
6458
|
+
// sanitize the last uri
|
6459
|
+
result += $$sanitizeUri(trim(lastTuple[0]), true);
|
6460
|
+
|
6461
|
+
// and add the last descriptor if any
|
6462
|
+
if( lastTuple.length === 2) {
|
6463
|
+
result += (" " + trim(lastTuple[1]));
|
6464
|
+
}
|
6465
|
+
this[key] = value = result;
|
6162
6466
|
}
|
6163
6467
|
|
6164
6468
|
if (writeAttr !== false) {
|
@@ -6196,12 +6500,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6196
6500
|
* @param {string} key Normalized key. (ie ngAttribute) .
|
6197
6501
|
* @param {function(interpolatedValue)} fn Function that will be called whenever
|
6198
6502
|
the interpolated value of the attribute changes.
|
6199
|
-
* See
|
6503
|
+
* See {@link ng.$compile#attributes $compile} for more info.
|
6200
6504
|
* @returns {function()} Returns a deregistration function for this observer.
|
6201
6505
|
*/
|
6202
6506
|
$observe: function(key, fn) {
|
6203
6507
|
var attrs = this,
|
6204
|
-
$$observers = (attrs.$$observers || (attrs.$$observers =
|
6508
|
+
$$observers = (attrs.$$observers || (attrs.$$observers = Object.create(null))),
|
6205
6509
|
listeners = ($$observers[key] || ($$observers[key] = []));
|
6206
6510
|
|
6207
6511
|
listeners.push(fn);
|
@@ -6277,7 +6581,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6277
6581
|
// We can not compile top level text elements since text nodes can be merged and we will
|
6278
6582
|
// not be able to attach scope data to them, so we will wrap them in <span>
|
6279
6583
|
forEach($compileNodes, function(node, index){
|
6280
|
-
if (node.nodeType ==
|
6584
|
+
if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
|
6281
6585
|
$compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
|
6282
6586
|
}
|
6283
6587
|
});
|
@@ -6286,27 +6590,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6286
6590
|
maxPriority, ignoreDirective, previousCompileContext);
|
6287
6591
|
compile.$$addScopeClass($compileNodes);
|
6288
6592
|
var namespace = null;
|
6289
|
-
var namespaceAdaptedCompileNodes = $compileNodes;
|
6290
|
-
var lastCompileNode;
|
6291
6593
|
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn, futureParentElement){
|
6292
6594
|
assertArg(scope, 'scope');
|
6293
6595
|
if (!namespace) {
|
6294
6596
|
namespace = detectNamespaceForChildElements(futureParentElement);
|
6295
6597
|
}
|
6296
|
-
|
6297
|
-
|
6598
|
+
var $linkNode;
|
6599
|
+
if (namespace !== 'html') {
|
6600
|
+
// When using a directive with replace:true and templateUrl the $compileNodes
|
6601
|
+
// (or a child element inside of them)
|
6602
|
+
// might change, so we need to recreate the namespace adapted compileNodes
|
6603
|
+
// for call to the link function.
|
6604
|
+
// Note: This will already clone the nodes...
|
6605
|
+
$linkNode = jqLite(
|
6298
6606
|
wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
|
6299
6607
|
);
|
6608
|
+
} else if (cloneConnectFn) {
|
6609
|
+
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
6610
|
+
// and sometimes changes the structure of the DOM.
|
6611
|
+
$linkNode = JQLitePrototype.clone.call($compileNodes);
|
6612
|
+
} else {
|
6613
|
+
$linkNode = $compileNodes;
|
6300
6614
|
}
|
6301
|
-
// When using a directive with replace:true and templateUrl the $compileNodes
|
6302
|
-
// might change, so we need to recreate the namespace adapted compileNodes.
|
6303
|
-
lastCompileNode = $compileNodes[0];
|
6304
|
-
|
6305
|
-
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
6306
|
-
// and sometimes changes the structure of the DOM.
|
6307
|
-
var $linkNode = cloneConnectFn
|
6308
|
-
? JQLitePrototype.clone.call(namespaceAdaptedCompileNodes) // IMPORTANT!!!
|
6309
|
-
: namespaceAdaptedCompileNodes;
|
6310
6615
|
|
6311
6616
|
if (transcludeControllers) {
|
6312
6617
|
for (var controllerName in transcludeControllers) {
|
@@ -6449,20 +6754,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6449
6754
|
|
6450
6755
|
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
|
6451
6756
|
|
6452
|
-
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement) {
|
6453
|
-
var scopeCreated = false;
|
6757
|
+
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
|
6454
6758
|
|
6455
6759
|
if (!transcludedScope) {
|
6456
|
-
transcludedScope = scope.$new();
|
6760
|
+
transcludedScope = scope.$new(false, containingScope);
|
6457
6761
|
transcludedScope.$$transcluded = true;
|
6458
|
-
scopeCreated = true;
|
6459
6762
|
}
|
6460
6763
|
|
6461
|
-
|
6462
|
-
if (scopeCreated && !elementTransclusion) {
|
6463
|
-
clone.on('$destroy', function() { transcludedScope.$destroy(); });
|
6464
|
-
}
|
6465
|
-
return clone;
|
6764
|
+
return transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn, futureParentElement);
|
6466
6765
|
};
|
6467
6766
|
|
6468
6767
|
return boundTranscludeFn;
|
@@ -6485,7 +6784,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6485
6784
|
className;
|
6486
6785
|
|
6487
6786
|
switch(nodeType) {
|
6488
|
-
case
|
6787
|
+
case NODE_TYPE_ELEMENT: /* Element */
|
6489
6788
|
// use the node name: <directive>
|
6490
6789
|
addDirective(directives,
|
6491
6790
|
directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
|
@@ -6497,37 +6796,35 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6497
6796
|
var attrEndName = false;
|
6498
6797
|
|
6499
6798
|
attr = nAttrs[j];
|
6500
|
-
|
6501
|
-
|
6502
|
-
value = trim(attr.value);
|
6503
|
-
|
6504
|
-
// support ngAttr attribute binding
|
6505
|
-
ngAttrName = directiveNormalize(name);
|
6506
|
-
if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
|
6507
|
-
name = snake_case(ngAttrName.substr(6), '-');
|
6508
|
-
}
|
6799
|
+
name = attr.name;
|
6800
|
+
value = trim(attr.value);
|
6509
6801
|
|
6510
|
-
|
6511
|
-
|
6512
|
-
|
6513
|
-
|
6514
|
-
|
6515
|
-
name = name.substr(0, name.length - 6);
|
6516
|
-
}
|
6517
|
-
}
|
6802
|
+
// support ngAttr attribute binding
|
6803
|
+
ngAttrName = directiveNormalize(name);
|
6804
|
+
if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
|
6805
|
+
name = snake_case(ngAttrName.substr(6), '-');
|
6806
|
+
}
|
6518
6807
|
|
6519
|
-
|
6520
|
-
|
6521
|
-
if (
|
6522
|
-
|
6523
|
-
|
6524
|
-
|
6525
|
-
}
|
6808
|
+
var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
|
6809
|
+
if (directiveIsMultiElement(directiveNName)) {
|
6810
|
+
if (ngAttrName === directiveNName + 'Start') {
|
6811
|
+
attrStartName = name;
|
6812
|
+
attrEndName = name.substr(0, name.length - 5) + 'end';
|
6813
|
+
name = name.substr(0, name.length - 6);
|
6526
6814
|
}
|
6527
|
-
addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
|
6528
|
-
addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
|
6529
|
-
attrEndName);
|
6530
6815
|
}
|
6816
|
+
|
6817
|
+
nName = directiveNormalize(name.toLowerCase());
|
6818
|
+
attrsMap[nName] = name;
|
6819
|
+
if (isNgAttr || !attrs.hasOwnProperty(nName)) {
|
6820
|
+
attrs[nName] = value;
|
6821
|
+
if (getBooleanAttrName(node, nName)) {
|
6822
|
+
attrs[nName] = true; // presence means true
|
6823
|
+
}
|
6824
|
+
}
|
6825
|
+
addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
|
6826
|
+
addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
|
6827
|
+
attrEndName);
|
6531
6828
|
}
|
6532
6829
|
|
6533
6830
|
// use class as directive
|
@@ -6542,10 +6839,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6542
6839
|
}
|
6543
6840
|
}
|
6544
6841
|
break;
|
6545
|
-
case
|
6842
|
+
case NODE_TYPE_TEXT: /* Text Node */
|
6546
6843
|
addTextInterpolateDirective(directives, node.nodeValue);
|
6547
6844
|
break;
|
6548
|
-
case
|
6845
|
+
case NODE_TYPE_COMMENT: /* Comment */
|
6549
6846
|
try {
|
6550
6847
|
match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
|
6551
6848
|
if (match) {
|
@@ -6585,7 +6882,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6585
6882
|
"Unterminated attribute, found '{0}' but no matching '{1}' found.",
|
6586
6883
|
attrStart, attrEnd);
|
6587
6884
|
}
|
6588
|
-
if (node.nodeType ==
|
6885
|
+
if (node.nodeType == NODE_TYPE_ELEMENT) {
|
6589
6886
|
if (node.hasAttribute(attrStart)) depth++;
|
6590
6887
|
if (node.hasAttribute(attrEnd)) depth--;
|
6591
6888
|
}
|
@@ -6764,11 +7061,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6764
7061
|
if (jqLiteIsTextNode(directiveValue)) {
|
6765
7062
|
$template = [];
|
6766
7063
|
} else {
|
6767
|
-
$template =
|
7064
|
+
$template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
|
6768
7065
|
}
|
6769
7066
|
compileNode = $template[0];
|
6770
7067
|
|
6771
|
-
if ($template.length != 1 || compileNode.nodeType !==
|
7068
|
+
if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
|
6772
7069
|
throw $compileMinErr('tplrt',
|
6773
7070
|
"Template for directive '{0}' must have exactly one root element. {1}",
|
6774
7071
|
directiveName, '');
|
@@ -6872,14 +7169,26 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6872
7169
|
|
6873
7170
|
function getControllers(directiveName, require, $element, elementControllers) {
|
6874
7171
|
var value, retrievalMethod = 'data', optional = false;
|
7172
|
+
var $searchElement = $element;
|
7173
|
+
var match;
|
6875
7174
|
if (isString(require)) {
|
6876
|
-
|
6877
|
-
|
6878
|
-
|
6879
|
-
|
6880
|
-
|
6881
|
-
|
7175
|
+
match = require.match(REQUIRE_PREFIX_REGEXP);
|
7176
|
+
require = require.substring(match[0].length);
|
7177
|
+
|
7178
|
+
if (match[3]) {
|
7179
|
+
if (match[1]) match[3] = null;
|
7180
|
+
else match[1] = match[3];
|
7181
|
+
}
|
7182
|
+
if (match[1] === '^') {
|
7183
|
+
retrievalMethod = 'inheritedData';
|
7184
|
+
} else if (match[1] === '^^') {
|
7185
|
+
retrievalMethod = 'inheritedData';
|
7186
|
+
$searchElement = $element.parent();
|
7187
|
+
}
|
7188
|
+
if (match[2] === '?') {
|
7189
|
+
optional = true;
|
6882
7190
|
}
|
7191
|
+
|
6883
7192
|
value = null;
|
6884
7193
|
|
6885
7194
|
if (elementControllers && retrievalMethod === 'data') {
|
@@ -6887,7 +7196,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
6887
7196
|
value = value.instance;
|
6888
7197
|
}
|
6889
7198
|
}
|
6890
|
-
value = value || $
|
7199
|
+
value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
|
6891
7200
|
|
6892
7201
|
if (!value && !optional) {
|
6893
7202
|
throw $compileMinErr('ctreq',
|
@@ -7093,7 +7402,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
7093
7402
|
if (!futureParentElement) {
|
7094
7403
|
futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
|
7095
7404
|
}
|
7096
|
-
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement);
|
7405
|
+
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
|
7097
7406
|
}
|
7098
7407
|
}
|
7099
7408
|
}
|
@@ -7234,11 +7543,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
7234
7543
|
if (jqLiteIsTextNode(content)) {
|
7235
7544
|
$template = [];
|
7236
7545
|
} else {
|
7237
|
-
$template =
|
7546
|
+
$template = removeComments(wrapTemplate(templateNamespace, trim(content)));
|
7238
7547
|
}
|
7239
7548
|
compileNode = $template[0];
|
7240
7549
|
|
7241
|
-
if ($template.length != 1 || compileNode.nodeType !==
|
7550
|
+
if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
|
7242
7551
|
throw $compileMinErr('tplrt',
|
7243
7552
|
"Template for directive '{0}' must have exactly one root element. {1}",
|
7244
7553
|
origAsyncDirective.name, templateUrl);
|
@@ -7277,6 +7586,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
7277
7586
|
boundTranscludeFn = linkQueue.shift(),
|
7278
7587
|
linkNode = $compileNode[0];
|
7279
7588
|
|
7589
|
+
if (scope.$$destroyed) continue;
|
7590
|
+
|
7280
7591
|
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
7281
7592
|
var oldClasses = beforeTemplateLinkNode.className;
|
7282
7593
|
|
@@ -7303,6 +7614,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
7303
7614
|
|
7304
7615
|
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
|
7305
7616
|
var childBoundTranscludeFn = boundTranscludeFn;
|
7617
|
+
if (scope.$$destroyed) return;
|
7306
7618
|
if (linkQueue) {
|
7307
7619
|
linkQueue.push(scope);
|
7308
7620
|
linkQueue.push(node);
|
@@ -7419,6 +7731,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|
7419
7731
|
"ng- versions (such as ng-click instead of onclick) instead.");
|
7420
7732
|
}
|
7421
7733
|
|
7734
|
+
// If the attribute was removed, then we are done
|
7735
|
+
if (!attr[name]) {
|
7736
|
+
return;
|
7737
|
+
}
|
7738
|
+
|
7422
7739
|
// we need to interpolate again, in case the attribute value has been updated
|
7423
7740
|
// (e.g. by another directive's compile function)
|
7424
7741
|
interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name),
|
@@ -7646,6 +7963,23 @@ function tokenDifference(str1, str2) {
|
|
7646
7963
|
return values;
|
7647
7964
|
}
|
7648
7965
|
|
7966
|
+
function removeComments(jqNodes) {
|
7967
|
+
jqNodes = jqLite(jqNodes);
|
7968
|
+
var i = jqNodes.length;
|
7969
|
+
|
7970
|
+
if (i <= 1) {
|
7971
|
+
return jqNodes;
|
7972
|
+
}
|
7973
|
+
|
7974
|
+
while (i--) {
|
7975
|
+
var node = jqNodes[i];
|
7976
|
+
if (node.nodeType === NODE_TYPE_COMMENT) {
|
7977
|
+
splice.call(jqNodes, i, 1);
|
7978
|
+
}
|
7979
|
+
}
|
7980
|
+
return jqNodes;
|
7981
|
+
}
|
7982
|
+
|
7649
7983
|
/**
|
7650
7984
|
* @ngdoc provider
|
7651
7985
|
* @name $controllerProvider
|
@@ -7949,7 +8283,8 @@ function $HttpProvider() {
|
|
7949
8283
|
var JSON_START = /^\s*(\[|\{[^\{])/,
|
7950
8284
|
JSON_END = /[\}\]]\s*$/,
|
7951
8285
|
PROTECTION_PREFIX = /^\)\]\}',?\n/,
|
7952
|
-
|
8286
|
+
APPLICATION_JSON = 'application/json',
|
8287
|
+
CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
|
7953
8288
|
|
7954
8289
|
/**
|
7955
8290
|
* @ngdoc property
|
@@ -7974,12 +8309,15 @@ function $HttpProvider() {
|
|
7974
8309
|
**/
|
7975
8310
|
var defaults = this.defaults = {
|
7976
8311
|
// transform incoming response data
|
7977
|
-
transformResponse: [function(data) {
|
8312
|
+
transformResponse: [function defaultHttpResponseTransform(data, headers) {
|
7978
8313
|
if (isString(data)) {
|
7979
8314
|
// strip json vulnerability protection prefix
|
7980
8315
|
data = data.replace(PROTECTION_PREFIX, '');
|
7981
|
-
|
8316
|
+
var contentType = headers('Content-Type');
|
8317
|
+
if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0) ||
|
8318
|
+
(JSON_START.test(data) && JSON_END.test(data))) {
|
7982
8319
|
data = fromJson(data);
|
8320
|
+
}
|
7983
8321
|
}
|
7984
8322
|
return data;
|
7985
8323
|
}],
|
@@ -8937,18 +9275,8 @@ function $HttpProvider() {
|
|
8937
9275
|
}];
|
8938
9276
|
}
|
8939
9277
|
|
8940
|
-
function createXhr(
|
8941
|
-
|
8942
|
-
//is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest
|
8943
|
-
//if it is available
|
8944
|
-
if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) ||
|
8945
|
-
!window.XMLHttpRequest)) {
|
8946
|
-
return new window.ActiveXObject("Microsoft.XMLHTTP");
|
8947
|
-
} else if (window.XMLHttpRequest) {
|
8948
|
-
return new window.XMLHttpRequest();
|
8949
|
-
}
|
8950
|
-
|
8951
|
-
throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
|
9278
|
+
function createXhr() {
|
9279
|
+
return new window.XMLHttpRequest();
|
8952
9280
|
}
|
8953
9281
|
|
8954
9282
|
/**
|
@@ -8974,11 +9302,8 @@ function $HttpBackendProvider() {
|
|
8974
9302
|
}
|
8975
9303
|
|
8976
9304
|
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
|
8977
|
-
var ABORTED = -1;
|
8978
|
-
|
8979
9305
|
// TODO(vojta): fix the signature
|
8980
9306
|
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
|
8981
|
-
var status;
|
8982
9307
|
$browser.$$incOutstandingRequestCount();
|
8983
9308
|
url = url || $browser.url();
|
8984
9309
|
|
@@ -8996,7 +9321,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
|
8996
9321
|
});
|
8997
9322
|
} else {
|
8998
9323
|
|
8999
|
-
var xhr = createXhr(
|
9324
|
+
var xhr = createXhr();
|
9000
9325
|
|
9001
9326
|
xhr.open(method, url, true);
|
9002
9327
|
forEach(headers, function(value, key) {
|
@@ -9005,44 +9330,39 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
|
9005
9330
|
}
|
9006
9331
|
});
|
9007
9332
|
|
9008
|
-
|
9009
|
-
|
9010
|
-
// always async
|
9011
|
-
xhr.onreadystatechange = function() {
|
9012
|
-
// onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by
|
9013
|
-
// xhrs that are resolved while the app is in the background (see #5426).
|
9014
|
-
// since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
|
9015
|
-
// continuing
|
9016
|
-
//
|
9017
|
-
// we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and
|
9018
|
-
// Safari respectively.
|
9019
|
-
if (xhr && xhr.readyState == 4) {
|
9020
|
-
var responseHeaders = null,
|
9021
|
-
response = null,
|
9022
|
-
statusText = '';
|
9023
|
-
|
9024
|
-
if(status !== ABORTED) {
|
9025
|
-
responseHeaders = xhr.getAllResponseHeaders();
|
9026
|
-
|
9027
|
-
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
9028
|
-
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
9029
|
-
response = ('response' in xhr) ? xhr.response : xhr.responseText;
|
9030
|
-
}
|
9333
|
+
xhr.onload = function requestLoaded() {
|
9334
|
+
var statusText = xhr.statusText || '';
|
9031
9335
|
|
9032
|
-
|
9033
|
-
|
9034
|
-
|
9035
|
-
statusText = xhr.statusText;
|
9036
|
-
}
|
9336
|
+
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
9337
|
+
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
9338
|
+
var response = ('response' in xhr) ? xhr.response : xhr.responseText;
|
9037
9339
|
|
9038
|
-
|
9039
|
-
|
9040
|
-
|
9041
|
-
|
9042
|
-
|
9340
|
+
// normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
|
9341
|
+
var status = xhr.status === 1223 ? 204 : xhr.status;
|
9342
|
+
|
9343
|
+
// fix status code when it is 0 (0 status is undocumented).
|
9344
|
+
// Occurs when accessing file resources or on Android 4.1 stock browser
|
9345
|
+
// while retrieving files from application cache.
|
9346
|
+
if (status === 0) {
|
9347
|
+
status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
|
9043
9348
|
}
|
9349
|
+
|
9350
|
+
completeRequest(callback,
|
9351
|
+
status,
|
9352
|
+
response,
|
9353
|
+
xhr.getAllResponseHeaders(),
|
9354
|
+
statusText);
|
9044
9355
|
};
|
9045
9356
|
|
9357
|
+
var requestError = function () {
|
9358
|
+
// The response is always empty
|
9359
|
+
// See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
|
9360
|
+
completeRequest(callback, -1, null, null, '');
|
9361
|
+
};
|
9362
|
+
|
9363
|
+
xhr.onerror = requestError;
|
9364
|
+
xhr.onabort = requestError;
|
9365
|
+
|
9046
9366
|
if (withCredentials) {
|
9047
9367
|
xhr.withCredentials = true;
|
9048
9368
|
}
|
@@ -9075,7 +9395,6 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
|
9075
9395
|
|
9076
9396
|
|
9077
9397
|
function timeoutRequest() {
|
9078
|
-
status = ABORTED;
|
9079
9398
|
jsonpDone && jsonpDone();
|
9080
9399
|
xhr && xhr.abort();
|
9081
9400
|
}
|
@@ -9085,17 +9404,6 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
|
9085
9404
|
timeoutId && $browserDefer.cancel(timeoutId);
|
9086
9405
|
jsonpDone = xhr = null;
|
9087
9406
|
|
9088
|
-
// fix status code when it is 0 (0 status is undocumented).
|
9089
|
-
// Occurs when accessing file resources or on Android 4.1 stock browser
|
9090
|
-
// while retrieving files from application cache.
|
9091
|
-
if (status === 0) {
|
9092
|
-
status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
|
9093
|
-
}
|
9094
|
-
|
9095
|
-
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
9096
|
-
status = status === 1223 ? 204 : status;
|
9097
|
-
statusText = statusText || '';
|
9098
|
-
|
9099
9407
|
callback(status, response, headersString, statusText);
|
9100
9408
|
$browser.$$completeOutstandingRequest(noop);
|
9101
9409
|
}
|
@@ -10047,9 +10355,7 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) {
|
|
10047
10355
|
}
|
10048
10356
|
|
10049
10357
|
|
10050
|
-
|
10051
|
-
LocationHashbangUrl.prototype =
|
10052
|
-
LocationHtml5Url.prototype = {
|
10358
|
+
var locationPrototype = {
|
10053
10359
|
|
10054
10360
|
/**
|
10055
10361
|
* Are we in html5 mode?
|
@@ -10058,7 +10364,7 @@ LocationHashbangInHtml5Url.prototype =
|
|
10058
10364
|
$$html5: false,
|
10059
10365
|
|
10060
10366
|
/**
|
10061
|
-
* Has any change been replacing
|
10367
|
+
* Has any change been replacing?
|
10062
10368
|
* @private
|
10063
10369
|
*/
|
10064
10370
|
$$replace: false,
|
@@ -10160,7 +10466,7 @@ LocationHashbangInHtml5Url.prototype =
|
|
10160
10466
|
* @return {string} path
|
10161
10467
|
*/
|
10162
10468
|
path: locationGetterSetter('$$path', function(path) {
|
10163
|
-
path = path ? path.toString() : '';
|
10469
|
+
path = path !== null ? path.toString() : '';
|
10164
10470
|
return path.charAt(0) == '/' ? path : '/' + path;
|
10165
10471
|
}),
|
10166
10472
|
|
@@ -10257,7 +10563,7 @@ LocationHashbangInHtml5Url.prototype =
|
|
10257
10563
|
* @return {string} hash
|
10258
10564
|
*/
|
10259
10565
|
hash: locationGetterSetter('$$hash', function(hash) {
|
10260
|
-
return hash ? hash.toString() : '';
|
10566
|
+
return hash !== null ? hash.toString() : '';
|
10261
10567
|
}),
|
10262
10568
|
|
10263
10569
|
/**
|
@@ -10274,6 +10580,46 @@ LocationHashbangInHtml5Url.prototype =
|
|
10274
10580
|
}
|
10275
10581
|
};
|
10276
10582
|
|
10583
|
+
forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function (Location) {
|
10584
|
+
Location.prototype = Object.create(locationPrototype);
|
10585
|
+
|
10586
|
+
/**
|
10587
|
+
* @ngdoc method
|
10588
|
+
* @name $location#state
|
10589
|
+
*
|
10590
|
+
* @description
|
10591
|
+
* This method is getter / setter.
|
10592
|
+
*
|
10593
|
+
* Return the history state object when called without any parameter.
|
10594
|
+
*
|
10595
|
+
* Change the history state object when called with one parameter and return `$location`.
|
10596
|
+
* The state object is later passed to `pushState` or `replaceState`.
|
10597
|
+
*
|
10598
|
+
* NOTE: This method is supported only in HTML5 mode and only in browsers supporting
|
10599
|
+
* the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
|
10600
|
+
* older browsers (like IE9 or Android < 4.0), don't use this method.
|
10601
|
+
*
|
10602
|
+
* @param {object=} state State object for pushState or replaceState
|
10603
|
+
* @return {object} state
|
10604
|
+
*/
|
10605
|
+
Location.prototype.state = function(state) {
|
10606
|
+
if (!arguments.length)
|
10607
|
+
return this.$$state;
|
10608
|
+
|
10609
|
+
if (Location !== LocationHtml5Url || !this.$$html5) {
|
10610
|
+
throw $locationMinErr('nostate', 'History API state support is available only ' +
|
10611
|
+
'in HTML5 mode and only in browsers supporting HTML5 History API');
|
10612
|
+
}
|
10613
|
+
// The user might modify `stateObject` after invoking `$location.state(stateObject)`
|
10614
|
+
// but we're changing the $$state reference to $browser.state() during the $digest
|
10615
|
+
// so the modification window is narrow.
|
10616
|
+
this.$$state = isUndefined(state) ? null : state;
|
10617
|
+
|
10618
|
+
return this;
|
10619
|
+
};
|
10620
|
+
});
|
10621
|
+
|
10622
|
+
|
10277
10623
|
function locationGetter(property) {
|
10278
10624
|
return function() {
|
10279
10625
|
return this[property];
|
@@ -10330,7 +10676,8 @@ function $LocationProvider(){
|
|
10330
10676
|
var hashPrefix = '',
|
10331
10677
|
html5Mode = {
|
10332
10678
|
enabled: false,
|
10333
|
-
requireBase: true
|
10679
|
+
requireBase: true,
|
10680
|
+
rewriteLinks: true
|
10334
10681
|
};
|
10335
10682
|
|
10336
10683
|
/**
|
@@ -10354,15 +10701,17 @@ function $LocationProvider(){
|
|
10354
10701
|
* @name $locationProvider#html5Mode
|
10355
10702
|
* @description
|
10356
10703
|
* @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
|
10357
|
-
* If object, sets `enabled` and `
|
10358
|
-
*
|
10359
|
-
*
|
10360
|
-
* in browsers that do not
|
10361
|
-
*
|
10362
|
-
*
|
10363
|
-
*
|
10364
|
-
* thrown when `$location` is injected.
|
10365
|
-
* {@link guide/$location $location guide for more information}
|
10704
|
+
* If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
|
10705
|
+
* properties:
|
10706
|
+
* - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
|
10707
|
+
* change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
|
10708
|
+
* support `pushState`.
|
10709
|
+
* - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
|
10710
|
+
* whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
|
10711
|
+
* true, and a base tag is not present, an error will be thrown when `$location` is injected.
|
10712
|
+
* See the {@link guide/$location $location guide for more information}
|
10713
|
+
* - **rewriteLinks** - `{boolean}` - (default: `false`) When html5Mode is enabled, disables
|
10714
|
+
* url rewriting for relative linksTurns off url rewriting for relative links.
|
10366
10715
|
*
|
10367
10716
|
* @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
|
10368
10717
|
*/
|
@@ -10371,12 +10720,19 @@ function $LocationProvider(){
|
|
10371
10720
|
html5Mode.enabled = mode;
|
10372
10721
|
return this;
|
10373
10722
|
} else if (isObject(mode)) {
|
10374
|
-
|
10375
|
-
|
10376
|
-
|
10377
|
-
|
10378
|
-
|
10379
|
-
|
10723
|
+
|
10724
|
+
if (isBoolean(mode.enabled)) {
|
10725
|
+
html5Mode.enabled = mode.enabled;
|
10726
|
+
}
|
10727
|
+
|
10728
|
+
if (isBoolean(mode.requireBase)) {
|
10729
|
+
html5Mode.requireBase = mode.requireBase;
|
10730
|
+
}
|
10731
|
+
|
10732
|
+
if (isBoolean(mode.rewriteLinks)) {
|
10733
|
+
html5Mode.rewriteLinks = mode.rewriteLinks;
|
10734
|
+
}
|
10735
|
+
|
10380
10736
|
return this;
|
10381
10737
|
} else {
|
10382
10738
|
return html5Mode;
|
@@ -10388,14 +10744,21 @@ function $LocationProvider(){
|
|
10388
10744
|
* @name $location#$locationChangeStart
|
10389
10745
|
* @eventType broadcast on root scope
|
10390
10746
|
* @description
|
10391
|
-
* Broadcasted before a URL will change.
|
10747
|
+
* Broadcasted before a URL will change.
|
10748
|
+
*
|
10749
|
+
* This change can be prevented by calling
|
10392
10750
|
* `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
|
10393
10751
|
* details about event object. Upon successful change
|
10394
10752
|
* {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired.
|
10395
10753
|
*
|
10754
|
+
* The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
|
10755
|
+
* the browser supports the HTML5 History API.
|
10756
|
+
*
|
10396
10757
|
* @param {Object} angularEvent Synthetic event object.
|
10397
10758
|
* @param {string} newUrl New URL
|
10398
10759
|
* @param {string=} oldUrl URL that was before it was changed.
|
10760
|
+
* @param {string=} newState New history state object
|
10761
|
+
* @param {string=} oldState History state object that was before it was changed.
|
10399
10762
|
*/
|
10400
10763
|
|
10401
10764
|
/**
|
@@ -10405,9 +10768,14 @@ function $LocationProvider(){
|
|
10405
10768
|
* @description
|
10406
10769
|
* Broadcasted after a URL was changed.
|
10407
10770
|
*
|
10771
|
+
* The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
|
10772
|
+
* the browser supports the HTML5 History API.
|
10773
|
+
*
|
10408
10774
|
* @param {Object} angularEvent Synthetic event object.
|
10409
10775
|
* @param {string} newUrl New URL
|
10410
10776
|
* @param {string=} oldUrl URL that was before it was changed.
|
10777
|
+
* @param {string=} newState New history state object
|
10778
|
+
* @param {string=} oldState History state object that was before it was changed.
|
10411
10779
|
*/
|
10412
10780
|
|
10413
10781
|
this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
|
@@ -10432,13 +10800,34 @@ function $LocationProvider(){
|
|
10432
10800
|
$location = new LocationMode(appBase, '#' + hashPrefix);
|
10433
10801
|
$location.$$parseLinkUrl(initialUrl, initialUrl);
|
10434
10802
|
|
10803
|
+
$location.$$state = $browser.state();
|
10804
|
+
|
10435
10805
|
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
|
10436
10806
|
|
10807
|
+
function setBrowserUrlWithFallback(url, replace, state) {
|
10808
|
+
var oldUrl = $location.url();
|
10809
|
+
var oldState = $location.$$state;
|
10810
|
+
try {
|
10811
|
+
$browser.url(url, replace, state);
|
10812
|
+
|
10813
|
+
// Make sure $location.state() returns referentially identical (not just deeply equal)
|
10814
|
+
// state object; this makes possible quick checking if the state changed in the digest
|
10815
|
+
// loop. Checking deep equality would be too expensive.
|
10816
|
+
$location.$$state = $browser.state();
|
10817
|
+
} catch (e) {
|
10818
|
+
// Restore old values if pushState fails
|
10819
|
+
$location.url(oldUrl);
|
10820
|
+
$location.$$state = oldState;
|
10821
|
+
|
10822
|
+
throw e;
|
10823
|
+
}
|
10824
|
+
}
|
10825
|
+
|
10437
10826
|
$rootElement.on('click', function(event) {
|
10438
10827
|
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
|
10439
10828
|
// currently we open nice url link and redirect then
|
10440
10829
|
|
10441
|
-
if (event.ctrlKey || event.metaKey || event.which == 2) return;
|
10830
|
+
if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.which == 2) return;
|
10442
10831
|
|
10443
10832
|
var elm = jqLite(event.target);
|
10444
10833
|
|
@@ -10464,6 +10853,9 @@ function $LocationProvider(){
|
|
10464
10853
|
|
10465
10854
|
if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
|
10466
10855
|
if ($location.$$parseLinkUrl(absHref, relHref)) {
|
10856
|
+
// We do a preventDefault for all urls that are part of the angular application,
|
10857
|
+
// in html5mode and also without, so that we are able to abort navigation without
|
10858
|
+
// getting double entries in the location history.
|
10467
10859
|
event.preventDefault();
|
10468
10860
|
// update location manually
|
10469
10861
|
if ($location.absUrl() != $browser.url()) {
|
@@ -10481,52 +10873,63 @@ function $LocationProvider(){
|
|
10481
10873
|
$browser.url($location.absUrl(), true);
|
10482
10874
|
}
|
10483
10875
|
|
10484
|
-
|
10485
|
-
$browser.onUrlChange(function(newUrl) {
|
10486
|
-
if ($location.absUrl() != newUrl) {
|
10487
|
-
$rootScope.$evalAsync(function() {
|
10488
|
-
var oldUrl = $location.absUrl();
|
10876
|
+
var initializing = true;
|
10489
10877
|
|
10490
|
-
|
10491
|
-
|
10492
|
-
|
10493
|
-
|
10494
|
-
|
10495
|
-
|
10496
|
-
|
10497
|
-
|
10498
|
-
|
10499
|
-
|
10500
|
-
|
10878
|
+
// update $location when $browser url changes
|
10879
|
+
$browser.onUrlChange(function(newUrl, newState) {
|
10880
|
+
$rootScope.$evalAsync(function() {
|
10881
|
+
var oldUrl = $location.absUrl();
|
10882
|
+
var oldState = $location.$$state;
|
10883
|
+
|
10884
|
+
$location.$$parse(newUrl);
|
10885
|
+
$location.$$state = newState;
|
10886
|
+
if ($rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
|
10887
|
+
newState, oldState).defaultPrevented) {
|
10888
|
+
$location.$$parse(oldUrl);
|
10889
|
+
$location.$$state = oldState;
|
10890
|
+
setBrowserUrlWithFallback(oldUrl, false, oldState);
|
10891
|
+
} else {
|
10892
|
+
initializing = false;
|
10893
|
+
afterLocationChange(oldUrl, oldState);
|
10894
|
+
}
|
10895
|
+
});
|
10896
|
+
if (!$rootScope.$$phase) $rootScope.$digest();
|
10501
10897
|
});
|
10502
10898
|
|
10503
10899
|
// update browser
|
10504
|
-
var changeCounter = 0;
|
10505
10900
|
$rootScope.$watch(function $locationWatch() {
|
10506
10901
|
var oldUrl = $browser.url();
|
10902
|
+
var oldState = $browser.state();
|
10507
10903
|
var currentReplace = $location.$$replace;
|
10508
10904
|
|
10509
|
-
if (
|
10510
|
-
|
10905
|
+
if (initializing || oldUrl !== $location.absUrl() ||
|
10906
|
+
($location.$$html5 && $sniffer.history && oldState !== $location.$$state)) {
|
10907
|
+
initializing = false;
|
10908
|
+
|
10511
10909
|
$rootScope.$evalAsync(function() {
|
10512
|
-
if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl
|
10513
|
-
defaultPrevented) {
|
10910
|
+
if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl,
|
10911
|
+
$location.$$state, oldState).defaultPrevented) {
|
10514
10912
|
$location.$$parse(oldUrl);
|
10913
|
+
$location.$$state = oldState;
|
10515
10914
|
} else {
|
10516
|
-
|
10517
|
-
|
10915
|
+
setBrowserUrlWithFallback($location.absUrl(), currentReplace,
|
10916
|
+
oldState === $location.$$state ? null : $location.$$state);
|
10917
|
+
afterLocationChange(oldUrl, oldState);
|
10518
10918
|
}
|
10519
10919
|
});
|
10520
10920
|
}
|
10921
|
+
|
10521
10922
|
$location.$$replace = false;
|
10522
10923
|
|
10523
|
-
return
|
10924
|
+
// we don't need to return anything because $evalAsync will make the digest loop dirty when
|
10925
|
+
// there is a change
|
10524
10926
|
});
|
10525
10927
|
|
10526
10928
|
return $location;
|
10527
10929
|
|
10528
|
-
function afterLocationChange(oldUrl) {
|
10529
|
-
$rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl
|
10930
|
+
function afterLocationChange(oldUrl, oldState) {
|
10931
|
+
$rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
|
10932
|
+
$location.$$state, oldState);
|
10530
10933
|
}
|
10531
10934
|
}];
|
10532
10935
|
}
|
@@ -10785,6 +11188,11 @@ forEach({
|
|
10785
11188
|
CONSTANTS[name] = constantGetter;
|
10786
11189
|
});
|
10787
11190
|
|
11191
|
+
//Not quite a constant, but can be lex/parsed the same
|
11192
|
+
CONSTANTS['this'] = function(self) { return self; };
|
11193
|
+
CONSTANTS['this'].sharedGetter = true;
|
11194
|
+
|
11195
|
+
|
10788
11196
|
//Operators - will be wrapped by binaryFn/unaryFn/assignment/filter
|
10789
11197
|
var OPERATORS = extend(createMap(), {
|
10790
11198
|
/* jshint bitwise : false */
|
@@ -12648,14 +13056,11 @@ function $RootScopeProvider(){
|
|
12648
13056
|
this.$$phase = this.$parent = this.$$watchers =
|
12649
13057
|
this.$$nextSibling = this.$$prevSibling =
|
12650
13058
|
this.$$childHead = this.$$childTail = null;
|
12651
|
-
this
|
13059
|
+
this.$root = this;
|
12652
13060
|
this.$$destroyed = false;
|
12653
|
-
this.$$asyncQueue = [];
|
12654
|
-
this.$$postDigestQueue = [];
|
12655
13061
|
this.$$listeners = {};
|
12656
13062
|
this.$$listenerCount = {};
|
12657
13063
|
this.$$isolateBindings = null;
|
12658
|
-
this.$$applyAsyncQueue = [];
|
12659
13064
|
}
|
12660
13065
|
|
12661
13066
|
/**
|
@@ -12704,18 +13109,23 @@ function $RootScopeProvider(){
|
|
12704
13109
|
* When creating widgets, it is useful for the widget to not accidentally read parent
|
12705
13110
|
* state.
|
12706
13111
|
*
|
13112
|
+
* @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
|
13113
|
+
* of the newly created scope. Defaults to `this` scope if not provided.
|
13114
|
+
* This is used when creating a transclude scope to correctly place it
|
13115
|
+
* in the scope hierarchy while maintaining the correct prototypical
|
13116
|
+
* inheritance.
|
13117
|
+
*
|
12707
13118
|
* @returns {Object} The newly created child scope.
|
12708
13119
|
*
|
12709
13120
|
*/
|
12710
|
-
$new: function(isolate) {
|
13121
|
+
$new: function(isolate, parent) {
|
12711
13122
|
var child;
|
12712
13123
|
|
13124
|
+
parent = parent || this;
|
13125
|
+
|
12713
13126
|
if (isolate) {
|
12714
13127
|
child = new Scope();
|
12715
13128
|
child.$root = this.$root;
|
12716
|
-
// ensure that there is just one async queue per $rootScope and its children
|
12717
|
-
child.$$asyncQueue = this.$$asyncQueue;
|
12718
|
-
child.$$postDigestQueue = this.$$postDigestQueue;
|
12719
13129
|
} else {
|
12720
13130
|
// Only create a child scope class if somebody asks for one,
|
12721
13131
|
// but cache it to allow the VM to optimize lookups.
|
@@ -12732,16 +13142,27 @@ function $RootScopeProvider(){
|
|
12732
13142
|
}
|
12733
13143
|
child = new this.$$ChildScope();
|
12734
13144
|
}
|
12735
|
-
child
|
12736
|
-
child
|
12737
|
-
|
12738
|
-
|
12739
|
-
|
12740
|
-
this.$$childTail = child;
|
13145
|
+
child.$parent = parent;
|
13146
|
+
child.$$prevSibling = parent.$$childTail;
|
13147
|
+
if (parent.$$childHead) {
|
13148
|
+
parent.$$childTail.$$nextSibling = child;
|
13149
|
+
parent.$$childTail = child;
|
12741
13150
|
} else {
|
12742
|
-
|
13151
|
+
parent.$$childHead = parent.$$childTail = child;
|
12743
13152
|
}
|
13153
|
+
|
13154
|
+
// When the new scope is not isolated or we inherit from `this`, and
|
13155
|
+
// the parent scope is destroyed, the property `$$destroyed` is inherited
|
13156
|
+
// prototypically. In all other cases, this property needs to be set
|
13157
|
+
// when the parent scope is destroyed.
|
13158
|
+
// The listener needs to be added after the parent is set
|
13159
|
+
if (isolate || parent != this) child.$on('$destroy', destroyChild);
|
13160
|
+
|
12744
13161
|
return child;
|
13162
|
+
|
13163
|
+
function destroyChild() {
|
13164
|
+
child.$$destroyed = true;
|
13165
|
+
}
|
12745
13166
|
},
|
12746
13167
|
|
12747
13168
|
/**
|
@@ -13217,8 +13638,6 @@ function $RootScopeProvider(){
|
|
13217
13638
|
$digest: function() {
|
13218
13639
|
var watch, value, last,
|
13219
13640
|
watchers,
|
13220
|
-
asyncQueue = this.$$asyncQueue,
|
13221
|
-
postDigestQueue = this.$$postDigestQueue,
|
13222
13641
|
length,
|
13223
13642
|
dirty, ttl = TTL,
|
13224
13643
|
next, current, target = this,
|
@@ -13383,6 +13802,10 @@ function $RootScopeProvider(){
|
|
13383
13802
|
if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
|
13384
13803
|
if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
|
13385
13804
|
|
13805
|
+
// Disable listeners, watchers and apply/digest methods
|
13806
|
+
this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
|
13807
|
+
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
|
13808
|
+
this.$$listeners = {};
|
13386
13809
|
|
13387
13810
|
// All of the code below is bogus code that works around V8's memory leak via optimized code
|
13388
13811
|
// and inline caches.
|
@@ -13393,15 +13816,7 @@ function $RootScopeProvider(){
|
|
13393
13816
|
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
|
13394
13817
|
|
13395
13818
|
this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
|
13396
|
-
this.$$childTail = this.$root = null;
|
13397
|
-
|
13398
|
-
// don't reset these to null in case some async task tries to register a listener/watch/task
|
13399
|
-
this.$$listeners = {};
|
13400
|
-
this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = [];
|
13401
|
-
|
13402
|
-
// prevent NPEs since these methods have references to properties we nulled out
|
13403
|
-
this.$destroy = this.$digest = this.$apply = noop;
|
13404
|
-
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
|
13819
|
+
this.$$childTail = this.$root = this.$$watchers = null;
|
13405
13820
|
},
|
13406
13821
|
|
13407
13822
|
/**
|
@@ -13468,19 +13883,19 @@ function $RootScopeProvider(){
|
|
13468
13883
|
$evalAsync: function(expr) {
|
13469
13884
|
// if we are outside of an $digest loop and this is the first time we are scheduling async
|
13470
13885
|
// task also schedule async auto-flush
|
13471
|
-
if (!$rootScope.$$phase &&
|
13886
|
+
if (!$rootScope.$$phase && !asyncQueue.length) {
|
13472
13887
|
$browser.defer(function() {
|
13473
|
-
if (
|
13888
|
+
if (asyncQueue.length) {
|
13474
13889
|
$rootScope.$digest();
|
13475
13890
|
}
|
13476
13891
|
});
|
13477
13892
|
}
|
13478
13893
|
|
13479
|
-
|
13894
|
+
asyncQueue.push({scope: this, expression: expr});
|
13480
13895
|
},
|
13481
13896
|
|
13482
13897
|
$$postDigest : function(fn) {
|
13483
|
-
|
13898
|
+
postDigestQueue.push(fn);
|
13484
13899
|
},
|
13485
13900
|
|
13486
13901
|
/**
|
@@ -13564,7 +13979,7 @@ function $RootScopeProvider(){
|
|
13564
13979
|
*/
|
13565
13980
|
$applyAsync: function(expr) {
|
13566
13981
|
var scope = this;
|
13567
|
-
expr &&
|
13982
|
+
expr && applyAsyncQueue.push($applyAsyncExpression);
|
13568
13983
|
scheduleApplyAsync();
|
13569
13984
|
|
13570
13985
|
function $applyAsyncExpression() {
|
@@ -13773,6 +14188,11 @@ function $RootScopeProvider(){
|
|
13773
14188
|
|
13774
14189
|
var $rootScope = new Scope();
|
13775
14190
|
|
14191
|
+
//The internal queues. Expose them on the $rootScope for debugging/testing purposes.
|
14192
|
+
var asyncQueue = $rootScope.$$asyncQueue = [];
|
14193
|
+
var postDigestQueue = $rootScope.$$postDigestQueue = [];
|
14194
|
+
var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
|
14195
|
+
|
13776
14196
|
return $rootScope;
|
13777
14197
|
|
13778
14198
|
|
@@ -13806,10 +14226,9 @@ function $RootScopeProvider(){
|
|
13806
14226
|
function initWatchVal() {}
|
13807
14227
|
|
13808
14228
|
function flushApplyAsync() {
|
13809
|
-
|
13810
|
-
while (queue.length) {
|
14229
|
+
while (applyAsyncQueue.length) {
|
13811
14230
|
try {
|
13812
|
-
|
14231
|
+
applyAsyncQueue.shift()();
|
13813
14232
|
} catch(e) {
|
13814
14233
|
$exceptionHandler(e);
|
13815
14234
|
}
|
@@ -13888,12 +14307,9 @@ function $$SanitizeUriProvider() {
|
|
13888
14307
|
return function sanitizeUri(uri, isImage) {
|
13889
14308
|
var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
|
13890
14309
|
var normalizedVal;
|
13891
|
-
|
13892
|
-
if (
|
13893
|
-
normalizedVal
|
13894
|
-
if (normalizedVal !== '' && !normalizedVal.match(regex)) {
|
13895
|
-
return 'unsafe:'+normalizedVal;
|
13896
|
-
}
|
14310
|
+
normalizedVal = urlResolve(uri).href;
|
14311
|
+
if (normalizedVal !== '' && !normalizedVal.match(regex)) {
|
14312
|
+
return 'unsafe:'+normalizedVal;
|
13897
14313
|
}
|
13898
14314
|
return uri;
|
13899
14315
|
};
|
@@ -14965,7 +15381,6 @@ function $SceProvider() {
|
|
14965
15381
|
* @requires $document
|
14966
15382
|
*
|
14967
15383
|
* @property {boolean} history Does the browser support html5 history api ?
|
14968
|
-
* @property {boolean} hashchange Does the browser support hashchange event ?
|
14969
15384
|
* @property {boolean} transitions Does the browser support CSS transition events ?
|
14970
15385
|
* @property {boolean} animations Does the browser support CSS animation events ?
|
14971
15386
|
*
|
@@ -15022,9 +15437,6 @@ function $SnifferProvider() {
|
|
15022
15437
|
// jshint -W018
|
15023
15438
|
history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
|
15024
15439
|
// jshint +W018
|
15025
|
-
hashchange: 'onhashchange' in $window &&
|
15026
|
-
// IE8 compatible mode lies
|
15027
|
-
(!documentMode || documentMode > 7),
|
15028
15440
|
hasEvent: function(event) {
|
15029
15441
|
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
|
15030
15442
|
// it. In particular the event is not fired when backspace or delete key are pressed or
|
@@ -16494,7 +16906,7 @@ function limitToFilter(){
|
|
16494
16906
|
* correctly, make sure they are actually being saved as numbers and not strings.
|
16495
16907
|
*
|
16496
16908
|
* @param {Array} array The array to sort.
|
16497
|
-
* @param {function(*)|string|Array.<(function(*)|string)
|
16909
|
+
* @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
|
16498
16910
|
* used by the comparator to determine the order of elements.
|
16499
16911
|
*
|
16500
16912
|
* Can be one of:
|
@@ -16507,10 +16919,13 @@ function limitToFilter(){
|
|
16507
16919
|
* is interpreted as a property name to be used in comparisons (for example `"special name"`
|
16508
16920
|
* to sort object by the value of their `special name` property). An expression can be
|
16509
16921
|
* optionally prefixed with `+` or `-` to control ascending or descending sort order
|
16510
|
-
* (for example, `+name` or `-name`).
|
16922
|
+
* (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
|
16923
|
+
* element itself is used to compare where sorting.
|
16511
16924
|
* - `Array`: An array of function or string predicates. The first predicate in the array
|
16512
16925
|
* is used for sorting, but when two items are equivalent, the next predicate is used.
|
16513
16926
|
*
|
16927
|
+
* If the predicate is missing or empty then it defaults to `'+'`.
|
16928
|
+
*
|
16514
16929
|
* @param {boolean=} reverse Reverse the order of the array.
|
16515
16930
|
* @returns {Array} Sorted copy of the source array.
|
16516
16931
|
*
|
@@ -16599,8 +17014,8 @@ orderByFilter.$inject = ['$parse'];
|
|
16599
17014
|
function orderByFilter($parse){
|
16600
17015
|
return function(array, sortPredicate, reverseOrder) {
|
16601
17016
|
if (!(isArrayLike(array))) return array;
|
16602
|
-
if (!sortPredicate) return array;
|
16603
17017
|
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
|
17018
|
+
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
|
16604
17019
|
sortPredicate = sortPredicate.map(function(predicate){
|
16605
17020
|
var descending = false, get = predicate || identity;
|
16606
17021
|
if (isString(predicate)) {
|
@@ -16608,6 +17023,12 @@ function orderByFilter($parse){
|
|
16608
17023
|
descending = predicate.charAt(0) == '-';
|
16609
17024
|
predicate = predicate.substring(1);
|
16610
17025
|
}
|
17026
|
+
if ( predicate === '' ) {
|
17027
|
+
// Effectively no predicate was passed so we compare identity
|
17028
|
+
return reverseComparator(function(a,b) {
|
17029
|
+
return compare(a, b);
|
17030
|
+
}, descending);
|
17031
|
+
}
|
16611
17032
|
get = $parse(predicate);
|
16612
17033
|
if (get.constant) {
|
16613
17034
|
var key = get();
|
@@ -16683,22 +17104,6 @@ function ngDirective(directive) {
|
|
16683
17104
|
var htmlAnchorDirective = valueFn({
|
16684
17105
|
restrict: 'E',
|
16685
17106
|
compile: function(element, attr) {
|
16686
|
-
|
16687
|
-
if (msie <= 8) {
|
16688
|
-
|
16689
|
-
// turn <a href ng-click="..">link</a> into a stylable link in IE
|
16690
|
-
// but only if it doesn't have name attribute, in which case it's an anchor
|
16691
|
-
if (!attr.href && !attr.name) {
|
16692
|
-
attr.$set('href', '');
|
16693
|
-
}
|
16694
|
-
|
16695
|
-
// add a comment node to anchors to workaround IE bug that causes element content to be reset
|
16696
|
-
// to new attribute content if attribute is updated with value containing @ and element also
|
16697
|
-
// contains value with @
|
16698
|
-
// see issue #1949
|
16699
|
-
element.append(document.createComment('IE fix'));
|
16700
|
-
}
|
16701
|
-
|
16702
17107
|
if (!attr.href && !attr.xlinkHref && !attr.name) {
|
16703
17108
|
return function(scope, element) {
|
16704
17109
|
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
|
@@ -17146,11 +17551,9 @@ var nullFormCtrl = {
|
|
17146
17551
|
$$renameControl: nullFormRenameControl,
|
17147
17552
|
$removeControl: noop,
|
17148
17553
|
$setValidity: noop,
|
17149
|
-
$$setPending: noop,
|
17150
17554
|
$setDirty: noop,
|
17151
17555
|
$setPristine: noop,
|
17152
|
-
$setSubmitted: noop
|
17153
|
-
$$clearControlValidity: noop
|
17556
|
+
$setSubmitted: noop
|
17154
17557
|
},
|
17155
17558
|
SUBMITTED_CLASS = 'ng-submitted';
|
17156
17559
|
|
@@ -17215,9 +17618,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
|
|
17215
17618
|
|
17216
17619
|
parentForm.$addControl(form);
|
17217
17620
|
|
17218
|
-
// Setup initial state of the control
|
17219
|
-
element.addClass(PRISTINE_CLASS);
|
17220
|
-
|
17221
17621
|
/**
|
17222
17622
|
* @ngdoc method
|
17223
17623
|
* @name form.FormController#$rollbackViewValue
|
@@ -17590,10 +17990,14 @@ var formDirectiveFactory = function(isNgForm) {
|
|
17590
17990
|
name: 'form',
|
17591
17991
|
restrict: isNgForm ? 'EAC' : 'E',
|
17592
17992
|
controller: FormController,
|
17593
|
-
compile: function() {
|
17993
|
+
compile: function ngFormCompile(formElement) {
|
17994
|
+
// Setup initial state of the control
|
17995
|
+
formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
|
17996
|
+
|
17594
17997
|
return {
|
17595
|
-
pre: function(scope, formElement, attr, controller) {
|
17596
|
-
if
|
17998
|
+
pre: function ngFormPreLink(scope, formElement, attr, controller) {
|
17999
|
+
// if `action` attr is not present on the form, prevent the default action (submission)
|
18000
|
+
if (!('action' in attr)) {
|
17597
18001
|
// we can't use jq events because if a form is destroyed during submission the default
|
17598
18002
|
// action is not prevented. see #1238
|
17599
18003
|
//
|
@@ -18747,16 +19151,15 @@ function createDateInputType(type, regexp, parseDate, format) {
|
|
18747
19151
|
badInputChecker(scope, element, attr, ctrl);
|
18748
19152
|
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
18749
19153
|
var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
|
19154
|
+
var previousDate;
|
18750
19155
|
|
18751
19156
|
ctrl.$$parserName = type;
|
18752
19157
|
ctrl.$parsers.push(function(value) {
|
18753
19158
|
if (ctrl.$isEmpty(value)) return null;
|
18754
19159
|
if (regexp.test(value)) {
|
18755
|
-
|
18756
|
-
|
18757
|
-
|
18758
|
-
previousDate = new Date(previousDate.getTime() + timezoneOffset);
|
18759
|
-
}
|
19160
|
+
// Note: We cannot read ctrl.$modelValue, as there might be a different
|
19161
|
+
// parser/formatter in the processing chain so that the model
|
19162
|
+
// contains some different data format!
|
18760
19163
|
var parsedDate = parseDate(value, previousDate);
|
18761
19164
|
if (timezone === 'UTC') {
|
18762
19165
|
parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
|
@@ -18767,8 +19170,18 @@ function createDateInputType(type, regexp, parseDate, format) {
|
|
18767
19170
|
});
|
18768
19171
|
|
18769
19172
|
ctrl.$formatters.push(function(value) {
|
18770
|
-
if (
|
19173
|
+
if (!ctrl.$isEmpty(value)) {
|
19174
|
+
if (!isDate(value)) {
|
19175
|
+
throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
|
19176
|
+
}
|
19177
|
+
previousDate = value;
|
19178
|
+
if (previousDate && timezone === 'UTC') {
|
19179
|
+
var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
|
19180
|
+
previousDate = new Date(previousDate.getTime() + timezoneOffset);
|
19181
|
+
}
|
18771
19182
|
return $filter('date')(value, format, timezone);
|
19183
|
+
} else {
|
19184
|
+
previousDate = null;
|
18772
19185
|
}
|
18773
19186
|
return '';
|
18774
19187
|
});
|
@@ -18794,6 +19207,11 @@ function createDateInputType(type, regexp, parseDate, format) {
|
|
18794
19207
|
ctrl.$validate();
|
18795
19208
|
});
|
18796
19209
|
}
|
19210
|
+
// Override the standard $isEmpty to detect invalid dates as well
|
19211
|
+
ctrl.$isEmpty = function(value) {
|
19212
|
+
// Invalid Date: getTime() returns NaN
|
19213
|
+
return !value || (value.getTime && value.getTime() !== value.getTime());
|
19214
|
+
};
|
18797
19215
|
|
18798
19216
|
function parseObservedDateValue(val) {
|
18799
19217
|
return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
|
@@ -19108,10 +19526,12 @@ var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
|
|
19108
19526
|
return {
|
19109
19527
|
restrict: 'E',
|
19110
19528
|
require: ['?ngModel'],
|
19111
|
-
link:
|
19112
|
-
|
19113
|
-
|
19114
|
-
|
19529
|
+
link: {
|
19530
|
+
pre: function(scope, element, attr, ctrls) {
|
19531
|
+
if (ctrls[0]) {
|
19532
|
+
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
|
19533
|
+
$browser, $filter, $parse);
|
19534
|
+
}
|
19115
19535
|
}
|
19116
19536
|
}
|
19117
19537
|
};
|
@@ -19412,11 +19832,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
19412
19832
|
var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
|
19413
19833
|
currentValidationRunId = 0;
|
19414
19834
|
|
19415
|
-
// Setup initial state of the control
|
19416
|
-
$element
|
19417
|
-
.addClass(PRISTINE_CLASS)
|
19418
|
-
.addClass(UNTOUCHED_CLASS);
|
19419
|
-
|
19420
19835
|
/**
|
19421
19836
|
* @ngdoc method
|
19422
19837
|
* @name ngModel.NgModelController#$setValidity
|
@@ -19709,14 +20124,17 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
19709
20124
|
};
|
19710
20125
|
|
19711
20126
|
this.$$parseAndValidate = function() {
|
19712
|
-
var
|
19713
|
-
|
19714
|
-
|
19715
|
-
|
19716
|
-
|
19717
|
-
|
19718
|
-
|
19719
|
-
|
20127
|
+
var viewValue = ctrl.$$lastCommittedViewValue;
|
20128
|
+
var modelValue = viewValue;
|
20129
|
+
var parserValid = isUndefined(modelValue) ? undefined : true;
|
20130
|
+
|
20131
|
+
if (parserValid) {
|
20132
|
+
for(var i = 0; i < ctrl.$parsers.length; i++) {
|
20133
|
+
modelValue = ctrl.$parsers[i](modelValue);
|
20134
|
+
if (isUndefined(modelValue)) {
|
20135
|
+
parserValid = false;
|
20136
|
+
break;
|
20137
|
+
}
|
19720
20138
|
}
|
19721
20139
|
}
|
19722
20140
|
if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
|
@@ -20033,42 +20451,51 @@ var ngModelDirective = function() {
|
|
20033
20451
|
restrict: 'A',
|
20034
20452
|
require: ['ngModel', '^?form', '^?ngModelOptions'],
|
20035
20453
|
controller: NgModelController,
|
20036
|
-
|
20037
|
-
|
20038
|
-
|
20039
|
-
|
20454
|
+
// Prelink needs to run before any input directive
|
20455
|
+
// so that we can set the NgModelOptions in NgModelController
|
20456
|
+
// before anyone else uses it.
|
20457
|
+
priority: 1,
|
20458
|
+
compile: function ngModelCompile(element) {
|
20459
|
+
// Setup initial state of the control
|
20460
|
+
element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
|
20040
20461
|
|
20041
|
-
|
20462
|
+
return {
|
20463
|
+
pre: function ngModelPreLink(scope, element, attr, ctrls) {
|
20464
|
+
var modelCtrl = ctrls[0],
|
20465
|
+
formCtrl = ctrls[1] || nullFormCtrl;
|
20042
20466
|
|
20043
|
-
|
20044
|
-
formCtrl.$addControl(modelCtrl);
|
20467
|
+
modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
|
20045
20468
|
|
20046
|
-
|
20047
|
-
|
20048
|
-
formCtrl.$$renameControl(modelCtrl, newValue);
|
20049
|
-
}
|
20050
|
-
});
|
20469
|
+
// notify others, especially parent forms
|
20470
|
+
formCtrl.$addControl(modelCtrl);
|
20051
20471
|
|
20052
|
-
|
20053
|
-
|
20054
|
-
|
20055
|
-
|
20056
|
-
post: function(scope, element, attr, ctrls) {
|
20057
|
-
var modelCtrl = ctrls[0];
|
20058
|
-
if (modelCtrl.$options && modelCtrl.$options.updateOn) {
|
20059
|
-
element.on(modelCtrl.$options.updateOn, function(ev) {
|
20060
|
-
modelCtrl.$$debounceViewValueCommit(ev && ev.type);
|
20472
|
+
attr.$observe('name', function(newValue) {
|
20473
|
+
if (modelCtrl.$name !== newValue) {
|
20474
|
+
formCtrl.$$renameControl(modelCtrl, newValue);
|
20475
|
+
}
|
20061
20476
|
});
|
20062
|
-
}
|
20063
20477
|
|
20064
|
-
|
20065
|
-
|
20478
|
+
scope.$on('$destroy', function() {
|
20479
|
+
formCtrl.$removeControl(modelCtrl);
|
20480
|
+
});
|
20481
|
+
},
|
20482
|
+
post: function ngModelPostLink(scope, element, attr, ctrls) {
|
20483
|
+
var modelCtrl = ctrls[0];
|
20484
|
+
if (modelCtrl.$options && modelCtrl.$options.updateOn) {
|
20485
|
+
element.on(modelCtrl.$options.updateOn, function(ev) {
|
20486
|
+
modelCtrl.$$debounceViewValueCommit(ev && ev.type);
|
20487
|
+
});
|
20488
|
+
}
|
20066
20489
|
|
20067
|
-
|
20068
|
-
modelCtrl.$
|
20490
|
+
element.on('blur', function(ev) {
|
20491
|
+
if (modelCtrl.$touched) return;
|
20492
|
+
|
20493
|
+
scope.$apply(function() {
|
20494
|
+
modelCtrl.$setTouched();
|
20495
|
+
});
|
20069
20496
|
});
|
20070
|
-
}
|
20071
|
-
}
|
20497
|
+
}
|
20498
|
+
};
|
20072
20499
|
}
|
20073
20500
|
};
|
20074
20501
|
};
|
@@ -20623,8 +21050,9 @@ function addSetValidityMethod(context) {
|
|
20623
21050
|
parentForm = context.parentForm,
|
20624
21051
|
$animate = context.$animate;
|
20625
21052
|
|
21053
|
+
classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
|
21054
|
+
|
20626
21055
|
ctrl.$setValidity = setValidity;
|
20627
|
-
toggleValidationCss('', true);
|
20628
21056
|
|
20629
21057
|
function setValidity(validationErrorKey, state, options) {
|
20630
21058
|
if (state === undefined) {
|
@@ -20774,11 +21202,9 @@ var ngBindDirective = ['$compile', function($compile) {
|
|
20774
21202
|
$compile.$$addBindingClass(templateElement);
|
20775
21203
|
return function ngBindLink(scope, element, attr) {
|
20776
21204
|
$compile.$$addBindingInfo(element, attr.ngBind);
|
21205
|
+
element = element[0];
|
20777
21206
|
scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
|
20778
|
-
|
20779
|
-
// catch when value is "null or undefined"
|
20780
|
-
// jshint -W041
|
20781
|
-
element.text(value == undefined ? '' : value);
|
21207
|
+
element.textContent = value === undefined ? '' : value;
|
20782
21208
|
});
|
20783
21209
|
};
|
20784
21210
|
}
|
@@ -20844,8 +21270,9 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate
|
|
20844
21270
|
return function ngBindTemplateLink(scope, element, attr) {
|
20845
21271
|
var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
|
20846
21272
|
$compile.$$addBindingInfo(element, interpolateFn.expressions);
|
21273
|
+
element = element[0];
|
20847
21274
|
attr.$observe('ngBindTemplate', function(value) {
|
20848
|
-
element.
|
21275
|
+
element.textContent = value === undefined ? '' : value;
|
20849
21276
|
});
|
20850
21277
|
};
|
20851
21278
|
}
|
@@ -20862,7 +21289,10 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate
|
|
20862
21289
|
* element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link
|
20863
21290
|
* ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize`
|
20864
21291
|
* is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
|
20865
|
-
* core Angular.
|
21292
|
+
* core Angular). In order to use {@link ngSanitize} in your module's dependencies, you need to
|
21293
|
+
* include "angular-sanitize.js" in your application.
|
21294
|
+
*
|
21295
|
+
* You may also bypass sanitization for values you know are safe. To do so, bind to
|
20866
21296
|
* an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
|
20867
21297
|
* under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}.
|
20868
21298
|
*
|
@@ -21624,7 +22054,125 @@ var ngControllerDirective = [function() {
|
|
21624
22054
|
...
|
21625
22055
|
</html>
|
21626
22056
|
```
|
21627
|
-
|
22057
|
+
* @example
|
22058
|
+
// Note: the suffix `.csp` in the example name triggers
|
22059
|
+
// csp mode in our http server!
|
22060
|
+
<example name="example.csp" module="cspExample" ng-csp="true">
|
22061
|
+
<file name="index.html">
|
22062
|
+
<div ng-controller="MainController as ctrl">
|
22063
|
+
<div>
|
22064
|
+
<button ng-click="ctrl.inc()" id="inc">Increment</button>
|
22065
|
+
<span id="counter">
|
22066
|
+
{{ctrl.counter}}
|
22067
|
+
</span>
|
22068
|
+
</div>
|
22069
|
+
|
22070
|
+
<div>
|
22071
|
+
<button ng-click="ctrl.evil()" id="evil">Evil</button>
|
22072
|
+
<span id="evilError">
|
22073
|
+
{{ctrl.evilError}}
|
22074
|
+
</span>
|
22075
|
+
</div>
|
22076
|
+
</div>
|
22077
|
+
</file>
|
22078
|
+
<file name="script.js">
|
22079
|
+
angular.module('cspExample', [])
|
22080
|
+
.controller('MainController', function() {
|
22081
|
+
this.counter = 0;
|
22082
|
+
this.inc = function() {
|
22083
|
+
this.counter++;
|
22084
|
+
};
|
22085
|
+
this.evil = function() {
|
22086
|
+
// jshint evil:true
|
22087
|
+
try {
|
22088
|
+
eval('1+2');
|
22089
|
+
} catch (e) {
|
22090
|
+
this.evilError = e.message;
|
22091
|
+
}
|
22092
|
+
};
|
22093
|
+
});
|
22094
|
+
</file>
|
22095
|
+
<file name="protractor.js" type="protractor">
|
22096
|
+
var util, webdriver;
|
22097
|
+
|
22098
|
+
var incBtn = element(by.id('inc'));
|
22099
|
+
var counter = element(by.id('counter'));
|
22100
|
+
var evilBtn = element(by.id('evil'));
|
22101
|
+
var evilError = element(by.id('evilError'));
|
22102
|
+
|
22103
|
+
function getAndClearSevereErrors() {
|
22104
|
+
return browser.manage().logs().get('browser').then(function(browserLog) {
|
22105
|
+
return browserLog.filter(function(logEntry) {
|
22106
|
+
return logEntry.level.value > webdriver.logging.Level.WARNING.value;
|
22107
|
+
});
|
22108
|
+
});
|
22109
|
+
}
|
22110
|
+
|
22111
|
+
function clearErrors() {
|
22112
|
+
getAndClearSevereErrors();
|
22113
|
+
}
|
22114
|
+
|
22115
|
+
function expectNoErrors() {
|
22116
|
+
getAndClearSevereErrors().then(function(filteredLog) {
|
22117
|
+
expect(filteredLog.length).toEqual(0);
|
22118
|
+
if (filteredLog.length) {
|
22119
|
+
console.log('browser console errors: ' + util.inspect(filteredLog));
|
22120
|
+
}
|
22121
|
+
});
|
22122
|
+
}
|
22123
|
+
|
22124
|
+
function expectError(regex) {
|
22125
|
+
getAndClearSevereErrors().then(function(filteredLog) {
|
22126
|
+
var found = false;
|
22127
|
+
filteredLog.forEach(function(log) {
|
22128
|
+
if (log.message.match(regex)) {
|
22129
|
+
found = true;
|
22130
|
+
}
|
22131
|
+
});
|
22132
|
+
if (!found) {
|
22133
|
+
throw new Error('expected an error that matches ' + regex);
|
22134
|
+
}
|
22135
|
+
});
|
22136
|
+
}
|
22137
|
+
|
22138
|
+
beforeEach(function() {
|
22139
|
+
util = require('util');
|
22140
|
+
webdriver = require('protractor/node_modules/selenium-webdriver');
|
22141
|
+
});
|
22142
|
+
|
22143
|
+
// For now, we only test on Chrome,
|
22144
|
+
// as Safari does not load the page with Protractor's injected scripts,
|
22145
|
+
// and Firefox webdriver always disables content security policy (#6358)
|
22146
|
+
if (browser.params.browser !== 'chrome') {
|
22147
|
+
return;
|
22148
|
+
}
|
22149
|
+
|
22150
|
+
it('should not report errors when the page is loaded', function() {
|
22151
|
+
// clear errors so we are not dependent on previous tests
|
22152
|
+
clearErrors();
|
22153
|
+
// Need to reload the page as the page is already loaded when
|
22154
|
+
// we come here
|
22155
|
+
browser.driver.getCurrentUrl().then(function(url) {
|
22156
|
+
browser.get(url);
|
22157
|
+
});
|
22158
|
+
expectNoErrors();
|
22159
|
+
});
|
22160
|
+
|
22161
|
+
it('should evaluate expressions', function() {
|
22162
|
+
expect(counter.getText()).toEqual('0');
|
22163
|
+
incBtn.click();
|
22164
|
+
expect(counter.getText()).toEqual('1');
|
22165
|
+
expectNoErrors();
|
22166
|
+
});
|
22167
|
+
|
22168
|
+
it('should throw and report an error when using "eval"', function() {
|
22169
|
+
evilBtn.click();
|
22170
|
+
expect(evilError.getText()).toMatch(/Content Security Policy/);
|
22171
|
+
expectError(/Content Security Policy/);
|
22172
|
+
});
|
22173
|
+
</file>
|
22174
|
+
</example>
|
22175
|
+
*/
|
21628
22176
|
|
21629
22177
|
// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
|
21630
22178
|
// bootstrap the system (before $parse is instantiated), for this reason we just have
|
@@ -22131,7 +22679,7 @@ forEach(
|
|
22131
22679
|
* Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
|
22132
22680
|
* is created when the element is restored. The scope created within `ngIf` inherits from
|
22133
22681
|
* its parent scope using
|
22134
|
-
* [prototypal inheritance](https://github.com/angular/angular.js/wiki/
|
22682
|
+
* [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
|
22135
22683
|
* An important implication of this is if `ngModel` is used within `ngIf` to bind to
|
22136
22684
|
* a javascript primitive defined in the parent scope. In this case any modifications made to the
|
22137
22685
|
* variable within the child scope will override (hide) the value in the parent scope.
|
@@ -22145,8 +22693,8 @@ forEach(
|
|
22145
22693
|
* and `leave` effects.
|
22146
22694
|
*
|
22147
22695
|
* @animations
|
22148
|
-
* enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container
|
22149
|
-
* leave - happens just before the ngIf contents are removed from the DOM
|
22696
|
+
* enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
|
22697
|
+
* leave - happens just before the `ngIf` contents are removed from the DOM
|
22150
22698
|
*
|
22151
22699
|
* @element ANY
|
22152
22700
|
* @scope
|
@@ -23286,6 +23834,8 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|
23286
23834
|
};
|
23287
23835
|
}];
|
23288
23836
|
|
23837
|
+
var NG_HIDE_CLASS = 'ng-hide';
|
23838
|
+
var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
|
23289
23839
|
/**
|
23290
23840
|
* @ngdoc directive
|
23291
23841
|
* @name ngShow
|
@@ -23447,7 +23997,11 @@ var ngShowDirective = ['$animate', function($animate) {
|
|
23447
23997
|
multiElement: true,
|
23448
23998
|
link: function(scope, element, attr) {
|
23449
23999
|
scope.$watch(attr.ngShow, function ngShowWatchAction(value){
|
23450
|
-
|
24000
|
+
// we're adding a temporary, animation-specific class for ng-hide since this way
|
24001
|
+
// we can control when the element is actually displayed on screen without having
|
24002
|
+
// to have a global/greedy CSS selector that breaks when other animations are run.
|
24003
|
+
// Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
|
24004
|
+
$animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, NG_HIDE_IN_PROGRESS_CLASS);
|
23451
24005
|
});
|
23452
24006
|
}
|
23453
24007
|
};
|
@@ -23602,7 +24156,9 @@ var ngHideDirective = ['$animate', function($animate) {
|
|
23602
24156
|
multiElement: true,
|
23603
24157
|
link: function(scope, element, attr) {
|
23604
24158
|
scope.$watch(attr.ngHide, function ngHideWatchAction(value){
|
23605
|
-
|
24159
|
+
// The comment inside of the ngShowDirective explains why we add and
|
24160
|
+
// remove a temporary class for the show/hide animation
|
24161
|
+
$animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, NG_HIDE_IN_PROGRESS_CLASS);
|
23606
24162
|
});
|
23607
24163
|
}
|
23608
24164
|
};
|
@@ -24024,6 +24580,12 @@ var ngOptionsMinErr = minErr('ngOptions');
|
|
24024
24580
|
* be bound to string values at present.
|
24025
24581
|
* </div>
|
24026
24582
|
*
|
24583
|
+
* <div class="alert alert-info">
|
24584
|
+
* **Note:** Using `select as` will bind the result of the `select as` expression to the model, but
|
24585
|
+
* the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
|
24586
|
+
* or property name (for object data sources) of the value within the collection.
|
24587
|
+
* </div>
|
24588
|
+
*
|
24027
24589
|
* @param {string} ngModel Assignable angular expression to data-bind to.
|
24028
24590
|
* @param {string=} name Property name of the form under which the control is published.
|
24029
24591
|
* @param {string=} required The control is considered valid only if value is entered.
|
@@ -24058,7 +24620,25 @@ var ngOptionsMinErr = minErr('ngOptions');
|
|
24058
24620
|
* DOM element.
|
24059
24621
|
* * `trackexpr`: Used when working with an array of objects. The result of this expression will be
|
24060
24622
|
* used to identify the objects in the array. The `trackexpr` will most likely refer to the
|
24061
|
-
* `value` variable (e.g. `value.propertyName`).
|
24623
|
+
* `value` variable (e.g. `value.propertyName`). With this the selection is preserved
|
24624
|
+
* even when the options are recreated (e.g. reloaded from the server).
|
24625
|
+
|
24626
|
+
* <div class="alert alert-info">
|
24627
|
+
* **Note:** Using `select as` together with `trackexpr` is not possible (and will throw).
|
24628
|
+
* Reasoning:
|
24629
|
+
* - Example: <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
|
24630
|
+
* values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItemß'}}],
|
24631
|
+
* $scope.selected = {name: 'aSubItem'};
|
24632
|
+
* - track by is always applied to `value`, with purpose to preserve the selection,
|
24633
|
+
* (to `item` in this case)
|
24634
|
+
* - to calculate whether an item is selected we do the following:
|
24635
|
+
* 1. apply `track by` to the values in the array, e.g.
|
24636
|
+
* In the example: [1,2]
|
24637
|
+
* 2. apply `track by` to the already selected value in `ngModel`:
|
24638
|
+
* In the example: this is not possible, as `track by` refers to `item.id`, but the selected
|
24639
|
+
* value from `ngModel` is `{name: aSubItem}`.
|
24640
|
+
*
|
24641
|
+
* </div>
|
24062
24642
|
*
|
24063
24643
|
* @example
|
24064
24644
|
<example module="selectExample">
|
@@ -24315,6 +24895,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
24315
24895
|
|
24316
24896
|
var displayFn = $parse(match[2] || match[1]),
|
24317
24897
|
valueName = match[4] || match[6],
|
24898
|
+
selectAs = / as /.test(match[0]) && match[1],
|
24899
|
+
selectAsFn = selectAs ? $parse(selectAs) : null,
|
24318
24900
|
keyName = match[5],
|
24319
24901
|
groupByFn = $parse(match[3] || ''),
|
24320
24902
|
valueFn = $parse(match[2] ? match[1] : valueName),
|
@@ -24325,7 +24907,16 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
24325
24907
|
// We try to reuse these if possible
|
24326
24908
|
// - optionGroupsCache[0] is the options with no option group
|
24327
24909
|
// - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
|
24328
|
-
optionGroupsCache = [[{element: selectElement, label:''}]]
|
24910
|
+
optionGroupsCache = [[{element: selectElement, label:''}]],
|
24911
|
+
//re-usable object to represent option's locals
|
24912
|
+
locals = {};
|
24913
|
+
|
24914
|
+
if (trackFn && selectAsFn) {
|
24915
|
+
throw ngOptionsMinErr('trkslct',
|
24916
|
+
"Comprehension expression cannot contain both selectAs '{0}' " +
|
24917
|
+
"and trackBy '{1}' expressions.",
|
24918
|
+
selectAs, track);
|
24919
|
+
}
|
24329
24920
|
|
24330
24921
|
if (nullOption) {
|
24331
24922
|
// compile the element since there might be bindings in it
|
@@ -24343,103 +24934,110 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
24343
24934
|
// clear contents, we'll add what's needed based on the model
|
24344
24935
|
selectElement.empty();
|
24345
24936
|
|
24346
|
-
selectElement.on('change',
|
24937
|
+
selectElement.on('change', selectionChanged);
|
24938
|
+
|
24939
|
+
ctrl.$render = render;
|
24940
|
+
|
24941
|
+
scope.$watchCollection(valuesFn, scheduleRendering);
|
24942
|
+
scope.$watchCollection(getLabels, scheduleRendering);
|
24943
|
+
|
24944
|
+
if (multiple) {
|
24945
|
+
scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
|
24946
|
+
}
|
24947
|
+
|
24948
|
+
// ------------------------------------------------------------------ //
|
24949
|
+
|
24950
|
+
function callExpression(exprFn, key, value) {
|
24951
|
+
locals[valueName] = value;
|
24952
|
+
if (keyName) locals[keyName] = key;
|
24953
|
+
return exprFn(scope, locals);
|
24954
|
+
}
|
24955
|
+
|
24956
|
+
function selectionChanged() {
|
24347
24957
|
scope.$apply(function() {
|
24348
24958
|
var optionGroup,
|
24349
24959
|
collection = valuesFn(scope) || [],
|
24350
|
-
locals = {},
|
24351
24960
|
key, value, optionElement, index, groupIndex, length, groupLength, trackIndex;
|
24352
|
-
|
24961
|
+
var viewValue;
|
24353
24962
|
if (multiple) {
|
24354
|
-
|
24355
|
-
|
24356
|
-
|
24357
|
-
|
24358
|
-
// list of options for that group. (first item has the parent)
|
24359
|
-
optionGroup = optionGroupsCache[groupIndex];
|
24360
|
-
|
24361
|
-
for(index = 1, length = optionGroup.length; index < length; index++) {
|
24362
|
-
if ((optionElement = optionGroup[index].element)[0].selected) {
|
24363
|
-
key = optionElement.val();
|
24364
|
-
if (keyName) locals[keyName] = key;
|
24365
|
-
if (trackFn) {
|
24366
|
-
for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
|
24367
|
-
locals[valueName] = collection[trackIndex];
|
24368
|
-
if (trackFn(scope, locals) == key) break;
|
24369
|
-
}
|
24370
|
-
} else {
|
24371
|
-
locals[valueName] = collection[key];
|
24372
|
-
}
|
24373
|
-
value.push(valueFn(scope, locals));
|
24374
|
-
}
|
24375
|
-
}
|
24376
|
-
}
|
24963
|
+
viewValue = [];
|
24964
|
+
forEach(selectElement.val(), function(selectedKey) {
|
24965
|
+
viewValue.push(getViewValue(selectedKey, collection[selectedKey]));
|
24966
|
+
});
|
24377
24967
|
} else {
|
24378
|
-
|
24379
|
-
|
24380
|
-
value = undefined;
|
24381
|
-
} else if (key === ''){
|
24382
|
-
value = null;
|
24383
|
-
} else {
|
24384
|
-
if (trackFn) {
|
24385
|
-
for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
|
24386
|
-
locals[valueName] = collection[trackIndex];
|
24387
|
-
if (trackFn(scope, locals) == key) {
|
24388
|
-
value = valueFn(scope, locals);
|
24389
|
-
break;
|
24390
|
-
}
|
24391
|
-
}
|
24392
|
-
} else {
|
24393
|
-
locals[valueName] = collection[key];
|
24394
|
-
if (keyName) locals[keyName] = key;
|
24395
|
-
value = valueFn(scope, locals);
|
24396
|
-
}
|
24397
|
-
}
|
24968
|
+
var selectedKey = selectElement.val();
|
24969
|
+
viewValue = getViewValue(selectedKey, collection[selectedKey]);
|
24398
24970
|
}
|
24399
|
-
ctrl.$setViewValue(
|
24971
|
+
ctrl.$setViewValue(viewValue);
|
24400
24972
|
render();
|
24401
24973
|
});
|
24402
|
-
}
|
24974
|
+
}
|
24403
24975
|
|
24404
|
-
|
24976
|
+
function getViewValue(key, value) {
|
24977
|
+
if (key === '?') {
|
24978
|
+
return undefined;
|
24979
|
+
} else if (key === '') {
|
24980
|
+
return null;
|
24981
|
+
} else {
|
24982
|
+
var viewValueFn = selectAsFn ? selectAsFn : valueFn;
|
24983
|
+
return callExpression(viewValueFn, key, value);
|
24984
|
+
}
|
24985
|
+
}
|
24405
24986
|
|
24406
|
-
|
24407
|
-
|
24408
|
-
var
|
24409
|
-
|
24410
|
-
|
24411
|
-
var toDisplay = new Array(values.length);
|
24987
|
+
function getLabels() {
|
24988
|
+
var values = valuesFn(scope);
|
24989
|
+
var toDisplay;
|
24990
|
+
if (values && isArray(values)) {
|
24991
|
+
toDisplay = new Array(values.length);
|
24412
24992
|
for (var i = 0, ii = values.length; i < ii; i++) {
|
24413
|
-
|
24414
|
-
toDisplay[i] = displayFn(scope, locals);
|
24993
|
+
toDisplay[i] = callExpression(displayFn, i, values[i]);
|
24415
24994
|
}
|
24416
24995
|
return toDisplay;
|
24996
|
+
} else if (values) {
|
24997
|
+
// TODO: Add a test for this case
|
24998
|
+
toDisplay = {};
|
24999
|
+
for (var prop in values) {
|
25000
|
+
if (values.hasOwnProperty(prop)) {
|
25001
|
+
toDisplay[prop] = callExpression(displayFn, prop, values[prop]);
|
25002
|
+
}
|
25003
|
+
}
|
24417
25004
|
}
|
24418
|
-
|
24419
|
-
|
24420
|
-
if (multiple) {
|
24421
|
-
scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
|
25005
|
+
return toDisplay;
|
24422
25006
|
}
|
24423
25007
|
|
24424
|
-
|
24425
|
-
|
24426
|
-
var selectedSet = false;
|
25008
|
+
function createIsSelectedFn(viewValue) {
|
25009
|
+
var selectedSet;
|
24427
25010
|
if (multiple) {
|
24428
|
-
|
24429
|
-
|
25011
|
+
if (!selectAs && trackFn && isArray(viewValue)) {
|
25012
|
+
|
24430
25013
|
selectedSet = new HashMap([]);
|
24431
|
-
var
|
24432
|
-
|
24433
|
-
|
24434
|
-
selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
|
25014
|
+
for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) {
|
25015
|
+
// tracking by key
|
25016
|
+
selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true);
|
24435
25017
|
}
|
24436
25018
|
} else {
|
24437
|
-
selectedSet = new HashMap(
|
25019
|
+
selectedSet = new HashMap(viewValue);
|
24438
25020
|
}
|
25021
|
+
} else if (!selectAsFn && trackFn) {
|
25022
|
+
viewValue = callExpression(trackFn, null, viewValue);
|
24439
25023
|
}
|
24440
|
-
return
|
24441
|
-
|
25024
|
+
return function isSelected(key, value) {
|
25025
|
+
var compareValueFn;
|
25026
|
+
if (selectAsFn) {
|
25027
|
+
compareValueFn = selectAsFn;
|
25028
|
+
} else if (trackFn) {
|
25029
|
+
compareValueFn = trackFn;
|
25030
|
+
} else {
|
25031
|
+
compareValueFn = valueFn;
|
25032
|
+
}
|
24442
25033
|
|
25034
|
+
if (multiple) {
|
25035
|
+
return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value)));
|
25036
|
+
} else {
|
25037
|
+
return viewValue == callExpression(compareValueFn, key, value);
|
25038
|
+
}
|
25039
|
+
};
|
25040
|
+
}
|
24443
25041
|
|
24444
25042
|
function scheduleRendering() {
|
24445
25043
|
if (!renderScheduled) {
|
@@ -24448,78 +25046,64 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
24448
25046
|
}
|
24449
25047
|
}
|
24450
25048
|
|
24451
|
-
|
24452
25049
|
function render() {
|
24453
25050
|
renderScheduled = false;
|
24454
25051
|
|
24455
|
-
|
25052
|
+
// Temporary location for the option groups before we render them
|
24456
25053
|
var optionGroups = {'':[]},
|
24457
25054
|
optionGroupNames = [''],
|
24458
25055
|
optionGroupName,
|
24459
25056
|
optionGroup,
|
24460
25057
|
option,
|
24461
25058
|
existingParent, existingOptions, existingOption,
|
24462
|
-
|
25059
|
+
viewValue = ctrl.$viewValue,
|
24463
25060
|
values = valuesFn(scope) || [],
|
24464
25061
|
keys = keyName ? sortedKeys(values) : values,
|
24465
25062
|
key,
|
25063
|
+
value,
|
24466
25064
|
groupLength, length,
|
24467
25065
|
groupIndex, index,
|
24468
|
-
locals = {},
|
24469
25066
|
selected,
|
24470
|
-
|
25067
|
+
isSelected = createIsSelectedFn(viewValue),
|
25068
|
+
anySelected = false,
|
24471
25069
|
lastElement,
|
24472
25070
|
element,
|
24473
25071
|
label;
|
24474
25072
|
|
24475
|
-
|
24476
25073
|
// We now build up the list of options we need (we merge later)
|
24477
25074
|
for (index = 0; length = keys.length, index < length; index++) {
|
24478
|
-
|
24479
25075
|
key = index;
|
24480
25076
|
if (keyName) {
|
24481
25077
|
key = keys[index];
|
24482
25078
|
if ( key.charAt(0) === '$' ) continue;
|
24483
|
-
locals[keyName] = key;
|
24484
25079
|
}
|
25080
|
+
value = values[key];
|
24485
25081
|
|
24486
|
-
|
24487
|
-
|
24488
|
-
optionGroupName = groupByFn(scope, locals) || '';
|
25082
|
+
optionGroupName = callExpression(groupByFn, key, value) || '';
|
24489
25083
|
if (!(optionGroup = optionGroups[optionGroupName])) {
|
24490
25084
|
optionGroup = optionGroups[optionGroupName] = [];
|
24491
25085
|
optionGroupNames.push(optionGroupName);
|
24492
25086
|
}
|
24493
|
-
|
24494
|
-
|
24495
|
-
|
24496
|
-
|
24497
|
-
|
24498
|
-
if (trackFn) {
|
24499
|
-
var modelCast = {};
|
24500
|
-
modelCast[valueName] = modelValue;
|
24501
|
-
selected = trackFn(scope, modelCast) === trackFn(scope, locals);
|
24502
|
-
} else {
|
24503
|
-
selected = modelValue === valueFn(scope, locals);
|
24504
|
-
}
|
24505
|
-
selectedSet = selectedSet || selected; // see if at least one item is selected
|
24506
|
-
}
|
24507
|
-
label = displayFn(scope, locals); // what will be seen by the user
|
25087
|
+
|
25088
|
+
selected = isSelected(key, value);
|
25089
|
+
anySelected = anySelected || selected;
|
25090
|
+
|
25091
|
+
label = callExpression(displayFn, key, value); // what will be seen by the user
|
24508
25092
|
|
24509
25093
|
// doing displayFn(scope, locals) || '' overwrites zero values
|
24510
25094
|
label = isDefined(label) ? label : '';
|
24511
25095
|
optionGroup.push({
|
24512
25096
|
// either the index into array or key from object
|
24513
|
-
id:
|
25097
|
+
id: (keyName ? keys[index] : index),
|
24514
25098
|
label: label,
|
24515
25099
|
selected: selected // determine if we should be selected
|
24516
25100
|
});
|
24517
25101
|
}
|
24518
25102
|
if (!multiple) {
|
24519
|
-
if (nullOption ||
|
25103
|
+
if (nullOption || viewValue === null) {
|
24520
25104
|
// insert null option if we have a placeholder, or the model is null
|
24521
|
-
optionGroups[''].unshift({id:'', label:'', selected:!
|
24522
|
-
} else if (!
|
25105
|
+
optionGroups[''].unshift({id:'', label:'', selected:!anySelected});
|
25106
|
+
} else if (!anySelected) {
|
24523
25107
|
// option could not be found, we have to insert the undefined item
|
24524
25108
|
optionGroups[''].unshift({id:'?', label:'', selected:true});
|
24525
25109
|
}
|
@@ -24600,6 +25184,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
24600
25184
|
id: option.id,
|
24601
25185
|
selected: option.selected
|
24602
25186
|
});
|
25187
|
+
selectCtrl.addOption(option.label, element);
|
24603
25188
|
if (lastElement) {
|
24604
25189
|
lastElement.after(element);
|
24605
25190
|
} else {
|
@@ -24611,7 +25196,9 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
24611
25196
|
// remove any excessive OPTIONs in a group
|
24612
25197
|
index++; // increment since the existingOptions[0] is parent element not OPTION
|
24613
25198
|
while(existingOptions.length > index) {
|
24614
|
-
existingOptions.pop()
|
25199
|
+
option = existingOptions.pop();
|
25200
|
+
selectCtrl.removeOption(option.label);
|
25201
|
+
option.element.remove();
|
24615
25202
|
}
|
24616
25203
|
}
|
24617
25204
|
// remove any excessive OPTGROUPs from select
|
@@ -24647,11 +25234,7 @@ var optionDirective = ['$interpolate', function($interpolate) {
|
|
24647
25234
|
selectCtrl = parent.data(selectCtrlName) ||
|
24648
25235
|
parent.parent().data(selectCtrlName); // in case we are in optgroup
|
24649
25236
|
|
24650
|
-
if (selectCtrl
|
24651
|
-
// For some reason Opera defaults to true and if not overridden this messes up the repeater.
|
24652
|
-
// We don't want the view to drive the initialization of the model anyway.
|
24653
|
-
element.prop('selected', false);
|
24654
|
-
} else {
|
25237
|
+
if (!selectCtrl || !selectCtrl.databound) {
|
24655
25238
|
selectCtrl = nullSelectCtrl;
|
24656
25239
|
}
|
24657
25240
|
|
@@ -24698,4 +25281,4 @@ var styleDirective = valueFn({
|
|
24698
25281
|
|
24699
25282
|
})(window, document);
|
24700
25283
|
|
24701
|
-
!window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-animate){display:none !important;}ng\\:form{display:block;}</style>');
|
25284
|
+
!window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}</style>');
|