PageSourceSearch

https://fxboss.firstcitizens.com/app/assets/angularjs/angular-sanitize.js?v=9.0.2

js firstcitizens.com collected 2026-09-24 21:50:39 UTC 33,196 bytes, 891 lines download raw bytes

vendor: 33,196 bytes, lines 1-891
1/**
2 * @license AngularJS v1.8.3
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
19var $sanitizeMinErr = angular.$$minErr('$sanitize');
20var bind;
21var extend;
22var forEach;
23var isArray;
24var isDefined;
25var lowercase;
26var noop;
27var nodeContains;
28var htmlParser;
29var 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>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</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>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
96&lt;/div&gt;</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>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</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("&lt;p style=\"color:blue\"&gt;an html\n" +
125              "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
126              "snippet&lt;/p&gt;");
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 &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
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 */
152function $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, '&amp;').
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, '&lt;').
579      replace(/>/g, '&gt;');
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
681function 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
690angular.module('ngSanitize', [])
691  .provider('$sanitize', $SanitizeProvider)
692  .info({ angularVersion: '1.8.3' });
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>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</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>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_blank'"&gt;<br>&lt;/div&gt;</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>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"&gt;<br>&lt;/div&gt;</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>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</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:[email protected],\n' +
775             '[email protected],\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/, [email protected], ' +
784                  '[email protected], 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:[email protected], ' +
791                  '[email protected], 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 */
821angular.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, '&quot;'),
883                '">');
884      addText(text);
885      html.push('</a>');
886    }
887  };
888}]);
889
890
891})(window, window.angular);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.