/**********************************************************/
/* BASIC ELEMENT RELATED JAVASCRIPT FUNCTIONS             */
/*                                                        */
/* METABASE JAVASCRIPT (c) 2009-2010 Vincent E. Milum Jr. */
/**********************************************************/




/*************************************\
*  FUNCTIONS FOR GETTING THE ELEMENT  *
\*************************************/


//GET A SINGLE ELEMENT BY ID.  CAN PASS IN AN EXISTING ELEMENT OBJECT AS WELL SAFELY.
function getElement(thing, doc) {
  if (typeof(thing) == 'object') return thing;
  if (typeof(thing) == 'string') {
    thing = thing.trim();
    if (thing == '') return null;
    if (!doc) doc = document;
    return doc.getElementById(thing);
  }
  return null;
}



function getElementEx(id, thing, doc) {
  var elem = getElement(thing, doc);
  if (elem === null) elem = getElement(thing + ''  + id, doc);
  if (elem === null) elem = getElement(thing + '_' + id, doc);
  return elem;
}



//GET MULTIPLE ELEMENTS THAT MATCH A SINGLE CLASS NAME (OPTIONALLY LIMITED TO SPECIFIC TAG NAME FOR SPEED)
function getElementsByClassName(name, tagname, doc) {
  if (!tagname) tagname = '*';
  if (!doc)     doc = document;

  var elements  = new Array();
  var elem_test = doc.getElementsByTagName(tagname);

  for (var i=0; i<elem_test.length; i++) {
    if (hasClass(elem_test[i], name)) {
      elements.push(elem_test[i]);
    }
  }

  return elements;
}



//GET AN ELEMENT'S PARENT ELEMENT
function getElementParent(thing, doc) {
  var element = getElement(thing, doc);
  return ( (element) ? (element.parentNode) : (null) );
}



//GET AN ELEMENT'S GRAND-PARENT ELEMENT (THE PARENT OF THIS ELEMENT'S PARENT)
function getElementGrandparent(thing, doc) {
  var element = getElement(thing, doc);
  element = ( (element) ? (element.parentNode) : (null) );
  return    ( (element) ? (element.parentNode) : (null) );
}



//GET THE FIRST CHILD ELEMENT OF THIS ELEMENT, IGNORING NON-ELEMENT CHILD OBJECTS (SUCH AS TEXT AND WHITE SPACE OBJECTS)
function getFirstChildElement(thing, doc) {
  var element = getElement(thing, doc);
  if (element) for (var i=0; i<element.childNodes.length; i++) {
    if (element.childNodes[i].nodeType == 1) return element.childNodes[i];
  }
  return null;
}



//GET THE LAST CHILD ELEMENT OF THIS ELEMENT, IGNORING NON-ELEMENT CHILD OBJECTS (SUCH AS TEXT AND WHITE SPACE OBJECTS)
function getLastChildElement(thing, doc) {
  var element = getElement(thing, doc);
  if (element) for (var i=element.childNodes.length; i>=0; i--) {
    if (element.childNodes[i].nodeType == 1) return element.childNodes[i];
  }
  return null;
}



//GET THE CHILD ELEMENT'S INDEX POSITION WITHIN ITS PARENT, RETURNING THE INDEX INTEGER POSTION, OR -1 ON FAILURE
function getElementChildPos(thing, child, doc) {
  var elem = getElement(thing, doc);
  child    = getElement(child, doc);
  if (elem && child) for (var i=0; i<elem.childNodes.length; i++) {
    if (element.childNodes[i].nodeType == 1) {
      if (child == element.childNodes[i]) return i;  //TODO: verify that this comparison actually works!
    }
  }
  return -1;
}



//GET THE INDEX POSITION OF THE PREVIOUS CHILD OF THIS ELEMENT, BASED ON THE POSITION OF ANOTHER CHILD ELEMENT
//"ID" MAY BE EITHER THE INTEGER INDEX POSITION OF A CHILD ELEMENT, OR THE OBJECT REFERENCE OF A CHILD ELEMENT
function getPreviousChildElementPos(thing, id, doc) {
  var element = getElement(thing, doc);
  if (!element) return -1;
  if (typeof(id) == 'object') id = getElementChildPos(element, id);
  if (--id > element.childNodes.length) return -1;
  for (var i=id; i>-1; i--) {
    if (element.childNodes[i].nodeType == 1) return i;
  }
  return -1;
}



