
/*
 *	Tile-based, prefetching javascript viewer for images served through the
 *	Internet Imaging Protocol (IIP).
 *
 *	Based on the javascript IIP client found at
 *	http://iipimage.sourceforge.net/ by Ruven Pillay
 *
 *	Tom Perry, MRC HGU 2006
 */


/*
 *	CONFIGURE THESE VARIABLES!
 */

// Web root
var webRoot = '/export/system0/MAWWW/Public/html/mrciip/'	// Trailing slash required!

// Location of stack metadata
var projectsPath = '/mrciip/projects/'    // Trailing slash required!

// Location of IIP server
var iipsrv = '/fcgi-bin/iipsrv.fcgi';

// JPEG compression level for images
var qlt = '50';

// Image format that IIP server should send images in.
// Currently, only 'jpeg' is supported.
var cvt = 'jpeg';

// Number of layers of tiles around the border to prefetch - increases lag as these tiles are downloaded, but may allow smoother movement.  Current maximum is 2.
var prefetchingLevel = 1;

// Number of permitted zoom levels beyond the native resolution of the image.  Images in such levels are resized using CSS, and will appear pixellated.  To disable digital zooming entirely, set this value to 0.
var digitalZoomLevels = 0;

// Time, in ms, after changing section or zoom level in which no further clicks are allowed.  May prevent the IIP server from crashing due to overenthusiastic loading of images.
var lockTime = 50;

/*
 *	END OF CONFIGURABLE VARIABLES
 */

var ts;

var filename;
var fif;
var stackDepth;
var resolution;

var zsimgsrc;

// Number of pyramidal tiff layers accessible by IIP
var numres;

// Are any events happening?
var beingDragged = false;
var beingClicked = false;
var beingPressed = false;

// Full image width and height, in units of pixels and tiles - px version is defined in mrciip.php
var fwpx;
var fhpx;
var fwt;
var fht;

// Width and height at current resolution index - can be calulated
var iwpx;
var ihpx;
var iwt;
var iht;

// Coordinates of top left tile, i.e. row and column
var ixt;
var iyt;

// Dimensions of nav region in bottom left
var pwpx;
var phpx;

// Current location of top left pixel of displayed image
var ixpx;
var iypx;

// Width and height of image region on page
var dwpx;
var dhpx; // in px
var dwt;
var dht;  // in tiles

// Width and height of navigation image
var nwpx;
var nhpx;

// Width and height of viewing region on navigation image
var rwpx;
var rhpx;

// Current location of top left pixel of viewing region
var rxpx;
var rypx;

// Maximum dimensions of Z-select image
var swpx_max;
var shpx_max;

// Maximum dimensions of navigator image
var pwpx_max;
var phpx_max;

// Dimensions of actual Z-select image, retrieved from metadata
var siwpx;
var sihpx;

// Current (calculated) dimensions of Z-select image
var swpx;
var shpx;

// Current position of Z-select image
var sxpx;
var sypx;

// Position of Z-selector bar
var zppx;

// Z-select borders
var zsdblt;
var zsdbrb;

var zsliceOrientation;
var rightwpx;
var iconswpx;
var iconsxpx;

// Values of ixpx and iypx that correspond to the limits of the current ixt and iyt values
var ixt_ll;
var ixt_ul;
var iyt_ll;
var iyt_ul;

// Objects containing stack and image metadata
var imageIndex;
var imageAttributes;

// Current image ID
var imageId;

// Useful variables for CSS repositioning
var rightxpx;
var leftwpx;

// IE browser?
var isIE;

// Variables used in dragging functions
var startX;
var startY;
var startZ;
var zoneStartX;
var zoneStartY;
var cursorX;
var cursorY;
var currentX;
var currentY;
var topelement;
var obj;

// Variables used in logging functions
var logId = 0;
var logLength = 12;

var lock = false;

// XMLHttpRequest object
var req;

var preloadTiles = new Array();
var tiles = new Array();


/**
 * START OF 'LIBRARY' FUNCTIONS
 */

function preloadTile(tileX,tileY)
{
	/*
		 if (tileX >= 0 && tileX < iwt && tileY >=0 && tileY < iht) // Sanity check
		 {
		 var tileId = 'x' + tileX + 'y' + tileY;
		 var IIPtileId = tileY * iwt + tileX;
		 preloadTiles[IIPtileId] = new Image(ts,th);
		 preloadTiles[IIPtileId].src = 'http://' + fqdn + '/' + iipsrv + fif + '&QLT=' + qlt + '&JTL=' + resolution + ',' + IIPtileId;
		 }
	 */
}

/**
 * Creates an <img> element, sets its src according to the arguments given,
 * sets its absolute position, and other useful stuff.  See getTiles() for
 * a function that creates all starting tiles appropriately
 */
function getTile(tileR,tileX,tileY)
{
	if (tileX >= 0 && tileX < iwt && tileY >=0 && tileY < iht) // Sanity check
	{
		var tileId = 'r' + tileR + 'x' + tileX + 'y' + tileY;
		var IIPtileId = tileY * iwt + tileX;
		/*
			 var tile = document.createElement('img');
			 tile.setAttribute('id',tileId);
			 tile.setAttribute('alt',tileId);
			 if (isIE)
			 tile.setAttribute('className','tile');
			 else
			 tile.setAttribute('class','tile');
			 tile.style.position = 'absolute';
			 tile.style.top = (tileY * th) + 'px';
			 tile.style.left = (tileX * ts) + 'px';
			 var tileSrc = 'http://' + fqdn + '/' + iipsrv + fif + '&QLT=' + qlt + '&JTL=' + resolution + ',' + IIPtileId;
			 tile.src = tileSrc;
			 tile.setAttribute("onload","setLoaded("+tileId+");");
			 setTimeout("checkLoaded("+tileId+");",1000);
			 var tileFrame = document.getElementById('tileframe');
			 tileFrame.appendChild(tile);
		 */
		tiles[IIPtileId] = document.createElement('img');
		tiles[IIPtileId].setAttribute('id',tileId);
		tiles[IIPtileId].setAttribute('alt',tileId);
		tiles[IIPtileId].setAttribute('useMap','#components_'+tileId);
		tiles[IIPtileId].setAttribute('border','0');
		if (isIE)
			tiles[IIPtileId].setAttribute('className','tile');
		else
			tiles[IIPtileId].setAttribute('class','tile');
		tiles[IIPtileId].style.position = 'absolute';
		tiles[IIPtileId].style.left = (tileX * ts) + 'px';
		tiles[IIPtileId].style.top = (tileY * ts) + 'px';
		if (resolution > numres - 1)
		{
			if (tileX == iwt - 1 && iwpx % ts != 0)
				tiles[IIPtileId].style.width = (iwpx % ts) + 'px';
			else
				tiles[IIPtileId].style.width = ts + 'px';
			if (tileY == iht - 1 && ihpx % ts != 0)
				tiles[IIPtileId].style.height = (ihpx % ts) + 'px';
			else
				tiles[IIPtileId].style.height = ts + 'px';
		}
		var IIPres;
		if (resolution < numres) IIPres = resolution;
		else IIPres = numres - 1;
		var tileSrc = iipsrv + fif + '&QLT=' + qlt + '&JTL=' + IIPres + ',' + IIPtileId;
		tiles[IIPtileId].src = tileSrc;
		//tiles[IIPtileId].setAttribute("onload","setLoaded("+tileId+");");
		//setTimeout("checkLoaded("+tileId+");",1000);
		var tileFrame = document.getElementById('tileframe');
		tileFrame.appendChild(tiles[IIPtileId]);
	}
}

