/*
dom_utils.js.php

Copyright (c) Gareth Hadfield 2008
*/


/* DOM Elements */

function has_parent(aNode, aParentNode){
  result = false;
  if((aNode!=undefined) && (aNode.parentNode!=undefined)){
    result = (aNode.parentNode==aParentNode) || has_parent(aNode.parentNode, aParentNode);
  }
  return(result);
}

function all_tags(aTagName, aParentNode, aRecurseAllAncestors, aCriteriaFunction){
  if(aParentNode==undefined){
    aParentNode=document;
  }

  if(aRecurseAllAncestors==undefined){
    aRecurseAllAncestors=true;
  }

  var aTags;
  if(aTagName==undefined){
    aTags = aParentNode.childNodes;
  }
  else{
    aTags = aParentNode.getElementsByTagName(aTagName);
  }

  var aResultTags = new Array;

  for(var iTag=0; iTag<aTags.length; iTag++){
    if(aRecurseAllAncestors || (aTags[iTag].parentNode==aParentNode)){ // conditions for adding to list
      if( (aCriteriaFunction == undefined) || aCriteriaFunction(aTags[iTag]) ){
        aResultTags.push(aTags[iTag]);
      }
    }

    if((aTagName==undefined) && aRecurseAllAncestors){
      // need to manually recurse children
      aResultTags = aResultTags.concat(all_tags(aTagName, aTags[iTag], aRecurseAllAncestors, aCriteriaFunction));
    }
  }

  return(aResultTags);
}

function all_divs(aParentNode, aRecurseAllAncestors, aCriteriaFunction){
  // return an array containing all <div> elements
  return(all_tags("div", aParentNode, aRecurseAllAncestors, aCriteriaFunction));
}

function is_class(aElement, aClassName, aIncludeSubClass){
  // true if aElement.className matches aClassName
  // opionally matching aElement.sub_class
  if(aIncludeSubClass == undefined){
    aIncludeSubClass = false;
  }

  if(aIncludeSubClass){
    var aSubClass = getAttributeText(aElement, "sub_class"); // possibly undefined
  }

  return((aElement.className==aClassName) || (aIncludeSubClass && (aSubClass==("." + aClassName))));
}

function get_all_class(aTagName, aClassName, aIncludeSubClass, aParentNode, aRecurseAllAncestors){
  var aCriteriaFunction = function(aElement){
    return(is_class(aElement, aClassName, aIncludeSubClass));
  };
  return(all_tags(aTagName, aParentNode, aRecurseAllAncestors, aCriteriaFunction));
}

function element_css_text(aElement){
  // return the css info for the given element
  var result = "";
  if(IS_IE){
    result = aElement.style.cssText;
  }
  else{
    result = aElement.getAttribute("style");
  }
  return(result);
}

// DOM Attributes

var EVENT_PREFIX = "EVENT_";

function event_attribute(aName){
  return(in_array(aName, EVENT_ATTRIBUTES));
}

function getAttributeText(aElement, aName){
  // distinguishes between content/style and other attributes
  // NB Generally call this function rather than getAttribute
  assert(aElement != undefined, "getAttributeText: aElement cannot be undefined");
  assert(aName != undefined, "getAttributeText: aName cannot be undefined");

  var result = "";
  if(aName=="z_index"){
    // a special attribute always kept in sync with real z_index
    result = get_z_index(aElement);
    if(result != aElement.getAttribute(aName)){
      setAttributeText(aElement, aName, result);
    }
  }
  else if(aName=="content"){
    if(aElement.getContent==undefined){
      result = aElement.innerHTML;
    }
    else{
      result = aElement.getContent();
    }
  }
  else if(aName=="style"){
    if(aElement.getCSS==undefined){
      if(IS_IE){
        result = aElement.style.cssText;
      }
      else{
        if(aElement.getAttribute != undefined){
          result = aElement.getAttribute("style");
        }
      }
    }
    else{
      result = aElement.getCSS();
    }
  }
  else{

    if(event_attribute(aName)){
      aName = EVENT_PREFIX + aName;
    }

    var aAttribute = aElement.attributes[aName];
    if(aAttribute == undefined){
      result = undefined;
    }
    else{
      result = aAttribute.value;
    }
  }
  return(result);
}

function update_child_parent_ids(aElement){
  // if child nodes have a parent_id attribute then update them to aElement.id
  var aNewParentId = getAttributeText(aElement, "id");

  for(var i=0; i<aElement.childNodes.length; i++){
    var aNode = aElement.childNodes[i];

    if(aNode.attributes!=undefined){
      var aCurrentParentId = getAttributeText(aNode, "parent_id");

      if((aCurrentParentId!=undefined) && (aCurrentParentId!=aNewParentId)){
        aNode.setAttribute("parent_id", aNewParentId); // just renaming so no need for resync
      }
    }
  }
}

