function synhl_escape(s) {
	var res = [];
	s = s.replace(/&lt;/gi, '<').replace(/&gt;/gi, '>').replace(/&amp;/gi, '&');
	for (var i = 0; i < s.length; ++i) {
		res.push('&#' + s.charCodeAt(i) + ';');
	}
	return res.join('');
}

function synhl_code(obj) {
}

function synhl_grammar(obj, genlink) {
	var html = obj.innerHTML;

	html = html.replace(/\(\*.*?\*\)/g,
		function (m) { return '<i>' + synhl_escape(m) + '</i>' } );
	html = html.replace(/"[^"]*"|'[^']*'/g,
		function (m) { return '<u>' + synhl_escape(m) + '</u>' } );
	html = html.replace(/(\[\^?)([^\]]+)(\])/g,
		function (_, p, q, r) { return p + '<u>' + synhl_escape(q) + '</u>' + r } );

	if (genlink) {
		html = html.replace(/([a-z0-9]+(?:-[a-z0-9]+)*)(?:(\s*::=)|(?![0-9]*[;>]))/gi,
			function (_, ident, defn) {
				frag = 'grammar-' + ident;
				if (defn) return '<b id="' + frag + '">' + ident + '</b>' + defn;
				return '<a href="#' + frag + '">' + ident + '</a>';
			});
	} else {
		html = html.replace(/([a-z0-9-]+)(?=\s*::=)/gi, '<b>$1</b>');
	}

	// post processing (for MSIE)
	html = html.replace(/\n/g, '&nbsp;<br/>');
	html = html.replace(/\t/g, '&nbsp; &nbsp; &nbsp; ');
	obj.innerHTML = html;
}

window.onload = function() {
	// generate TOC.
	var elems = document.body.childNodes;
	var result = '';
	var prevlevel = 2;
	for (var i = 0; i < elems.length; ++i) {
		if (elems[i].nodeType == 1 && elems[i].tagName.match(/^h[2-6]$/i) && elems[i].id) {
			var level = parseInt(elems[i].tagName.charAt(1));
			if (prevlevel < level) {
				for (; prevlevel < level; ++prevlevel) result += '<ul>';
			} else {
				for (; prevlevel > level; --prevlevel) result += '</li></ul>';
				if (result) result += '</li>';
			}
			result += '<li>';
			var html = elems[i].innerHTML;
			html = html.replace(/<span>/i, '<a href="#' + elems[i].id + '">');
			html = html.replace(/<\/span>/i, '</a>');
			result += html;
		}
	}
	var level = 2;
	for (; prevlevel > level; --prevlevel) result += '</li></ul>';
	result += '</li>';
	document.getElementById('toc').innerHTML = result;

	// syntax highlighting.
	var elems = document.getElementsByTagName('pre');
	for (var i = 0; i < elems.length; ++i) {
		if (elems[i].className == 'code') {
			synhl_code(elems[i]);
		} else if (elems[i].className == 'grammar') {
			synhl_grammar(elems[i], elems[i].parentNode.className != 'example');
		}
	}

	// add additional <div> node in all list items.
	var elems = document.getElementsByTagName('li');
	for (var i = 0; i < elems.length; ++i) {
		var div = document.createElement('div');
		while (elems[i].hasChildNodes()) {
			div.appendChild(elems[i].removeChild(elems[i].firstChild));
		}
		elems[i].appendChild(div);
	}
}