//GET THE INDEX POSITION OF THE NEXT CHILD OF THIS ELEMENT, BASED ON THE POSITION OF ANOTHER CHILD ELEMENT
//"ID" MAY BE EITHER THE INTEGER INDEX POSITION OF A CHILD ELEMENT, OR THE OBJECT REFERENCE OF A CHILD ELEMENT
function getNextChildElementPos(thing, id, doc) {
  var element = getElement(thing, doc);
  if (!element) return -1;
  if (typeof(id) == 'object') id = getElementChildPos(element, id);
  for (var i=id+1; i<element.childNodes.length; i++) {
    if (element.childNodes[i].nodeType == 1) return i;
  }
  return -1;
}





/****************************************************************\
*  FUNCTIONS FOR GETTING THE ELEMENT'S PLACEMENT AND DIMENSIONS  *
\****************************************************************/


//GET THE ELEMENT'S TOP BOARDER POSITION FROM THE TOP OF THE PAGE, IN PIXELS
function getElementTop(thing, doc) {
  var element = getElement(thing, doc);
  var total = 0;
  while (element) {
    total  += element.offsetTop;
    element = element.offsetParent;
  }
  return total;
}



//GET THE ELEMENT'S LEFT BOARDER POSITION FROM THE LEFT OF THE PAGE, IN PIXELS
function getElementLeft(thing, doc) {
  var element = getElement(thing, doc);
  var total = 0;
  while (element) {
    total  += element.offsetLeft;
    element = element.offsetParent;
  }
  return total;
}



//GET THE ELEMENT'S WIDTH DIMENSION, IN PIXELS
function getElementWidth(thing, doc) {
  var element = getElement(thing, doc);
  if (!element) return 0;
  return element.offsetWidth;
}



//GET THE ELEMENT'S HEIGHT DIMENSION, IN PIXELS
function getElementHeight(thing, doc) {
  var element = getElement(thing, doc);
  if (!element) return 0;
  return element.offsetHeight;
}





/************************************************************\
*  FUNCTIONS FOR GETTING/SETTING THE CONTENTS OF AN ELEMENT  *
\************************************************************/


//GET AN ATTRIBUTE PROPERTY OF AN ELEMENT - EG: 'ID', 'TYPE', 'STYLE', 'HREF', 'SRC', ETC
function getElementAttribute(thing, attr, doc) {
  var element = getElement(thing, doc);
  if (element) return element.getAttribute(attr);
  return null;
}



//SET AN ATTRIBUTE PROPERTY OF AN ELEMENT - EG: 'ID', 'TYPE', 'STYLE', 'HREF', 'SRC', ETC
function setElementAttribute(thing, attr, value, doc) {
  var element = getElement(thing, doc);
  if (element) element.setAttribute(attr, value);
}



//GET THE ELEMENT'S VALUE PROPERTY (USED FOR FORM OBJECTS, SUCH AS TEXT INPUT)
function getElementValue(thing, doc) {
  var element = getElement(thing, doc);
  return ( (element) ? (element.value) : ('') );
}



//SET THE ELEMENT'S VALUE PROPERTY (USED FOR FORM OBJECTS, SUCH AS TEXT INPUT)
function setElementValue(thing, value, doc) {
  var element = getElement(thing, doc);
  if (element) element.value = value;
}



//COPYS THE VALUE PROPERTY FROM ONE ELEMENT TO ANOTHER
function copyElementValue(source, dest, source_doc, dest_doc) {
  setElementValue( dest, getElementValue(source, source_doc), dest_doc );
}



//GET THE ELEMENT'S INNER CONTENTS, RETURNED WITH FULL HTML MARKUP
function getElementHtml(name, doc) {
  var element = getElement(name, doc);
  return (element) ? (element.innerHTML) : ('');
}



//SET THE ELEMENT'S INNER CONTENTS, ALLOWING FOR HTML MARKUP TO BE PROCCESSED BY THE BROWSER
function setElementHtml(name, html, doc) {
  var element = getElement(name, doc);
  if (element) element.innerHTML = html;
}



//COPIES THE INNER HTML MARKUP CONTENTS FROM ONE ELEMENT TO ANOTHER
function copyElementHtml(source, dest, source_doc, dest_doc) {
  setElementHtml( dest, getElementHtml(source, source_doc), dest_doc );
}



//GET THE ELEMENT'S INNER TEXT CONTENTS, IGNORING ALL HTML MARKUP
function getElementText(thing, doc) {
  var element = getElement(thing, doc);
  return ( (element) ? (element.innerText) : ('') );
}