function setAttributeText(aElement, aName, aValue){
  if(aName=="class"){
    if(IS_IE){
      aElement.className = aValue;
    }
    else{
      aElement.setAttribute(aName, aValue);
    }
  }
  else if(aName=="id"){
    aElement.setAttribute(aName, aValue);
    update_child_parent_ids(aElement);
  }
  else if(aName=="z_index"){
    set_z_index(aElement, aValue);
    aElement.setAttribute(aName, aValue);
  }
  else if(aName=="content"){
    if(aElement.setContent==undefined){
      aElement.innerHTML = aValue;
    }
    else{
      aElement.setContent(aValue);
    }
  }
  else if(aName=="style"){
    if(aElement.setCSS==undefined){
      if(IS_IE){
        aElement.style.cssText = aValue;
      }
      else{
        aElement.setAttribute("style", aValue);
      }
    }
    else{
      aElement.setCSS(aValue);
    }
  }
  else if(event_attribute(aName)){

    aElement.setAttribute(aName,
//      "process_element_event(aEvent, this, '"+aName+"', '"+escape(aValue)+"');"
      "" // dummy
    );

//    if(IS_IE || IS_SAFARI){
      var aScript = "aElement."+aName+" = bind(aElement, function(aEvent){process_element_event(aEvent, this, \""+aName+"\", \""+escape(aValue)+"\");});";
      eval(aScript);
//    }

    // event value is stored in an attribute with a prefixed name
    aName = EVENT_PREFIX + aName;
    aElement.setAttribute(aName, aValue);
  }
  else{
    aElement.setAttribute(aName, aValue);
  }
}

function process_element_event(aEvent, aElement, aName, aValue){
  if(!aEvent){if(window.event!=undefined){aEvent=event;}}
//  debug(aEvent);

  if(OPENDOTS_MODE == OPENDOTS_EDIT_MODE){
    if(aName == "onmouseover"){
      aElement.editonmouseover(aEvent);
    }
    else if(aName == "onmouseout"){
      aElement.editonmouseout(aEvent);
    }
    else if(aName == "onload"){
      eval("bind(aElement, function(){"+unescape(aValue)+"})();"); // todo use call
    }
  }
  else{
//  if((OPENDOTS_MODE != OPENDOTS_EDIT_MODE) || (aName != "onclick")){

    // call event code
    if(aValue != ""){
      eval("bind(aElement, function(){"+unescape(aValue)+"})();"); // todo use call
    }

    // do an other processing that needs to be done transparently to the event
    if(aName=="onload"){
      // todo - future add any extra onload processing
    }
    else if(aName=="onclick"){

      if(aElement.is_opendot){
        var aLinkUrl = aElement.getAttributeText("link_url");
        if((aLinkUrl != undefined) && (aLinkUrl != "") && (aElement.activateLinkUrl != undefined)){
          aElement.activateLinkUrl();
        }
      }

    }
  }
}

function setCSS(aElement, aNewCSS){
  aElement.rawCSS = aNewCSS; // keep raw copy since browsers modify the css

  if(document.loading==0){

    // Add sub-class info
    if(aElement.subClassInfo == undefined){ // check if cached
      var aSubClass = getAttributeText(aElement, "sub_class");
      if((aSubClass!=undefined) && (aSubClass!="")){
        var aSubClassInfo = getCSSClassInfo(aSubClass);
        aNewCSS = aSubClassInfo + aNewCSS;

        if(OPENDOTS_MODE != OPENDOTS_EDIT_MODE){
          // cache the info
          aElement.subClassInfo = aSubClassInfo;
        }
      }
    }
    else{
      // use cached info
      aNewCSS = aElement.subClassInfo + aNewCSS;
    }

  }

  // For the document.body add the template info
  if(aElement.id == "document.body"){
    var aTemplateCSS = aElement.getAttributeText(OPENDOTS_TEMPLATE_PREFIX+"style");
    if(aTemplateCSS == undefined){
      aTemplateCSS = "";
    }
    aNewCSS = aTemplateCSS + aNewCSS;
  }


  if(IS_IE){
    aElement.style.cssText = aNewCSS;
  }
  else{
    aElement.setAttribute("style", aNewCSS);
  }
}

function getCSS(aElement){
  if(aElement.rawCSS==undefined){
    aElement.rawCSS = element_css_text(aElement);
  }
  return(aElement.rawCSS);
}

