//////// object extensions ////////////

//obtain remainder of string after first index of ch
String.prototype.from = function (ch)
{
   return this.substr(this.indexOf(ch) + ch.length);
}

// obtain part of string before first index of ch
String.prototype.to = function (ch)
{
   return this.substr(0,this.indexOf(ch));
}

//clear spaces from start and end
String.prototype.trim = function ()
{
   return this.replace(/^\s+|\s+$/g,'');
}

//////// element selection ////////////

function elemById(id)
{
   return document.getElementById(id);
}
function elemsByTag(tagName, elParent)
{
	var arElems = []; //requires new instance of Array, because DOM method returns nodeList, not Array.

   if (isDefined(elParent))
      var parent = elParent;
   else
      var parent = document;

   var listElems = parent.getElementsByTagName(tagName);

   for (var i=0; i < listElems.length; i++ ) arElems[arElems.length] = listElems[i];

   return arElems;
}
function elemsByClass(className)
{
   var allNodes = Everything(document);
   var classNodes = [];

   for (var i=0; i < allNodes.length; i++)
      if (String(allNodes[i].className).indexOf(className) != -1)
         classNodes[classNodes.length] = allNodes[i];

   return classNodes;
}
function Style(id)
{
	return elemById(id).style;
}

//////// measurement ////////////

// credit: functions getTop() and getLeft() graciously borrowed and edited from http://www.quirksmode.org/
// they now take ids as well as node objects.
function getTop(obj)
{
   if (typeof obj == 'string') obj = elemById(obj);

  var curtop = 0;
  if (obj.offsetParent)
  {
    while (obj.offsetParent)
    {
      curtop += obj.offsetTop;
      obj = obj.offsetParent;
    }
  }
  else if (obj.y)
    curtop += obj.y;
  return curtop;
}
function getLeft(obj)
{
	if (typeof obj == 'string') obj = elemById(obj);

  var curleft = 0;
  if (obj.offsetParent)
  {
    while (obj.offsetParent)
    {
      curleft += obj.offsetLeft;
      obj = obj.offsetParent;
    }
  }
  else if (obj.x)
    curleft += obj.x;
  return curleft;
}

//in string 'window' | 'document' | 'scroll'
//out: obj with .x and .y for width and height
// Many thanks to Peter-Paul Koch of quirksmode.org for doing my dirty work.
function measure(part)
{
   var Size = {};
   var x, y;

   switch (part)
   {
      case 'window':
         //viewport size
         if (self.innerHeight) // all except Explorer
         {
            x = self.innerWidth;
            y = self.innerHeight;
         }
         else if (document.documentElement && document.documentElement.clientHeight) // Explorer 6 Strict
         {
            x = document.documentElement.clientWidth;
            y = document.documentElement.clientHeight;
         }
         else if (document.body) // other Explorers
         {
            x = document.body.clientWidth;
            y = document.body.clientHeight;
         }
      break;
      case 'scroll':
         // pixels scrolled
         if (self.pageYOffset) // all except Explorer
         {
            x = self.pageXOffset;
            y = self.pageYOffset;
         }
         else if (document.documentElement && document.documentElement.scrollTop) // Explorer 6 Strict
         {
            x = document.documentElement.scrollLeft;
            y = document.documentElement.scrollTop;
         }
         else if (document.body) // all other Explorers
         {
            x = document.body.scrollLeft;
            y = document.body.scrollTop;
         }
      break;
      case 'document':
         //total doc height
         var test1 = document.body.scrollHeight;
         var test2 = document.body.offsetHeight
         if (test1 > test2) // all but Explorer Mac
         {
            x = document.body.scrollWidth;
            y = document.body.scrollHeight;
         }
         else // Explorer Mac;
              //would also work in Explorer 6 Strict, Mozilla and Safari
         {
            x = document.body.offsetWidth;
            y = document.body.offsetHeight;
         }
      break;
   }

   Size.x = x;
   Size.y = y;

   return Size;
}

//in: [idstring || objectelement], string css-syntax property, [optional bool nounit-switch]
//out: string CSS-value || int CSS-value
// The nounit boolean converts the value of a property to a number, eg '15px' -> 15
function getStyleValue(elem, property, nounit)
{
   if (typeof elem == 'string') elem = Thing(elem);

   if (window.getComputedStyle) //if Moz/Opera, just get the style:
   {
      var styleValue = window.getComputedStyle(elem,null).getPropertyValue(property);
   }
   else if (elem.currentStyle) //if IE, rewrite property and get the style.
   {
      var cssSyntax = property.split('-'); //cut up property
      for (var i=1; i < cssSyntax.length ; i++) //capitalize element values from second
         cssSyntax[i] = cssSyntax[i].substr(0,1).toUpperCase() + cssSyntax[i].substr(1);
      var jsSyntax = cssSyntax.join(''); //glue the thing back together

      var styleValue = eval('elem.currentStyle.' + jsSyntax);
   }

   if (nounit) //convert string value to number
   {
      if (isNaN(styleValue.charAt(0))) //if it's no number, kill it and return NaN
      {
         styleValue = parseInt(styleValue);
      }
      else
      {
         var unitfree = '';
         for (var i=0; !isNaN(styleValue.charAt(i)); i++)
            unitfree += styleValue.charAt(i);
         styleValue = parseInt(unitfree);
      }
   }
   return styleValue;
}

