/**
 * Functions and classes for e-Grip CMS front end
 * @version $Id: dom_common.js,v 1.5 2007/05/02 07:36:35 kruithof Exp $
 */

/**
 * Gets the nodeValue for a child node with a given name
 * @access public
 * @param Node node the parent node
 * @param string subNodeName the child node's name
 * @return string the subnode's value if existing, otherwise false
 */
function getSubNodeValue(node, subNodeName)
{
    var value = false;
    if ((node.nodeType == 1) && (node.hasChildNodes())) {
        for (var i = 0; i < node.childNodes.length; i++) {
            childNode = node.childNodes.item(i);
            if ((childNode.nodeType == 1) && (childNode.tagName == subNodeName)) {
                value = getNodeValue(childNode);
            }
        }
    }
    return value;
}

/**
 * Gets node value, searches in child nodes if node is not textnode.
 */
function getNodeValue(node)
{
    var content = '';

    if (node.hasChildNodes()) { // node contains CDATA
        for (var i = 0; i < node.childNodes.length; i++) {
            content = getNodeValue(node.childNodes[i]);
        }
    } else {
        content = node.nodeValue;
    }
    return content;
}

/**
 * Sets node value, searches in first childnode if node is not textnode.
 */
function setNodeValue(node, value)
{
    if (node.hasChildNodes() && (node.childNodes.length == 1) && (node.firstChild.nodeType == 3)) { // node has one subnode, which is a textnode
        setNodeValue(node.firstChild, value);
    } else if (node.nodeType == 3) {
        node.nodeValue = value;
    }
}

/**
 * Sets class attribute, makes sure IE's proprietary 'className' is supported
 * @deprecated Deprecated. Use setAttribute instead.
 */
function setClassName(node, classname)
{
    if (node.setAttribute('class', classname)) {
        return true;
    } else if (node.setAttribute('className', classname)) {
        return true;
    }
    return false;
}

/**
 * Sets for attribute, makes sure IE's proprietary 'htmlFor' is supported
 * @deprecated Deprecated. Use setAttribute instead.
 */
function setForAttribute(node, value)
{
    if (node.setAttribute('for', value)) {
        return true;
    } else if (node.setAttribute('htmlFor', value)) {
        return true;
    }
    return false;
}

/**
 * Adds text node to node
 */
function addTextNode(node, text)
{
    var txt = document.createTextNode(text);
    node.appendChild(txt);
    return txt;
}

/**
 * Removed all children from node
 */
function removeChildren(node)
{
    while (node.firstChild) node.removeChild(node.firstChild);
}

/**
 * Removed node from its parent
 */
function removeNode(node)
{
    node.parentNode.removeChild(node);
}

/**
 * See if node matches given nodename
 */
function nodeHasName(node, nodeName)
{
    return (node.nodeName.toLowerCase() == nodeName);
}

/**
 * Clones a node to given document
 * @param Document doc Document to clone the node to
 * @param Node node The node to clone
 * @param Node dupNode The duplicate node
 * @param Boolean deep Boolean to indicate deep recursion
 */
function cloneNode(doc, node, dupNode, deep)
{
    // create new element
    if (!dupNode) {
        if (in_array(node.nodeType, [3,4,8])) {
            dupNode = doc.createTextNode(node.nodeValue);
        } else {
            dupNode = doc.createElement(node.nodeName);
        }
    }
    if (!deep) deep = true;

    // clone attributes
    if ((node.attributes != null) || node.hasAttributes()) {
        cloneNodeAttributes(dupNode, node.attributes);
    }

    // clone children
    if (node.hasChildNodes()) {

        var tmp_node;

        for (var i = 0; i < node.childNodes.length; i++) {
            if (node.childNodes[i].nodeType == 1) {

                tmp_node = doc.createElement(node.childNodes[i].nodeName);

                if ((node.childNodes[i].attributes != null) || node.childNodes[i].hasAttributes()) {
                    cloneNodeAttributes(tmp_node, node.childNodes[i].attributes);
                }

                if (node.childNodes[i].hasChildNodes() && (deep == true)) {
                    tmp_node = cloneNode(doc, node.childNodes[i], tmp_node, deep);
                }

            } else if (in_array(node.childNodes[i].nodeType, [3,4,8])) {
                tmp_node = doc.createTextNode(node.childNodes[i].nodeValue);
            }
            dupNode.appendChild(tmp_node);
        }
    }

    return dupNode;
}

