One
of the challenges in importing external RSS feeds for the annotations aggregator is
how to safely display untrusted HTML. Enter the SECURITY attribute
of the IFRAME, a great way of instructing the browser to render the contents of the
frame in the Restricted
Sites zone,
thus (by default) limiting the capabilities of HTML in that frame to markup and not
much else.
To
cut a long story short, the client-side component encodes the incoming HTML from the
RSS feed and ships it around in that HtmlEncoded state through a series of transforms
to provide sorting, create borders and generally beautify. Just before it’s rendered
in the browser, there’s a final decoding step to decode that content back into HTML
so that the markup is interpreted for display to the end user.
Since
there’s no built-in client side implementation, here’s a rendition of Server.HtmlDecode
in Javascript:
//
HtmlDecode http://lab.msdn.microsoft.com/annotations/htmldecode.js
// client
side version of the useful Server.HtmlDecode method
// takes
one string (encoded) and returns another (decoded)
function HtmlDecode(s)
{
var out
= "";
if (s==null) return;
var l
= s.length;
for (var i=0;
i<l; i++)
{
var ch
= s.charAt(i);
if (ch
== '&')
{
var semicolonIndex
= s.indexOf(';', i+1);
if (semicolonIndex
> 0)
{
var entity
= s.substring(i + 1, semicolonIndex);
if (entity.length
> 1 && entity.charAt(0) == '#')
{
if (entity.charAt(1)
== 'x' || entity.charAt(1) == 'X')
ch
= String.fromCharCode(eval('0'+entity.substring(1)));
else
ch
= String.fromCharCode(eval(entity.substring(1)));
}
else
{
switch (entity)
{
case 'quot':
ch = String.fromCharCode(0x0022); break;
case 'amp':
ch = String.fromCharCode(0x0026); break;
case 'lt':
ch = String.fromCharCode(0x003c); break;
case 'gt':
ch = String.fromCharCode(0x003e); break;
case 'nbsp':
ch = String.fromCharCode(0x00a0); break;
case 'iexcl':
ch = String.fromCharCode(0x00a1); break;
case 'cent':
ch = String.fromCharCode(0x00a2); break;
case 'pound':
ch = String.fromCharCode(0x00a3); break;
case 'curren':
ch = String.fromCharCode(0x00a4); break;
case 'yen':
ch = String.fromCharCode(0x00a5); break;
case 'brvbar':
ch = String.fromCharCode(0x00a6); break;
case 'sect':
ch = String.fromCharCode(0x00a7); break;
case 'uml':
ch = String.fromCharCode(0x00a8); break;
case 'copy':
ch = String.fromCharCode(0x00a9); break;
case 'ordf':
ch = String.fromCharCode(0x00aa); break;
case 'laquo':
ch = String.fromCharCode(0x00ab); break;
case 'not':
ch = String.fromCharCode(0x00ac); break;
case 'shy':
ch = String.fromCharCode(0x00ad); break;
case 'reg':
ch = String.fromCharCode(0x00ae); break;
case 'macr':
ch = String.fromCharCode(0x00af); break;
case 'deg':
ch = String.fromCharCode(0x00b0); break;
case 'plusmn':
ch = String.fromCharCode(0x00b1); break;
case 'sup2':
ch = String.fromCharCode(0x00b2); break;
case 'sup3':
ch = String.fromCharCode(0x00b3); break;
case 'acute':
ch = String.fromCharCode(0x00b4); break;
case 'micro':
ch = String.fromCharCode(0x00b5); break;
case 'para':
ch = String.fromCharCode(0x00b6); break;
case 'middot':
ch = String.fromCharCode(0x00b7); break;
case 'cedil':
ch = String.fromCharCode(0x00b8); break;
case 'sup1':
ch = String.fromCharCode(0x00b9); break;
case 'ordm':
ch = String.fromCharCode(0x00ba); break;
case 'raquo':
ch = String.fromCharCode(0x00bb); break;
case 'frac14':
ch = String.fromCharCode(0x00bc); break;
case 'frac12':
ch = String.fromCharCode(0x00bd); break;
case 'frac34':
ch = String.fromCharCode(0x00be); break;
case 'iquest':
ch = String.fromCharCode(0x00bf); break;
case 'Agrave':
ch = String.fromCharCode(0x00c0); break;
case 'Aacute':
ch = String.fromCharCode(0x00c1); break;
case 'Acirc':
ch = String.fromCharCode(0x00c2); break;
case 'Atilde':
ch = String.fromCharCode(0x00c3); break;
case 'Auml':
ch = String.fromCharCode(0x00c4); break;
case 'Aring':
ch = String.fromCharCode(0x00c5); break;
case 'AElig':
ch = String.fromCharCode(0x00c6); break;
case 'Ccedil':
ch = String.fromCharCode(0x00c7); break;
case 'Egrave':
ch = String.fromCharCode(0x00c8); break;
case 'Eacute':
ch = String.fromCharCode(0x00c9); break;
case 'Ecirc':
ch = String.fromCharCode(0x00ca); break;
case 'Euml':
ch = String.fromCharCode(0x00cb); break;
case 'Igrave':
ch = String.fromCharCode(0x00cc); break;
case 'Iacute':
ch = String.fromCharCode(0x00cd); break;
case 'Icirc':
ch = String.fromCharCode(0x00ce
); break;
case 'Iuml':
ch = String.fromCharCode(0x00cf); break;
case 'ETH':
ch = String.fromCharCode(0x00d0); break;
case 'Ntilde':
ch = String.fromCharCode(0x00d1); break;
case 'Ograve':
ch = String.fromCharCode(0x00d2); break;
case 'Oacute':
ch = String.fromCharCode(0x00d3); break;
case 'Ocirc':
ch = String.fromCharCode(0x00d4); break;
case 'Otilde':
ch = String.fromCharCode(0x00d5); break;
case 'Ouml':
ch = String.fromCharCode(0x00d6); break;
case 'times':
ch = String.fromCharCode(0x00d7); break;
case 'Oslash':
ch = String.fromCharCode(0x00d8); break;
case 'Ugrave':
ch = String.fromCharCode(0x00d9); break;
case 'Uacute':
ch = String.fromCharCode(0x00da); break;
case 'Ucirc':
ch = String.fromCharCode(0x00db); break;
case 'Uuml':
ch = String.fromCharCode(0x00dc); break;
case 'Yacute':
ch = String.fromCharCode(0x00dd); break;
case 'THORN':
ch = String.fromCharCode(0x00de); break;
case 'szlig':
ch = String.fromCharCode(0x00df); break;
case 'agrave':
ch = String.fromCharCode(0x00e0); break;
case 'aacute':
ch = String.fromCharCode(0x00e1); break;
case 'acirc':
ch = String.fromCharCode(0x00e2); break;
case 'atilde':
ch = String.fromCharCode(0x00e3); break;
case 'auml':
ch = String.fromCharCode(0x00e4); break;
case 'aring':
ch = String.fromCharCode(0x00e5); break;
case 'aelig':
ch = String.fromCharCode(0x00e6); break;
case 'ccedil':
ch = String.fromCharCode(0x00e7); break;
case 'egrave':
ch = String.fromCharCode(0x00e8); break;
case 'eacute':
ch = String.fromCharCode(0x00e9); break;