var ID_PARAM = "id=";

var XML_NOT_INIT = 0;
var XML_LOADING = 1;
var XML_LOADED = 2;
var XML_HAS_DATA = 3;
var XML_COMPLETE = 4;
var HTML_OK = 200;

//for AJAX calls
var obXmlHttp = false;
var obXmlDoc = null;
var obCallbackObj = null;
var igXmlHttp = false;
var igXmlDoc = null;
var igCallbackObj = null;

//for debugging
var DEBUG_ELEMENT = "debugDiv";
var DEBUG_ALLOW_POPUPS = false;

//for XML parsing
var NODE_TAG = "node";
var ROOT_ATTRIBUTE = "id";
var ACCESSION_ATTRIBUTE = "accession";
var NAME_ATTRIBUTE = "name";
var CHILDREN_ATTRIBUTE = "has_children";
var RELATION_ID_ATTRIBUTE = "relation_id";
var RELATION_NAME_ATTRIBUTE = "relation_name";
var ROOT_TERM_TYPE = "root_term";
var MAP_FILE = "mapFile";
var IMG_FILE = "imgFile";
var MAP_CONTENT = "map";

//for HTML generation
var HTML_TABLE_ELEMENT = "table";
var HTML_TBODY_ELEMENT = "tbody";
var HTML_TR_ELEMENT = "tr";
var HTML_TD_ELEMENT = "td";
var HTML_DIV_ELEMENT = "div";
var HTML_SPAN_ELEMENT = "span";
var HTML_IMG_ELEMENT = "img";

//for internal tracking
var DUPLICATE_ID_TAG = "_duplicate_";

var lineArray = new Array("line-l.gif", "line-t.gif", "line-r.gif", "blank.gif", "blank.gif");
var minusArray = new Array("minus-l.gif", "minus-t.gif", "minus-t.gif", "minus.gif", "minus-dash.gif");
var plusArray = new Array("plus-l.gif", "plus-t.gif", "plus-r.gif", "plus.gif", "plus-dash.gif");
var imageArray = new Array("leaf.gif", "folderOpen.gif", "folderClosed.gif");

//this is matched with the TreeBuilderAjaxServlet. The order of these files is important
//0 = not used!!
//1 = is_a
//2 = part_of
//3 = dev_from
//4 = other
var nodeImageArray = new Array("", "isa.gif", "partof.gif", "devfrom.gif", "leaf.gif");
var OTHER_REL_TYPE = 4;

/******************************************************************************** CONSTRUCTORS */

function showStatus(sMsg) {
    window.status = sMsg;
    return true;
}

function OntologyBrowser(htmlObject, width, height, rootId, rootName, hideRoot) {

    //display
    this.parentObject = document.getElementById(htmlObject);
    this.width = width;
    this.height = height;
    this.rootId = rootId;
    this.rootName = rootName;
    this.imgPath = "images/tree/";
    this.style_pointer = "pointer";
    this.hoverOutput = 0;
    this.hoverOutMessage = "";
    if (navigator.appName == 'Microsoft Internet Explorer') {
        this.style_pointer = "hand";
    }

    //handlers
    this.openFuncHandler = 0;
    this.dblclickFuncHandler = 0;
    this.clickFuncHandler = 0;
    this.lastSelected = 0;

    //loading
    this.xmlURL = 0;
    this.loadInProgress = false;

    //memory storage
    this.id_array = new Array();
    this.node_array = new Array();
    this.nodeCount = 0;

    //create root
    //change 4 to -1 if using folder-style icons
    //change "root_term" to "" if using folder-style ivons
    this.htmlNode = new Term(this.rootId, this.rootName, 0, this, 0, 4, "root_term");
    this.htmlNode.parentObject = this.htmlNode;
    if (hideRoot) {
        this.htmlNode.htmlNode.childNodes[0].childNodes[0].style.display = "none";
        this.htmlNode.htmlNode.childNodes[0].childNodes[0].childNodes[0].className = "hiddenRow";
    }

    //init tree structures
    this.allTree = this.createBaseTree();
    this.allTree.appendChild(this.htmlNode.htmlNode);
    this.allTree.onselectstart = new Function("return false;");

    return this;
}
;

/**
 *   tree node constructor
 *	@param: itemId - node id
 *	@param: itemText - node label
 *	@param: parentObject - parent item object
 *	@param: treeObject - tree object
 *	@param: actionHandler - onclick event handler(optional)
 *	@param: nodeTypeId - will define which image to display.
 if > 0, will use the ontology images otherwise will default to "folder" icons
 *	@param: nodeTypeName - alt text to put in images
 */