function is_css_dimension(aName){
  aName = aName.toLowerCase();
  return(
    tail_is(aName, "width") ||
    tail_is(aName, "height") ||
    (aName=="left") ||
    (aName=="right") ||
    (aName=="bottom") ||
    (aName=="top")
  );
}

function is_digit(aChar){
  return(!isNaN(parseInt(aChar)));
}

function has_units(aValue){
  aValue = aValue.toString();
  return(
    (aValue != "") &&
    !is_digit(aValue.substr(aValue.length-1, 1))
  );
}

function setCSSAttributeFor(aElement, aName, aValue, aAttributeName){
  // todo - validation

  // add px
  if(is_css_dimension(aName) && !has_units(aValue)){
    aValue += "px";
  }

  var aCSSText = getAttributeText(aElement, aAttributeName);

  var aCSSArray = css_array(aCSSText);

  var aFound = false;

  if(aValue==undefined){
    aValue="";
  }

  for(var iCss=0; iCss<aCSSArray.length; iCss++){
    if(aCSSArray[iCss][0].toUpperCase()==aName.toUpperCase()){
      aCSSArray[iCss][1]=aValue;
      aFound = true;
      break;
    }
  }

  if(!aFound){
    aCSSArray.push(new Array(aName, aValue));
  }

  return(setAttributeText(aElement, aAttributeName, css_text(aCSSArray)));
}

function setCSSAttribute(aElement, aName, aValue){
  return(setCSSAttributeFor(aElement, aName, aValue, "style"));
}

function getCSSAttribute(aElement, aName){
  var aCSSText = getAttributeText(aElement, "style");

  var aCSSArray = css_array(aCSSText);

  var result;
  for(var iCss=0; iCss<aCSSArray.length; iCss++){
    if(aCSSArray[iCss][0].toUpperCase()==aName.toUpperCase()){
      result = trim(aCSSArray[iCss][1]); // css attributes shouldn't have leading/trailing spaces
      break;
    }
  }
  return(result);
}

function removeCSSAttribute(aElement, aName){
  var aCSSText = getAttributeText(aElement, "style");
  var aCSSArray = css_array(aCSSText);
  var result;

  for(var iCss=0; iCss<aCSSArray.length; iCss++){
    if(aCSSArray[iCss][0].toUpperCase()==aName.toUpperCase()){
      result = aCSSArray.splice(iCss, 1);
      break;
    }
  }

  setAttributeText(aElement, "style", css_text(aCSSArray));
  return(result);
}

// DOM zIndex (not css z-index)

function get_z_index(aObject){
  // return position of child in parent's child list
  assert(aObject != undefined, "get_z_index(aObject): aObject cannot be undefined");


  if(aObject.parentNode != undefined){
    for(var i=0; i<aObject.parentNode.childNodes.length; i++){
      if(aObject.parentNode.childNodes[i]==aObject){
        return(i);
      }
    }
  }

  return(-1);// not a child
}

function set_z_index(aObject, aNewZ){
  // set position of child in parent's child list

  if(aObject.parentNode!=undefined){
    var aNextSibling = aObject.parentNode.childNodes[aNewZ];
    if(aNextSibling!=undefined){
      aObject.parentNode.insertBefore(aObject, aNextSibling);
    }
  }
}

function do_lower_object(aObject){
  current_z = get_z_index(aObject);
  if(isNaN(current_z) || (current_z<=0)){
    current_z = 1;
  }
  set_z_index(aObject, current_z-1);
}

function do_raise_object(aObject){
  current_z = get_z_index(aObject);
  if(isNaN(current_z)){
    current_z = 0;
  }
  set_z_index(aObject, current_z+2);
}

function bring_to_front(aNode){
  if((aNode != undefined) && (aNode.parentNode != undefined) && (aNode != aNode.parentNode.lastChild)){
    // reinsert a node at the top
    aNode.parentNode.appendChild(aNode);
  }
}

function send_to_back(aNode){
  if((aNode != undefined) && (aNode.parentNode != undefined) && (aNode != aNode.parentNode.firstChild)){
    // reinsert a node at the bottom
    aNode.parentNode.insertBefore(aNode, aNode.parentNode.firstChild);
  }
}

function highlight_element(aElement, aColor){
  if(!IS_OPERA){ // todo

    var aBorder = parseInt("4px");

    var aHiDiv = ce("div");

    aHiDiv.className = "opendot_highlight";

    aHiDiv.style.left = absoluteLeft(aElement)+"px";
    aHiDiv.style.top = absoluteTop(aElement)+"px";
    aHiDiv.style.height = aElement.offsetHeight+"px";
    aHiDiv.style.width = aElement.offsetWidth+"px";

    if(!IS_IE){
      aHiDiv.style.height = parseInt(aHiDiv.style.height, 10) - (2*aBorder)+"px";
      aHiDiv.style.width = parseInt(aHiDiv.style.width, 10) - (2*aBorder)+"px";
    }

    if(aColor==undefined){
      aColor="#FF0000";
    }
    aHiDiv.style.borderColor=aColor;

    aHiDiv.close = bind(aHiDiv,
      function(){
        this.parentNode.removeChild(this);
      }
    );

    aHiDiv.timeout = setTimeout(aHiDiv.close,
      1000);

    document.body.appendChild(aHiDiv);
  }
}

