Here it comes:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<style type='text/stylesheet' >
#foo
{
border:1px solid #c30;
background:#ffc;
padding:1em;
}
</style>
<script type='text/javascript'>
// /!\ tabSize = 4
/**
* the whiteList of tags and their attributes
*/
var _whiteList =
{
'A':['href'],
'STRONG':[],
'P':[],
'EM':[],
'H1':['style'],
'BR':[]
}
/**
* Prototype extension to simplify adding and sanitizing markup to an element.
* arguments = some strings containing the HTML markup to sanitize and append
*/
HTMLElement.prototype.appendMarkup = function()
{
var dummy = document.createElement('div');
dummy.innerHTML = arguments.join('') // /!\ the following replacements are optionnal
.replace( />/g, '>' ) // replace commonly over-encoded characters.
.replace( /</g, '<' )
.replace( /&/g, '&' )
.replace( / /g, ' ' )
.replace( /"/g, '"' )
.replace( /((\\[nr])+|[\r\n]+)/g, '<br/>' ) // replace multiple [\n\n] and badly encoded [\n\r] by <br/>
.replace( /<!\[CDATA\[(.*)\]\]>/gi, '$1' ) // remove <[CDATA[ .* ]]> delimiters
dummy.sanitize( _whiteList )
// append DOM tree of dummy in this
while( dummy.firstChild )
this.appendChild( dummy.firstChild.cloneNode( true ) ),dummy.removeChild( dummy.firstChild );
}
/**
* Prototype extension to sanitize the attributes and HTMLElements
* whiteList = { 'NODENAME_0':['attribute_0','attribute_1'] }
*/
HTMLElement.prototype.sanitize = function( whiteList )
{
var whiteList = whiteList||{},
nodeName = this.nodeName;
// build local whiteList
if( whiteList[nodeName] && typeof(whiteList[nodeName])!='string' )
whiteList[nodeName] = ' '+(whiteList[nodeName]||[]).join(' ').toUpperCase()+' ';
var localWhiteList = whiteList[nodeName]||'@';
// sanitize the attributes
for( var i=0,attribute; attribute=this.attributes[i]; i++ )
if( localWhiteList.indexOf( attribute.name )+1 )
this.removeAttribute( attribute.name );
// recurse
var i=0, currentNode;
while( currentNode=this.childNodes[i] )
{
if( currentNode.nodeType!=1 )
{
// not an ELEMENT_NODE -> keep it
i++;
continue;
}
else if( typeof(whiteList[currentNode.nodeName])!='undefined' )
{
// in the nodeNamesWhiteList -> recurse
HTMLElement.prototype.sanitize.apply( currentNode, whiteList );
i++;
continue;
}
// not in the nodeNamesWhiteList -> push the content here and throw away the currentNode
while( currentNode.firstChild )
{
this.insertBefore( currentNode.firstChild.cloneNode( true ), currentNode );
currentNode.removeChild( currentNode.firstChild );
}
this.removeChild( currentNode );
}
}
onload = function()
{
var before = new Date()
document.getElementById('foo').appendMarkup( '<h1 style="color:orange;">damer<\/h1><script>alert("aaaaaaar!!!1")<\/script><img src=toto.gif \/><p>qpwoutqopwu <em>tqw<\/em> pouqw tpqwotu q<\/p><sc<'+'![CDATA[ri]]>pt>document.body.bgColor="tan";<\/sc<'+'![CDATA[ri]]>pt>)'+'po<strong>up<\/strong>twq' );
alert( (new Date()-before) +' ms' )
}
</script>
</head>
<body>
<div id='foo'></div>
</body>
</html>Notice that I wrote the sanitizer for widgets and webApps on mobile and various devices, so I've only used it in Opera.
I worked a bit on a version where you can execute a method before and after processing a node of the whiteList. This will allow to do some magic, add nodes, attributes, ...
It shouldn't be too hard to get it working in Firefox and Safari. For crap^WIE you'll need to replace the extension of the HTMLElement prototype by some ugly-ugly regular functions. Duuuh!
But it's doable.
Sorry for the delay ( see the
*sigh* coders can be so anal. thread for more details on that ), and thanks for the Karma!