/**
 * Set of static methods to allow Document Object Model (DOM)
 * nodes manipulation
 */
var TDOM = function() { }

/**
 * Get an element in document
 *
 * @param {Mixed} el: DOM Element or element id
 * @retun {Object} element or null if not found
 */
TDOM.getElement = function(el) {
  return typeof el == "string" ? document.getElementById(el) : el;
}

/**
 * Return the document node of an element
 * If no el is sent, current document is returned
 *
 * @param {Mixed} el: DOM Element or element id
 * @return {Document} document element
 */
TDOM.getDocumentNode = function(el) {
  el = TDOM.getElement(el) || document;
  return el.nodeType == 9 ? el : el.ownerDocument || el.document || document;
}

/**
 * Return the root node of the document of an element
 * @return {Body} document root node
 */
TDOM.getDocumentElement = function(el) {
  var doc = TDOM.getDocumentNode(el);
  return document.compatMode == 'CSS1Compat' ? doc.documentElement : doc.body;
}

/**
 * Resolve an HTML Text Node to his HTML Element parent
 *
 * @param {Node} node: DOM Node
 * @retun {Element} DOM Element Node
 */
TDOM.resolveTextNode = function(node) {
  return node && node.nodeType == 3 && node.parentNode ? node.parentNode : node;
}

/**
 * Seek an HTML Element node starting from node
 *
 * @param {Node} node: node from where start seeking
 * @return {Boolean} prev: true to look backward, false to look forward
 */
TDOM.seekElement = function(node, prev) {
  while (node && node.nodeType != 1)  {
    node = prev ? node.previousSibling : node.nextSibling;
  }
  return node;
}

/**
 * Return the next node of type Element
 *
 * @param {Node} node: node from where start seeking
 * @return {Element} node of type 1 or null if none found
 */
TDOM.nextElement = function(node) {
  return TDOM.seekElement(node ? node.nextSibling : null);
}

/**
 * Return the previous node of type Element
 *
 * @param {Node} node: node from where start seeking
 * @return {Element} node of type 1 or null if none found
 */
TDOM.previousElement = function(node) {
  return TDOM.seekElement(node ? node.previousSibling : null, true);
}

/**
 * Return the first child Element node
 *
 * @param {Node} node: node from where start seeking
 * @return {Element} node of type 1 or null if none found
 */
TDOM.firstChild = function(node) {
  return TDOM.seekElement(node ? node.firstChild : null);
}

/**
 * Return the last child Element node
 *
 * @param {Node} node: node from where start seeking
 * @return {Element} node of type 1 or null if none found
 */
TDOM.lastChild = function(node) {
  return TDOM.seekElement(node ? node.lastChild : null);
}

/**
 * Insert an element node before or after
 * another element node
 *
 * @param {Element} a: element node to insert
 * @param {Boolean} after: true intert a after b, false insert a before b
 * @param {Element} b: node of reference
 */
TDOM.insertElement = function(a, after, b) {
  if (b && b.parentNode) {
    b = after ? TDOM.nextElement(b) : b;
    if (b) {
      b.parentNode.insertBefore(a,b);
    } else {
      b.parentNode.appendChild(a);
    }
  }
}

/**
 * Remove an element from the DOM tree
 * @param {Element} el: element to remove
 */
TDOM.removeElement = function(el) {
  if (el && el.parentNode) {
    el.parentNode.removeChild(el);
  }
}

/**
 * Return the size of an element
 *
 * @param {Mixed} el: DOM Element or element id
 * @return {Object} element size object
 */
TDOM.getSize = function(el, autoWidth) {
  var w = h = 0;
  el = TDOM.getElement(el);
  if (TDOM.getStyle(el, "display") != "none") {
    w = el.offsetWidth ? el.offsetWidth : 0;
    h = el.offsetHeight ? el.offsetHeight : 0;
  } else {
    var style = el.style;
    w = TDOM.getStyle(el, "width");
    if (!autoWidth) {
      if (w == "auto" || w.indexOf("%") != -1) {
        var parent = el.parentNode;
        while (parent && TDOM.getStyle(parent, "display") == "none") {
          parent = parent.parentNode;
        }
        if (parent) {
          w = parent.offsetWidth ? parent.offsetWidth : 0;
        }
      }
    }
    var oldPos = style.position;
    var oldVis = style.visibility;
    var oldWidth = style.width;
    style.width = isNaN(parseInt(w)) ? w : w + 'px';
    style.visibility = "hidden";
    style.position = "absolute";
    style.display = "";
    w = el.offsetWidth ? el.offsetWidth : 0;
    h = el.offsetHeight ? el.offsetHeight : 0;
    style.width = oldWidth;
    style.display = "none";
    style.position = oldPos;
    style.visibility = oldVis;
  }
  return {width:w, height:h};
}

/**
 * Return the position of an element in document
 *
 * @param {Mixed} el: DOM Element or element id
 * @return {Point} point object
 */