function Term(itemId, itemText, parentObject, treeObject, actionHandler, nodeTypeId, nodeTypeName) {

    this.htmlNode = "";
    this.tableRowPointer = 0;
    this.childrenCount = 0;
    this.xmlLoaded = 0;
    this.span = 0;
    this.closeable = 1;
    this.childNodes = new Array();
    this.treeNode = treeObject;
    this.label = itemText;
    this.itemId = itemId;
    this.parentObject = parentObject;
    this.actionHandler = actionHandler;
    this.id = treeObject.addToStorageArrays(itemId, this);
    this.nodeTypeName = nodeTypeName;
    this.nodeTypeId = nodeTypeId;
    this.htmlNode = treeObject.createHtmlNode(this, nodeTypeId, nodeTypeName);
    return this;

}
;

/*
 table
 |_ tbody
 |_tr
 |_ td0
 |_ img0 (+/- line)
 |_ td1
 |_ img1 (folder/leaf)
 |_ td2
 |_ span
 |_ text

 this.htmlNode.childNodes[0].childNodes[0].childNodes[2].childNodes[0];
 table    tbody         tr              td            img/span
 */

Term.prototype.getTRElement = function(i) {
    return this.htmlNode.childNodes[0].childNodes[i];
};

Term.prototype.getNodeLeafImage = function() {
    return this.htmlNode.childNodes[0].childNodes[0].childNodes[1].childNodes[0];
};

Term.prototype.getNodeLineImage = function() {
    return this.htmlNode.childNodes[0].childNodes[0].childNodes[0].childNodes[0];
};

Term.prototype.getChildNodes = function() {
    return this.htmlNode.childNodes[0].childNodes;
};

/**
 *     create and return main html element of tree
 */
OntologyBrowser.prototype.createBaseTree = function() {

    var div = document.createElement(HTML_DIV_ELEMENT);
    div.style.overflow = "auto";
    div.style.width = this.width;
    div.style.height = this.height;
    this.parentObject.appendChild(div);
    return div;

};

/**
 *     create HTML elements for tree node
 *     @param: itemObject - item object
 *     @param: nodeTypeId - type of ontology node to create (must be > 0 to use)
 *     @param: nodeTypeName - alt text to associate with node image
 */
OntologyBrowser.prototype.createHtmlNode = function(itemObject, nodeTypeId, nodeTypeName) {

    var table = document.createElement(HTML_TABLE_ELEMENT);
    table.cellSpacing = 0;
    table.cellPadding = 0;
    table.className = "compactItem";

    var tbody = document.createElement(HTML_TBODY_ELEMENT);
    var tr = document.createElement(HTML_TR_ELEMENT);
    tr.className = "compactItem";
    var td0 = document.createElement(HTML_TD_ELEMENT);
    td0.className = "compactItem";
    var img0 = document.createElement(HTML_IMG_ELEMENT);
    img0.className = "imageItem";
    td0.appendChild(img0);

    var td1 = document.createElement(HTML_TD_ELEMENT);
    td1.className = "compactItem";
    var img1 = document.createElement(HTML_IMG_ELEMENT);
    img1.className = "imageItem";
    if (nodeTypeId > -1) {
        img1.src = this.imgPath + nodeImageArray[nodeTypeId];
        img1.alt = nodeTypeName;
    } else {
        //use the default "folder" icons
        img1.src = this.imgPath + imageArray[2];
        img1.alt = "";
    }
    td1.appendChild(img1);

    //it is not advisable to allow clicking of the image
    //as it looks ugly on screen.
    //td1.onclick = this.onSelectClickHandler;
    //td1.ondblclick = this.onDoubleClickHandler;
    //td1.parentObject = itemObject;

    var td2 = document.createElement(HTML_TD_ELEMENT);
    td2.noWrap = true;
    td2.style.width = "100%";
    td2.className = "compactItem";
    itemObject.span = document.createElement(HTML_SPAN_ELEMENT);
    itemObject.span.className = "standardItem";
    itemObject.span.appendChild(document.createTextNode(itemObject.label));
    td2.appendChild(itemObject.span);
    td2.parentObject = itemObject;
    td0.parentObject = itemObject;
    td0.onclick = this.onPlusMinusClickHandler;
    td2.onclick = this.onSelectClickHandler;
    td2.ondblclick = this.onDoubleClickHandler;
    td2.style.verticalAlign = "";
    td2.style.cursor = this.style_pointer;
    tr.appendChild(td0);
    tr.appendChild(td1);
    tr.appendChild(td2);

    tr.parentObject = itemObject;
    tr.onmouseover = this.mouseOverHandler;
    tr.onmouseout = this.mouseOutHandler;

    tbody.appendChild(tr);
    table.appendChild(tbody);

    return table;

};

/**
 *     create new child node
 *     @param: parentId - parent node id
 *     @param: itemId - new node id
 *     @param: itemText - new node text
 *     @param: itemActionHandler - function fired on node select event
 *     @param: children - node children flag
 *     @param: nodeTypeId - node type id
 *     @param: nodeTypeName - node type name
 */
