
// For every element in root with an id, add a property to target
// with the id as the name and the element as the value.
function setIdProperties(target, root)
{
    var nodes = root.getElementsByTagName('*');
    foreach(nodes, function (node) {
	if (node.id != null && node.id != '')
	    target[node.id] = node;
    });
}

// Call fn on each object in objects.
function foreach(objects, fn)
{
    for (var i = 0, l = objects.length; i < l; ++i) {
        fn(objects[i]);
    }
}

/// Create a text node containing TEXT and append it to NODE.  Set
/// NODE.text to the text node.
function appendTextNode(node, text)
{
    node.text = document.createTextNode(text);
    node.appendChild(node.text);
}

/// Given a TABLE, add rows and cells to make it a table for displaying
/// 256 consecutive characters. Set TABLE.cells to an Array of the 256
/// cells that will contain the characters. Each cell has a property
/// named "text" that refers to the text node in the cell.
function makeCharTable(table)
{
    table.cells = new Array();

    function addColHeaderRow(table) {
	var row = table.tBodies[0].insertRow(table.rows.length);
	var cell = row.insertCell(0);
	for (var i = 0; i < 16; ++i) {
	    cell = row.insertCell(i + 1);
	    cell.className = 'columnLabel';
	    appendTextNode(cell, i.toString(16));
	}
    }

    function addCharRow(table, i) {
	var row = table.tBodies[0].insertRow(table.rows.length);
	var cell = row.insertCell(0);
	cell.className = 'rowLabel';
	appendTextNode(cell, i.toString(16));

	for (var j = 0; j < 16; ++j) {
	    cell = row.insertCell(j + 1);
	    cell.className = 'charCell';
	    appendTextNode(cell, 'X');
	    table.cells.push(cell);
	}
    }

    addColHeaderRow(table);
    for (var i = 0; i < 16; ++i) {
	addCharRow(table, i);
    }
}

/// Given a TABLE set up by makeCharTable, fill it with the characters
/// starting at C * 256.
function fillCharTable(table, c)
{
    c *= 256;
    for (var i = 0; i < 256; ++i) {
	table.cells[i].text.replaceData(0, 100, String.fromCharCode(c + i));
	table.cells[i].title = '&#' + (c + i) + ';';
    }
}

window.addEventListener('load', function () {

    charTable = document.getElementById('charTable');

    setIdProperties(self, document);

    /// Modify my URI to reflect whatever page I'm currently showing.
    function saveState() {
	var newState = 'page=' + encodeURIComponent(pageNumber().toString(16));
	var oldState = location.hash.replace(/^#/, '');
	if (newState != oldState)
	    location.hash = newState;
    }

    /// Extract a page number from the URI and show that page.
    function loadState() {
	var pn = 0;
	var defs = location.hash.replace(/^#/, '').split('&');
	foreach(defs, function (def) {
	    if (def.match(/^page=/)) {
		pn = parseInt(def.replace(/^page=/, ''), 16);
	    }
	});
	setPage(pn);
    }

    /// Return the current page number according to the value in
    /// the page number box.
    function pageNumber() {
	return parseInt(pageNumberBox.value, 16);
    }

    /// Set the current page to PN, updating the page number box in
    /// the process.
    function setPage(pn) {
	pageNumberBox.value = pn.toString(16);
	showPage();
    }

    /// Show whatever page is currently selected in the page number box.
    function showPage() {
	saveState();
	fillCharTable(charTable, pageNumber());
    }

    pageNumberBox.onchange =
    pageNumberBox.onkeyup = function () {
	showPage();
    }

    pageLeftButton.onclick = function () {
	var pn = pageNumber() - 1;
	if (pn < 0) pn = 0;
	setPage(pn);
    }

    pageRightButton.onclick = function () {
	var pn = pageNumber() + 1;
	setPage(pn);
    }

    var cells = makeCharTable(charTable);
    loadState();

}, false);

