window.onload = function()
{
    prepareLinks();
    preparePlaceholder();
    prepareGallery();
    displayAbbreviations();
    displayCitations();
    displayAccesskey();
    styleElementSiblings("h1", "intro");
    stripeTable();
    prepareSlideShow("linklist");
    pullquote();
    prepareForms();
    focusLabels();

    sortTableRows();
    alternateRowColors();
}

function prepareLinks()
{
    if (!supportIdTag())
        return false;

    var links = document.getElementsByTagName("a");
    for (var i = 0; i < links.length; i++) {
        if (links[i].className == "popup") {
            links[i].onclick = function() {
                popUp(this.getAttribute("href"));
                return false;
            }
        }
    }
}

function popUp(winURL)
{
    window.open(winURL, "popup", "width=320,height=480");
}

function prepareGallery()
{
    if (!supportIdTag())
        return false;

    var gallery = document.getElementById("imagegallery");
    if (!gallery)
        return false;
    var links = gallery.getElementsByTagName("a");

    for (var i = 0; i < links.length; i++) {
        links[i].onclick = function() {
            return showPic(this);
        }
    }
}

function showPic(img)
{
    var placeholder = document.getElementById("placeholder");
    if (!placeholder)
        return true;
    if (placeholder.nodeName.toLowerCase() != "img")
        return true;

    var source = img.getAttribute("href");      /* img.href */
    placeholder.setAttribute("src", source);    /* placeholder.src = source */

    var description = document.getElementById("description");
    if (!description)
        return false;

    var text = img.getAttribute("title");
    if (!text)
        text = "";

    if (description.firstChild.nodeType == 3)
        description.firstChild.nodeValue = text;

    return false;
}

function preparePlaceholder()
{
    if (!supportIdTag() || !supportCreate())
        return false;

    var gallery = document.getElementById("imagegallery");
    if (!gallery)
        return false;

    var placeholder = document.createElement("img");
    placeholder.setAttribute("id", "placeholder");
    placeholder.setAttribute("src", "images/placeholder.gif");  /* relative to HTML!!! */
    placeholder.setAttribute("alt", "my image gallery");

    var description = document.createElement("p");
    description.setAttribute("id", "description");
    var desctext = document.createTextNode("Choose an image");
    description.appendChild(desctext);

    insertAfter(placeholder, gallery);
    insertAfter(description, placeholder);
}

function displayAbbreviations()
{
    if (!supportIdTag() || !supportCreate())
        return false;

    var abbrs = document.getElementsByTagName("abbr");
    var num_nodes = abbrs.length;
    if (num_nodes < 1)
        return false;

    var defs = new Array();     /* as array, not object: defs.length does not work! */
    var num = 0;
    for (var i = 0; i < num_nodes; i++) {
        var k = abbrs[i];
        if (k.childNodes.length < 1)    /* IE does not have 'abbr' tag */
            continue;
        var value = k.getAttribute("title");
        if (value) {
            var key = k.lastChild.nodeValue;
            defs[key] = value;
            num++;
        }
    }
    if (!num)
        return false;

    var dlist = document.createElement("dl");
    for (key in defs) {
        var value = defs[key];

        var dt = document.createElement("dt");
        var dt_text = document.createTextNode(key);
        dt.appendChild(dt_text);

        var dd = document.createElement("dd");
        var dd_text = document.createTextNode(value);
        dd.appendChild(dd_text);

        dlist.appendChild(dt);
        dlist.appendChild(dd);
    }

    var header = elementWithText("h2", "Abbreviations");
    var container = getBody("container");
    container.appendChild(header);
    container.appendChild(dlist);
}

function displayCitations()
{
    if (!supportIdTag() || !supportCreate())
        return false;

    var quotes = document.getElementsByTagName("blockquote");
    for (var i = 0; i < quotes.length; i++) {
        var url = quotes[i].getAttribute("cite");
        if (!url)
            continue;

        var elem = lastChildElement(quotes[i]);
        if (!elem)
            continue;

        var link = document.createElement("a");
        var link_text = document.createTextNode("source");
        link.appendChild(link_text);
        link.setAttribute("href", url);

        var superscript = document.createElement("sup");
        superscript.appendChild(link);

        elem.appendChild(superscript);
    }
}