/**
 * Clones attributes to given node
 */
function cloneNodeAttributes(node, attributes)
{
    for (var i = 0; i < attributes.length; i++) {
        if (attributes[i].name == 'class') {
            setClassName(node, attributes[i].value);
        } else if (attributes[i].name == 'for') {
            setForAttribute(node, attributes[i].value);
        } else {
            node.setAttribute(attributes[i].name, attributes[i].value);
        }
    }
}

/**
 * This is what insertAfter would do, if there was a specification for it (similar to insertBefore, which does exist)
 * @param Node node The new node to insert
 * @param Node referenceNode The node to insert it after
 * @return Node insertedNode The inserted node
 */
function insertAfter(node, referenceNode)
{
    var insertedNode;
    var parentNode = referenceNode.parentNode;

    if (referenceNode == parentNode.lastSibling) {
        insertedNode = parentNode.appendChild(node);
    } else {
        var sibling = referenceNode.nextSibling;
        insertedNode = parentNode.insertBefore(node, sibling);
    }
    return insertedNode;
}

/**
 * Sets attribute, makes sure IE's proprietary 'className' and 'htmlFor' are supported
 * @param DOMNode node The node to apply attribute on
 * @param String name The attribute name
 * @param String value The attribute value
 * @return void
 */
function setAttribute(node, name, value)
{
    if (name == 'class') {
        node.className = value;
    } else if (name == 'for') {
        node.htmlFor = value;
    } else {
        node.setAttribute(name, value);
    }
}

/**
 * Returns html tag (with nested children if applicable) as a dom node
 * @param string tagName The tag name
 * @param array attributes Associative array holding attributes
 * @param string|array contents (optional) Can be either string or array:
 *                 - If passed as string, value will be used as nodeValue
 *                 - If passed as array, function will recursively include them as child nodes.
 *                   Each element can contain three elements: 'tagname' and optional 'attributes' and 'contents'
 * @return HTMLNode The created node with children appended to it
 */
function getHTMLNode(tagName, attributes, contents)
{
    var node = document.createElement(tagName);

    for (var attr in attributes) {
        setAttribute(node, attr, attributes[attr]);
    }

    if ((typeof contents) == 'string') {
        node.appendChild(document.createTextNode(contents));
    } else if ((typeof contents) == 'object') {
        for (var i = 0; i < contents.length; i++) {
            var subconfig = contents[i];
            var subnode = getHTMLNode(subconfig[0], subconfig[1], subconfig[2] ? subconfig[2] : []);
            node.appendChild(subnode);
        }
    }

    return node;
}

/**
 * Returns string representing the node (and child nodes)
 * Useful if node must be added using document.write() for some reason
 * @param HTMLNode node The node to convert to string
 * @return String The node as string
 */
function nodeToString(node)
{
    var str = "";

    if (node.nodeType == 3) { // text node
        str = getNodeValue(node);
    } else if (node.nodeType == 1) {
        // add node name
        str = "<" + node.tagName;

        // add attributes
        if (node.hasAttributes()) {
            for (var i = 0; i < node.attributes.length; i++) {
                str = str + " " + node.attributes[i].name + "=\"" + node.attributes[i].value + "\"";
            }
        }

        // add child nodes if necessary
        if (node.childNodes.length > 0) {

            // close node
            str = str + ">";

            // add child nodes
            for (var i = 0; i < node.childNodes.length; i++) {
                str = str + nodeToString(node.childNodes.item(i));
            }

            // add end node
            str = str + "</" + node.tagName + ">";

        } else {
            // fold and close node
            str = str + "/>";
        }

    }

    return str;
}