/*
 * domUtils.js
 *
 * JavaScript library consisting of a single object containing a number of
 * methods that fix browser-specific (read: MSIE) inconsitencies in the W3C
 * described Document Object Model (DOM).
 *
 * W3C DOM specifications can be found at:
 *  http://www.w3.org/DOM
 *
 *  This object adds the following methods:
 *
 *  Node Methods:
 *      .lastSibling(node)
 *      .firstSibling(node)
 *      .closestSibling(node)
 *      .getText
 *      .setText(txt)
 *      .createLink(url, txt)
 *      .createTextElement(elm, txt)
 *
 *      .initDebug
 *      .setDebug(txt)
 *      .stopDebug
 *
 *      .getTarget(e)
 *      .cancelClick(e)
 *      .addEvent(elm, evType, fn, userCapture)
 *
 *  Node Properties:
 *      .debugWindowID: name of debug <div></div> id attribute
 *
 *  --------------------------------------------------------------------------
 *
 * Standard DOM methods:
 *
 *  Retrieving Nodes:
 *      document.getElementById()
 *      document.getElementsByTagName()
 *
 *  Node Properties:
 *      .nodeName (tag or #text)
 *      .nodeType (1=element node, 3=text node)
 *      .nodeValue (null if element, text content if text node)
 *      .childNodes (array)
 *      .parentNode
 *      .firstChild
 *      .lastChild
 *      .nextSibling
 *      .previousSibling
 *
 *  Node Methods:
 *      .hasChildNodes()
 *      .insertBefore()
 *      .replaceChild()
 *
 */