function getMapForTile(tileR,tileX,tileY) {
	//$('bigmeta').appendText('in start of method ;');
    var tileId = 'r' + tileR + 'x' + tileX + 'y' + tileY;
    var IIPtileId = tileY * iwt + tileX;
    //$('bigmeta').appendText('about to get overlaydata ;');
    //var mapAttributes = getJSOFile(projectsPath + project + '/' + stackId + '/' + 'metadata/' + filename +'_tilemetadata/resolutionLevel'+(resolution+1)+'/tile_'+IIPtileId+'.js');
    
    //var overlayData;
    
    new Ajax(projectsPath + project + '/' + stackId + '/overlaydata/' + filename.substring(0, filename.indexOf('.jpg.256.pyr.tif')) +'_Bnd/tile_'+resolution+','+IIPtileId+'.txt',
    {
    	method: 'get',
    	
    	onComplete: function(transport){
    		var object = eval("("+transport+")");
    		createMapForTile(object, tileId);
    	},
    	oFailure: function(){ alert('Unable to get data from server!'); }
    }).request();
    
    /*$('bigmeta').appendText('checkoverlay ;');
    if(overlayData){
    	
    	var polyLines = overlayData.polylines;
    	$('bigmeta').appendText('check polylines ;');
    	if(polyLines) {
    
    		var mapElement= document.createElement('map');
    		mapElement.setAttribute('id','components_'+tileId);
        	mapElement.setAttribute('name','components_'+tileId);
        	var tileFrame = document.getElementById('tileframe');
            tileFrame.appendChild(mapElement);
        	
        	var domains = overlayData.domains;
        	$('bigmeta').appendText('got domains ;');
    
    		for(var i=0;i<polyLines.length;i++){
    			
    			var areaElement = new Element('area',
    				{
    					'shape':'poly',
    					'coords':polyLines[i].coords,
    					'id':polyLines[i].index+'_'+i+'_'+tileId,
    					'name':'comp_'+i+'_'+tileId
    				}
    			);
    			
    			areaElement.setStyle('cursor','pointer');
    
    			//areaElement.setAttribute('shape','poly');
    			//areaElement.setAttribute('coords', polyLines[i].coords);
    			//areaElement.setAttribute('id', polyLines[i].index+'_'+i+'_'+tileId);
    			//areaElement.setAttribute('name','comp_'+i+'_'+tileId);
    			//areaElement.style.cursor = 'pointer';
    			
    			
    			
    			areaElement.addEvent('click', function(event){
    				//var indexValue = event.target.id.substring(0, event.target.id.indexOf('_'));
    				//var domainName = domains[indexValue-1].name;
    				//var anatomyTerms = getJSOFile('javascript/domainMapping.js');
    				//alert(domainName);
    				alert(event);
    				//alert(anatomyTerms[domainName].id);
    				//openNodesMatchingComponentId(anatomyTerms[domainName].id);
    				//showComponentSummary(anatomyTerms[domainName].id);
    				
    				
    				
   				
    			});
    			
    			mapElement.appendChild(areaElement);
        			
    		}
    $('bigmeta').appendText('made it thru for loop;    ');
    		
    		
    	}
    	$('bigmeta').appendText('end of polylines ;');
    }
    $('bigmeta').appendText('end of overlay and method ;');*/
}

function createMapForTile(overlayData, tileId){
	
	if(overlayData){
    	
    	var polyLines = overlayData.polylines;
    	//$('bigmeta').appendText('check polylines ;');
    	if(polyLines) {
    		
    		var mapElement= document.createElement('map');
    		mapElement.setAttribute('id','components_'+tileId);
        	mapElement.setAttribute('name','components_'+tileId);
        	var tileFrame = document.getElementById('tileframe');
        	tileFrame.appendChild(mapElement);
        	
        	var anatomyTerms = getJSOFile('../scripts/domainMapping.js');
        	
        	var domains = overlayData.domains;
        	//$('bigmeta').appendText('got domains ;');
    
    		for(var i=0;i<polyLines.length;i++){
    			
    			var areaElement = new Element('area',
    				{
    					'shape':'poly',
    					'coords':polyLines[i].coords,
    					'id':polyLines[i].index+'_'+i+'_'+tileId,
    					'name':'comp_'+i+'_'+tileId
    				}
    			);
    			
    			areaElement.setStyle('cursor','pointer');
    			
    			areaElement.addEvents({
    				'click': function(event){
    					
    					var tgt;
    					
    					if(isIE){
    						tgt = event.srcElement;
    					}
    					else {
    						tgt = event.target
    					}
    					
    					var indexValue = tgt.id.substring(0, tgt.id.indexOf('_'));
    					var domainName = domains[indexValue-1].name;
    					
    					showComponentSummary(anatomyTerms[domainName].id);
    					openNodesMatchingComponentId(anatomyTerms[domainName].id);
    					
    				},
    				'mouseover': function(event) {
    					var tgt;
    					
    					if(isIE){
    						tgt = event.srcElement;
    					}
    					else {
    						tgt = event.target
    					}
    					
    					var indexValue = tgt.id.substring(0, tgt.id.indexOf('_'));
    					var domainName = domains[indexValue-1].name;
    					$('termName').setHTML(anatomyTerms[domainName].name);
    				},
    				'mouseout': function(){
    					$('termName').setHTML('Click on a painted anatomical structure to highlight it in the tree and retrieve expression data');
    				}
    			});
    			mapElement.appendChild(areaElement);		
    		}	
    	}	
    }
}

function showComponentSummary(componentId){
	
	var viewerSize = $("viewer").getSize();
	var cover = new Element('div',
	{
		'styles': {
			'width':viewerSize.size.x+'px',
       	  	'height':viewerSize.size.y+'px',
       	  	'z-index': '2001',
       	  	'display': 'block',
       	  	'position':'absolute'
       	},
       	'id': 'cover'
    });
    cover.injectInside($("viewer"));
	
	$("wait").setStyle('display', 'block');
	$("wait").setStyle('z-index', '2000');
	$("wait").setStyle('left', Math.ceil(viewerSize.size.x/2)-20);
	$("wait").setStyle('top', Math.ceil(viewerSize.size.y/2)-20);
	$("wait").setStyle('position', 'absolute');
	
	new Ajax('component_summary.html?id='+componentId,
    {
    	method: 'get',
    	update: $('compInfContainer'),
    	onComplete: function(){
    		
    		$("wait").setStyle('display', 'none');
    		$('cover').remove();
    		var containerDisplay = $('compInfContainer').getStyle('display');
    		if(containerDisplay == 'none') {
    			var topEl = $('tileview');
				var topX = topEl.getLeft();
				var topY = topEl.getTop();
    			$("compInfContainer").setStyle('left', topX+5);
				$("compInfContainer").setStyle('top', topY+5);
				$("compInfContainer").setStyle('display', 'block');
				$("compInfContainer").setStyle('z-index', '1000');
				$("compInfContainer").setStyle('visibility', 'visible');
				
    		}
    		$('compInfClose').addEvent('click', function() {
    			$("compInfContainer").setStyle('display', 'none');
			});
            
            //$('compInfContainer').style.backgroundColor = "white";
    	}.bind(this),
    	onFailure: function(){ alert('Unable to get data from server!'); }
    }).request();
	
	
	/*var topSize = topEl.getSize();
	
	var el = $("compInfContainer");
	var elSize = el.getSize();
	
	el.setStyle('display', 'block');
	el.setStyle('z-index', '1000');
	
	if(e.pageX || e.pageY) {
            
    	if(e.pageX > ((topX+topSize.size.x)-elSize.size.x)) {
    		
    		el.setStyle('left', e.pageX-elSize.size.x);
    	}
    	else {
    		el.setStyle('left',e.pageX);
    	}
    	if(e.pageY > ((topY+topSize.size.y)-elSize.size.y)){
    		el.setStyle('top', e.pageY-elSize.size.y);
    	}
    	else {
    		el.setStyle('top', e.pageY);
    	}
    }
    else if (e.clientX || e.clientY) {
            
    	if(e.clientX > ((topX+topSize.size.x)-elSize.size.x))
        	el.setStyle('left',e.clientX + document.documentElement.scrollLeft + document.body.scrollLeft - elSize.size.x)
        else
        	el.setStyle('left',e.clientX + document.documentElement.scrollLeft + document.body.scrollLeft)
        if(e.clientY > ((topY+topSize.size.y)-elSize.size.y))
        	el.setStyle('top', e.clientY + document.documentElement.scrollTop + document.body.scrollTop-elSize.size.y)
        else
        	el.setStyle('top', e.clientY + document.documentElement.scrollTop + document.body.scrollTop);
    }
	
	el.setStyle('visibility', 'visible');*/
	
	/*new Ajax('component_summary.html?id='+componentId,
    {
    	method: 'get',
    	update: $('compInfBody'),
    	onComplete: function(){
    		var containerDisplay = $('compInfContainer').getStyle('display');
    		if(containerDisplay == 'none') {
    			$('compInfContainer').setStyle('left', $('centre').getLeft() + 5 +'px');
                $('compInfContainer').setStyle('top', $('centre').getTop() + 5 +'px');
    		}
            $('compInfContainer').style.display = 'block';
            $('compInfContainer').style.zIndex = '1000';
            //$('compInfContainer').style.backgroundColor = "white";
    	},
    	onFailure: function(){ alert('Unable to get data from server!'); }
    }).request();*/
}

