@angular-wave/angular.ts 0.0.30 → 0.0.33
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.
- package/dist/angular-ts.esm.js +1 -1
- package/dist/angular-ts.umd.js +1 -1
- package/index.html +5 -14
- package/package.json +1 -1
- package/src/core/compile.js +5 -4
- package/src/core/location.js +1 -1
- package/src/core/parser/parse.js +1 -2
- package/src/core/root-scope.js +49 -99
- package/src/directive/events.js +2 -1
- package/src/directive/model.js +4 -2
- package/src/router/directives/state-directives.js +33 -18
- package/src/router/directives/view-directive.js +1 -2
- package/src/router/globals.js +2 -0
- package/src/router/hooks/url.js +4 -4
- package/src/router/index.js +23 -27
- package/src/router/injectables.js +1 -52
- package/src/router/services.js +6 -84
- package/src/router/state/state-builder.js +7 -6
- package/src/router/state/state-queue-manager.js +2 -1
- package/src/router/state/state-registry.js +39 -21
- package/src/router/state/state-service.js +173 -6
- package/src/router/state/views.js +46 -2
- package/src/router/transition/reject-factory.js +0 -8
- package/src/router/transition/transition-service.js +43 -1
- package/src/router/url/url-config.js +32 -1
- package/src/router/url/url-rule.js +4 -4
- package/src/router/url/url-service.js +161 -14
- package/src/router/view/view.js +7 -51
- package/src/services/http.js +1 -1
- package/src/shared/common.js +1 -1
- package/src/shared/strings.js +7 -2
- package/test/core/compile.spec.js +2 -2
- package/test/core/scope.spec.js +2 -37
- package/test/router/services.spec.js +14 -31
- package/test/router/state-directives.spec.js +2 -2
- package/test/router/state-filter.spec.js +0 -2
- package/test/router/state.spec.js +4 -4
- package/test/router/template-factory.spec.js +19 -10
- package/test/router/{url-matcher-factory.spec.js → url-service.spec.js} +126 -132
- package/test/router/view-directive.spec.js +9 -9
- package/test/router/view-hook.spec.js +10 -10
- package/test/router/view.spec.js +4 -11
- package/types/router/core/params/interface.d.ts +2 -2
- package/types/router/core/url/urlMatcherFactory.d.ts +1 -1
- package/legacy/angular-animate.js +0 -4272
- package/legacy/angular-aria.js +0 -426
- package/legacy/angular-message-format.js +0 -1072
- package/legacy/angular-messages.js +0 -829
- package/legacy/angular-route.js +0 -1266
- package/legacy/angular-sanitize.js +0 -891
- package/legacy/angular.js +0 -36600
- package/src/router/router.js +0 -125
- package/src/router/url/url-matcher-factory.js +0 -76
- package/src/router/url/url-router.js +0 -101
- package/test/original-test.html +0 -33
|
@@ -1,891 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license AngularJS v1.8.4-local+sha.4e1bd4b90
|
|
3
|
-
* (c) 2010-2020 Google LLC. http://angularjs.org
|
|
4
|
-
* License: MIT
|
|
5
|
-
*/
|
|
6
|
-
(function(window, angular) {'use strict';
|
|
7
|
-
|
|
8
|
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
9
|
-
* Any commits to this file should be reviewed with security in mind. *
|
|
10
|
-
* Changes to this file can potentially create security vulnerabilities. *
|
|
11
|
-
* An approval from 2 Core members with history of modifying *
|
|
12
|
-
* this file is required. *
|
|
13
|
-
* *
|
|
14
|
-
* Does the change somehow allow for arbitrary javascript to be executed? *
|
|
15
|
-
* Or allows for someone to change the prototype of built-in objects? *
|
|
16
|
-
* Or gives undesired access to variables likes document or window? *
|
|
17
|
-
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
18
|
-
|
|
19
|
-
var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
|
20
|
-
var bind;
|
|
21
|
-
var extend;
|
|
22
|
-
var forEach;
|
|
23
|
-
var isArray;
|
|
24
|
-
var isDefined;
|
|
25
|
-
var lowercase;
|
|
26
|
-
var noop;
|
|
27
|
-
var nodeContains;
|
|
28
|
-
var htmlParser;
|
|
29
|
-
var htmlSanitizeWriter;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @ngdoc module
|
|
33
|
-
* @name ngSanitize
|
|
34
|
-
* @description
|
|
35
|
-
*
|
|
36
|
-
* The `ngSanitize` module provides functionality to sanitize HTML.
|
|
37
|
-
*
|
|
38
|
-
* See {@link ngSanitize.$sanitize `$sanitize`} for usage.
|
|
39
|
-
*/
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @ngdoc service
|
|
43
|
-
* @name $sanitize
|
|
44
|
-
* @kind function
|
|
45
|
-
*
|
|
46
|
-
* @description
|
|
47
|
-
* Sanitizes an html string by stripping all potentially dangerous tokens.
|
|
48
|
-
*
|
|
49
|
-
* The input is sanitized by parsing the HTML into tokens. All safe tokens (from a trusted URI list) are
|
|
50
|
-
* then serialized back to a properly escaped HTML string. This means that no unsafe input can make
|
|
51
|
-
* it into the returned string.
|
|
52
|
-
*
|
|
53
|
-
* The trusted URIs for URL sanitization of attribute values is configured using the functions
|
|
54
|
-
* `aHrefSanitizationTrustedUrlList` and `imgSrcSanitizationTrustedUrlList` of {@link $compileProvider}.
|
|
55
|
-
*
|
|
56
|
-
* The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}.
|
|
57
|
-
*
|
|
58
|
-
* @param {string} html HTML input.
|
|
59
|
-
* @returns {string} Sanitized HTML.
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
<example module="sanitizeExample" deps="angular-sanitize.js" name="sanitize-service">
|
|
63
|
-
<file name="index.html">
|
|
64
|
-
<script>
|
|
65
|
-
angular.module('sanitizeExample', ['ngSanitize'])
|
|
66
|
-
.controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
|
|
67
|
-
$scope.snippet =
|
|
68
|
-
'<p style="color:blue">an html\n' +
|
|
69
|
-
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
|
|
70
|
-
'snippet</p>';
|
|
71
|
-
$scope.deliberatelyTrustDangerousSnippet = function() {
|
|
72
|
-
return $sce.trustAsHtml($scope.snippet);
|
|
73
|
-
};
|
|
74
|
-
}]);
|
|
75
|
-
</script>
|
|
76
|
-
<div ng-controller="ExampleController">
|
|
77
|
-
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
|
78
|
-
<table>
|
|
79
|
-
<tr>
|
|
80
|
-
<td>Directive</td>
|
|
81
|
-
<td>How</td>
|
|
82
|
-
<td>Source</td>
|
|
83
|
-
<td>Rendered</td>
|
|
84
|
-
</tr>
|
|
85
|
-
<tr id="bind-html-with-sanitize">
|
|
86
|
-
<td>ng-bind-html</td>
|
|
87
|
-
<td>Automatically uses $sanitize</td>
|
|
88
|
-
<td><pre><div ng-bind-html="snippet"><br/></div></pre></td>
|
|
89
|
-
<td><div ng-bind-html="snippet"></div></td>
|
|
90
|
-
</tr>
|
|
91
|
-
<tr id="bind-html-with-trust">
|
|
92
|
-
<td>ng-bind-html</td>
|
|
93
|
-
<td>Bypass $sanitize by explicitly trusting the dangerous value</td>
|
|
94
|
-
<td>
|
|
95
|
-
<pre><div ng-bind-html="deliberatelyTrustDangerousSnippet()">
|
|
96
|
-
</div></pre>
|
|
97
|
-
</td>
|
|
98
|
-
<td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
|
|
99
|
-
</tr>
|
|
100
|
-
<tr id="bind-default">
|
|
101
|
-
<td>ng-bind</td>
|
|
102
|
-
<td>Automatically escapes</td>
|
|
103
|
-
<td><pre><div ng-bind="snippet"><br/></div></pre></td>
|
|
104
|
-
<td><div ng-bind="snippet"></div></td>
|
|
105
|
-
</tr>
|
|
106
|
-
</table>
|
|
107
|
-
</div>
|
|
108
|
-
</file>
|
|
109
|
-
<file name="protractor.js" type="protractor">
|
|
110
|
-
it('should sanitize the html snippet by default', function() {
|
|
111
|
-
expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')).
|
|
112
|
-
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should inline raw snippet if bound to a trusted value', function() {
|
|
116
|
-
expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).
|
|
117
|
-
toBe("<p style=\"color:blue\">an html\n" +
|
|
118
|
-
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
|
119
|
-
"snippet</p>");
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should escape snippet without any filter', function() {
|
|
123
|
-
expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).
|
|
124
|
-
toBe("<p style=\"color:blue\">an html\n" +
|
|
125
|
-
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
|
126
|
-
"snippet</p>");
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should update', function() {
|
|
130
|
-
element(by.model('snippet')).clear();
|
|
131
|
-
element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
|
|
132
|
-
expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')).
|
|
133
|
-
toBe('new <b>text</b>');
|
|
134
|
-
expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).toBe(
|
|
135
|
-
'new <b onclick="alert(1)">text</b>');
|
|
136
|
-
expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).toBe(
|
|
137
|
-
"new <b onclick=\"alert(1)\">text</b>");
|
|
138
|
-
});
|
|
139
|
-
</file>
|
|
140
|
-
</example>
|
|
141
|
-
*/
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* @ngdoc provider
|
|
146
|
-
* @name $sanitizeProvider
|
|
147
|
-
* @this
|
|
148
|
-
*
|
|
149
|
-
* @description
|
|
150
|
-
* Creates and configures {@link $sanitize} instance.
|
|
151
|
-
*/
|
|
152
|
-
function $SanitizeProvider() {
|
|
153
|
-
var hasBeenInstantiated = false;
|
|
154
|
-
var svgEnabled = false;
|
|
155
|
-
|
|
156
|
-
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
|
|
157
|
-
hasBeenInstantiated = true;
|
|
158
|
-
if (svgEnabled) {
|
|
159
|
-
extend(validElements, svgElements);
|
|
160
|
-
}
|
|
161
|
-
return function(html) {
|
|
162
|
-
var buf = [];
|
|
163
|
-
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
|
|
164
|
-
return !/^unsafe:/.test($$sanitizeUri(uri, isImage));
|
|
165
|
-
}));
|
|
166
|
-
return buf.join('');
|
|
167
|
-
};
|
|
168
|
-
}];
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* @ngdoc method
|
|
173
|
-
* @name $sanitizeProvider#enableSvg
|
|
174
|
-
* @kind function
|
|
175
|
-
*
|
|
176
|
-
* @description
|
|
177
|
-
* Enables a subset of svg to be supported by the sanitizer.
|
|
178
|
-
*
|
|
179
|
-
* <div class="alert alert-warning">
|
|
180
|
-
* <p>By enabling this setting without taking other precautions, you might expose your
|
|
181
|
-
* application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned
|
|
182
|
-
* outside of the containing element and be rendered over other elements on the page (e.g. a login
|
|
183
|
-
* link). Such behavior can then result in phishing incidents.</p>
|
|
184
|
-
*
|
|
185
|
-
* <p>To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg
|
|
186
|
-
* tags within the sanitized content:</p>
|
|
187
|
-
*
|
|
188
|
-
* <br>
|
|
189
|
-
*
|
|
190
|
-
* <pre><code>
|
|
191
|
-
* .rootOfTheIncludedContent svg {
|
|
192
|
-
* overflow: hidden !important;
|
|
193
|
-
* }
|
|
194
|
-
* </code></pre>
|
|
195
|
-
* </div>
|
|
196
|
-
*
|
|
197
|
-
* @param {boolean=} flag Enable or disable SVG support in the sanitizer.
|
|
198
|
-
* @returns {boolean|$sanitizeProvider} Returns the currently configured value if called
|
|
199
|
-
* without an argument or self for chaining otherwise.
|
|
200
|
-
*/
|
|
201
|
-
this.enableSvg = function(enableSvg) {
|
|
202
|
-
if (isDefined(enableSvg)) {
|
|
203
|
-
svgEnabled = enableSvg;
|
|
204
|
-
return this;
|
|
205
|
-
} else {
|
|
206
|
-
return svgEnabled;
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* @ngdoc method
|
|
213
|
-
* @name $sanitizeProvider#addValidElements
|
|
214
|
-
* @kind function
|
|
215
|
-
*
|
|
216
|
-
* @description
|
|
217
|
-
* Extends the built-in lists of valid HTML/SVG elements, i.e. elements that are considered safe
|
|
218
|
-
* and are not stripped off during sanitization. You can extend the following lists of elements:
|
|
219
|
-
*
|
|
220
|
-
* - `htmlElements`: A list of elements (tag names) to extend the current list of safe HTML
|
|
221
|
-
* elements. HTML elements considered safe will not be removed during sanitization. All other
|
|
222
|
-
* elements will be stripped off.
|
|
223
|
-
*
|
|
224
|
-
* - `htmlVoidElements`: This is similar to `htmlElements`, but marks the elements as
|
|
225
|
-
* "void elements" (similar to HTML
|
|
226
|
-
* [void elements](https://rawgit.com/w3c/html/html5.1-2/single-page.html#void-elements)). These
|
|
227
|
-
* elements have no end tag and cannot have content.
|
|
228
|
-
*
|
|
229
|
-
* - `svgElements`: This is similar to `htmlElements`, but for SVG elements. This list is only
|
|
230
|
-
* taken into account if SVG is {@link ngSanitize.$sanitizeProvider#enableSvg enabled} for
|
|
231
|
-
* `$sanitize`.
|
|
232
|
-
*
|
|
233
|
-
* <div class="alert alert-info">
|
|
234
|
-
* This method must be called during the {@link angular.Module#config config} phase. Once the
|
|
235
|
-
* `$sanitize` service has been instantiated, this method has no effect.
|
|
236
|
-
* </div>
|
|
237
|
-
*
|
|
238
|
-
* <div class="alert alert-warning">
|
|
239
|
-
* Keep in mind that extending the built-in lists of elements may expose your app to XSS or
|
|
240
|
-
* other vulnerabilities. Be very mindful of the elements you add.
|
|
241
|
-
* </div>
|
|
242
|
-
*
|
|
243
|
-
* @param {Array<String>|Object} elements - A list of valid HTML elements or an object with one or
|
|
244
|
-
* more of the following properties:
|
|
245
|
-
* - **htmlElements** - `{Array<String>}` - A list of elements to extend the current list of
|
|
246
|
-
* HTML elements.
|
|
247
|
-
* - **htmlVoidElements** - `{Array<String>}` - A list of elements to extend the current list of
|
|
248
|
-
* void HTML elements; i.e. elements that do not have an end tag.
|
|
249
|
-
* - **svgElements** - `{Array<String>}` - A list of elements to extend the current list of SVG
|
|
250
|
-
* elements. The list of SVG elements is only taken into account if SVG is
|
|
251
|
-
* {@link ngSanitize.$sanitizeProvider#enableSvg enabled} for `$sanitize`.
|
|
252
|
-
*
|
|
253
|
-
* Passing an array (`[...]`) is equivalent to passing `{htmlElements: [...]}`.
|
|
254
|
-
*
|
|
255
|
-
* @return {$sanitizeProvider} Returns self for chaining.
|
|
256
|
-
*/
|
|
257
|
-
this.addValidElements = function(elements) {
|
|
258
|
-
if (!hasBeenInstantiated) {
|
|
259
|
-
if (isArray(elements)) {
|
|
260
|
-
elements = {htmlElements: elements};
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
addElementsTo(svgElements, elements.svgElements);
|
|
264
|
-
addElementsTo(voidElements, elements.htmlVoidElements);
|
|
265
|
-
addElementsTo(validElements, elements.htmlVoidElements);
|
|
266
|
-
addElementsTo(validElements, elements.htmlElements);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return this;
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* @ngdoc method
|
|
275
|
-
* @name $sanitizeProvider#addValidAttrs
|
|
276
|
-
* @kind function
|
|
277
|
-
*
|
|
278
|
-
* @description
|
|
279
|
-
* Extends the built-in list of valid attributes, i.e. attributes that are considered safe and are
|
|
280
|
-
* not stripped off during sanitization.
|
|
281
|
-
*
|
|
282
|
-
* **Note**:
|
|
283
|
-
* The new attributes will not be treated as URI attributes, which means their values will not be
|
|
284
|
-
* sanitized as URIs using `$compileProvider`'s
|
|
285
|
-
* {@link ng.$compileProvider#aHrefSanitizationTrustedUrlList aHrefSanitizationTrustedUrlList} and
|
|
286
|
-
* {@link ng.$compileProvider#imgSrcSanitizationTrustedUrlList imgSrcSanitizationTrustedUrlList}.
|
|
287
|
-
*
|
|
288
|
-
* <div class="alert alert-info">
|
|
289
|
-
* This method must be called during the {@link angular.Module#config config} phase. Once the
|
|
290
|
-
* `$sanitize` service has been instantiated, this method has no effect.
|
|
291
|
-
* </div>
|
|
292
|
-
*
|
|
293
|
-
* <div class="alert alert-warning">
|
|
294
|
-
* Keep in mind that extending the built-in list of attributes may expose your app to XSS or
|
|
295
|
-
* other vulnerabilities. Be very mindful of the attributes you add.
|
|
296
|
-
* </div>
|
|
297
|
-
*
|
|
298
|
-
* @param {Array<String>} attrs - A list of valid attributes.
|
|
299
|
-
*
|
|
300
|
-
* @returns {$sanitizeProvider} Returns self for chaining.
|
|
301
|
-
*/
|
|
302
|
-
this.addValidAttrs = function(attrs) {
|
|
303
|
-
if (!hasBeenInstantiated) {
|
|
304
|
-
extend(validAttrs, arrayToMap(attrs, true));
|
|
305
|
-
}
|
|
306
|
-
return this;
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
310
|
-
// Private stuff
|
|
311
|
-
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
312
|
-
|
|
313
|
-
bind = angular.bind;
|
|
314
|
-
extend = angular.extend;
|
|
315
|
-
forEach = angular.forEach;
|
|
316
|
-
isArray = angular.isArray;
|
|
317
|
-
isDefined = angular.isDefined;
|
|
318
|
-
lowercase = angular.$$lowercase;
|
|
319
|
-
noop = angular.noop;
|
|
320
|
-
|
|
321
|
-
htmlParser = htmlParserImpl;
|
|
322
|
-
htmlSanitizeWriter = htmlSanitizeWriterImpl;
|
|
323
|
-
|
|
324
|
-
nodeContains = window.Node.prototype.contains || /** @this */ function(arg) {
|
|
325
|
-
// eslint-disable-next-line no-bitwise
|
|
326
|
-
return !!(this.compareDocumentPosition(arg) & 16);
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
// Regular Expressions for parsing tags and attributes
|
|
330
|
-
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
|
331
|
-
// Match everything outside of normal chars and " (quote character)
|
|
332
|
-
NON_ALPHANUMERIC_REGEXP = /([^#-~ |!])/g;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
// Good source of info about elements and attributes
|
|
336
|
-
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
|
337
|
-
// http://simon.html5.org/html-elements
|
|
338
|
-
|
|
339
|
-
// Safe Void Elements - HTML5
|
|
340
|
-
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
|
341
|
-
var voidElements = stringToMap('area,br,col,hr,img,wbr');
|
|
342
|
-
|
|
343
|
-
// Elements that you can, intentionally, leave open (and which close themselves)
|
|
344
|
-
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
|
345
|
-
var optionalEndTagBlockElements = stringToMap('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'),
|
|
346
|
-
optionalEndTagInlineElements = stringToMap('rp,rt'),
|
|
347
|
-
optionalEndTagElements = extend({},
|
|
348
|
-
optionalEndTagInlineElements,
|
|
349
|
-
optionalEndTagBlockElements);
|
|
350
|
-
|
|
351
|
-
// Safe Block Elements - HTML5
|
|
352
|
-
var blockElements = extend({}, optionalEndTagBlockElements, stringToMap('address,article,' +
|
|
353
|
-
'aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' +
|
|
354
|
-
'h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul'));
|
|
355
|
-
|
|
356
|
-
// Inline Elements - HTML5
|
|
357
|
-
var inlineElements = extend({}, optionalEndTagInlineElements, stringToMap('a,abbr,acronym,b,' +
|
|
358
|
-
'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,' +
|
|
359
|
-
'samp,small,span,strike,strong,sub,sup,time,tt,u,var'));
|
|
360
|
-
|
|
361
|
-
// SVG Elements
|
|
362
|
-
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
|
|
363
|
-
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
|
|
364
|
-
// They can potentially allow for arbitrary javascript to be executed. See #11290
|
|
365
|
-
var svgElements = stringToMap('circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,' +
|
|
366
|
-
'hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,' +
|
|
367
|
-
'radialGradient,rect,stop,svg,switch,text,title,tspan');
|
|
368
|
-
|
|
369
|
-
// Blocked Elements (will be stripped)
|
|
370
|
-
var blockedElements = stringToMap('script,style');
|
|
371
|
-
|
|
372
|
-
var validElements = extend({},
|
|
373
|
-
voidElements,
|
|
374
|
-
blockElements,
|
|
375
|
-
inlineElements,
|
|
376
|
-
optionalEndTagElements);
|
|
377
|
-
|
|
378
|
-
//Attributes that have href and hence need to be sanitized
|
|
379
|
-
var uriAttrs = stringToMap('background,cite,href,longdesc,src,xlink:href,xml:base');
|
|
380
|
-
|
|
381
|
-
var htmlAttrs = stringToMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
|
|
382
|
-
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
|
|
383
|
-
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
|
|
384
|
-
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
|
|
385
|
-
'valign,value,vspace,width');
|
|
386
|
-
|
|
387
|
-
// SVG attributes (without "id" and "name" attributes)
|
|
388
|
-
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
|
|
389
|
-
var svgAttrs = stringToMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
|
|
390
|
-
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
|
|
391
|
-
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
|
|
392
|
-
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
|
|
393
|
-
'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
|
|
394
|
-
'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
|
|
395
|
-
'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
|
|
396
|
-
'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
|
|
397
|
-
'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
|
|
398
|
-
'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
|
|
399
|
-
'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
|
|
400
|
-
'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
|
|
401
|
-
'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
|
|
402
|
-
'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
|
|
403
|
-
'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
|
|
404
|
-
|
|
405
|
-
var validAttrs = extend({},
|
|
406
|
-
uriAttrs,
|
|
407
|
-
svgAttrs,
|
|
408
|
-
htmlAttrs);
|
|
409
|
-
|
|
410
|
-
function stringToMap(str, lowercaseKeys) {
|
|
411
|
-
return arrayToMap(str.split(','), lowercaseKeys);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function arrayToMap(items, lowercaseKeys) {
|
|
415
|
-
var obj = {}, i;
|
|
416
|
-
for (i = 0; i < items.length; i++) {
|
|
417
|
-
obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true;
|
|
418
|
-
}
|
|
419
|
-
return obj;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
function addElementsTo(elementsMap, newElements) {
|
|
423
|
-
if (newElements && newElements.length) {
|
|
424
|
-
extend(elementsMap, arrayToMap(newElements));
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Create an inert document that contains the dirty HTML that needs sanitizing.
|
|
430
|
-
* We use the DOMParser API by default and fall back to createHTMLDocument if DOMParser is not
|
|
431
|
-
* available.
|
|
432
|
-
*/
|
|
433
|
-
var getInertBodyElement /* function(html: string): HTMLBodyElement */ = (function(window, document) {
|
|
434
|
-
if (isDOMParserAvailable()) {
|
|
435
|
-
return getInertBodyElement_DOMParser;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
if (!document || !document.implementation) {
|
|
439
|
-
throw $sanitizeMinErr('noinert', 'Can\'t create an inert html document');
|
|
440
|
-
}
|
|
441
|
-
var inertDocument = document.implementation.createHTMLDocument('inert');
|
|
442
|
-
var inertBodyElement = (inertDocument.documentElement || inertDocument.getDocumentElement()).querySelector('body');
|
|
443
|
-
return getInertBodyElement_InertDocument;
|
|
444
|
-
|
|
445
|
-
function isDOMParserAvailable() {
|
|
446
|
-
try {
|
|
447
|
-
return !!getInertBodyElement_DOMParser('');
|
|
448
|
-
} catch (e) {
|
|
449
|
-
return false;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
function getInertBodyElement_DOMParser(html) {
|
|
454
|
-
// We add this dummy element to ensure that the rest of the content is parsed as expected
|
|
455
|
-
// e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the `<head>` tag.
|
|
456
|
-
html = '<remove></remove>' + html;
|
|
457
|
-
try {
|
|
458
|
-
var body = new window.DOMParser().parseFromString(html, 'text/html').body;
|
|
459
|
-
body.firstChild.remove();
|
|
460
|
-
return body;
|
|
461
|
-
} catch (e) {
|
|
462
|
-
return undefined;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function getInertBodyElement_InertDocument(html) {
|
|
467
|
-
inertBodyElement.innerHTML = html;
|
|
468
|
-
|
|
469
|
-
// Support: IE 9-11 only
|
|
470
|
-
// strip custom-namespaced attributes on IE<=11
|
|
471
|
-
if (document.documentMode) {
|
|
472
|
-
stripCustomNsAttrs(inertBodyElement);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return inertBodyElement;
|
|
476
|
-
}
|
|
477
|
-
})(window, window.document);
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* @example
|
|
481
|
-
* htmlParser(htmlString, {
|
|
482
|
-
* start: function(tag, attrs) {},
|
|
483
|
-
* end: function(tag) {},
|
|
484
|
-
* chars: function(text) {},
|
|
485
|
-
* comment: function(text) {}
|
|
486
|
-
* });
|
|
487
|
-
*
|
|
488
|
-
* @param {string} html string
|
|
489
|
-
* @param {object} handler
|
|
490
|
-
*/
|
|
491
|
-
function htmlParserImpl(html, handler) {
|
|
492
|
-
if (html === null || html === undefined) {
|
|
493
|
-
html = '';
|
|
494
|
-
} else if (typeof html !== 'string') {
|
|
495
|
-
html = '' + html;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
var inertBodyElement = getInertBodyElement(html);
|
|
499
|
-
if (!inertBodyElement) return '';
|
|
500
|
-
|
|
501
|
-
//mXSS protection
|
|
502
|
-
var mXSSAttempts = 5;
|
|
503
|
-
do {
|
|
504
|
-
if (mXSSAttempts === 0) {
|
|
505
|
-
throw $sanitizeMinErr('uinput', 'Failed to sanitize html because the input is unstable');
|
|
506
|
-
}
|
|
507
|
-
mXSSAttempts--;
|
|
508
|
-
|
|
509
|
-
// trigger mXSS if it is going to happen by reading and writing the innerHTML
|
|
510
|
-
html = inertBodyElement.innerHTML;
|
|
511
|
-
inertBodyElement = getInertBodyElement(html);
|
|
512
|
-
} while (html !== inertBodyElement.innerHTML);
|
|
513
|
-
|
|
514
|
-
var node = inertBodyElement.firstChild;
|
|
515
|
-
while (node) {
|
|
516
|
-
switch (node.nodeType) {
|
|
517
|
-
case 1: // ELEMENT_NODE
|
|
518
|
-
handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
|
|
519
|
-
break;
|
|
520
|
-
case 3: // TEXT NODE
|
|
521
|
-
handler.chars(node.textContent);
|
|
522
|
-
break;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
var nextNode;
|
|
526
|
-
if (!(nextNode = node.firstChild)) {
|
|
527
|
-
if (node.nodeType === 1) {
|
|
528
|
-
handler.end(node.nodeName.toLowerCase());
|
|
529
|
-
}
|
|
530
|
-
nextNode = getNonDescendant('nextSibling', node);
|
|
531
|
-
if (!nextNode) {
|
|
532
|
-
while (nextNode == null) {
|
|
533
|
-
node = getNonDescendant('parentNode', node);
|
|
534
|
-
if (node === inertBodyElement) break;
|
|
535
|
-
nextNode = getNonDescendant('nextSibling', node);
|
|
536
|
-
if (node.nodeType === 1) {
|
|
537
|
-
handler.end(node.nodeName.toLowerCase());
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
node = nextNode;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
while ((node = inertBodyElement.firstChild)) {
|
|
546
|
-
inertBodyElement.removeChild(node);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
function attrToMap(attrs) {
|
|
551
|
-
var map = {};
|
|
552
|
-
for (var i = 0, ii = attrs.length; i < ii; i++) {
|
|
553
|
-
var attr = attrs[i];
|
|
554
|
-
map[attr.name] = attr.value;
|
|
555
|
-
}
|
|
556
|
-
return map;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Escapes all potentially dangerous characters, so that the
|
|
562
|
-
* resulting string can be safely inserted into attribute or
|
|
563
|
-
* element text.
|
|
564
|
-
* @param value
|
|
565
|
-
* @returns {string} escaped text
|
|
566
|
-
*/
|
|
567
|
-
function encodeEntities(value) {
|
|
568
|
-
return value.
|
|
569
|
-
replace(/&/g, '&').
|
|
570
|
-
replace(SURROGATE_PAIR_REGEXP, function(value) {
|
|
571
|
-
var hi = value.charCodeAt(0);
|
|
572
|
-
var low = value.charCodeAt(1);
|
|
573
|
-
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
|
|
574
|
-
}).
|
|
575
|
-
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
|
|
576
|
-
return '&#' + value.charCodeAt(0) + ';';
|
|
577
|
-
}).
|
|
578
|
-
replace(/</g, '<').
|
|
579
|
-
replace(/>/g, '>');
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
/**
|
|
583
|
-
* create an HTML/XML writer which writes to buffer
|
|
584
|
-
* @param {Array} buf use buf.join('') to get out sanitized html string
|
|
585
|
-
* @returns {object} in the form of {
|
|
586
|
-
* start: function(tag, attrs) {},
|
|
587
|
-
* end: function(tag) {},
|
|
588
|
-
* chars: function(text) {},
|
|
589
|
-
* comment: function(text) {}
|
|
590
|
-
* }
|
|
591
|
-
*/
|
|
592
|
-
function htmlSanitizeWriterImpl(buf, uriValidator) {
|
|
593
|
-
var ignoreCurrentElement = false;
|
|
594
|
-
var out = bind(buf, buf.push);
|
|
595
|
-
return {
|
|
596
|
-
start: function(tag, attrs) {
|
|
597
|
-
tag = lowercase(tag);
|
|
598
|
-
if (!ignoreCurrentElement && blockedElements[tag]) {
|
|
599
|
-
ignoreCurrentElement = tag;
|
|
600
|
-
}
|
|
601
|
-
if (!ignoreCurrentElement && validElements[tag] === true) {
|
|
602
|
-
out('<');
|
|
603
|
-
out(tag);
|
|
604
|
-
forEach(attrs, function(value, key) {
|
|
605
|
-
var lkey = lowercase(key);
|
|
606
|
-
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
|
607
|
-
if (validAttrs[lkey] === true &&
|
|
608
|
-
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
|
609
|
-
out(' ');
|
|
610
|
-
out(key);
|
|
611
|
-
out('="');
|
|
612
|
-
out(encodeEntities(value));
|
|
613
|
-
out('"');
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
out('>');
|
|
617
|
-
}
|
|
618
|
-
},
|
|
619
|
-
end: function(tag) {
|
|
620
|
-
tag = lowercase(tag);
|
|
621
|
-
if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
|
|
622
|
-
out('</');
|
|
623
|
-
out(tag);
|
|
624
|
-
out('>');
|
|
625
|
-
}
|
|
626
|
-
// eslint-disable-next-line eqeqeq
|
|
627
|
-
if (tag == ignoreCurrentElement) {
|
|
628
|
-
ignoreCurrentElement = false;
|
|
629
|
-
}
|
|
630
|
-
},
|
|
631
|
-
chars: function(chars) {
|
|
632
|
-
if (!ignoreCurrentElement) {
|
|
633
|
-
out(encodeEntities(chars));
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
* When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
|
|
642
|
-
* ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
|
|
643
|
-
* to allow any of these custom attributes. This method strips them all.
|
|
644
|
-
*
|
|
645
|
-
* @param node Root element to process
|
|
646
|
-
*/
|
|
647
|
-
function stripCustomNsAttrs(node) {
|
|
648
|
-
while (node) {
|
|
649
|
-
if (node.nodeType === window.Node.ELEMENT_NODE) {
|
|
650
|
-
var attrs = node.attributes;
|
|
651
|
-
for (var i = 0, l = attrs.length; i < l; i++) {
|
|
652
|
-
var attrNode = attrs[i];
|
|
653
|
-
var attrName = attrNode.name.toLowerCase();
|
|
654
|
-
if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
|
|
655
|
-
node.removeAttributeNode(attrNode);
|
|
656
|
-
i--;
|
|
657
|
-
l--;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
var nextNode = node.firstChild;
|
|
663
|
-
if (nextNode) {
|
|
664
|
-
stripCustomNsAttrs(nextNode);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
node = getNonDescendant('nextSibling', node);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
function getNonDescendant(propName, node) {
|
|
672
|
-
// An element is clobbered if its `propName` property points to one of its descendants
|
|
673
|
-
var nextNode = node[propName];
|
|
674
|
-
if (nextNode && nodeContains.call(node, nextNode)) {
|
|
675
|
-
throw $sanitizeMinErr('elclob', 'Failed to sanitize html because the element is clobbered: {0}', node.outerHTML || node.outerText);
|
|
676
|
-
}
|
|
677
|
-
return nextNode;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
function sanitizeText(chars) {
|
|
682
|
-
var buf = [];
|
|
683
|
-
var writer = htmlSanitizeWriter(buf, noop);
|
|
684
|
-
writer.chars(chars);
|
|
685
|
-
return buf.join('');
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
// define ngSanitize module and register $sanitize service
|
|
690
|
-
angular.module('ngSanitize', [])
|
|
691
|
-
.provider('$sanitize', $SanitizeProvider)
|
|
692
|
-
.info({ angularVersion: '1.8.4-local+sha.4e1bd4b90' });
|
|
693
|
-
|
|
694
|
-
/**
|
|
695
|
-
* @ngdoc filter
|
|
696
|
-
* @name linky
|
|
697
|
-
* @kind function
|
|
698
|
-
*
|
|
699
|
-
* @description
|
|
700
|
-
* Finds links in text input and turns them into html links. Supports `http/https/ftp/sftp/mailto` and
|
|
701
|
-
* plain email address links.
|
|
702
|
-
*
|
|
703
|
-
* Requires the {@link ngSanitize `ngSanitize`} module to be installed.
|
|
704
|
-
*
|
|
705
|
-
* @param {string} text Input text.
|
|
706
|
-
* @param {string} [target] Window (`_blank|_self|_parent|_top`) or named frame to open links in.
|
|
707
|
-
* @param {object|function(url)} [attributes] Add custom attributes to the link element.
|
|
708
|
-
*
|
|
709
|
-
* Can be one of:
|
|
710
|
-
*
|
|
711
|
-
* - `object`: A map of attributes
|
|
712
|
-
* - `function`: Takes the url as a parameter and returns a map of attributes
|
|
713
|
-
*
|
|
714
|
-
* If the map of attributes contains a value for `target`, it overrides the value of
|
|
715
|
-
* the target parameter.
|
|
716
|
-
*
|
|
717
|
-
*
|
|
718
|
-
* @returns {string} Html-linkified and {@link $sanitize sanitized} text.
|
|
719
|
-
*
|
|
720
|
-
* @usage
|
|
721
|
-
<span ng-bind-html="linky_expression | linky"></span>
|
|
722
|
-
*
|
|
723
|
-
* @example
|
|
724
|
-
<example module="linkyExample" deps="angular-sanitize.js" name="linky-filter">
|
|
725
|
-
<file name="index.html">
|
|
726
|
-
<div ng-controller="ExampleController">
|
|
727
|
-
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
|
728
|
-
<table>
|
|
729
|
-
<tr>
|
|
730
|
-
<th>Filter</th>
|
|
731
|
-
<th>Source</th>
|
|
732
|
-
<th>Rendered</th>
|
|
733
|
-
</tr>
|
|
734
|
-
<tr id="linky-filter">
|
|
735
|
-
<td>linky filter</td>
|
|
736
|
-
<td>
|
|
737
|
-
<pre><div ng-bind-html="snippet | linky"><br></div></pre>
|
|
738
|
-
</td>
|
|
739
|
-
<td>
|
|
740
|
-
<div ng-bind-html="snippet | linky"></div>
|
|
741
|
-
</td>
|
|
742
|
-
</tr>
|
|
743
|
-
<tr id="linky-target">
|
|
744
|
-
<td>linky target</td>
|
|
745
|
-
<td>
|
|
746
|
-
<pre><div ng-bind-html="snippetWithSingleURL | linky:'_blank'"><br></div></pre>
|
|
747
|
-
</td>
|
|
748
|
-
<td>
|
|
749
|
-
<div ng-bind-html="snippetWithSingleURL | linky:'_blank'"></div>
|
|
750
|
-
</td>
|
|
751
|
-
</tr>
|
|
752
|
-
<tr id="linky-custom-attributes">
|
|
753
|
-
<td>linky custom attributes</td>
|
|
754
|
-
<td>
|
|
755
|
-
<pre><div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"><br></div></pre>
|
|
756
|
-
</td>
|
|
757
|
-
<td>
|
|
758
|
-
<div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"></div>
|
|
759
|
-
</td>
|
|
760
|
-
</tr>
|
|
761
|
-
<tr id="escaped-html">
|
|
762
|
-
<td>no filter</td>
|
|
763
|
-
<td><pre><div ng-bind="snippet"><br></div></pre></td>
|
|
764
|
-
<td><div ng-bind="snippet"></div></td>
|
|
765
|
-
</tr>
|
|
766
|
-
</table>
|
|
767
|
-
</file>
|
|
768
|
-
<file name="script.js">
|
|
769
|
-
angular.module('linkyExample', ['ngSanitize'])
|
|
770
|
-
.controller('ExampleController', ['$scope', function($scope) {
|
|
771
|
-
$scope.snippet =
|
|
772
|
-
'Pretty text with some links:\n' +
|
|
773
|
-
'http://angularjs.org/,\n' +
|
|
774
|
-
'mailto:us@somewhere.org,\n' +
|
|
775
|
-
'another@somewhere.org,\n' +
|
|
776
|
-
'and one more: ftp://127.0.0.1/.';
|
|
777
|
-
$scope.snippetWithSingleURL = 'http://angularjs.org/';
|
|
778
|
-
}]);
|
|
779
|
-
</file>
|
|
780
|
-
<file name="protractor.js" type="protractor">
|
|
781
|
-
it('should linkify the snippet with urls', function() {
|
|
782
|
-
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
|
|
783
|
-
toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
|
|
784
|
-
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
|
|
785
|
-
expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
|
|
786
|
-
});
|
|
787
|
-
|
|
788
|
-
it('should not linkify snippet without the linky filter', function() {
|
|
789
|
-
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
|
|
790
|
-
toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
|
|
791
|
-
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
|
|
792
|
-
expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
|
|
793
|
-
});
|
|
794
|
-
|
|
795
|
-
it('should update', function() {
|
|
796
|
-
element(by.model('snippet')).clear();
|
|
797
|
-
element(by.model('snippet')).sendKeys('new http://link.');
|
|
798
|
-
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
|
|
799
|
-
toBe('new http://link.');
|
|
800
|
-
expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
|
|
801
|
-
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
|
|
802
|
-
.toBe('new http://link.');
|
|
803
|
-
});
|
|
804
|
-
|
|
805
|
-
it('should work with the target property', function() {
|
|
806
|
-
expect(element(by.id('linky-target')).
|
|
807
|
-
element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()).
|
|
808
|
-
toBe('http://angularjs.org/');
|
|
809
|
-
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
it('should optionally add custom attributes', function() {
|
|
813
|
-
expect(element(by.id('linky-custom-attributes')).
|
|
814
|
-
element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()).
|
|
815
|
-
toBe('http://angularjs.org/');
|
|
816
|
-
expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow');
|
|
817
|
-
});
|
|
818
|
-
</file>
|
|
819
|
-
</example>
|
|
820
|
-
*/
|
|
821
|
-
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
|
822
|
-
var LINKY_URL_REGEXP =
|
|
823
|
-
/((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
|
|
824
|
-
MAILTO_REGEXP = /^mailto:/i;
|
|
825
|
-
|
|
826
|
-
var linkyMinErr = angular.$$minErr('linky');
|
|
827
|
-
var isDefined = angular.isDefined;
|
|
828
|
-
var isFunction = angular.isFunction;
|
|
829
|
-
var isObject = angular.isObject;
|
|
830
|
-
var isString = angular.isString;
|
|
831
|
-
|
|
832
|
-
return function(text, target, attributes) {
|
|
833
|
-
if (text == null || text === '') return text;
|
|
834
|
-
if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
|
|
835
|
-
|
|
836
|
-
var attributesFn =
|
|
837
|
-
isFunction(attributes) ? attributes :
|
|
838
|
-
isObject(attributes) ? function getAttributesObject() {return attributes;} :
|
|
839
|
-
function getEmptyAttributesObject() {return {};};
|
|
840
|
-
|
|
841
|
-
var match;
|
|
842
|
-
var raw = text;
|
|
843
|
-
var html = [];
|
|
844
|
-
var url;
|
|
845
|
-
var i;
|
|
846
|
-
while ((match = raw.match(LINKY_URL_REGEXP))) {
|
|
847
|
-
// We can not end in these as they are sometimes found at the end of the sentence
|
|
848
|
-
url = match[0];
|
|
849
|
-
// if we did not match ftp/http/www/mailto then assume mailto
|
|
850
|
-
if (!match[2] && !match[4]) {
|
|
851
|
-
url = (match[3] ? 'http://' : 'mailto:') + url;
|
|
852
|
-
}
|
|
853
|
-
i = match.index;
|
|
854
|
-
addText(raw.substr(0, i));
|
|
855
|
-
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
|
|
856
|
-
raw = raw.substring(i + match[0].length);
|
|
857
|
-
}
|
|
858
|
-
addText(raw);
|
|
859
|
-
return $sanitize(html.join(''));
|
|
860
|
-
|
|
861
|
-
function addText(text) {
|
|
862
|
-
if (!text) {
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
html.push(sanitizeText(text));
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
function addLink(url, text) {
|
|
869
|
-
var key, linkAttributes = attributesFn(url);
|
|
870
|
-
html.push('<a ');
|
|
871
|
-
|
|
872
|
-
for (key in linkAttributes) {
|
|
873
|
-
html.push(key + '="' + linkAttributes[key] + '" ');
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
if (isDefined(target) && !('target' in linkAttributes)) {
|
|
877
|
-
html.push('target="',
|
|
878
|
-
target,
|
|
879
|
-
'" ');
|
|
880
|
-
}
|
|
881
|
-
html.push('href="',
|
|
882
|
-
url.replace(/"/g, '"'),
|
|
883
|
-
'">');
|
|
884
|
-
addText(text);
|
|
885
|
-
html.push('</a>');
|
|
886
|
-
}
|
|
887
|
-
};
|
|
888
|
-
}]);
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
})(window, window.angular);
|