/** Eganu Mosaic (TM) Object
 * @author Erick S <esatire@gmail.com>
 * @version 1.0.3 11/09/2007 Added Mosaic typeof checks to prevent errors
 * 
 * requires common.js for '$', getRealOffset and getRand
 * 
 * potential problem - when moving mouse into the container, body is assigned onmouseover
 * which is removed when leaving the container. Make sure your body tag doesn't have
 * any other onmouseover events attached when using this.
 */

/** Now I COULD extend the Array object, but I don't like doing stuff like that */
function shuffleArray(arr,i)
{
	if (typeof i == 'undefined') i = 50;
	var a,b,t;
	for (;i>-1;i--)
	{
		a = Math.floor(Math.random()*arr.length);
		b = Math.floor(Math.random()*arr.length);
		t = arr[a];
		arr[a] = arr[b];
		arr[b] = t;
	}
	return arr;
}

/** Static Constants */
Mosaic.IMAGE_SIZE = 64;
Mosaic.SWITCH_FADE_RATE = 40;
Mosaic.SWITCH_PAUSE = 1800;
Mosaic.LOADING_TEXT = "Loading Eganu&trade; Mosaic...";
/** how many pixels from the top of the container the loading label and bar will be set */
Mosaic.LOADLABEL_POS = 50;
/** How pixels before the right side of the container the loadBar will stop */
Mosaic.LOADBAR_PADDING = 15;
/** when the tooltip pops up, how much to the top and to the left it goes */
Mosaic.TOOLTIP_INDENT = 10;
Mosaic.TOOLTIP_CHECK_INTERVAL = 50;
// If the links have a common path (usually) 
Mosaic.LINK_PREFIX = '/people/card.php?id=';

/** Mosaic Constructor
 * 
 * @param elementId The DOM element that will be used as container
 * @param globalData An array of items - each cell has 0->link 1->title 2->image path
 * @param numHoles How many spaces will be left unused
 */
