angularjs-rails 1.2.25 → 1.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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>');
|