String.prototype.xescape = function() {
  return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}

String.prototype.strip = function() {
  return this.replace(/^\s+/, '').replace(/\s+$/, '');
}

// create (or reset) a cookie
function createCookie(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else expires = "";
  document.cookie = name+"="+value+expires+"; path=/";
}
 
// read a cookie
function readCookie(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i < ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
  }
  return null;
}

var cp1252 = {
  128: 8364, // euro sign
  130: 8218, // single low-9 quotation mark
  131:  402, // latin small letter f with hook
  132: 8222, // double low-9 quotation mark
  133: 8230, // horizontal ellipsis
  134: 8224, // dagger
  135: 8225, // double dagger
  136:  710, // modifier letter circumflex accent
  137: 8240, // per mille sign
  138:  352, // latin capital letter s with caron
  139: 8249, // single left-pointing angle quotation mark
  140:  338, // latin capital ligature oe
  142:  381, // latin capital letter z with caron
  145: 8216, // left single quotation mark
  146: 8217, // right single quotation mark
  147: 8220, // left double quotation mark
  148: 8221, // right double quotation mark
  149: 8226, // bullet
  150: 8211, // en dash
  151: 8212, // em dash
  152:  732, // small tilde
  153: 8482, // trade mark sign
  154:  353, // latin small letter s with caron
  155: 8250, // single right-pointing angle quotation mark
  156:  339, // latin small ligature oe
  158:  382, // latin small letter z with caron
  159:  376  // latin capital letter y with diaeresis
};