function Mosaic(elementId,globalData,width,height,numHoles)
{
	var curInitedImage=0;
	var myself = this;
	this.timer = null;
	this.tooltipTimer = null;
	var tooltipTime = 0;
	var currentItem = null;
	this.globalData = globalData;

	var pickedCellPos = 0,pickedCell = null;
	// fade effect control options
	var fadeOpacity = 0,fadeStop=0,fadeChange=0,fadeFunc=null;
	
	var doFades = true;
	var isFadeOut = true;

	var freeImages = usedImages = null;
	var usedCells = freeCells = null;
	
	var numRows = Math.floor(height/Mosaic.IMAGE_SIZE);
	var numColumns = Math.floor(width/Mosaic.IMAGE_SIZE);
	var numCells = numRows*numColumns
	var numShown = numCells - numHoles;
	
	this.container = $(elementId);
	this.container.style.width = width + 'px';
	this.container.style.height = height + 'px';
	// keep the positions of the container
	var offset = getRealOffset(this.container);

	/** Init has to be done in 2 phases, due to image prefetching */
	function startInit()
	{
		var html = new Array("<div>"+Mosaic.LOADING_TEXT+"</div><div class='loadBar'>&nbsp;</div>");
		for (var i=0;i<this.globalData.length;i++)
		{
			html.push('<img/>');
		}
		this.container.innerHTML = html.join('');
		var divs = this.container.getElementsByTagName('div');
		divs[0].style.width = this.container.style.width;
		divs[0].style.paddingTop  = Mosaic.LOADLABEL_POS + 'px';
		this.loadLabel = divs[0];
		this.loadBar = divs[1];
		this.loadBarMax = this.container.offsetWidth - Mosaic.LOADBAR_PADDING;
		divs = null;
		var loadImages = this.container.getElementsByTagName('img');
		for (var i=0;i<loadImages.length;i++)
		{
			loadImages[i].style.position = 'absolute';
			loadImages[i].style.visibility = 'hidden';
			loadImages[i].style.left = offset.x;
			loadImages[i].style.top = offset.y;
			// setting the onload before the source is MANDATORY for IE to work properly
			loadImages[i].onload = this.prefetchImage;
			loadImages[i].onerror = this.prefetchImageError;
			loadImages[i].src = this.globalData[i][2];
		}
		loadImages = null;
	}
	
	this.prefetchImage = function()
	{
		//if (this.src == myself.globalData[0][2])
		//alert(this.src);
		myself.loadBar.style.width = (curInitedImage*myself.loadBarMax/myself.globalData.length) + 'px';
		curInitedImage++;
		if (curInitedImage == myself.globalData.length)
		{
			myself.loadLabel.innerHTML = 'Done!';
			delete(myself.loadLabel);	delete(myself.loadBar);	delete(myself.loadBarMax);
			if (this.timer == null)
			{
				this.timer = window.setTimeout(initComplete,500);
			}
			return;
		}
	}
	this.prefetchImageError = function()
	{
		this.src = '/images/noPhoto.gif';
	}
	
	function initComplete()
	{
		myself.container.onmouseover = myself.stopAnimation;
	
		freeImages = shuffleArray(myself.globalData,myself.globalData.length*2);
		delete(myself.globalData);
	
		// splice images
		usedImages = freeImages.slice(0,numShown);
		freeImages.splice(0,numShown);
	
		// init image array
		var html = new Array();
		for (var i=0;i<numCells;i++)
		{
			html.push("<img/>");
		}
		html.push("<div></div>");
		myself.container.innerHTML = html.join('');
		html = null;
	
		usedCells = new Array();
		freeCells = new Array();
	
		myself.domImages = myself.container.getElementsByTagName('img');

		// prepare an array with holes in it - the spread of the images
		var positions = new Array(myself.domImages.length);
		for (var i=0;i<numShown;i++) positions[i] = i;
		shuffleArray(positions,positions.length*2);
		// position images and fill in the used and unused arrays
		for (var i=0;i<myself.domImages.length;i++)
		{
			myself.domImages[i].style.width=myself.domImages[i].style.height=Mosaic.IMAGE_SIZE + 'px';
			myself.domImages[i].style.position = 'relative';
			myself.domImages[i].style.padding = '1px';
			myself.domImages[i].onmouseover = onImage;

			if (typeof positions[i] != 'undefined')
			{
//			alert(usedImages[positions[i]]);
				myself.domImages[i].src = usedImages[positions[i]][2];
				myself.domImages[i].id = positions[i];
				usedCells.push(i);
			}
			else
			{
				// myself.domImages[i].style.display = 'none';
				myself.domImages[i].style.visibility = 'hidden';
				myself.domImages[i].src = '/images/noPhoto.gif';
				myself.domImages[i].id = '';	
				freeCells.push(i);
			}
		}
		// prepare tooltip
		myself.tooltip = myself.container.getElementsByTagName('div')[0];	
		myself.tooltip.className = 'tooltip';
		myself.tooltip.onmouseover = function(evt)
		{
			if (window.event) window.event.cancelBubble = true;
			else evt.cancelBubble = true;
		}

		//alert(usedCells + " ||| " + freeCells);
		// prepare fadeout
		fadeFunc = switchOut;
		myself.timer = window.setTimeout(fadeFunc,Mosaic.SWITCH_PAUSE);
		// alert(myself.container.offsetWidth + '/' + myself.container.offsetHeight);		
	}
	
	/* ### This line start the init procedure ### */
	startInit.apply(this);
	
	/** Prepares an image for fadeout */
	function switchOut()
	{
		if (!doFades)
		{
			myself.timer = null; 
			return;
		}
		pickedCellPos = Math.floor(Math.random()*usedCells.length);
		pickedCell = myself.domImages[usedCells[pickedCellPos]];
		// prepare fade		
		fadeOpacity = 100; fadeStop = 0;
		fadeChange = -10; fadeFunc = switchIn;
		// call fade effect
		myself.timer = window.setInterval(fade,Mosaic.SWITCH_FADE_RATE);
	}
	/** Prepares an image for fadein */
	function switchIn()
	{
		if (!doFades)
		{
			myself.timer = null; 
			return;
		}
		// make all the switches - switch cell positions and image positions between the used and unused arrays
		var newImagePos = Math.floor(Math.random()*freeImages.length);
		var newCellPos = Math.floor(Math.random()*freeCells.length);
		var temp;
		
		// switch the current cell and content
		var oldPos = pickedCell.id;
		temp = usedImages[oldPos];
		 
		usedImages[oldPos] = freeImages[newImagePos]
		freeImages[newImagePos] = temp;
 
		pickedCell = myself.domImages[freeCells[newCellPos]];
		pickedCell.src = usedImages[oldPos][2];
		pickedCell.id = oldPos;

		temp = freeCells[newCellPos]; 
		freeCells[newCellPos] = usedCells[pickedCellPos];
		usedCells[pickedCellPos] = temp;

		// prepare fade 
		fadeOpacity = 0; fadeStop = 100;
		fadeChange = 10;	fadeFunc = switchOut;
		// call fade effect
		fade(); // one run to give it opacity 0 before making it visible
		// pickedCell.style.display = 'inline';
		pickedCell.style.visibility = 'visible';
		myself.timer = window.setInterval(fade,Mosaic.SWITCH_FADE_RATE);
	}
	/** Called during the fadein/fadeout animation */
	function fade()
	{
		if (typeof(pickedCell.style.opacity) != 'undefined')
			pickedCell.style.opacity=fadeOpacity/100;
		else if (typeof(pickedCell.style.filter) != 'undefined')
			pickedCell.style.filter = 'alpha(opacity='+fadeOpacity+')';

		if (fadeOpacity != fadeStop) fadeOpacity += fadeChange;
		else
		{
			window.clearInterval(myself.timer)
			if (isFadeOut)
			{
				pickedCell.style.visibility = 'hidden';
				// pickedCell.style.display = 'none';
			}
			isFadeOut = !isFadeOut;
			if (doFades) myself.timer = window.setTimeout(fadeFunc,Mosaic.SWITCH_PAUSE);
			else myself.timer = null;
		}
	}

	/** Assigned to body's onmouseover event handler */
	this.restartAnimation = function(evt)
	{
		document.onmouseover = null;
		doFades = true;
		window.clearInterval(myself.tooltipTimer);
		myself.tooltipTimer = null;
		if (myself.timer == null)
		{
			myself.timer = window.setTimeout(fadeFunc,Mosaic.SWITCH_PAUSE);
		}
		myself.container.onmousemove = null;
		myself.tooltip.style.display = 'none';
//		window.status = 'restarting';
	}
	
	this.stopAnimation = function(evt)
	{
		if (window.event) window.event.cancelBubble = true;
		else evt.cancelBubble = true;

		currentItem = null;
		if (myself.tooltip.style.display != 'none')
		{
			myself.tooltip.style.display = 'none';
			if (myself.tooltipTimer == null && typeof (Mosaic) != 'undefined')
				myself.tooltipTimer = window.setInterval(tooltipCheck,Mosaic.TOOLTIP_CHECK_INTERVAL);
		}

		if (doFades)
		{
			doFades = false;
			this.onmousemove = function() { tooltipTime = 0; }
			document.onmouseover = myself.restartAnimation;
			if (myself.tooltipTimer == null && typeof (Mosaic) != 'undefined')
				myself.tooltipTimer = window.setInterval(tooltipCheck,Mosaic.TOOLTIP_CHECK_INTERVAL);
//			window.status = 'stopping';
		}
	}

	/** Correct processing when mouse is over images - tooltip related */
	function onImage(evt)
	{
		if (window.event) window.event.cancelBubble = true;
		else evt.cancelBubble = true;

		currentItem = this;
		if (myself.tooltip.style.display != 'none')
		{
			myself.tooltip.style.display = 'none';
			if (myself.tooltipTimer == null && typeof (Mosaic) != 'undefined')
				myself.tooltipTimer = window.setInterval(tooltipCheck,Mosaic.TOOLTIP_CHECK_INTERVAL);
		}
		// necessary because after loading, mouse might be
		// directly on an image, bypassing container's onmouseover
		if (doFades)
		{
			doFades = false;
			this.parentNode.onmousemove = function() { tooltipTime = 0; }
			document.onmouseover = myself.restartAnimation;
			if (myself.tooltipTimer == null && typeof (Mosaic) != 'undefined')
				myself.tooltipTimer = window.setInterval(tooltipCheck,Mosaic.TOOLTIP_CHECK_INTERVAL);
//			window.status = 'stopping';
		}
	}
	/** Runs on interval when mouse roams through the element */
	function tooltipCheck()
	{
		tooltipTime+= Mosaic.TOOLTIP_CHECK_INTERVAL;
		if (tooltipTime >= Mosaic.TOOLTIP_CHECK_INTERVAL*5)
		{
			tooltipTime = 0;
			if (currentItem==null) return;
			var data = usedImages[currentItem.id];
			if (data[1] != null)
			{
				myself.tooltip.innerHTML = '<a href="'+Mosaic.LINK_PREFIX+data[0]+
													'"><img src="'+data[2]+'" border="0" /><br/>' + 
													data[1]+'</a>';
				myself.tooltip.style.left = (currentItem.offsetLeft - Mosaic.TOOLTIP_INDENT) + 'px';
				myself.tooltip.style.top =  (currentItem.offsetTop - Mosaic.TOOLTIP_INDENT) + 'px';
				myself.tooltip.style.display = 'block';
				window.clearInterval(myself.tooltipTimer);
				myself.tooltipTimer = null;
//				window.status = 'Tooltip';
			}
		}
	}
}