// branch test
EventHierarchyPane = function () {};

Object.extend(EventHierarchyPane.prototype, {
	eventhierarchydtree : null,
	eventsearchresultshierarchydtree : null,
	focus_pathway_id : null,
	treehighlitesurl : '/cgi-bin/entitylevelview/treehighlites',
	treehighlites1 : new Array(), //db_id of the current event instance (if it is an event)
	treehighlites2 : new Array(), //db_ids of the ancestors of the current event
	expandingTree : new Array(), // when auto expanding search results tree, keep track of dbids of nodes on path
	
	/*
	 *  Defines the shade of green tree nodes will be coloured. ancestors of selected node are different shade.
	 *  1cls for currently selected node, 2cls for ancestors
	 */
	treehighlites1cls : 'highlitedTreeNode1',
	treehighlites2cls : 'highlitedTreeNode2',
	focustreetrunkcls : 'focustreetrunkcls',
    searchResultsList : null,
	searchResultsIdList : null,
	searchresultstreehighlites1 : new Array(), //db_id of the current event instance (if it is an event)
	searchresultstreehighlites2 : new Array(), //db_ids of the ancestors of the current event
	treeTrunkLoadedEvent : new YAHOO.util.CustomEvent("TreeTrunkLoadedEvent", this),
	treeNodeMouseOverEvent : new YAHOO.util.CustomEvent("TreeNodeMouseOverEvent", this),
	treeNodeMouseOutEvent : new YAHOO.util.CustomEvent("TreeNodeMouseOutEvent", this),
	treeNodeClickEvent : new YAHOO.util.CustomEvent("TreeNodeClickEvent", this),
	searchResultsLoadedEvent : new YAHOO.util.CustomEvent("SearchResultsLoadedEvent", this),
	newSpeciesTreeLoadedEvent : new YAHOO.util.CustomEvent("NewSpeciesTreeLoadedEvent", this),
	speciesInitializedEvent : new YAHOO.util.CustomEvent("SpeciesInitializedEvent", this),
	topNodeFull:null,
 	focus_pathway_id:0,	
 	keepExpanding:false, /*	
 							Used in the search results hierarchy. Keep expanding the pathway down the deepest pathway.
 	 						This ensures that the user is presented with a pathway containing the query entity. 
 	 						*/
 	clickSearchTreeNode:null,
 	eventTracker : new EventTracker({
    	category: "Hierarchy"
    }),
	
 	/*
	 * Previous species pathway id is used when switching species. It is the id of the currently displayed pathway and is used
	 * to retrieve identifier of orthologous pathway in the new species.
	 */
	loadNodeData : function (node, fnLoadComplete, prevSpeciesPathwayId, pathwayName)  {
		var t = this;
		//prepare URL for XHR request:
		var sUrl = "/cgi-bin/entitylevelview/yuieventhierarchy?DB=" + this.db;
		if (this.focus_species_id) sUrl += "&FOCUS_SPECIES_ID=" + this.focus_species_id;
		if (node.data.dbid ) sUrl += "&ID=" + node.data.dbid;
		var ol;
		if (!node.data.dbid && $("hierarchytab").parentNode && $("hierarchytab").parentNode.parentNode && $("hierarchytab").parentNode.parentNode.parentNode && $("hierarchytab").parentNode.parentNode.parentNode.parentNode) {
			ol = overlayElementWithTimeIndicator($("hierarchytab").parentNode.parentNode.parentNode.parentNode);
		}
		
		//prepare our callback object
		var callback = {
			//if our XHR call is successful, we want to make use
			//of the returned data and create child nodes.
			success: function(oResponse) {
				//var oResults = eval(oResponse.responseText);
				try{
				var oResults = getJsonFromRawText(oResponse.responseText);	
				if((oResults) && (oResults.length)) {
					for (var i=0, j=oResults.length; i<j; i++) {
						var html = '<span class="nowrap">';
                        html += '<span class="icon-' + oResults[i].cls + '">';
						if (oResults[i].status) {
							html += '<span class="neworupdated"> ' + oResults[i].status + ' </span> ';
						}
						html += '<a href="javascript:void(0)">' + oResults[i].name + '</a></span>';
						var tempNode = new YAHOO.widget.HTMLNode(
							{"html" : html, "dbid" : oResults[i].id, "cls" :  oResults[i].cls,"name" : oResults[i].name,"hasdiagram" : oResults[i].hasdiagram, "childpaths" : oResults[i].childpaths }, node, false, true);
						if (! oResults[i].expands) tempNode.isLeaf = true;
					}
				}
				/*
					When we're done creating child nodes, we execute the node's
					loadComplete callback method which comes in via the argument
					in the response object (we could also access it at node.loadComplete,
					if necessary):
				*/
				oResponse.argument.fnLoadComplete();
				//Have to bind the listeners only after the nodes have been rendered
				$A(node.children).each(function(n){
					var el = $(n.getEl()).down('a');
					Event.observe(el, 'mouseover', t.fireTreeNodeMouseOverEvent.bindAsEventListener(t, new Array(n.data.dbid, n.data.childpaths)));
					Event.observe(el, 'mouseout', t.fireTreeNodeMouseOutEvent.bindAsEventListener(t));
					Event.observe(el, 'click', t.handleClickOnEventHierarchy.bindAsEventListener(t));
				});
				//Can highlite only after rendering
				t.highliteTreeNodeToBeHighlited();
				if (ol) ol.remove();
				
				//log("t.treehighlites2 = " + t.treehighlites2);
				//log("node.children = " + node.children);
				
				// Also have to have the following bit here as at the time Node.expand is called
				// the child nodes haven't been highlighted yet.
				$A(node.children).each(function(n){
					if (t.treehighlites2.include(n.data.dbid)) {
						t.scrollToNode(n);
						
						/*
						 * The following try/catch block handles the scenario when the search results tree
						 * is first built. When this happens the main tree is expanded to synchronize (same nodes
						 * opened) with the state of the search results tree. When the main tree is synchronized 
						 * with the search results tree, 'click' that pathway tree. This causes that pathway to be 
						 * selected (display the diagram, updating highlighting etc). 
						 * The query entity is selected in the diagram.
						 * 
						 */
						try{
							/*
							 * clickSearchTreeNode is the id of the last pathway reached in the search results
							 * tree. Compare it to the current child node to see if we have found the matching
							 * pathway in the main tree.
							 */
							if(t.clickSearchTreeNode != null && t.clickSearchTreeNode == n.data.dbid ){
								/*
								 * An event can be part of multiple top level pathways. Check that the top
								 * level pathway of this event is the same as the one selected in the search
								 * results tree.
								 */
								var topNodeForChild = t.getTopNodeForNodeFull(n);
								if(topNodeForChild.data.dbid == t.topNodeFull){
									t.clickSearchTreeNode = null;
									var searchnode = null;
									/*
									 * If the query entity is a pathway, select it. Otherwise, select the pathway it is found in.
									 */
									if(n.data.cls == "Pathway"){
										searchnode = t.eventsearchresultshierarchydtree.getNodeByProperty("dbid", n.data.dbid);
									}else{
										searchnode = t.eventsearchresultshierarchydtree.getNodeByProperty("dbid", node.data.dbid);
									}
									t.processClickedNodeOnSearchResultsHierarchy(searchnode);
								}
							}
						}catch(err){
						}
						n.expand();
					}
				});
				if(prevSpeciesPathwayId){
					t.newSpeciesTreeLoadedEvent.fire(prevSpeciesPathwayId, pathwayName, t.topNodeFull);
				}}catch(err){
					alert(err.message);
				}
			},
			
			//if our XHR call is not successful, we want to
			//fire the TreeView callback and let the Tree
			//proceed with its business.
			failure: function(oResponse) {
				log("Failed to process XHR transaction.", "info");
				oResponse.argument.fnLoadComplete();
				if (ol) ol.remove();
			},
			
			//our handlers for the XHR response will need the same
			//argument information we got to loadNodeData, so
			//we'll pass those along:
			argument: {
				"node": node,
				"fnLoadComplete": fnLoadComplete
			}
		};
		
		//With our callback object ready, it's now time to 
		//make our XHR call using Connection Manager's
		//asyncRequest method:
		YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
	},
	loadSearchResultsNodeData : function (node, fnLoadComplete, searchResultsHierarchy)  {
		var t = this;
		/*
		 * prepare URL for XHR request:
		 * 
		 */
		var sUrl = "/cgi-bin/entitylevelview/yuieventhierarchy?DB=" + this.db;
		if (this.focus_species_id) sUrl += "&FOCUS_SPECIES_ID=" + this.focus_species_id;
		if (node.data.dbid) sUrl += "&ID=" + node.data.dbid;
		var ol;
		if (!node.data && $("searchresultstab").parentNode && $("searchresultstab").parentNode.parentNode) {
			ol = overlayElementWithTimeIndicator($("searchresultstab").parentNode.parentNode);
		}
		
		//prepare our callback object
		var callback = {
			//if our XHR call is successful, we want to make use
			//of the returned data and create child nodes.
			success: function(oResponse) {
			//log("oResponse.responseText = " + oResponse.responseText);
			//var oResults = eval(oResponse.responseText);
				var oResults = getJsonFromRawText(oResponse.responseText);
				
			if(searchResultsHierarchy){
				t.searchResultsIdList = searchResultsHierarchy;
			}
			oResults = t.filterSearchResults(oResults, t.searchResultsIdList);
			if((oResults) && (oResults.length)) {
				for (var i=0, j=oResults.length; i<j; i++) {
					var html = '<span class="nowrap">';
                    html += '<span class="icon-' + oResults[i].cls + '">';
					if (oResults[i].status) {
						html += '<span class="neworupdated"> ' + oResults[i].status + ' </span> ';
					}
					html += '<a href="javascript:void(0)">' + oResults[i].name + '</a></span>';
					var tempNode = new YAHOO.widget.HTMLNode(
						{"html" : html, "dbid" : oResults[i].id, "name" : oResults[i].name,
						"hasdiagram" : oResults[i].hasdiagram, "cls" : oResults[i].cls}, node, false, true);
					if (! oResults[i].expands) tempNode.isLeaf = true;
					}
				}
				
				//When we're done creating child nodes, we execute the node's
				//loadComplete callback method which comes in via the argument
				//in the response object (we could also access it at node.loadComplete,
				//if necessary):
				oResponse.argument.fnLoadComplete();
				var childNodes = new Array();
				var childNodesIndex=0;
				//Have to bind the listeners only after the nodes have been rendered
				$A(node.children).each(function(n){
					if(t.searchResultsIdList){
                        for(var i=0; i<t.searchResultsIdList.length; i++){
                        	if(n.data.dbid == t.searchResultsIdList[i]){
                        		childNodes[childNodesIndex] = n;
                        		childNodesIndex++;
                        	}
					  }
					}
				});
				for(var i=0, childNodesLength = childNodes.length; i<childNodesLength; i++){
					var el = $(childNodes[i].getEl()).down('a');
					Event.observe(el, 'mouseover', t.fireTreeNodeMouseOverEvent.bindAsEventListener(t, childNodes[i].data.dbid));
					Event.observe(el, 'mouseout', t.fireTreeNodeMouseOutEvent.bindAsEventListener(t));
					Event.observe(el, 'click', t.handleClickOnSearchResultsHierarchy.bindAsEventListener(t));
				}
				//Can highlite only after rendering
				if (ol) ol.remove();
				
				//log("t.treehighlites2 = " + t.treehighlites2);
				//log("node.children = " + node.children);
				
				/* 
				 * Also have to have the following bit here as at the time Node.expand is called
				 * the child nodes haven't been highlited yet.
				 */
				$A(node.children).each(function(n){
					var nIndex = $A(node.children).indexOf(n);
					/*
					 * nIndex of 0. Only expand the first path down to bottom.
					 * 
					 */
					if (t.treehighlites2.include(n.data.dbid) || (t.keepExpanding == true && nIndex==0)) {
						t.expandingTree.push(n.data.dbid);
						t.scrollToNode(n);
						
						/*
						 * When the search tree is first loaded, the first path is expanded down to the bottom level
						 * Detect when bottom is reached (reaction node encountered) and signal main tree to expand
						 * to this level. This has to happen before the bottom pathway node can be clicked in the
						 * search results tree. 
						 */
						
						if(((n.data.cls == "Reaction" || n.data.cls == "BlackBoxEvent") 
								|| ( n.data.cls == "Pathway" && n.data.dbid == t.currentSearchEntityId))
								&& t.keepExpanding == true){
							t.keepExpanding = false;
							t.clickSearchTreeNode = n.data.dbid;
							t.treehighlites2 = t.expandingTree;
							
							t.expandingTree = new Array();
							try{
								var mainTreeNode = null;
								/*
								 * Handle the case where query entity is a pathway. 
								 */
								if(node._type != "RootNode" && (n.data.cls != "Pathway" && n.data.dbid != t.currentSearchEntityId)){
									mainTreeNode = t.eventhierarchydtree.getNodeByProperty("dbid", node.data.dbid);
								}else{
									mainTreeNode = t.eventhierarchydtree.getNodeByProperty("dbid", n.data.dbid);
								}
							    /*
							     * If mainTreeNode is defined, the event with dbid has already neem unfurled in the
							     * main tree.
							     */
							    if(mainTreeNode){
							    	var searchNode = t.eventsearchresultshierarchydtree.getNodeByProperty("dbid", mainTreeNode.data.dbid);
							    	t.processClickedNodeOnSearchResultsHierarchy(searchNode);
							    }else{
							    	/*
							    	 * The event has not been unfurled in the main tree. In this case, starting from the search node 
							    	 * iterate up the main tree using the dbids of the ancestors of the search node until we find a 
							    	 * node that has been loaded. Then unfurl it.
							    	 * We can't just expand the top level pathway in the main tree - it might already have been expanded
							    	 * in which case loadNodeData won't be called and its child nodes won't be expanded.
							    	 */
							    	mainTreeNode = t.getDeepestLoadedNodeInMainTreeForSearchResultsNode(node);
							    	mainTreeNode.expand();
							    }
							}catch(err){
							}
						}
						n.expand();
					}
				});
				/*
				 * The main tree should be kept synchronized with the search results tree. When nodes are clicked or expanded in the
				 * search results tree, do the same for the equivalent node in the main tree.
				 */
				if(t.keepExpanding == false){
					var maintreenodes = t.eventhierarchydtree.getNodesByProperty("dbid", node.data.dbid);
					if(maintreenodes){
						for(var maini=0; maini<maintreenodes.length;maini++){
							maintreenode = maintreenodes[maini];
							maintreenode.expand();
						}
					}
			}
			},
			
			//if our XHR call is not successful, we want to
			//fire the TreeView callback and let the Tree
			//proceed with its business.
			failure: function(oResponse) {
				log("Failed to process XHR transaction.", "err");
				oResponse.argument.fnLoadComplete();
				if (ol) ol.remove();
			},
			
			//our handlers for the XHR response will need the same
			//argument information we got to loadNodeData, so
			//we'll pass those along:
			argument: {
				"node": node,
				"fnLoadComplete": fnLoadComplete
			}
		};
		
		//With our callback object ready, it's now time to 
		//make our XHR call using Connection Manager's
		//asyncRequest method:
		YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
	},
	
	/*
	 * Given a node from the search results tree, iterate from it through its ancestors
	 * and return the first node with a matching identifier in the main tree. Used when main tree is 
	 * not synchronized with search results tree. This can happen when the search results tree is unfurled
	 * automatically. Also, if matching pathway is partly unfurld in main tree, loadNodeData is might not be
	 * called as the matching node could already be unfurled. This can be a problem because even if an identifier
	 * for a child node in the main tree is present in treehighlites, its parent has already been expanded and
	 * expand is never called on the child node.
	 */
	getDeepestLoadedNodeInMainTreeForSearchResultsNode : function(searchNode){
		var mainTreeNode = this.eventhierarchydtree.getNodeByProperty("dbid", searchNode.data.dbid);
		if (mainTreeNode == null && searchNode.parent && searchNode.parent.parent) {
	         return this.getDeepestLoadedNodeInMainTreeForSearchResultsNode(searchNode.parent);
        } else {
		     return mainTreeNode;
        }
	},
	
	/*
	 * Given a dbid of an event, find the node in the tree representing that event and call expand. When the tree was set up,
	 * the loadNodeData function was assigned as handler for the expand event. When expanded, the tree should auto expand all the
	 * descendant events with identifiers present in the treehighlights structure.
	 */
    expandNodeById:function(dbid){
		var node = this.eventhierarchydtree.getNodeByProperty("dbid", dbid);
		if(node){
			node.expand();
		}
	},
    filterSearchResults: function(nodes, searchResultsHierarchy){
          var filteredArray = new Array();
          var filteredIndex=0;
	      for (var index = 0, len = nodes.length; index < len; ++index) {
	    	  for (var resultsIndex = 0, resultsLen = searchResultsHierarchy.length; resultsIndex < resultsLen; resultsIndex++){
                     if(nodes[index].id == searchResultsHierarchy[resultsIndex]){
                    	 filteredArray[filteredIndex] = nodes[index];
                    	 filteredIndex++;
                     }
	    	  }
        }
	    return filteredArray;
	},
	/*
	 * Current id is used when switching species. It is the id of the currently displayed pathway and is used
	 * to retrieve identifier of orthologous pathway in the new species.
	 */
	buildEventHierarchy : function (currentId, pathwayName) {
		var t = this;
		YAHOO.util.Event.onDOMReady(function() {
			t.eventhierarchydtree = new YAHOO.widget.TreeView("hierarchytab");
		   	t.eventhierarchydtree.setDynamicLoad(t.loadNodeData.bind(t), 1);
	   		var root = t.eventhierarchydtree.getRoot();
	   		t.loadNodeData(root, t.completeLoadingTreeTrunk.bind(t), currentId, pathwayName);
	   		t.eventhierarchydtree.subscribe("expand",t.highliteTreeNodeToBeHighlited,t,true);
		})	
	},
	
	/*
	 * entityId is the id of the entity clicked on in the search results. This will be used to select the query
	 * entity in the diagram.
	 *
	 */
	buildSearchResultsHierarchy : function (hierarchy, leftpaneloverlay, entityId) {
		var t = this;
		t.currentSearchEntityId = entityId;
		this.leftpaneloverlay = leftpaneloverlay;
		YAHOO.util.Event.onDOMReady(function() {
		   	t.eventsearchresultshierarchydtree = new YAHOO.widget.TreeView("searchresultstab");
		   	t.eventsearchresultshierarchydtree.setDynamicLoad(t.loadSearchResultsNodeData.bind(t), 1);
	   		var root = t.eventsearchresultshierarchydtree.getRoot();
	   		t.loadSearchResultsNodeData(root, t.completeLoadingSearchResultsTreeTrunk.bind(t), hierarchy);
	   		t.eventsearchresultshierarchydtree.subscribe("expand",t.highliteTreeNodeToBeHighlited,t,true);
		})	
	},
	highliteTreeNodeToBeHighlited : function () {
		this.highliteTreeNodesByEventDbIds(this.treehighlites1,this.treehighlites1cls);
		this.highliteTreeNodesByEventDbIds(this.treehighlites2,this.treehighlites2cls);
	},
	/*
	 * Run when the main pathway tree has finished loading.
	 */
	completeLoadingTreeTrunk : function () {
		this.eventhierarchydtree.draw();
		var treenode = this.eventhierarchydtree.getNodeByProperty('dbid',this.focus_pathway_id);
		this.makeBrowserSpecificMods();
		if (treenode) {
			treenode.highlite(this.focustreetrunkcls);
			treenode.highlite(this.treehighlites1cls);
		}
		this.treeTrunkLoadedEvent.fire();
		this.scrollToFocusPathway();
	},
	/*
	 * Run when the search results tree has finished loading.
	 */
	completeLoadingSearchResultsTreeTrunk : function () {
		try{
		this.eventsearchresultshierarchydtree.draw();
		this.makeBrowserSpecificModsForSearch();
		try{
			this.leftpaneloverlay.remove();
		}catch(err){
			
		}
		this.searchResultsLoadedEvent.fire();
		this.removeSearchResultsTreeNodeHilites();
		/*
		 * Now that the search results hierarchy is loaded, select first pathway
		 */
		var rootNode = this.eventsearchresultshierarchydtree.getRoot();
		var children = $A(rootNode.children);
		if(children && children.length > 0){
			var childNode = children[0];
			this.topNodeFull = childNode.data.dbid;
			this.keepExpanding = true;
		}
		}catch(err){
			this.leftpaneloverlay.remove();
		}
	},
	scrollToFocusPathway : function () {
		var treenode = this.eventhierarchydtree.getNodeByProperty('dbid',parseInt(this.focus_pathway_id));
		if ((treenode != null) && $("hierarchytab").parentNode) {
			this.scrollToNode(treenode);
		}
	},
	/*
	 * 
	 */
	getDbIdOfDeepestLoaded : function (dbidList, dtree) {
		for(var i=0;i<dbidList.length; i++){
			var node = dtree.getNodeByProperty('dbid',parseInt(dbidList[i]));
			if(node){
				return parseInt(dbidList[i]);
			}
		}
		return null;
	},
	getDbIdOfDeepestLoadedByTopNodeId : function (dbidList, dtree, topNodeId) {
		for(var i=0;i<dbidList.length; i++){
			var nodes = dtree.getNodesByProperty('dbid',parseInt(dbidList[i]));
			if(nodes){
			//var node = dtree.getNodeByProperty('dbid',parseInt(dbidList[i]));
			for(var j=0; j<nodes.length; j++){
				var node = nodes[j];
				if(node){
					var topNode = this.getTopNodeForNodeFull(node);
					if(topNode.data.dbid == topNodeId){
						return parseInt(dbidList[i]);
					}
				}
			 }
		  }
		}
		return null;
	},
	getDeepestLoadedTreeNodeByTopNodeId : function (dbidList, dtree, topNodeId) {
		for(var i=0;i<dbidList.length; i++){
			var nodes = dtree.getNodesByProperty('dbid',parseInt(dbidList[i]));
			if(nodes){
			for(var j=0; j<nodes.length; j++){
				var node = nodes[j];
				if(node){
					var topNode = this.getTopNodeForNodeFull(node);
					if(topNode.data.dbid == topNodeId){
						return node;
					}
				}
			 }
		  }
		}
		return null;
	},
	
	scrollToNode : function (treenode) {
		var el = treenode.getEl();
		var y = YAHOO.util.Dom.getY(el) - YAHOO.util.Dom.getY("hierarchytab");
		if(el.firstChild){
			var offsetTop =0;
			if(navigator.appName == "Microsoft Internet Explorer"){
				offsetTop = this.accumulateOffsetTopForNode(treenode);
			}else{
				offsetTop = el.offsetTop;
			}
			y = offsetTop - (el.firstChild.offsetHeight*2.5);
		}else if(el.firstElementChild){
			y = el.offsetTop - (el.firstElementChild.offsetHeight*2.5);	
		}
		var anim = new YAHOO.util.Scroll($("hierarchytab"), {scroll: { to: [0, y] }});
		anim.animate();
	},
	scrollToNodeSearch : function (treenode) {
		var el = treenode.getEl();
		if(this.treehighlites2 && this.treehighlites2.length > 0){
			var firstLoadedDbId = this.getDbIdOfDeepestLoaded(this.treehighlites2, this.eventsearchresultshierarchydtree);
			if(firstLoadedDbId != null && treenode.data.dbid != firstLoadedDbId){
				return;
			}
		}
		var y = YAHOO.util.Dom.getY(el) - YAHOO.util.Dom.getY("searchresultstab");
		if(el.firstChild){
			var offsetTop =0;
			if(navigator.appName == "Microsoft Internet Explorer"){
				offsetTop = this.accumulateOffsetTopForNode(treenode);
			}else{
				offsetTop = el.offsetTop;
			}
			y = offsetTop - (el.firstChild.offsetHeight*2.5);
		}else if(el.firstElementChild){
			y = el.offsetTop - (el.firstElementChild.offsetHeight*2.5);	
		}
		var anim = new YAHOO.util.Scroll($("searchresultstab"), {scroll: { to: [0, y] }});
		anim.animate();
	},
	scrollToNodeById:function(dbid){
		var treenode = this.eventhierarchydtree.getNodeByProperty('dbid',parseInt(dbid));
	    if((treenode != null) && $("hierarchytab").parentNode) {
            this.scrollToNode(treenode);
        }
	},
	/*
	 * Used by scrollToNode. In IE, offsetTop of node does NOT include the accumulated offsetTops for parent nodes
	 * which means scrolling behaviour does not behave correctly under following condition:
	 * 
	 * Select a root pathway that has not been expanded. Select a node in the diagram. Expand the pathway. The event
	 * hierarchy should scroll up to the top which is not what we want.
	 */
	accumulateOffsetTop:function(nodeElement){
		var accumOffsetTop = nodeElement.offsetTop;
		if(nodeElement.parentElement) {
			accumOffsetTop += this.accumulateOffsetTop(nodeElement.parentElement);
		}
		return accumOffsetTop;
	},
	
	accumulateOffsetTopForNode:function(treenode){
		var el = treenode.getEl();
		var accumOffsetTop = el.offsetTop;
		if(treenode.parent) {
			accumOffsetTop += this.accumulateOffsetTopForNode(treenode.parent);
		}
		return accumOffsetTop;
	},
	/*
	   the horizontal scroll bar in the event hierarchy pane doesnt work properly in IE. This function implements a hack to get around this.
	*/
	makeBrowserSpecificMods: function(){
		if(navigator.appName == "Microsoft Internet Explorer"){
	//set ygtv0 overflow to auto, width to 500px
			//hierarchytab overflow auto, width 290
			// wrappers 300
			var hierarchytab =$("hierarchytab"); 
			hierarchytab.style.overflowY="auto";
			//AF -> hierarchytab.style.width="290px";
			try{
				/*
				 * For IE we need to set style for an element generated by the YUI framework. The id
				 * of this can change from run to run so get first child of the hierarchy tab.
				 */
				var yuiItem = hierarchytab.firstChild;
				yuiItem.style.overflow = "auto";
				//AF -> yuiItem.style.width = "1800px";
			}catch(err){
				
			}
			$("outerwrapper").style.overflow="auto";				
			//$("outerwrapper").style.width="300px";
			$("innerwrapper").style.overflow="auto";
			//$("innerwrapper").style.width="300px";
		}
	},
	makeBrowserSpecificModsForSearch: function(){
		if(navigator.appName == "Microsoft Internet Explorer"){
	//set ygtv0 overflow to auto, width to 500px
			//hierarchytab overflow auto, width 290
			// wrappers 300
			var searchresultstab = $("searchresultstab");
			searchresultstab.style.overflowY="auto";
			searchresultstab.style.width="290px";
			
			try{
				var yuiItem = searchresultstab.firstChild;
				yuiItem.style.overflow = "auto";
				yuiItem.style.width = "1800px";
			}catch(err){
				
			}
		}
	},
	getPathwayName : function(dbid){
		var node = this.eventhierarchydtree.getNodeByProperty("dbid", dbid);
		if(node)
			return node.data.name;
		else 
			return null;
	},
	handleClickOnEventHierarchy : function (event) {
		/*
		 * Use Event to prevent expansion of tree node when node label clicked
		 */
		YAHOO.util.Event.preventDefault(event);
		YAHOO.util.Event.stopPropagation(event);
        var el = Event.element(event);
		var node = this.eventhierarchydtree.getNodeByElement(el);
        this.handleNodeClick(node, false, true);
	},
    handleNodeClick : function (node, topNodeFull, removeSearchResultsHighlites, selectedEntity){
		/*
		 * Now that subpathways can have their own diagrams and they can be shared among multiple top level pathways the focuspathwayid
		 * is no longer enough to identify which top level pathway to highight. We need to keep track of the top level pathway as we
		 * navigate the tree. 
		 * 
		 */
		var topNode 		= this.getTopNodeForNode(node);
		if(!topNodeFull){
			this.topNodeFull 	= this.getTopNodeForNodeFull(node);
			if(this.topNodeFull) 
				this.topNodeFull = this.topNodeFull.data.dbid;
		}
		this.removeTreeNodeHilites();
		/*
		 * If this flag is set, the user has clicked on a node in Pathway tab (not the search results tab). In this case remove
		 * highlights from the search results tree.
		 */
		if(removeSearchResultsHighlites){
			this.removeSearchResultsTreeNodeHilites();
		}
		node.highlite(this.treehighlites1cls);
		node.highliteAncestors(this.treehighlites2cls);
		this.treeNodeClickEvent.fire(topNode, node, selectedEntity);
		
		/* ##### Next line is for the GOOGLE ANALYTICS event tracking ##### */
		this.eventTracker.track(node.data.cls, node.data.name);
		/* ################################################################ */
    },
    handleClickOnSearchResultsHierarchy : function (event) {
    	/*
		 * Use Event to prevent expansion of tree node when node label clicked
		 */
		YAHOO.util.Event.preventDefault(event);
		YAHOO.util.Event.stopPropagation(event);
		var el = Event.element(event);
		var node = this.eventsearchresultshierarchydtree.getNodeByElement(el);
		this.processClickedNodeOnSearchResultsHierarchy(node);
	},
	
	/*
	 * Abstract this out to make the functionality independent of actually clicking on the tree. When the search hierarchy is
	 * first loaded the first pathway is automatically selected.
	 */
	processClickedNodeOnSearchResultsHierarchy: function(node){
		this.handleSearchNodeClick(node);
		var dbid = node.data.dbid;
		/*
		 * Synchronize main tree with search results tree. When a node is clicked in search results tree, 'click' the equivalent node
		 * in the main tree.
		 */
		var nodes = this.eventhierarchydtree.getNodesByProperty("dbid", dbid);
		if(nodes){
			for(var i=0;i<nodes.length;i++){
				node = nodes[i];
				if(this.getTopNodeForNodeFull(node).data.dbid == this.topNodeFull){
					if(node.data.cls == "Pathway"){
						this.handleNodeClick(node, this.topNodeFull, false, this.currentSearchEntityId);
					}else{
						this.handleNodeClick(node, this.topNodeFull, false);
					}
					this.scrollToNode(node);
					break;
				}
			}
		}
	},
    handleSearchNodeClick:function(node){
   		var topNode 		= this.getTopNodeForNode(node);
   		this.topNodeFull 	= this.getTopNodeForNodeFull(node);
   		if(this.topNodeFull) this.topNodeFull = this.topNodeFull.data.dbid;
		this.removeTreeNodeHilites();
		this.removeSearchResultsTreeNodeHilites();
		node.highlite(this.treehighlites1cls);
		node.highliteAncestors(this.treehighlites2cls);
    },
    selectPathway : function (dbid){
        var rootnode = this.eventhierarchydtree.getNodeByProperty("dbid", dbid);
        if(rootnode == null) return "false";
        this.handleNodeClick(rootnode, false, true);
    },
    /*
     * Populate the switch species drop down.
     */
	initSpeciesList: function(){
		var t = this;
		var callback = {
			success: function(oResponse){
				if(oResponse.responseText.length == 0){
					return;
				}
				data = getJsonFromRawText(oResponse.responseText);
				var speciesDropdown = $("specieslistdropdown");			
				if(speciesDropdown){
					for(var speciesId in data){
						var elOptNew = document.createElement('option');
						elOptNew.text = data[speciesId];
						elOptNew.value = speciesId;
						if(speciesId == t.focus_species_id){
							elOptNew.selected="true";
						}
						try{
							speciesDropdown.add(elOptNew, null); // standards compliant; doesn't work in IE
						}catch(err){
							speciesDropdown.add(elOptNew);
						}
					}
					Event.observe(speciesDropdown, 'change', t._speciesListOnChangeHandler.bindAsEventListener(t));
				}
				t.speciesInitializedEvent.fire();
			},
			failure: function(oResponse){									
			}
		};
		YAHOO.util.Connect
			.asyncRequest(
				'POST',
				"/ReactomeGWT/entrypoint/elv/ELVUtilsServlet",
				callback,
				'action=listspecies'
			);

	},
	_speciesListOnChangeHandler: function (event){
		var name = event.target.childNodes[event.target.selectedIndex].innerHTML;
		/* ##### Next line is for the GOOGLE ANALYTICS event tracking ##### */
		this.eventTracker.track("Species List", name);
		/* ################################################################ */
	},
	removeTreeNodeHilites : function () {
		this.treehighlites1.clear();
		this.treehighlites2.clear();
		$A($("hierarchytab").getElementsByClassName(this.treehighlites1cls)).invoke('removeClassName',this.treehighlites1cls);
		$A($("hierarchytab").getElementsByClassName(this.treehighlites2cls)).invoke('removeClassName',this.treehighlites2cls);
	},
	removeSearchResultsTreeNodeHilites : function () {
		try{
			$A($("searchresultstab").getElementsByClassName(this.treehighlites1cls)).invoke('removeClassName',this.treehighlites1cls);
			$A($("searchresultstab").getElementsByClassName(this.treehighlites2cls)).invoke('removeClassName',this.treehighlites2cls);
		}catch(err){
			
		}
	},
	/*
	 * Iterate back up the event hierarchy until we find an event with a diagram (which is not necessarily a top level pathway).
	 */
	getTopNodeForNode : function (node) {
	     // added check for hasdiagram to accommodate subpathways that have diagrams
         if (node.parent && node.parent.parent && node.data.hasdiagram == false) {
	         return this.getTopNodeForNode(node.parent);
         } else {
		     return node;
         }
	},
	/*
	 * Iterate back up the tree to get tree node for the top level pathway
	 */
	getTopNodeForNodeFull : function (node) {
	     // added check for hasdiagram to accommodate subpathways that have diagrams
        if (node.parent && node.parent.parent) {
	         return this.getTopNodeForNodeFull(node.parent);
        } else {
		     return node;
        }
	},
	highliteNodeInEventHierarchy: function(diagramNode, activateHierarchyTab) {
		this.removeTreeNodeHilites();
		this.removeSearchResultsTreeNodeHilites(); // search
		var topLevelId = 0;	
		if(this.topNodeFull){
			topLevelId = this.topNodeFull;
		}
		var treenode = this.eventhierarchydtree.getNodeByProperty('dbid',this.focus_pathway_id);
		if (treenode != null) {
			treenode.highlite(this.focustreetrunkcls);
			treenode.highlite(this.treehighlites2cls);
		}
		// Search
		var searchtreenode = null;
		try{
			searchtreenode = this.eventsearchresultshierarchydtree.getNodeByProperty('dbid',this.focus_pathway_id);
			if (searchtreenode != null) {
				searchtreenode.highlite(this.focustreetrunkcls);
				searchtreenode.highlite(this.treehighlites2cls);
			}
		}catch(err){
		}


		this.treehighlites1 = [diagramNode.userObject.id];

		var treenodes = this.eventhierarchydtree.getNodesByProperty('dbid',diagramNode.userObject.id);
		if (treenodes) {
			for(var i=0; i<treenodes.length; ++i){
				treenode = treenodes[i];
				if (treenode != null) {
					 treenode.highlite(this.treehighlites1cls);
					this.scrollToNode(treenode);
				}
			}
		}
		
		try{
			searchtreenode = this.eventsearchresultshierarchydtree.getNodeByProperty('dbid',diagramNode.userObject.id);
			if (searchtreenode != null) {
				searchtreenode.highlite(this.treehighlites1cls);
				this.scrollToNodeSearch(searchtreenode);
			}
		}catch(err){
			
		}
		
		var sUrl = "/ReactomeGWT/entrypoint/elv/ELVUtilsServlet?action=listpathids&pathwayid=" + this.focus_pathway_id
				+ "&entityid="+diagramNode.userObject.id
				+ "&toplevelid="+topLevelId;
		var t = this;
		var callback = {
			scope: this,
			success: function(oResponse) {		
				var o = getJsonFromRawText(oResponse.responseText);
				
				t.topNodeFull = o.toplevel;
				var pathlist = new Array();
				if(o.pathlist){
					for(var i =0; i<o.pathlist.length; i++){
						pathlist[i] = parseInt(o.pathlist[i]);
					}
				}
				
				t.treehighlites2 = pathlist;
			 	this.highliteTreeNodesByEventDbIds(t.treehighlites2,t.treehighlites2cls, parseInt(o.toplevel));
			 	this.highliteSearchTreeNodesByEventDbIds(t.treehighlites2,t.treehighlites2cls, parseInt(o.toplevel));
			 	var node = t.getNodeByPropertyGivenTopNode(t.eventhierarchydtree,  t.getDbIdOfDeepestLoaded(this.treehighlites2, this.eventhierarchydtree), t.topNodeFull, pathlist);
			 			 	
				if(node){
					node.expand();
					this.scrollToNode(node);
				}else{
					var topnode = t.eventhierarchydtree.getNodeByProperty('dbid',parseInt(o.toplevel));
                    if(topnode){
                        topnode.expand();
                        this.scrollToNode(topnode);
                    }
                }
				//Search
				try{
					var searchnode = t.eventsearchresultshierarchydtree.getNodeByProperty('dbid',t.getDbIdOfDeepestLoaded(this.treehighlites2, this.eventsearchresultshierarchydtree));
					if(searchnode){
						this.scrollToNodeSearch(searchnode);
					}else{
						var topnode = t.eventsearchresultshierarchydtree.getNodeByProperty('dbid',parseInt(o.toplevel));
						if(topnode){
							topnode.expand();
						}
					}
				}catch(err){
				}
			}, 
			failure: function(oResponse) {
				//log(oResponse.statusText);
			}
		};
		
		YAHOO.util.Connect.asyncRequest('POST', sUrl, callback);
	},
	/*
	 * Because a subpathway can be shared between multiple top level pathways it is not enough in some case to get a node by id. The
	 * top level node must also me considered so that the event hierarchy scrolls correctly.
	 * 
	 * dtree - eventhierarchydtree or eventsearchresultshierarchydtree, for example
	 * 
	 */
	getNodeByPropertyGivenTopNode : function(dtree, dbid, topNodeId, pathList){
		var defaultNode = null;
		try{
			defaultNode = dtree.getNodeByProperty("dbid", dbid);
			// need to use getNodeSbyproprerty insude this function
			//var deepestDbid = this.getDbIdOfDeepestLoadedByTopNodeId(pathList, dtree, topNodeId);
			var deepestTreeNode = this.getDeepestLoadedTreeNodeByTopNodeId(pathList, dtree, topNodeId);
			return deepestTreeNode;
		}catch(err){
		
		}
		return defaultNode;
	},
	highliteNodeInEventHierarchyById: function(dbid, activateHierarchyTab) {
		this.removeTreeNodeHilites();
		var topLevelId = 0;
		var t = this;	
		if(this.topNodeFull){
			topLevelId = this.topNodeFull;
		}
		var treenode = this.eventhierarchydtree.getNodeByProperty('dbid',this.focus_pathway_id);
		if (treenode != null) {
			treenode.highlite(this.focustreetrunkcls);
			treenode.highlite(this.treehighlites2cls);
		}
		this.treehighlites1 = [dbid];
		treenode = this.eventhierarchydtree.getNodeByProperty('dbid',dbid);
		if (treenode != null) {
			treenode.highlite(this.treehighlites1cls);
			this.scrollToNode(treenode);
		}
		var sUrl = "/ReactomeGWT/entrypoint/elv/ELVUtilsServlet?action=listpathids&pathwayid=" + this.focus_pathway_id
				+ "&entityid="+dbid
				+ "&toplevelid="+topLevelId;
		var callback = {
			scope: this,
			success: function(oResponse) {
				var o = getJsonFromRawText(oResponse.responseText);
				
				var pathlist = new Array();
				this.topNodeFull = o.toplevel;
				if(o.pathlist){
					for(var i =0; i<o.pathlist.length; i++){
						pathlist[i] = parseInt(o.pathlist[i]);
					}
				}
				/*
				 * specifically for the scenario where a non-event link is clicked on in the details pane (ie input/output/catalyst
				 * of reaction) so that eventhierarchy will scroll to node of event in the tree.
				 */
				try{
					var deepestId = this.getDbIdOfDeepestLoadedByTopNodeId(pathlist, this.eventhierarchydtree, parseInt(this.topNodeFull));
					var treenode = this.eventhierarchydtree.getNodeByProperty("dbid", deepestId);
					this.scrollToNode(treenode);
					
					// process search results tree
					deepestId = this.getDbIdOfDeepestLoadedByTopNodeId(pathlist, this.eventsearchresultshierarchydtree, parseInt(this.topNodeFull));
					treenode = this.eventsearchresultshierarchydtree.getNodeByProperty("dbid", deepestId);
					this.scrollToNodeSearch(treenode);

				}catch(err){

				}
				this.treehighlites2 = pathlist.without(this.treehighlites1);
				this.highliteTreeNodesByEventDbIds(this.treehighlites2,this.treehighlites2cls, parseInt(o.toplevel));
			}, 
			failure: function(oResponse) {
				//log(oResponse.statusText);
			}
		};
	
		YAHOO.util.Connect.asyncRequest('POST', sUrl, callback);
	},
	/*
	 * toplevelid is used to ensure only path from top level node to event is highlighted and not paths in all pathways
	 * it is a part of of.
	 */
	highliteTreeNodesByEventDbIds : function (dbids,hlcls, toplevelid) {
		for (var i = 0, len = dbids.length; i < len; ++i) {
			var treenodes = this.eventhierarchydtree.getNodesByProperty('dbid',dbids[i]);
			if(toplevelid ){
				if(treenodes && treenodes != null)
				for(var j=0; j<treenodes.length;j++){
					var node = treenodes[j];
					var topNode = this.getTopNodeForNodeFull(node);
					if(topNode && topNode.data.dbid == toplevelid){
						node.highlite(hlcls);
					}
				}
			}else
			$A(treenodes).invoke('highlite',hlcls);
		}
	},
	highliteSearchTreeNodesByEventDbIds : function (dbids,hlcls, toplevelid) {
		try{
		for (var i = 0, len = dbids.length; i < len; ++i) {
			var treenodes = this.eventsearchresultshierarchydtree.getNodesByProperty('dbid',dbids[i]);
			
			if(toplevelid ){
				if(treenodes && treenodes != null)
				for(var j=0; j<treenodes.length;j++){
					var node = treenodes[j];
					var topNode = this.getTopNodeForNodeFull(node);
					if(topNode && topNode.data.dbid == toplevelid){
						node.highlite(hlcls);
					}
				}
			}else
			$A(treenodes).invoke('highlite',hlcls);
		}
		}catch(err){
			
		}
	},
	fireTreeNodeMouseOverEvent : function (event, dbId) {
		this.treeNodeMouseOverEvent.fire(dbId);
	},
	fireTreeNodeMouseOutEvent : function () {
		this.treeNodeMouseOutEvent.fire();
	},
	fireTreeNodeClickEvent : function (e, dbId) {
		this.treeNodeClickEvent.fire(dbId);
	},
	toString: function() {
		return "EventHierarchyPane";
	}
});

/*
 * Extend YAHOO.widget.HTMLNode (used in the event hierarchy) so that it can highlite
 * itself and its ancestors.
 */
YAHOO.extend(YAHOO.widget.HTMLNode, YAHOO.widget.HTMLNode, {
	highlite : function (hlcls) {
		$(this.getEl()).down('a').addClassName(hlcls);
	},
	highliteAncestors : function (hlcls) {
		if (this.parent && this.parent.parent) {
			this.parent.highlite(hlcls);
			this.parent.highliteAncestors(hlcls);
		}
	}
});


