﻿
//stores a list of functions that need
//to be called when the slideshow ends
var showPopups = new Array();

var self;

var skipClicked = false;

function blockClicked()
{
	var hdnVal = document.getElementById("block_url_"+this.id);
	
	window.location = hdnVal.value;
}



function fileClicked()
{
	var hdnVal = document.getElementById("url_"+this.id);
	
	window.location = hdnVal.value;
}	

function toEnd()
{
	if (self != undefined)
	{
		skipClicked = true;
		self.toLastLayout();
	}
}

function Slideshow(id, w, h)
{

	this.displayID = id;
	this.display = null;
	this.width = w;
	this.height = h;
	
	this.currentLayout;
	this.currentLayoutIndex = -1;
	this.data = null;	
	this.skipToEnd = 0;
	
	this.layouts = null;


}

Slideshow.prototype.stop = function()
{
	
	for(var i=0; i < showPopups.length; i++)
	{
		showPopups[i].showPopup();
	}
	
	showPopups = new Array();

}



Slideshow.prototype.start = function(d)
{
	this.data = d;
	
	
	
	this.display = document.getElementById(this.displayID);
	
	//build the first Layout
	//If there is a LayoutID, then there will only be 1 layout, and it will not loop
	
	//start by adding a blank layout

	
	var blankLayout = document.createElement("div");
	blankLayout.style.width = this.width+"px";
	blankLayout.style.height = this.height+"px";
	blankLayout.style.display = "block";
	blankLayout.style.float = "left";
	blankLayout.style.backgroundColor = "#ffffff";
	

	this.display.appendChild(blankLayout);
	
	this.nextLayout();

	if (this.skipToEnd > 0)
	{
		//show a skip to end button
		var skipDisplay = document.createElement("div");
		skipDisplay.id = "skipper";
		skipDisplay.className = "skipToEnd";
		//skipDisplay.innerHTML = "Continue";
		skipDisplay.style.color = "#ffffff";
		skipDisplay.style.position = "absolute";
		skipDisplay.style.top = (this.height + 80)+"px";
		skipDisplay.style.width = (this.width - 20)+"px";
		skipDisplay.style.textAlign = "right";
		skipDisplay.style.textShadow = "2px 2px #555555";
		skipDisplay.style.zIndex = 5;
		
		var skipRef = document.createElement("a");
		skipRef.style.color = "#ffffff";
		skipRef.style.textDecoration = "none";
		skipRef.innerHTML = "Continue";
		skipRef.href = "javascript:toEnd();";
		
		skipDisplay.appendChild(skipRef);
		
		
		this.display.parentNode.appendChild(skipDisplay);
	
	}
	
	/*
		data is of the form
	
		data
		-LayoutDelay ( number of milliseconds to wait before going to the next layout )
		-PageLoop ( 1 if this slideshow should loop )
		-DefaultBlockDelay ( number of milliseconds to wait before transitioning to next block - this is applied to all blocks, unless a block specifies it's own )
		-DefaultLayoutDelay ( number of milliseconds to wait before transitioning to next layout - this is applied to all layouts, unless a lyaout specifies it's own )
		-Layouts<LayoutItem> ( list of LayoutItems in this SlideShow )
			-LayoutDelay ( number of milliseconds to wait before transitioning to next layout )
			-RandomBlocks ( 1 if blocks should play in random order )
			-Blocks<BlockItem> ( list of BlockItems in this Layout )
				-LayoutOrder ( the order this Block is in for layout )
				-PlayOrder ( the order this block is in for playing )
				-BlockDelay ( number of milliseconds to wait before playing next block )
				-RandomFiles ( 1 if this block should play it's list of files randomly )
				-Hyperlink ( if not "", a URL to be applied to this block when clicked on )
				-Width ( the width of this block in pixels )
				-Height ( the height of this block in pixels )
				-Files<FileItem> ( list of FileItems in this block )
					-FileID ( ID of this file - used in URL to reference this file )
					-FileType ( type of file - only images for js version )
					-Name ( name of this file - different than the actual file name )
					-Content ( any text associated with this file )
					-FileDelay ( number of milliseconds to wait before playing next file )
					-Hyperlink ( if not "", a URL to be applied to this file )
					-Extension ( the file's extension )
					-BGColor ( a background color to be applied if this image is smaller than the block size )
					-TextColor ( the text color for any text displayed over the file )
					-FontSize ( the size of the font )
					-HAlign ( horizontal alignment: left, center, right )
					-VAlign ( vertical alignment: top, middle, bottom )
					-PopupContent ( if present, shows text when mouse rolled over - not present in js version, since ipad doesn't have rollovers )
					
					
	
	
	
	
	*/
	
	
}