function displayAccesskey()
{
    if (!supportIdTag() || !supportCreate())
        return false;

    var links = document.getElementsByTagName("a");
    var num_nodes = links.length;
    if (num_nodes < 1)
        return false;

    var akeys = new Array();
    var num = 0;
    for (var i = 0; i < num_nodes; i++) {
        var k = links[i];
        var key = k.getAttribute("accesskey");
        if (!key)
            continue;
        var text = k.lastChild.nodeValue;
        akeys[key] = text;
        num++;
    }
    if (!num)
        return false;

    var list = document.createElement("ul");
    for (key in akeys) {
        var text = akeys[key];
        var str = key + ": " + text;

        var item = document.createElement("li");
        var item_text = document.createTextNode(str);
        item.appendChild(item_text);

        list.appendChild(item);
    }

    var header = elementWithText("h3", "Accesskeys");
    var container = getBody("container");
    container.appendChild(header);
    container.appendChild(list);
}

function styleElementSiblings(tag, theClass)
{
    if (!supportIdTag())
        return false;

    var elems = document.getElementsByTagName(tag);
    for (var i = 0; i < elems.length; i++) {
        var elem = getNextElement(elems[i].nextSibling);
        if (!elem)
            continue;
        addClass(elem, theClass);
    }
}

function stripeTable()
{
    if (!supportIdTag())
        return false;

    var tables = document.getElementsByTagName("table");
    for (var i = 0; i < tables.length; i++) {
        var odd = false;
        var rows = tables[i].getElementsByTagName("tr");
        for (var j = 0; j < rows.length; j++) {
            if (odd) {
                addClass(rows[j], "odd");
                odd = false;
            } else
                odd = true;
        }
    }
}

function prepareSlideShow(linkId)
{
    if (!supportIdTag())
        return false;

    var list = document.getElementById(linkId);
    if (!list)
        return false;

    var slideshow = document.createElement("div");
    slideshow.setAttribute("id", "slideshow");

    var previewId = "preview";
    var preview = document.createElement("img");
    preview.setAttribute("src", "images/topics.gif");
    preview.setAttribute("alt", "building blocks of web design");
    preview.setAttribute("id", previewId);

    slideshow.appendChild(preview);
    insertAfter(slideshow, list);

    var links = list.getElementsByTagName("a");

    /* can't use a for-loop... */
    var ypos = 0;
    var interval = 10;
    links[0].onmouseover = function() {
        moveElement(previewId, -100, ypos, interval);
        return false;
    }
    links[1].onmouseover = function() {
        moveElement(previewId, -200, ypos, interval);
        return false;
    }
    links[2].onmouseover = function() {
        moveElement(previewId, -300, ypos, interval);
        return false;
    }
}

function addLoadEvent(func)
{       /* does not support func arguments */
    var oldonload = window.onload;
    if (typeof window.onload != "function")
        window.onload = func;
    else
        window.onload = function() {
            oldonload();
            func();
        }
}

function insertAfter(newElement, targetElement)
{
    var parent = targetElement.parentNode;
    if (parent.lastChild == targetElement)
        parent.appendChild(newElement);
    else
        parent.insertBefore(newElement, targetElement.nextSibling);
}

function getBody(id)
{
    var elem = document.getElementById(id);
    if (!elem)  /* use body */
        elem = document.getElementsByTagName("body")[0];        /* document.body */
    return elem;
}

function lastChildElement(targetElement)
{
    if (targetElement.nodeType != 1)    /* not an element node */
        return false;

    var childElements = targetElement.getElementsByTagName("*");
    var num_nodes = childElements.length;
    if (num_nodes < 1)
        return false;
    else
        return childElements[num_nodes - 1];
}

function getNextElement(node)
{
    if (node.nodeType == 1)
        return node;
    if (node.nextSibling)
        return getNextElement(node.nextSibling);
    return null;
}

function addClass(element, theClass)
{
    if (!element.className)
        element.className = theClass;
    else
        element.className += " " + theClass;
}

function delClass(element, theClass)
{
    var str = "\s*" + theClass;
    var re = new RegExp(str, "g");
    element.className = element.className.replace(re, "");
}

function supportIdTag()
{
    if (!document.getElementById)
        return false;
    if (!document.getElementsByTagName)
        return false;
    return true;
}

function supportCreate()
{
    if (!document.createElement)
        return false;
    if (!document.createTextNode)
        return false;
    return true;
}

function elementWithText(targetElement, str)
{
    var elem = document.createElement(targetElement);
    var elem_text = document.createTextNode(str);
    elem.appendChild(elem_text);
    return elem;
}

