// htmlDom.js
// HTML DOM helper functions

window.htmlDOM = new Object();

/**
 * appendBody
 * Append the given DOM node to the body; optionally convert an HTML string
 * into a DOM node and append to the body.
 * mixed                     DOM node or HTML string to append to the body
 */
window.htmlDOM.appendBody = function(n){
  if(!n){
    n = "<p>htmlDOM.appendBody: n evaluates to false</p>";
    n = htmlDOM.DomFromHTML(n);
    if(!n){ return; }                  // Early return
  }
  if(typeof n == "string"){
    n = htmlDOM.DomFromHTML(n);
  }
  var body = htmlDOM.getBody();
  if(n && body){
    body.appendChild(n);
  }
}

/**
 * getBody
 * Return the body element or null on failure
 */
window.htmlDOM.getBody = function(){
  var body = null;
  try{
    body = document.getElementsByTagName("body")[0];
  }catch(e){
    ;
  }
  return body;
}

// duplicateValue
// Duplicate a control's value from one control to another
// src - source control
// dest - destination control
window.htmlDOM.duplicateValue = function(src, dest){
  try{
    if(src.type && dest.type && src.type == dest.type){
      switch(src.type){
        case 'text':
        case 'textarea':
        case 'password':
        case 'hidden':
          dest.value = src.value;
          break;
        case 'checkbox':
        case 'radio':
          dest.value = src.value;
          dest.checked = src.checked;
          break;
        case 'select-one':
        case 'select-multiple':
          for(var i = 0; i < src.options.length; i++){
            dest.options[i].value = src.options[i].value;
            dest.options[i].selected = src.options[i].selected;
          }
          break;
      }
    }
  }catch(e){
    alert("Error: htmlDOM: duplicateValue");
  }
  return;
}

// duplicateForm
// Given a form id / node, create another identical form and return it's DOM
// node.
// form - the form we wish to duplicate
// trim_ids - optional true to trim the IDs from the duped form's elements
//                     false to leave the IDs in place
//                     default true
// dup_values - optional true to duplicate the values in the form
//                       false to just create an identical form
//                       default true
window.htmlDOM.duplicateForm = function(form, trim_ids, dup_values ){
  var dup = null;
  if(trim_ids == null || trim_ids == undefined){
    trim_ids = true;
  }
  if(dup_values == null || dup_values == undefined){
    dup_values = true;
  }
  try{
    dup = form.cloneNode(true);
    if(trim_ids || dup_values){
      if(trim_ids && dup.id){
        dup.id = ""; // Trim the duped form's ID
      }
      for(var i = 0; i < form.elements.length; i++){
        // For each form element

        if(trim_ids && dup.elements[i].id){
          dup.elements[i].id = "";
        }
        if(dup_values){
          htmlDOM.duplicateValue(form.elements[i], dup.elements[i]);
        }
      }
    }
  }catch(e){
    alert("Error: htmlDOM: duplicateForm");
  }
  return dup;
}

// removeWhiteSpace
// Starting with n, move through siblings and remove each empty text node and
// each of the following tags: br
// Stop at the first non-br tag or non-empty text node
// n - the node to remove white space from
// inc_tags - bool true to include tags in stripped nodes, false to strip empty
//                 text nodes only
window.htmlDOM.removeWhiteSpace = function(n, inc_tags){
  if(!inc_tags){
    inc_tags = false;
  }
  try{
    var p = n.parentNode;
    for( ; n != null; n = next != null ? next : n.nextSibling ){
      var next = null;
      if(n.nodeType && n.nodeType == 8){
        continue; // skip comment nodes
      }else if(htmlDOM.isEmpty(n, inc_tags)){
        // Empty, so trim
        next = n.nextSibling;
        p.removeChild(n);
      }else{
        break;
      }
    }
  }catch(e){
    alert("Error: htmlDOM: removeWhiteSpace");
  }
  return;
}