function setLoaded(tileId)
{
	var tile = document.getElementById(tileId);
	tile.setAttribute("class","loaded");
}

function checkLoaded(tileId)
{
	/*
		 var tile = document.getElementById(tileId);
		 if (tile.className != 'loaded')
		 getTile(tileX,tileY);
	 */
}

/**
 * Deletes an <img> element.  See removeTiles() for a tile frame clearing
 * function.
 */
function removeTile(tileR,tileX,tileY)
{
	var tileId = 'r' + tileR + 'x' + tileX + 'y' + tileY;
	//log("Removing",tileId);
	var tile = document.getElementById(tileId);
	if (tile)
	{
		var tileFrame = document.getElementById('tileframe');
		tileFrame.removeChild(tile);
	}
	
	var mapTile = $('components_'+tileId);//document.getElementById('components_'+tileId);
	if(mapTile) {
		mapTile.empty();
        tileFrame.removeChild(mapTile);
    }
}

/**
 * Gets the initial images for the view defined by various global variables.
 *
 * There's an additional section that will only display the tile frame
 * after the last tile (bottom right) has loaded, but this is largely
 * unneccesary.
 */
function getTiles(res)
{
	var tempw, temph;
	if (iwt <= dwt) tempw = iwt-1; else tempw = dwt;
	if (iht <= dht) temph = iht-1; else temph = dht;
	for (var y=iyt; y<=iyt+temph; y++)
		for (var x=ixt; x<=ixt+tempw; x++)
		{
			//setTimeout("preloadTile("+x+","+y+");",foo*500);
			//setTimeout("getTile("+x+","+y+");",foo*500);
			//preloadTile(x,y);
			getTile(res,x,y);
		}

	// Don't display until all tiles have loaded
	//var lastTileId = 'r' + (i-1) + 'c' + (j-1);
	//var lastTile = document.getElementById(lastTileId);
	// FIXME - this doesn't work in Internet Explorer
	//lastTile.setAttribute('onload',"javascript: showTiles('"+lastTileId+"');");
	//showTiles(lastTileId);
	//showTiles();
	//getMapsForTiles(res);

	// Prefetches some additional tiles around the outer edge of the grid
	if (prefetchingLevel >= 1)
	{
		for (var i=iyt; i<=iyt+dht; i++)
			getTile(res,ixt-1,i);
		for (var i=iyt; i<=iyt+dht+1; i++)
			getTile(res,ixt+dwt+1,i);
		for (var i=ixt; i<=ixt+dwt; i++)
			getTile(res,i,iyt-1);
		for (var i=ixt; i<=ixt+dwt+1; i++)
			getTile(res,i,iyt+dht+1);

		getTile(res,ixt-1,iyt-1);
		getTile(res,ixt-1,iyt+dht+1);
		getTile(res,ixt+dwt+1,iyt-1);
		getTile(res,ixt+dwt+1,iyt+dht+1);
	}

	if (prefetchingLevel >= 2)
	{
		for (var i=iyt-1; i<=(iyt+dht+1); i++)
			getTile(res,ixt-2,i);
		for (var i=iyt-1; i<=(iyt+dht+1); i++)
			getTile(res,ixt+dwt+2,i);
		for (var i=ixt-1; i<=ixt+dwt+1; i++)
			getTile(res,i,iyt-2);
		for (var i=ixt-1; i<=ixt+dwt+1; i++)
			getTile(res,i,iyt+dht+2);

		getTile(res,ixt-2,iyt-2);
		getTile(res,ixt-2,iyt+dht+2);
		getTile(res,ixt+dwt+2,iyt-2);
		getTile(res,ixt+dwt+2,iyt+dht+2);
	}
}

function getMapsForTiles(res) {
    var tempw, temph;
	if (iwt <= dwt) tempw = iwt-1; else tempw = dwt;
	if (iht <= dht) temph = iht-1; else temph = dht;
	//$('bigmeta').appendText('value of ixt+tempw: '+(ixt+tempw));
	for (var y=iyt; y<=iyt+temph; y++) {
		//$('bigmeta').appendText('y is '+y);
		for (var x=ixt; x<=ixt+tempw; x++)
		{
		//	$('bigmeta').appendText('method called; ');
			//$('bigmeta').appendText('x is '+x+';');
			getMapForTile(res,x,y);
			//$('bigmeta').appendText('end of x');
		}
	//	$('bigmeta').appendText('end of y');
	}
}

/**
 * Currently doesn't do anything much.  See the comment on getTiles() for
 * an explanation of this function's existence.
 */
function showTiles(lastTileId)
{
	//var lastTile = document.getElementById(lastTileId);
	//lastTile.removeAttribute('onload');
	//for (var i=ixt; i<=ixt+dht; i++)
	//	for (var j=iyt; j<=iyt+dwt; j++)
	//		showTile(i,j);
	//hideLoadingBox();
	//for (var i=0; i<=49; i++)
	//{
	//	setTimeout("changeOpacity(" + (i*2) + ",'" + 'tileframe' + "')",i*20);
	//}
	// Can't use 100% opacity due to a 'blink' bug in Mozilla, so we use 99% instead
	//setTimeout("changeOpacity(" + 99 + ",'" + 'tileframe' + "')",200);
	var tileFrame = document.getElementById('tileframe');
	tileFrame.style.display = 'block';
}

/**
 * This is currently unused.  It formed part of an idea I had that would
 * fade the tile frame in in an aesthetically pleasing manner, but is
 * poorly supported in too many browsers.
 * 
 * Works fine in Firefox, not so well in Mozilla, IE...
 */
function changeOpacity(opacity, id)
{
	var object = document.getElementById(id);
	object.style.opacity = (opacity/100);
	object.style.MozOpacity = (opacity/100);
	object.style.KhtmlOpacity = (opacity/100);
	object.style.filter = "alpha(opacity=" + opacity + ")";
}

/**
 * Clears the tile frame
 *
 * I've had a lot of trouble with this function.  Sometimes it doesn't remove
 * all the tiles, so this method just attempts to nuke everything.
 */
function removeTiles(res)
{
	//changeOpacity(0,'tileframe');
	var tileFrame = document.getElementById('tileframe');
	tileFrame.style.display = 'none';
	// Just remove everything
	/*
		 for (var i=0; i<tileFrame.childNodes.length; i++)
		 {
		 log("Removing",tileFrame.childNodes[i].id);
		 tileFrame.removeChild(tileFrame.childNodes[i]);
		 }
	 */
	for (var r=res-1; r<=res+1; r++)
		for (var i=0; i<iwt*2; i++)
			for (var j=0; j<iht*2; j++)
				removeTile(r,i,j);
}

/**
 * Moves the tile frame by adjusting its CSS absolute positioning.
 */
function moveTileFrame()
{
	var tileFrame = document.getElementById('tileframe');
	tileFrame.style.left = (-ixpx) + 'px';
	tileFrame.style.top = (-iypx) + 'px';
}

/**
 * Moves the region indicator on the navigation image by adjusting its
 * CSS absolute positioning.
 */
function moveZone()
{
	var zone = document.getElementById('zone');
	zone.style.left = rxpx + 'px';
	zone.style.top = rypx + 'px';
}

/**
 * Displays the region indicator.
 */
function showZone()
{
	var zone = document.getElementById('zone');
	zone.style.display = 'block';
}

/**
 * Hides the region indicator.
 */
function hideZone()
{
	var zone = document.getElementById('zone');
	zone.style.display = 'none';
}

/**
 * Moves the image selector indicator by adjusting its CSS absolute
 * positioning.
 */
function moveZSlice()
{
	//var zSlice = document.getElementById('zslice');
	//zSlice.style.top = zppx + 'px';
}

/**
 * Loads an appropriate navigation image.
 */
function getNav()
{
	var navImg = document.getElementById('navimg');
	var navImgSrc = iipsrv + fif + '&QLT=' + qlt + '&RGN=0,0,1,1' + '&HEI=' + (nhpx*2) + '&CVT=' + cvt;
	navImg.setAttribute('src',navImgSrc);
}

/**
 * Loads the selector image (zsel.jpg)
 */
function getZSel()
{
	var src = projectsPath + project + '/'  + stackId + '/' + zsimgsrc;
	var zSelectImg = document.getElementById('zselectimg');
	zSelectImg.setAttribute('src',src);
}


/**
 * END OF 'LIBRARY' FUNCTIONS
 */

/**
 * START OF ZOOM FUNCTIONS
 */

/**
 * Zooms in
 */