//SET THE ELEMENT'S INNER TEXT CONTENTS, IGNORING ALL HTML MARKUP
function setElementText(thing, text, doc) {
  var element = getElement(thing, doc);
  if (element) element.innerText = text;
}



//COPIES THE INNER TEXT CONTENTS FROM ONE ELEMENT TO ANOTHER, IGNORING ALL HTML MARKUP
function copyElementText(source, dest, source_doc, dest_doc) {
  setElementText( dest, getElementText(source, source_doc), dest_doc );
}





/**************************************************************\
*  FUNCTIONS FOR GETTING/SETTING THE PROPERTIES OF AN ELEMENT  *
\**************************************************************/


//GET THE ELEMENT'S TAG NAME - EG: 'DIV' (ALWAYS RETURNED AS LOWER CASE)
function getElementTag(thing, doc) {
  var element = getElement(thing, doc);
  return ( (element) ? (element.nodeName.toLowerCase()) : ('') );
}



//TODO: OBSOLETE - GET AN ATTRIBUTE PROPERTY OF AN ELEMENT - EG: 'ID', 'TYPE', 'STYLE', 'HREF', 'SRC', ETC
function getAttrib(thing, attr, doc) {
  var element = getElement(thing, doc);
  if (element) return element.getAttribute(attr);
  return null;
}



//TODO: OBSOLETE - SET AN ATTRIBUTE PROPERTY OF AN ELEMENT - EG: 'ID', 'TYPE', 'STYLE', 'HREF', 'SRC', ETC
function setAttrib(thing, attr, value, doc) {
  var element = getElement(thing, doc);
  if (element) element.setAttribute(attr, value);
}



//DISABLES THE ELEMENT FROM USER INTERACTION
function disable_element(thing, doc) {
  var element = getElement(thing, doc);
  if (element) element.disabled = true;
}



//ENABLES THE ELEMENT FOR USER INTERACTION (DEFAULT FOR ALL ELEMENTS IS ENABLED, SO THIS IS ONLY TO RE-ENABLE ONCE DISABLED)
function enable_element(thing, doc) {
  var element = getElement(thing, doc);
  if (element) element.disabled = false;
}




/*****************************************************/
/* ELEMENT CLASS FUNCTIONS                           */
/*****************************************************/

//source:  http://www.stringify.com/words/javascript/class_attributes_javascript_and_you.html

function getClasses(element) {
  return element.className.trim().split(/\s+/);
}


function addClass(thing, c) {
  var element = getElement(thing);
  if (!element) return;

  var classes = getClasses(element);
  if (classes.indexOf(c) == -1) {
    classes.push(c);
    element.className = classes.join(' ');
  }
}


function removeClass(thing, c) {
  var element = getElement(thing);
  if (!element) return;

  var classes = getClasses(element);
  var idx = classes.indexOf(c);
  if (idx != -1) {
    classes.splice(idx, 1);
    element.className = classes.join(' ');
  }
}


function toggleClass(thing, c) {
  if (hasClass(thing, c)) {
    removeClass(thing, c);
  } else {
    addClass(thing, c);
  }
}


function setClass(thing, c, enabled) {
  if (enabled) {
    addClass(thing, c);
  } else {
    removeClass(thing, c);
  }
}


function hasClass(thing, c) {
  var element = getElement(thing);
  if (!element) return false;

  return getClasses(element).indexOf(c) != -1;
}



function removeClassFromTree(thing, c) {
  var element = getElement(thing);
  if (!element) return;

  var children = element.childNodes;
  for (var i=0; i < children.length; i++) {
    var child = children[i];
    if (child.nodeType == 1) {
      removeClass(child, c);
      removeClassFromTree(child, c);
    }
  }
}




/**************************\
*  MISC ELEMENT FUNCTIONS  *
\**************************/


//REMOVES THE ELEMENT FROM THE DOCUMENT
function removeElement(thing, doc) {
  var element = getElement(thing, doc);
  var parent  = getElementParent(thing, doc);
  if (!parent) return;
  
  if (getElementTag(element) == 'tr') {
    parent.deleteRow(element.rowIndex);
  } else {
    parent.removeChild(element);
  }
}




function createElement(tag, doc) {
  if (!doc) doc = document;
  return doc.createElement(tag);
}



//SET USER INPUT FOCUS TO THE EVENT
function focusElement(thing, doc) {
  var element = getElement(thing, doc);
  if (element) element.focus();
}




/***************************************************\
* PAGE SIZE AND SCROLL FUNCTIONS                    *
* SOURCE: http://www.softcomplex.com                *
\***************************************************/