Slideshow.prototype.toLastLayout = function()
{
	this.currentLayoutIndex = this.data.Layouts.length-2;

	this.currentLayout.stopTimers();
	
	this.nextLayout();
}


Slideshow.prototype.nextLayout = function()
{
	//renders the next layout over the current layout
	//starts by playing each block
	//If more than 1 layout exists, then always have 2 layouts present, 1 in front and 1 in back
	
	this.currentLayoutIndex++;
	//alert(this.currentLayoutIndex);
	if (this.currentLayoutIndex > this.data.Layouts.length-1)
	{
	
		if (this.data.PageLoop > 0)
		{
			//reset and keep going
			this.currentLayoutIndex = -1;
			this.nextLayout();
		}
		else 
		{
			this.stop();
		}
	}
	else 
	{
		//start the current layout
		
		var lout = new Layout(this.data.Layouts[this.currentLayoutIndex], this.width, this.height, this);
		
		this.currentLayout = lout;
		
		//alert(this.display.childNodes.length);
		this.display.appendChild(lout.getContent());
		//alert(this.display.innerHTML);
		
		lout.start();
		
		while(this.display.childNodes.length > 3)
		{
		//	alert(this.display.childNodes.length);
			this.display.removeChild(this.display.childNodes[0]);
		}
		
	
	}
	
	if (this.currentLayoutIndex == this.data.Layouts.length-1 && this.skipToEnd > 0)
	{
		this.display.parentNode.removeChild(document.getElementById("skipper"));
	}
	

}





function Layout(layoutData, w, h, sshow)
{

	
	this.blockTimerID = 0;
	this.layoutTimerID = 0;
	
	this.slideshow = sshow;
	this.data = layoutData;
	this.width = w;
	this.height = h;
	this.display = document.createElement("div");
	this.display.className = "layoutHolder";
	
	if (this.data.LayoutDelay == 0)
	{
		this.data.LayoutDelay = Number(this.slideshow.data.DefaultLayoutDelay*1000);
	}
	
	
	
	this.DefaultBlockDelay = this.slideshow.DefaultBlockDelay;
	this.currentBlockDelay = this.DefaultBlockDelay;
	
	this.cycleBlocks = 0;
	
	this.blocks = new Array();
	this.heap = new BinaryHeap();
	
	
	
	if (this.data.Blocks.length == 6)
	{
		//the six-up layout order won't work for css layout
		//so evens on top and odds on bottom will work
		
		//first layout even blocks
		for(var i=0; i < this.data.Blocks.length; i += 2)
		{
			this.blocks.push(new Block(this.data.Blocks[i], this));
			
			this.display.appendChild(this.blocks[this.blocks.length-1].getContent());	
			
		}
		
		//then layout odd blocks
		for(var i=1; i < this.data.Blocks.length; i += 2)
		{
			this.blocks.push(new Block(this.data.Blocks[i], this));
			
			this.display.appendChild(this.blocks[this.blocks.length-1].getContent());	
			
		}
	}
	else 
	{
		for(var i=0; i < this.data.Blocks.length; i++)
		{
			this.blocks.push(new Block(this.data.Blocks[i], this));
			
			this.display.appendChild(this.blocks[i].getContent());	

		}
	
	}

	
}

Layout.prototype.getContent = function()
{
	return this.display;
}