function zoomIn()
{
	if (lock == false)
	{
		if (resolution < numres + digitalZoomLevels - 1)
		{
			removeTiles(resolution);
			resolution++;
			lock = true;
			setTimeout("removeLock()",lockTime);
			ixpx = ixpx * 2 + Math.floor(dwpx / 2);
			iypx = iypx * 2 + Math.floor(dhpx / 2);
			if (resolution > numres - 1)
				ts *= 2;
			getView();
		}
	}
}

/**
 * Zooms out
 */
function zoomOut()
{
	if (lock == false)
	{
		if (resolution > 0)// && (iwpx > dwpx || ihpx > dhpx) )
		{
			if (resolution >= numres)
				ts /= 2;
			removeTiles(resolution);
			resolution--;
			lock = true;
			// Nice exponential increase in lock time with resolution
			setTimeout("removeLock()",lockTime*Math.pow(1.3,resolution));
			iypx = Math.ceil(iypx/2 - dhpx/4);
			ixpx = Math.ceil(ixpx/2 - dwpx/4);
			getView();
		}
	}
}

/**
 * END OF ZOOM FUNCTIONS
 */

/**
 * START OF DRAG FUNCTIONS
 */

/**
 * Double click handling function
 */
function doubleClick(e)
{
	var button;
	if (isIE==true)
	{
		obj = event.srcElement;
		topelement = "BODY";
		button = event.button;
	}
	else
	{
		obj = e.target;
		topelement = "HTML";
		button = e.which;
	}

	while (obj.tagName != topelement && obj.className != 'tileframe')
		obj = (isIE == true) ? obj.parentElement : obj.parentNode;

	if (obj.className == 'tileframe')
	{
		if (button > 1)
			zoomOut();
		else
			zoomIn();
	}
}

function startDrag(e)
{
	if (isIE==true)
	{
		obj = event.srcElement;
		cursorX = event.clientX;
		cursorY = event.clientY;
		topelement = "BODY";
		if (event.button > 1)
		{
			event.returnValue = false;
			return false;
		}
	}
	else
	{
		obj = e.target;
		cursorX = e.clientX;
		cursorY = e.clientY;
		topelement = "HTML";
		if (e.which > 1)
		{
			e.preventDefault();
			return false;
		}
	}

	while (obj.tagName != topelement && obj.className != 'tileframe' && obj.className != 'zone' && obj.className != 'zslice')
		obj = (isIE == true) ? obj.parentElement : obj.parentNode;

	if (obj.className == 'tileframe')
	{
		beingDragged = true;
		startX = ixpx;
		startY = iypx;
		zoneStartX = rxpx;
		zoneStartY = rypx;
		document.onmousemove = dragTileFrame;
		document.onmouseup = stopDrag;
		return false;
	}
	else if (obj.className == 'zone')
	{
		beingDragged = true;
		startX = ixpx;
		startY = iypx;
		zoneStartX = rxpx;
		zoneStartY = rypx;
		document.onmousemove = dragZone;
		document.onmouseup = stopDrag;
		return false;
	}
	else if (obj.className == 'zslice')
	{
		beingDragged = true;
		hideZone();
		removeTiles(resolution);
		(zsliceOrientation == 'horizontal') ? startY = zppx : startX = zppx;
		document.onmousemove = dragZSlice;
		document.onmouseup = stopDragZSlice;
		return false;
	}
	/* FIXME - this needs finishing.  Need a way to get initial position.
		 else if (e.target.className == 'navimg')
		 {
		 ixpx = ixpx - e.clientX * (iwpx / nwpx);
		 iypx = iypx - e.clientY * (ihpx / nhpx);
		 rxpx = rxpx - e.clientX;
		 rypx = rypx - e.clientY;

		 showCommonVars();
		 moveTileFrame();
		 moveZone();
		 }
	 */
}

function dragTileFrame(e)
{
	if (beingDragged)
	{
		if (isIE == true)
		{
			currentX = event.clientX;
			currentY = event.clientY;
		}
		else
		{
			currentX = e.clientX;
			currentY = e.clientY;
		}
		ixpx = startX + (cursorX - currentX);
		iypx = startY + (cursorY - currentY);
		rxpx = zoneStartX + (cursorX - currentX) / (iwpx / nwpx);
		rypx = zoneStartY + (cursorY - currentY) / (ihpx / nhpx);

		fixVars();

		if (iypx > iyt_ul)
			dragDown();
		if (iypx < iyt_ll)
			dragUp();
		if (ixpx > ixt_ul)
			dragRight();
		if (ixpx < ixt_ll)
			dragLeft();

		moveTileFrame();
		moveZone();
	}
	return false;
}

function dragZone(e)
{
	if (beingDragged)
	{
		if (isIE == true)
		{
			currentX = event.clientX;
			currentY = event.clientY;
		}
		else
		{
			currentX = e.clientX;
			currentY = e.clientY;
		}
		ixpx = startX - (cursorX - currentX) * (iwpx / nwpx);
		iypx = startY - (cursorY - currentY) * (ihpx / nhpx);
		rxpx = zoneStartX - (cursorX - currentX);
		rypx = zoneStartY - (cursorY - currentY);

		fixVars();

		if (iypx > iyt_ul)
			dragDown();
		if (iypx < iyt_ll)
			dragUp();
		if (ixpx > ixt_ul)
			dragRight();
		if (ixpx < ixt_ll)
			dragLeft();

		moveTileFrame();
		moveZone();
	}
	return false;
}

function dragZSlice(e)
{
	if (beingDragged)
	{
		if (zsliceOrientation == 'horizontal')
		{
			if (isIE == true)
				currentY = event.clientY;
			else
				currentY = e.clientY;
			zppx = startY - (cursorY - currentY);
			imageId = Math.floor( (zppx-zsdblt) * stackDepth / (shpx-zsdblt-zsdbrb) );
		}
		else
		{
			if (isIE == true)
				currentX = event.clientX;
			else
				currentX = e.clientX;
			zppx = startX - (cursorX - currentX);
			imageId = Math.floor( (zppx-zsdblt) * stackDepth / (swpx-zsdblt-zsdbrb) );
		}
		fixVars();
		if (lock == false)
		{
			lock = true;
			setTimeout("removeLock()",lockTime);
			getImage();
		}
		var imageLevel = imageId + ' of ' + stackDepth;
		showMeta("Current section",imageLevel);
		applyCSS();
	}
	return false;
}

/*
 * These functions get new tiles and remove old ones as the tile frame is
 * moved around.
 */
function dragDown()
{
	for (var i=ixt-prefetchingLevel; i<=ixt+dwt+prefetchingLevel; i++)
		getTile(resolution,i,iyt+dht+prefetchingLevel);
	for (var i=ixt-prefetchingLevel; i<=ixt+dwt+prefetchingLevel; i++)
		removeTile(resolution,i,iyt-prefetchingLevel);
	iyt_ll += ts;
	iyt_ul += ts;
	iyt++;
	if (iypx > iyt_ul)
		dragDown();
}

function dragUp()
{
	for (var i=ixt-prefetchingLevel; i<=ixt+dwt+prefetchingLevel; i++)
		getTile(resolution,i,iyt-prefetchingLevel-1);
	for (var i=ixt-prefetchingLevel; i<=ixt+dwt+prefetchingLevel; i++)
		removeTile(resolution,i,iyt+dht+prefetchingLevel);
	iyt_ll -= ts;
	iyt_ul -= ts;
	iyt--;
	if (iypx < iyt_ll)
		dragUp();
}

function dragRight()
{
	for (var i=iyt-prefetchingLevel; i<=iyt+dht+prefetchingLevel; i++)
		getTile(resolution,ixt+dwt+prefetchingLevel,i);
	for (var i=iyt-prefetchingLevel; i<=iyt+dht+prefetchingLevel; i++)
		removeTile(resolution,ixt-prefetchingLevel,i);
	ixt_ll += ts;
	ixt_ul += ts;
	ixt++;
	if (ixpx > ixt_ul)
		dragRight();
}

function dragLeft()
{
	for (var i=iyt-prefetchingLevel; i<=iyt+dht+prefetchingLevel; i++)
		getTile(resolution,ixt-prefetchingLevel-1,i);
	for (var i=iyt-prefetchingLevel; i<=iyt+dht+prefetchingLevel; i++)
		removeTile(resolution,ixt+dwt+prefetchingLevel,i);
	ixt_ll -= ts;
	ixt_ul -= ts;
	ixt--;
	if (ixpx < ixt_ll)
		dragLeft();
}

function stopDrag()
{
	beingDragged = false;
	//document.onmousemove = null;
	document.onmouseup = null;
}

