vendor/assets/javascripts/unstable/angular-sanitize.js in angularjs-rails-1.3.0 vs vendor/assets/javascripts/unstable/angular-sanitize.js in angularjs-rails-1.3.1

- old
+ new

@@ -1,7 +1,7 @@ /** - * @license AngularJS v1.3.0 + * @license AngularJS v1.3.1 * (c) 2010-2014 Google, Inc. http://angularjs.org * License: MIT */ (function(window, angular, undefined) {'use strict'; @@ -43,20 +43,20 @@ * @ngdoc service * @name $sanitize * @kind function * * @description - * The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are + * The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are * then serialized back to properly escaped html string. This means that no unsafe input can make * it into the returned string, however, since our parser is more strict than a typical browser * parser, it's possible that some obscure input, which would be recognized as valid HTML by a - * browser, won't make it through the sanitizer. + * browser, won't make it through the sanitizer. The input may also contain SVG markup. * The whitelist is configured using the functions `aHrefSanitizationWhitelist` and * `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}. * - * @param {string} html Html input. - * @returns {string} Sanitized html. + * @param {string} html HTML input. + * @returns {string} Sanitized HTML. * * @example <example module="sanitizeExample" deps="angular-sanitize.js"> <file name="index.html"> <script> @@ -196,29 +196,60 @@ // 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")); +// SVG Elements +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements +var svgElements = makeMap("animate,animateColor,animateMotion,animateTransform,circle,defs," + + "desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient," + + "line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,set," + + "stop,svg,switch,text,title,tspan,use"); // Special Elements (can contain anything) var specialElements = makeMap("script,style"); var validElements = angular.extend({}, voidElements, blockElements, inlineElements, - optionalEndTagElements); + optionalEndTagElements, + svgElements); //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,'+ +var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href"); + +var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ 'scope,scrolling,shape,size,span,start,summary,target,title,type,'+ - 'valign,value,vspace,width')); + 'valign,value,vspace,width'); +// SVG attributes (without "id" and "name" attributes) +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes +var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,'+ + 'attributeName,attributeType,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,'+ + 'color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,'+ + 'font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,'+ + 'gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,'+ + 'keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,'+ + 'markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,'+ + 'overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,'+ + 'repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,'+ + 'stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,'+ + 'stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,'+ + 'stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,'+ + 'underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,'+ + 'viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,'+ + 'xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,'+ + 'zoomAndPan'); + +var validAttrs = angular.extend({}, + uriAttrs, + svgAttrs, + htmlAttrs); + function makeMap(str) { var obj = {}, items = str.split(','), i; for (i = 0; i < items.length; i++) obj[items[i]] = true; return obj; } @@ -234,122 +265,122 @@ * }); * * @param {string} html string * @param {object} handler */ -function htmlParser( html, handler ) { +function htmlParser(html, handler) { if (typeof html !== 'string') { if (html === null || typeof html === 'undefined') { html = ''; } else { html = '' + html; } } var index, chars, match, stack = [], last = html, text; stack.last = function() { return stack[ stack.length - 1 ]; }; - while ( html ) { + while (html) { text = ''; chars = true; // Make sure we're not in a script or style element - if ( !stack.last() || !specialElements[ stack.last() ] ) { + if (!stack.last() || !specialElements[ stack.last() ]) { // Comment - if ( html.indexOf("<!--") === 0 ) { + if (html.indexOf("<!--") === 0) { // comments containing -- are not allowed unless they terminate the comment index = html.indexOf("--", 4); - if ( index >= 0 && html.lastIndexOf("-->", index) === index) { - if (handler.comment) handler.comment( html.substring( 4, index ) ); - html = html.substring( index + 3 ); + 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 ); + } else if (DOCTYPE_REGEXP.test(html)) { + match = html.match(DOCTYPE_REGEXP); - if ( match ) { - html = html.replace( match[0], ''); + 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 ); + } else if (BEGING_END_TAGE_REGEXP.test(html)) { + match = html.match(END_TAG_REGEXP); - if ( match ) { - html = html.substring( match[0].length ); - match[0].replace( END_TAG_REGEXP, parseEndTag ); + if (match) { + html = html.substring(match[0].length); + match[0].replace(END_TAG_REGEXP, parseEndTag); chars = false; } // start tag - } else if ( BEGIN_TAG_REGEXP.test(html) ) { - match = html.match( START_TAG_REGEXP ); + } else if (BEGIN_TAG_REGEXP.test(html)) { + match = html.match(START_TAG_REGEXP); - if ( match ) { + if (match) { // We only have a valid start-tag if there is a '>'. - if ( match[4] ) { - html = html.substring( match[0].length ); - match[0].replace( START_TAG_REGEXP, parseStartTag ); + if (match[4]) { + html = html.substring(match[0].length); + match[0].replace(START_TAG_REGEXP, parseStartTag); } chars = false; } else { // no ending tag found --- this piece should be encoded as an entity. text += '<'; html = html.substring(1); } } - if ( chars ) { + if (chars) { index = html.indexOf("<"); - text += index < 0 ? html : html.substring( 0, index ); - html = index < 0 ? "" : html.substring( index ); + text += index < 0 ? html : html.substring(0, index); + html = index < 0 ? "" : html.substring(index); - if (handler.chars) handler.chars( decodeEntities(text) ); + if (handler.chars) handler.chars(decodeEntities(text)); } } else { html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), - function(all, text){ + 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 ""; }); - parseEndTag( "", stack.last() ); + parseEndTag("", stack.last()); } - if ( html == last ) { + if (html == last) { throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " + "of html: {0}", html); } last = html; } // Clean up any remaining tags parseEndTag(); - function parseStartTag( tag, tagName, rest, unary ) { + function parseStartTag(tag, tagName, rest, unary) { tagName = angular.lowercase(tagName); - if ( blockElements[ tagName ] ) { - while ( stack.last() && inlineElements[ stack.last() ] ) { - parseEndTag( "", stack.last() ); + if (blockElements[ tagName ]) { + while (stack.last() && inlineElements[ stack.last() ]) { + parseEndTag("", stack.last()); } } - if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) { - parseEndTag( "", tagName ); + if (optionalEndTagElements[ tagName ] && stack.last() == tagName) { + parseEndTag("", tagName); } unary = voidElements[ tagName ] || !!unary; - if ( !unary ) - stack.push( tagName ); + if (!unary) + stack.push(tagName); var attrs = {}; rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) { @@ -358,26 +389,26 @@ || unquotedValue || ''; attrs[name] = decodeEntities(value); }); - if (handler.start) handler.start( tagName, attrs, unary ); + if (handler.start) handler.start(tagName, attrs, unary); } - function parseEndTag( tag, tagName ) { + function parseEndTag(tag, tagName) { var pos = 0, i; tagName = angular.lowercase(tagName); - if ( tagName ) + if (tagName) // Find the closest opened tag of the same type - for ( pos = stack.length - 1; pos >= 0; pos-- ) - if ( stack[ pos ] == tagName ) + for (pos = stack.length - 1; pos >= 0; pos--) + if (stack[ pos ] == tagName) break; - if ( pos >= 0 ) { + if (pos >= 0) { // Close all the open elements, up the stack - for ( i = stack.length - 1; i >= pos; i-- ) - if (handler.end) handler.end( stack[ i ] ); + for (i = stack.length - 1; i >= pos; i--) + if (handler.end) handler.end(stack[ i ]); // Remove the open elements from the stack stack.length = pos; } } @@ -419,16 +450,16 @@ * @returns {string} escaped text */ function encodeEntities(value) { return value. replace(/&/g, '&amp;'). - replace(SURROGATE_PAIR_REGEXP, function (value) { + replace(SURROGATE_PAIR_REGEXP, function(value) { var hi = value.charCodeAt(0); var low = value.charCodeAt(1); return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; }). - replace(NON_ALPHANUMERIC_REGEXP, function(value){ + replace(NON_ALPHANUMERIC_REGEXP, function(value) { return '&#' + value.charCodeAt(0) + ';'; }). replace(/</g, '&lt;'). replace(/>/g, '&gt;'); } @@ -441,23 +472,23 @@ * end: function(tag) {}, * chars: function(text) {}, * comment: function(text) {} * } */ -function htmlSanitizeWriter(buf, uriValidator){ +function htmlSanitizeWriter(buf, uriValidator) { var ignore = false; var out = angular.bind(buf, buf.push); return { - start: function(tag, attrs, unary){ + start: function(tag, attrs, unary) { tag = angular.lowercase(tag); if (!ignore && specialElements[tag]) { ignore = tag; } if (!ignore && validElements[tag] === true) { out('<'); out(tag); - angular.forEach(attrs, function(value, key){ + angular.forEach(attrs, function(value, key) { var lkey=angular.lowercase(key); var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); if (validAttrs[lkey] === true && (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { out(' '); @@ -468,21 +499,21 @@ } }); out(unary ? '/>' : '>'); } }, - end: function(tag){ + end: function(tag) { tag = angular.lowercase(tag); if (!ignore && validElements[tag] === true) { out('</'); out(tag); out('>'); } if (tag == ignore) { ignore = false; } }, - chars: function(chars){ + chars: function(chars) { if (!ignore) { out(encodeEntities(chars)); } } };