OntologyBrowser.prototype.createNode = function(parentId, itemId, itemText, itemActionHandler, children, nodeTypeId, nodeTypeName) {

    var parentObject = this.findNodeById(parentId);

    if (!parentObject) {

        doDebug("Could not find parent object, cannot continue");
        return -1;

    } else {

        if (((parentObject.xmlLoaded == 0) && (this.xmlURL)) && (!this.loadInProgress))
        {
            parentObject.xmlLoaded = 1;
            this.loadXML(this.buildURL(parentObject.itemId));
            doDebug("was supposed to create node for " + itemId);
            doDebug("but needed to create parent node first for " + parentId);
        }

        var count = parentObject.childrenCount;
        var nodeArr = parentObject.childNodes;

        if ((!itemActionHandler) && (this.clickFuncHandler)) {
            itemActionHandler = this.clickFuncHandler;
        }

        nodeArr[count] = new Term(itemId, itemText, parentObject, this, itemActionHandler, nodeTypeId, nodeTypeName);

        parentObject.childrenCount++;
        var tr = this.createTreeHtmlRow(nodeArr[count].htmlNode);

        if (this.loadInProgress) {
            nodeArr[count].htmlNode.parentNode.parentNode.style.display = "none";
        }

        parentObject.htmlNode.childNodes[0].appendChild(tr);

        if (children && this.xmlURL) {
            nodeArr[count].xmlLoaded = 0;
        } else {
            nodeArr[count].xmlLoaded = 1;
            nodeArr[count].childrenCount = 0;
        }

        nodeArr[count].tableRowPointer = tr;
        tr.nodem = nodeArr[count];

        if (parentObject.itemId == 0) {
            tr.childNodes[0].className = "hiddenRow";
        }

        if (!this.loadInProgress)
        {
            if (this.getOpenState(parentObject) < 0) {
                this.openItem(parentObject.id);
            }

            this.updatePlusMinusIcon(parentObject);
            this.updateTreeLine(parentObject);
            this.updatePlusMinusIcon(nodeArr[count]);
            if (parentObject.childrenCount >= 2) {
                this.updatePlusMinusIcon(nodeArr[parentObject.childrenCount - 2]);
                this.updateTreeLine(nodeArr[parentObject.childrenCount - 2]);
            }
            if (parentObject.childrenCount != 2) this.updatePlusMinusIcon(nodeArr[0]);
        }

        return nodeArr[count];
    }

};

/**
 *     create and return new line in tree
 *     @param: htmlObject - parent Node object
 */
OntologyBrowser.prototype.createTreeHtmlRow = function(htmlObject) {

    var tr = document.createElement(HTML_TR_ELEMENT);
    var td1 = document.createElement(HTML_TD_ELEMENT);
    var td2 = document.createElement(HTML_TD_ELEMENT);
    td1.appendChild(document.createTextNode(""));
    td2.colSpan = 3;
    td2.appendChild(htmlObject);
    tr.appendChild(td1);
    tr.appendChild(td2);
    return tr;

};

/**************************************************************************** END CONSTRUCTORS */

/***************************************************************************** CONFIG SETTERS */

OntologyBrowser.prototype.setHoverOutput = function(hoverOutput) {
    this.hoverOutput = hoverOutput;
};

OntologyBrowser.prototype.setHoverOutMessage = function(hoverOutMessage) {
    this.hoverOutMessage = hoverOutMessage;
};

OntologyBrowser.prototype.setImagePath = function(newPath) {
    this.imgPath = newPath;
};

OntologyBrowser.prototype.setXMLAutoLoading = function(url) {
    this.xmlURL = url;
};

OntologyBrowser.prototype.setOnClickHandler = function(func) {
    this.clickFuncHandler = makeFunction(func);
};

OntologyBrowser.prototype.setOnOpenHandler = function(func) {
    this.openFuncHandler = makeFunction(func);
};

OntologyBrowser.prototype.setOnDblClickHandler = function(func) {
    this.dblclickFuncHandler = makeFunction(func);
};

OntologyBrowser.prototype.setRootOnClickHandler = function(func) {
    this.htmlNode.actionHandler = makeFunction(func);
};


/************************************************************************* END CONFIG SETTERS */

/****************************************************************************** VALUE GETTERS */

OntologyBrowser.prototype.getSelectedItemText = function() {

    if (this.lastSelected) {
        return this.lastSelected.parentObject.label;
    } else {
        return ("");
    }

};

OntologyBrowser.prototype.getSelectedItemId = function() {

    if (this.lastSelected) {
        return this.lastSelected.parentObject.itemId;
    } else {
        return ("");
    }

};

/************************************************************************** END VALUE GETTERS */

/****************************************************************************** CLICK HANDLER */

/**
 *     mouseOver item  event handler
 */
OntologyBrowser.prototype.mouseOverHandler = function() {
    if (this.parentObject && this.parentObject.treeNode.hoverOutput) {
        if (this.parentObject.nodeTypeName != ROOT_TERM_TYPE) {
            var out = document.getElementById(this.parentObject.treeNode.hoverOutput);
            out.innerHTML = this.parentObject.label + " " + this.parentObject.nodeTypeName + " " + this.parentObject.parentObject.label;
        }
    }
};