function moveElement(elementId, final_x, final_y, interval)
{
    if (!supportIdTag())
        return false;
    var elem = document.getElementById(elementId);
    if (!elem)
        return false;
    if (elem.movement)
        clearTimeout(elem.movement);

    if (!elem.style.left)
        elem.style.left = "0px";
    if (!elem.style.top)
        elem.style.top = "0px";

    var xpos = parseInt(elem.style.left);
    var ypos = parseInt(elem.style.top);
    if (xpos == final_x && ypos == final_y)
        return true;

    var step = 5;
    xpos = getCurrentPos(xpos, final_x, step);
    ypos = getCurrentPos(ypos, final_y, step);

    elem.style.left = xpos + "px";
    elem.style.top = ypos + "px";

    var repeat = "moveElement('" + elementId + "'," + final_x + ","
        + final_y + "," + interval + ")";
    elem.movement = setTimeout(repeat, interval);
}

function getCurrentPos(cpos, fpos, step)
{
    if (cpos < fpos)
        cpos += Math.ceil((fpos - cpos) / step);
    if (cpos > fpos)
        cpos -= Math.ceil((cpos - fpos) / step);
    return cpos;
}

function pullquote()
{
    if (!supportIdTag() || !supportCreate())
        return false;

    var all_spans = document.getElementsByTagName("span");
    var pullquoteClass = new RegExp("(^|\\s)pullquote(\\s|$)", "i");
    for (var i = 0; i < all_spans.length; i++) {
        var span = all_spans[i];
        if (pullquoteClass.test(span.className)) {
            var blockquote = document.createElement("blockquote");
            blockquote.className = span.className;
            blockquoteP = document.createElement("p");
            for (j = 0; j < span.childNodes.length; j++)
                blockquoteP.appendChild(span.childNodes[j].cloneNode(true));
            blockquote.appendChild(blockquoteP);
            var sp = span.parentNode;
            sp.parentNode.insertBefore(blockquote, sp);
        }
    }
}

/* here are som global variables */
var tables = document.getElementsByTagName("table");
var lastTableNumber;
var lastColumnNumber;
var lastOrder;

function alternateRowColors()
{
    if (!supportIdTag())
        return;

    for (var i = 0; i < tables.length; i++) {
        var table = tables[i];
        var tbodies = table.getElementsByTagName("tbody");
        for (var j = 0; j < tbodies.length; j++) {
            var tbody = tbodies[j];
            rows = tbody.getElementsByTagName("tr");
            addStripeHighlight(rows, false);
        }
    }
}

function addStripeHighlight(rows, tbody)
{
    var even = false;
    for (var k = 0; k < rows.length; k++) {
        var row = rows[k];
        row.onmouseover = function() {
            addClass(this, "ruled");
            return false;
        }
        row.onmouseout = function() {
            delClass(this, "ruled");
            return false;
        }
        delClass(row, "odd");
        delClass(row, "even");
        addClass(row, (even) ? " even" : " odd");
        even = !even;
        if (tbody)
            tbody.appendChild(row);
    }
}

function sortTableRows()
{
    if (!supportIdTag())
        return false;

    for (var i = 0; i < tables.length; i++) {
        var table = tables[i];
        var headerRow = getHeaderRow(table);
        if (!headerRow)
            continue;
        var columns = headerRow.getElementsByTagName("th");
        for (var j = 0; j < columns.length; j++) {
            var column = columns[j];
            var attrValue = "sortColumn(" + i + ", " + j + ");";
            column.setAttribute("onclick", attrValue);
            /* column.onclick = attrValue; -- does not work: maybe eval? */
        }
    }
}