function f_clientWidth() {
  return f_filterResults (
    window.innerWidth ? window.innerWidth : 0,
    document.documentElement ? document.documentElement.clientWidth : 0,
    document.body ? document.body.clientWidth : 0
  );
}


function f_clientHeight() {
  return f_filterResults (
    window.innerHeight ? window.innerHeight : 0,
    document.documentElement ? document.documentElement.clientHeight : 0,
    document.body ? document.body.clientHeight : 0
  );
}


function f_scrollLeft() {
  return f_filterResults (
    window.pageXOffset ? window.pageXOffset : 0,
    document.documentElement ? document.documentElement.scrollLeft : 0,
    document.body ? document.body.scrollLeft : 0
  );
}


function f_scrollTop() {
  return f_filterResults (
    window.pageYOffset ? window.pageYOffset : 0,
    document.documentElement ? document.documentElement.scrollTop : 0,
    document.body ? document.body.scrollTop : 0
  );
}


function f_filterResults(n_win, n_docel, n_body) {
  var n_result = n_win ? n_win : 0;
  if (n_docel && (!n_result || (n_result > n_docel))) n_result = n_docel;
  return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
}



/***************************************************\
* ELEMENT DISPLAY FUNCTIONS                         *
\***************************************************/


function display_none(name, doc) {
  var element = getElement(name, doc);
  if (element) element.style.display = 'none';
}

function display_inline(name, doc) {
  var element = getElement(name, doc);
  if (!element) return;
  if (getElementTag(element) == 'tr') {
    try        { element.style.display = 'table-row'; }
    catch(err) { element.style.display = 'inline';    }
  } else if (getElementTag(element) == 'td'  ||  getElementTag(element) == 'th') {
    try        { element.style.display = 'table-cell'; }
    catch(err) { element.style.display = 'inline';    }
  } else {
    element.style.display = 'inline';
  }
}

function display_inline_toggle(name, doc) {
  var element = getElement(name, doc);
  if (!element) return;
  if (element.style.display == 'none') {
    display_inline(block);
  } else {
    display_none(block);
  }
}

function display_block(name, doc) {
  var element = getElement(name, doc);
  if (!element) return;
  if (getElementTag(element) == 'tr') {
    try        { element.style.display = 'table-row'; }
    catch(err) { element.style.display = 'block';     }
  } else if (getElementTag(element) == 'td'  ||  getElementTag(element) == 'th') {
    try        { element.style.display = 'table-cell'; }
    catch(err) { element.style.display = 'inline';    }
  } else {
    element.style.display = 'block';
  }
}

function display_block_toggle(name, doc) {
  var element = getElement(name, doc);
  if (!element) return;
  if (element.style.display == 'none') {
    display_block(element);
  } else {
    display_none(element);
  }
}



function convertToPx(value) {
  var number = parseIntX(value);
  var units  = value.substring( number.toString().length ).trim();
  
  switch (units.toLowerCase()) {
    case 'px': return number;
    case 'em': return number * 16;
    case 'em': return number * 8;
    case 'pc': return number * 16;
    case '%':  return Math.round(number * 16 / 100);
    case 'pt': return Math.round(number * 16 / 12);
    case 'in': return Math.round(number * 16 * 6);
  }
  return 0;
}



function width_auto(name, doc) {
  var element = getElement(name, doc);
  if (element) element.style.width = 'auto';
}



function getMaxZIndex(doc) {
  if (!doc) doc = document;
  var highest = 0;
  var index   = 0;
  
  var elements = doc.getElementsByTagName('*');
  
  for (var i=0; i<elements.length; i++) {
    //TODO: this should probably be computedStyle (or what ever it is called)???
    index = parseIntX(elements[i].style.zIndex);
    if (index > highest) highest = index;
    if (index != 0) alert(index);
  }
  
  return highest;
}



function toggle_image(id, size1, size2) {
  var img = getElement('image'+id);
  if (!img) return true;

  var src = getAttrib(img, 'src');
  var pic = src.substr(src.length-5, 1);

  if (pic == 'n') {
    setAttrib(img, 'src', src.substr(0, src.length-5)+'o'+src.substr(src.length-4));
    setElementText('imagetext'+id, '100%');
  } else {
    setAttrib(img, 'src', src.substr(0, src.length-5)+'n'+src.substr(src.length-4));
    setElementText('imagetext'+id, Math.round(size1/size2*100)+'%');
  }

  return false;
}