/**
 *     mouseOut item  event handler
 */
OntologyBrowser.prototype.mouseOutHandler = function() {
    if (this.parentObject && this.parentObject.treeNode.hoverOutput) {
        var out = document.getElementById(this.parentObject.treeNode.hoverOutput);
        out.innerHTML = this.parentObject.treeNode.hoverOutMessage;
    }
};

/**
 *     ondblclick item  event handler
 */
OntologyBrowser.prototype.onDoubleClickHandler = function() {

    if (this.parentObject.treeNode.dblclickFuncHandler) {
        if (!this.parentObject.treeNode.dblclickFuncHandler(stripDuplicateTag(this.parentObject.id))) {
            return 0;
        }
    }

    if ((this.parentObject.closeable) && (this.parentObject.closeable != "0")) {
        this.parentObject.treeNode.toggleBranch(this.parentObject);
    } else {
        this.parentObject.treeNode.toggleBranch(this.parentObject, 2);
    }

};

/**
 *     onclick item event handler
 */
OntologyBrowser.prototype.onPlusMinusClickHandler = function() {

    if (this.parentObject.treeNode.openFuncHandler) {
        if (!this.parentObject.treeNode.openFuncHandler(this.parentObject.id, this.parentObject.treeNode.getOpenState(this.parentObject)))
            return 0;
    }

    if ((this.parentObject.closeable) && (this.parentObject.closeable != "0")) {
        this.parentObject.treeNode.toggleBranch(this.parentObject);
    } else {
        this.parentObject.treeNode.toggleBranch(this.parentObject, 2);
    }

};

/**
 *     select node text event handler
 *     @param: e - event object
 *     @param: htmlObject - node object
 *     @param: mode - if false - call onSelect event
 */
OntologyBrowser.prototype.onSelectClickHandler = function(e, htmlObject, mode) {

    if (!htmlObject) {
        htmlObject = this;
    }

    htmlObject.childNodes[0].className = "selectedItem";

    if ((htmlObject.parentObject.treeNode.lastSelected) &&
        (htmlObject.parentObject.treeNode.lastSelected != htmlObject)) {

        htmlObject.parentObject.treeNode.lastSelected.childNodes[0].className = "standardItem";

    }

    htmlObject.parentObject.treeNode.lastSelected = htmlObject;
    if (!mode) {
        if (htmlObject.parentObject.actionHandler) {
            htmlObject.parentObject.actionHandler(stripDuplicateTag(htmlObject.parentObject.id));
        }
    }

};

/**
 *     open/close node
 *     @param: itemObject - node object
 *     @param: mode - open/close mode [1-close 2-open](optional)
 */
OntologyBrowser.prototype.toggleBranch = function(itemObject, mode) {

    if (((this.xmlURL) && (!itemObject.xmlLoaded)) && (!mode)) {
        if (!this.loadInProgress) {
            itemObject.xmlLoaded = 1;
            this.addWaitNotificationNode(itemObject.id);
            //dont strip duplicate id tag here, because we'll need to return back to the proper node
            //but be aware that you'll need to strip it away on the server side to get the proper id
            this.loadXML(this.buildURL(itemObject.id));
        }
        return;
    }
    ;

    var nodeArr = itemObject.getChildNodes();
    var count = nodeArr.length;

    if (count > 1) {
        if (((nodeArr[1].style.display != "none") || (mode == 1)) && (mode != 2))
            nodestyle = "none";
        else
            nodestyle = "";

        for (var i = 1; i < count; i++) {
            nodeArr[i].style.display = nodestyle;
        }
    }

    this.updatePlusMinusIcon(itemObject);

}

/************************************************************************** END CLICK HANDLER */

/************************************************************************************ LOADING */
/**
 * method to build URL for ajax call
 */
OntologyBrowser.prototype.buildURL = function(itemId) {

    var url = this.xmlURL + getUrlSymbol(this.xmlURL) + ID_PARAM + escape(itemId);
    return url;

};
/**
 * method to perform ajax call
 */
OntologyBrowser.prototype.loadXML = function(url) {

    try {
        //test for IE5+
        obXmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
        doDebug("Created Msxml2.XMLHTTP");
    } catch (notIE5) {
        try {
            //test for IE4
            obXmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            doDebug("Created Microsoft.XMLHTTP");
        } catch (notIE4) {
            obXmlHttp = false;
        }
    }

    if (!obXmlHttp && typeof XMLHttpRequest != 'undefined') {
        try {
            netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
        } catch (e) {
            doDebug("Denied privilege: " + e);
        }
        obXmlHttp = new XMLHttpRequest();
        doDebug("Created XMLHttpRequest");
    }

    try {
        doDebug("Sending request: " + url);
        obCallbackObj = this;
        obXmlHttp.onreadystatechange = this.loadProgressMonitor;
        obXmlHttp.open("GET", url, true);
        obXmlHttp.send(null);
    } catch (err) {
        doDebug("Unable to send request: " + err);
        doDebug("Unable to send request: " + err.message);
    }

};