function stopDragZSlice()
{
	beingDragged = false;
	document.onmousemove = null;
	document.onmouseup = null;
	getImage();
	getView();
}

/* END OF ZOOM FUNCTIONS
 */


/* START OF CLICK-SCROLL FUNCTIONS
 */

var speed;
var clickDirection;

function clickUp(e)
{
	speed = 0;
	document.onmouseup = stopClick;
	beingClicked = true; 
	clickDirection = 'up';
	clickLoop(clickDirection);
} 

function clickDown(e)
{
	speed = 0;
	document.onmouseup = stopClick;
	beingClicked = true; 
	clickDirection = 'down';
	clickLoop(clickDirection);
}

function clickLeft(e)
{
	speed = 0;
	document.onmouseup = stopClick;
	beingClicked = true;
	clickDirection = 'left';
	clickLoop(clickDirection);
}

function clickRight(e)
{
	speed = 0;
	document.onmouseup = stopClick;
	beingClicked = true;
	clickDirection = 'right';
	clickLoop(clickDirection);
}

function clickUpLeft(e)
{
	speed = 0;
	document.onmouseup = stopClick;
	beingClicked = true;
	clickDirection = 'upleft';
	clickLoop(clickDirection);
}

function clickUpRight(e)
{
	speed = 0;
	document.onmouseup = stopClick;
	beingClicked = true;
	clickDirection = 'upright';
	clickLoop(clickDirection);
}

function clickDownLeft(e)
{
	speed = 0;
	document.onmouseup = stopClick;
	beingClicked = true;
	clickDirection = 'downleft';
	clickLoop(clickDirection);
}

function clickDownRight(e)
{
	speed = 0;
	document.onmouseup = stopClick;
	beingClicked = true;
	clickDirection = 'downright';
	clickLoop(clickDirection);
}

function clickLoop(direction)
{
	if (direction == clickDirection)
	{
		if (beingClicked == true)
		{
			if (speed<50) speed+=2;
			setTimeout("clickMove("+speed+",'"+direction+"');",10);
		}
		else if (speed > 0)
		{
			speed-=4;
			if (speed < 0) speed = 0;
			setTimeout("clickMove("+speed+",'"+direction+"');",10);
		}
	}
}

function clickMove(px,direction)
{
	if (direction == 'up')
	{
		iypx -= px;
		rypx -= px * (nhpx / ihpx);
	}
	if (direction == 'down')
	{
		iypx += px;
		rypx += px * (nhpx / ihpx);
	}
	if (direction == 'left')
	{
		ixpx -= px;
		rxpx -= px * (nwpx / iwpx);
	}
	if (direction == 'right')
	{
		ixpx += px;
		rxpx += px * (nwpx / iwpx);
	}
	if (direction == 'upleft')
	{
		ixpx -= px/Math.sqrt(2);
		iypx -= px/Math.sqrt(2);
		rxpx -= px/Math.sqrt(2) * (nwpx / iwpx);
		rypx -= px/Math.sqrt(2) * (nhpx / ihpx);
	}
	if (direction == 'upright')
	{
		ixpx += px/Math.sqrt(2);
		iypx -= px/Math.sqrt(2);
		rxpx += px/Math.sqrt(2) * (nwpx / iwpx);
		rypx -= px/Math.sqrt(2) * (nhpx / ihpx);
	}
	if (direction == 'downleft')
	{
		ixpx -= px/Math.sqrt(2);
		iypx += px/Math.sqrt(2);
		rxpx -= px/Math.sqrt(2) * (nwpx / iwpx);
		rypx += px/Math.sqrt(2) * (nhpx / ihpx);
	}
	if (direction == 'downright')
	{
		ixpx += px/Math.sqrt(2);
		iypx += px/Math.sqrt(2);
		rxpx += px/Math.sqrt(2) * (nwpx / iwpx);
		rypx += px/Math.sqrt(2) * (nhpx / ihpx);
	}
	fixVars();
	if (iypx < iyt_ll)
		dragUp();
	if (iypx > iyt_ul)
		dragDown();
	if (ixpx < ixt_ll)
		dragLeft();
	if (ixpx > ixt_ul)
		dragRight();
	moveTileFrame();
	moveZone();
	clickLoop(clickDirection);
}

function stopClick()
{
	beingClicked = false;
	beingPressed = false;
}

/* END OF CLICK-SCROLL FUNCTIONS
 */


/* START OF RELOADING FUNCTIONS
 */

/**
 * Uses an XMLHttpRequest (of AJAX fame) to retrieve metadata
 */
function getJSOFile(url)
{
	var date = new Date();
	url += '?';
	// Append a timestamp to prevent caching
	url += date.getTime();
	if (window.XMLHttpRequest) // For Mozilla, Safari, everything really
	{
		try { req = new XMLHttpRequest(); }
		catch(e) { req = false; }
	}
	else if (window.ActiveXObject) // For MSIE
	{
		try {	req = new ActiveXObject("Msxml2.XMLHTTP"); }
		catch(e)
		{
			try { req = new ActiveXObject("Microsoft.XMLHTTP"); }
			catch(e) { req = false; }
		}
	}
	if (req)
	{
		req.open("GET",url,false);
		req.send(null);
		if (req.status == 200 || req.status == 304)
		{
			// Parse JSON metadata into object 'object'
			var object = eval("("+req.responseText+")");
		}
		else alert("Error: could not load "+url+"\nRequest status: "+req.status);
		return (object);
	}
	else return false;
}

/**
 * Similar idea, but for text files holding metadata to go in the box
 * at the right-hand side
 */
function getMetadata()
{
	var url = projectsPath + project + '/' + stackId + '/' + stackId + '.txt';
	if (window.XMLHttpRequest)
	{
		try { req = new XMLHttpRequest(); }
		catch(e) { req = false; }
	}
	else if (window.ActiveXObject)
	{
		try { req = new ActiveXObject("Msxml2.XMLHTTP"); }
		catch(e)
		{
			try { req = new ActiveXObject("Microsoft.XMLHTTP"); }
			catch(e) { req = false; }
		}
	}
	if (req)
	{
		req.open("GET",url,false);
		req.send(null);
		//var bigMeta = document.getElementById('bigmeta');
		if (req.status == 200 || req.status == 304){
			//bigMeta.innerHTML = req.responseText;
		}
		else {
			//bigMeta.innerHTML = "No metadata could be found for this image stack.";
		}
	}
	else return false;
}

/**
 * Loads metadata for a stack, calculates appropriate variables
 */
function getStack()
{
	imageIndex = getJSOFile(projectsPath + project + '/' + stackId + '.js');
	stackDepth = imageIndex.fulldepth;
	stackPath = imageIndex.imagepath;
	zsimgsrc = imageIndex.zsimgsrc;
	ts = imageIndex.ts - 0; // Subtracting zero casts string to integer
	lwpx = imageIndex.largestwidth - 0;
	lhpx = imageIndex.largestheight - 0;
	siwpx = imageIndex.zselwidth - 0;
	sihpx = imageIndex.zselheight - 0;
	zsdblt = imageIndex.zseldragborderlefttop - 0;
	zsdbrb = imageIndex.zseldragborderrightbottom - 0;
	zsliceOrientation = imageIndex.zsliceorientation;

	if (zsliceOrientation == 'horizontal')
		shpx_max -= 60;
	else
		swpx_max -= 60;

	// Resizes zsel image
	if (siwpx > swpx_max || sihpx > shpx_max)
	{
		if (sihpx / shpx_max > siwpx / swpx_max)
		{
			shpx = shpx_max;
			swpx = shpx * siwpx / sihpx;
		}
		else
		{
			swpx = swpx_max;
			shpx = swpx * sihpx / siwpx;
		}
	}
	sxpx = (leftwpx - swpx) / 2;

	var zSelectImgContainer = document.getElementById('zsimgcontainer');
	zSelectImgContainer.style.width = swpx + 'px';
	zSelectImgContainer.style.height = shpx + 'px';
	zSelectImgContainer.style.left = sxpx + 'px';

	getZSel();
}

/**
 * Loads metadata for a single image, calculates appropriate variables
 */
