PageSourceSearch

https://www.readycloud.com/static/bower_components/angular-sanitize/angular-sanitize.353eb99b818c.js

js readycloud.com collected 2026-10-02 11:01:19 UTC 24,546 bytes, 683 lines download raw bytes

1/**
2 * @license AngularJS v1.4.8
3 * (c) 2010-2015 Google, Inc. http://angularjs.org
4 * License: MIT
5 */
6(function(window, angular, undefined) {'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');
20
21/**
22 * @ngdoc module
23 * @name ngSanitize
24 * @description
25 *
26 * # ngSanitize
27 *
28 * The `ngSanitize` module provides functionality to sanitize HTML.
29 *
30 *
31 * <div doc-module-components="ngSanitize"></div>
32 *
33 * See {@link ngSanitize.$sanitize `$sanitize`} for usage.
34 */
35
36/*
37 * HTML Parser By Misko Hevery ([email protected])
38 * based on:  HTML Parser By John Resig (ejohn.org)
39 * Original code by Erik Arvidsson, Mozilla Public License
40 * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
41 *
42 * // Use like so:
43 * htmlParser(htmlString, {
44 *     start: function(tag, attrs, unary) {},
45 *     end: function(tag) {},
46 *     chars: function(text) {},
47 *     comment: function(text) {}
48 * });
49 *
50 */
51
52
53/**
54 * @ngdoc service
55 * @name $sanitize
56 * @kind function
57 *
58 * @description
59 *   The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
60 *   then serialized back to properly escaped html string. This means that no unsafe input can make
61 *   it into the returned string, however, since our parser is more strict than a typical browser
62 *   parser, it's possible that some obscure input, which would be recognized as valid HTML by a
63 *   browser, won't make it through the sanitizer. The input may also contain SVG markup.
64 *   The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
65 *   `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
66 *
67 * @param {string} html HTML input.
68 * @returns {string} Sanitized HTML.
69 *
70 * @example
71   <example module="sanitizeExample" deps="angular-sanitize.js">
72   <file name="index.html">
73     <script>
74         angular.module('sanitizeExample', ['ngSanitize'])
75           .controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
76             $scope.snippet =
77               '<p style="color:blue">an html\n' +
78               '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
79               'snippet</p>';
80             $scope.deliberatelyTrustDangerousSnippet = function() {
81               return $sce.trustAsHtml($scope.snippet);
82             };
83           }]);
84     </script>
85     <div ng-controller="ExampleController">
86        Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
87       <table>
88         <tr>
89           <td>Directive</td>
90           <td>How</td>
91           <td>Source</td>
92           <td>Rendered</td>
93         </tr>
94         <tr id="bind-html-with-sanitize">
95           <td>ng-bind-html</td>
96           <td>Automatically uses $sanitize</td>
97           <td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
98           <td><div ng-bind-html="snippet"></div></td>
99         </tr>
100         <tr id="bind-html-with-trust">
101           <td>ng-bind-html</td>
102           <td>Bypass $sanitize by explicitly trusting the dangerous value</td>
103           <td>
104           <pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
105&lt;/div&gt;</pre>
106           </td>
107           <td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
108         </tr>
109         <tr id="bind-default">
110           <td>ng-bind</td>
111           <td>Automatically escapes</td>
112           <td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
113           <td><div ng-bind="snippet"></div></td>
114         </tr>
115       </table>
116       </div>
117   </file>
118   <file name="protractor.js" type="protractor">
119     it('should sanitize the html snippet by default', function() {
120       expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
121         toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
122     });
123
124     it('should inline raw snippet if bound to a trusted value', function() {
125       expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
126         toBe("<p style=\"color:blue\">an html\n" +
127              "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
128              "snippet</p>");
129     });
130
131     it('should escape snippet without any filter', function() {
132       expect(element(by.css('#bind-default div')).getInnerHtml()).
133         toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
134              "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
135              "snippet&lt;/p&gt;");
136     });
137
138     it('should update', function() {
139       element(by.model('snippet')).clear();
140       element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
141       expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
142         toBe('new <b>text</b>');
143       expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
144         'new <b onclick="alert(1)">text</b>');
145       expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
146         "new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
147     });
148   </file>
149   </example>
150 */
151function $SanitizeProvider() {
152  this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
153    return function(html) {
154      var buf = [];
155      htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
156        return !/^unsafe/.test($$sanitizeUri(uri, isImage));
157      }));
158      return buf.join('');
159    };
160  }];
161}
162
163function sanitizeText(chars) {
164  var buf = [];
165  var writer = htmlSanitizeWriter(buf, angular.noop);
166  writer.chars(chars);
167  return buf.join('');
168}
169
170
171// Regular Expressions for parsing tags and attributes
172var START_TAG_REGEXP =
173       /^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
174  END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
175  ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
176  BEGIN_TAG_REGEXP = /^</,
177  BEGING_END_TAGE_REGEXP = /^<\//,
178  COMMENT_REGEXP = /<!--(.*?)-->/g,
179  DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
180  CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
181  SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
182  // Match everything outside of normal chars and " (quote character)
183  NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
184
185
186// Good source of info about elements and attributes
187// http://dev.w3.org/html5/spec/Overview.html#semantics
188// http://simon.html5.org/html-elements
189
190// Safe Void Elements - HTML5
191// http://dev.w3.org/html5/spec/Overview.html#void-elements
192var voidElements = makeMap("area,br,col,hr,img,wbr");
193
194// Elements that you can, intentionally, leave open (and which close themselves)
195// http://dev.w3.org/html5/spec/Overview.html#optional-tags
196var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
197    optionalEndTagInlineElements = makeMap("rp,rt"),
198    optionalEndTagElements = angular.extend({},
199                                            optionalEndTagInlineElements,
200                                            optionalEndTagBlockElements);
201
202// Safe Block Elements - HTML5
203var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
204        "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
205        "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
206
207// Inline Elements - HTML5
208var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
209        "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
210        "samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
211
212// SVG Elements
213// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
214// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
215// They can potentially allow for arbitrary javascript to be executed. See #11290
216var svgElements = makeMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
217        "hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
218        "radialGradient,rect,stop,svg,switch,text,title,tspan,use");
219
220// Special Elements (can contain anything)
221var specialElements = makeMap("script,style");
222
223var validElements = angular.extend({},
224                                   voidElements,
225                                   blockElements,
226                                   inlineElements,
227                                   optionalEndTagElements,
228                                   svgElements);
229
230//Attributes that have href and hence need to be sanitized
231var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
232
233var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
234    'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
235    'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
236    'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
237    'valign,value,vspace,width');
238
239// SVG attributes (without "id" and "name" attributes)
240// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
241var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
242    'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
243    'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
244    'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
245    'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
246    'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
247    'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
248    'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
249    'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
250    'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
251    'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
252    'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
vendor: 5,658 bytes, lines 253-447
253    'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
254    'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
255    'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
256
257var validAttrs = angular.extend({},
258                                uriAttrs,
259                                svgAttrs,
260                                htmlAttrs);
261
262function makeMap(str, lowercaseKeys) {
263  var obj = {}, items = str.split(','), i;
264  for (i = 0; i < items.length; i++) {
265    obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true;
266  }
267  return obj;
268}
269
270
271/**
272 * @example
273 * htmlParser(htmlString, {
274 *     start: function(tag, attrs, unary) {},
275 *     end: function(tag) {},
276 *     chars: function(text) {},
277 *     comment: function(text) {}
278 * });
279 *
280 * @param {string} html string
281 * @param {object} handler
282 */
283function htmlParser(html, handler) {
284  if (typeof html !== 'string') {
285    if (html === null || typeof html === 'undefined') {
286      html = '';
287    } else {
288      html = '' + html;
289    }
290  }
291  var index, chars, match, stack = [], last = html, text;
292  stack.last = function() { return stack[stack.length - 1]; };
293
294  while (html) {
295    text = '';
296    chars = true;
297
298    // Make sure we're not in a script or style element
299    if (!stack.last() || !specialElements[stack.last()]) {
300
301      // Comment
302      if (html.indexOf("<!--") === 0) {
303        // comments containing -- are not allowed unless they terminate the comment
304        index = html.indexOf("--", 4);
305
306        if (index >= 0 && html.lastIndexOf("-->", index) === index) {
307          if (handler.comment) handler.comment(html.substring(4, index));
308          html = html.substring(index + 3);
309          chars = false;
310        }
311      // DOCTYPE
312      } else if (DOCTYPE_REGEXP.test(html)) {
313        match = html.match(DOCTYPE_REGEXP);
314
315        if (match) {
316          html = html.replace(match[0], '');
317          chars = false;
318        }
319      // end tag
320      } else if (BEGING_END_TAGE_REGEXP.test(html)) {
321        match = html.match(END_TAG_REGEXP);
322
323        if (match) {
324          html = html.substring(match[0].length);
325          match[0].replace(END_TAG_REGEXP, parseEndTag);
326          chars = false;
327        }
328
329      // start tag
330      } else if (BEGIN_TAG_REGEXP.test(html)) {
331        match = html.match(START_TAG_REGEXP);
332
333        if (match) {
334          // We only have a valid start-tag if there is a '>'.
335          if (match[4]) {
336            html = html.substring(match[0].length);
337            match[0].replace(START_TAG_REGEXP, parseStartTag);
338          }
339          chars = false;
340        } else {
341          // no ending tag found --- this piece should be encoded as an entity.
342          text += '<';
343          html = html.substring(1);
344        }
345      }
346
347      if (chars) {
348        index = html.indexOf("<");
349
350        text += index < 0 ? html : html.substring(0, index);
351        html = index < 0 ? "" : html.substring(index);
352
353        if (handler.chars) handler.chars(decodeEntities(text));
354      }
355
356    } else {
357      // IE versions 9 and 10 do not understand the regex '[^]', so using a workaround with [\W\w].
358      html = html.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
359        function(all, text) {
360          text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
361
362          if (handler.chars) handler.chars(decodeEntities(text));
363
364          return "";
365      });
366
367      parseEndTag("", stack.last());
368    }
369
370    if (html == last) {
371      throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
372                                        "of html: {0}", html);
373    }
374    last = html;
375  }
376
377  // Clean up any remaining tags
378  parseEndTag();
379
380  function parseStartTag(tag, tagName, rest, unary) {
381    tagName = angular.lowercase(tagName);
382    if (blockElements[tagName]) {
383      while (stack.last() && inlineElements[stack.last()]) {
384        parseEndTag("", stack.last());
385      }
386    }
387
388    if (optionalEndTagElements[tagName] && stack.last() == tagName) {
389      parseEndTag("", tagName);
390    }
391
392    unary = voidElements[tagName] || !!unary;
393
394    if (!unary) {
395      stack.push(tagName);
396    }
397
398    var attrs = {};
399
400    rest.replace(ATTR_REGEXP,
401      function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
402        var value = doubleQuotedValue
403          || singleQuotedValue
404          || unquotedValue
405          || '';
406
407        attrs[name] = decodeEntities(value);
408    });
409    if (handler.start) handler.start(tagName, attrs, unary);
410  }
411
412  function parseEndTag(tag, tagName) {
413    var pos = 0, i;
414    tagName = angular.lowercase(tagName);
415    if (tagName) {
416      // Find the closest opened tag of the same type
417      for (pos = stack.length - 1; pos >= 0; pos--) {
418        if (stack[pos] == tagName) break;
419      }
420    }
421
422    if (pos >= 0) {
423      // Close all the open elements, up the stack
424      for (i = stack.length - 1; i >= pos; i--)
425        if (handler.end) handler.end(stack[i]);
426
427      // Remove the open elements from the stack
428      stack.length = pos;
429    }
430  }
431}
432
433var hiddenPre=document.createElement("pre");
434/**
435 * decodes all entities into regular string
436 * @param value
437 * @returns {string} A string with decoded entities.
438 */
439function decodeEntities(value) {
440  if (!value) { return ''; }
441
442  hiddenPre.innerHTML = value.replace(/</g,"&lt;");
443  // innerText depends on styling as it doesn't display hidden elements.
444  // Therefore, it's better to use textContent not to cause unnecessary reflows.
445  return hiddenPre.textContent;
446}
447
vendor: 1,248 bytes, lines 448-488
448/**
449 * Escapes all potentially dangerous characters, so that the
450 * resulting string can be safely inserted into attribute or
451 * element text.
452 * @param value
453 * @returns {string} escaped text
454 */
455function encodeEntities(value) {
456  return value.
457    replace(/&/g, '&amp;').
458    replace(SURROGATE_PAIR_REGEXP, function(value) {
459      var hi = value.charCodeAt(0);
460      var low = value.charCodeAt(1);
461      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
462    }).
463    replace(NON_ALPHANUMERIC_REGEXP, function(value) {
464      return '&#' + value.charCodeAt(0) + ';';
465    }).
466    replace(/</g, '&lt;').
467    replace(/>/g, '&gt;');
468}
469
470/**
471 * create an HTML/XML writer which writes to buffer
472 * @param {Array} buf use buf.jain('') to get out sanitized html string
473 * @returns {object} in the form of {
474 *     start: function(tag, attrs, unary) {},
475 *     end: function(tag) {},
476 *     chars: function(text) {},
477 *     comment: function(text) {}
478 * }
479 */
480function htmlSanitizeWriter(buf, uriValidator) {
481  var ignore = false;
482  var out = angular.bind(buf, buf.push);
483  return {
484    start: function(tag, attrs, unary) {
485      tag = angular.lowercase(tag);
486      if (!ignore && specialElements[tag]) {
487        ignore = tag;
488      }
vendor: 2,266 bytes, lines 489-564
489      if (!ignore && validElements[tag] === true) {
490        out('<');
491        out(tag);
492        angular.forEach(attrs, function(value, key) {
493          var lkey=angular.lowercase(key);
494          var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
495          if (validAttrs[lkey] === true &&
496            (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
497            out(' ');
498            out(key);
499            out('="');
500            out(encodeEntities(value));
501            out('"');
502          }
503        });
504        out(unary ? '/>' : '>');
505      }
506    },
507    end: function(tag) {
508        tag = angular.lowercase(tag);
509        if (!ignore && validElements[tag] === true) {
510          out('</');
511          out(tag);
512          out('>');
513        }
514        if (tag == ignore) {
515          ignore = false;
516        }
517      },
518    chars: function(chars) {
519        if (!ignore) {
520          out(encodeEntities(chars));
521        }
522      }
523  };
524}
525
526
527// define ngSanitize module and register $sanitize service
528angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
529
530/* global sanitizeText: false */
531
532/**
533 * @ngdoc filter
534 * @name linky
535 * @kind function
536 *
537 * @description
538 * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
539 * plain email address links.
540 *
541 * Requires the {@link ngSanitize `ngSanitize`} module to be installed.
542 *
543 * @param {string} text Input text.
544 * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
545 * @returns {string} Html-linkified text.
546 *
547 * @usage
548   <span ng-bind-html="linky_expression | linky"></span>
549 *
550 * @example
551   <example module="linkyExample" deps="angular-sanitize.js">
552     <file name="index.html">
553       <script>
554         angular.module('linkyExample', ['ngSanitize'])
555           .controller('ExampleController', ['$scope', function($scope) {
556             $scope.snippet =
557               'Pretty text with some links:\n'+
558               'http://angularjs.org/,\n'+
559               'mailto:[email protected],\n'+
560               '[email protected],\n'+
561               'and one more: ftp://127.0.0.1/.';
562             $scope.snippetWithTarget = 'http://angularjs.org/';
563           }]);
564       </script>
565       <div ng-controller="ExampleController">
566       Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
567       <table>
568         <tr>
569           <td>Filter</td>
570           <td>Source</td>
571           <td>Rendered</td>
572         </tr>
573         <tr id="linky-filter">
574           <td>linky filter</td>
575           <td>
576             <pre>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
577           </td>
578           <td>
579             <div ng-bind-html="snippet | linky"></div>
580           </td>
581         </tr>
582         <tr id="linky-target">
583          <td>linky target</td>
584          <td>
585            <pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
586          </td>
587          <td>
588            <div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
589          </td>
590         </tr>
591         <tr id="escaped-html">
592           <td>no filter</td>
593           <td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
594           <td><div ng-bind="snippet"></div></td>
595         </tr>
596       </table>
597     </file>
598     <file name="protractor.js" type="protractor">
599       it('should linkify the snippet with urls', function() {
600         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
601             toBe('Pretty text with some links: http://angularjs.org/, [email protected], ' +
602                  '[email protected], and one more: ftp://127.0.0.1/.');
603         expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
604       });
605
606       it('should not linkify snippet without the linky filter', function() {
607         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
608             toBe('Pretty text with some links: http://angularjs.org/, mailto:[email protected], ' +
609                  '[email protected], and one more: ftp://127.0.0.1/.');
610         expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
611       });
612
613       it('should update', function() {
614         element(by.model('snippet')).clear();
615         element(by.model('snippet')).sendKeys('new http://link.');
616         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
617             toBe('new http://link.');
618         expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
619         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
620             .toBe('new http://link.');
621       });
622
623       it('should work with the target property', function() {
624        expect(element(by.id('linky-target')).
625            element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
626            toBe('http://angularjs.org/');
627        expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
628       });
629     </file>
630   </example>
631 */
632angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
633  var LINKY_URL_REGEXP =
634        /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
635      MAILTO_REGEXP = /^mailto:/i
vendor: 1,184 bytes, lines 635-683
635;
636
637  return function(text, target) {
638    if (!text) return text;
639    var match;
640    var raw = text;
641    var html = [];
642    var url;
643    var i;
644    while ((match = raw.match(LINKY_URL_REGEXP))) {
645      // We can not end in these as they are sometimes found at the end of the sentence
646      url = match[0];
647      // if we did not match ftp/http/www/mailto then assume mailto
648      if (!match[2] && !match[4]) {
649        url = (match[3] ? 'http://' : 'mailto:') + url;
650      }
651      i = match.index;
652      addText(raw.substr(0, i));
653      addLink(url, match[0].replace(MAILTO_REGEXP, ''));
654      raw = raw.substring(i + match[0].length);
655    }
656    addText(raw);
657    return $sanitize(html.join(''));
658
659    function addText(text) {
660      if (!text) {
661        return;
662      }
663      html.push(sanitizeText(text));
664    }
665
666    function addLink(url, text) {
667      html.push('<a ');
668      if (angular.isDefined(target)) {
669        html.push('target="',
670                  target,
671                  '" ');
672      }
673      html.push('href="',
674                url.replace(/"/g, '&quot;'),
675                '">');
676      addText(text);
677      html.push('</a>');
678    }
679  };
680}]);
681
682
683})(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.