/**
 * method called on state change during ajax. on completion, callback obj method will
 * be executed.
 */
OntologyBrowser.prototype.loadProgressMonitor = function() {


    if (obXmlHttp.readyState != XML_COMPLETE) {  // not yet finished
        return;
    }

    if (obXmlHttp.status == HTML_OK) {
        doDebug("Got server response: " + obXmlHttp.statusText);

        obXmlDoc = obXmlHttp.responseXML;
        if (!obXmlDoc) {
            doDebug("Error: Invalid server response");
        }

        //on completion, parse XML
        obCallbackObj.parseXML();

    } else {
        doDebug("There was an error from the server: " + obXmlHttp.statusText);
    }

};

/**
 * will add a notification node in the tree while loading
 */
OntologyBrowser.prototype.addWaitNotificationNode = function(parentId) {

    this.createNode(parentId, "LOADING" + parentId, "Loading...", 0, false, OTHER_REL_TYPE, "Waiting...");
    this.allTree.style.cursor = "wait";
    this.loadInProgress = true;

}

/**
 * removes notification after load
 */
OntologyBrowser.prototype.removeWaitNotificationNode = function(parentId) {

    var sNode = this.findNodeById("LOADING" + parentId);
    if (sNode) {
        this.deleteNode(sNode.id, sNode);
    }
    this.allTree.style.cursor = "pointer";
    if (navigator.appName == 'Microsoft Internet Explorer') {
        this.allTree.style.cursor = "hand";
    }
    this.loadInProgress = false;

}

/**
 * will parse the returned xml from the server and create objects as required
 */
OntologyBrowser.prototype.parseXML = function() {

    var root = obXmlDoc.documentElement;

    var parentId = root.getAttribute(ROOT_ATTRIBUTE);

    //update loaded flag for parent
    var parentObj = this.findNodeById(parentId);
    parentObj.xmlLoaded = 1;

    //remove loading notification
    this.removeWaitNotificationNode(parentId);

    doDebug(root.childNodes.length + " children loaded for " + parentId);

    for (var i = 0; i < root.childNodes.length; i++) {

        var child = root.childNodes[i];

        //start parsing
        if (child.tagName == NODE_TAG) {

            var name = child.getAttribute(NAME_ATTRIBUTE);
            var cId = child.getAttribute(ACCESSION_ATTRIBUTE);
            doDebug("parsed: " + cId);
            var nodeTypeId = child.getAttribute(RELATION_ID_ATTRIBUTE);
            var nodeTypeName = child.getAttribute(RELATION_NAME_ATTRIBUTE);
            var hasChildren = child.getAttribute(CHILDREN_ATTRIBUTE);
            if (hasChildren == "1") {
                hasChildren = true;
            } else {
                hasChildren = false;
            }
            //unescapeHTML is from prototype.js
            this.createNode(parentId, cId, name.unescapeHTML(), 0, hasChildren, nodeTypeId, nodeTypeName);

        }//end parsing
    }
    ;
    //end for

    this.updateTree(parentId);
    this.loadInProgress = 0;

};

/******************************************************************************** END LOADING */

/************************************************************************** TREE MANIPULATION */

/**
 *     set correct tree-line and node images
 *     @param: itemObject - item object
 */
OntologyBrowser.prototype.updatePlusMinusIcon = function(itemObject) {

    var workArray = lineArray;
    if ((this.xmlURL) && (!itemObject.xmlLoaded)) {
        workArray = plusArray;
        //this sets the "folderclosed icon by default"
        if (itemObject.getNodeLeafImage().alt == "") {
            //if no alt text, we're using default folder icon set
            itemObject.getNodeLeafImage().src = this.imgPath + imageArray[2];
        }

    } else {
        if (itemObject.childrenCount) {
            if (itemObject.htmlNode.childNodes[0].childNodes[1].style.display != "none") {
                workArray = minusArray;
                if (itemObject.getNodeLeafImage().alt == "") {
                    //if no alt text, we're using default folder icon set
                    itemObject.getNodeLeafImage().src = this.imgPath + imageArray[1];
                }
            } else {
                workArray = plusArray;
                if (itemObject.getNodeLeafImage().alt == "") {
                    //if no alt text, we're using default folder icon set
                    itemObject.getNodeLeafImage().src = this.imgPath + imageArray[2];
                }
            }
        } else {
            if (itemObject.getNodeLeafImage().alt == "") {
                //if no alt text, we're using default folder icon set
                itemObject.getNodeLeafImage().src = this.imgPath + imageArray[0];
            }
        }
        //fixes a weird bug, never mind...
        this.allTree.childNodes[0].border = "1";
        this.allTree.childNodes[0].border = "0";
    }

    //update +/- sign with corresponding line
    var tempNum = 2;
    if (itemObject.parentObject) {
        tempNum = this.getNodeStatus(itemObject.id, itemObject.parentObject);
    }
    itemObject.getNodeLineImage().src = this.imgPath + workArray[tempNum];

};