function getImage()
{
	removeTiles(resolution);
	imageAttributes = getJSOFile(projectsPath + project + '/' + stackId + '/' + 'metadata/' + imageIndex.image[imageId].jsopath);
	fwpx = imageAttributes.fullwidth;
	fhpx = imageAttributes.fullheight;
	filename = imageAttributes.filename;

	if (lwpx > pwpx_max || lhpx > phpx_max)
	{
		if (lhpx / phpx_max > lwpx / pwpx_max)
		{
			phpx = phpx_max;
			pwpx = phpx * lwpx / lhpx;
		}
		else
		{
			pwpx = pwpx_max;
			phpx = pwpx * lhpx / lwpx;
		}
	}
	pxpx = Math.floor((leftwpx - pwpx)/2);

	nwpx = Math.floor(pwpx * fwpx / lwpx);
	nhpx = Math.floor(phpx * fhpx / lhpx);
	nxpx = Math.floor((pwpx - nwpx)/2);
	nypx = Math.floor((phpx - nhpx)/2);
	fif = '?FIF=' + webRoot + 'projects/' + project + '/' + stackId + '/' + 'images/' + filename;  // forms IIP server request
	if (window.numResIsDefined)
	{
		var reschange = numres - imageAttributes.numres;
		numres = imageAttributes.numres - 0; // Subtracting zero casts string to integer
		if (reschange > resolution)
			resolution = 0;
		else
			resolution = resolution - reschange;
	}
	else
	{
		numres = imageAttributes.numres - 0;
		resolution = 1;
	}
	window.numResIsDefined = true;
	var nav = document.getElementById('nav');
	nav.style.width = nwpx + 'px';
	nav.style.height = nhpx + 'px';
	nav.style.left = nxpx + 'px';
	nav.style.top = nypx + 'px';
	var plane = document.getElementById('plane');
	plane.style.width = pwpx + 'px';
	plane.style.height = phpx + 'px';
	plane.style.left = pxpx + 'px';
	if (zsliceOrientation == 'horizontal')
		zppx = Math.ceil(imageId * (shpx-zsdblt-zsdbrb) / stackDepth) + zsdblt;
	else
		zppx = Math.ceil(imageId * (swpx-zsdblt-zsdbrb) / stackDepth) + zsdblt;

	getMetadata();
	getNav();
}

/**
 * Calculates variables relevant when only the 'view' is being changed,
 * not the image or stack data
 */
function getView()
{
	var scaleFactor = 1;

	if (resolution < numres)
		for (var i=resolution; i<numres-1; i++)
			scaleFactor /= 2;

	else if (resolution >= numres)
		for (var i=resolution; i>numres-1; i--)
			scaleFactor *= 2;

	iwpx = Math.floor(fwpx * scaleFactor);
	ihpx = Math.floor(fhpx * scaleFactor);

	iwt = Math.ceil(iwpx/ts);
	iht = Math.ceil(ihpx/ts);

	rxpx = Math.floor(ixpx * nwpx / iwpx);
	rypx = Math.floor(iypx * nhpx / ihpx);
	rwpx = Math.floor(dwpx * nwpx / iwpx);
	rhpx = Math.floor(dhpx * nhpx / ihpx);

	ixt = Math.floor(ixpx / ts);
	iyt = Math.floor(iypx / ts);

	ixt_ll = ixt * ts;
	ixt_ul = (ixt+1) * ts - 1;
	iyt_ll = iyt * ts;
	iyt_ul = (iyt+1) * ts - 1;

	fixVars();

	displayMeta();
	// Timeout prevents the tile frame from moving before the images have been cleared
	removeTiles(resolution);
	setTimeout("getTiles(resolution);",20);
	setTimeout("getMapsForTiles(resolution);",20);
	setTimeout("applyCSS();",40);
	setTimeout("showTiles();",100);
	setTimeout("showZone();",100);
}

/**
 * Catch-all function to stop e.g. dragging beyond sides of image
 */
function fixVars()
{
	if (ixpx < 0)
	{
		ixpx = 0;
		rxpx = 0;
	}
	else if (ixpx > iwpx - dwpx)
	{
		ixpx = iwpx - dwpx;
		rxpx = nwpx - rwpx;
	}
	if (iypx < 0)
	{
		iypx = 0;
		rypx = 0;
	}
	else if (iypx > ihpx - dhpx)
	{
		iypx = ihpx - dhpx;
		rypx = nhpx - rhpx;
	}
	if (ixt < 0) ixt = 0;
	if (iyt < 0) iyt = 0;
	if (iwpx < dwpx) ixpx = iwpx/2 - dwpx/2;
	if (ihpx < dhpx) iypx = ihpx/2 - dhpx/2;
	if (rxpx < 0) rxpx = 0;
	if (rypx < 0) rypx = 0;
	if (rwpx > nwpx) rwpx = nwpx;
	if (rhpx > nhpx) rhpx = nhpx;

	if (zppx < zsdblt) zppx = zsdblt;
	if (zsliceOrientation == 'horizontal')
	{
		if (zppx > shpx-zsdbrb) zppx = shpx-zsdbrb;
	}
	else
	{
		if (zppx > swpx-zsdbrb) zppx = swpx-zsdbrb;
	}

	if (imageId < 0) imageId = 0;
	if (imageId >= stackDepth) imageId = stackDepth - 1;
}

/* END OF RELOADING FUNCTIONS
 */


/* START OF MISCELLANEOUS FUNCTIONS
 */

function init()
{
	/* Determines whether to make concessions for the MS Internet Explorer
	 * browser.
	 */
	var agt=navigator.userAgent.toLowerCase();
	isIE = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));

	// Sets initial tile frame position
	ixpx = 0;
	iypx = 0;

	// Sets some variables used for CSS positioning
	leftwpx = 200;
	rightwpx = 300;
	rightxpx = 746;
	swpx_max = 200;
	shpx_max = 200;
	pwpx_max = 180;
	phpx_max = 130;

	getStack();
	section = parseInt(section);
	if (section > 0 && section <= stackDepth)
		imageId = section;
	else
		imageId = parseInt(stackDepth/2);
	getImage();

	if (resize == 'none' || resize == 'toggle')
	{
		if (resize == 'none')
		{
			document.getElementById('resize').style.display = 'none';
			iconswpx = 75;
		}
		else
		{
			vwpx = 958;
			vhpx = 535;
			iconswpx = 110;
		}
		iconsxpx = leftwpx/2 - iconswpx/2;
		dwpx = vwpx - leftwpx - rightwpx - 46;
		dhpx = vhpx - 23;
		dwt = Math.ceil(dwpx/ts);
		dht = Math.ceil(dhpx/ts);
		getView();
		applyCSS();
	}
	else
	{
		iconswpx = 75;
		iconsxpx = leftwpx/2 - iconswpx/2;
		document.getElementById('resize').style.display = 'none';
		getWindowSize();
		window.onresize = getWindowSize;
	}
	
	
	document.onmousedown = startDrag;
	document.ondblclick = doubleClick;

	document.getElementById('clickup').onmousedown = clickUp;
	document.getElementById('clickdown').onmousedown = clickDown;
	document.getElementById('clickleft').onmousedown = clickLeft;
	document.getElementById('clickright').onmousedown = clickRight;
	document.onkeydown = getKeys;
	document.onkeyup = stopClick;

	var tileView = document.getElementById("tileview");
	var zSelectImgContainer = document.getElementById("zsimgcontainer");

	/**
	 * The following code is lifted from http://adomas.org/javascript-mouse-wheel/
	 * 
	 * Its purpose is to handle scroll-wheel movement.
	 */

	/** Initialization code. 
	 * If you use your own event management code, change it as required.
	 */
	if (window.addEventListener)
	{
		/** DOMMouseScroll is for mozilla. */
		tileView.addEventListener('DOMMouseScroll', wheel, false);
		zSelectImgContainer.addEventListener('DOMMouseScroll', wheel, false);
	}
	/** IE/Opera. */
	tileView.onmousewheel = document.onmousewheel = wheel;
	zSelectImgContainer.onmousewheel = document.onmousewheel = wheel;

	/**
	 * End of code lift
	 */
}

/**
 * Portions of the following code are lifted from http://adomas.org/javascript-mouse-wheel/
 */

/** This is high-level function.
 * It must react to delta being more/less than zero.
 */
function handle(delta,objClass)
{
	if (objClass == 'tileview')
	{
		if (delta < 0)
			zoomOut();
		else
			zoomIn();
	}
	else if (objClass == 'zsimgcontainer')
	{
		if (delta < 0)
			incZSel(Math.ceil(stackDepth/50));
		else
			incZSel(-Math.ceil(stackDepth/50));
	}
}

/** Event handler for mouse wheel event.
 */