function sortColumn(tableNumber, columnNumber)
{
    var order = "ascending";
    if (lastTableNumber == tableNumber &&
        lastColumnNumber == columnNumber && lastOrder == "ascending")
        order = "descending";

    var table = tables[tableNumber];
    var tbodies = table.getElementsByTagName("tbody");
    if (tbodies.length < 1)
        return false;

    for (var i = 0; i < tbodies.length; i++) {
        var tbody = tbodies[i];
        var rows = tbody.getElementsByTagName("tr");

        var rowData = new Array();
        for (var j = 0; j < rows.length; j++) {
            var row = rows[j];
            var tds = row.getElementsByTagName("td");
            var data = tds[columnNumber].firstChild.nodeValue;
            rowData[rowData.length] = new Array(j, data);       /* j as index */
        }

        var rowDataSorted = new Array();
        if (isValidNumber(rowData[0][1]))       /* 1st row, 2nd column data */
            rowDataSorted = rowData.sort(numeric);
        else
            rowDataSorted = rowData.sort(normal);

        var rowToBeRemoved = new Array();
        var rowToBeInserted = new Array();
        for (var j = 0; j < rowDataSorted.length; j++) {
            var originalRow = rows[rowDataSorted[j][0]];
            rowToBeInserted.push(originalRow.cloneNode(true));
            rowToBeRemoved.push(originalRow);
        }

        if (order == "descending")
            rowToBeInserted.reverse();

        addStripeHighlight(rowToBeInserted, tbody);

        for (var j = 0; j < rowToBeRemoved.length; j++)
            tbody.removeChild(rowToBeRemoved[j]);
    }

    var headerRow = getHeaderRow(table);        /* surely exist from sortTableRows() */
    var columns = headerRow.getElementsByTagName("th");
    for (var i = 0; i < columns.length; i++) {
        var column = columns[i];
        delClass(column, order);
        if (i == columnNumber)
            addClass(column, order);
    }

    lastTableNumber = tableNumber;
    lastColumnNumber = columnNumber;
    lastOrder = order;
}

function numeric(foo, bar)
{
    var valueOne = foo[1];      /* 2nd column data */
    var valueTwo = bar[1];
    return valueOne - valueTwo;
}

function normal(foo, bar)
{
    var valueOne = foo[1];      /* 2nd column data */
    var valueTwo = bar[1];
    if (valueOne < valueTwo)
        return -1;
    else if (valueOne == valueTwo)
        return 0;
    else
        return 1;
}

function isValidNumber(inpString)
{
    return /^[-+]?\d+(\.\d+)?([eE][-+]\d+)?$/.test(inpString);
}

function getHeaderRow(table)
{
    var thead = table.getElementsByTagName("thead");
    if (thead.length < 1)       /* no "thead" tag */
        return false;
    var headerRow = thead[0].getElementsByTagName("tr")[0];
    if (!headerRow)     /* no "tr" row in header */
        return false;
    return headerRow;
}

function ltrim(value) {
    var re = /\s*((\S+\s*)*)/;
    return value.replace(re, "$1");
}

function rtrim(value) {
    var re = /((\s*\S+)*)\s*/;
    return value.replace(re, "$1");
}

function trim(value) {
    return ltrim(rtrim(value));
}

/* ==================== for form validation ==================== */
function focusLabels() 
{
    if (!supportIdTag())
        return false;

    var labels = document.getElementsByTagName("label");
    for (var i = 0; i < labels.length; i++) {
        if  (!labels[i].getAttribute("for"))
            continue;
        labels[i].onclick = function() {
            var id = this.getAttribute("for");
            var elem = document.getElementById(id);
            if (!elem) return false;
            elem.focus();
        }
    }
}

function resetFields(whichform) 
{
    for (var i = 0; i < whichform.elements.length; i++) {
        var elem = whichform.elements[i];
        if (elem.type == "submit")
            continue;
        if (!elem.defaultValue)
            continue;
        elem.onfocus = function() {
            if (this.value == this.defaultValue) {
                /* this.value = ""; */
            }
        }
        elem.onblur = function() {
            if (this.value == "") {
                this.value = this.defaultValue;
            }
        }
    }
}

function isFilled(field) 
{
    if (field.value.length < 1 || field.value == field.defaultValue)
        return false;
    else
        return true;
}

function isEmail(field) 
{
    if (field.value.indexOf("@") == -1 || field.value.indexOf(".") == -1)
        return false;
    else
        return true;
}

function validateForm(whichform) 
{
    for (var i = 0; i < whichform.elements.length; i++) {
        var elem = whichform.elements[i];
        if ((elem.className.indexOf("required") != -1) &&
            !isFilled(elem)) {
            alert("Please fill in the " + elem.name + " field.");
            return false;
        }
        if ((elem.className.indexOf("email") != -1) &&
            !isEmail(elem)) {
            alert("The " + elem.name + " field must be a valid email address.");
            return false;
        }
    }
    return true;
}

function prepareForms()
{
    for (var i = 0; i < document.forms.length; i++) {
        var thisform = document.forms[i];
        resetFields(thisform);
        thisform.onsubmit = function() {
            return validateForm(this);
        }
    }
}