dom={

    // Properties ------------------------------------------------------------
    debugWindowID: 'debug', // id for debugging console

    // Methods ---------------------------------------------------------------

    /*
     * init()
     * Checks for W3C DOM support and allows for subsequent helper methods to
     * be loaded.
     */
    init:function() {
         if (!document.getElementById || !document.getElementsByTagName) {
             return;
         }
    },

    /*
     * lastSibling(node)
     * Returns the last sibling of the specified node that is not a text node
     * (#text).  Returns false on failure.
     */
    lastSibling:function(node){

        var obj = node.parentNode.lastChild;
        if (!obj) {
            return false;
        }

        while (obj.nodeType != 1 && obj.previousSibling != null) {
            obj = obj.previousSibling;
        }

        if (obj.NodeType != 1) {
            return false;
        }

        return obj;

    },

    /*
     * firstSibling(node)
     * Returns the first sibling of the specified node that is not a text node
     * (#text).  Returns false on failure.
     */
    firstSibling:function(node){

         var obj = node.parentNode.firstChild;
         if (!obj) {
             return;
         }

         // Element nodes have nodeType==1
         while (obj.nodeType != 1 && obj.nextSibling != null) {
             obj = obj.nextSibling;
         }

         if (obj.nodeType != 1) {
             return false;
         }

         return obj;

     },

    /*
     * closestSibling(node, dir)
     * If dir == 1, returns the next non-text sibling node.
     * If dir == -1, returns the previous non-text sibling node.
     * Returns false if the sibling does not exist.
     */
    closestSibling:function(node, dir) {

       var obj;

       // previous sibling
       if (dir == -1 && node.previousSibling != null) {
           obj = node.previousSibling;
           while (obj.nodeType != 1 && obj.previousSibling != null) {
               obj = obj.previousSibling;
           }
       }
       // next sibling
       else if (dir == 1 && node.nextSibling != null) {
           obj = node.nextSibling;
           while (obj.nodeType != 1 && obj.nextSibling != null) {
               obj = obj.nextSibling;
           }
       }

       if (obj.nodeType != 1) {
           return false;
       }

       // Return the sibling
       return obj;

    },

     /*
      * getText(node)
      * Returns the text contents of the first text node of an element.
      * Returns false on failure.
      */
    getText:function(node) {

        // Make sure the specified node has at least one child (the first
        // child of the node is normally the element text
        if (!node.hasChildNodes()) {
            return false;
        }

        var obj = node.firstChild;

        // Some browsers treat newlines as text nodes.  These nodes have
        // whitespace characters as it's .nodeValue property.
        var reg = /^\s+$/;

        // Text Nodes have nodeType==3
        while (obj.nodeType != 3
                && obj.nextSibling != null
                || reg.test(obj.nodeValue)) {
            obj = obj.nextSibling;
        }

        if (obj.nodeType != 3) {
            return false;
        }

        return obj.nodeValue;

    },

    /*
     * setText(node, txt)
     * Sets the node.nodeValue to the string contained in text and returns
     * true.  Returns false on failure.
     */
    setText:function(node, txt) {

        // Make sure the specified node has at least one child (the first
        // child of the node is normally the element text
        if (!node.hasChildNodes()) {
            return false;
        }

        var obj = node.firstChild;

        // Some browsers treat newlines as text nodes.  These nodes have
        // whitespace characters as it's .nodeValue property.
        var reg = /^\s+$/;

        // text nodes have nodeType==3
        if (obj.nodeType != 3
                && obj.nextSibling != null
                || reg.test(obj.nodeValue)) {
            obj = obj.nextSibling;
        }

        if (obj.nodeType != 3) {
            return false;
        }

        // Set the node text
        obj.nodeValue = txt;
        return true;

    },

    /*
     * createLink(url, txt)
     * Creates a link to the url with the string contained in txt.
     */
    createLink:function(url, txt) {

       var obj = document.createElement('a');
       obj.appendChild(document.createTextNode(txt));
       obj.setAttribute('href', url);

       return obj;

    },

    /*
     * createTextElement(type, txt)
     * Creates an html element of type containing the string in txt.
     */
    createTextElement:function(type, txt) {

       var obj = document.createElement(type);
       obj.appendChild(document.createTextNode(txt));

       return obj;

    },
    // -----------------------------------------------------------------------

    // Methods for creation and use of the debugging console -----------------

    /*
     * initDebug()
     * Creates a console (enclosed in div tags) with the id
     * contained in .debugWindowID
     */
    initDebug:function() {

      if (dom.debugInit) {
          dom.stopDebug();
      }

      dom.debug = document.createElement('div');
      dom.debug.setAttribute('id', dom.debugWindowID);
      document.body.insertBefore(dom.debug, document.body.firstChild);

    },

    /*
     * setDebug(txt)
     * Displays the string contained in txt in the debugging console
     * created by initDebug().
     */
    setDebug:function(txt) {

         if (!dom.debug) {
             dom.initDebug();
         }

         dom.debug.innerHTML += txt + '\n';

    },

    /*
     * stopDebug()
     * Removes any existing debug console.
     */
    stopDebug:function() {

        if (!dom.debug) {
            return;
        }

        // Remove the debugging console div
        dom.debug.parentNode.removeChild(dom.debug);
        // Clear the node from the browser memory
        dom.debug = null;

    },

    // Event-handling methods ------------------------------------------------

    /*
     * getTarget(e)
     * Returns the node that triggered the event.  Accounts for differences
     * in registering events among all major browers.
     */
    getTarget:function(e) {

        // Retrieve the target depending on the user agent event model
        // MSIE: uses one global event object: window.event
        // Others: uses an event object for each listener
        var target;
        if (window.event) { //MSIE
            target = window.event.srcElement;
        }
        else if (e) { // W3C COM compliant user agent
            target = e.target;
        }
        else {
            alert('W3C');
            return false;
        }

        // Safari fix
        if (target.nodeName.toLowerCase() != 'a') {
            target = target.parentNode;
        }

        return target;

    },

    /*
     * cancelClick(e)
     * Prevents default action and event bubbling.
     */
    cancelClick:function(e) {

        if (window.event) { // MSIE
            window.event.cancelBubble = true;
            window.event.returnValue  = false;
        }
        if (e && e.stopPropagation && e.preventDefault) { // W3C DOM compliant
            e.stopPropagation();
            e.preventDefault();
        }

    },

    /*
     * addEvent(elm, evType, fn, useCapture)
     * Determines which event model is supported by the user agent
     * and attaches the event accordingly.
     */
    addEvent:function(elm, evType, fn, useCapture) {

         if (elm.addEventListener) {
             elm.addEventListener(evType, fn, useCapture);
             return true;
         }
         else if (elm.attachEvent) {
             var r = elm.attachEvent('on' + evType, fn);
             return r;
         }
         else {
             elm['on' + evType] = fn;
         }

    },

    // CSS methods -----------------------------------------------------------

    cssjs:function(action, elm, c1, c2) {

       switch (action) {
           /*
            * Test for the existence of class c1:
            * If it has not been assigned to the object, add it.
            * If c1 has been assigned to the object, replace it with c2.
            */
           case 'swap':
               if (dom.cssjs('check', elm, c1)) {
                   elm.className = elm.className.replace(c1,c2);
               }
               else {
                   elm.className = dom.cssjs('add', elm, c1);
               }
               break;
           /*
            * If c1 has not previously been added to the object, add it
            * (preceded by a whitespace if this object already has been
            * assigned a class.
            */
           case 'add':
               if (!dom.cssjs('check', elm, c1)) {
                   if (elm.className) {
                       elm.className += ' ' + c1;
                   }
                   else {
                       elm.className = c1;
                   }
               }
               break;
           /*
            * If the class (c1) is not the first class assigned to the object,
            * remove the class and the preceding whitespace.
            */
           case 'remove':
               var rep = elm.className.match(' ' + c1);
               if (rep) {
                   rep = ' ' + c1;
               }
               else {
                   rep = c1;
               }
               elm.className = elm.className.replace(rep, '');
               break;
           /*
            * Search for the class name (c1) preceeded by or followed by a
            * whitespace.  Return true on a match, false otherwise.
            */
           case 'check':
			   var found=false;
			   var temparray=elm.className.split(' ');
			   for(var i=0;i<temparray.length;i++){
			       if(temparray[i]==c1){found=true;}
			   }
			   return found;
               break;
       }

    }

}
// Initialize the object once the page has loaded.
dom.addEvent(window, 'load', dom.init, false);