//simple showHide
function showHide(id,state)
{
	if (state == 'hide')
		Style(id).display = 'none';
	else if (state == 'show')
		Style(id).display = 'block';

	return false;
}
//advanced showHide
function Toggle(elem)
{
   if (typeof elem == 'string') elem = elemById(elem);
   var displayState = getStyleValue(elem,'display');

   if (displayState == 'none')
      elem.style.display = 'block';
   else if (displayState == 'block')
      elem.style.display = 'none';
}

/// DOM-proof treewalkers that only return useful nodes.
function isValidNode(node)
{
   //8 is comment, 3 is text node
   return !(node.nodeType == 8 || (node.nodeType == 3 && !Chars(node.nodeValue)) )
}

function _nextSibling(node)
{
   while (node.nextSibling && !isValidNode(node.nextSibling))
		node = node.nextSibling;

   return node.nextSibling;
}

function _childNodes(parent)
{
  var oldChilds = parent.childNodes;
  var newChilds = [];
  for (var i=0; i < oldChilds.length; i++)
  {
    if (isValidNode(oldChilds[i]))
      newChilds[newChilds.length] = oldChilds[i];
  }
  return newChilds;
}

function parentNodes(node)
{
   if (typeof node == 'string') node = elemById(node);

   var ancestors = [node];
   while (node.parentNode)
   {
      node = node.parentNode;
      ancestors[ancestors.length] = node;
   }
   ancestors.reverse();

   return ancestors;
}

function Everything(root, doRoot, schwing)
{
  if (typeof schwing == 'undefined')
	 everything = [];

  if (doRoot)
	 everything[everything.length] = root;
  doRoot = true;

  var childs = _childNodes(root);
  if (childs.length != 0)
  {
	 for (var i=0; i < childs.length; i++)
		Everything(childs[i],doRoot,true);
  }
  return everything;
}

//event assignment. e.g.: Listen(window,'load',prepLayout);
function Listen(elem, event, func)
{
  if (elem.addEventListener)
    elem.addEventListener(event,func,false);
  else if (elem.attachEvent)
    elem.attachEvent('on'+event,func);
  else
    eval('elem.on'+event+' = func;');
}
function Deaf(elem, event, func)
{
  if (elem.removeEventListener)
    elem.removeEventListener(event,func,false);
  else if (elem.detachEvent)
    elem.detachEvent('on'+event,func);
  else
    eval('elem.on'+event+' = null;');
}

///// Element creation functions

//simple spawn
function Spawn(tagname)
{
  return document.createElement(tagname);
}
function spawnText(content)
{
  return document.createTextNode(content);
}

//advanced spawn: specify the parent object, the tag to spawn, and the contents of the spawned element
function makeAndFill(parent,elem,content)
{
  var newElem = Spawn(elem);
  if (typeof content != 'undefined')
  {
     var newText = spawnText(content);
     newElem.appendChild(newText);
  }
  parent.appendChild(newElem);

  return newElem;
}

//create a quick div in body with specified id, classname and contents.
function spawnBox(id,className,content)
{
   if (typeof content == 'undefined') content = '';
   if (typeof className == 'undefined') className = '';

   var newElem = makeAndFill(elemsByTag('body')[0],'div',content);
   newElem.id = id;
   newElem.className = className;

   return newElem;
}

//deletes a node and returns it. Yes, that's possible.
function deleteNode(node)
{
   return node.parentNode.removeChild(node);
}
//obtain depth in tree. Takes object.
function level(elem)
{
  var levelCount = 0;
  while (elem.parentNode) levelCount++;
  return levelCount;
}

///////////  general purpose functions ////////////
function isDefined(input)
{
   return (typeof input != 'undefined' || (typeof input  == 'object' && String(input) != 'undefined'));
}

// returns the largest of a given set of numbers. Takes argument list eg. (this,that, so) or array with ints eg. (arrNums).
function largestValue()
{
   var constr = String(arguments[0].constructor).toLowerCase();
   if (constr.indexOf('array') > -1)
      var numbers = arguments[0];
   else
      var numbers = arguments;

   if (numbers.length == 0) return 0;

   var largest = numbers[0];

   for (var i=0; i < numbers.length; i++)
   {
      if (largest < numbers[i])
         largest = numbers[i];
   }

   return largest;
}


//replaces all instances of string with other string inside specified String object.
function Replace(theString, chars, Replacement)
{
   return theString.replace(new RegExp(chars,'g'),Replacement);
}

//returns true if a string contains valid characters, i.e. is NOT empty.
function Chars(data)
{
  return /[^\t\n\r ]/.test(String(data));
}

function isValidEmail(strEmail)
{
   var strPattern = "^[-_.\\w]+@((([\\w]|[\\w][\\w-]*[\\w])\\.)+(ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|com|coop|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|in|info|int|io|iq|ir|is|it|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mil|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no|np|nr|nt|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5])\\.){3}([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$";
   return new RegExp(strPattern).test(strEmail.toLowerCase());
}
