/* 
FUNCTIONLIST:
  FirstElement(rootNode)
  LastElement(rootNode)
  FirstElementByTagName(rootNode, tagName)
  LastElementByTagName(rootNode, tagName)
  GetElementsByTagName(rootNode, tagName)
  FirstElementByClassName(rootNode, className)
  LastElementByClassName(rootNode, className)
  GetElementsByClassName(rootNode, className)
  CountElementsByTagName(rootNode, tagName)
  CountElementsByClassName(rootNode, className)
  PreviousElementSibling(node)
  NextElementSibling(node)
  ArrayWalk(array, func)  
*/

//INTERFACE *******************************************************************
function AddClassName(node, className) {
  var classNames = node.className.split(" ");
  for(var i=0; i<classNames.length; i++) {
    if(classNames[i] == className)
      return;
  }
  classNames.push(className);
  node.className = classNames.join(" ");
}

function RemClassName(node, className) {
  var oldClassNames = node.className.split(" ");
  var newClassNames = new Array();
  for(var i=0; i<oldClassNames.length; i++) {
    if(oldClassNames[i] != className)
      newClassNames.push(oldClassNames[i]);
  }
  node.className = newClassNames.join(" ");
}
function FirstElement(rootNode) {
  return FindNode(rootNode, CreatePredicate("nodeType", 1), DIR_FORWARD)
}

function LastElement(rootNode) {
  return FindNode(rootNode, CreatePredicate("nodeType", 1), DIR_BACKWARD)
}

function FirstElementByTagName(rootNode, tagName) {
  return FindNode(rootNode, CreatePredicate("tagName", tagName.toUpperCase()), DIR_FORWARD)
}

function LastElementByTagName(rootNode, tagName) {
  return FindNode(rootNode, CreatePredicate("tagName", tagName.toUpperCase()), DIR_BACKWARD)
}

function GetElementsByTagName(rootNode, tagName, recursionDeep) {
  if(!(tagName instanceof Array))
  	tagName = new Array(tagName);
  
  code = "if(node.tagName==undefined) return false;var tags = new Array();";
  for(i=0; i<tagName.length; i++) {
  	code += "tags.push('"+tagName[i].toUpperCase()+"');";
  }
  code +=
  "var item=null;"+
  "while(item=tags.shift()) {"+
  "  if(item==node.tagName) return true;"+
  "}"+
  "return false;";

  return FindNodesR(rootNode, new Function("node", code));
}

function GetElementsByTagName2(rootNode, tagName, recursionDeep) {
  return FindNodesR(rootNode, CreatePredicate("tagName", tagName.toUpperCase()),recursionDeep)
}

function FirstElementByClassName(rootNode, className) {
  return FindNode(rootNode, CreatePredicate("className", className), DIR_FORWARD)
}

function LastElementByClassName(rootNode, className) {
  return FindNode(rootNode, CreatePredicate("className", className), DIR_BACKWARD)
}

function GetElementsByAttribute(rootNode, attribute, value, recursionDeep) {
  if(value==undefined)
    return FindNodesR(rootNode,CreatePredicate("hasAttribute('"+attribute+"')",1))
  else
    return FindNodesR(rootNode,
    new Function("node",
    "if(node.getAttribute==undefined)return false;if(node.getAttribute('"+attribute+"')==null)return false;"+
    "if(node.getAttribute('"+attribute+"').match("+
    new RegExp("^("+value+"|.+\\s"+value+
    "|"+value+"\\s.+|.+\\s"+value+"\\s.+)$")+
    "))return true;else return false"
    ),recursionDeep);
}

function GetElementsByClassName(rootNode, className, recursionDeep) {
  return FindNodesR(rootNode,
  new Function("element",
  "if(element.className==null)return false;"+
  "if(element.className.match("+
  new RegExp("^("+className+"|.+\\s"+className+
  "|"+className+"\\s.+|.+\\s"+className+"\\s.+)$")+
  "))return true;else return false"
  ),recursionDeep);
}

function PreviousElementSibling(node) {
  while((node = node.previousSibling) != null) {
    if(node.nodeType == 1)
      break; 
  }
  return node  
}

function NextElementSibling(node) {
  while((node = node.nextSibling) != null) {
    if(node.nodeType == 1)
      break;
  }
  return node
}

function CountElementsByTagName(rootNode, tagName) {
  return FindNodes(rootNode, CreatePredicate("tagName", tagName.toUpperCase())).length
}

function CountElementsByClassName(rootNode, className) {
  return GetElementsByClassName(rootNode, className).length
}

function ArrayWalk(array, func) {
  for(var i=0; i<array.length; i++)
    func(array[i])
}

function TreeClimb(node, steps) {
  if(steps == undefined)
    steps = 1
  for(var i=0; i<steps; i++)
    node = node.parentNode
  return node  
}

function TreeClimbToTag(node, tag) {
  tag = tag.toLowerCase();
  while(tag != node.tagName.toLowerCase() && node.parentNode != null)
  	node = node.parentNode
  return node  
}

