app/assets/javascripts/angular/angular-sanitize.js in angular-rails-engine-1.2.0.0 vs app/assets/javascripts/angular/angular-sanitize.js in angular-rails-engine-1.2.0.1

- old
+ new

@@ -1,7 +1,7 @@ /** - * @license AngularJS v1.2.0rc1 + * @license AngularJS v1.2.0 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ (function(window, angular, undefined) {'use strict'; @@ -9,29 +9,20 @@ /** * @ngdoc overview * @name ngSanitize * @description - * + * + * # ngSanitize + * * The `ngSanitize` module provides functionality to sanitize HTML. - * - * # Installation - * As a separate module, it must be loaded after Angular core is loaded; otherwise, an 'Uncaught Error: - * No module: ngSanitize' runtime error will occur. * - * <pre> - * <script src="angular.js"></script> - * <script src="angular-sanitize.js"></script> - * </pre> + * {@installModule sanitize} * - * # Usage - * To make sure the module is available to your application, declare it as a dependency of you application - * module. + * <div doc-module-components="ngSanitize"></div> * - * <pre> - * angular.module('app', ['ngSanitize']); - * </pre> + * See {@link ngSanitize.$sanitize `$sanitize`} for usage. */ /* * HTML Parser By Misko Hevery (misko@hevery.com) * based on: HTML Parser By John Resig (ejohn.org) @@ -64,98 +55,106 @@ * @param {string} html Html input. * @returns {string} Sanitized html. * * @example <doc:example module="ngSanitize"> - <doc:source> - <script> - function Ctrl($scope, $sce) { - $scope.snippet = - '<p style="color:blue">an html\n' + - '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' + - 'snippet</p>'; - $scope.deliberatelyTrustDangerousSnippet = function() { - return $sce.trustAsHtml($scope.snippet); - }; - } - </script> - <div ng-controller="Ctrl"> - Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea> - <table> - <tr> - <td>Directive</td> - <td>How</td> - <td>Source</td> - <td>Rendered</td> - </tr> - <tr id="bind-html-with-sanitize"> - <td>ng-bind-html</td> - <td>Automatically uses $sanitize</td> - <td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td> - <td><div ng-bind-html="snippet"></div></td> - </tr> - <tr id="bind-html-with-trust"> - <td>ng-bind-html</td> - <td>Bypass $sanitize by explicitly trusting the dangerous value</td> - <td><pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;<br/>&lt;/div&gt;</pre></td> - <td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td> - </tr> - <tr id="bind-default"> - <td>ng-bind</td> - <td>Automatically escapes</td> - <td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td> - <td><div ng-bind="snippet"></div></td> - </tr> - </table> - </div> - </doc:source> - <doc:scenario> - it('should sanitize the html snippet by default', function() { - expect(using('#bind-html-with-sanitize').element('div').html()). - toBe('<p>an html\n<em>click here</em>\nsnippet</p>'); - }); + <doc:source> + <script> + function Ctrl($scope, $sce) { + $scope.snippet = + '<p style="color:blue">an html\n' + + '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' + + 'snippet</p>'; + $scope.deliberatelyTrustDangerousSnippet = function() { + return $sce.trustAsHtml($scope.snippet); + }; + } + </script> + <div ng-controller="Ctrl"> + Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea> + <table> + <tr> + <td>Directive</td> + <td>How</td> + <td>Source</td> + <td>Rendered</td> + </tr> + <tr id="bind-html-with-sanitize"> + <td>ng-bind-html</td> + <td>Automatically uses $sanitize</td> + <td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td> + <td><div ng-bind-html="snippet"></div></td> + </tr> + <tr id="bind-html-with-trust"> + <td>ng-bind-html</td> + <td>Bypass $sanitize by explicitly trusting the dangerous value</td> + <td> + <pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt; +&lt;/div&gt;</pre> + </td> + <td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td> + </tr> + <tr id="bind-default"> + <td>ng-bind</td> + <td>Automatically escapes</td> + <td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td> + <td><div ng-bind="snippet"></div></td> + </tr> + </table> + </div> + </doc:source> + <doc:scenario> + it('should sanitize the html snippet by default', function() { + expect(using('#bind-html-with-sanitize').element('div').html()). + toBe('<p>an html\n<em>click here</em>\nsnippet</p>'); + }); - it('should inline raw snippet if bound to a trusted value', function() { - expect(using('#bind-html-with-trust').element("div").html()). - toBe("<p style=\"color:blue\">an html\n" + - "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + - "snippet</p>"); - }); + it('should inline raw snippet if bound to a trusted value', function() { + expect(using('#bind-html-with-trust').element("div").html()). + toBe("<p style=\"color:blue\">an html\n" + + "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + + "snippet</p>"); + }); - it('should escape snippet without any filter', function() { - expect(using('#bind-default').element('div').html()). - toBe("&lt;p style=\"color:blue\"&gt;an html\n" + - "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" + - "snippet&lt;/p&gt;"); - }); + it('should escape snippet without any filter', function() { + expect(using('#bind-default').element('div').html()). + toBe("&lt;p style=\"color:blue\"&gt;an html\n" + + "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" + + "snippet&lt;/p&gt;"); + }); - it('should update', function() { - input('snippet').enter('new <b onclick="alert(1)">text</b>'); - expect(using('#bind-html-with-sanitize').element('div').html()).toBe('new <b>text</b>'); - expect(using('#bind-html-with-trust').element('div').html()).toBe('new <b onclick="alert(1)">text</b>'); - expect(using('#bind-default').element('div').html()).toBe("new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;"); - }); - </doc:scenario> + it('should update', function() { + input('snippet').enter('new <b onclick="alert(1)">text</b>'); + expect(using('#bind-html-with-sanitize').element('div').html()).toBe('new <b>text</b>'); + expect(using('#bind-html-with-trust').element('div').html()).toBe( + 'new <b onclick="alert(1)">text</b>'); + expect(using('#bind-default').element('div').html()).toBe( + "new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;"); + }); + </doc:scenario> </doc:example> */ var $sanitize = function(html) { var buf = []; htmlParser(html, htmlSanitizeWriter(buf)); return buf.join(''); }; // Regular Expressions for parsing tags and attributes -var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/, +var START_TAG_REGEXP = + /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/, END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/, ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, BEGIN_TAG_REGEXP = /^</, BEGING_END_TAGE_REGEXP = /^<\s*\//, COMMENT_REGEXP = /<!--(.*?)-->/g, + DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i, CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g, URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/i, - NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character) + // Match everything outside of normal chars and " (quote character) + NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Good source of info about elements and attributes // http://dev.w3.org/html5/spec/Overview.html#semantics // http://simon.html5.org/html-elements @@ -166,27 +165,33 @@ // Elements that you can, intentionally, leave open (and which close themselves) // http://dev.w3.org/html5/spec/Overview.html#optional-tags var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), optionalEndTagInlineElements = makeMap("rp,rt"), - optionalEndTagElements = angular.extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements); + optionalEndTagElements = angular.extend({}, + optionalEndTagInlineElements, + optionalEndTagBlockElements); // Safe Block Elements - HTML5 -var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article,aside," + - "blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6," + - "header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")); +var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," + + "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," + + "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")); // Inline Elements - HTML5 -var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b,bdi,bdo," + - "big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small," + - "span,strike,strong,sub,sup,time,tt,u,var")); +var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," + + "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," + + "samp,small,span,strike,strong,sub,sup,time,tt,u,var")); // Special Elements (can contain anything) var specialElements = makeMap("script,style"); -var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements); +var validElements = angular.extend({}, + voidElements, + blockElements, + inlineElements, + optionalEndTagElements); //Attributes that have href and hence need to be sanitized var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap"); var validAttrs = angular.extend({}, uriAttrs, makeMap( 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ @@ -224,18 +229,26 @@ // Make sure we're not in a script or style element if ( !stack.last() || !specialElements[ stack.last() ] ) { // Comment if ( html.indexOf("<!--") === 0 ) { - index = html.indexOf("-->"); + // comments containing -- are not allowed unless they terminate the comment + index = html.indexOf("--", 4); - if ( index >= 0 ) { + if ( index >= 0 && html.lastIndexOf("-->", index) === index) { if (handler.comment) handler.comment( html.substring( 4, index ) ); html = html.substring( index + 3 ); chars = false; } + // DOCTYPE + } else if ( DOCTYPE_REGEXP.test(html) ) { + match = html.match( DOCTYPE_REGEXP ); + if ( match ) { + html = html.replace( match[0] , ''); + chars = false; + } // end tag } else if ( BEGING_END_TAGE_REGEXP.test(html) ) { match = html.match( END_TAG_REGEXP ); if ( match ) { @@ -263,25 +276,25 @@ if (handler.chars) handler.chars( decodeEntities(text) ); } } else { - html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){ - text = text. - replace(COMMENT_REGEXP, "$1"). - replace(CDATA_REGEXP, "$1"); + html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), + function(all, text){ + text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1"); - if (handler.chars) handler.chars( decodeEntities(text) ); + if (handler.chars) handler.chars( decodeEntities(text) ); - return ""; + return ""; }); parseEndTag( "", stack.last() ); } if ( html == last ) { - throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block of html: {0}", html); + throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " + + "of html: {0}", html); } last = html; } // Clean up any remaining tags @@ -304,17 +317,18 @@ if ( !unary ) stack.push( tagName ); var attrs = {}; - rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) { - var value = doubleQuotedValue - || singleQuotedValue - || unquotedValue - || ''; + rest.replace(ATTR_REGEXP, + function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) { + var value = doubleQuotedValue + || singleQuotedValue + || unquotedValue + || ''; - attrs[name] = decodeEntities(value); + attrs[name] = decodeEntities(value); }); if (handler.start) handler.start( tagName, attrs, unary ); } function parseEndTag( tag, tagName ) { @@ -382,16 +396,16 @@ start: function(tag, attrs, unary){ tag = angular.lowercase(tag); if (!ignore && specialElements[tag]) { ignore = tag; } - if (!ignore && validElements[tag] == true) { + if (!ignore && validElements[tag] === true) { out('<'); out(tag); angular.forEach(attrs, function(value, key){ var lkey=angular.lowercase(key); - if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) { + if (validAttrs[lkey]===true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) { out(' '); out(key); out('="'); out(encodeEntities(value)); out('"'); @@ -400,11 +414,11 @@ out(unary ? '/>' : '>'); } }, end: function(tag){ tag = angular.lowercase(tag); - if (!ignore && validElements[tag] == true) { + if (!ignore && validElements[tag] === true) { out('</'); out(tag); out('>'); } if (tag == ignore) { @@ -421,19 +435,23 @@ // define ngSanitize module and register $sanitize service angular.module('ngSanitize', []).value('$sanitize', $sanitize); +/* global htmlSanitizeWriter: false */ + /** * @ngdoc filter * @name ngSanitize.filter:linky * @function * * @description - * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and - * plain email address links. + * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and + * plain email address links. * + * Requires the {@link ngSanitize `ngSanitize`} module to be installed. + * * @param {string} text Input text. * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in. * @returns {string} Html-linkified text. * * @usage @@ -518,10 +536,11 @@ }); </doc:scenario> </doc:example> */ angular.module('ngSanitize').filter('linky', function() { - var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/, + var LINKY_URL_REGEXP = + /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/, MAILTO_REGEXP = /^mailto:/; return function(text, target) { if (!text) return text; var match;