/**
 *     set correct tree-line images
 *     @param: itemObject - item object
 */
OntologyBrowser.prototype.updateTreeLine = function(itemObject) {
    var sNode = itemObject.parentObject;
    if (sNode) {
        if ((this.getLineType(itemObject.id, sNode) == 0)) {
            for (var i = 1; i <= itemObject.childrenCount; i++) {
                //update the lines in the background of the 1st td of the row
                itemObject.getTRElement(i).childNodes[0].style.backgroundImage = "";
                itemObject.getTRElement(i).childNodes[0].style.backgroundRepeat = "";
            }
        } else {
            for (var i = 1; i <= itemObject.childrenCount; i++) {
                //update the lines in the background of the 1st td of the row
                itemObject.getTRElement(i).childNodes[0].style.backgroundImage = "url(" + this.imgPath + "line-straight.gif)";
                itemObject.getTRElement(i).childNodes[0].style.backgroundRepeat = "repeat-y";
            }
        }
    }
};

/**
 *     returns if node is opened, closed, has children or is leaf
 *     for use in array image selection
 *     @param: itemId - item id
 *     @param: itemObject - parent node object
 */
OntologyBrowser.prototype.getNodeStatus = function(itemId, itemObject) {

    if (itemObject.childrenCount <= 1) {
        if (itemObject.id == this.rootId)
            return 4;
        else
            return 0;
    }

    if (itemObject.getTRElement(1).nodem.id == itemId) {
        if (!itemObject.id) {
            return 2;
        } else {
            return 1;
        }
    }

    if (itemObject.getTRElement(itemObject.childrenCount).nodem.id == itemId) {
        return 0;
    }

    return 1;

};

/**
 *     return type of node
 *     @param: itemId - node id
 *     @param: itemObject - parent node object
 */
OntologyBrowser.prototype.getLineType = function(itemId, itemObject) {

    if (itemObject.getTRElement(itemObject.childrenCount).nodem) {
        if (itemObject.getTRElement(itemObject.childrenCount).nodem.id == itemId) {
            return 0;
        }
        return 1;
    } else {
        return 0;
    }

}

/**
 *     refreshes the tree images from selected level
 *     @param: tree - whole tree
 *     @param: itemObject - current item
 */
OntologyBrowser.prototype.updateTree = function(tree, itemObject) {

    //make sure we have a root to redraw from
    var tmpObj;
    tmpObj = this.findNodeById(tree);
    if (!tmpObj) {
        return 0;
    }

    for (var i = 0; i < tmpObj.childrenCount; i++)
    {
        tmpObj.childNodes[i].htmlNode.parentNode.parentNode.style.display = "";
        this.updateTree(tmpObj.childNodes[i].id);
        this.updateTreeLine(tmpObj.childNodes[i]);
        this.updatePlusMinusIcon(tmpObj.childNodes[i]);
    }
    ;
    this.updateTreeLine(tmpObj);
    this.updatePlusMinusIcon(tmpObj);

};

/**
 *     looks to see if node is opened or closed
 *     @param: itemId - node obj
 */
OntologyBrowser.prototype.getOpenState = function(itemObject) {
    var nodes = itemObject.getChildNodes();
    if (nodes.length <= 1) {
        return 0;
    }
    if (nodes[1].style.display != "none") {
        return 1;
    } else {
        return -1;
    }
};

/**
 *     expand node
 *     @param: itemId - identificator of node
 */
OntologyBrowser.prototype.openItem = function(itemId) {
    var temp = this.findNodeById(itemId);
    if (!temp) {
        return 0;
    } else {
        this.toggleBranch(temp, 2);
        if ((temp.parentObject) && (this.getOpenState(temp.parentObject) < 0)) {
            this.openItem(temp.parentObject.id);
        }
    }
};

/**
 *     collapse node
 *     @param: itemId - identificator of node
 */
OntologyBrowser.prototype.closeItem = function(itemId) {
    if (this.rootId == itemId) return 0;
    var temp = this.findNodeById(itemId);
    if (!temp) {
        return 0;
    } else if (temp.closeable) {
        this.toggleBranch(temp, 1);
    }
};


/**
 *     delete node
 *     @param: itemId - target node identificator
 *     @param: htmlObject - target node object
 */