TDOM.getPos = function(el) {
  el = TDOM.getElement(el);
  var doc = TDOM.getDocumentNode(el);
  if (el.getBoundingClientRect) {
    var box = el.getBoundingClientRect();
    var scroll = TDOM.getScroll(doc);
    return {x:box.left + scroll.x, y:box.top  + scroll.y}
  } else {
    var y = el.offsetTop;
    var x = el.offsetLeft;
    var parent = el.offsetParent;
    if (parent != el) {
      while (parent) {
        y += parent.offsetTop;
        x += parent.offsetLeft;
        parent = parent.offsetParent;
      }
    }
    if (TUserAgent.OPERA || TUserAgent.SAFARI && TDOM.getStyle(el, "position") == "absolute") {
      y -= doc.body.offsetTop;
    }
    parent = el.parentNode;
    var body = TDOM.getDocumentElement(doc);
    while (parent && parent != body) {
      x -= parent.scrollLeft;
      if (!TUserAgent.OPERA || parent.tagName != "TR") {
        y -= parent.scrollTop;
      }
      parent = parent.parentNode;
    }
    return {x:x, y:y};
  }
}

/**
 * Return a rect object with the bounds of the given element
 *
 * @param {Mixed} el: DOM Element or element id
 * @return {Rect} bounds of the element
 */
TDOM.getBounds = function(el, autoWidth) {
  var size = TDOM.getSize(el, autoWidth);
  var pos = TDOM.getPos(el);
	return {top:pos.y, left:pos.x, width:size.width, height:size.height};
}

/**
 * Return the size of the viewport
 * (Current visible area of the window)
 * @return {Object} viewport size object
 */
TDOM.getViewportSize = function() {
  // Safari
  var w = window.innerWidth;
  var h = window.innerHeight;
  if (!w && !h) {
    // Strict or quirks mode
    var b = TDOM.getDocumentElement();
    var w = b ? b.clientWidth : 0;
    var h = b ? b.clientHeight : 0;
  }
  return {width:w, height:h};
}

/**
 * Return the size of the document
 * (Entire document size, including scrolls)
 * @return {Object} document size object
 */
TDOM.getDocumentSize = function() {
  var v = TDOM.getViewportSize();
  var b = TDOM.getDocumentElement();
  return {width:Math.max(b.scrollWidth, v.width), height:Math.max(b.scrollHeight, v.height)};
}

/**
 * Return a point object with the document scrollbars positions
 * @return {Point} document scroll positions
 */
TDOM.getScroll = function(doc) {
  var b = TDOM.getDocumentElement(doc);
  return {x:b.scrollLeft, y:b.scrollTop};
}

/**
 * Return a Rect object with the bounds of the viewport
 * @return {Rect} viewpor bounds
 */
TDOM.getViewportBounds = function() {
  var pos = TDOM.getScroll();
  var size = TDOM.getViewportSize();
  return {top:pos.y, left:pos.x, width:size.width, height:size.height};
}

/**
 * Test is an element has a class
 *
 * @param {Element} el
 * @param {String} cl: class name to test
 */
TDOM.hasClass = function(el,cl) {
  var re = new RegExp('(^|\\s+)?' + cl + '(\\s+|$)?');
  return re.test(el.className);
}

/**
 * Add a new class to an element
 *
 * @param {Element} el
 * @param {String} cl: class to add
 */
TDOM.addClass = function(el,cl) {
  if (TDOM.hasClass(el,cl)) return;
  if (!el.className) {
    el.className = cl;
  } else {
    el.className = [el.className, cl].join(' ');
  }
}

/**
 * Remove a class from an element
 *
 * @param {Object} el
 * @param {String} cl: class to remove
 */
TDOM.removeClass = function(el,cl) {
  if (!TDOM.hasClass(el,cl)) return;
  var re = new RegExp('(^|\\s+)?' + cl + '(\\s+|$)?','g');
  var cl = el.className;
  el.className = cl.replace(re,' ');
}

/**
 * Get an style attribute value of an element
 *
 * @param {Element} el: DOM Element
 * @param {String} property: css property
 */
TDOM.getStyle = function(el, property) {
  el = TDOM.getElement(el);
  var doc = TDOM.getDocumentNode(el);
  // W3C way
  if (doc.defaultView && doc.defaultView.getComputedStyle) {
    property = property == 'float' ? 'cssFloat' : property;
    var styles = doc.defaultView.getComputedStyle(el, "");
    if (styles) {
      if (property == 'opacity') {
        property = "MozOpacity" in styles ? "MozOpacity" : property;
        property = "KhtmlOpacity" in styles ? "KhtmlOpacity" : property;
      }
      return styles[property];
    }
  } else
  // IE way
  if (el.currentStyle) {
    if (property == 'opacity' && el.filters) {
      var val = 100;
      try {
        val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity;
      } catch(e) {
        try {
          val = el.filters('alpha').opacity;
        } catch(e) { }
      }
      return val / 100;
    }
    property = property == 'float' ? 'styleFloat' : property;
    return el.currentStyle[property];
  }
  return el.style[property];
}

/**
 * Set an style to an element
 *
 * @param {Element} el: DOM element
 * @param {String} property: css property
 */
TDOM.setStyle = function(el, property, value) {
  el = TDOM.getElement(el);
  if (property == 'opacity') {
    if (value < 0 || value > 1) {
      return;
    }
    if ("opacity" in el.style) {
      el.style.opacity = value;
    } else
    if ("MozOpacity" in el.style) {
      el.style.MozOpacity = value;
    } else
    if ("KhtmlOpacity" in el.style) {
      el.style.KhtmlOpacity = value;
    } else
    if ("filter" in el.style) {
      el.style.filter = "alpha(opacity=" + value * 100 + ")";
    }
    return;
  }
  if (property == 'float') {
    property = TUserAgent.IE ? 'styleFloat' : 'cssFloat';
  }
  el.style[property] = value;
}

