https://www.readycloud.com/static/bower_components/angular-sanitize/angular-sanitize.353eb99b818c.js
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><div ng-bind-html="snippet"><br/></div></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><div ng-bind-html="deliberatelyTrustDangerousSnippet()"> 105</div></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><div ng-bind="snippet"><br/></div></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("<p style=\"color:blue\">an html\n" + 134 "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + 135 "snippet</p>"); 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 <b onclick=\"alert(1)\">text</b>"); 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,"<"); 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, '&'). 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, '<'). 467 replace(/>/g, '>'); 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><div ng-bind-html="snippet | linky"><br></div></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><div ng-bind-html="snippetWithTarget | linky:'_blank'"><br></div></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><div ng-bind="snippet"><br></div></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, '"'), 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.