OntologyBrowser.prototype.deleteNode = function(itemId, htmlObject) {

    this.recursiveRemoveFromStorageArray(htmlObject);

    if ((!htmlObject) || (!htmlObject.parentObject)) return 0;
    var tempos = 0;
    var tempos2 = 0;
    if (htmlObject.tableRowPointer.nextSibling)  tempos = htmlObject.tableRowPointer.nextSibling.nodem;
    if (htmlObject.tableRowPointer.previousSibling)  tempos2 = htmlObject.tableRowPointer.previousSibling.nodem;

    var sN = htmlObject.parentObject;
    var count = sN.childrenCount;
    var nodeArr = sN.childNodes;
    for (var i = 0; i < count; i++)
    {
        if (nodeArr[i].id == itemId) {
            sN.htmlNode.childNodes[0].removeChild(nodeArr[i].tableRowPointer);
            nodeArr[i] = 0;
            break;
        }
    }
    this.correctChildList(count, nodeArr);
    sN.childrenCount--;

    if (tempos) {
        this.updatePlusMinusIcon(tempos);
        this.updateTreeLine(tempos);
    }
    if (tempos2) {
        this.updatePlusMinusIcon(tempos2);
        this.updateTreeLine(tempos2);
    }
};

/*********************************************************************** END TREE MANIPULATION */

/******************************************************************* INTERNAL OBJECT HANDLING */

/**
 *     return node object
 *     @param: itemId - node identificator
 */
OntologyBrowser.prototype.findNodeById = function(itemId) {
    for (var i = 0; i < this.nodeCount; i++) {
        if (this.id_array[i] == itemId) {
            return this.node_array[i];
        }
    }
    return 0;
};

/**
 *     add to storage array
 *     @param: itemId - node identificator
 *     @param: itemObject - node object
 */
OntologyBrowser.prototype.addToStorageArrays = function(itemId, itemObject) {

    if (this.findNodeById(itemId)) {
        d = new Date();
        itemId = d.valueOf() + DUPLICATE_ID_TAG + itemId;
        return this.addToStorageArrays(itemId, itemObject);
    }

    this.id_array[this.nodeCount] = itemId;
    this.node_array[this.nodeCount] = itemObject;
    this.nodeCount++;

    return itemId;

};

/**
 *     correct childNode list after node deleting
 *     @param: count - childNodes collection length
 *     @param: nodeArr - childNodes collection
 */
OntologyBrowser.prototype.correctChildList = function(count, nodeArr) {
    count--;
    for (var i = 0; i < count; i++) {
        if (nodeArr[i] == 0) {
            nodeArr[i] = nodeArr[i + 1];
            nodeArr[i + 1] = 0;
        }
    }
    ;
};

/**
 *     unregister node
 *     @param: itemId - node identificator
 */
OntologyBrowser.prototype.removeFromStorageArray = function(itemId) {
    for (var i = 0; i < this.nodeCount; i++) {
        if (this.id_array[i] == itemId) {
            this.id_array[i] = this.id_array[this.nodeCount - 1];
            this.node_array[i] = this.node_array[this.nodeCount - 1];
            this.nodeCount--;
            this.id_array[this.nodeCount] = 0;
            this.node_array[this.nodeCount] = 0;
        }
    }
};

/**
 *     uregister all child nodes of target node
 *     @param: itemObject - node object
 */
OntologyBrowser.prototype.recursiveRemoveFromStorageArray = function(itemObject) {
    for (var i = 0; i < itemObject.childrenCount; i++) {
        this.recursiveRemoveFromStorageArray(itemObject.childNodes[i]);
        this.removeFromStorageArray(itemObject.childNodes[i].id);
    }
    ;
    this.removeFromStorageArray(itemObject.id);
};

/*************************************************************** END INTERNAL OBJECT HANDLING */

/*************************************************************************** GLOBAL UTILITIES */

function getUrlSymbol(str) {
    if (str.indexOf("?") != -1)
        return "&"
    else
        return "?"
}

function doDebug(str) {
    var divEl = document.getElementById(DEBUG_ELEMENT);
    if (divEl != null) {
        divEl.innerHTML = new Date().toString() + ": " + str + "<br>" + divEl.innerHTML;
    } else {
        if (DEBUG_ALLOW_POPUPS) {
            alert(str);
        }
    }
}

function makeFunction(func) {
    if (typeof(func) == "function") {
        return func;
    } else {
        return eval(func);
    }
}
/*
 * remove duplicate tag that is appended to ID when object with same id already
 * present in storage array
 */
function stripDuplicateTag(idStr) {

    var ndx = idStr.indexOf(DUPLICATE_ID_TAG);
    if (ndx > 0) {
        return idStr.substring(ndx + DUPLICATE_ID_TAG.length);
    } else {
        return idStr;
    }
}

/*************************************************************************** END UTILITIES */

/*************************************************************************** ImageMapGenerator */

function initializeXmlHttp(){

    try {
        //test for IE5+
        igXmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
        doDebug("Created Msxml2.XMLHTTP");
    } catch (notIE5) {
        try {
            //test for IE4
            igXmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            doDebug("Created Microsoft.XMLHTTP");
        } catch (notIE4) {
            igXmlHttp = false;
        }
    }

    if (!igXmlHttp && typeof XMLHttpRequest != 'undefined') {
        try {
            netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
        } catch (e) {
            doDebug("Denied privilege: " + e);
        }
        igXmlHttp = new XMLHttpRequest();
        doDebug("Created XMLHttpRequest");
    }

}

