PathwayDiagramPane = function() {};

Object
		.extend(
				PathwayDiagramPane.prototype,
				{
					// Configuration
					imgwww : "", // could get images from another server
					basedir : "/entitylevelview/pathway_diagram_statics/",
					diagramhighlitesurl : '/cgi-bin/entitylevelview/diagramhighlites',
					maxz : 4, // max zoom level
					z : 1, // zoom level
					dz : 1, // default zoom multiplier
					dx : 25, // the number of pixels to move the image
								// (left-right)
					dy : 25, // the number of pixels to move the image
								// (up-down)
					tilewidth : 200, // standard width of each tile
					tileheight : 200, // standard height of each tile
					highliteBorderThickness : 5,
					thumbnailw : 100,
					thumbnailh : 100,

					// Class/object variables set dynamically
					db : null,
					current_instance_id : null,
					focus_species_id : null,
					focus_pathway_id : null,
					representedInstances : null,
					highlitedCoordinates : new Array(),
					orderedRepresentedInstances : new Array(),
					nodes : null, // EntityVertex db_id
					refdbnodes : null, // ReferenceEntity db_id
					dbnodes : null, // PhysicalEntity db_id
					complexnodes : null, // complex nodes (PhysicalEntity)
					node2highlite : new Object(),
					dbidOfNodeBeingHighlited : null,
					transientHighliteIdCache : new Hash(),
					imgdir : null, // image home directory (where the images
									// live)
					imgw : null,
					imgh : null,
					imgx : 0, // absolute image width
					imgy : 0, // absolute image height
					ntilesx : 0, // number of tiles (x)
					ntilesy : 0, // number of tiles (y)
					starty : 0, // default starting position of image (y)
					startx : 0, // default starting position of image (x)
					inLoadTransientHighlites : false,
					iDiffX : 0,
					iDiffY : 0,
					iOrigLeft : null,
					iOrigTop : null,
					viewportRight : null,
					viewportTop : null,
					viewportBottom : null,
					highlitesInViewportOnly : true,
					zoomChange : null,
					iThumbDiffX : 0,
					iThumbDiffY : 0,
					expressionPainter : null,
					currentExperiment : 0,
					tolerance : 10,
					
					/*
					 * max, min and mid expression values for current pathway
					 */
					maxValue:null,
					minValue:null,
					midValue:null,
					
					paintExpressionData : 0,
					noHitBG : "rgb(170, 170, 170)", // Used to colour nodes that
													// have no hits in
					// data inputted for expression painter.
					cpbg : "RGB(240,240,240)", // Background used for table
												// displaying expression levels
					// of complex participating molecules.
					complexCellLength : 50, // length/width of cells
											// representing component proteins
											// for
					// complex expression painter display.
					zoomFactor : 1, // zoomFactor 1 corresponds to zoom level
									// (z) 1, 0.5 -> 2, 0.25 -> 3, 0.125 -> 4,
									// 0.0625 -> 5
					currentCplxNode : null, // keep track of which node popup is
											// displayed for so data can be
											// reused if repainting required
					// Event that is fired when a diagram node is clicked.
					// Subscribe to this event if you
					// want to react somehow to the user clicking the node. This
					// is e.g. how PathwayBrowser
					// is informed that the node has been clicked.
					diagramNodeClickedEvent : new YAHOO.util.CustomEvent(
							"DiagramNodeClickedEvent", this),

					menuItemClickedEvent : new YAHOO.util.CustomEvent(
							"menuItemClickedEvent", this),
							
					diagramLoadedEvent : new YAHOO.util.CustomEvent(
							"diagramLoadedEvent", this),
					participatingProteinClickedEvent : new YAHOO.util.CustomEvent(
							"participatingProteinClickedEvent", this),

					// Event that is fired when zoom level is changed.
					zoomChange : new YAHOO.util.CustomEvent('zoom level change'),
					actorDiffX : 0,
					actorDiffY : 0,
					xOrigin : 0,
					yOrigin : 0,
					complexpopupZ:50,
					speciesdata:null,
					displayComparison:0,
					defaultBackgroundColour : 'rgb(174,174,174)',
					controlPanel : null,
					fiDisplay : null,
					oContextMenu : null,
					tipZ : 21, // z index of tip displayed when node moused
								// over.
					miCanvasMargin : 500, /*
											 * Leave a margin around diagram so
											 * that interactors are displayed
											 * even if they are not on diagram
											 */
					intactSearchUrl : "http://www.ebi.ac.uk/intact/search/do/search?searchString=EBIINTERACTION",
					thumbnailHTML : null, // We are appending the image tag to
											// the thumbnail HTML
					complexTableMargin : 10,
					COMPARISON_NOT_ALLOWED_STRING:"Species comparison is only possible if the current species is Homo Sapiens. Please select Homo Sapiens from the 'Switch Species' dropdown to perform a species comparison.",
					speciesComparisonChangeEvent : new YAHOO.util.CustomEvent("SpeciesComparisonChangeEvent", this),
					COMPLEX_COMPONENTS_EXPRESSION : 0,
					COMPLEX_COMPONENTS_SPECIES_COMPARISON: 1,
					COMPLEX_COMPONENTS_NODE_HIGHLIGHTING: 2,
					COMPLEX_COMPONENTS_PARTICIPATING_MOLECULES: 3,
					
					stableDbName : "gk_current_pathway_diagram",
					stableHref : "http://www.reactome.org/entitylevelview/PathwayBrowser.html#DB=",
					//elvEventManagers : null,

					// Initialise
					init : function() {
						this.diagramLoadedEvent.subscribe(this.fitPathway.bind(this));
						this.oContextMenu = new ContextMenu();
						this.oContextMenu.init(this.db);
						this.controlPanel = new ControlPanel();
						// this.controlPanel.init(null);
						this.init_handlers();
						if (this.db && this.focus_species_id
								&& this.focus_pathway_id) {
							this.setImgDir();
							this.loadOrderedCoordinates();
						}
						this.expressionPainter = new ExpressionPainter();
						this.expressionPainter.init(15);
						this.fiDisplay = new FIDisplay();
						this.fiDisplay.init(this.intactSearchUrl, this.miCanvasMargin);
						this.thumbnailHTML = $("thumbnail").innerHTML;
						this.stableHref += this.stableDbName;
						//this.elvEventManagers = new Array();
						try{
							if(navigator.appName == "Microsoft Internet Explorer"){
								log("IE Detected")
								this.complexTableMargin = 20;
							}
						}catch(err){
							
						}
					},
					// Set up the handlers for various events
					init_handlers : function() {
						var t = this;
						YAHOO.util.Event.addListener('clipwindow', 'mousedown',
								this.engage, this, true);
						YAHOO.util.Event.addListener('clipwindow', 'mousemove',
								this.handleMoveOverCanvas, this, true);
						YAHOO.util.Event.addListener('clipwindow', 'mouseout',
								function() {
									YAHOO.util.Event.removeListener(document,
											'keypress', this.keypresshandler)
								}, this, true);
						YAHOO.util.Event.addListener('thumbwindow',
								'mousedown', this.engageThumb, this, true);
						YAHOO.util.Event.addListener('thumbnail', 'mousedown',
								this.engageThumbnail, this, true);
						
						// Add listeners for right & left experiment browser
						// arrows
						YAHOO.util.Event
								.addListener(
										'experimentrightarrow',
										'mousedown',
										function() {
											this.currentExperiment++;
											if (this.currentExperiment > (this.expressionPainter.columnCount - 1)) {
												this.currentExperiment = 0;
											}
											this.displayExpressionLevels();
											this.updateComplexPopup();
										}, this, true);
						YAHOO.util.Event
								.addListener(
										'experimentleftarrow',
										'mousedown',
										function() {
											this.currentExperiment--;
											if (this.currentExperiment < 0) {
												this.currentExperiment = this.expressionPainter.columnCount - 1;
											}
											this.displayExpressionLevels();
											this.updateComplexPopup();
										}, this, true);
						YAHOO.util.Event
								.addListener(
										'experimentclose',
										'mousedown',this.closeExperiment,
										 this, true);
						YAHOO.util.Event.addListener($("speciesclose"), "mousedown", this.onSpeciesComparisonClose, this, true);
						YAHOO.util.Event.addListener($("speciesnameclose"), "mousedown", this.onSpeciesNameClose, this, true);
						this.oContextMenu.menuItemClickedEvent.subscribe(this.onCtxMenuItemClick.bind(this), this);
						this.init_slider();
						this.bindListenersToMapNavigationArrow();
					/*
					 * this.controlPanel.tableBuiltEvent.subscribe(this.interactionTableBuiltHandler.bind(this));
					 * this.controlPanel.rowClickedEvent.subscribe(this.interactionEntryClickedHandler.bind(this));
					 * this.controlPanel.closeSpeciesComparisonEvent.subscribe(this.onSpeciesComparisonClose.bind(this));
					 * this.controlPanel.displayInteractorsButtonClickedEvent.subscribe(this.displayInteractorsButtonClickedHandler.bind(this));
					 * this.controlPanel.speciesComparisonChangeEvent.subscribe(this.controlPanelSpeciesComparisonChangedEventHandler.bind(this));
					 */
						try{
							this.initMouseWheelHandler();
						}catch(err){
							alert("Error while setting up mousewheel handler: " + err.message);
						}
					},
					initMouseWheelHandler:function(){
						if($("clipwindow").addEventListener){
							$("clipwindow").addEventListener('DOMMouseScroll', this.onMouseWheelSpin.bind(this), false);
							$("clipwindow").addEventListener('mousewheel', this.onMouseWheelSpin.bind(this), false);
						}else{
							// $("clipwindow").onmousewheel =
							// $("clipwindow").onmousewheel =
							// this.onMouseWheelSpin.bind(this);
							$("clipwindow").attachEvent("onmousewheel", this.onMouseWheelSpin.bind(this));
						}
						
					},
					
					init_slider : function() {
						var Event = YAHOO.util.Event, Dom = YAHOO.util.Dom, lang = YAHOO.lang, slider, bg = "slider-bg", thumb = "slider-thumb", valuearea = "slider-value", textfield = "slider-converted-value";
						var topConstraint = 0;
						var bottomConstraint = 60;
						var scaleFactor = 20;
						var keyIncrement = 20;
						var tickSize = 20;
						var t = this;
						// Even though the init is done when DOM is ready we
						// still need to put the
						// following in a separate onDOMReady as otherwise the
						// slide thumb would "jump"
						// outside its normal path.
						YAHOO.util.Event.onDOMReady( function() {
							slider = YAHOO.widget.Slider.getVertSlider(bg,
									thumb, topConstraint, bottomConstraint,
									tickSize);

								t.zoomChange.subscribe(t._setZoomLevel.bind(t), slider);
								
								slider.getRealValue = function() {
									return Math.round(this.getValue()
											/ scaleFactor + 1);
								}
								slider.setRealValue = function(value) {
									slider.setValue((value - 1) * scaleFactor);
								}
								slider.subscribe("change", function(
										offsetFromStart) {
									var actualValue = slider.getRealValue();
									// Update the title attribute on the
									// background. This helps assistive
										// technology to communicate the state
										// change
										Dom.get(bg).title = "slider value = "
												+ actualValue;
										t.zoomChange.fire(actualValue);
									});
								slider.setRealValue(t.z);
								t.slider = slider;
							});
					},
					_setZoomLevel : function(type, zl, slider) {
						//console.info(type + ", " + zl + ", " + slider);
						this.removeImageTiles();
						this.setZoomLevel(zl);
						slider.setRealValue(zl);
						
					},
					onMouseWheelSpin : function(e){
						/*
						 * Check if the mouse cursor is currently hovering over
						 * a complex. if it is, return from function and allow
						 * default handling to continue (if a complex has > 9
						 * proteins a scroll bar will be displayed).
						 * 
						 * Otherwise, set zoom level to new level.
						 */
						var oClipwindow = $("clipwindow");
						var cwPos = Position.cumulativeOffset(oClipwindow);
						var clipWindowX = YAHOO.util.Event.getPageX(e)
								- cwPos[0];
						var clipWindowY = YAHOO.util.Event.getPageY(e)
								- cwPos[1];
						var oCanvas = $("canvas");
						var cPos = this.getPositionByStyle(oCanvas);
						var canvasX = (-cPos[0] + clipWindowX)
								/ this.zoomFactor;
						var canvasY = (-cPos[1] + clipWindowY)
								/ this.zoomFactor;
						if(this.isPopupAtCoordinates(canvasX, canvasY) == true){
							return;
						}
						
						
						var nDelta = 0;
					    if (!e) { // For IE, access the global (window) event
									// object
					        e = window.event;
					    }
					    // cross-bowser handling of eventdata to boil-down delta
						// (+1 or -1)
					    if ( e.wheelDelta ) { // IE and Opera
					        nDelta= e.wheelDelta;
					        if ( window.opera ) {  // Opera has the values
													// reversed
					            nDelta= -nDelta;
					        }
					    }
					    else if (e.detail) { // Mozilla FireFox
					        nDelta= -e.detail;
					    }
					    if (nDelta > 0) {
					    	this.z--;
					    	if(this.z<1) this.z = 1;
					    }
					    if (nDelta < 0) {

					    	this.z++;
					    	if(this.z>this.maxz) this.z = this.maxz;
					    }
					    this._setZoomLevel(null, this.z, this.slider);
					    if ( e.preventDefault ) {  // Mozilla FireFox
					        e.preventDefault();
					    }
					    e.returnValue = false;  // cancel default action

					},
					controlPanelSpeciesComparisonChangedEventHandler : function(type, args){
						var data = args[0];
						var speciesId = args[1];
						var selectedIndex = args[2];
						// this.getComplexComponentsForSpeciesComparison(data);
						// this.otherSpeciesDbId = speciesId;
						this.speciesComparisonChangeEvent.fire(data, speciesId, selectedIndex);
					},
					// Set up the handlers for navigation arrows
					bindListenersToMapNavigationArrow : function() {
						if ($('uparrow') && $('leftarrow') && $('rightarrow') && $('downarrow')) {
							Event.observe('uparrow', 'click', this.animatedReposition.bind(this, 0, 100));
							Event.observe('leftarrow', 'click', this.animatedReposition.bind(this, 100, 0));
							Event.observe('rightarrow', 'click', this.animatedReposition.bind(this, -100, 0));
							Event.observe('downarrow', 'click',this.animatedReposition.bind(this, 0, -100));
							Event.observe('fiticon', 'click', this.fitPathway.bind(this));
						}
					},
					/*
					 * The action of holding down the left mouse button and
					 * dragging will depend on what is displayed at the location
					 * of the mouse pointer. For example, if a popup is
					 * displayed we do not want to drag the diagram. This means
					 * scoll bars in the popup will work properly.
					 * 
					 */
					cancelEngage : function(oEvent){
						var oClipwindow = $("clipwindow");
						var cwPos = Position.cumulativeOffset(oClipwindow);
						var clipWindowX = YAHOO.util.Event.getPageX(oEvent)
								- cwPos[0];
						var clipWindowY = YAHOO.util.Event.getPageY(oEvent)
								- cwPos[1];
						var oCanvas = $("canvas");
						var cPos = this.getPositionByStyle(oCanvas);
						var canvasX = (-cPos[0] + clipWindowX)
								/ this.zoomFactor;
						var canvasY = (-cPos[1] + clipWindowY)
								/ this.zoomFactor;
						
						return this.isPopupAtCoordinates(canvasX, canvasY);
		
					},
					// select image to drag
					engage : function(oEvent) {
						if(this.cancelEngage(oEvent)){
							return;
						}
						/*
						 * Put this in to stop a display bug infirefox on linux
						 * where holding the left mouse button down and dragging
						 * would result in glitches, including staying in drag
						 * mode when left mouse button was released.
						 */
						if(oEvent.preventDefault){
							oEvent.preventDefault();
						}else {
							oEvent.returnValue = false; 
						}
						var oCanvas = $("canvas");
						var oClipwindow = $("clipwindow");
						// calculate offset
						this.iDiffX = oEvent.clientX - oCanvas.offsetLeft;
						this.iDiffY = oEvent.clientY - oCanvas.offsetTop;
						// change cursor to hand
						oClipwindow.style.cursor = "move";
						// setup event handlers
						YAHOO.util.Event.addListener(document.body,
								"mousedown", this.mousedown, this, true);
						YAHOO.util.Event.addListener(document.body,
								"mousemove", this.move, this, true);
						YAHOO.util.Event.addListener(document.body, "mouseup",
								this.drop, this, true);
						/*
						 * disable mousemovehandler as it makes Safari noticably
						 * sluggish.
						 */
						YAHOO.util.Event.removeListener('clipwindow',
								'mousemove', this.handleMoveOverCanvas);
					},
					// Drag image
					move : function(oEvent) {
						var oCanvas = $("canvas");
						// set new position
						oCanvas.style.left 	= oEvent.clientX - this.iDiffX;
						oCanvas.style.top 	= oEvent.clientY - this.iDiffY;
						// refresh screen
						this.reposition(0, 0);
					},
					mousedown : function(oEvent) {
						if (Event.element(oEvent).tagName == "IMG") {
							Event.stop(oEvent);
						}
					},
					// stop dragging image
					drop : function(oEvent) {
						var oClipwindow = $("clipwindow");
						// turn off events
						YAHOO.util.Event.removeListener(document.body,
								"mousemove", this.move);
						YAHOO.util.Event.removeListener(document.body,
								"mouseup", this.drop);
						YAHOO.util.Event.removeListener(document.body,
								"mousedown", this.mousedown);
						// Re-instate mousemovehandler.
						YAHOO.util.Event.addListener('clipwindow', 'mousemove',
								this.handleMoveOverCanvas, this, true);
						// change cursor back to normal
						oClipwindow.style.cursor = "default";
					},
					handleMoveOverCanvas : function(oEvent) {
						// log("handleMoveOverCanvas()");
						var oClipwindow = $("clipwindow");
						var cwPos = Position.cumulativeOffset(oClipwindow);
						var clipWindowX = YAHOO.util.Event.getPageX(oEvent)
								- cwPos[0];
						var clipWindowY = YAHOO.util.Event.getPageY(oEvent)
								- cwPos[1];
						var oCanvas = $("canvas");
						var cPos = this.getPositionByStyle(oCanvas);
						var canvasX = (-cPos[0] + clipWindowX)
								/ this.zoomFactor;
						var canvasY = (-cPos[1] + clipWindowY)
								/ this.zoomFactor;
						var node = this.findNodeAtCoordinate(canvasX, canvasY);
	
					// if (node != null && !this.isPopupAtCoordinates(canvasX,
					// canvasY)) {
						if (node != null){
							this.handleHighliteMouseover(oEvent, node.id);
						}else{
							// Add a check to see if menu displayed before
							// destroying.
							// this.oContextMenu.destroyMenuIfNecessary();
							this.hideToolTipsIfNecessary(canvasX, canvasY);
						}
					},
					
					/*
					 * Given coordinates and dimensions of a node, find out if
					 * it overlaps a visible complex popup. If it does, dont
					 * return the node.
					 */
					isPopupAtCoordinates : function (x,y, w, h){
						var popup = $("cplxcompstipid");
						if(popup && popup.style.visibility == "visible"){
							var popupx = popup.baseX;
							var popupy = popup.baseY;
							var namepopup = $("cplxnamedivid");
							namepopup.style.height="50px";
							if(
									/*
									 * If the x coord is overlaps the popup The
									 * coordinates of the node are scaled
									 * depending on zoom level. We need to do
									 * the same with the width and height of the
									 * popup so that overlaps will be detected
									 * correctly. Tolerance adds a margin around
									 * the popup
									 */
									((x >= (popupx - (this.tolerance/this.zoomFactor)) &&
									x <= (popupx + (parseInt(popup.style.width)/this.zoomFactor)
											+ (this.tolerance/this.zoomFactor)))
											||
										(w &&
												/*
												 * If x coord + width overlaps
												 * the popup.
												 */
												(x + w*this.zoomFactor)>= (popupx - (this.tolerance/this.zoomFactor)) &&
												(x + w*this.zoomFactor) <= (popupx + (parseInt(popup.style.width)/this.zoomFactor)
														+ (this.tolerance/this.zoomFactor))
												
												)
									)	
											
											&&
									/*
									 * Same as above for y with the addition of
									 * including the height of the name div in
									 * the calculation.
									 * 
									 */
									((y >= (popupy - (this.tolerance/this.zoomFactor)) &&
									y <= (popupy + (parseInt(popup.style.height)/this.zoomFactor)
											+ parseInt(namepopup.style.height)
											+ (this.tolerance/this.zoomFactor)))
											||
											( y &&
													(y + h*this.zoomFactor) >= (popupy - (this.tolerance/this.zoomFactor)) &&
													(y + h*this.zoomFactor) <= (popupy + (parseInt(popup.style.height)/this.zoomFactor)
															+ parseInt(namepopup.style.height)
															+ (this.tolerance/this.zoomFactor))
													)
											
							 ))
									{
								return true;	
							}
						}
						return false;
					},

					/*
					 * When moving the mouse pointer around, check to see if an
					 * interactor is present at the current coordinates. If so,
					 * return true. This is used to prevent the tooltip being
					 * displayed when user mouses over a node that has an
					 * interactor drawn over it.
					 * 
					 */
					isInteractorAtCoordinates : function (x,y, w, h){
						try{
							/*
							 * Used to define a buffer around nodes in the
							 * diagram. Interactors overlapping the node plus
							 * buffer will return true
							 */
							var fiTolerance = 2;
							if(this.fiDisplay.currentInteractors){
								for(var i in this.fiDisplay.currentInteractors){
									var element = this.fiDisplay.currentInteractors[i].bodyEl;
									if(element){
										if(
												/*
												 * If the x coord is overlaps
												 * the popup The coordinates of
												 * the node are scaled depending
												 * on zoom level. We need to do
												 * the same with the width and
												 * height of the popup so that
												 * overlaps will be detected
												 * correctly. Tolerance adds a
												 * margin around the popup.
												 * Note: the coordinates for
												 * interactors are scaled by the
												 * zoom level. When comparing
												 * these coordinates to the node
												 * coordinates we must scale the
												 * node coordinates as well.
												 * micanvasmargin must also be
												 * used as the ficanvas is
												 * larger than canvas so even if
												 * an interactor appears to
												 * occupy the same space as a
												 * node the coordinates will
												 * differ by miCanvasMargin
												 * zoomFactor will range from 1
												 * to .125.
												 */
												((((x*this.zoomFactor) + (this.miCanvasMargin))>= (parseInt(element.parentNode.style.left) - (fiTolerance)) &&
														((x*this.zoomFactor)+(this.miCanvasMargin)) <= (parseInt(element.parentNode.style.left) + (parseInt(element.style.width)*this.zoomFactor)
																+ (fiTolerance/this.zoomFactor)))
																||
																(w &&
																		/*
																		 * If x
																		 * coord +
																		 * width
																		 * overlaps
																		 * the
																		 * popup.
																		 */
																		((x*this.zoomFactor) + (this.miCanvasMargin)+ (w*this.zoomFactor))>= (parseInt(element.parentNode.style.left) - (fiTolerance)) &&
																		((x*this.zoomFactor) + (this.miCanvasMargin)) <= (parseInt(element.parentNode.style.left) + (fiTolerance))
																)
												)	

												&&
												/*
												 * Same as above for y
												 * 
												 */
												((((y*this.zoomFactor)+(this.miCanvasMargin)) >= (parseInt(element.parentNode.style.top) - (fiTolerance)) &&
														((y*this.zoomFactor)+(this.miCanvasMargin)) <= (parseInt(element.parentNode.style.top) + (parseInt(element.style.height)*this.zoomFactor)
																+ (fiTolerance/this.zoomFactor)))
																||
																( y &&
																		((y*this.zoomFactor) + (this.miCanvasMargin) + (h*this.zoomFactor)) >= (parseInt(element.parentNode.style.top) - (fiTolerance)) &&
																		((y*this.zoomFactor) + (this.miCanvasMargin)) <= (parseInt(element.parentNode.style.top) + (fiTolerance))
																)

												)){
											/*
											 * If this interactor does overlap
											 * the node, return true
											 */
											return true;
										}
									}

								}
							}
						}catch(err){

						}
						/*
						 * Return false by default (no interactor at
						 * coordinates).
						 */
						return false;
					},
					hideToolTipsIfNecessary : function(canvasX, canvasY){
						if($("canvasoverlay")){
						var localX = canvasX * this.zoomFactor;
						var localY = canvasY * this.zoomFactor;
						var tipList = $("canvas").getElementsBySelector('div.tip');
						for ( var tipIndex = 0; tipIndex < tipList.length ; tipIndex++) {
							var tip = tipList[tipIndex];
							if ((localX > tip.offsetLeft )
									&& (localX < tip.offsetLeft + tip.clientWidth)
									&& (localY > tip.offsetTop )
									&& (localY < tip.offsetTop + tip.clientHeight)) {
								
							}else{
								tip.setStyle({visibility: "hidden"});
							}
						}
						}
					},
					// select thumbnail window to move
					engageThumb : function(oEvent) {
						if(oEvent.preventDefault){
							oEvent.preventDefault();
						}else {
							oEvent.returnValue = false;
						}
						var oThumbwindow = $("thumbwindow");
						// calculate offset
						this.iThumbDiffX = oEvent.clientX
								- oThumbwindow.offsetLeft;
						this.iThumbDiffY = oEvent.clientY
								- oThumbwindow.offsetTop;
						// change cursor to hand
						oThumbwindow.style.cursor = "move";
						
						// setup event handlers
						YAHOO.util.Event.addListener(document.body,
								"mousemove", this.moveThumb, this, true);
						YAHOO.util.Event.addListener(document.body, "mouseup",
								this.releaseThumb, this, true);

					},
					// Drag thumbnail window
					moveThumb : function(oEvent) {
						if(oEvent.preventDefault){
							oEvent.preventDefault();
						}else {
							oEvent.returnValue = false;
						}
						var oThumbwindow = $("thumbwindow");
						// set new position
						oThumbwindow.style.left = oEvent.clientX
								- this.iThumbDiffX;
						oThumbwindow.style.top = oEvent.clientY
								- this.iThumbDiffY;
						var oThumbnail = $("thumbnail");
						var offsetTopsum = 0;
						var offsetLeftsum = 0;
						var offsetParent = oThumbwindow.offsetParent;

						while (offsetParent != null) {
							if (!(offsetParent == undefined)) {
								offsetTopsum = offsetTopsum
										+ offsetParent.offsetTop;
								offsetLeftsum = offsetLeftsum
										+ offsetParent.offsetLeft;
							}
							offsetParent = offsetParent.offsetParent;
						}

						if ((oEvent.clientX > offsetLeftsum && oEvent.clientX < (offsetLeftsum + oThumbnail.clientWidth))
								&& (oEvent.clientY > offsetTopsum)
								&& oEvent.clientY < (offsetTopsum + oThumbnail.clientHeight)) {
						} else {
							/*
							 * Prevent the thumbwindow from being dragged
							 * outside thumbnail by detecting when its
							 * coordinates are outside the bounds of the
							 * thumbnail. When this happens reposition the thumb
							 * window so that it is at the thumbnail edge
							 * closest to it.
							 */
							if(oEvent.clientX < offsetLeftsum){
								oThumbwindow.style.left = "0px";
							}else if(oEvent.clientX > (offsetLeftsum + oThumbnail.clientWidth)){
								oThumbwindow.style.left = oThumbnail.clientWidth - oThumbwindow.clientWidth;
							}
							
							if(oEvent.clientY < offsetTopsum){
								oThumbwindow.style.top = "0px";
							}else if(oEvent.clientY > (offsetTopsum + oThumbnail.clientHeight)){
								oThumbwindow.style.top = oThumbnail.clientHeight - oThumbwindow.clientHeight;
							}
							this.moveImage();
							// turn off events
							YAHOO.util.Event.removeListener(document.body,
									"mousemove", this.moveThumb);
							YAHOO.util.Event.removeListener(document.body,
									"mouseup", this.releaseThumb);
							oThumbwindow.style.cursor = "default";
						}
					},
					// stop draging thumbnail window
					releaseThumb : function(oEvent) {
						var oThumbwindow = $("thumbwindow");
						var oThumbnail = $("thumbnail");
						var offsetTopsum = 0;
						var offsetLeftsum = 0;
						var offsetParent = oThumbwindow.offsetParent;

						while (offsetParent != null) {
							if (!(offsetParent == undefined)) {
								offsetTopsum = offsetTopsum
										+ offsetParent.offsetTop;
								offsetLeftsum = offsetLeftsum
										+ offsetParent.offsetLeft;
							}
							offsetParent = offsetParent.offsetParent;
						}

						// turn off events
						YAHOO.util.Event.removeListener(document.body,
								"mousemove", this.moveThumb);
						YAHOO.util.Event.removeListener(document.body,
								"mouseup", this.releaseThumb);
						oThumbwindow.style.cursor = "default";

						if ((oEvent.clientX > offsetLeftsum && oEvent.clientX < (offsetLeftsum + oThumbnail.clientWidth))
								&& (oEvent.clientY > offsetTopsum)
								&& oEvent.clientY < (offsetTopsum + oThumbnail.clientHeight)) {
							// change position of main image
							this.moveImage();
							if (this.highlitesInViewportOnly)
								this.reposition(0, 0);
								this.showDiagramsForVisibleComplexAndSetNodesIfNecessary();
						} else {
							// oThumbwindow.clientHeight + ", " + oEvent.clientX
							// + ", " + oEvent.clientY);
							oThumbwindow.style.left = (oThumbnail.clientWidth / 2)
									- (oThumbwindow.clientWidth / 2);
							oThumbwindow.style.top = (oThumbnail.clientHeight / 2)
									- (oThumbwindow.clientHeight / 2);
							this.moveImage();
						}
					},
					// Move thumbnail to mouse click position.
					engageThumbnail : function(oEvent) {
						var oThumbwindow = $("thumbwindow");
						var offsetTopsum = 0;
						var offsetLeftsum = 0;
						var offsetParent = oThumbwindow.offsetParent;

						while (offsetParent != null) {
							if (!(offsetParent == undefined)) {
								offsetTopsum = offsetTopsum
										+ offsetParent.offsetTop;
								offsetLeftsum = offsetLeftsum
										+ offsetParent.offsetLeft;
							}
							offsetParent = offsetParent.offsetParent;
						}

						var baseY = offsetTopsum
								+ (oThumbwindow.clientHeight / 2);
						var baseX = offsetLeftsum
								+ (oThumbwindow.clientWidth / 2);
						oThumbwindow.style.left = oEvent.clientX - baseX;
						oThumbwindow.style.top = oEvent.clientY - baseY;
						this.moveImage();
						if (this.highlitesInViewportOnly)
							this.reposition(0, 0);
						this
								.showDiagramsForVisibleComplexAndSetNodesIfNecessary();
					},

					// reposition main image to match location of thumbwindow
					moveImage : function() {
						var oThumbwindow = $("thumbwindow");
						var oCanvas = $("canvas");
						// get current position of thumbwindow
						var iThumbX = parseInt(oThumbwindow.style.left);
						var iThumbY = parseInt(oThumbwindow.style.top);
						// calculate position of main image
						var tx = ((this.imgx / this.thumbnailw) * iThumbX)
								- (((this.imgx / this.thumbnailw) * iThumbX) % 1); // change
																					// position
																					// (left)
						var ty = ((this.imgy / this.thumbnailh) * iThumbY)
								- (((this.imgy / this.thumbnailh) * iThumbY) % 1); // change
																					// position
																					// (top)
						// set image position
						oCanvas.style.left = tx * -1 + "px";
						oCanvas.style.top = ty * -1 + "px";
						// refresh screen
						this.fetchtile();
					},
					initControlPanel : function(){
						this.controlPanel.init(null);
					    this.controlPanel.tableBuiltEvent.subscribe(this.interactionTableBuiltHandler.bind(this));
						this.controlPanel.rowClickedEvent.subscribe(this.interactionEntryClickedHandler.bind(this));
						this.controlPanel.closeSpeciesComparisonEvent.subscribe(this.onSpeciesComparisonClose.bind(this));
						this.controlPanel.displayInteractorsButtonClickedEvent.subscribe(this.displayInteractorsButtonClickedHandler.bind(this));
						this.controlPanel.speciesComparisonChangeEvent.subscribe(this.controlPanelSpeciesComparisonChangedEventHandler.bind(this));

					},
					scrollToNode : function(dbid) {
						var tn = this.dbnodes[dbid];
						var n = this.nodes[tn];
						if (n) {
							this.centreOnCoordinate(n.x, n.y);
						}
					},
					// register event handler(s)
					//
					// Don't do it wholesale - set it only when mouseovering the
					// clip window. Otherwise you'll have all sorts of
					// weird effects i.e. not being able to type into a text
					// box.
					// Safari 3.1.1 doesn't seem to recognise arrow keys any
					// more. Bizarre.
					keypresshandler : function(oEvent) {
						if (!oEvent) {
							return;
						}
						// stop event performing default action
						if (!oEvent.preventDefault) {
							oEvent.preventDefault = function() {
								this.returnValue = false;
							};
						} else {
							oEvent.preventDefault();
						}
						var key;
						if (oEvent.keyCode) {
							key = oEvent.keyCode; // IE key value
						} else if (oEvent.charCode) {
							key = oEvent.charCode; // W3C DOM key value
						} else if (oEvent.which) {
							key = oEvent.which; // other browsers key value
						}
						if (key == 38 || key == 63232) { // up
							this.reposition(0, +1);
						} else if (key == 40 || key == 63233) { // down
							this.reposition(0, -1);
						} else if (key == 37 || key == 63234) { // left
							this.reposition(+1, 0);
						} else if (key == 39 || key == 63235) { // right
							this.reposition(-1, 0);
						} else if (key == 43) {
							this.zoom(-1);
						} else if (key == 45) {
							this.zoom(+1);
						} 
						return true;
					},
					// load chosen image into page
					reloadimage : function() {
						// log("reloadimage");
						// setup and populate tiled image
						this.initcanvas();
						this.fetchtile();
					},
					// function: populates the canvas div with empty image tiles
					initcanvas : function() {
						// calculate image (scaled to zoom level)
						var scaling = 1 / Math.pow(2, (this.z - 1));
						this.imgx = Math.floor(this.imgw * scaling);
						this.imgy = Math.floor(this.imgh * scaling);

						// calculate number of tiles needed (x & y)
						var modx = this.imgx % this.tilewidth;
						var mody = this.imgy % this.tileheight;
						this.ntilesx = (this.imgx - modx) / this.tilewidth;
						this.ntilesy = (this.imgy - mody) / this.tileheight;
						if (this.imgx - modx < this.imgx) {
							this.ntilesx++
						}
						;
						if (this.imgx < this.tilewidth) {
							this.ntilesx = 1
						}
						;
						if (this.imgy - mody < this.imgy) {
							this.ntilesy++
						}
						;
						if (this.imgy < this.tileheight) {
							this.ntilesy = 1
						}
						;

						// calculate size of image in tiles
						var maxx = this.ntilesx * this.tilewidth;
						var maxy = this.ntilesy * this.tileheight;

						// decide on position of image when first displayed
						// (middle and center)
						this.startx = -(this.imgx / 2)
								+ (this.iClipWidth() / 2);
						this.starty = -(this.imgy / 2)
								+ (this.iClipHeight() / 2);

						// place enlarged thumb image in the background and set
						// the size
						var oCanvas = $('canvas');
						//oCanvas.update(); //Clearing the canvas content
						
						// Add fi canvas
						var fiCanvas = $('ficanvas');
						if(fiCanvas){
							fiCanvas.remove();
							fiCanvas = undefined;
						}
						if(!fiCanvas){
							fiCanvas = Element.extend(document.createElement('div'));
							fiCanvas.id = "ficanvas";
							fiCanvas.setStyle( {
								top: (-1*this.miCanvasMargin)+ "px",
								left: (-1*this.miCanvasMargin) +"px",
								width: (maxx+(2*this.miCanvasMargin)) + "px",
								height: (maxy+(2*this.miCanvasMargin)) + "px",
								zIndex: 21
							});
							oCanvas.appendChild(fiCanvas);
						}
						var underlay = $("canvasunderlay");
						if (!underlay) {
							underlay = Element.extend(document
									.createElement('div'));
							underlay.id = "canvasunderlay";
							oCanvas.appendChild(underlay);
						}
						$A(underlay.childNodes).invoke('remove');
						var tiles = $("tiles");
						if (!tiles) {
							tiles = Element.extend(document
									.createElement('div'));
							tiles.id = "tiles";
							tiles.setStyle( {
								position : "absolute",
								top : "0px",
								left : "0px"
							});
							oCanvas.appendChild(tiles);
						}
						var entitydiagrams = $("entitydiagrams");
						if (!entitydiagrams) {
							entitydiagrams = Element.extend(document
									.createElement('div'));
							entitydiagrams.id = "entitydiagrams";
							entitydiagrams.setStyle( {
								position : "absolute",
								top : "0px",
								left : "0px",
								display : "none"
							});
							oCanvas.appendChild(entitydiagrams);
						}
						$A(entitydiagrams.childNodes).invoke('remove');
						var overlay = $("canvasoverlay");
						if (!overlay) {
							overlay = Element.extend(document
									.createElement('div'));
							overlay.id = "canvasoverlay";
							overlay.setStyle( {
								position : "absolute",
								top : "0px",
								left : "0px",
								zIndex: "20"
							});
							oCanvas.appendChild(overlay);
						}

						$A(overlay.childNodes).invoke('remove');
						oCanvas.style.width = (maxx+this.miCanvasMargin) + "px";
						oCanvas.style.height = (maxy+this.miCanvasMargin) + "px";
					},
					closeExperiment: function() {
						this.currentExperiment 		= 0;
						this.paintExpressionData 	= 0;
						this.paintExpressionDataFromGWT 	= 0;
						try{
							$("canvasoverlay")
								.getElementsBySelector(
										'div.expressiontips')
								.invoke('remove');
						}catch(err){
							
						}
						var complexpopup = $("ficanvas").getElementsBySelector('div.cplxcompstip');
						if(complexpopup){
							complexpopup.invoke('remove');
						}
						this.expressionPainter = null;
						this.expressionPainter = new ExpressionPainter();
						this.expressionPainter.init(15);
						this.expressionPainter
								.setExpressionVisibility("hidden");
						this.fiDisplay.resetQueryTerminals();
						$("expmodecheck").checked=false;
					},
					
					positionThumbnail : function() {
						var oThumbnailframe = $("thumbnailframe");
						if (this.imgw > 0) {
							oThumbnailframe.show();
							var oClipwindow = $("clipwindow");
							var pos = Position.cumulativeOffset(oClipwindow);
							var oThumbnail = $("thumbnail");
							// calculate dimensions of thumbnail
							var largest = (this.imgw > this.imgh) ? this.imgw
									: this.imgh;
							var ratio = 100 / largest;
							var thumbnail = this.imgwww + this.imgdir
									+ "/thumb.png";
							this.thumbnailh = (Math.ceil(ratio * this.imgh));
							this.thumbnailw = (Math.ceil(ratio * this.imgw));
							
							oThumbnail.innerHTML = "<img id=\"thumbnailid\" src=\""+thumbnail+"\" width=\"" +this.thumbnailw+"px\" height=\""+this.thumbnailh+"px\"/>"
										+ this.thumbnailHTML;
							
							oThumbnail.style.width = this.thumbnailw + "px";
							oThumbnail.style.height = this.thumbnailh + "px";
							oThumbnailframe.style.width = (this.thumbnailw + 5)
									+ "px";
							oThumbnailframe.style.height = (this.thumbnailh + 5)
									+ "px";
							
						} else {
							oThumbnailframe.hide();
						}
					},
					// populate canvas with tiled images. Create the tile divs
					// if necessary.
					fetchtile : function() {
						var oCanvas = $("canvas");
						var oTiles = $("tiles");
						var iX = parseInt(oCanvas.style.left) || 0;
						var iY = parseInt(oCanvas.style.top) || 0;
						// find the top left tile in the clip
						var iTileX = Math.floor(-iX / this.tilewidth);
						var iTileY = Math.floor(-iY / this.tileheight);
						// correction in case we are at the top left corner of
						// the whole image already
						if (iTileX < 0)
							iTileX = 0;
						if (iTileY < 0)
							iTileY = 0;
						// determine bottom and right edge of the area to tile
						var rightEdge = -iX + this.iClipWidth();
						var bottomEdge = -iY + this.iClipHeight();
						for ( var ty = iTileY; ty * this.tileheight < bottomEdge; ty++) {
							for ( var tx = iTileX; tx * this.tilewidth < rightEdge; tx++) {
								if (tx >= 0 && tx < this.ntilesx && ty >= 0
										&& ty < this.ntilesy) {
									var iTileId = tx + "x" + ty;
									var oTile = $(iTileId);
									if (!oTile) {
										oTile = Element.extend(document
												.createElement("div"));
										oTile.setAttribute("id", iTileId);
										oTile.className = "tile";
										oTile
												.setStyle( {
													top : (ty * this.tileheight + "px"),
													left : (tx * this.tilewidth + "px")
												});
										oTiles.appendChild(oTile);
									}
									var uri = "url(" + this.imgwww
											+ this.imgdir + "/" + this.z + "/"
											+ iTileId + ".png)";
									// log(uri);
									if (oTile.style.backgroundImage != uri) {
										oTile.style.backgroundImage = uri;
									}
								}
							}
						}
					},
					// move image back to the center of the viewing area
					recentre : function() {
						var oCanvas = $("canvas");
						oCanvas.style.left = this.startx + "px";
						oCanvas.style.top = this.starty + "px";
						this.reposition(0, 0);
					},
					// move image
					reposition : function(fx, fy) {
						// get current image position
						var oCanvas = $("canvas");
						var iX = parseInt(oCanvas.style.left);
						var iY = parseInt(oCanvas.style.top);

						if (isNaN(iX) || isNaN(iY)) {
							return;
						}
						// change image position values
						iX += Math.round(this.dx * fx);
						iY += Math.round(this.dy * fy);
						// set image position values
						oCanvas.style.top = iY + "px";
						oCanvas.style.left = iX + "px";
						// change position/size of thumbnail window according to
						// new image position
						var iTw = Math.round((this.iClipWidth() / this.imgx)
								* this.thumbnailw); // change size of box
													// (width)
						
						var iTh = Math.round((this.iClipHeight() / this.imgy)
								* this.thumbnailh); // change size of box
													// (height)
                        if(iTh > $("thumbnail").clientHeight || iTw > $("thumbnail").clientWidth){
                            $("thumbwindow").style.visibility = "hidden";
                        }else{
                            $("thumbwindow").style.visibility = "visible";
                        }

						var iTx = Math
								.round((this.thumbnailw / this.imgx) * iX); // change
																			// position
																			// of
																			// box
																			// (left)
						var iTy = Math
								.round((this.thumbnailh / this.imgy) * iY); // change
																			// position
																			// of
																			// box
																			// (top)
						if (isNaN(iTx) || isNaN(iTy)) {
							return;
						}
						// set thumbnail window (red box) values
						var oThumbnail = $("thumbwindow"); // select thumbnail
															// div
						oThumbnail.style.left = -iTx + "px"; // set left :
																// negate value
																// so box moves
																// in opposite
																// direction
						oThumbnail.style.top = -iTy + "px"; // set top : negate
															// value so box
															// moves in opposite
															// direction
						oThumbnail.style.width = iTw + "px"; // set width
						oThumbnail.style.height = iTh + "px"; // set height
						//
						this.viewportLeft = -iX / this.zoomFactor;
						this.viewportTop = -iY / this.zoomFactor;
						this.viewportRight = (-iX + this.iClipWidth())
								/ this.zoomFactor;
						this.viewportBottom = (-iY + this.iClipHeight())
								/ this.zoomFactor;
						// refresh screen image tiles
						this.fetchtile();
						this
								.showDiagramsForVisibleComplexAndSetNodesIfNecessary();
						// Required for "lazy" highliting of things in viewport
						// only
						if (this.highlitesInViewportOnly) {
							this.highliteCachedNodes();
						}
					},
					iClipWidth : function() {
						return $('clipwindow').getWidth();
					},
					
					iClipHeight : function() {
						return $('clipwindow').getHeight();
					},
					
					fitPathway: function() {
						this.recentre();
						for(var z=1 ; z<=this.maxz; ++z){
							this.setZoomLevel(z);
							var vaz = this.getVisibleArea();
							if ( parseFloat(vaz.x) < 0 && parseFloat(vaz.y) < 0 ){
								this.zoomChange.fire(z);
								break;
							}
						}
					},
					
					zoom : function(fz) {
						// For some reason passing this.z to 'fire' seems to
						// turn it into string.
						this.z = parseInt(this.z) + this.dz * fz;
						this.zoomChange.fire(this.z);
					},
					setZoomLevel : function(zl) {
						// change zoom level
						this.z = zl;
						// set min and max zoom levels
						if (this.z < 1) {
							this.z = 1;
						} else if (this.z > this.maxz) {
							this.z = this.maxz;
						}
						this.zoomFactor = this.calculateZoomFactor(this.z);
						// find current position
						var oCanvas = $("canvas");
						var iX = parseInt(oCanvas.style.left);
						var iY = parseInt(oCanvas.style.top);
						if (isNaN(iX) || isNaN(iY)) {
							log("No canvas coordinates yet.", "warn");
							return;
						}
						// old image size
						var iOldX = this.imgx;
						var iOldY = this.imgy;
						// reload image
						this.reloadimage();
						// new image size
						var iNewX = this.imgx;
						var iNewY = this.imgy;
						// find pixel from image in central point of viewer
						var iPixOldX = ((iX) * -1) + (this.iClipWidth() / 2);
						var iPixOldY = ((iY) * -1) + (this.iClipHeight() / 2);
						// find where pixel has moved to in next image
						var iPixNewX = (iNewX / iOldX) * iPixOldX;
						var iPixNewY = (iNewY / iOldY) * iPixOldY;
						// change position values according to degree of
						// movement of pixel
						var iNewPosX = (((iPixNewX - iPixOldX) * -1) - (((iPixNewX - iPixOldX) * -1) % 1))
								+ iX;
						var iNewPosY = (((iPixNewY - iPixOldY) * -1) - (((iPixNewY - iPixOldY) * -1) % 1))
								+ iY;
						// set new position
						oCanvas.style.left = iNewPosX + "px";
						oCanvas.style.top = iNewPosY + "px";
						// refresh page with new position
						this.reposition(0, 0);
						if (!this.highlitesInViewportOnly) {
							this.highliteCachedCoordinates();
						}
						if (this.paintExpressionData == 1) {
							this.displayExpressionLevels();
						}
						if(this.displayComparison == 1){
							this.paintExistingComparison({
								nodes				: this.nodes,
								focus_pathway_id	: this.focus_pathway_id
							});
						}
						// Clear the mi overlay of displayed interactors and
						// redraw, scaling for new
						// zoom level.
						this.fiDisplay.clearDisplay();
						this.fiDisplay.displayAll(this.zoomFactor);
					},
					calculateZoomFactor : function(zl) {
						return 1 / Math.pow(2, (zl - 1));
					},
					centreOnCoordinate : function(x, y) {
						// log("centreOnCoordinate(" + x + ", " + y + ")");
						x *= this.zoomFactor;
						y *= this.zoomFactor;
						var canvas = $('canvas');
						var cx = -parseInt(canvas.getStyle('left'))
								+ this.iClipWidth() / 2;
						var cy = -parseInt(canvas.getStyle('top'))
								+ this.iClipHeight() / 2;
						var dx = cx - x;
						var dy = cy - y;
						if ((dx != 0) || (dy != 0)) {
							this.animatedReposition(dx, dy);
						}
					},
					displayNodeHighlight: function(x, y, w, h, keepPrevious) {
						var margin = 10;
						var borderwidth = 5;
						var overlay = $("canvasoverlay");
						if (!keepPrevious) {
							// alert("Removing highlite");
							overlay.getElementsBySelecter('div.hl').invoke('remove');
						}
						var hlw = (w + margin + (borderwidth*2)) * this.zoomFactor;
						var hlh = (h + margin + (borderwidth*2)) * this.zoomFactor;
						x = this.zoomFactor * (x + w / 2) - hlw / 2;
						y = this.zoomFactor * (y + h / 2) - hlh / 2;
						w = hlw;
						h = hlh;
						if(YAHOO.env.ua.ie > 0){
							w +=2*this.highliteBorderThickness;
							h +=2*this.highliteBorderThickness;
						}
						var hl = Element.extend(document.createElement('div'));
						hl.className = 'hl';
						hl.setStyle({position:"absolute",
									top:y - margin - borderwidth, 
									left:x - margin - borderwidth, 
									width:w + (margin*2), 
									height:h + (margin*2), 
									zIndex: 2,
									border: borderwidth +"px solid lime"
									});
						overlay.appendChild(hl);
					},
					highliteCoordinate : function(x, y, w, h, keepPrevious) {
						this.displayNodeHighlight(x,y,w,h, keepPrevious)						
					},
					clearHighlites : function() {
						this.highlitedCoordinates.map( function(n) {
							n.isHighlited = false;
						});
						this.highlitedCoordinates.length = 0;
						var oCO = $("canvasunderlay");
						if (oCO) {
							$A(oCO.childNodes).invoke('remove');
						}
						
						if($("canvasoverlay")){
							$("canvasoverlay").getElementsBySelector("div.hl").invoke('remove');
						}
						var to = $("thumbnailoverlay");
						if (to) {
							$A(to.childNodes).invoke('remove');
						}
						
						
					},
					
					/*
					 * Check if the db name equals the stable db name (gk_current_pathway_diagram). If not then
					 * display a notifiction informing the user they are working with an older database.
					 */
					processDbOutOfDataNotification : function(){
						try{
							if(this.stableDbName != this.db && this.hideDbOutOfDateNotification == 0){
								var notification = $("dboutofdatebanner");
								/*
								 * Set the href to gk_current_pathway_diagram in the Pathway Browser at www.reactome.org
								 * with the db id and species id appended
								 */
								var linkout = $("uptodatelinkout");
								var href = this.stableHref;
								href += "&FOCUS_SPECIES_ID=" + this.focus_species_id;
								href += "&FOCUS_PATHWAY_ID=" + this.focus_pathway_id;
								linkout.href = href;
								this.placeDbOutOfDataNotification();
								
								notification.style.display="block";
							}
						}catch(err){
							
						}
					},
					placeDbOutOfDataNotification : function(){
						var notification = $("dboutofdatebanner");
						var availableWidth = $("clipwindow").getWidth();
						var browserWidth = notification.getWidth();
						notification.style.left = ((availableWidth/2) - (browserWidth/2))+"px";
					},
					loadOrderedCoordinates : function(callforward) {
						var sUrl = this.imgdir + '/orderedcoordinates.json';
						var callback = {
							scope : this,
							customevents : {
								onSuccess : function(eventType, args) {
									var o = getJsonFromRawText(args[0].responseText);
									this.handleCoordinates(o);
									this.processDbOutOfDataNotification();
									
									// Fire this event to let PathwayBrowser
									// know
									// the diagram is ready and that the
									// expression
									// painter now has everything it needs to
									// paint the diagram correctly.
									this.diagramLoadedEvent.fire();
									if (callback.callforward)
										callback.callforward();
								},
								onFailure : function(eventType, args) {
									// log(args[0].statusText);
								this.orderedRepresentedInstance = [];
								this.reloadimage();
								this.recentre();
								if (callback.callforward)
									callback.callforward();
							}
							}
						};
						if (callforward)
							callback.callforward = callforward;
						if (this.focus_pathway_id != null) {
							YAHOO.util.Connect.asyncRequest('GET', sUrl,
									callback);
						} else if (callback.callforward) {
							callback.callforward();
						}
					},
					handleCoordinates : function(o) {
						//log("handleCoordinates");
						this.setImgDir();
						this.imgw = o.width;
						this.imgh = o.height;
						this.positionThumbnail();
						this.orderedRepresentedInstances = o.coordinates;
						this
								.processOrderedRepresentedInstances(this.orderedRepresentedInstances);
						this.reloadimage();
						this.recentre();
					},
					processOrderedRepresentedInstances : function(
							orderedRepresentedInstances) {
						this.representedInstances = new Object();
						this.nodes = new Object();
						this.dbnodes = new Object();
						this.refdbnodes = new Object();
						for ( var i = 0, l = orderedRepresentedInstances.length; i < l; ++i) {
							var n = orderedRepresentedInstances[i];
							this.representedInstances[n.id] = n;
							var coords = n.coords;
							for ( var j = 0, m = coords.length; j < m; ++j) {
								coords[j].userObject = n;
								this.nodes[coords[j].id] = coords[j];
								this.dbnodes[n.id] = coords[j].id;
								this.refdbnodes[n.refid] = coords[j].id;
							}
						}
					},
					/*
					 * reset variables when no pathway selected. This can happen
					 * when switching to a species that does not have a pathway
					 * orthologous to the currently displayed one.
					 */
					noPathwaySelected : function(){
						this.removeImageTiles();
						this.focus_pathway_id=null;
						var oThumbnailframe = $("thumbnailframe");
						oThumbnailframe.hide();
						this.setImgDir();
						this.representedInstances = new Object();
						this.nodes = new Object();
						this.dbnodes = new Object();
						this.refdbnodes = new Object();
						this.imgw = 0;
					},
					highliteNodesWithId : function(dbids, keepPrevious, selectedEntityId) {
						// log("highliteNodesWithId\t" + dbids);
						keepPrevious || this.clearHighlites();
						if (this.nodes != null) {
							var l = dbids.length;
							if (l > 100) {
								l = 100;
								// log("Highliting 1st " + l + " nodes only from
								// total " + dbids.length);
							}
							for ( var i = 0; i < l; ++i) {
								var n = this.nodes[dbids[i]];
								if (n != null) {
									try{
									if(selectedEntityId && selectedEntityId != n.userObject.id){
										continue;
									}
								}catch(err){
									
								}
								this.cacheHighlitedCoordinate(n);
								this.highliteCoordinate(n.x, n.y, n.w, n.h,	true);
								this.highliteThumbCoordinate(n.x, n.y);
								} else {
									log("No node with id " + dbids[i], "warn");
								}
							}
						} else {
							log("No coordinate data!", "warn");
						}
					},
					highliteNodeWithId : function(dbid, keepPrevious) {
							if (this.nodes != null) {
								var n = this.nodes[dbid];
							if (n != null) {
								keepPrevious || this.clearHighlites();
								this.cacheHighlitedCoordinate(n);
								this.highliteCoordinate(n.x, n.y, n.w, n.h,
										true);
								this.highliteThumbCoordinate(n.x, n.y);
							} else {
								log("No node with DB_ID " + dbid, "warn");
							}
						} else {
							log("No coordinate data!", "warn");
						}
					},
					highliteNode : function(node) {
						// log("EntityLevelView.highliteNode()");
						var oCO = $("canvasunderlay");
						var hlw = 200;
						var x = this.zoomFactor * (node.x + node.w / 2) - hlw
								/ 2;
						var y = this.zoomFactor * (node.y + node.h / 2) - hlw
								/ 2;
						var w = hlw;
						var h = hlw;
						var node_id = 'hl_' + node.id;
						var hl = Element.extend(document.createElement('img'));
						hl.id = node_id;
						hl.src = "/entitylevelview/icons/hlbg.png";
						oCO.appendChild(hl);
						node.isHighlited = true;
						hl.setStyle( {
							position : "absolute",
							top : y + "px",
							left : x + "px",
							width : w + "px",
							height : h + "px"
						});
						return hl;
					},
					highliteNodeIfInViewPort : function(node) {
						if (this.isInViewPort(node)) {
							this.displayNodeHighlight(node.x,node.y, node.w, node.h, true);
						}
					},
					highliteInViewportAndCache : function(node, keepPrevious, selectedEntity) {
						if(selectedEntity){
							var hit = false;
							if(selectedEntity == parseInt(node.userObject.id)) {
								hit = true;
							}
							if(hit == false){
							for(var complex in this.complexnodes){
								var components = this.complexnodes[complex];
								for(var index=0;index<components.length; index++){
								if(parseInt(components[index].dbid) == selectedEntity && node.userObject.id == complex){
									hit = true;
									break;
								}
							}
							}
							}
							if(hit == false) {
								return;
							}
							}
						if (!node.isHighlited) {
							keepPrevious || this.clearHighlites();
							if (this.highlitesInViewportOnly) {
								this.highliteNodeIfInViewPort(node);
							} else {
								this.highliteNode(node);
							}
							this.highliteThumbCoordinate(node.x, node.y);
							this.cacheHighlitedNode(node);
						}
					},
					highliteNodesWithInstanceId : function(dbid, keepPrevious) {
						if (this.representedInstances != null) {
							var n = this.representedInstances[dbid];
							if (n != null) {
								keepPrevious || this.clearHighlites();
								var coords = n.coords;
								for ( var i = 0; i < coords.length; i++) {
									this.highliteInViewportAndCache(coords[i],
											true);
								}
							} else {
								log("No node with DB_ID " + dbid, "warn");
							}
						} else {
							// alert("No coordinate data!");
						}
					},
					cacheHighlitedCoordinate : function(coords) {
						this.highlitedCoordinates.push(coords);
					},
					cacheHighlitedNode : function(node) {
						this.highlitedCoordinates.push(node);
					},
					highliteCachedCoordinates : function() {
						for ( var i = 0; i < this.highlitedCoordinates.length; i++) {
							var coords = this.highlitedCoordinates[i];
							if (coords.userObject != null) {
								this.highliteNode(coords);
							} else {
								this.highliteCoordinate(coords.x, coords.y,
										coords.w, coords.h, true);
							}
						}
					},
					highliteCachedNodes : function() {
						var t1 = new Date().getTime();
						var c = 0;
						for ( var i = 0; i < this.highlitedCoordinates.length; i++) {
							var node = this.highlitedCoordinates[i];
							var hl = this.highliteNodeIfInViewPort(node);
							if (hl != null)
								c++;
						}
						var t2 = new Date().getTime();
						// log("Highlited " + c + " nodes in " + (t2 - t1)/1000
						// + "s");
					},
					highliteThumbCoordinate : function(x, y) {
						x = Math.round(x / this.imgw * this.thumbnailw - 1);
						y = Math.round(y / this.imgh * this.thumbnailh - 1);
						var node_id = 'thl_' + Math.round(x / 2) + '_'
								+ Math.round(y / 2);
						var thl = $(node_id);
						if (thl == null) {
							thl = Element.extend(document.createElement('div'));
							thl.innerHTML = "&nbsp;";
							thl.id = node_id;
							thl.className = "thumbhighlite";
							thl.setStyle( {
								position : "absolute",
								top : (y - 1) + "px",
								left : (x - 1) + "px",
								width : "3px",
								height : "3px"
							});
							$("thumbnailoverlay").appendChild(thl);
						}
					},
					getPositionByStyle : function(obj) {
						return [ parseInt(obj.style.left),
								parseInt(obj.style.top) ];
					},
					findNodeAtCoordinate : function(x, y) {
						var tolerance = this.tolerance;
						// log("findNodeAtCoordinate: " + x + ", " + y);
						for ( var id in this.nodes) {
							var node = this.nodes[id];
							if ((x > node.x - tolerance)
									&& (x < node.x + node.w + tolerance)
									&& (y > node.y - tolerance)
									&& (y < node.y + node.h + tolerance)) {
								// Check if the node overlaps a visible complex
								// popup
								if(this.isPopupAtCoordinates(node.x, node.y, node.w, node.h) == false
										&& this.isInteractorAtCoordinates(node.x, node.y, node.w, node.h) == false){
									return node;
								}else{
									return null;
								}
							}
						}
					},
					
				
					
					highlitePathwayComponents : function(dbid) {
						var ol = this
								.overlayElementWithTimeIndicator($("clipwrapper"));
						var t = this;
						new Ajax.Request(
								'/cgi-bin/entitylevelview/pathwaycomponents', {
									parameters : {
										'ID' : dbid,
										'DB' : this.db
									},
									method : 'get',
									onSuccess : function(transport) {
										var rtxt = transport.responseText;
										// var a = eval(rtxt);
										var a = getJsonFromRawText(rtxt);
										
										t.clearHighlites();
										a.map( function(id) {
											t.highliteNodesWithInstanceId(id,
													true);
										});
										t.centreOnCoordinate(
												t.highlitedCoordinates[0].x,
												t.highlitedCoordinates[0].y);
									},
									onFailure : function(transport) {
										// alert(transport.statusText);
									},
									onComplete : function(transport) {
										if (ol)
											ol.remove();
									}
								});
					},
					setSpeciesCompareDropDown : function(speciesId){
						this.controlPanel.setSpeciesDropDown(speciesId);
					},
					updateSpeciesComparisonDropDown : function(e, selectedIndex){
						if(e){
							this.controlPanel.speciesCompareChange(e);
						}
						this.controlPanel.setSpeciesComparisonDropDown(selectedIndex);
					},
					updateSpeciesComparisonDropDownBySpeciesId:function(otherSpeciesId){
						this.controlPanel.setSpeciesComparisonDropDownBySpeciesId(otherSpeciesId);
					},
					onSpeciesComparisonClose : function(){
						this.displayComparison = 0;
						this.displayComparisonFromGWT = 0;
						if($("canvasoverlay")){
							$("canvasoverlay").getElementsBySelector('div.comparetips').invoke('remove');
						}
						
						/*
						 * The complex popup is shared between species
						 * comparison and expression painter so we need to check
						 * if expression painting mode is on before removing the
						 * complex popup. This should be refactored so that
						 * species comparison and expression painter each have
						 * their own popup.
						 */
						if(this.paintExpressionData == 0){
							var fiCanvas = $("ficanvas");
							if(fiCanvas){
								var complexpopup = fiCanvas.getElementsBySelector('div.cplxcompstip');
								if(complexpopup){
									complexpopup.invoke('remove');
								}
							}
						}
						var speciesSelector = $("speciescomparedropdown");
						$("speciescomparisonpanel").style.display = "none";
						$("speciescomparedropdown").selectedIndex=0;
						if(speciesSelector.onchange)
						speciesSelector.onchange();
						try{
						var speciesDetailsSelector = $("speciescomparisondropdowndetails");
						speciesDetailsSelector.selectedIndex=0;
						}catch(err){
							
						}
						this.fiDisplay.resetQueryTerminals();
					},
					onSpeciesNameClose : function(){
						$("speciesnamepanel").style.display = "none";
					},
					paintSpeciesComparison: function(speciesdata){
						/*
						 * this.speciesComparator.paintComparison(this.nodes,
						 * this.focus_pathway_id, comparisons, this.zoomFactor)
						 */
						this.paintComparison(this.nodes, this.focus_pathway_id, 
										speciesdata, this.zoomFactor)
					},
					paintExistingComparison : function(param){
						var nodes = param.nodes;
						var pathwayId = param.focus_pathway_id;
						if(this.speciesdata == null){
							alert("No comparison data found.");
							return;
						}
						this.paintComparison(nodes, pathwayId, this.speciesdata);
					},
					paintComparison : function(nodes, pathwayId, speciesdata){
						this.manageOverlays(1);
						comparisons = speciesdata.comparisons;
						$("canvasoverlay").getElementsBySelector(
						'div.comparetips').invoke('remove');
						
						var w=0;
						var h=0;
						for ( var nodeitem in nodes) {
							try{
							if (nodeitem) {
								if(nodes[nodeitem].cls != "ReactionVertex"){
								var tip = Element.extend(document
										.createElement('div'));
								tip.className = 'comparetips';
								tip.id = "comparetip_" + nodeitem;
								var node = nodes[nodeitem];
								
									w = node.w;
									h = node.h;
									var nw, nh = 5;
									nw = w - 1;
									nh = h - 1;
									if (this.zoomFactor == 1) {
										tip.innerHTML = node.userObject.name;
										tip.innerHTML = tip.innerHTML
										.replace(/\[.*/, "");
										tip.innerHTML = tip.innerHTML.replace(/\:/g,": ");

									}
									nw = nw * this.zoomFactor;
									nh = nh * this.zoomFactor;
									tip.setStyle( {
										position: "absolute",
										width : nw + "px",
										height : nh + "px",
										fontSize : "10px",
										fontWeight: "bold",
										textAlign : "center"
									});
									$("canvasoverlay").appendChild(tip);
									var border_w = 5;
									nh = tip.getHeight();

									var nodex = nodes[nodeitem].x;
									var nodey = nodes[nodeitem].y;
									nodex = nodex * this.zoomFactor
									- border_w
									- (nw - w * this.zoomFactor)
									/ 2;
									nodey = nodey * this.zoomFactor
									- border_w
									- (nh - h * this.zoomFactor)
									/ 2;
									var fontcolour="black";
									var backgroundcolour=this.defaultBackgroundColour;
									var matched=0;
									if (node.userObject.cls == "EntityWithAccessionedSequence") {
									for(var compIndex=0; compIndex < comparisons.length; compIndex++){
										if(node.userObject.refid == comparisons[compIndex].refid){
											if(comparisons[compIndex].other=="1"){
												backgroundcolour = "yellow";
												fontcolour = "black";
											}else{
												fontcolour = "white";
												backgroundcolour = "blue";
											}
											tip.setStyle( {
												left : nodex + 5
												+ "px",
												top : nodey + 5
												+ "px",
												height : nh + "px",
												color: fontcolour,
												background: backgroundcolour
											});
											matched=1;
										}
									}
									// if the EWAS was not matched with data
									// returned from the server
									// colour it grey. Should this happen ?
									if(matched == 0){
										tip.setStyle( {
											left : nodex + 5
											+ "px",
											top : nodey + 5
											+ "px",
											height : nh + "px",
											color: fontcolour,
											background: this.noHitBG
										});
										
									}
									
									// check if queryterminal overlay exists.
									this.processFITerminal(node, tip);
									}else
									if(node.userObject.cls == "SimpleEntity"){
										this.processDisplaySimpleEntity(tip, node, nw, nh, nodex, nodey);
									} else if (node.userObject.cls == "Complex" 
											|| node.userObject.cls == "DefinedSet"
											|| node.userObject.cls == "OpenSet"
											|| node.userObject.cls == "CandidateSet"
											|| node.userObject.cls == "Polymer") {
										var components = this
										.complexHasComponentsForSpeciesComparison(
												node.userObject.id)
										if (components == true) {
											tip.setStyle( {
												background : 'rgb(0,0,0)'
											});
										}else{
											tip.setStyle( {
												background : this.noHitBG
											});
											 
										}
										
											tip.setStyle( {
												left : nodex + 5 + "px",
												top : nodey + 5 + "px",
												height : nh + "px"
											});

											tip.style.color = "white";
									}
									else{
										tip.setStyle( {
													left : nodex + 5
													+ "px",
													top : nodey + 5
													+ "px",
													height : nh + "px",
													color: fontcolour,
													background: this.defaultBackgroundColour
												});
									}
									backgroundcolour = this.defaultBackgroundColour;
							}
							}
							}catch(ex){
							}
						}
						this.displayComparison = 1;
						this.speciesdata = speciesdata;
						this.placeSpeciesComparisonPanel(speciesdata.name);
				},
				processDisplaySimpleEntity : function(tip, node, nw, nh, nodex, nodey){
					// 8 is the multiplier for firefox on
					// linux.
					var dimAdjustment = 8 * this.zoomFactor;
					tip.innerHTML = "<img src=\"/entitylevelview/icons/circle.png\" width=\"" + (nw+dimAdjustment) + 
					"px\" height=\"" + (nh+dimAdjustment)	+ "px\" />";
					if(this.zoomFactor == 1){
					tip.innerHTML = tip.innerHTML + "<div align=\"center\" style=\"vertical-align:middle;width:"  + (nw+dimAdjustment) 
					+ ";height:" + (nh+dimAdjustment) +	";color:black;position:absolute;left: 0;top: 0;\">"+
					node.userObject.name.replace(/\[.*/, "") + "</div>";
					
					}
					
					// 3 is the ideal location adjustment
					// for linux firefox.
					var locadjustment = 3;
				     locadjustment +=  (1* (1 - this.zoomFactor ));
					if(this.zoomFactor == 1){
						// 2 is the ideal location
						// adjustment modifier for
						// linux firefox at maximum zoom
						locadjustment -=2;
					}
					tip
					.setStyle( {
						left : nodex + locadjustment
								+ "px",
						top : nodey +locadjustment 
								+ "px",
						height : (nh) + "px"
					});
				},
				
				placeSpeciesComparisonPanel : function(name){
					var availableWidth = $("clipwindow").getWidth();
					var browserWidth = $("speciescomparisonpanel").getWidth();
					if(name && name != null){
						$("speciesname").innerHTML = name;
					}else{
						$("speciesname").innerHTML = "Species Comparison";
					}
					$("speciescomparisonpanel").style.left = ((availableWidth/2) - (browserWidth/2))+"px";
					$("speciescomparisonpanel").style.visibility = "visible";
					$("speciescomparisonpanel").style.display = "block";
				},
				placeSpeciesNamePanel : function(name){
					var availableWidth = $("clipwindow").getWidth();
					var browserWidth = $("speciesnamepanel").getWidth();
					if(name && name != null){
						$("speciesnamelabel").innerHTML = "Pathway for species: " + name;
					}else{
						try{
							if($("speciesnamelabel").innerHTML.length == 0){
								$("speciesnamelabel").innerHTML = "Species name is missing";
							}
						}catch(err){
							
						}
					}
					$("speciesnamepanel").style.left = (availableWidth - browserWidth-10)+"px";
					$("speciesnamepanel").style.visibility = "visible";
					$("speciesnamepanel").style.display = "block";
				},
				handleHighliteMouseover : function(event, dbid) {
						//log("highlite");
						// Determing is complex pupup is displayed and if so
						// dont display
						// tooltip for complex.
						var cplxpopdisplayed = $("ficanvas")
						.getElementsBySelector(
						'div.cplxcompstip');

						if(cplxpopdisplayed.length > 0 
								&& (this.currentCplxNode != null 
										&& cplxpopdisplayed[0].style.visibility == "visible")){
							if(dbid == this.currentCplxNode.id){
								return;
							}else{
								if(!event.target){
									event.target = event.srcElement;
								}
								var node = this.nodes[dbid];
								var eX = event.target.offsetLeft;
								var eY = event.target.offsetTop;
								// 1 is index of close button div element
								var tX = cplxpopdisplayed[1].offsetLeft;
								var tY = cplxpopdisplayed[1].offsetTop;
								var margin = 20;

								if(((tX) > (node.x - margin)) 
										&& ((tX +cplxpopdisplayed[1].clientWidth ) < (node.x+node.w+margin)) 
										&& ((tY)>(node.y-margin)) 
										&& ((tY + cplxpopdisplayed[1].clientHeight ) < (node.y + node.h+margin))){
									return;
								}
							}
						}
						var id = "tip_" + dbid;
						var tipList = $("canvas").getElementsBySelector('div.tip');
						for(var tipIndex=0; tipIndex < tipList.length; tipIndex++){
								tipList[tipIndex].setStyle({visibility: "hidden"});
						}
						{
							// For some reason Safari often ignores the request
							// to remove the popups on mouseout.
							// Hence the need to "manually" remove them here.
							var tip = $(id);
							
							if(!tip){
							//	console.log("removing tip");
								//tip.remove();
							}
							tip = Element.extend(document
									.createElement('div'));
							tip.className = 'tip';
							tip.id = id;
							
							var node = this.nodes[dbid];
							var w = node.w;
							var h = node.h;
							var nw, nh;
							
							if (node.cls == "ReactionVertex") {
								nw = 100;
								nh = 100;
							} else {
								nw = 2.5 * w;
								nh = 2 * h;
							}
							tip.innerHTML = node.userObject.name;
							if(this.expressionPainter && this.expressionPainter.refIdMapping){
								var matchedEntity = this.expressionPainter.refIdMapping[node.userObject.refid];
								if (matchedEntity) {
									tip.innerHTML += ". Id : " 
										+ matchedEntity.id + ". Level: " 
										+ matchedEntity.data[this.currentExperiment];
								}
							}
							tip.setStyle( {
								width : nw + "px",
								display : 'none'
							});
							$("canvas").appendChild(tip);
							var t = this;	
							eventManager = new ElvEventManager({
								elem		: tip,
								leftclick_fn	: function(event) {
									var ctrl = event.ctrlKey;
									if ( ctrl == false) {
										t.highliteNodeWithId(dbid,
												false);
										t.diagramNodeClickedEvent
										.fire(node);
									} 
								},
								leftdblclick_fn	: function(event){
									if(node.userObject.cls == "Pathway"){
										t.menuItemClickedEvent.fire(node.userObject.id, node.userObject.id, node.userObject.name);
									}else{
										t.highliteNodeWithId(dbid,
												false);
										t.diagramNodeClickedEvent
										.fire(node);
									}
								},
								rightclick_fn	: function(event){
									if(t.fiDisplay.currentlyDisplayed[node.id]){
										node.interactors = "hide";
									}else{
										node.interactors = "display";
									}
									node.expressionDisplayed = t.paintExpressionData;
									var expressionTip = $("expressiontip_" + dbid);
									var speciesCompareTip = $("comparetip_" + dbid);
									if((expressionTip && expressionTip.style.backgroundColor == t.noHitBG) ||
											(speciesCompareTip && speciesCompareTip.style.backgroundColor == t.noHitBG)){
										node.greyedOut = true;
									}else{
										node.greyedOut = false;
									}
									node.displayComparison = t.displayComparison;
									t.createMenu(node, tip);

								}
							});

							/*		
							Event.observe(tip, 'mousedown', function(event) {

								Event.stop(event);
								var ctrl = event.ctrlKey;
								if ((event.button == 0
										|| event.button == 1) && ctrl == false) {
									t.highliteNodeWithId(dbid,
											false);
									t.diagramNodeClickedEvent
											.fire(node);
								} else if (event.button == 2 || (event.button == 0 && ctrl ==true)) {
										if(t.fiDisplay.currentlyDisplayed[node.id]){
											node.interactors = "hide";
										}else{
											node.interactors = "display";
										}
										node.expressionDisplayed = t.paintExpressionData;
										var expressionTip = $("expressiontip_" + dbid);
										var speciesCompareTip = $("comparetip_" + dbid);
										if((expressionTip && expressionTip.style.backgroundColor == t.noHitBG) ||
												(speciesCompareTip && speciesCompareTip.style.backgroundColor == t.noHitBG)){
											node.greyedOut = true;
										}else{
											node.greyedOut = false;
										}
										node.displayComparison = t.displayComparison;
										t.createMenu(node, tip);
								}
								}); 
							 */		 
							var border_w = 5;
							nh = tip.getHeight();
							var x = node.x;
							var y = node.y;
							x = x * this.zoomFactor - border_w
							- (nw - w * this.zoomFactor) / 2;
							y = y * this.zoomFactor - border_w
							- (nh - h * this.zoomFactor) / 2;

							tip.setStyle( {
								left : x + "px",
								top : y + "px",
								opacity : 0.85,
								height : nh + "px",
								border : border_w + "px solid #DCDCDC",
								background : "#DCDCDC",
								zIndex : 30
							});

							new Effect.Appear(tip, {
								duration : 0.5,
								to : 0.85
							});
						}
						/*
						 * The bringElementIntoViewPort function is what
						 * causes the diagram to scroll to bring the whole
						 * of a tooltip into view. I found this annoying as
						 * diagrams can be quite busy and merely moving the
						 * cursor around could cause the diagram to 'skid'
						 * away. Also, it can interfere with the other
						 * pathways feature. When the new diagram is loaded
						 * the view will be over the centre of the pathway
						 * and will then scroll to focus on the selected
						 * node. If the cursor is over a node it will
						 * 'catch' and stop the view from focusing on the
						 * node selected by the user
						 */
//						this.bringElementIntoViewPort(tip);
						//	}

					},
					clearFIOverlay: function(){
						/*
						 * Get a list of expressiontips for proteins. When the
						 * fioverlay is cleared need to redisplay these.
						 */
						var exptips = $("canvasoverlay")
						.getElementsBySelector(
								'div.proteinexpressiontip');
						if(exptips && exptips.length > 0){
							for(var i=0; i<exptips.length;i++){
								exptips[i].style.display="";
							}
						}
						this.fiDisplay.cleanUp();
					},
					displayFIOverlay : function(node, oResponse){
						
						this.controlPanel.colourRows(node.userObject.id, node.id)
						return this.fiDisplay.displayFIOverlay(node, oResponse, this.zoomFactor);
					},
					applyConfidenceColours: function(confthreshold, exceedColour, belowColour){
						this.fiDisplay.colourInteractionsBasedOnThreshold(confthreshold, exceedColour, belowColour);
					},
					resetColours:function(){
						this.fiDisplay.resetColours();
					},
					
					createMenu : function(node, tip){
						this.oContextMenu.displayMenu(node, tip);
					},
					
					
					/*
					 * The control panel can display a table of interactions for
					 * proteins in the pathway. This table is constructed
					 * dynamically. If interactors are already displayed in the
					 * diagram we need to colour the relevant rows in the table
					 * accordingly and this is done after the table is
					 * constructed.
					 * 
					 */
					interactionTableBuiltHandler : function(){
						try{
							/*
							 * Need to check for as if currentlyDisplayed is
							 * empty nodeVertexId is assigned value of "each"
							 * which is not a valid index for this.nodes
							 */
							// if(this.fiDisplay.currentlyDisplayed &&
							// this.fiDisplay.currentlyDisplayed.length > 0){
								for(nodeVertexId in this.fiDisplay.currentlyDisplayed){
									var node = this.nodes[nodeVertexId];
									if(node){
										this.controlPanel.colourRows(node.userObject.id, node.id);
									}
								}
							// }
						}catch(err){
							alert("There was a problem colouring entries in the interaction table: " 
									+ err.description);
						}
						this.controlPanel.showTable();
					},
					/*
					 * Handles the event when the user left clicks on a query
					 * protein (protein in Reactome pathway) in the interaction
					 * table view. Move view to that protein, display
					 * interactors for that protein.
					 */
					interactionEntryClickedHandler : function(eventType, args){
						var pid = args[0]; // protein id = node.userObject.id
						var vid = args[1]; // Vertex id = node.id
						this.scrollToNode(pid);
					    this.highliteNodeWithId(this.dbnodes[pid], false);
					    this.diagramNodeClickedEvent.fire(this.nodes[this.dbnodes[pid]]);
						var node = this.nodes[vid];
						if(node){
							var args = new Array();
							args[0] = node;
							pba.onDiagramNodeClick(null, args, null);
						}
					},
					displayInteractorsButtonClickedHandler:function(eventType, args){
						var pid = args[0]; // protein id = node.userObject.id
						var vid = args[1]; // Vertex id = node.id
						var node = this.nodes[vid];
						var args = new Array();
						args[0] = pid;
						args[1] = vid;
						/*
						 * Move view to node in diagram before displaying
						 */
						this.interactionEntryClickedHandler(null, args);
						/*
						 * If interactors already displayed then hide
						 * interactors and change colour or row
						 */
						if(this.fiDisplay.currentlyDisplayed[vid]){
							this.fiDisplay.hideInteractors(node);
							var colour = "white";
							this.controlPanel.colourRows(node.userObject.id, node.id, colour);
						}else{
							// Only call this if interactors not already
							// displayed.
							this.displayInteractors(node);
						}
					},
					displayInteractors:function(node){
						var t = this;
						var callback = {
							success: function(oResponse){
							if(oResponse.responseText.length == 0){
								$("clipwindow").style.cursor = "default";					
								alert("No interactors found for query protein in current interaction data source.");
								return;
							}

							var data = getJsonFromRawText(oResponse.responseText);
							// Keep track of which nodes the user has selected.
							// Even if no interactors for current interaction
							// data source there might be interactors found in
							// another datasource when the user switches.
							var alreadySelected = false;
							for(var i=0; i< t.fiDisplay.currentFINodes.length; i++){
								if(t.fiDisplay.currentFINodes[i].id == node.id){
									alreadySelected = true;
								    break;
								}
							}
							if(alreadySelected == false){
								t.fiDisplay.currentFINodes.push(node);
							}
							var ret = pba.displayFIOverlay(node, data.queryresults[0]);
							var displayNoConfNotification = false;
							if(data.queryresults.length == 0){
								$("clipwindow").style.cursor = "default";					
								alert("No interactors found in selected data source.");
								return;
							}
							if(ret && ret == "noconf=1")
								displayNoConfNotification = true;
							
							if(displayNoConfNotification == true){
								alert(t.controlPanel.NOCONFNOTIFICATION);
							}
							$("clipwindow").style.cursor = "default";					
						},
						failure: function(oResponse){
							// t.currentFINodes = new Array();
							$("clipwindow").style.cursor = "default";					
						},
						complete: function(oResponse){
							$("clipwindow").style.cursor = "default";
						}
					};
						var dblistid = $("dblistid");
						var id = $("dblistid").value;
						// Method stores the retrieval method to be used on
						// server.
						// 1 indicates a REST request
						// 2 for SOAP
						// 3 for local request for user uploaded data.
						var method = $(id).service;
						var label = id.replace(/Option/,"");
						var si = $(id).si;
						$("clipwindow").style.cursor = "wait";					
						if(method == "1"){
							YAHOO.util.Connect
							.asyncRequest(
									'POST',
									"/ReactomeGWT/entrypoint/elv/PSICQUICProxyServlet",
									callback,
									'refid='
									+ node.userObject.refid
								
									+ '&retrievalmethod=1'
									+ '&si='
									+ si
							);
						}else if(method == "3"){
							YAHOO.util.Connect
							.asyncRequest(
										'POST',
										"/ReactomeGWT/entrypoint/elv/PSICQUICProxyServlet",
										callback,
												'label=' + label
												+ '&refid='	+ node.userObject.refid
												+ '&retrievalmethod=3'
											);
						}
					},
					
					getCurrentFINodes : function(){
						return this.fiDisplay.currentFINodes;
					},
    				 disableContextMenu: function(element) {
				        element.oncontextmenu = function() {
				        return false;
				        }
				    },
				    
				    displayComplexPopupGeneral : function(param){
				    	var action = param.action;
			    	    var tip = $("ficanvas").getElementsBySelector('div.cplxcompstip');
			    	    var cplxNameDiv = null;
			    	    var closeTip = null;
			    	    var newlyCreated = false;
			    	    // Check if cplx tip has already been created. If not,
						// create new one.
			    	    if( tip.length == 0){				    	    	
			    	    	tip = Element
								.extend(document
										.createElement('div'));
			    	    	cplxNameDiv = Element.extend(document.createElement('div'));
			    	    	closetip = Element.extend(document.createElement('div'));
			    	    	closetip.className = 'cplxcompstip';
							closetip.id = "cplxclosetip";
							closetip.title = "Close Window";
							closetip.style.cursor = "pointer";
							closetip.innerHTML = "<img src=\"/entitylevelview/icons/whiteclose.png\" width=\"12\" height=\"12\" />";
							tip.className = 'cplxcompstip cplxcompstipid';
							tip.id = "cplxcompstipid";
							cplxNameDiv.className = 'cplxcompstip';
							cplxNameDiv.id = 'cplxnamedivid';
							newlyCreated = true;
			    	    }else{
			    	    	tip = tip[0];
			    	    	cplxNameDiv = document.getElementById('cplxnamedivid');
			    	    	closetip = document.getElementById('cplxclosetip');
			    	    }
						var node = this.currentCplxNode;
						var components = this.complexnodes[node.userObject.id];
						var cc = 3; // column count
									// : how many
									// cells to
									// display in a
									// single row
						var compCount = components.length;
						var rc = Math
								.ceil(compCount
										/ cc); // row
												// count
				
						var cellWidth = 50;
						var height = (cellWidth * Math.min(3, rc))+2;
						// var width = (cc * cellWidth)+10;
						var width = (cc * cellWidth)+this.complexTableMargin;
						var y = node.y;
						var x = node.x;
						var borderWidth = 10;
						var rBlue = "rgb(176,196,222)"; // Reactome blue
						
						// use unscaled coords for tooltip as displayTooltip
						// function
						// applies zoom
			
						var unscaledLeft = (x - (width / 2));
						var unscaledTop = (y - (height / 2));
						try{
							/*
							 * Save these values for use when checking if moused
							 * over node overlaps with popup
							 */
							tip.baseX = (x - (width / 2));
							tip.baseY = (y - (height / 2));
						}catch(err){
							
						}

						var left = ((x - (width / 2)) *this.zoomFactor) + this.miCanvasMargin;
						var top = ((y - (height / 2)) *this.zoomFactor) + this.miCanvasMargin;
						
						tip.setStyle( {
									left : left
											+ "px",
									top : top
											+ "px",
									height : height
											+ "px",
									width : width
											+ "px",
									background : this.cpbg,
									visibility : "visible",
									border: borderWidth + "px",
									borderColor: rBlue,
									zIndex : this.complexpopupZ,
									borderStyle:"solid"
								});
						var compsTable = "<div id=\"cplxtablediv\" style=\" width:" 
							+ width + "px; height:" + height + "px; overflow:auto;margin:mvxx\">";
						compsTable += "<table id=\"cplxpopuptable\"  onMouseOver=\"pba.pathwayDiagramPane.displayComplexTableTip(event," 
							+ unscaledLeft +"," 
							+ unscaledTop +", " 
							+ cellWidth 
							+")\"><colgroup><col/><col/><col/></colgroup>";
						var ieOffset = 0;
						var ieNameDivMod = 0;
						if(navigator.appName == "Microsoft Internet Explorer"){
							if(rc <= 3){
								compsTable = compsTable.replace("mvxx", "-10px");
								ieOffset = 10;
							}
							else{
								compsTable = compsTable.replace("mvxx", "0px");
								ieNameDivMod = 10;
							}
						}

						closetip
						.setStyle( {
							position:"absolute",
							left : (left+width+9-(ieOffset*2))
									+ "px",
							top : (top)
									+ "px",
									zIndex : this.complexpopupZ,
							height :12
									+ "px",
							width : 12
									+ "px"
						
						});
						cplxNameDiv.setStyle({
									left : left
											+ "px",
									top : (top+height+borderWidth-ieOffset)
											+ "px",
									width : (width + (ieNameDivMod*2))
											+ "px",
									background : rBlue,
									zIndex : this.complexpopupZ,
									border: borderWidth + "px",
									borderColor: rBlue,
									borderStyle:"solid",
									overflow:'hidden',
									textAlign : 'center',
									color : 'black'
								});

						cplxNameDiv.innerHTML = node.userObject.name;
						var cinc=0;
						for ( var ri = 0; ri < rc; ri++) { // row index
							compsTable += "<tr>";
							for ( var ci = 0; ci < cc; ci++) { // column index
								// If we have generated all tiles fill remainder
								// of row
								// with empty space.
								if ((cinc) >= compCount) {
									var colspan = (rc*cc) - cinc;
									compsTable += "<td  colspan=\"1\" id=\""
										+ p.dbid
										+ "\" width="
										+ cellWidth
										+ " height="
										+ cellWidth
										+ " style=\"background-color:" + this.cpbg 
										+ ";border-style:outset;border-width:0px;border-color:" 
										+ this.cpbg + ";\">";
								compsTable += "</td>";
									break;
								}
								var p = components[cinc];
								cinc++;
								
								if(action == "expression"){
									/* Start expression painting */
									var colour = this.expressionPainter.refIdMapping[p.refdbid];
									if(!colour){
										colour = this.noHitBG;
									}else{

										var rgb = colour.dataColours.data[this.currentExperiment];

										colour = 
											'rgb('
											+ this.expressionPainter.colourSpectrum[rgb].rgb[0]
										+ ','
										+ this.expressionPainter.colourSpectrum[rgb].rgb[1]
										+ ','
										+ this.expressionPainter.colourSpectrum[rgb].rgb[2]
										+ ')';
									}
									/* end expression painting */
								}else if(action == "species"){
									/* Start of species comparison specific code */
									colour = "blue";
									for(var speciesEntityIndex =0; speciesEntityIndex < this.speciesdata.comparisons.length; 
									speciesEntityIndex++){
										if(this.speciesdata.comparisons[speciesEntityIndex].refid == p.refdbid
												&& this.speciesdata.comparisons[speciesEntityIndex].other == 1){
											colour = "yellow";
											break;
										}
									}

									/* End of species comparison specific code */
								}
								
								compsTable += "<td id=\""
										+ p.dbid + ":" + p.name + ":" +p.refdbid
										+ "\" width="
										+ cellWidth
										+ " height="
										+ cellWidth
										+ " style=\"background-color: " 
										+ colour 
										+ ";border-style:outset;border-width:2px;border-color:" 
										+ this.cpbg + ";\">";
										// p.name;
										compsTable += "</td>";
							}
							compsTable += "</tr>";
						}
						compsTable += "</table>";
						compsTable += "</div>";
						tip.innerHTML = compsTable;
						if(newlyCreated == true){
							$("ficanvas")
								.appendChild(tip);
							$("ficanvas")
								.appendChild(closetip);
							$("ficanvas").appendChild(cplxNameDiv);
							YAHOO.util.Event.addListener("cplxclosetip",
								"mousedown", this.closetipHandler, this, true);
							YAHOO.util.Event.addListener('cplxcompstipid', 'mouseout',
								this.cplxmouseout, this, true);
							YAHOO.util.Event.addListener('cplxcompstipid', 'mouseover',
								this.cplxmouseover, this, true);
						}else{
							tip.setStyle({visibility: "visible", display:""});
							closetip.setStyle({visibility: "visible", display : ""});
							cplxNameDiv.setStyle({visibility: "visible", display:""});
						}
						this.cplxpopupdisplayed = true;

				    	
				    },

					cplxmouseover : function(e){	
						YAHOO.util.Event.removeListener('clipwindow', 'mousemove');
						
					},
					cplxmouseout : function(e){			
						YAHOO.util.Event.addListener('clipwindow', 'mousemove',
								this.handleMoveOverCanvas, this, true);
					},
					// Called by left/right arrows of experiment browser in
					// order
					// to update cells of table if displayed.
					updateComplexPopup: function(){
						var overlay = $("canvasoverlay");
						var table = $("ficanvas").getElementsBySelector('div.cplxcompstip');
						if(table.length > 0 && table[0].style.visibility == "visible"){
							
							this.displayComplexPopupGeneral({
								action	: "expression"
							});

						}
					},
					displayComplexTableTip : function(e, x , y, cellWidth){
						if(e.target){

						}else if(e.srcElement){
							e.target = e.srcElement;
						}
						var data = e.target.id.split(":");
						var dbid = data[0];
						var name = data[1];
 						var refdbid = data[2];
						var cellIndex = e.target.cellIndex;

						var rowIndex = e.target.parentNode.rowIndex;
						var scrollbar = document.getElementById('cplxtablediv');
						
						// calc position of tooltip by using cell/row indexes.
						// add to x,y pos of
						// complex popup.
						var xTip = x;
						
						var offsetX = ((cellIndex * cellWidth) - (cellWidth/2));
						// Use scroll position to adjust position of tooltip to
						// account for scrolling
						// down through list.
                     
						
                        var yTip = y;
                        // 3 is a 'magic' number that lines the tooltips up over
						// the cells in the complex
                        // popup table.
                        var offsetY = (((rowIndex* cellWidth)) - scrollbar.scrollTop) + (cellWidth/3); 

						
						// if there is no name then most likely an empty cell
						// has been moused over.
						if(name){
							this.displayTip(dbid, cellWidth * 1.3, cellWidth * 1.3, name, xTip, yTip, offsetX, offsetY, refdbid);
						}
					},
					closetipHandler : function(e){
						document.getElementById('cplxcompstipid').setStyle({visibility: "hidden"});
						document.getElementById('cplxclosetip').setStyle({visibility: "hidden"});
						document.getElementById('cplxnamedivid').setStyle({visibility: "hidden"});
					},
					onCtxMenuItemClick : function(type, args, me) {
						//console.log("args " + args);
						if(args[1] == "participatingmols"){
							var refdbid = args[0];
							this.participatingProteinClickedEvent.fire(refdbid);
						}else if(args[1] == "displayinteractors"){
							var node = args[0];
							this.displayInteractors(node);
						}else if(args[1] == "hideinteractors"){
							var node = args[0];
							this.fiDisplay.hideInteractors(node);
							var colour = "white";
							this.controlPanel.colourRows(node.userObject.id, node.id,  colour);
						} else if(args[1] == "displaycomponents"){
							var node = args[0];
							if (this.paintExpressionData == 1) {
								if(node.userObject.cls == "Complex" 
									|| 	node.userObject.cls == "OpenSet"
										|| 	node.userObject.cls == "DefinedSet"
										|| 	node.userObject.cls == "CandidateSet"
										|| 	node.userObject.cls == "Polymer"){
									var components = this
											.complexHasComponents(
													node.userObject.id,
													"check");
									if (components.length > 0) {
										this.currentCplxNode = node;
										this.displayComplexPopupGeneral({
											action	: "expression"
										});

									} // components.length > 0
								} // cls==complex
							} // expressiondata == 1
							else if(this.displayComparison == 1){
								if(node.userObject.cls == "Complex"
										|| 	node.userObject.cls == "OpenSet"
										|| 	node.userObject.cls == "DefinedSet"
										|| 	node.userObject.cls == "CandidateSet" 
										|| 	node.userObject.cls == "Polymer"){
									this.currentCplxNode = node;
									this.displayComplexPopupGeneral({
										action	: "species"
									});
								} // cls==complex
							}// displaycomparison==1
						}
						else{
							this.menuItemClickedEvent.fire(args[0], args[1], args[2]);
						}
					},
                    displayTip : function(dbid, w, h, name, x,y, offsetX, offsetY, refdbid){
						var id = "tip_" + dbid;
							// For some reason Safari often ignores the request
							// to remove the popups on mouseout.
							// Hence the need to "manually" remove them here.
							$("ficanvas").getElementsBySelector('div.tip')
									.invoke('remove');
							var tip = Element.extend(document
									.createElement('div'));
							tip.className = 'tip';
							tip.id = id;
							var nw, nh;
								nw = 2.5 * w;
								nh = 2 * h;
							tip.innerHTML = name;
							if (this.paintExpressionData == 1){
								var matchedEntity = this.expressionPainter.refIdMapping[refdbid];
								if(matchedEntity){
									tip.innerHTML += ". Id : " + matchedEntity.id + ". Level: " + matchedEntity.data[this.currentExperiment];
								}
							}
							
							tip.setStyle( {
								width : nw + "px",
								display : 'none'
							});
							Event.observe(tip, 'mousedown', function(event) {
								  // Event.stop(event);
								  // window.open("http://www.reactome.org");
								  var tokens = event.target.id.split("_",2);
								  var localid = tokens[1];
								 // Hard coded db for use with manual
									// diagrams. Replace
									// test_reactome_31_pathway_diagram with
									// 'this.db'
								 /*
									 * window.open("http://www.reactome.org/cgi-bin/eventbrowser?DB=" +
									 * this.db + "&ID=" + localid);
									 */
								  this.participatingProteinClickedEvent.fire(localid);
							}.bind(this));
							$("ficanvas").appendChild(tip);
							var border_w = 10;
							nh = tip.getHeight();
							x = (x*this.zoomFactor) + offsetX - (w/2) + this.miCanvasMargin;
							y = (y*this.zoomFactor) + offsetY + this.miCanvasMargin;
						
						tip.setStyle( {
							left : x + "px",
							top : y + "px",
							opacity : 0.85,
							height : nh + "px",
							border : border_w + "px solid #DCDCDC",
							background : "#DCDCDC",
							zIndex:this.complexpopupZ+1
						});
						new Effect.Appear(tip, {
							duration : 0.5,
							to : 0.85
						});
					},
					placeExperimentBrowser : function(){
						var availableWidth = $("clipwindow").getWidth();
						var browserWidth = $("experimentbrowser").getWidth();
						$("experimentbrowser").style.left = ((availableWidth/2) - (browserWidth/2))+"px";
						$("experimentbrowser").style.visibility = "visible";
					},
					/*
					 * If one type of overlay is acticated, deactivate the other
					 * one.
					 * 
					 * overlay 0 = newly activated mode is expression painter
					 * overlay 1 = newly activated mode is species comparison
					 */
					manageOverlays: function (overlay) {
						if(overlay == 0){
							this.onSpeciesComparisonClose();
						}else if (overlay == 1){
							this.closeExperiment();
						}
					},
					/*
					 * Calculate max, min
					 */
					calcSpectrumValues:function(){
						
					},
					displayExpressionLevels : function(scale) {
						this.manageOverlays(0);
						this.maxValue = null;
						this.minValue = null;
						this.midValue = null;
						document.body.style.cursor = "wait";
						try{
							var i = 0;
							var colourIndex = 0;
							this.paintExpressionData = 1;
							if (this.focus_pathway_id != null) {
								$("canvasoverlay").getElementsBySelector(
								'div.expressiontips').invoke('remove');
								this.placeExperimentBrowser();
								if (scale) {
									this.expressionPainter.displayKey(scale);
								} else {
									this.expressionPainter.displayKey(1);
								}

								for ( var nodeitem in this.nodes) {
									if (nodeitem && nodeitem != "undefined") {
										var tip = Element.extend(document
												.createElement('div'));
										tip.className = 'expressiontips';
										tip.id = "expressiontip_" + nodeitem;
										var node = this.nodes[nodeitem];
									if (node.cls != "ReactionVertex") {
											var w = node.w;
											var h = node.h;
											var nw, nh = 5;
											nw = w +1;
											nh = h +1;
											if (this.zoomFactor == 1) {
												tip.innerHTML = node.userObject.name;
												tip.innerHTML = tip.innerHTML
												.replace(/\[.*/, "");
												tip.innerHTML = tip.innerHTML.replace(/\:/g,": ");
											}
											nw = nw * this.zoomFactor;
											nh = nh * this.zoomFactor;
											tip.setStyle( {
												width : nw + "px",
												height : nh + "px",
												fontSize : "10px",
												textAlign : "center"
											});
											$("canvasoverlay").appendChild(tip);
											var border_w = 5;
											nh = tip.getHeight();

											var nodex = this.nodes[nodeitem].x;
											var nodey = this.nodes[nodeitem].y;
											nodex = nodex * this.zoomFactor
											- border_w
											- (nw - w * this.zoomFactor)
											/ 2;
											nodey = nodey * this.zoomFactor
											- border_w
											- (nh - h * this.zoomFactor)
											/ 2;

											colourIndex++;
											if(node.userObject.cls == "SimpleEntity"){
												this.processDisplaySimpleEntity(tip, node, nw, nh, nodex, nodey);
											}else
												if (node.userObject.cls != "Complex"
													&& node.userObject.cls != "OpenSet"
													&& node.userObject.cls != "DefinedSet"
													&& node.userObject.cls != "CandidateSet"
													&& node.userObject.cls != "Polymer") {
													// Check if we have this
													// entity matches something
													// in the input data.
													var matchedEntity = this.expressionPainter.refIdMapping[node.userObject.refid];
													if (matchedEntity) {
														// var rgb =
														// this.expressionPainter.dataColours[colourIndex];
														/*
														 * Calculate max/min
														 * values
														 */
														if(this.maxValue == null || this.maxValue < parseFloat(matchedEntity.data[this.currentExperiment])){
														this.maxValue = parseFloat(matchedEntity.data[this.currentExperiment]);
														}
														if(this.minValue == null || this.minValue > parseFloat(matchedEntity.data[this.currentExperiment])){
															this.minValue = parseFloat(matchedEntity.data[this.currentExperiment]);
														}
														/*
														 * End calculate max/min
														 * values
														 */
														if(this.zoomFactor == 1){
															tip.innerHTML += ". Id : " + matchedEntity.id + ". Level: " + matchedEntity.data[this.currentExperiment];
														}
														tip.className = tip.className + " proteinexpressiontip"
														var rgb = matchedEntity.dataColours.data[this.currentExperiment];
														tip
														.setStyle( {
															left : nodex + 5
															+ "px",
															top : nodey + 5
															+ "px",
															height : nh + "px",

															background : 'rgb('
																+ this.expressionPainter.colourSpectrum[rgb].rgb[0]
																                                                 + ','
																                                                 + this.expressionPainter.colourSpectrum[rgb].rgb[1]
																                                                                                                  + ','
																                                                                                                  + this.expressionPainter.colourSpectrum[rgb].rgb[2]
																                                                                                                                                                   + ')'
														});

													} else {
														// No match so set to
														// grey.
														tip
														.setStyle( {
															left : nodex + 5
															+ "px",
															top : nodey + 5
															+ "px",
															height : nh + "px",
															background : this.noHitBG
														});
														tip.style.color="white";
													}

													// check if queryterminal
													// overlay exists.
													this.processFITerminal(node, tip);

												} else { // its a complex
													var components = this
													.complexHasComponents(
															this.nodes[nodeitem].userObject.id,
													"check");
													if (components.length > 0) {
														// tip.style.backgroundImage
														// =
														// "url(/icons/complex.png)";
														tip.setStyle( {
															background : 'rgb(0,0,0)'
														});
													} else {
														tip
														.setStyle( {
															background : this.noHitBG
														});
													}
													tip.setStyle( {
														left : nodex + 5 + "px",
														top : nodey + 5 + "px",
														height : nh + "px"
													});
													tip.style.color = "white";
												}
											var name = this.expressionPainter.columnNames[this.currentExperiment];
											$("experimentname").innerHTML= name;
											
											var number = (this.currentExperiment + 1) + "/" + this.expressionPainter.columnNames.length;
											$("experimentnumber").innerHTML = number;
										} 
										tip.style.zIndex=20;
									}
								}
							} // end if(focus pathway != null)
							$("expmodecheck").checked = true;
						}catch(ex){
							alert("There was a problem overlaying expression data.");
							this.closeExperiment();
						}

						document.body.style.cursor = "default";

						/* Calculate max/min values */
						this.midValue=0.00;
						if(this.minValue != null && this.maxValue != null){
							this.midValue = ((this.maxValue - this.minValue)/2)+this.minValue;
							this.minValue = this.minValue.toFixed(2);
							this.maxValue = this.maxValue.toFixed(2);
							this.midValue = this.midValue.toFixed(2);
						}
						else{
							this.maxValue=0;
							this.minValue=0;
						}
						$("experimentmaxvalue").innerHTML=parseFloat(this.maxValue).toFixed(2);
						$("experimentmidvalue").innerHTML=parseFloat(this.midValue).toFixed(2);
						$("experimentminvalue").innerHTML=parseFloat(this.minValue).toFixed(2);
			            /* End calculate max/min values */
					},
					
					/*
					 * if a queryterminal (from mi overlay) exists for this
					 * node, modify its appearance and hide expression tip.
					 */
					processFITerminal : function(node, expressiontip){
						var queryTerminal = $("queryterminal_" + node.id);
						if(queryTerminal){
						
							queryTerminal.className = 'proteincolour queryTerminal queryComponents'+node.id + " expressiontips";
							queryTerminal.style.background = expressiontip.style.background;
							queryTerminal.innerHTML = expressiontip.innerHTML;
							expressiontip.style.display = "none";
						}
					},
					complexHasComponents : function(index, check) {
						var complex = this.complexnodes[index];
						var components = new Array();
						if(complex){
						for ( var cptI = 0; cptI < complex.length; cptI++) {
							var cpt = this.expressionPainter.refIdMapping[complex[cptI].refdbid];
							// set = null if no hit in refIdMapping?
							if (cpt) {							
								
								components[cptI] = complex[cptI];
								
								/* Calculate max/min values */
								if(this.maxValue == null || this.maxValue < parseFloat(cpt.data[this.currentExperiment])){
									this.maxValue = parseFloat(cpt.data[this.currentExperiment]);	
								}
								if(this.minValue == null || this.minValue > parseFloat(cpt.data[this.currentExperiment])){
									this.minValue = parseFloat(cpt.data[this.currentExperiment]);	
								}
								
								/* End calculate max/min values */
							}
						
						}
					}	
						return components;
					},
					complexHasComponentsForSpeciesComparison : function(index) {
						var complex = this.complexnodes[index];
						var components = new Array();
						if(complex) return true;
						return false;
					},
					getComplexComponents : function(scale) {
						this.getComplexComponentsGeneral({
							'scale' 	: scale,
							'selector' 		: this.COMPLEX_COMPONENTS_EXPRESSION
						});
						},
					getComplexComponentsForSpeciesComparison : function(speciesdata) {
						this.getComplexComponentsGeneral({
							'speciesdata' 	: speciesdata,
							'selector' 		: this.COMPLEX_COMPONENTS_SPECIES_COMPARISON
						});

					},

					/*
					 * When an id is submitted the server, it returns a list of
					 * dbid for that entity and entities that contain it, such
					 * as complexes. Entities in this list are highlighted
					 */
					getComplexComponentsForNodeHighlighting : function(map_highlights, selectedEntity) {
						this.getComplexComponentsGeneral({
							'map_highlights' : map_highlights,
							'selectedEntity' : selectedEntity,
							'selector'		 : this.COMPLEX_COMPONENTS_NODE_HIGHLIGHTING
						});
					},
					getComplexComponentsGeneral: function(params){
						if(params.selector == this.COMPLEX_COMPONENTS_SPECIES_COMPARISON){
							var speciesdata = params.speciesdata;

							if(speciesdata == undefined || speciesdata.comparisons == undefined ){
								if(this.focus_species_id == "48887"){
									alert("No data found for species comparison. Closing species comparison.");
								}else{
									alert(this.COMPARISON_NOT_ALLOWED_STRING);
								}
								this.onSpeciesComparisonClose();
								return;
							}
						}
						
						var idstring = "complexidlist=";
						for ( var nodeitem in this.dbnodes) {
							if (nodeitem) {
								var currentnode = this.nodes[this.dbnodes[nodeitem]];
								if (currentnode.userObject.cls == "Complex"
								|| 	currentnode.userObject.cls == "OpenSet"
								|| 	currentnode.userObject.cls == "DefinedSet"
								||      currentnode.userObject.cls == "CandidateSet"
								|| 	currentnode.userObject.cls == "Polymer")
								{
									idstring = idstring + "," + nodeitem;
								}
							}
						}
						idstring = idstring.replace(/^complexidlist=,/,
								"complexidlist=");
						idstring = idstring.replace(/,$/, "");
						
						var callback = {
								scope : this,
								customevents : {
									onSuccess : function(eventType, args) {							
									// var o = eval('(' + args[0].responseText +
									// ')');
										var o = getJsonFromRawText(args[0].responseText);
										this.complexnodes = new Object();
										if(o.clxs){
											for ( var nodeitem = 0; nodeitem < o.clxs.length; nodeitem++) {
												try{
													if(o.clxs[nodeitem].cpts.size && o.clxs[nodeitem].cpts.size() > 1){
														this.complexnodes[o.clxs[nodeitem].dbid] = o.clxs[nodeitem].cpts;
													}else{
														this.complexnodes[o.clxs[nodeitem].dbid] = new Array();
														this.complexnodes[o.clxs[nodeitem].dbid][0] = o.clxs[nodeitem].cpts; 
													}
												}catch(err){
													// alert("Problem comparing
													// species " +
													// err.description);
												}
											}
										}
										// this.paintSpeciesComparison(speciesdata);
										var selector = parseInt(params.selector);
										switch(selector){
										case this.COMPLEX_COMPONENTS_EXPRESSION:
										case this.COMPLEX_COMPONENTS_PARTICIPATING_MOLECULES:
											var scale = params.scale;
											this.displayExpressionLevels(scale);
											break;
										case this.COMPLEX_COMPONENTS_SPECIES_COMPARISON:
											var speciesdata = params.speciesdata;
											this.paintSpeciesComparison(speciesdata);								
											break;
										case this.COMPLEX_COMPONENTS_NODE_HIGHLIGHTING:
											var map_highlights = params.map_highlights;
											var selectedEntity = params.selectedEntity;
											this.processHighlites(map_highlights, null, selectedEntity);
											break;
										default:
											alert("Warning: no matching mode found for complexes");
										}
					
								},
								onFailure : function(eventType, args) {						
								}
								}
							};
							
							

							YAHOO.util.Connect.asyncRequest('POST',
									"/ReactomeGWT/entrypoint/elv/ComplexComponentsServlet",
									callback, idstring
											+ "&class=EntityWithAccessionedSequence");
						
					},

					initDisplayExpressionLevels : function(scale) {
						this.getComplexComponents(scale);
					},
					bringElementIntoViewPort : function(el) {
						el = $(el);
						var dims = Element.getDimensions(el);
						var ex = parseInt(el.getStyle('left'));
						var ey = parseInt(el.getStyle('top'));
						var canvas = $('canvas');
						var cx = parseInt(canvas.getStyle('left'));
						var cy = parseInt(canvas.getStyle('top'));
						var dx = 0;
						if (ex < -cx) {
							dx = -cx - ex;
						} else if ((ex + dims.width) > (-cx + this.iClipWidth())) {
							dx = (-cx + this.iClipWidth()) - (ex + dims.width);
						}
						var dy = 0;
						if (ey < -cy) {
							dy = -cy - ey;
						} else if ((ey + dims.height) > (-cy + this
								.iClipHeight())) {
							dy = (-cy + this.iClipHeight())
									- (ey + dims.height);
							dy -= 10;
						}
						if ((dx != 0) || (dy != 0)) {
							this.animatedReposition(dx, dy);
						}
					},
					handleHighliteMouseout : function(event, dbid) {
					},
					animatedReposition : function(x, y) {
						if(isNaN(x) || isNaN(y)){
							return;
						}
						new Effect.Move2($("canvas"), {
							x : x,
							y : y,
							obj : this
							//,afterFinish: this.fitPathway.bind(this)
						});
					},
					processHighlites : function(highlites, vertexId, selectedEntityId, centre) {
						//Centre is only a parameter passed by loadAndProcessData in
						//PathwayBrojer.js, and the next line sets it to TRUE when is not passed
						centre = ( centre == undefined ) ? true : centre;
						if (highlites) {
							if (highlites.length > 1000) {
								this
										.highliteNodesWithIdsWithPeriodicalExecutioner(highlites);
							} else {
								/*
								 * If the id is for a vertex highlight just that
								 * vertex. If we highlight by reactome db id and
								 * the entity appears multiple times in the
								 * diagram (like ADP) then all instances of the
								 * entity in the diagram. In the case of a URL
								 * this is probably not what the user wnats if
								 * they have bokmarked a URL with a particular
								 * VID (vertex id)
								 */
								if(vertexId){
									this
									.highliteNodesWithId(highlites, null, selectedEntityId);
								}else{
									this
										.highliteNodesForRepresentedInstanceIds(highlites, null, selectedEntityId);
								}
								if (centre && ((highlites.length == 1) || (this.highlitedCoordinates.length == 1))
										&& this.highlitedCoordinates.length > 0) {
									this.centreOnCoordinate(
											this.highlitedCoordinates[0].x,
											this.highlitedCoordinates[0].y);
								}
							}
						} else {
							this.clearHighlites();
						}
					},
					showDiagramsForVisibleComplexAndSetNodesIfNecessary : function() {
						if ($("entitydiagrams")
								&& $("entitydiagrams").visible()) {
							this.showDiagramsForVisibleComplexAndSetNodes();
						}
					},
					showDiagramsForVisibleComplexAndSetNodes : function() {
						var coords = this.findVisibleComplexAndSetNodes();
						coords.each( function(n) {
							showDiagramForNode(n);
						});
					},
					showDiagramForNode : function(node) {
						var id = "diagram_" + node.userObject.id + "_" + node.x
								+ "_" + node.y;
						if (!$(id)) {
							var x = this.zoomFactor * node.x;
							var y = this.zoomFactor * node.y;
							/*
							 * For some reason, have to make images a bit wider
							 * in order to cover the underlying node completely.
							 */
							var w = this.zoomFactor * node.w + 1;
							var h = this.zoomFactor * node.h + 1;
							var imgsrc = this.imgdir + '/entitydiagrams/'
									+ node.userObject.id + '.png';
							var img = Element.extend(document
									.createElement('img'));
							img.id = id;
							img.setStyle( {
								position : "absolute",
								top : y + "px",
								left : x + "px",
								width : w + "px",
								height : h + "px"
							});
							img.src = imgsrc;
							$("entitydiagrams").appendChild(img);
						}
					},
					findVisibleComplexAndSetNodes : function() {
						var canvas = $('canvas');
						var x1 = -parseInt(canvas.getStyle('left'))
								/ this.zoomFactor;
						var y1 = -parseInt(canvas.getStyle('top'))
								/ this.zoomFactor;
						var x2 = (-parseInt(canvas.getStyle('left')) + this
								.iClipWidth())
								/ this.zoomFactor;
						var y2 = (-parseInt(canvas.getStyle('top')) + this
								.iClipHeight())
								/ this.zoomFactor;
						return this
								.findNodesInArea(x1, y1, x2, y2)
								.findAll(
										function(n) {
											return ((n.userObject.cls == "Complex")
													|| (n.userObject.cls == "DefinedSet") || (n.userObject.cls == "CandidateSet"));
										});
					},
					resetExpressionKey : function(scale) {
						
						if (this.expressionPainter != null
								&& this.paintExpressionData == 1
								&& this.focus_pathway_id != null) {
						
							this.expressionPainter.clearSpectrum();
							this.expressionPainter.displayKey(scale);
						}
					},

					findNodesInArea : function(x1, y1, x2, y2) {
						var out = new Array();
						for ( var j = 0, l = this.orderedRepresentedInstances.length; j < l; ++j) {
							var coords = this.orderedRepresentedInstances[j].coords;
							for ( var i = 0; i < coords.length; i++) {
								if ((x2 > coords[i].x)
										&& (x1 < coords[i].x + coords[i].w)
										&& (y2 > coords[i].y)
										&& (y1 < coords[i].y + coords[i].h)) {
									out.push(coords[i]);
								}
							}
						}
						return out;
					},
										
					getVisibleArea : function() {
						var canvas = $('canvas');
						var x = -parseInt(canvas.getStyle('left'))
								/ this.zoomFactor;
						var y = -parseInt(canvas.getStyle('top'))
								/ this.zoomFactor;
						var w = this.iClipWidth() / this.zoomFactor;
						var h = this.iClipHeight() / this.zoomFactor;
						return {
							"x" : x,
							"y" : y,
							"w" : w,
							"h" : h
						};
					},
					
					isAnyNodeVisible : function(nodeArray) {
						var r = getVisibleArea();
						var x1 = r.x;
						var x2 = r.x + r.w;
						var y1 = r.y;
						var y2 = r.y + r.h;
						for ( var i = 0, l = nodeArray.length; i < l; ++i) {
							var n = nodeArray[i];
							if ((x2 > n.x) && (x1 < n.x + n.w) && (y2 > n.y)
									&& (y1 < n.y + n.h)) {
								return true;
							}
						}
						return false;
					},
					
					transientlyHighliteNode : function(node) {
						// log("transientlyHighliteNode x = " + node.x + ", y =
						// " + node.y);
						var x = this.zoomFactor * node.x;
						var y = this.zoomFactor * node.y;
						var w = this.zoomFactor * node.w;
						var h = this.zoomFactor * node.h;
						if (YAHOO.env.ua.ie > 0) {
							w += 2 * this.highliteBorderThickness;
							h += 2 * this.highliteBorderThickness;
						}
						x -= this.highliteBorderThickness;
						y -= this.highliteBorderThickness;
						var thl = Element.extend(document.createElement('div'));
						thl.innerHTML = "&nbsp;";
						thl.id = "thl_" + node.id;
						thl.className = "transienthighlite";
						thl.setStyle( {
							position : "absolute",
							top : y + "px",
							left : x + "px",
							width : w + "px",
							height : h + "px"
						});
						$("canvasoverlay").appendChild(thl);
						// Thumb
						x = node.x / this.imgw * this.thumbnailw - 1;
						y = node.y / this.imgh * this.thumbnailh - 1;
						var t_thl = Element.extend(document
								.createElement('div'));
						t_thl.innerHTML = "&nbsp;";
						t_thl.id = "t_thl_" + node.id;
						t_thl.className = "thumbtransienthighlite";
						t_thl.setStyle( {
							position : "absolute",
							top : (y - 1) + "px",
							left : (x - 1) + "px",
							width : "3px",
							height : "3px"
						});
						$("thumbnailoverlay").appendChild(t_thl);
					},
					removeTransientHighlites : function() {
						this.dbidOfNodeBeingHighlited = null;
						if ($("canvasoverlay") != null) {
							$A(
									$("canvasoverlay").getElementsByClassName(
											"transienthighlite")).map(
									function(e) {
										e.remove();
									});
							$A(
									$("thumbnailoverlay")
											.getElementsByClassName(
													"thumbtransienthighlite"))
									.map( function(e) {
										e.remove();
									});
						}
					},
					removeTransientHighliteFromNode : function(node) {
						this.removeTransientHighliteFromNodeWithId(node.id);
					},
					removeTransientHighliteFromNodeWithId : function(id) {
						var thl = $("thl_" + id);
						if (thl) {
							thl.remove();
						}
						var t_thl = $("t_thl_" + id);
						if (t_thl) {
							t_thl.remove();
						}
					},
					transientlyHighliteNodesForRepresentedInstance : function(i) {
						var t = this;
						i.coords.map( function(n) {
							// t.transientlyHighliteNode(n);
						});
					},
					removeTransientlyHighliteFromNodesForRepresentedInstance : function(
							i) {
						var t = this;
						i.coords.map( function(n) {
							t.removeTransientHighliteFromNode(n);
						});
					},
					transientlyHighliteNodesForRepresentedInstanceId : function(
							args) {
						this.removeTransientHighlites();
						var id = args[0];
						if (this.current_instance_id == id)
							return;
						if (this.representedInstances == null)
							return;
						var i = this.representedInstances[id];
						if (i) {
							this
									.transientlyHighliteNodesForRepresentedInstance(i);
							// centreOnCoordinate(i.coords[0].x, i.coords[0].y);
						} else if (this.transientHighliteIdCache[id]) {
							this
									.transientlyHighliteNodesWithIds(this.transientHighliteIdCache[id]);
							// if (this.transientHighliteIdCache[id][0])
							// centreOnNodeWithId(this.transientHighliteIdCache[id][0]);
						} else {
							this.loadTransientHighlites(id);
						}
						var childpaths = args[1].split(" ");
						if(childpaths){
						var childpathslist = $A(childpaths);
						if(childpathslist.length > 0){
							for(var index=0; index< childpathslist.length; index++){
								var cid = childpaths[index];
								i = this.representedInstances[cid];
								if (i) {
									this
											.transientlyHighliteNodesForRepresentedInstance(i);
									// centreOnCoordinate(i.coords[0].x,
									// i.coords[0].y);
								} else if (this.transientHighliteIdCache[id]) {
									this
											.transientlyHighliteNodesWithIds(this.transientHighliteIdCache[id]);
									// if (this.transientHighliteIdCache[id][0])
									// centreOnNodeWithId(this.transientHighliteIdCache[id][0]);
								} else {
									this.loadTransientHighlites(id);
								}
							}
						}
						}
						
					},
					removeTransientlyHighliteFromNodesForRepresentedInstanceId : function(
							id) {
						var i = this.representedInstances[id];
						if (i) {
							this
									.removeTransientlyHighliteFromNodesForRepresentedInstance(i);
						} else if (this.transientHighliteIdCache[id]) {
							// log(this.transientHighliteIdCache[id]);
							this
									.removeTransientHighlitesFromNodesWithIds(this.transientHighliteIdCache[id]);
						}
					},
					highliteNodesForRepresentedInstance : function(i,
							keepPrevious, selectedEntityId) {
				if (!keepPrevious)
							this.clearHighlites();
						var t = this;
						i.coords.map( function(n) {
							t.highliteInViewportAndCache(n, true, selectedEntityId);
						});
					},
					highliteNodesForRepresentedInstanceId : function(id,
							keepPrevious) {
						var i = this.representedInstances[id];
						if (i)
							this.highliteNodesForRepresentedInstance(i,
									keepPrevious);
					},
					highliteNodesForRepresentedInstanceIds : function(ids,
							keepPrevious, selectedEntityId) {
					if (!keepPrevious)
							this.clearHighlites();
						for ( var j = 0, l = ids.length; j < l; ++j) {
							var i = this.representedInstances[ids[j]];
							if (i) {
								this.highliteNodesForRepresentedInstance(i,
										true, selectedEntityId);
							} else {
								// log("No representedInstance with id
								// "+ids[j]);
							}
						}
					},
					transientlyHighliteNodesWithIds : function(ids) {
						var t = this;
						ids.map( function(id) {
							var n = t.nodes[id];
							if (n != null && n.userObject.cls == "Reaction"){
								
							// t.transientlyHighliteNode(n)
							}
						});
					},
					removeTransientHighlitesFromNodesWithIds : function(ids) {
						var t = this;
						ids.map( function(id) {
							t.removeTransientHighliteFromNodeWithId(id)
						});
					},
					isInViewPort : function(n) {
						// log(this.viewportRight+">"+n.x+","+this.viewportLeft
						// +"<"+(n.x+n.w)+","+this.viewportBottom+">"+n.y+","+this.viewportTop+"<"+(n.y+n.h));
						return ((this.viewportRight > n.x)
								&& (this.viewportLeft < n.x + n.w)
								&& (this.viewportBottom > n.y) && (this.viewportTop < n.y
								+ n.h));
					},
					anyInViewPort : function(nodes) {
						for ( var i = 0, l = nodes.length; i < l; ++i) {
							if (this.isInViewPort(nodes[i]))
								return true;
						}
						return false;
					},
					/*
					 * The point of this is to do the highliting is small chunks
					 * so that the browser doesn't get "tired" with the process
					 * and suggest to kill it.
					 */
					highliteWithPeriodicalExecutioner : function(nodeArray) {
						var c = 0;
						var step = 500;
						var t = this;
						new PeriodicalExecuter( function(pe) {
							var t1 = new Date().getTime();
							for ( var i = c; i < c + step; ++i) {
								if (i >= nodeArray.length) {
									pe.stop();
									break;
								} else {
									var node = nodeArray[i];
									t.highliteInViewportAndCache(node, true);
								}
							}
							var t2 = new Date().getTime();
							// log("PeriodicalExecuter "+c+" ran for "+(t2 -
							// t1)/1000 + "s");
								c += step;
							}, 1);
					},
					/*
					 * The point of this is to do the highliting is small chunks
					 * so that the browser doesn't get "tired" with the process
					 * and suggest to kill it.
					 */
					highliteNodesWithIdsWithPeriodicalExecutioner : function(
							idArray) {
						this.clearHighlites();
						var c = 0;
						var step = 1000;
						var t = this;
						new PeriodicalExecuter(
								function(pe) {
									var t1 = new Date().getTime();
									for ( var i = c; i < c + step; ++i) {
										if (i >= idArray.length) {
											pe.stop();
											break;
										} else {
											var node = t.representedInstances[idArray[i]];
											if (node != null) {
												t
														.highliteNodesForRepresentedInstance(
																node, true);
											} else {
												// log("No node: Idx: " + i + ",
												// DB_ID:" + idArray[i]);
											}
										}
									}
									var t2 = new Date().getTime();
									// log("PeriodicalExecuter "+c+" ran for
									// "+(t2 - t1)/1000 + "s");
									c += step;
								}, 1);
					},
					centreOnNodeWithId : function(node_id) {
						var node = this.nodes[node_id];
						if (node) {
							this.centreOnCoordinate(node.x, node.y);
						} else {
							log("No node with id " + node_id, "warn");
						}
					},
					getNodesWithIds : function(ids) {
						var out = [];
						for ( var i = 0, l = ids.length; i < l; ++i) {
							var n = this.nodes[ids[i]];
							if (n != null) {
								out.push(n);
							}
						}
						return (out);
					},
					/* not used */
					getNodesWithRepresentedInstanceIds : function(ids) {
						var out = [];
						for ( var i = 0, l = ids.length; i < l; ++i) {
							var n = this.representedInstances[ids[i]];
							if (n != null) {
								var coords = n.coords;
								for ( var j = 0, m = coords.length; j < m; ++j) {
									out.push(coords[j]);
								}
							}
						}
						return (out);
					},
					loadTransientHighlites : function(dbid) {
						if (!this.inLoadTransientHighlites) {
							this.dbidOfNodeBeingHighlited = dbid;
							this.inLoadTransientHighlites = true;
							var t = this;
							new Ajax.Request(
									this.diagramhighlitesurl + '?DB=' + this.db
											+ '&FOCUS_SPECIES_ID='
											+ this.focus_species_id + '&ID='
											+ dbid,
									{
										method : 'get',
										onSuccess : function(transport) {
											var rtxt = transport.responseText;
											// var o = eval(rtxt);
											var o = getJsonFromRawText(rtxt);
											t.transientHighliteIdCache[dbid] = o.highlites;
											if (t.dbidOfNodeBeingHighlited == dbid)
												t
														.transientlyHighliteNodesWithIds(o.highlites);
										},
										onComplete : function() {
											t.inLoadTransientHighlites = false;
										}
									});
						}
					},
					removeImageTiles : function() {
						// log("removeImageTiles()");
						var tiles = $("tiles");
						if (tiles) {
							// for ( var i = 0, l = tiles.childNodes.length; i <
							// l; ++i) {
							for ( var i = tiles.childNodes.length -1 ; i >-1; i--) {
								/*
								 * Some more black magic - have to remove the
								 * tiles from the end in order to be able to
								 * remove them all (or most of them). Ones which
								 * have become 'undefined' nevertheless show up
								 * on screen so that zoomed-out diagram contains
								 * tiles from previous zoom level.
								 */
								var c = tiles.childNodes[i];
								if (typeof (c) != 'undefined') {
									$(c).remove();
								}
							}
						}
					},
					setImgDir : function() {
						this.imgdir = this.basedir + this.db + "/"
								+ this.focus_species_id + "/"
								+ this.focus_pathway_id;
					},
					changeFocusPathway : function(pathway_id) {
						if (this.focus_species_id != null) {
							// Clear the mi overlay when changing pathways.
							// An alternative would be to check if new pathway
							// shares proteins with
							// old pathway that the user has overlayed
							// interactors for and overlay
							// these interactors in new pathway.
							if(this.fiDisplay != null){
								this.fiDisplay.cleanUp();
								this.fiDisplay = new FIDisplay();
								this.fiDisplay.init(this.intactSearchUrl, this.miCanvasMargin);
							}
							if(this.controlPanel != null){
								this.controlPanel.hideTable();
							}
							this.clearHighlites();
							this.removeImageTiles();
							this.focus_pathway_id = pathway_id;
							this.setImgDir();
							this.loadOrderedCoordinates();
					}
			},
			onTransientHighliteRequest : function(type) {
				// log("onTransientHighliteRequest " + args);
				var args = $A(arguments); 
					this.transientlyHighliteNodesForRepresentedInstanceId(args[1][0]);
				},
				onTransientHighliteRemovalRequest : function(type) {
					// log("onTransientHighliteRemovalRequest");
					this.removeTransientHighlites();
				}
				});

/*
 * Extend scriptaculous' Effect.Move so that the tiles get created/populated
 * upon move. This is what the reposition(0,0) is for. OK, undoubtedly things
 * could be done more efficiently.
 */
Effect.Move2 = Class.create();
Object.extend(Object.extend(Effect.Move2.prototype, Effect.Move.prototype), {
	update : function(position) {
		this.element
				.setStyle( {
					left : Math.floor(this.options.x * position
							+ this.originalLeft) + 'px',
					top : Math.floor(this.options.y * position
							+ this.originalTop) + 'px'
				});
		this.options.obj.reposition(0, 0);
	}
});

/*
 * Overload diagram highlighting related methods if the browser is old IE which
 * doesn't support transparency.
 */
if ((YAHOO.env.ua.ie > 0) && (YAHOO.env.ua.ie < 7)) {
	Object.extend(PathwayDiagramPane.prototype, {
		highliteNode : function(node) {
			var oCO = $("canvasoverlay");
			var x = this.zoomFactor * node.x;
			var y = this.zoomFactor * node.y;
			var w = this.zoomFactor * node.w;
			var h = this.zoomFactor * node.h;
			if (YAHOO.env.ua.ie > 0) {
				w += 2 * this.highliteBorderThickness;
				h += 2 * this.highliteBorderThickness;
			}
			x -= this.highliteBorderThickness;
			y -= this.highliteBorderThickness;
			var node_id = 'hl_' + node.id;
			var hl = Element.extend(document.createElement('div'));
			hl.innerHTML = "&nbsp;";
			hl.id = node_id;
			hl.className = "highlite";
			hl.setStyle( {
				position : "absolute",
				top : y + "px",
				left : x + "px",
				width : w + "px",
				height : h + "px"
			});
			Event.observe(hl, 'mouseover', this.handleHighliteMouseover
					.bindAsEventListener(this, node.id));
			Event.observe(hl, 'mouseout', this.handleHighliteMouseout
					.bindAsEventListener(this, node.id));
			Event.observe(hl, 'mousemove', this.handleHighliteMouseover
					.bindAsEventListener(this, node.id));
			$("canvasoverlay").appendChild(hl);
			node.isHighlited = true;
			return hl;
		},
		highliteCoordinate : function(x, y, w, h, keepPrevious) {
			// log("EntityLevelView.highliteCoordinate()");
			x *= this.zoomFactor;
			y *= this.zoomFactor;
			w *= this.zoomFactor;
			h *= this.zoomFactor;
			var oCO = $("canvasoverlay");
			if (YAHOO.env.ua.ie > 0) {

				w += 2 * this.highliteBorderThickness;
				h += 2 * this.highliteBorderThickness;
			}
			x -= this.highliteBorderThickness;
			y -= this.highliteBorderThickness;
			var tmp = '<div class="highlite" style="position:absolute;top:' + y
					+ ';left:' + x + ';width:' + w + 'px;height:' + h
					+ 'px;" onmouseover=""><!-- --></div>';
			if (keepPrevious) {
				oCO.innerHTML += tmp;
			} else {
				oCO.innerHTML = tmp;
			}
		},
		highliteNodeIfInViewPort : function(node) {
			if (this.isInViewPort(node)) {
				var oCO = $("canvasoverlay");
				var x = this.zoomFactor * node.x;
				var y = this.zoomFactor * node.y;
				var w = this.zoomFactor * node.w;
				var h = this.zoomFactor * node.h;
				if (YAHOO.env.ua.ie > 0) {
					w += 2 * this.highliteBorderThickness;
					h += 2 * this.highliteBorderThickness;
				}
				x -= this.highliteBorderThickness;
				y -= this.highliteBorderThickness;
				var node_id = 'hl_' + node.id;
				var hl = $(node_id);
				if (hl == null) {
					var hl = Element.extend(document.createElement('div'));
					hl.innerHTML = "&nbsp;";
					hl.id = node_id;
					hl.className = "highlite";
					Event.observe(hl, 'mouseover', this.handleHighliteMouseover
							.bindAsEventListener(this, node.id));
					Event.observe(hl, 'mouseout', this.handleHighliteMouseout
							.bindAsEventListener(this, node.id));
					Event.observe(hl, 'mousemove', this.handleHighliteMouseover
							.bindAsEventListener(this, node.id));
					$("canvasoverlay").appendChild(hl);
				}
				hl.setStyle( {
					position : "absolute",
					top : y + "px",
					left : x + "px",
					width : w + "px",
					height : h + "px"
				});
				return hl;
			}
		},
		clearHighlites : function() {
			this.highlitedCoordinates.map( function(n) {
				n.isHighlited = false;
			});
			this.highlitedCoordinates.length = 0;
			var oCO = $("canvasoverlay");
			if (oCO) {
				oCO.innerHTML = '';
				var to = $("thumbnailoverlay");
				to.innerHTML = '';
			}
		}
	});
	YAHOO.util.Event
			.onDOMReady( function() {
				if ($('iedialogcontainer') != null) {
					var iedialog = new YAHOO.widget.SimpleDialog(
							"iedialog",
							{
								width : "300px",
								fixedcenter : true,
								visible : false,
								draggable : false,
								close : true,
								text : "This page works better with Internet Explorer 7, Firefox or Safari.",
								constraintoviewport : true
							});
					iedialog.setHeader("Note");
					iedialog.render("iedialogcontainer");
					iedialog.show();
				}
			});
	
}

/*
 * Global functions
 */
function log(msg,level) {
	if (SHOW_LOG) YAHOO.log(msg,level,"ELV");
}

function clearLog() {
	if (SHOW_LOG) YAHOO.widget.Logger.reset();
}