Layout.prototype.buildPlayOrder = function()
{
	var isSingleLayout = (this.slideshow.data.Layouts.length == 1);
	//alert(isSingleLayout);

	
	for(var i=0; i < this.blocks.length; i++)
	{
		var b = this.blocks[i];
		
		if (!b.hasEnded())
		{

			if (isSingleLayout)
			{
				//if this is the only layout, then
				//all the blocks should start at the same time
				//for the first time.
				b.data.PlayOrder = 1;
				this.heap.add(new Element(b, 1));
			}
			else if(this.data.RandomBlocks > 0)
			{
				this.heap.add(new Element(b, Math.round(Math.random()*(this.blocks.length*100))));
			}
			else
			{
				this.heap.add(new Element(b, b.getPlayOrder()));
			}
		}
		
	}
	
	this.cycleBlocks = this.heap.size();

}

Layout.prototype.start = function()
{
	this.buildPlayOrder();
	
	var element = this.heap.peek();

	if (element != null)
	{
	
		this.playNextBlock();
		
	}
	else
	{
		//if we don't have anything in the heap
		//then this layout is empty
		//so go to the next layout

		
		
	}

}

Layout.prototype.playNextBlock = function()
{
	var element = this.heap.remove();

	//alert("element is null: "+(element == null));
	if (element == null)
	{
		//end layout
		var ss = this.slideshow;
		var layoutRef = this;
		var delay = function() { if (layoutRef.layoutTimerID != 0) { ss.nextLayout();} };
		
		var layoutDelay = this.data.LayoutDelay;
		
		if (this.slideshow.data.Layouts.length == 1)
		{
			layoutDelay = 100;
		}
		
		this.layoutTimerID = setTimeout(delay, layoutDelay);

		
	}
	else
	{
		//loop through and play all the blocks with this play order
		var b = element.data;
		b.nextFile();
	
		
		var blockDelay = b.data.BlockDelay;
		
		while(this.heap.peek() != null && (this.heap.peek().data.getPlayOrder() == b.getPlayOrder()))
		{				
		
			var block = this.heap.remove();
			block.data.nextFile();
			if (block.data.BlockDelay > 0)
			{
				blockDelay = block.data.BlockDelay;
			}
			
		}
		
		if (blockDelay == 0)
		{
			blockDelay = this.layout.DefaultBlockDelay;
		}
		
		this.currentBlockDelay = blockDelay;
		
		
		
	}
		

}

Layout.prototype.continueBlocks = function()
{
	var layout = this;
	
	var delay = function() { if (layout.blockTimerID != 0) {layout.playNextBlock();} };
		
	//now do this again after waiting for the specified time
	/*
	if (this.slideshow.data.Layouts.length == 1 && this.data.Blocks.length == 1)
	{
		setTimeout(delay, 1000);
	}
	else 
	{
	*/
		this.blockTimerID = setTimeout(delay, this.currentBlockDelay);
	//}

}

Layout.prototype.stopTimers = function()
{
	clearTimeout(this.blockTimerID);
	this.blockTimerID = 0;

	clearTimeout(this.layoutTimerID);
	this.layoutTimerID = 0;
	
	var txt = "";
	for(var i=0; i < this.heap.size(); i++)
	{
		var b = this.heap.peekAt(i).data;
		b.stopTimers();
		
		//this.heap.peekAt(i).data.stopTimers();
	}
	
	
}



function Block(blockData, lout)
{
	
	this.liveFiles = new Array();
	
	this.timerID = 0;
	
	this.didStop = false;
	
	this.layout = lout;
	this.data = blockData;
	
	//alert(this.data.Files.length);
	
	this.display = document.createElement("div");
	this.display.className = "blockHolder";
	this.display.style.width = this.data.Width+"px";
	this.display.style.height = this.data.Height+"px";
	
	this.currentIndex = -1;
	this.totalFiles = this.data.Files.length;
	

	if (this.data.BlockDelay == 0)
	{
		this.data.BlockDelay = this.layout.DefaultBlockDelay;
		
	}
	

}

Block.prototype.stopTimers = function()
{
	this.didStop = true;
	for(var i=0; i < this.liveFiles.length; i++)
	{
		this.liveFiles[i].stopTimer();
	}
}

Block.prototype.getContent = function()
{
	return this.display;
}

Block.prototype.getPlayOrder = function()
{
	return this.data.PlayOrder;
}

Block.prototype.getLayoutOrder = function()
{
	return this.data.LayoutOrder;
}

Block.prototype.hasEnded = function()
{
	return (this.currentIndex == this.data.Files.length);
}