var ImageMapGenerator = Class.create();
ImageMapGenerator.prototype = {

    initialize: function(serverPath, termId, termName, ontologyName, imgType) {
        //params
        this.serverPath = serverPath;
        this.termId = termId;
        this.termName = termName;
        this.ontologyName = ontologyName;
        this.imgType = imgType;
        this.callbackImgFunction = 0;
        this.callbackMapFunction = 0;
        this.url = this.serverPath + "/generateSSFiles.do?termId=" + escape(this.termId) + "&termName=" + escape(this.termName) + "&ontologyName=" + escape(this.ontologyName) + "&graphType=" + escape(this.imgType);
        this.mapurl = this.serverPath + "/serveMap.do?mapFileName=";
        doDebug("ImageGenerator instantiated to " + this.url);
    },

    setImageCallbackFunction: function(fn){
        this.callbackImgFunction = fn;
    },

    setMapCallbackFunction: function(fn){
        this.callbackMapFunction = fn;
    },

    updateClientData: function() {

        initializeXmlHttp();
        try {
            doDebug("Sending request: " + this.url);
            igCallbackObj = this;
            igXmlHttp.onreadystatechange = this.processSSXML;
            igXmlHttp.open("GET", this.url, true);
            igXmlHttp.send(null);
        } catch (err) {
            doDebug("Unable to send request: " + err);
            doDebug("Unable to send request: " + err.message);
        }

    },

    fetchMapContent: function() {

        initializeXmlHttp();
        try {
            doDebug("Sending request: " + this.mapurl);
            //don't update callback object for second ajax call as it's already properly set
            //callbackObj = this;
            igXmlHttp.onreadystatechange = this.processMapXML;
            igXmlHttp.open("GET", this.mapurl, true);
            igXmlHttp.send(null);
        } catch (err) {
            doDebug("Unable to send request: " + err);
            doDebug("Unable to send request: " + err.message);
        }

    },

    /**
     * method called on state change during ajax. on completion, callback obj method will
     * be executed.
     */
    processSSXML : function() {

        if (igXmlHttp.readyState != XML_COMPLETE) {  // not yet finished
            return;
        }

        if (igXmlHttp.status == HTML_OK) {
            doDebug("Got server response: " + igXmlHttp.statusText);

            igXmlDoc = igXmlHttp.responseXML;
            if (!igXmlDoc) {
                doDebug("Error: Invalid server response");
            }

            //on completion, parse XML
            var root = igXmlDoc.documentElement;

            //get img file
            items = root.getElementsByTagName(IMG_FILE);
            if (items.length == 1) {
                igCallbackObj.imgFile = items[0].firstChild.nodeValue;
                doDebug("img file is " + igCallbackObj.imgFile);
                igCallbackObj.callbackImgFunction(igCallbackObj.imgFile);
            }

            //get map file
            var items = root.getElementsByTagName(MAP_FILE);
            if (items.length == 1) {
                igCallbackObj.mapFile = items[0].firstChild.nodeValue;
                doDebug("map file is " + igCallbackObj.mapFile);
                igCallbackObj.mapurl += igCallbackObj.mapFile;
                //this is asynch and needs to finish before it returns
                igCallbackObj.fetchMapContent();
            }


        } else {
            doDebug("There was an error from the server: " + igXmlHttp.statusText);
        }


    },

    /**
     * method called on state change during ajax. on completion, callback obj method will
     * be executed.
     */
    processMapXML : function() {

        if (igXmlHttp.readyState != XML_COMPLETE) {  // not yet finished
            return;
        }

        if (igXmlHttp.status == HTML_OK) {
            doDebug("Got server response: " + igXmlHttp.statusText);

            igXmlDoc = igXmlHttp.responseXML;
            var root = igXmlDoc.documentElement;

            //get img file
            var areas = root.getElementsByTagName("area");

            var mapStr = "";

            var area, attr;
            for (var i =0; i < areas.length;i++){

                area = areas[i];

                mapStr += "<area ";

                for (var j = 0; j < area.attributes.length; j++){
                    attr = area.attributes[j];

                    mapStr += attr.nodeName;
                    mapStr += "=" ;
                    if ("title" != attr.nodeName){
                        mapStr += "\""+ attr.nodeValue + "\"";
                    } else {
                        var tmpStr = attr.nodeValue;
                        tmpStr = tmpStr.substring(tmpStr.lastIndexOf("\\n")+2);
                        mapStr += "\""+ tmpStr + "\"";
                    }

                    mapStr += " ";

                }

                mapStr += "/>";

            }

            igCallbackObj.callbackMapFunction(mapStr);

        } else {
            doDebug("There was an error from the server: " + igXmlHttp.statusText);
        }

    }

}