function TreeClimbToClassName(node, className) {
  className = className.toLowerCase();
  while(node.parentNode != null) {
    if(node.className != null) {
      if(className != node.className.toLowerCase())
        node = node.parentNode;
      else
        break;
    }
  }
  return node;
}

function RemoveTableRow(item) {
  var tr = TreeClimbToTag(item,"tr");
  if(tr != null)
  	RemoveNode(tr);
}

function sort(item, mode) {
  var sortItems = GetElementsByAttribute(TreeClimbToTag(item, "tr"), "sortfield", "");
  while(sortItem = sortItems.shift()) {
  	if(sortItem != item.firstChild.nextSibling)
  	  sortItem.setAttribute("src", "/icon/empty.gif");
  }
  var column = 0;
  var desc = false;
  var img = GetElementsByTagName(item, "img").shift();
  if(img != null) {
	if(img.src.match("/icon/cc_arrow_dn.gif")) {
	  img.setAttribute("src","/icon/cc_arrow_up.gif");
	 desc = true;
	}
	else {
      img.setAttribute("src","/icon/cc_arrow_dn.gif");
	  desc = false;
	}
	
	var td = TreeClimbToTag(item, "td");
	while(td=td.previousSibling)
	  column++;
  }
  
  sortTable(TreeClimbToTag(item, "tbody"), column, desc, mode);
}

function sortnumeric(item) {
  sort(item, "numeric");
}

function sortalphanumeric(item) {
  sort(item, "alphanumeric");
}

function sortTable(table, column, desc, mode) {
  var rows = GetElementsByTagName(table, "TR");
  table.appendChild(rows.shift());
  if(mode == "numeric")
  	rows.sort(createCompFunc(column, desc));
  else
  	rows.sort(createCompFunc2(column, desc));
  while(row = rows.shift())
  	table.appendChild(row);
}

//Alphanumeric
function createCompFunc2(column, desc) {
  var return1 = "-1";
  var return2 = "1";
  if(desc) {
  	return1 = "1";
  	return2 = "-1";
  }
  code  = "v1=r1.childNodes.item("+column+");v2=r2.childNodes.item("+column+");";
  code += "if((v1=FindNodeR(v1,CreatePredicate('nodeType',3)))==null)v1=0;else v1=v1.nodeValue;if((v2=FindNodeR(v2,CreatePredicate('nodeType',3)))==null)v2=0;else v2=v2.nodeValue;";
  code += "if(v1<v2)return "+return1+";if(v1>v2)return "+return2+";return 0;";
  return new Function("r1,r2", code); 
}

//Numeric
function createCompFunc(column, desc) {
  var return1 = "-1";
  var return2 = "1";
  if(desc) {
  	return1 = "1";
  	return2 = "-1";
  }
  code  = "v1=r1.childNodes.item("+column+");v2=r2.childNodes.item("+column+");";
  code += "if((v1=FindNodeR(v1,CreatePredicate('nodeType',3)))==null)v1=0;else v1=parseFloat(v1.nodeValue);if((v2=FindNodeR(v2,CreatePredicate('nodeType',3)))==null)v2=0;else v2=parseFloat(v2.nodeValue);";
  code += "if(v1<v2)return "+return1+";if(v1>v2)return "+return2+";return 0;";
  return new Function("r1,r2", code); 
}

function RemoveNode(node) {
  return node.parentNode.removeChild(node)
}

function ResetFormElement(element) {
  if(element.tagName == 'SELECT')
    element.selectedIndex = 0
  else if(element.tagName == 'INPUT') {
    switch(element.type) {
      case 'checkbox':
        element.checked = false
        break;
      case 'text':
        element.value = ''
        break;
    }
  }
}

function InitFormElement(element) {
  var initVal = element.getAttribute("init")
  switch(element.tagName.toLowerCase()) {
    case "select":
      element.selectedIndex = initVal
      break;
    case "input":
      element.value = initVal
      break;
  }
}

function roundNumber(number,decimals) {
  return Math.round(number*Math.pow(10,decimals))/Math.pow(10,decimals);
}

function GetEventTarget(e) {
  return e ? e.target : window.event.srcElement;
}

function GetEvent(e) {
  return e ? e : window.event;
}

function FindPosX(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;
}

function FindPosY(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;
}

// ****************************************************************************






// GLOBAL CONSTANTS ***********************************************************
var DIR_FORWARD  = 1
var DIR_BACKWARD = 2
// ****************************************************************************

// IMPLEMENTATION *************************************************************
function GetFirst(node, direction) {
  return direction == DIR_FORWARD ? node.firstChild : node.lastChild
}

function GetSibling(node, direction) {
  return direction == DIR_FORWARD ? node.nextSibling : node.previousSibling
}

function GetElement(rootNode, direction) {
  var node = GetFirst(rootNode, direction)
  if(node.nodeType == 1)
    return node
  while((node = GetSibling(node, direction)) != null)
    if(node.nodeType == 1)
      return node
}

function FindElementByTagName(rootNode, tagName, direction) {
  var node = GetFirst(rootNode, direction)
  while(node != null) {
    if(node.nodeName == tagName)
      break;
    node = GetSibling(node, direction)
  }
  return node.nodeName == tagName ? node : null
}

