PageSourceSearch

https://www.imperial-rac.com/wp-content/plugins/ng-filter/assets/j…/angular-sanitize.js?ver=7.1.2

js imperial-rac.com collected 2026-10-04 02:41:33 UTC 21,794 bytes, 647 lines download raw bytes

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