// isEmpty
// Return true if the node is empty (a text node with all white space or one of
// the following tags: br)
// n - the node to check
// inc_tags - bool true to include tags in stripped nodes, false to strip empty
//                 text nodes only
window.htmlDOM.isEmpty = function(n, inc_tags){
  if(!inc_tags){
    inc_tags = false;
  }
  var ret = false;
  try{
    if(n.nodeType == 3){
      // Text node
      var tmp = n.nodeValue;
      tmp = tmp.trim();
      ret = tmp.length == 0;
    }else if(inc_tags && n.nodeType == 1){
      // Tag node
      switch(htmlDOM.tagName(n)){
        case "br":
          ret = true;
          break;
      }
    }
  }catch(e){
    alert("Error: htmlDOM: isEmpty");
  }
  return ret;
}

// 
// removeChildren
// p - parent whose children to remove
// children - children to remove from parent, specified as array
window.htmlDOM.removeChildren = function(p, children){
  try{
    for(var i = 0; i < children.length; i++){
      var e = htmlDOM.getElementById(children[i]);
      p.removeChild(e);
    }
  }catch(e){
    alert("Error: htmlDOM: removeChildren");
  }
  return;
}

// getNodeRangeByTag
// Starting with DOM node el, search forward or backwards for the nth occurance
// of the node with the specified tag.  Return all nodes inclusive as an array.
// If no corresponding stop node can be found, return all siblings to the first
// or last child, depending on direction of search.
// el - DOM node to start the range at
// tag - the tag to stop at
// dir - 'prev' to search before el, 'next' to search after
// n - number of DOM nodes with tag 'tag' to skip over before stopping
window.htmlDOM.getNodeRangeByTag = function(el, tag, dir, n){
  var ret = new Array;
  var count = 0;
  try{
    var prop = dir == "prev" ? "previousSibling" : "nextSibling";
    for( var d = el; d != null; d = d[prop] ){
      ret[count++] = d;
      if(count > 1 && d.tagName && htmlDOM.tagName(d) == tag){
        // Matching tag, decrement n
        if(--n == 0){
          // Stop counting
          break;
        }
      }
    }
    if(ret.length > 0 && dir == 'prev'){
      // Need to reverse the array if we were going front to back
      var rev = new Array;
      for(var i = ret.length - 1; i >= 0; i--){
        rev[ret.length - (i + 1)] = ret[i];
      }
      ret = rev;
    }
  }catch(e){
    alert("Error: htmlDOM: getNodeRangeByTag");
  }
  if(ret.length == 0){
    ret = null; // return null if empty array
  }
  return ret;
}

// tagName
// el - DOM element to inspect
// RETURN: lowercase string of the tag name
window.htmlDOM.tagName = function(el){
  try{
    if(el.tagName){
      return el.tagName.toLowerCase();
    }
  }catch(e){
    alert("Error: htmlDOM: tagName");
  }
  return null;
}

// replaceFirstChild
// node - node to insert, can be null to delete the first child
// parent - node whose child to replace
// not - optional, if the first child is this node, insert before and don't
//       replace
window.htmlDOM.replaceFirstChild = function(node, parent, not){
  if(!not){ not = null; }
  if((node || node == null) && parent){
    if(parent.firstChild === not){
      // first child is the one we don't want to replace, so insert before
      if(node != null){
        parent.insertBefore(node, not);
      }
    }else{
      if(node != null){
        parent.replaceChild(node, parent.firstChild);
      }else{
        parent.removeChild(parent.firstChild);
      }
    }
  }
  return;
}

// insertBefore
// arr - array of nodes to
// anchor - the insertion node
window.htmlDOM.insertBefore = function(arr, anchor){
  try{
    for(var i = 0; i < arr.length; i++){
      anchor.parentNode.insertBefore(arr[i], anchor);
    }
  }catch(e){
    alert("Error: htmlDOM: insertBefore");
  }
  return;
}

// DomArrayFromHTML
// html - the html string
// Convert a string of html to an array of DOM objects
window.htmlDOM.DomArrayFromHTML = function(html){
  var arr = null;
  if(html.length > 0){
    var obj = document.createElement('div');
    if(obj){
      obj.innerHTML = html;
      arr = new Array;
      for(var i = 0; i < obj.childNodes.length; i++){
        arr[i] = obj.childNodes[i];
      }
    }
  }
  return arr;
}