function wheel(e)
{
	if (isIE==true)
	{
		obj = event.srcElement;
		topelement = "BODY";
	}
	else
	{
		obj = e.target;
		topelement = "HTML";
	}

	while (obj.tagName != topelement && obj.className != 'tileview' && obj.className != 'zsimgcontainer')
		obj = (isIE == true) ? obj.parentElement : obj.parentNode;

	var delta = 0;
	if (!e) /* For IE. */
		e = window.event;
	if (e.wheelDelta) /* IE/Opera. */
	{ 
		delta = e.wheelDelta/120;
		/** In Opera 9, delta differs in sign as compared to IE.
		 */
		if (window.opera)
			delta = -delta;
	}
	else if (e.detail) /** Mozilla case. */
	{
		/** In Mozilla, sign of delta is different than in IE.
		 * Also, delta is multiple of 3.
		 */
		delta = -e.detail/3;
	}
	/** If delta is nonzero, handle it.
	 * Basically, delta is now positive if wheel was scrolled up,
	 * and negative, if wheel was scrolled down.
	 */
	if (delta)
		handle(delta,obj.className);
	/** Prevent default actions caused by mouse wheel.
	 * That might be ugly, but we handle scrolls somehow
	 * anyway, so don't bother here..
	 */
	if (e.preventDefault)
		e.preventDefault();
	e.returnValue = false;
}

/**
 * End of code lift
 */

/**
 * Allows navigation using keyboard arrow/numpad keys
 */
function getKeys(e)
{
	var code;
	if (!e) var e = window.event;
	e.preventDefault();
	code = e.keyCode;
	if (e.keyCode && !beingPressed)
	{
		beingPressed = true;
		if (code == 33) zoomIn();
		if (code == 34) zoomOut();
		if (code == 37 || code == 100) clickLeft();
		if (code == 38 || code == 104) clickUp();
		if (code == 39 || code == 102) clickRight();
		if (code == 40 || code == 98) clickDown();
		if (code == 103) clickUpLeft();
		if (code == 105) clickUpRight();
		if (code == 97) clickDownLeft();
		if (code == 99) clickDownRight();
	}
}

/**
 * Gets window size.  Ooesn't work properly in IE.
 */
function getWindowSize()
{
	if (parseInt(navigator.appVersion)>3)
	{
		if (isIE == true)
		{
			vwpx = document.body.offsetWidth - vxpx * 2;
			vhpx = document.body.offsetHeight - vxpx - vypx;
		}
		else
		{
			vwpx = window.innerWidth - vxpx * 2 - 1;
			vhpx = window.innerHeight - vxpx - vypx - 1;
		}
	}
	if (vwpx < 900)
	{
		vwpx = 900;
		vhpx -= 16;
	}
	if (vhpx < 540)
	{
		vhpx = 532;
		vwpx -= 16;
	}
	dwpx = vwpx - leftwpx - rightwpx - 46;//384;
	dhpx = vhpx - 22;
	dwt = Math.ceil(dwpx/ts);
	dht = Math.ceil(dhpx/ts);
	getView();
	applyCSS();
}

/**
 * Displays useful debugging information in a window
 */
function log(description,value)
{
	var log = document.getElementById('log');
	log.style.display = 'block';
	var logItem = document.createElement('p');
	logItem.setAttribute('id','log'+logId);
	logItem.setAttribute('class','log');
	log.appendChild(logItem);
	logItem.innerHTML = logId + " " + description + " " + value;
	if (logId > logLength)
	{
		var oldLogItem = document.getElementById('log'+(logId-logLength-1));
		if (oldLogItem)
			log.removeChild(oldLogItem);
	}
	logId++;
}

/**
 * Similar idea, not often used
 */
function showVar(id,value)
{
	var vars = document.getElementById('vars');
	var varsItem;
	if (varsItem = document.getElementById(id))
		varsItem.innerHTML = id + value;
	else
	{
		varsItem = document.createElement('p');
		varsItem.setAttribute('id',id);
		varsItem.setAttribute('class','log');
		vars.appendChild(varsItem);
		varsItem.innerHTML = id + value;
	}
}

/**
 * Better idea, used for 'Current section' at left-hand side
 */
function showMeta(id,value,strong)
{
	var meta = document.getElementById('meta');
	var metaId = document.getElementById('metaid');
	var metaValue = document.getElementById('metavalue');

	var metaItem = document.getElementById('meta-'+id);
	var metaItemId = document.getElementById('metaid-'+id);
	var metaItemValue = document.getElementById('metavalue-'+id);

	if (metaItem)
		metaItemValue.innerHTML = value;
	else
	{
		metaItem = document.createElement('tr');
		metaItemId = document.createElement('td');
		metaItemValue = document.createElement('td');

		metaItem.setAttribute('id','meta-'+id);
		metaItemId.setAttribute('id','metaid-'+id);
		metaItemValue.setAttribute('id','metavalue-'+id);
		meta.appendChild(metaItem);
		metaItem.appendChild(metaItemId);
		metaItem.appendChild(metaItemValue);
		metaItemId.innerHTML = id;
		metaItemValue.innerHTML = value;
	}
	if (strong)
	{
		metaItemId.style.color = 'red';
		metaItemValue.style.color = 'red';
	}
	else
	{
		metaItemId.style.color = 'black';
		metaItemValue.style.color = 'black';
	}
}

function displayMeta()
{
	var metaContainer = document.getElementById('metacontainer');
	metaContainer.style.display = 'block';
	var imageLevel = imageId + ' of ' + stackDepth;
	showMeta("Current section",imageLevel);
	var reslevel = (resolution+1) + ' of ' + numres;
	if (resolution >= numres)
		showMeta("Resolution level",reslevel,true);
	else
		showMeta("Resolution level",reslevel);
	// Extra debugging information is commented out below
	/*
		 showMeta('ixt_ll',ixt_ll);
		 showMeta('ixt_ul',ixt_ul);
		 showMeta('iyt_ll',iyt_ll);
		 showMeta('iyt_ul',iyt_ul);
		 showMeta('ihpx',ihpx);
		 showMeta('iwpx',iwpx);
		 showMeta('iht',iht);
		 showMeta('iwt',iwt);
		 showMeta('ixpx',ixpx);
		 showMeta('iypx',iypx);
		 showMeta('ixt',ixt);
		 showMeta('iyt',iyt);
		 showMeta('rhpx',rhpx);
		 showMeta('rwpx',rwpx);
		 showMeta('rxpx',rxpx);
		 showMeta('rypx',rypx);
		 showMeta('nwpx',nwpx);
		 showMeta('nhpx',nhpx);
		 showMeta('fwpx',fwpx);
		 showMeta('fhpx',fhpx);
		 showMeta('dwt',dwt);
		 showMeta('dht',dht);
	 */
	var meta = document.getElementById('meta');
	mhpx = 10 + meta.childNodes.length * 15;
	metaContainer.style.height = mhpx + 'px';
}

var mhpx;

/**
 * Not used.
 */
function showLoadingBox()
{
	var loadingBox = document.getElementById('loadingbox');
	loadingBox.style.top = dhpx / 2 - 5 + 'px';
	loadingBox.style.left = dwpx / 2 - 10 + 'px';
	loadingBox.style.display = 'block';
}

/**
 * Not used.
 */
function hideLoadingBox()
{
	var loadingBox = document.getElementById('loadingbox');
	loadingBox.style.display = 'none';
}

/**
 * Uses showVar() to display debugging information
 */
function showCommonVars()
{
	var vars = document.getElementById('vars');
	vars.style.display = 'block';
	showVar('ixt_ll',ixt_ll);
	showVar('ixt_ul',ixt_ul);
	showVar('iyt_ll',iyt_ll);
	showVar('iyt_ul',iyt_ul);
	showVar('ihpx',ihpx);
	showVar('iwpx',iwpx);
	showVar('iht',iht);
	showVar('iwt',iwt);
	showVar('ixpx',ixpx);
	showVar('iypx',iypx);
	showVar('ixt',ixt);
	showVar('iyt',iyt);
	showVar('rhpx',rhpx);
	showVar('rwpx',rwpx);
	showVar('rxpx',rxpx);
	showVar('rypx',rypx);
	showVar('nwpx',nwpx);
	showVar('nhpx',nhpx);
	showVar('fwpx',fwpx);
	showVar('fhpx',fhpx);
	showVar('dwt',dwt);
	showVar('dht',dht);
}		

/**
 * Applies CSS to HTML elements that require it
 */
