angularjs-rails 1.5.6 → 1.5.8
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 +1 -1
- data/vendor/assets/javascripts/angular-animate.js +40 -49
- data/vendor/assets/javascripts/angular-aria.js +1 -1
- data/vendor/assets/javascripts/angular-cookies.js +1 -1
- data/vendor/assets/javascripts/angular-loader.js +3 -3
- data/vendor/assets/javascripts/angular-message-format.js +136 -32
- data/vendor/assets/javascripts/angular-messages.js +394 -379
- data/vendor/assets/javascripts/angular-mocks.js +30 -16
- data/vendor/assets/javascripts/angular-parse-ext.js +1 -1
- data/vendor/assets/javascripts/angular-resource.js +104 -9
- data/vendor/assets/javascripts/angular-route.js +39 -2
- data/vendor/assets/javascripts/angular-sanitize.js +319 -298
- data/vendor/assets/javascripts/angular-scenario.js +2639 -1602
- data/vendor/assets/javascripts/angular-touch.js +15 -9
- data/vendor/assets/javascripts/angular.js +1167 -422
- metadata +11 -11
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.5.
|
2
|
+
* @license AngularJS v1.5.8
|
3
3
|
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
4
4
|
* License: MIT
|
5
5
|
*/
|
@@ -113,19 +113,29 @@ angular.mock.$Browser = function() {
|
|
113
113
|
* @param {number=} number of milliseconds to flush. See {@link #defer.now}
|
114
114
|
*/
|
115
115
|
self.defer.flush = function(delay) {
|
116
|
+
var nextTime;
|
117
|
+
|
116
118
|
if (angular.isDefined(delay)) {
|
117
|
-
|
119
|
+
// A delay was passed so compute the next time
|
120
|
+
nextTime = self.defer.now + delay;
|
118
121
|
} else {
|
119
122
|
if (self.deferredFns.length) {
|
120
|
-
|
123
|
+
// No delay was passed so set the next time so that it clears the deferred queue
|
124
|
+
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
|
121
125
|
} else {
|
126
|
+
// No delay passed, but there are no deferred tasks so flush - indicates an error!
|
122
127
|
throw new Error('No deferred tasks to be flushed');
|
123
128
|
}
|
124
129
|
}
|
125
130
|
|
126
|
-
while (self.deferredFns.length && self.deferredFns[0].time <=
|
131
|
+
while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
|
132
|
+
// Increment the time and call the next deferred function
|
133
|
+
self.defer.now = self.deferredFns[0].time;
|
127
134
|
self.deferredFns.shift().fn();
|
128
135
|
}
|
136
|
+
|
137
|
+
// Ensure that the current time is correct
|
138
|
+
self.defer.now = nextTime;
|
129
139
|
};
|
130
140
|
|
131
141
|
self.$$baseHref = '/';
|
@@ -934,13 +944,10 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
|
934
944
|
* @name angular.mock.dump
|
935
945
|
* @description
|
936
946
|
*
|
937
|
-
* *NOTE*:
|
938
|
-
*
|
939
|
-
* Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
|
940
|
-
* debugging.
|
947
|
+
* *NOTE*: This is not an injectable instance, just a globally available function.
|
941
948
|
*
|
942
|
-
*
|
943
|
-
* console.
|
949
|
+
* Method for serializing common angular objects (scope, elements, etc..) into strings.
|
950
|
+
* It is useful for logging objects to the console when debugging.
|
944
951
|
*
|
945
952
|
* @param {*} object - any object to turn into string.
|
946
953
|
* @return {string} a serialized string of the argument
|
@@ -2201,9 +2208,15 @@ angular.mock.$RootElementProvider = function() {
|
|
2201
2208
|
angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
|
2202
2209
|
return function(expression, locals, later, ident) {
|
2203
2210
|
if (later && typeof later === 'object') {
|
2204
|
-
var
|
2205
|
-
angular.extend(
|
2206
|
-
|
2211
|
+
var instantiate = $delegate(expression, locals, true, ident);
|
2212
|
+
angular.extend(instantiate.instance, later);
|
2213
|
+
|
2214
|
+
var instance = instantiate();
|
2215
|
+
if (instance !== instantiate.instance) {
|
2216
|
+
angular.extend(instance, later);
|
2217
|
+
}
|
2218
|
+
|
2219
|
+
return instance;
|
2207
2220
|
}
|
2208
2221
|
return $delegate(expression, locals, later, ident);
|
2209
2222
|
};
|
@@ -2275,7 +2288,7 @@ angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compi
|
|
2275
2288
|
* * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
|
2276
2289
|
* `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
|
2277
2290
|
* * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
|
2278
|
-
* * [Bower](http://bower.io) e.g. `bower install angular-mocks
|
2291
|
+
* * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
|
2279
2292
|
* * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use) e.g.
|
2280
2293
|
* `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
|
2281
2294
|
*
|
@@ -2325,6 +2338,7 @@ angular.module('ngMock', ['ng']).provider({
|
|
2325
2338
|
* the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
|
2326
2339
|
*/
|
2327
2340
|
angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
2341
|
+
$provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
|
2328
2342
|
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
|
2329
2343
|
}]);
|
2330
2344
|
|
@@ -2923,7 +2937,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2923
2937
|
angular.forEach(angular.callbacks, function(val, key) {
|
2924
2938
|
delete angular.callbacks[key];
|
2925
2939
|
});
|
2926
|
-
angular.callbacks
|
2940
|
+
angular.callbacks.$$counter = 0;
|
2927
2941
|
};
|
2928
2942
|
|
2929
2943
|
(window.beforeEach || window.setup)(module.$$beforeEach);
|
@@ -3026,7 +3040,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
3026
3040
|
this.stack = e.stack + '\n' + errorForStack.stack;
|
3027
3041
|
if (e.stackArray) this.stackArray = e.stackArray;
|
3028
3042
|
};
|
3029
|
-
ErrorAddingDeclarationLocationStack.prototype
|
3043
|
+
ErrorAddingDeclarationLocationStack.prototype = Error.prototype;
|
3030
3044
|
|
3031
3045
|
window.inject = angular.mock.inject = function() {
|
3032
3046
|
var blockFns = Array.prototype.slice.call(arguments, 0);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.5.
|
2
|
+
* @license AngularJS v1.5.8
|
3
3
|
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
4
4
|
* License: MIT
|
5
5
|
*/
|
@@ -61,7 +61,21 @@ function shallowClearAndCopy(src, dst) {
|
|
61
61
|
*
|
62
62
|
* <div doc-module-components="ngResource"></div>
|
63
63
|
*
|
64
|
-
* See {@link ngResource.$
|
64
|
+
* See {@link ngResource.$resourceProvider} and {@link ngResource.$resource} for usage.
|
65
|
+
*/
|
66
|
+
|
67
|
+
/**
|
68
|
+
* @ngdoc provider
|
69
|
+
* @name $resourceProvider
|
70
|
+
*
|
71
|
+
* @description
|
72
|
+
*
|
73
|
+
* Use `$resourceProvider` to change the default behavior of the {@link ngResource.$resource}
|
74
|
+
* service.
|
75
|
+
*
|
76
|
+
* ## Dependencies
|
77
|
+
* Requires the {@link ngResource } module to be installed.
|
78
|
+
*
|
65
79
|
*/
|
66
80
|
|
67
81
|
/**
|
@@ -105,8 +119,9 @@ function shallowClearAndCopy(src, dst) {
|
|
105
119
|
* can escape it with `/\.`.
|
106
120
|
*
|
107
121
|
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
|
108
|
-
* `actions` methods. If a parameter value is a function, it will be
|
109
|
-
*
|
122
|
+
* `actions` methods. If a parameter value is a function, it will be called every time
|
123
|
+
* a param value needs to be obtained for a request (unless the param was overridden). The function
|
124
|
+
* will be passed the current data value as an argument.
|
110
125
|
*
|
111
126
|
* Each key value in the parameter object is first bound to url template if present and then any
|
112
127
|
* excess keys are appended to the url search query after the `?`.
|
@@ -114,10 +129,13 @@ function shallowClearAndCopy(src, dst) {
|
|
114
129
|
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
115
130
|
* URL `/path/greet?salutation=Hello`.
|
116
131
|
*
|
117
|
-
* If the parameter value is prefixed with
|
118
|
-
* from the corresponding property on the `data` object (provided when calling
|
132
|
+
* If the parameter value is prefixed with `@`, then the value for that parameter will be
|
133
|
+
* extracted from the corresponding property on the `data` object (provided when calling a
|
134
|
+
* "non-GET" action method).
|
119
135
|
* For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
|
120
136
|
* `someParam` will be `data.someProp`.
|
137
|
+
* Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
|
138
|
+
* method that does not accept a request body)
|
121
139
|
*
|
122
140
|
* @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
|
123
141
|
* the default set of resource actions. The declaration should be created in the format of {@link
|
@@ -134,8 +152,9 @@ function shallowClearAndCopy(src, dst) {
|
|
134
152
|
* - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
|
135
153
|
* `DELETE`, `JSONP`, etc).
|
136
154
|
* - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
|
137
|
-
* the parameter value is a function, it will be
|
138
|
-
* be obtained for a request (unless the param was overridden).
|
155
|
+
* the parameter value is a function, it will be called every time when a param value needs to
|
156
|
+
* be obtained for a request (unless the param was overridden). The function will be passed the
|
157
|
+
* current data value as an argument.
|
139
158
|
* - **`url`** – {string} – action specific `url` override. The url templating is supported just
|
140
159
|
* like for the resource-level urls.
|
141
160
|
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
|
@@ -256,6 +275,14 @@ function shallowClearAndCopy(src, dst) {
|
|
256
275
|
* - `$cancelRequest`: If there is a cancellable, pending request related to the instance or
|
257
276
|
* collection, calling this method will abort the request.
|
258
277
|
*
|
278
|
+
* The Resource instances have these additional methods:
|
279
|
+
*
|
280
|
+
* - `toJSON`: It returns a simple object without any of the extra properties added as part of
|
281
|
+
* the Resource API. This object can be serialized through {@link angular.toJson} safely
|
282
|
+
* without attaching Angular-specific fields. Notice that `JSON.stringify` (and
|
283
|
+
* `angular.toJson`) automatically use this method when serializing a Resource instance
|
284
|
+
* (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior)).
|
285
|
+
*
|
259
286
|
* @example
|
260
287
|
*
|
261
288
|
* # Credit card resource
|
@@ -407,10 +434,78 @@ angular.module('ngResource', ['ng']).
|
|
407
434
|
var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
|
408
435
|
var provider = this;
|
409
436
|
|
437
|
+
/**
|
438
|
+
* @ngdoc property
|
439
|
+
* @name $resourceProvider#defaults
|
440
|
+
* @description
|
441
|
+
* Object containing default options used when creating `$resource` instances.
|
442
|
+
*
|
443
|
+
* The default values satisfy a wide range of usecases, but you may choose to overwrite any of
|
444
|
+
* them to further customize your instances. The available properties are:
|
445
|
+
*
|
446
|
+
* - **stripTrailingSlashes** – `{boolean}` – If true, then the trailing slashes from any
|
447
|
+
* calculated URL will be stripped.<br />
|
448
|
+
* (Defaults to true.)
|
449
|
+
* - **cancellable** – `{boolean}` – If true, the request made by a "non-instance" call will be
|
450
|
+
* cancelled (if not already completed) by calling `$cancelRequest()` on the call's return
|
451
|
+
* value. For more details, see {@link ngResource.$resource}. This can be overwritten per
|
452
|
+
* resource class or action.<br />
|
453
|
+
* (Defaults to false.)
|
454
|
+
* - **actions** - `{Object.<Object>}` - A hash with default actions declarations. Actions are
|
455
|
+
* high-level methods corresponding to RESTful actions/methods on resources. An action may
|
456
|
+
* specify what HTTP method to use, what URL to hit, if the return value will be a single
|
457
|
+
* object or a collection (array) of objects etc. For more details, see
|
458
|
+
* {@link ngResource.$resource}. The actions can also be enhanced or overwritten per resource
|
459
|
+
* class.<br />
|
460
|
+
* The default actions are:
|
461
|
+
* ```js
|
462
|
+
* {
|
463
|
+
* get: {method: 'GET'},
|
464
|
+
* save: {method: 'POST'},
|
465
|
+
* query: {method: 'GET', isArray: true},
|
466
|
+
* remove: {method: 'DELETE'},
|
467
|
+
* delete: {method: 'DELETE'}
|
468
|
+
* }
|
469
|
+
* ```
|
470
|
+
*
|
471
|
+
* #### Example
|
472
|
+
*
|
473
|
+
* For example, you can specify a new `update` action that uses the `PUT` HTTP verb:
|
474
|
+
*
|
475
|
+
* ```js
|
476
|
+
* angular.
|
477
|
+
* module('myApp').
|
478
|
+
* config(['resourceProvider', function ($resourceProvider) {
|
479
|
+
* $resourceProvider.defaults.actions.update = {
|
480
|
+
* method: 'PUT'
|
481
|
+
* };
|
482
|
+
* });
|
483
|
+
* ```
|
484
|
+
*
|
485
|
+
* Or you can even overwrite the whole `actions` list and specify your own:
|
486
|
+
*
|
487
|
+
* ```js
|
488
|
+
* angular.
|
489
|
+
* module('myApp').
|
490
|
+
* config(['resourceProvider', function ($resourceProvider) {
|
491
|
+
* $resourceProvider.defaults.actions = {
|
492
|
+
* create: {method: 'POST'}
|
493
|
+
* get: {method: 'GET'},
|
494
|
+
* getAll: {method: 'GET', isArray:true},
|
495
|
+
* update: {method: 'PUT'},
|
496
|
+
* delete: {method: 'DELETE'}
|
497
|
+
* };
|
498
|
+
* });
|
499
|
+
* ```
|
500
|
+
*
|
501
|
+
*/
|
410
502
|
this.defaults = {
|
411
503
|
// Strip slashes by default
|
412
504
|
stripTrailingSlashes: true,
|
413
505
|
|
506
|
+
// Make non-instance requests cancellable (via `$cancelRequest()`)
|
507
|
+
cancellable: false,
|
508
|
+
|
414
509
|
// Default actions configuration
|
415
510
|
actions: {
|
416
511
|
'get': {method: 'GET'},
|
@@ -556,7 +651,7 @@ angular.module('ngResource', ['ng']).
|
|
556
651
|
var ids = {};
|
557
652
|
actionParams = extend({}, paramDefaults, actionParams);
|
558
653
|
forEach(actionParams, function(value, key) {
|
559
|
-
if (isFunction(value)) { value = value(); }
|
654
|
+
if (isFunction(value)) { value = value(data); }
|
560
655
|
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
|
561
656
|
lookupDottedPath(data, value.substr(1)) : value;
|
562
657
|
});
|
@@ -1,10 +1,44 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.5.
|
2
|
+
* @license AngularJS v1.5.8
|
3
3
|
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
4
4
|
* License: MIT
|
5
5
|
*/
|
6
6
|
(function(window, angular) {'use strict';
|
7
7
|
|
8
|
+
/* global shallowCopy: true */
|
9
|
+
|
10
|
+
/**
|
11
|
+
* Creates a shallow copy of an object, an array or a primitive.
|
12
|
+
*
|
13
|
+
* Assumes that there are no proto properties for objects.
|
14
|
+
*/
|
15
|
+
function shallowCopy(src, dst) {
|
16
|
+
if (isArray(src)) {
|
17
|
+
dst = dst || [];
|
18
|
+
|
19
|
+
for (var i = 0, ii = src.length; i < ii; i++) {
|
20
|
+
dst[i] = src[i];
|
21
|
+
}
|
22
|
+
} else if (isObject(src)) {
|
23
|
+
dst = dst || {};
|
24
|
+
|
25
|
+
for (var key in src) {
|
26
|
+
if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
27
|
+
dst[key] = src[key];
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
return dst || src;
|
33
|
+
}
|
34
|
+
|
35
|
+
/* global shallowCopy: false */
|
36
|
+
|
37
|
+
// There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
|
38
|
+
// They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available.
|
39
|
+
var isArray;
|
40
|
+
var isObject;
|
41
|
+
|
8
42
|
/**
|
9
43
|
* @ngdoc module
|
10
44
|
* @name ngRoute
|
@@ -40,6 +74,9 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
|
|
40
74
|
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
41
75
|
*/
|
42
76
|
function $RouteProvider() {
|
77
|
+
isArray = angular.isArray;
|
78
|
+
isObject = angular.isObject;
|
79
|
+
|
43
80
|
function inherit(parent, extra) {
|
44
81
|
return angular.extend(Object.create(parent), extra);
|
45
82
|
}
|
@@ -159,7 +196,7 @@ function $RouteProvider() {
|
|
159
196
|
*/
|
160
197
|
this.when = function(path, route) {
|
161
198
|
//copy original route object to preserve params inherited from proto chain
|
162
|
-
var routeCopy =
|
199
|
+
var routeCopy = shallowCopy(route);
|
163
200
|
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
|
164
201
|
routeCopy.reloadOnSearch = true;
|
165
202
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.5.
|
2
|
+
* @license AngularJS v1.5.8
|
3
3
|
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
4
4
|
* License: MIT
|
5
5
|
*/
|
@@ -17,6 +17,14 @@
|
|
17
17
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
18
18
|
|
19
19
|
var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
20
|
+
var bind;
|
21
|
+
var extend;
|
22
|
+
var forEach;
|
23
|
+
var isDefined;
|
24
|
+
var lowercase;
|
25
|
+
var noop;
|
26
|
+
var htmlParser;
|
27
|
+
var htmlSanitizeWriter;
|
20
28
|
|
21
29
|
/**
|
22
30
|
* @ngdoc module
|
@@ -149,7 +157,7 @@ function $SanitizeProvider() {
|
|
149
157
|
|
150
158
|
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
|
151
159
|
if (svgEnabled) {
|
152
|
-
|
160
|
+
extend(validElements, svgElements);
|
153
161
|
}
|
154
162
|
return function(html) {
|
155
163
|
var buf = [];
|
@@ -192,334 +200,344 @@ function $SanitizeProvider() {
|
|
192
200
|
* without an argument or self for chaining otherwise.
|
193
201
|
*/
|
194
202
|
this.enableSvg = function(enableSvg) {
|
195
|
-
if (
|
203
|
+
if (isDefined(enableSvg)) {
|
196
204
|
svgEnabled = enableSvg;
|
197
205
|
return this;
|
198
206
|
} else {
|
199
207
|
return svgEnabled;
|
200
208
|
}
|
201
209
|
};
|
202
|
-
}
|
203
|
-
|
204
|
-
function sanitizeText(chars) {
|
205
|
-
var buf = [];
|
206
|
-
var writer = htmlSanitizeWriter(buf, angular.noop);
|
207
|
-
writer.chars(chars);
|
208
|
-
return buf.join('');
|
209
|
-
}
|
210
|
-
|
211
|
-
|
212
|
-
// Regular Expressions for parsing tags and attributes
|
213
|
-
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
214
|
-
// Match everything outside of normal chars and " (quote character)
|
215
|
-
NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
|
216
|
-
|
217
|
-
|
218
|
-
// Good source of info about elements and attributes
|
219
|
-
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
220
|
-
// http://simon.html5.org/html-elements
|
221
|
-
|
222
|
-
// Safe Void Elements - HTML5
|
223
|
-
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
224
|
-
var voidElements = toMap("area,br,col,hr,img,wbr");
|
225
|
-
|
226
|
-
// Elements that you can, intentionally, leave open (and which close themselves)
|
227
|
-
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
228
|
-
var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
|
229
|
-
optionalEndTagInlineElements = toMap("rp,rt"),
|
230
|
-
optionalEndTagElements = angular.extend({},
|
231
|
-
optionalEndTagInlineElements,
|
232
|
-
optionalEndTagBlockElements);
|
233
|
-
|
234
|
-
// Safe Block Elements - HTML5
|
235
|
-
var blockElements = angular.extend({}, optionalEndTagBlockElements, toMap("address,article," +
|
236
|
-
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
|
237
|
-
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
|
238
|
-
|
239
|
-
// Inline Elements - HTML5
|
240
|
-
var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
|
241
|
-
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
|
242
|
-
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
|
243
|
-
|
244
|
-
// SVG Elements
|
245
|
-
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
|
246
|
-
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
|
247
|
-
// They can potentially allow for arbitrary javascript to be executed. See #11290
|
248
|
-
var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
|
249
|
-
"hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
|
250
|
-
"radialGradient,rect,stop,svg,switch,text,title,tspan");
|
251
|
-
|
252
|
-
// Blocked Elements (will be stripped)
|
253
|
-
var blockedElements = toMap("script,style");
|
254
|
-
|
255
|
-
var validElements = angular.extend({},
|
256
|
-
voidElements,
|
257
|
-
blockElements,
|
258
|
-
inlineElements,
|
259
|
-
optionalEndTagElements);
|
260
|
-
|
261
|
-
//Attributes that have href and hence need to be sanitized
|
262
|
-
var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
|
263
|
-
|
264
|
-
var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
|
265
|
-
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
|
266
|
-
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
|
267
|
-
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
|
268
|
-
'valign,value,vspace,width');
|
269
|
-
|
270
|
-
// SVG attributes (without "id" and "name" attributes)
|
271
|
-
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
|
272
|
-
var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
|
273
|
-
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
|
274
|
-
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
|
275
|
-
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
|
276
|
-
'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
|
277
|
-
'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
|
278
|
-
'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
|
279
|
-
'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
|
280
|
-
'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
|
281
|
-
'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
|
282
|
-
'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
|
283
|
-
'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
|
284
|
-
'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
|
285
|
-
'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
|
286
|
-
'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
|
287
|
-
|
288
|
-
var validAttrs = angular.extend({},
|
289
|
-
uriAttrs,
|
290
|
-
svgAttrs,
|
291
|
-
htmlAttrs);
|
292
|
-
|
293
|
-
function toMap(str, lowercaseKeys) {
|
294
|
-
var obj = {}, items = str.split(','), i;
|
295
|
-
for (i = 0; i < items.length; i++) {
|
296
|
-
obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true;
|
297
|
-
}
|
298
|
-
return obj;
|
299
|
-
}
|
300
210
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
211
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////
|
212
|
+
// Private stuff
|
213
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////
|
214
|
+
|
215
|
+
bind = angular.bind;
|
216
|
+
extend = angular.extend;
|
217
|
+
forEach = angular.forEach;
|
218
|
+
isDefined = angular.isDefined;
|
219
|
+
lowercase = angular.lowercase;
|
220
|
+
noop = angular.noop;
|
221
|
+
|
222
|
+
htmlParser = htmlParserImpl;
|
223
|
+
htmlSanitizeWriter = htmlSanitizeWriterImpl;
|
224
|
+
|
225
|
+
// Regular Expressions for parsing tags and attributes
|
226
|
+
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
227
|
+
// Match everything outside of normal chars and " (quote character)
|
228
|
+
NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
|
229
|
+
|
230
|
+
|
231
|
+
// Good source of info about elements and attributes
|
232
|
+
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
233
|
+
// http://simon.html5.org/html-elements
|
234
|
+
|
235
|
+
// Safe Void Elements - HTML5
|
236
|
+
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
237
|
+
var voidElements = toMap("area,br,col,hr,img,wbr");
|
238
|
+
|
239
|
+
// Elements that you can, intentionally, leave open (and which close themselves)
|
240
|
+
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
241
|
+
var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
|
242
|
+
optionalEndTagInlineElements = toMap("rp,rt"),
|
243
|
+
optionalEndTagElements = extend({},
|
244
|
+
optionalEndTagInlineElements,
|
245
|
+
optionalEndTagBlockElements);
|
246
|
+
|
247
|
+
// Safe Block Elements - HTML5
|
248
|
+
var blockElements = extend({}, optionalEndTagBlockElements, toMap("address,article," +
|
249
|
+
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
|
250
|
+
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
|
251
|
+
|
252
|
+
// Inline Elements - HTML5
|
253
|
+
var inlineElements = extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
|
254
|
+
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
|
255
|
+
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
|
256
|
+
|
257
|
+
// SVG Elements
|
258
|
+
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
|
259
|
+
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
|
260
|
+
// They can potentially allow for arbitrary javascript to be executed. See #11290
|
261
|
+
var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
|
262
|
+
"hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
|
263
|
+
"radialGradient,rect,stop,svg,switch,text,title,tspan");
|
264
|
+
|
265
|
+
// Blocked Elements (will be stripped)
|
266
|
+
var blockedElements = toMap("script,style");
|
267
|
+
|
268
|
+
var validElements = extend({},
|
269
|
+
voidElements,
|
270
|
+
blockElements,
|
271
|
+
inlineElements,
|
272
|
+
optionalEndTagElements);
|
273
|
+
|
274
|
+
//Attributes that have href and hence need to be sanitized
|
275
|
+
var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
|
276
|
+
|
277
|
+
var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
|
278
|
+
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
|
279
|
+
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
|
280
|
+
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
|
281
|
+
'valign,value,vspace,width');
|
282
|
+
|
283
|
+
// SVG attributes (without "id" and "name" attributes)
|
284
|
+
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
|
285
|
+
var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
|
286
|
+
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
|
287
|
+
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
|
288
|
+
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
|
289
|
+
'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
|
290
|
+
'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
|
291
|
+
'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
|
292
|
+
'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
|
293
|
+
'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
|
294
|
+
'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
|
295
|
+
'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
|
296
|
+
'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
|
297
|
+
'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
|
298
|
+
'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
|
299
|
+
'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
|
300
|
+
|
301
|
+
var validAttrs = extend({},
|
302
|
+
uriAttrs,
|
303
|
+
svgAttrs,
|
304
|
+
htmlAttrs);
|
305
|
+
|
306
|
+
function toMap(str, lowercaseKeys) {
|
307
|
+
var obj = {}, items = str.split(','), i;
|
308
|
+
for (i = 0; i < items.length; i++) {
|
309
|
+
obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true;
|
310
|
+
}
|
311
|
+
return obj;
|
320
312
|
}
|
321
|
-
})(window);
|
322
313
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
* @param {object} handler
|
334
|
-
*/
|
335
|
-
function htmlParser(html, handler) {
|
336
|
-
if (html === null || html === undefined) {
|
337
|
-
html = '';
|
338
|
-
} else if (typeof html !== 'string') {
|
339
|
-
html = '' + html;
|
340
|
-
}
|
341
|
-
inertBodyElement.innerHTML = html;
|
314
|
+
var inertBodyElement;
|
315
|
+
(function(window) {
|
316
|
+
var doc;
|
317
|
+
if (window.document && window.document.implementation) {
|
318
|
+
doc = window.document.implementation.createHTMLDocument("inert");
|
319
|
+
} else {
|
320
|
+
throw $sanitizeMinErr('noinert', "Can't create an inert html document");
|
321
|
+
}
|
322
|
+
var docElement = doc.documentElement || doc.getDocumentElement();
|
323
|
+
var bodyElements = docElement.getElementsByTagName('body');
|
342
324
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
325
|
+
// usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
|
326
|
+
if (bodyElements.length === 1) {
|
327
|
+
inertBodyElement = bodyElements[0];
|
328
|
+
} else {
|
329
|
+
var html = doc.createElement('html');
|
330
|
+
inertBodyElement = doc.createElement('body');
|
331
|
+
html.appendChild(inertBodyElement);
|
332
|
+
doc.appendChild(html);
|
348
333
|
}
|
349
|
-
|
334
|
+
})(window);
|
350
335
|
|
351
|
-
|
352
|
-
|
353
|
-
|
336
|
+
/**
|
337
|
+
* @example
|
338
|
+
* htmlParser(htmlString, {
|
339
|
+
* start: function(tag, attrs) {},
|
340
|
+
* end: function(tag) {},
|
341
|
+
* chars: function(text) {},
|
342
|
+
* comment: function(text) {}
|
343
|
+
* });
|
344
|
+
*
|
345
|
+
* @param {string} html string
|
346
|
+
* @param {object} handler
|
347
|
+
*/
|
348
|
+
function htmlParserImpl(html, handler) {
|
349
|
+
if (html === null || html === undefined) {
|
350
|
+
html = '';
|
351
|
+
} else if (typeof html !== 'string') {
|
352
|
+
html = '' + html;
|
354
353
|
}
|
355
|
-
html = inertBodyElement.innerHTML; //trigger mXSS
|
356
354
|
inertBodyElement.innerHTML = html;
|
357
|
-
} while (html !== inertBodyElement.innerHTML);
|
358
|
-
|
359
|
-
var node = inertBodyElement.firstChild;
|
360
|
-
while (node) {
|
361
|
-
switch (node.nodeType) {
|
362
|
-
case 1: // ELEMENT_NODE
|
363
|
-
handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
|
364
|
-
break;
|
365
|
-
case 3: // TEXT NODE
|
366
|
-
handler.chars(node.textContent);
|
367
|
-
break;
|
368
|
-
}
|
369
355
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
356
|
+
//mXSS protection
|
357
|
+
var mXSSAttempts = 5;
|
358
|
+
do {
|
359
|
+
if (mXSSAttempts === 0) {
|
360
|
+
throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
|
374
361
|
}
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
362
|
+
mXSSAttempts--;
|
363
|
+
|
364
|
+
// strip custom-namespaced attributes on IE<=11
|
365
|
+
if (window.document.documentMode) {
|
366
|
+
stripCustomNsAttrs(inertBodyElement);
|
367
|
+
}
|
368
|
+
html = inertBodyElement.innerHTML; //trigger mXSS
|
369
|
+
inertBodyElement.innerHTML = html;
|
370
|
+
} while (html !== inertBodyElement.innerHTML);
|
371
|
+
|
372
|
+
var node = inertBodyElement.firstChild;
|
373
|
+
while (node) {
|
374
|
+
switch (node.nodeType) {
|
375
|
+
case 1: // ELEMENT_NODE
|
376
|
+
handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
|
377
|
+
break;
|
378
|
+
case 3: // TEXT NODE
|
379
|
+
handler.chars(node.textContent);
|
380
|
+
break;
|
381
|
+
}
|
382
|
+
|
383
|
+
var nextNode;
|
384
|
+
if (!(nextNode = node.firstChild)) {
|
385
|
+
if (node.nodeType == 1) {
|
386
|
+
handler.end(node.nodeName.toLowerCase());
|
387
|
+
}
|
388
|
+
nextNode = node.nextSibling;
|
389
|
+
if (!nextNode) {
|
390
|
+
while (nextNode == null) {
|
391
|
+
node = node.parentNode;
|
392
|
+
if (node === inertBodyElement) break;
|
393
|
+
nextNode = node.nextSibling;
|
381
394
|
if (node.nodeType == 1) {
|
382
|
-
|
395
|
+
handler.end(node.nodeName.toLowerCase());
|
396
|
+
}
|
383
397
|
}
|
384
398
|
}
|
385
399
|
}
|
400
|
+
node = nextNode;
|
386
401
|
}
|
387
|
-
node = nextNode;
|
388
|
-
}
|
389
402
|
|
390
|
-
|
391
|
-
|
403
|
+
while (node = inertBodyElement.firstChild) {
|
404
|
+
inertBodyElement.removeChild(node);
|
405
|
+
}
|
392
406
|
}
|
393
|
-
}
|
394
407
|
|
395
|
-
function attrToMap(attrs) {
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
408
|
+
function attrToMap(attrs) {
|
409
|
+
var map = {};
|
410
|
+
for (var i = 0, ii = attrs.length; i < ii; i++) {
|
411
|
+
var attr = attrs[i];
|
412
|
+
map[attr.name] = attr.value;
|
413
|
+
}
|
414
|
+
return map;
|
400
415
|
}
|
401
|
-
return map;
|
402
|
-
}
|
403
416
|
|
404
417
|
|
405
|
-
/**
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
function encodeEntities(value) {
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
}
|
418
|
+
/**
|
419
|
+
* Escapes all potentially dangerous characters, so that the
|
420
|
+
* resulting string can be safely inserted into attribute or
|
421
|
+
* element text.
|
422
|
+
* @param value
|
423
|
+
* @returns {string} escaped text
|
424
|
+
*/
|
425
|
+
function encodeEntities(value) {
|
426
|
+
return value.
|
427
|
+
replace(/&/g, '&').
|
428
|
+
replace(SURROGATE_PAIR_REGEXP, function(value) {
|
429
|
+
var hi = value.charCodeAt(0);
|
430
|
+
var low = value.charCodeAt(1);
|
431
|
+
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
|
432
|
+
}).
|
433
|
+
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
|
434
|
+
return '&#' + value.charCodeAt(0) + ';';
|
435
|
+
}).
|
436
|
+
replace(/</g, '<').
|
437
|
+
replace(/>/g, '>');
|
438
|
+
}
|
426
439
|
|
427
|
-
/**
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
function
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
440
|
+
/**
|
441
|
+
* create an HTML/XML writer which writes to buffer
|
442
|
+
* @param {Array} buf use buf.join('') to get out sanitized html string
|
443
|
+
* @returns {object} in the form of {
|
444
|
+
* start: function(tag, attrs) {},
|
445
|
+
* end: function(tag) {},
|
446
|
+
* chars: function(text) {},
|
447
|
+
* comment: function(text) {}
|
448
|
+
* }
|
449
|
+
*/
|
450
|
+
function htmlSanitizeWriterImpl(buf, uriValidator) {
|
451
|
+
var ignoreCurrentElement = false;
|
452
|
+
var out = bind(buf, buf.push);
|
453
|
+
return {
|
454
|
+
start: function(tag, attrs) {
|
455
|
+
tag = lowercase(tag);
|
456
|
+
if (!ignoreCurrentElement && blockedElements[tag]) {
|
457
|
+
ignoreCurrentElement = tag;
|
458
|
+
}
|
459
|
+
if (!ignoreCurrentElement && validElements[tag] === true) {
|
460
|
+
out('<');
|
461
|
+
out(tag);
|
462
|
+
forEach(attrs, function(value, key) {
|
463
|
+
var lkey = lowercase(key);
|
464
|
+
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
465
|
+
if (validAttrs[lkey] === true &&
|
466
|
+
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
467
|
+
out(' ');
|
468
|
+
out(key);
|
469
|
+
out('="');
|
470
|
+
out(encodeEntities(value));
|
471
|
+
out('"');
|
472
|
+
}
|
473
|
+
});
|
474
|
+
out('>');
|
475
|
+
}
|
476
|
+
},
|
477
|
+
end: function(tag) {
|
478
|
+
tag = lowercase(tag);
|
479
|
+
if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
|
480
|
+
out('</');
|
481
|
+
out(tag);
|
482
|
+
out('>');
|
483
|
+
}
|
484
|
+
if (tag == ignoreCurrentElement) {
|
485
|
+
ignoreCurrentElement = false;
|
486
|
+
}
|
487
|
+
},
|
488
|
+
chars: function(chars) {
|
489
|
+
if (!ignoreCurrentElement) {
|
490
|
+
out(encodeEntities(chars));
|
491
|
+
}
|
478
492
|
}
|
479
|
-
}
|
480
|
-
}
|
481
|
-
}
|
493
|
+
};
|
494
|
+
}
|
482
495
|
|
483
496
|
|
484
|
-
/**
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
function stripCustomNsAttrs(node) {
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
497
|
+
/**
|
498
|
+
* When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
|
499
|
+
* ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
|
500
|
+
* to allow any of these custom attributes. This method strips them all.
|
501
|
+
*
|
502
|
+
* @param node Root element to process
|
503
|
+
*/
|
504
|
+
function stripCustomNsAttrs(node) {
|
505
|
+
if (node.nodeType === window.Node.ELEMENT_NODE) {
|
506
|
+
var attrs = node.attributes;
|
507
|
+
for (var i = 0, l = attrs.length; i < l; i++) {
|
508
|
+
var attrNode = attrs[i];
|
509
|
+
var attrName = attrNode.name.toLowerCase();
|
510
|
+
if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
|
511
|
+
node.removeAttributeNode(attrNode);
|
512
|
+
i--;
|
513
|
+
l--;
|
514
|
+
}
|
501
515
|
}
|
502
516
|
}
|
503
|
-
}
|
504
517
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
518
|
+
var nextNode = node.firstChild;
|
519
|
+
if (nextNode) {
|
520
|
+
stripCustomNsAttrs(nextNode);
|
521
|
+
}
|
509
522
|
|
510
|
-
|
511
|
-
|
512
|
-
|
523
|
+
nextNode = node.nextSibling;
|
524
|
+
if (nextNode) {
|
525
|
+
stripCustomNsAttrs(nextNode);
|
526
|
+
}
|
513
527
|
}
|
514
528
|
}
|
515
529
|
|
530
|
+
function sanitizeText(chars) {
|
531
|
+
var buf = [];
|
532
|
+
var writer = htmlSanitizeWriter(buf, noop);
|
533
|
+
writer.chars(chars);
|
534
|
+
return buf.join('');
|
535
|
+
}
|
516
536
|
|
517
537
|
|
518
538
|
// define ngSanitize module and register $sanitize service
|
519
539
|
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
|
520
540
|
|
521
|
-
/* global sanitizeText: false */
|
522
|
-
|
523
541
|
/**
|
524
542
|
* @ngdoc filter
|
525
543
|
* @name linky
|
@@ -653,12 +671,20 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
|
653
671
|
MAILTO_REGEXP = /^mailto:/i;
|
654
672
|
|
655
673
|
var linkyMinErr = angular.$$minErr('linky');
|
674
|
+
var isDefined = angular.isDefined;
|
675
|
+
var isFunction = angular.isFunction;
|
676
|
+
var isObject = angular.isObject;
|
656
677
|
var isString = angular.isString;
|
657
678
|
|
658
679
|
return function(text, target, attributes) {
|
659
680
|
if (text == null || text === '') return text;
|
660
681
|
if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
|
661
682
|
|
683
|
+
var attributesFn =
|
684
|
+
isFunction(attributes) ? attributes :
|
685
|
+
isObject(attributes) ? function getAttributesObject() {return attributes;} :
|
686
|
+
function getEmptyAttributesObject() {return {};};
|
687
|
+
|
662
688
|
var match;
|
663
689
|
var raw = text;
|
664
690
|
var html = [];
|
@@ -687,19 +713,14 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
|
687
713
|
}
|
688
714
|
|
689
715
|
function addLink(url, text) {
|
690
|
-
var key;
|
716
|
+
var key, linkAttributes = attributesFn(url);
|
691
717
|
html.push('<a ');
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
if (angular.isObject(attributes)) {
|
696
|
-
for (key in attributes) {
|
697
|
-
html.push(key + '="' + attributes[key] + '" ');
|
698
|
-
}
|
699
|
-
} else {
|
700
|
-
attributes = {};
|
718
|
+
|
719
|
+
for (key in linkAttributes) {
|
720
|
+
html.push(key + '="' + linkAttributes[key] + '" ');
|
701
721
|
}
|
702
|
-
|
722
|
+
|
723
|
+
if (isDefined(target) && !('target' in linkAttributes)) {
|
703
724
|
html.push('target="',
|
704
725
|
target,
|
705
726
|
'" ');
|