function ParseAttr(attr) {
  var index=attr.indexOf("(")
  if(index != -1)
    return attr.substring(0,index)
  else
    return attr
}

function CreatePredicate(attr, value, binOper) {
  var code = ""
  if(attr instanceof Array) {
    if(attr.length == value.length) {
      if(binOper == undefined)
        binOper = "&&"
      for(var i=0; i<attr.length; i++) {
        code += "if(node." +ParseAttr(attr[i])+ "==undefined)return false;"
      }
      code += "return " 
      for(var i=0; i<attr.length; i++) {
        code += "node." +attr[i]+ "==\"" +value[i]+ "\" " +binOper+ " "
      }
    }
  }
  
  if(code == "")
    code = "if(node."+ParseAttr(attr)+"==undefined) return false;return node."+attr+"==\""+value+"\""
  
  if((index = code.lastIndexOf("\"")) != -1)
    code = code.substring(0,index+1)
    
  return new Function("node", code)
}

function CreatePredicate_old(attr, value, binOper, objectType) {
  var code = ""
  if(attr instanceof Array) {
    if(attr.length == value.length) {
      if(binOper == undefined)
        binOper = "&&"
      for(i=0; i<attr.length; i++) {
        code += "node." +attr[i]+ "==\"" +value[i]+ "\" " +binOper+ " "
      }
    }
  }
  
  if(code == "")
    code = "node."+attr+"==\""+value+"\""
  
  if((index = code.lastIndexOf("\"")) != -1)
    code = code.substring(0,index+1)
  
  var typeCheck = "";
  if(objectType != undefined)
    typeCheck = "(node instanceof "+objectType+")==false?false:";   
  
  return new Function("node", "return " +typeCheck+code+ ";")
}

function FindNode(rootNode, predicate, direction) {
  var node = GetFirst(rootNode, direction)
  if(node == null)
    return node
  if(predicate(node))
    return node
  while((node = GetSibling(node, direction)) != null) {
    if(predicate(node))
      return node
  }
  return null
}

function FindNodes(rootNode, predicate) {
  var result = new Array()
  var node = rootNode.firstChild 
  if(node == null)
    return result    
  if(predicate(node))
    result.push(node)    
  while((node = node.nextSibling) != null) {
    if(predicate(node))
      result.push(node)
  }
  return result
}

/*
 * Käy läpi rekursiivisesti puurankenteen "rootNode" elementit,
 * ja palauttaa ensimmäisen funktion "predicate" määrittelemän ehdon
 * toteuttavan elementin
 *
 * Parametrit:
 *  -rootNode
 *    tyyppi: Node-object
 *    kuvaus: Elementti jonka lapsielementit käydään läpi.
 *  -predicate
 *    tyyppi: Function-object
 *    kuvaus: Ehdon määrittelevä funktio. Algoritmi olettaa
 *            funktion ottavan yhden parametrin ja palauttavan [true,false]
 *  -deep (vaihtoehtoinen)
 *    tyyppi: integer [1,ääretön]
 *    kuvaus: Rekursion syvyys
 *
 * Palautusarvo:
 *  Node-object
 */
function FindNodeR(rootNode, predicate, deep) {
  if(deep != undefined) {
    if(deep > 0)
      deep -= 1
    else
      return null
  }
  var result = null
  var node   = rootNode.firstChild
  if(node == null)
    return node
  if(predicate(node))
    return node
  if(node.hasChildNodes()) {
    result = FindNodeR(node, predicate, deep)
    if(result != null)
      return result
  }   
  while((node = node.nextSibling) != null) {
    if(predicate(node))
      return node
    if(node.hasChildNodes()) {
      result = FindNodeR(node, predicate, deep)
      if(result != null)
        return result
    }
  }
  return result  
}

/*
 * Käy läpi rekursiivisesti puurankenteen "rootNode" elementit,
 * ja palauttaa taulukon elementeistä, jotka toteuttavat
 * funktion "predicate" määrittelemän ehdon.
 *
 * Parametrit:
 *  -rootNode
 *    tyyppi: Node-object
 *    kuvaus: Elementti jonka lapsielementit käydään läpi.
 *  -predicate
 *    tyyppi: Function-object
 *    kuvaus: Ehdon määrittelevä funktio. Algoritmi olettaa
 *            funktion ottavan yhden parametrin ja palauttavan [true,false]
 *  -deep (vaihtoehtoinen)
 *    tyyppi: integer [1,ääretön]
 *    kuvaus: Rekursion syvyys
 *
 * Palautusarvo:
 *  Array-object
 */
function FindNodesR(rootNode, predicate, deep) {
  var result = new Array()
  if(deep != undefined) {
    if(deep > 0)
      deep -= 1
    else
      return result
  }
  var node   = rootNode.firstChild 
  if(node == null)
    return result
  if(predicate(node))
    result.push(node)
  if(node.hasChildNodes())
    result = result.concat(FindNodesR(node, predicate, deep))  
  while((node = node.nextSibling) != null) {
    if(predicate(node))
      result.push(node)
    if(node.hasChildNodes())
      result = result.concat(FindNodesR(node, predicate, deep))
  }
  return result  
}

// ****************************************************************************