function applyCSS()
{
	var tileFrame = document.getElementById('tileframe');
	tileFrame.style.left = (-ixpx) + 'px';
	tileFrame.style.top = (-iypx) + 'px';
	tileFrame.style.width = iwpx + 'px';
	tileFrame.style.height = ihpx + 'px';
	tileFrame.style.color = imageIndex.bgcolor;
	var termView = document.getElementById('termview');
	termView.style.height = '25px';
	termView.style.width = dwpx + 'px';
	var tileView = document.getElementById('tileview');
	tileView.style.width = dwpx + 'px';
	tileView.style.height = dhpx - 26 + 'px';
	var compInfContainer = document.getElementById('compInfContainer');
	compInfContainer.style.width = Math.ceil(dwpx/2) +'px';
	compInfContainer.style.height = dhpx - 26 - 5 + 'px';
	tileView.style.backgroundColor = imageIndex.bgcolor;
	var viewer = document.getElementById('viewer');
	viewer.style.left = vxpx + 'px';
	viewer.style.top = vypx + 'px';
	viewer.style.width = vwpx + 'px';
	viewer.style.height = vhpx + 'px';
	var left = document.getElementById('left');
	left.style.width = leftwpx + 'px';
	left.style.height = dhpx + 'px';
	var icons = document.getElementById('icons');
	icons.style.width = iconswpx + 'px';
	icons.style.left = iconsxpx + 'px';
	var right = document.getElementById('right');
	var right = document.getElementById('right');
	right.style.left = (vwpx-rightwpx-12) + 'px'; //355
	right.style.height = dhpx + 'px';
	right.style.width = rightwpx + 'px';
	var clickMove = document.getElementById('clickmove');
	clickMove.style.left = (dwpx-70) + 'px';
	clickMove.style.top = (dhpx-110) + 'px';
	clickMove.style.width = '66px';
	clickMove.style.height = '104px';
	if (isIE == true)
	{
		clickMove.style.backgroundColor = '#ddd';
		clickMove.style.border = '1px solid #999';
	}
	var plane = document.getElementById('plane');
	plane.style.backgroundColor = imageIndex.bgcolor;
	var zone = document.getElementById('zone');
	zone.style.left = rxpx + 'px';
	zone.style.top = rypx + 'px';
	zone.style.width = rwpx + 'px';
	zone.style.height = rhpx + 'px';
	var zSelect = document.getElementById('zselect');
	zSelect.style.height = shpx_max + 'px';
	zSelect.style.width = swpx_max + 'px';
	var zSelectImgContainer = document.getElementById('zsimgcontainer');
	var zSlice = document.getElementById('zslice');
	var zup = document.getElementById('zup');
	var zdown = document.getElementById('zdown');
	var zupMore = document.getElementById('zupmore');
	var zdownMore = document.getElementById('zdownmore');
	if (zsliceOrientation == 'horizontal')
	{
		zSelectImgContainer.style.top = 25 + 'px';
		zSelectImgContainer.style.left = (leftwpx/2 - swpx/2) + 'px';
		zSlice.style.top = zppx + 'px';
		zSlice.style.left = 0 + 'px';
		zSlice.style.width = swpx + 'px';
		zSlice.style.height = '4px';
		zSlice.style.borderTop = '1px solid yellow';
		zup.style.top = '0px';
		zup.style.left = (leftwpx/2 - 25) + 'px';
		zupMore.style.top = '0px';
		zupMore.style.left = (leftwpx/2 + 3) + 'px';
		zdown.style.top = (shpx+30) + 'px';
		zdown.style.left = (leftwpx/2 - 25) + 'px';
		zdownMore.style.top = (shpx+30) + 'px';
		zdownMore.style.left = (leftwpx/2 + 3) + 'px';
		zup.src = '../images/crystal_clear/22x22/actions/1uparrow.png';
		zupMore.src = '../images/crystal_clear/22x22/actions/2uparrow.png';
		zdown.src = '../images/crystal_clear/22x22/actions/1downarrow.png';
		zdownMore.src = '../images/crystal_clear/22x22/actions/2dowarrow.png';
	}
	else
	{
		zSelectImgContainer.style.top = 0 + 'px';
		zSelectImgContainer.style.left = (leftwpx/2 - swpx/2) + 'px';
		zSlice.style.left = zppx + 'px';
		zSlice.style.top = 0 + 'px';
		zSlice.style.height = shpx + 'px';
		zSlice.style.width = '4px';
		zSlice.style.borderLeft = '1px solid yellow';
		zup.style.top = (shpx/2-25) + 'px';
		zup.style.left = (leftwpx/2 - swpx/2 - 25) + 'px';
		zupMore.style.top = (shpx/2+3) + 'px';
		zupMore.style.left = (leftwpx/2 - swpx/2 - 25) + 'px';
		zdown.style.top = (shpx/2-25) + 'px';
		zdown.style.left = (leftwpx/2 + swpx/2 + 5) + 'px';
		zdownMore.style.top = (shpx/2+3) + 'px';
		zdownMore.style.left = (leftwpx/2 + swpx/2 + 5) + 'px';
		zup.src = '../images/crystal_clear/22x22/actions/1leftarrow.png';
		zupMore.src = '../images/crystal_clear/22x22/actions/2leftarrow.png';
		zdown.src = '../images/crystal_clear/22x22/actions/1rightarrow.png';
		zdownMore.src = '../images/crystal_clear/22x22/actions/2rightarrow.png';
	}
}

/**
 * Resizes tile view when resize = 'toggle'
 */
function toggleTileViewSize()
{
	var largeView = new Object();
	var midView = new Object();
	var smallView = new Object();
	largeView.width = 768;
	largeView.height = 768;
	midView.width = 512;
	midView.height = 512;
	smallView.width = 256;
	smallView.height = 256;
	if (dwpx == largeView.width && dhpx == largeView.height)
	{
		ixpx += (largeView.width - smallView.width)/4;
		iypx += (largeView.height - smallView.height)/4;
		resizeTileView(midView.width-largeView.width,midView.height-largeView.height);
	}
	else if (dwpx == midView.width && dhpx == midView.height)
	{
		ixpx += (midView.width - largeView.width)/2;
		iypx += (midView.height - largeView.height)/2;
		resizeTileView(largeView.width-midView.width,largeView.height-midView.height);
	}
}

function resizeTileView(x,y)
{
	dwpx = dwpx + x;
	dhpx = dhpx + y;
	dwt = parseInt(dwpx/ts);
	dht = parseInt(dhpx/ts);
	vwpx += x;
	vhpx += y;
	rightxpx += x;
	applyCSS();
	getView();
}

/**
 * Change current image
 */
function incZSel(value)
{
	if (lock == false)
	{
		if (!(imageId == 0 && value < 0) && !(imageId == stackDepth-1 && value > 0))
		{
			lock = true;
			setTimeout("removeLock()",lockTime);
			imageId += value;
			fixVars();
			getImage();
			getView();
		}
	}
}

function removeLock()
{
	lock = false;
}

/**
 * Saves full image in JPEG format
 */
function saveImage()
{
	// To download the 'current view', use these values:
	/*
		 var x = ixpx/iwpx;
		 var y = iypx/ihpx;
		 var w = dwpx/iwpx;
		 var h = dhpx/ihpx;
	 */

	// To download the whole image, use these values:
	var x = 0;
	var y = 0;
	var w = 1;
	var h = 1;

	var wid = fwpx;
	var tempImageName = stackId + '_d' + imageId + '.jpg';

	// Remove spaces from imageName
	var imageName = "";
	tempImageName = '' + tempImageName;
	var splitstring = tempImageName.split(" ");
	for (var i=0; i<splitstring.length; i++)
		imageName += splitstring[i];

	var url = 'download-jpeg.php' + '?iipsrv=' + iipsrv + '&path=' + webRoot + 'projects/' + project + '/' + stackId + '/' + 'images/' +'&filename=' + filename + '&qlt=' + qlt + '&x=' + x + '&y=' + y + '&w=' + w + '&h=' + h + '&wid=' + wid + '&cvt=' + cvt + '&imagename=' + imageName;
	var dlFrame = document.createElement('iframe');
	// Can't use display: 'none' due to a bug in Safari, so we make the iframe invisible through other means
	dlFrame.style.height = '0px';
	dlFrame.style.width = '0px';
	dlFrame.style.border = '0px';
	document.body.appendChild(dlFrame);
	dlFrame.src = url;
	// Wait 5 seconds, then remove the iframe
	setTimeout("document.body.removeChild(dlFrame);",5000);
}

function showHelp()
{
	var helpWindow = window.open('help.html','Image viewer help','width=400,height=200,scrollbars=no,resizeable=yes,toolbar=no,location=no,directories=no,status=no,menubar=no');

}