Block.prototype.nextFile = function()
{
	
	//load the next file
	//fade in over the previous file
	//remove any other file from the dom( only leave current and prev )
	
	this.currentIndex++;
	
	if (this.currentIndex < this.data.Files.length)
	{
		//load the next file
		var nextFile = new File(this.data.Files[this.currentIndex], this.data.Width, this.data.Height, this);
		
		this.liveFiles.push(nextFile);
		
		this.display.appendChild(nextFile.getContent());
		
		if (nextFile.data.PopupContent != "")
		{
			showPopups.push(nextFile);
		}
		
		while(this.display.childNodes.length > 3)
		{
			this.display.removeChild(this.display.childNodes[0]);
			this.liveFiles.shift();
		}
		
		/*
		var delay = function() { nextFile.fadeIn(); };

		var fileDelay = 600;
		setTimeout(delay, fileDelay);
		*/
		
	}
	else 
	{
		this.currentIndex = this.data.Files.length;
		
		if (!this.didStop)
		{
			this.layout.continueBlocks();
		}
	}
		

}




function File(fileData, w, h, b) 
{

	this.timerID = 0;

	this.data = fileData;
	this.block = b;
	
	this.width = w;
	this.height = h;
	
	this.display = document.createElement("div");
	this.display.id = "file_"+this.data.FileID;
	this.display.className = "fileHolder";
	
	
	
	if (this.data.Hyperlink != "")
	{
		this.display.onclick = fileClicked;
		this.display.style.cursor="pointer";
		this.display.id = this.data.FileID;
		
		var hdnVal = document.createElement("input");
		hdnVal.type = "hidden";
		hdnVal.value = this.data.Hyperlink;
		hdnVal.id = "url_"+this.data.FileID;
		
		this.display.appendChild(hdnVal);
		
	}
	
	var disp = this.display;
	var fi = this;
	
	var img = new Image();
	img.src = "http://www.xsurfaces.com/login/home_page/data/files/file_"+this.data.FileID+"."+this.data.Extension;
	
	img.addEventListener('load', function(e) { disp.appendChild(img);fi.fadeIn(); }, false);
	
	//this.display.appendChild(img);
	

}

File.prototype.stopTimer = function()
{
	clearTimeout(this.timerID);
	this.timerID = 0;
}

File.prototype.fadeIn = function()
{
	
	this.display.style.opacity = 1.0;
	
	var b = this.block;
	var fileDelay = this.data.FileDelay*1000;
	var fileRef = this;
	if (fileDelay == 0)
	{
		fileDelay = 1000;
	}
	
	
	
	var delay = function() { if (fileRef.timerID != 0) {b.nextFile();} };
	
	this.timerID = setTimeout(delay, fileDelay);

}

File.prototype.showPopup = function()
{
	
	var popupHeight = 50;

	var p = document.createElement("div");
	p.className = "popupHolder";
	p.style.width = this.width+"px";
	p.style.height = popupHeight+"px";
	p.style.position = "absolute";
	p.style.top = (this.height-popupHeight)+"px";
	p.style.backgroundColor = "#000000";
	p.style.color = "#ffffff";
	p.style.fontSize = 12;
	p.style.fontFamily = "Arial";
	p.style.textAlign = "center";
	
	var pc = document.createElement("div");
	pc.style.width = this.width+"px";
	pc.style.height = popupHeight+"px";
	pc.style.position = "absolute";
	pc.style.top = (this.height-popupHeight)+"px";
	pc.style.color = "#ffffff";
	pc.style.textAlign = "center";

	var sp = document.createElement("div");
	sp.className = "popupText";
	sp.innerHTML = this.data.PopupContent;
	sp.style.paddingTop = "15px";
	
	
	pc.appendChild(sp);
	
	
	this.display.appendChild(p);
	this.display.appendChild(pc);
	
	
	var delay = function() { p.style.opacity = 0.4;sp.style.opacity = 0.8; };
	setTimeout(delay, 200);
	
}


File.prototype.getContent = function()
{
	
	return this.display;	

}

function Element(obj, p)
{
	this.data = obj;
	this.priority = p;
}
//-1: this is less than other
// 0: this is equal to other
// 1: this is greater than other
Element.prototype.compare = function(other)
{
	return this.priority - other.priority;
}