// DomFromHTML
// html - the html string
// Convert a string of html text to a dom object
window.htmlDOM.DomFromHTML = function(html){
  if(html.length > 0){
    var obj = document.createElement('div');
    if(obj){
      obj.innerHTML = html;
      return obj.firstChild;
    }
  }
  return null;
}

// getElementById
// This is an expanded wrapper of document.getElementById.  The argument to
// this function can be a string or a DOM object.  If it is a DOM object, that
// object is simply returned.  If it is a string, document.getElementById is
// called on that string.  Else null is returned.
// Why do this? Because it allows other functions that expect elements to take
// them as string or DOM object parameters, which simplifies javascript code.
// el - ID of object to return
window.htmlDOM.getElementById = function(el){
  var obj = null;
  try{
    switch(typeof(el)){
      case "object":
        if(el.id){
          var tmp = document.getElementById(el.id);
          if(tmp === el){
            obj = el;
          }
        }else{
          obj = el;
        }
        break;
      case "string":
        obj = document.getElementById(el);
        if(!obj){
          obj = null;
        }
        break;
    }
  }catch(e){
    alert("Error: htmlDOM: getElementById");
  }
  return obj;
}

/*******************************************************************************
** LEGACY FUNCTIONS
** USE OBJECT FUNCTIONS ABOVE - PHASE THESE OUT
*/

// htmlDom_replaceFirstChild
// node - node to insert, can be null to delete the first child
// parent - node whose child to replace
// not - optional, if the first child is this node, insert before and don't
//       replace
function htmlDom_replaceFirstChild(node, parent, not){
  if(!not){ not = null; }
  if((node || node == null) && parent){
    if(parent.firstChild === not){
      // first child is the one we don't want to replace, so insert before
      if(node != null){
        parent.insertBefore(node, not);
      }
    }else{
      if(node != null){
        parent.replaceChild(node, parent.firstChild);
      }else{
        parent.removeChild(parent.firstChild);
      }
    }
  }
  return;
}

// htmlDom_html2domByChild
// html - the html string
// Convert a string of html text to a dom object
function htmlDom_html2domByChild(html, child){
  child = !child ? 0 : child; // Set to zero if not supplied
  if(html.length > 0){
    var obj = document.createElement('div');
    if(obj){
      obj.innerHTML = html;
      return obj.childNodes[child].firstChild;
    }
  }
  return null;
}


// htmlDom_html2dom
// html - the html string
// Convert a string of html text to a dom object
function htmlDom_html2dom(html){
  if(html.length > 0){
    var obj = document.createElement('div');
    if(obj){
      obj.innerHTML = html;
      return obj.firstChild;
    }
  }
  return null;
}

// htmlDom_getTableCellSiblingCell
// node - the node to start with, can be any node type
// Start with node and search up until we find the containing TD tag.
// When we find that tag, search the siblings for the next containing
// TD tag
function htmlDom_getTableCellSiblingCell(node){
  var pTD = null; // the parent TD
  if(node){
    // If node is a TD itself, use it
    if(node.nodeType == 1 /* ELEMENT */ && node.tagName.toLowerCase() == "td"){
      pTD = node;
    }else{
      pTD = htmlDom_getNodeParentByTag(node, "td");
    }
  }
  // Now return sibling TD
  return htmlDom_getNodeSiblingByTag(pTD, "td");
}

// htmlDom_getNodeSiblingByTag
// node - the node whose sibling to find
// tag - the tag we're searching for
function htmlDom_getNodeSiblingByTag(node, tag){
  var s = null; // sibling
  if(node && tag && tag.length > 0){
    s = node.nextSibling;
    while(s != null){
      if(s.nodeType == 1 /* ELEMENT */ && s.tagName.toLowerCase() == tag){
        break;
      }
      s = s.nextSibling;
    }
  }
  return s;
}

// htmlDom_getNodeParentByTag
// node - the node whose parent to find
// tag - the tag we're searching for
function htmlDom_getNodeParentByTag(node, tag){
  var p = null; // parent
  if(node && tag && tag.length > 0){
    p = node.parentNode;
    while(p != null){
      if(p.nodeType == 1 /* ELEMENT */ && p.tagName.toLowerCase() == tag){
        break;
      }
      p = p.parentNode;
    }
  }
  return p;
}