function sanitize(body) {
  var hyperlink = function(str, href, text) {
    href = href.replace(/&amp;amp;/g, '&amp;');
    return '<a href="' + href + '">' + text + '</a>';
  }

  // code (literal) support
  var chunks = body.split(/\{\{\{([\s\S]*?)\}\}\}/);
  if (chunks.length > 1) {
    var work = chunks.slice();
    for (var i=1; i<chunks.length; i+=2) {
      if (chunks[i].indexOf('\n') >= 0) {
        chunks[i] = '<pre class="code">' + chunks[i].strip().xescape() + '</pre>';
        work[i] = '\n\n{{{' + i + '}}}\n\n';
      } else {
        chunks[i] = '<code>' + chunks[i] + '</code>';
        work[i] = '{{{' + i + '}}}';
      }
    }
    body = work.join('');
  } else {
    // naked urls become hypertext links
    var before = body;
    body = body.replace(/(^|[\s.:;?\-\]<\(])(http:\/\/[-\w;\/?:@&=+$.!~*\'()%,#]+[\w\/])(?=$|[\s.:;?\-\[\]>\)])/, '$1<a href="$2">[link]</a>');

  }

  // html characters used in text become escaped
  body = body.xescape();

  // canonicalize line control characters
  body = body.strip().replace(/\r\n?/g, '\n');

  // map windows-1252 to unicode
  for (var i=body.length-1; i>0; i--) {
    if (cp1252[body.charCodeAt(i)]) {
      body = body.substr(0,i) + '&#' + cp1252[body.charCodeAt(i)] + ';' + body.substr(i+1);
    }
  }
  
  // remove control characters
  body = body.replace(/[\x01-\x08\x0B\x0C\x0E-\x1F]/g, function(str) {
    var code = "000" + str.charCodeAt(0).toString(16).toUpperCase();
    return '<acronym title="U+' + code.substr(code.length-4) + '">&#xFFFD;</acronym>'; 
  });

  // passthru <a href>, <em>, <i>, <b>, <blockquote>, <br/>, <p>
  body=body.replace(/&lt;a href="([^"]*)"&gt;([^&]*)&lt;\/a&gt;/g, hyperlink);
  body=body.replace(/&lt;a href='([^']*)'&gt;([^&]*)&lt;\/a&gt;/g, hyperlink);
  body=body.replace(/&lt;abbr title="([^"]*)"&gt;([^&]*)&lt;\/abbr&gt;/,
            '<abbr title="$1">$2</abbr>');
  body=body.replace(/&lt;acronym title="([^"]*)"&gt;([^&]*)&lt;\/acronym&gt;/g,
            '<acronym title="$1">$2<\/acronym>');
  body=body.replace(/&lt;em&gt;([^&]*)&lt;\/em&gt;/g, '<em>$1<\/em>');
  body=body.replace(/&lt;i&gt;([^&]*)&lt;\/i&gt;/g, '<i>$1<\/i>');
  body=body.replace(/&lt;b&gt;([^&]*)&lt;\/b&gt;/g, '<b>$1<\/b>');
  body=body.replace(/&lt;strong&gt;([^&]*)&lt;\/strong&gt;/g, '<strong>$1<\/strong>');
  body=body.replace(/&lt;blockquote&gt;([^~]*?)&lt;\/blockquote&gt;/g,
            '\n\n<blockquote><p>$1<\/p><\/blockquote>\n\n');
  body=body.replace(/&lt;br\s*\/?&gt;\n?/g, '\n');
  body=body.replace(/&lt;\/?p&gt;/g, '\n\n').strip();

  // typographic support for mdash, curly quotes
  body=body.replace(/(\s)--(\s)/g, '$1&#8212;$2');
  body=body.replace(/(\w)\'(\w)/g, '$1&#8217;$2');
  body=body.replace(/(^|\s)'([^<]*?)'(\s|[,;.\]]|$)/g, '$1&#8216;$2&#8217;$3');
  body=body.replace(/(^|\s)"([^<]*?)"(\s|[,;.\]]|$)/g, '$1&#8220;$2&#8221;$3');

  // wiki like support: _em_, -del-, *b*, [url title]
  body=body.replace(/\b_(\w.*?)_\b/g, '<em>$1</em>');
  body=body.replace(/\*(\w.*?\w)\*/g, '<b>$1</b>');
  body=body.replace(/\[(\w+:\S+\.gif) (.*?)\]/g, '<img src="$1" alt="$2" />');
  body=body.replace(/\[(\w+:\S+\.jpg) (.*?)\]/g, '<img src="$1" alt="$2" />');
  body=body.replace(/\[(\w+:\S+\.png) (.*?)\]/g, '<img src="$1" alt="$2" />');
  body=body.replace(/\[(\w+:\S+) (.*?)\]/g, hyperlink).strip();

  // cvs urls
  body=body.replace(/\/<b>checkout<\/b>\//g, '/*checkout*/')
  body=body.replace(/\/<em>checkout<\/em>\//g, '/_checkout_/')

  // email style quotes (lines beginning with '>')
  body=body.replace(/^&gt;(.*)\n\n+&gt;/g, '&gt;$1\n&gt;\n&gt;');
  var quotes=body.split(/^( *&gt;.*(?:\n *&gt;.*)*)/m);
  for (var i=1; i<quotes.length; i+=2) {
    var html = '';
    var depth = 0;
    var qline=/^((?: |&gt;)*)(.*)/gm;
    for (var match; match=qline.exec(quotes[i]);) {
      var indent=match[1].replace(/ /g,'').replace(/&gt;/g,'>').length;
      while (indent>depth) depth++, html+='<blockquote class="quote"><p>';
      while (indent<depth) depth--, html+='</p></blockquote>';
      html+=match[2]+"\r";
    }
    while (depth>0) depth--, html+='</p></blockquote>';
    quotes[i]=html;
  }
  body=quotes.join('');

  // unordered lists: consecutive lines starting with spaces and an asterisk
  var lists=body.split(/^( *\*.*(?:\n *\*.*)*)\n*/m);
  for (var i=1; i<lists.length; i+=2) {
    var html = '';
    var stack = [''];
    var li = /( +)\* +(.*)/g;
    for (var match; match=li.exec(lists[i]);) {
      var indent=match[1];
      if (indent>stack[stack.length-1]) stack=stack.concat(indent), html+='\n<ul>\r';
      while (indent<stack[stack.length-1]) stack.pop(), html+='</ul>\r';
      if (match[2]) html += '<li>'+match[2]+'</li>\r';
    }
    while (stack.length > 1) stack.pop(), html+='</ul>\r';
    lists[i]=html+"\n\n";
  }
  body=lists.join('');

  // white space
  var stanzas=body.strip().split(/\n\n+/);
  if (stanzas.length>1) body='<p>' + stanzas.join('</p>\r<p>') + '</p>\r';
  if (stanzas.length>1) {
    for (var i=0; i<stanzas.length; i++) {
      if (stanzas[i].substr(0,4) != '<ul>' && stanzas[i].substr(0,11) != '<blockquote') {
        stanzas[i] = '<p>' + stanzas[i] + '</p>'
      }
    }
    body=stanzas.join('\r');
  }
  body=body.replace(/\n<\/p>/g, '</p>');
  body=body.replace(/\n/g, '<br />\n');
  body=body.replace(/  +/g, '&#160; ');

  // reinsert literals
  if (chunks.length > 1) {
    for (var i=1; i<chunks.length; i++) {
      body=body.replace('<p>{{{'+i+'}}}</p>', chunks[i]);
      body=body.replace('{{{'+i+'}}}', chunks[i]);
    }
  }

  // renormalize linefeeds
  body=body.replace(/\r\n*/g, '\n').strip();

  return body;
}

prev_value = '';

function live_preview() {
  var checkbox = document.getElementById('livePreview');
  if (!checkbox.checked) return;

  var input = document.getElementById('comment');
  if (input.value != prev_value) {
    var output = document.getElementById('preview');

    try {
      output.innerHTML = sanitize(input.value);
    } catch(err) {
      output.innerHTML = input.value.xescape();
    }

    prev_value = input.value;
  }
  setTimeout(live_preview,500);
}


if (document.addEventListener) {
    onDOMLoad = function() {
      var cookie = readCookie("livePreview");
      var checkbox = document.getElementById('livePreview');
      if (cookie && cookie == 'true') {
        checkbox.checked = true;
        live_preview();
      }
      checkbox.onclick = function() {
        var checkbox = document.getElementById('livePreview');
        if (!checkbox) return;
        createCookie("livePreview", checkbox.checked?'true':'false', 365);
        if (checkbox.checked) setTimeout(live_preview,500);
      }
    };
    document.addEventListener("DOMContentLoaded", onDOMLoad, false);
}