function add_child(aElement, aName){
  return(aElement.appendChild(ce(aName)));
}

function add_br(aElement){
  return(add_child(aElement, "br"));
}

function add_text(aElement, aString){
  return(aElement.appendChild(document.createTextNode(aString)));
}

function add_space(aElement){
  return(add_text(aElement, " "));
}

function is_node_name(aElement, aNodeName){
  return((aElement != undefined) && (aElement.nodeName != undefined) &&
    (aElement.nodeName.toUpperCase() == aNodeName.toUpperCase()));
}

function is_link(aElement){
  return(is_node_name(aElement, "A"));
}

function contains_node(aNeedle, aHaystack){
  var result = false;
  for(var iChild=0; iChild<aHaystack.childNodes.length; iChild++){
    if((aHaystack.childNodes[iChild]==aNeedle) || contains_node(aNeedle, aHaystack.childNodes[iChild])){
      result = true;
      break;
    }
  }
  return(result);
}

function has_parent_type(aElement, aNodeType){
  // return true if aElement has an ancestor of type aNodeType (eg "span")
  var result = false;
  if(aElement.parentNode != undefined){
    result = is_node_name(aElement.parentNode, aNodeType) || has_parent_type(aElement.parentNode, aNodeType);
  }
  return(result);
}

function has_child_type(aElement, aNodeType){
  // return true if aElement has an descendent of type aNodeType
  var result = false;

  if(aElement.childNodes != undefined){
    for(var iChild=0; iChild<aElement.childNodes.length; iChild++){
      var aChild = aElement.childNodes[iChild];
      if(is_node_name(aChild, aNodeType) || has_child_type(aChild, aNodeType)){
        result = true;
        break;
      }
    }
  }

  return(result);
}

function move_children(aOldParent){
  // move children of oldParent to oldParent.parentNode
  var aChild;
  var aNewParent = aOldParent.parentNode;

  for(var iChild=0; iChild<aOldParent.childNodes.length; iChild++){
    aChild = aOldParent.childNodes[iChild];
    aNewParent.insertBefore(aChild, aOldParent);
  }
}

/* Memory Leak Fixes */
function free_attributes(aElement){
  if((aElement != undefined) && (aElement.attributes != undefined)){
    for(var iAttribute=0; iAttribute<aElement.attributes.length; iAttribute++){
      try{
        aElement.attributes[iAttribute] = undefined;
      }
      catch(aError){
        // ignore
      }
    }
  }
}

function deleteNode(aNode){
  if((!IS_SAFARI) && (aNode.outerHTML != undefined)){
    aNode.outerHTML = "";
  }
  if(aNode.innerHTML != undefined){
    aNode.innerHTML = "";
  }
  if(aNode.childNodes != undefined){
    for(var iChild=0; iChild<aNode.childNodes.length; iChild++){
      deleteNode(aNode.childNodes[iChild]);
    }
  }
  free_attributes(aNode);
  if(aNode.parentNode != undefined){
    aNode.parentNode.removeChild(aNode);
  }
}

/* shorthand functions that add the px */

function set_left(aElement, aValue){
  aElement.style.left = parseInt(aValue) + "px";
}

function set_right(aElement, aValue){
  aElement.style.right = parseInt(aValue) + "px";
}

function set_top(aElement, aValue){
  aElement.style.top = parseInt(aValue) + "px";
}

function set_bottom(aElement, aValue){
  aElement.style.bottom = parseInt(aValue) + "px";
}

function set_width(aElement, aValue){
  aElement.style.width = parseInt(aValue) + "px";
}

function set_height(aElement, aValue){
  aElement.style.height = parseInt(aValue) + "px";
}

function insertAtCursor(aField, aText){
  if(document.selection != undefined){
    aField.focus();
    document.selection.createRange().text = aText;
  }
  else if(aField.selectionStart != undefined) {
    var aStartPos = aField.selectionStart;
    var aEndPos = aField.selectionEnd;
    aField.value = aField.value.substring(0, aStartPos) + aText + aField.value.substring(aEndPos, aField.value.length);

    aField.selectionStart = aStartPos + aText.length;
    aField.selectionEnd = aEndPos + aText.length;
  }
}