function BinaryHeap()
{
	
	
	this.heap = new Array();
	
}


	
//add a single Element<E> to the heap
//bubbleUp is called after each add
BinaryHeap.prototype.add = function(element)
{
	this.heap.push(element);
	this.bubbleUp(this.heap.length-1);
	
}
	
	
	
BinaryHeap.prototype.peek = function()
{
	if (this.heap.length > 0)
	{
		return this.heap[0];
	}
		
	return null;
}

BinaryHeap.prototype.peekAt = function(index)
{
	return this.heap[index];
}
	
	//remove the root and return it
BinaryHeap.prototype.remove = function()
{
	if (this.heap.length == 0)
	{
		return null;
	}
	else
	{
		
		
		var element = this.heap[0];
		this.swap(0, this.heap.length-1);
		this.heap.pop();
		this.bubbleDown(0);
		
		return element;
	}
}
	
BinaryHeap.prototype.size = function()
{
	return this.heap.length;
}
	
	//swap item at index 1 with item at index 2
BinaryHeap.prototype.swap = function(item1, item2)
{
	
	var temp = this.heap[item1];
	this.heap[item1] = this.heap[item2];
	this.heap[item2] = temp;
	
	
	
}
	
//this returns the parent index for the given index
BinaryHeap.prototype.parentIndex = function(i)
{
	return Math.floor((i-1)/2);
}
	
//these return the left and right child indexes
BinaryHeap.prototype.rightChildIndex = function(i)
{
	return 2*i+2;
}
BinaryHeap.prototype.leftChildIndex = function(i)
{
	return 2*i+1;
}
	
	
//after any changes to the heap, go through and make it a valid BinaryHeap
//index is the index of a child
BinaryHeap.prototype.bubbleUp = function(index)
{
	//make sure the last element added is in the right place
	//if index is 0, then stop bubbling
	if (index > 0)
	{
		var parentI = this.parentIndex(index);
		var childIndex = this.rightChildIndex(parentI);
		var leftI = this.leftChildIndex(parentI);
		
		if (childIndex >= this.heap.length)
		{
			childIndex = leftI;
		}
		else
		{
			var compareChildren = this.compare(this.heap[leftI], this.heap[childIndex]);
			
			if (compareChildren >= 0)
			{
				childIndex = leftI;
			}
		}
		
		var c = this.compare(this.heap[parentI], this.heap[childIndex]);
		//if parent is smaller than child
		if (c < 0)
		{
			this.swap(parentI, index);
			this.bubbleUp(parentI);
		}
	}
	
}
	
//index is the index of a parent
BinaryHeap.prototype.bubbleDown = function(index)
{
	//ensure BinaryHeap properties after root is deleted	
	//stop the recursion when we've reached the last level
	
	if (index < this.heap.length-1)
	{
		
		var childIndex = this.rightChildIndex(index);
		var leftIndex = this.leftChildIndex(index);
		
		
		//if the rightChildIndex is out of bounds, then use the leftIndex
		if (childIndex > this.heap.length-1)
		{
			childIndex = leftIndex;
		}
		else
		{
			//compare the right child with the left child
			//take the one that has the highest priority
			//if they are equal in priority, take the left child
			//	-this will ensure the first one in has highest priority
			if (this.compare(this.heap[leftIndex], this.heap[childIndex]) >= 0)
			{
				childIndex = leftIndex;
			}
		
		}
		
		
		//check to make sure the childIndex is in bounds
		if (childIndex < this.heap.length)
		{
			//if the child has higher priority, swap it with the parent
			var compareChild = this.compare(this.heap[childIndex], this.heap[index]);
			
			if (compareChild > 0)
			{
				this.swap(childIndex, index);
				this.bubbleDown(childIndex);
			}
		}
	
	}
	
	
}
	
//use this function to determine how elements compare to each other
// for sorting. 
//-1: element 1 is less than element 2
// 0: element 1 is equal to element 2
// 1: element 1 is greater than element 2
BinaryHeap.prototype.compare = function(element1, element2) 
{
	
	//we want the smallest element to be the biggest
	//so multiply by -1

	return -1*(element1.compare(element2));
	
}
	

