/* Content Carousel			*/
/* (c) 2008 Vortx, Inc. */

// **************************************************************************************************************************
// Slideshow controller
// **************************************************************************************************************************
function ContentCarousel(content, controls)
{
	var contentElement = content, controlsElement = controls;
	var autoStart, loop, displayTime, fadeSteps, fadeTime, userPauseTime, autoStartAfterDelay;

	if(content.nodeType != 1)
	{
		// Content is a configuration object
		contentElement = content['content'];
		
		if(content['controls'] != null)			// If a config object controls overrides controls param if it's given.
			controlsElement = content['controls'];

		loop = content['loop'];
		displayTime = content['displayTime'];
		fadeSteps = content['fadeSteps'];
		fadeTime = content['fadeTime'];
		userPauseTime = content['userPauseTime'];
		autoStart = content['autoStart'];
		autoStartAfterDelay = content['autoStartAfterDelay'];
	}

	// Create the content blocks from the immediate children of the content element.
	this._containerElement = contentElement;
	this._containerElement.style.position = "relative";

	// Construct the array of blocks. Use all of the immediate children except text nodes with only whitespace.
	this._blocks = new Array();
	for(var i = 0; i < this._containerElement.childNodes.length; i++)
		if(!(this._containerElement.childNodes[i].nodeType == 3 && !this._containerElement.childNodes[i].nodeValue.match(/\w+/)))
			this._blocks.push(new ContentCarouselBlock(this._containerElement.childNodes[i]));
	
	this.blockCount = this._blocks.length;

	// Attach the controls if we were passed any
	if (controlsElement != null)
	    this._controls = new ContentCarouselControls(controlsElement, this);

	// Show the first block by default
	this._currentBlock = this._blocks[0];
	this._currentBlock.display();

	// Set up the container sizer
	this._containerElementSizer = document.createElement("div");
	this._containerElement.appendChild(this._containerElementSizer);
	this._sizeContainer();

	// Contruct the transitions list
	this._buildTransitions();
	
	// Apply options
	if(loop != null)
		this.setLoop(loop);

	if(displayTime != null)
		this.setDisplayTime(displayTime);
	
	if(fadeSteps != null)
		this.setFadeSteps(fadeSteps);

	if(fadeTime != null)
		this.setFadeTime(fadeTime);

	if(userPauseTime != null)
		this.setUserPauseTime(userPauseTime);

    if(autoStartAfterDelay == null)
		autoStartAfterDelay = 0;

	if(autoStart != null && autoStart === true)
		this._delayedStart(autoStartAfterDelay);

}

ContentCarousel.prototype._displayTimer = null;			// Timer to display image
ContentCarousel.prototype._displayTime = 2500;			// Display time in ms. This is the time the image sits unfaded.
ContentCarousel.prototype._fadeTime = 250;				// Fade duration in ms. This is how long the fade takes.
ContentCarousel.prototype._fadeSteps = 10;				// Number of times to adjust fade over fade duration - higher value means smoother fade, but too hight can lead to performance problems.
ContentCarousel.prototype._userPauseTime = 5;		// How long to pause the slideshow when the user pauses
ContentCarousel.prototype._loop = true;					// Whether to start again at the beginning when the end is reached
ContentCarousel.prototype._blocks = null;				// The blocks to fade between and associated variables
ContentCarousel.prototype._transitionsHead = null;
ContentCarousel.prototype._currentTransition = null;
ContentCarousel.prototype._currentBlock = null;
ContentCarousel.prototype._controls = null;				// Controls
ContentCarousel.prototype._running = false;				// Status
ContentCarousel.prototype._containerElement = null;
ContentCarousel.prototype._containerElementSizer = null;
ContentCarousel.prototype.blockCount = 0;

ContentCarousel.prototype.start = function()
{
	this._stop();
	this._start();
}

ContentCarousel.prototype.delayedStart = function(delay)
{
	this._stop();
	this._delayedStart(delay);
}

ContentCarousel.prototype.resume = function()
{
	this._stop();
	this._delayedStart(0);
}

ContentCarousel.prototype.stop = function()
{
	this._stop();
}

ContentCarousel.prototype.next = function()
{
	var wasRunning = this._running;

	this._stop();
	this._next();
	
	if(wasRunning)
		this._delayedStart(this._userPauseTime);
}

ContentCarousel.prototype.previous = function()
{
	var wasRunning = this._running;

	this._stop();
	this._previous();
	
	if(wasRunning)
		this._delayedStart(this._userPauseTime);
}	

ContentCarousel.prototype.jump = function(index)
{
	var wasRunning = this._running;

	this._stop();
	this._transitionToIndex(index);
	
	if(wasRunning)
		this._delayedStart(this._userPauseTime);
}

ContentCarousel.prototype._start = function()
{
	this._delayedStart(this._displayTime);
}

ContentCarousel.prototype._delayedStart = function(delay)
{
	var me = this;
	this._running = true;
	this._displayTimer = setTimeout(function() { me._autoNext(); }, delay);
}

ContentCarousel.prototype._stop = function()
{
	clearTimeout(this._displayTimer);
	this._running = false;
}

ContentCarousel.prototype._autoNext = function()
{
	var me = this;
	if(this._next())
		this._displayTimer = setTimeout(function() { me._autoNext(); }, this._displayTime);
}

ContentCarousel.prototype._next = function()
{
	if(this._currentTransition == null)					// Loop if we're at the end
		this._currentTransition = this._transitionsHead;
	else if(this._currentTransition.data.inProgress)	// Don't transition while another is in progress
		return true;

   	// Update controls
    if(this._currentTransition.data.controlsBlock != null)
    {
    	this._currentTransition.data.controlsBlock.normal();
    	this._currentTransition.data.controlsBlock.enableHover();
	}
	
	// Move to previous
	this._currentTransition = this._currentTransition._next;

	// If there's a problem, leave
	if(this._currentTransition == null)
		return false;

	// Fade to the next (now current) item
	this._currentTransition.data.fade(this._currentBlock);
	this._currentBlock = this._currentTransition.data._targetBlock;
	this._sizeContainer();
	
	return true;
}

ContentCarousel.prototype._previous = function()
{
	if(this._currentTransition == null)					// Loop if we're at the beginning
		this._currentTransition = this._transitionsHead;
	else if(this._currentTransition.data.inProgress)	// Don't transition while another is in progress
		return true;
	
	// Update controls
	if(this._currentTransition.data.controlsBlock != null)
    {
		this._currentTransition.data.controlsBlock.normal();
		this._currentTransition.data.controlsBlock.enableHover();
	}
	
	// Move to previous
	this._currentTransition = this._currentTransition._previous;

	// If there's a problem, leave
	if(this._currentTransition == null)
		return false;

	// Fade to the previous (now current) item
	this._currentTransition.data.fade(this._currentBlock);
	this._currentBlock = this._currentTransition.data._targetBlock;
	this._sizeContainer();

	return true;
}

ContentCarousel.prototype._transitionToIndex = function(index)
{
	// Don't allow a transition while another is currently in progress
	if(this._currentTransition != null && this._currentTransition.data.inProgress)
		return;
	
	// Start at the beginning of the list
	var current = this._transitionsHead;
	
	// Iterate through the list until we've gone throught the number of nodes passed in
	for(var i = 0; i < index && current != null; i++)
		current = current._next;

	// Check to make sure we don't transition back to the same block
	if(current == null || current.data._targetBlock == this._currentBlock) 
		return;

	// Update the controls
	if(this._controls != null)
	{		
		if(this._currentTransition != null && this._currentTransition.data.controlsBlock != null)
		{
			this._currentTransition.data.controlsBlock.normal();
			this._currentTransition.data.controlsBlock.enableHover();
		}
		else
		{
			this._controls.revertAll();
		}
	}
	
	// Transition
    this._currentTransition = current;
	this._currentTransition.data.fade(this._currentBlock);
	this._currentBlock = this._currentTransition.data._targetBlock;
	this._sizeContainer();
}

ContentCarousel.prototype.setLoop = function(loop)
{
	this._loop = loop;
	this._buildTransitions();
}

ContentCarousel.prototype.getLoop = function()
{
	return this._loop;
}

ContentCarousel.prototype.setDisplayTime = function(time)
{
	this._displayTime = time;
}

ContentCarousel.prototype.getDisplayTime = function()
{
	return this._displayTime;
}

ContentCarousel.prototype.setFadeSteps = function(steps)
{
	this._fadeSteps = steps;
	this._buildTransitions();
}

ContentCarousel.prototype.getFadeSteps = function()
{
	return this._fadeSteps;
}

ContentCarousel.prototype.setFadeTime = function(time)
{
	this._fadeTime = time;
	this._buildTransitions();
}

ContentCarousel.prototype.getFadeTime = function()
{
	return this._fadeTime;
}

ContentCarousel.prototype.setUserPauseTime = function(time)
{
	this._userPauseTime = time;
}

ContentCarousel.prototype.getUserPauseTime = function()
{
	return this._userPauseTime;
}

ContentCarousel.prototype._buildTransitions = function()
{
	this._transitionsHead = null;
	var last = null;
	var current = null
	
	for(var i = 0; i < this._blocks.length; i++)
	{
		// Set the Data
		var ctlBlk = null
		if(this._controls != null)
			ctlBlk = this._controls.blocks[i];
			
		current = new ContentCarouselList(new ContentCarouselTransition(this._blocks[i], this._fadeTime / this._fadeSteps, 1 / this._fadeSteps, ctlBlk));
		
		// Set link between the previous item
		if(last != null)
		{
			last._next = current;
			current._previous = last;
		}
	
		// Set the head if not yet initialized
		if(this._transitionsHead == null)
			this._transitionsHead = current;

		last = current;
	}
	
	if(this._loop && last != null)
	{
		last._next = this._transitionsHead;
		this._transitionsHead._previous = last;
	}
}

ContentCarousel.prototype._sizeContainer = function()
{
	// Size to the current block
	this._containerElementSizer.style.width = this._currentBlock.element.clientWidth + "px";
	this._containerElementSizer.style.height = this._currentBlock.element.clientHeight + "px";
}

// **************************************************************************************************************************
// Image Fader Controls coordinator
// **************************************************************************************************************************
function ContentCarouselControls(controls, faderObject)
{
	var onBlock, offBlock, imageIdx = 0;
    this.blocks = new Array();
	
	if(controls instanceof Array)
	{
		// Array of distinct functions
		for(var n = 0; n < controls.length; n++)
			this.blocks.push(new ImageControlsFunction(controls[n].normal, controls[n].active, n));
	}
	else if(controls.nodeType == 1)
	{
		// HTML Element
		controls.style.position = "relative";
	
		for(var i = 0; i < controls.childNodes.length; i++)
		{
			// Find an element
			if(controls.childNodes[i].nodeType == 1)
			{
				// Save the first actual element
				onBlock = controls.childNodes[i];
				
				// Iterate until we find another one
				do
				{
					i++;
				}
				while(controls.childNodes[i].nodeType != 1)
				
				// Create an image control block
				offBlock = controls.childNodes[i];
				this.blocks.push(new ImageControlsBlock(onBlock, offBlock, faderObject, imageIdx));
				imageIdx++;
			}
		}
	}
	else
	{
		// Global functions
		for(var n = 0; n < faderObject.blockCount; n++)
			this.blocks.push(new ImageControlsFunction(controls.normal, controls.active, n));
	}
	
	// Show the first block
	this.blocks[0].active();
	this.blocks[0].disableHover();
	
	this.revertAll = function()
	{
		for(var i = 0; i < this.blocks.length; i++)
		{
			this.blocks[i].normal();
			this.blocks[i].enableHover();
		}
	}
}

ContentCarouselControls.prototype.blocks = null;

// **************************************************************************************************************************
// Image Fader Controls Block
// **************************************************************************************************************************
function ImageControlsBlock(elementNormal, elementActive, faderObject, imageIndex)
{
    this._elementNormal = elementNormal;
    this._elementActive = elementActive;
	this._faderObject = faderObject;
	this._imageIndex = imageIndex;

	var me = this;
    this._elementActive.onclick = function(){ me._click(); }

	this.enableHover();
    this.normal();
}

ImageControlsBlock.prototype._elementNormal = null;
ImageControlsBlock.prototype._elementActive = null;
ImageControlsBlock.prototype._faderObject = null;
ImageControlsBlock.prototype._imageIndex = 0;

ImageControlsBlock.prototype.active = function()
{
    this._elementNormal.style.display = "none";
    this._elementActive.style.display = "inline";
}
ImageControlsBlock.prototype.normal = function()
{
    this._elementActive.style.display = "none";
    this._elementNormal.style.display = "inline";
}

ImageControlsBlock.prototype._click = function()
{
    this._faderObject.jump(this._imageIndex);
}

ImageControlsBlock.prototype.disableHover = function()
{
    this._elementNormal.onmouseover = null;
    this._elementActive.onmouseout = null;    
}

ImageControlsBlock.prototype.enableHover = function()
{
	var me = this;
    this._elementNormal.onmouseover = function(){ me.active(); }
    this._elementActive.onmouseout = function(){ me.normal(); }
}

// **************************************************************************************************************************
// Image Fader Controls Function
// **************************************************************************************************************************
function ImageControlsFunction(functionNormal, functionActive, index)
{
    this._functionNormal = functionNormal;
    this._functionActive = functionActive;
    this._index = index;
}

ImageControlsFunction.prototype._functionNormal = null;
ImageControlsFunction.prototype._functionActive = null;
ImageControlsFunction.prototype._index = null;

ImageControlsFunction.prototype.active = function()
{
	if(this._functionActive)
	    this._functionActive(this._index);
}

ImageControlsFunction.prototype.normal = function()
{
	if(this._functionNormal)
	    this._functionNormal(this._index);
}

ImageControlsFunction.prototype.disableHover = function()
{ }

ImageControlsFunction.prototype.enableHover = function()
{ }

// **************************************************************************************************************************
// Represents an element to fade between
// **************************************************************************************************************************
function ContentCarouselBlock(element)
{
	this.element = element;
	this.element.style.position		= "absolute";
	this.element.style.top			= "0px";
	this.element.style.left			= "0px";
	this.element.style.width		= "100%";
	this.element.style.height		= "100%";
	this.element.style.width		= "auto";
	this.element.style.height		= "auto";
	this.element.style.display		= "none";
	this.element.style.opacity		= "1.0";
	this.element.style.filter		= "progid:DXImageTransform.Microsoft.Alpha(opacity = 100)";
	this.element.style.zIndex		= "0";

	if(this.element.filters != null)
		this.useFilters = true;
}

ContentCarouselBlock.prototype.element = null;
ContentCarouselBlock.prototype._useFilters = false;		// Determines if we should use IE's method of transparency (alpha filter)

ContentCarouselBlock.prototype.setOpacity = function(opacity)
{
	// Browser dependent opacity setting
	if(this._useFilters)	// IE
		this.element.filters.item("DXImageTransform.Microsoft.Alpha").opacity = opacity * 100;
	else					// Opacity CSS attribute
		this.element.style.opacity = opacity;
}

ContentCarouselBlock.prototype.display = function()
{
	this.element.style.display = "block";
}

ContentCarouselBlock.prototype.hide = function()
{
	this.element.style.display = "none";
}

// **************************************************************************************************************************
// Defines a transition to an ContentCarouselBlock
// **************************************************************************************************************************
function ContentCarouselTransition(targetBlock, fadeStepTime, fadeStepAmount, controlsBlock)
{
	this._fadeStepTime = fadeStepTime;				
	this._fadeStepAmount = fadeStepAmount;			
	this._CurrentOpacity = 1.0;
	this._startBlock = null;							
	this._targetBlock = targetBlock;
	this.controlsBlock = controlsBlock;
}

ContentCarouselTransition.prototype._fadeStepTime = 0;			// The amount of time between each step in the crossfade
ContentCarouselTransition.prototype._fadeStepAmount = 0;			// The amount of fade to apply for each step in the crossfade
ContentCarouselTransition.prototype._CurrentOpacity = 1.0;		// Holds the opacity of the displayed image. Used to avoid excessive casting.
ContentCarouselTransition.prototype._targetBlock = null; 			// The target block to fade to
ContentCarouselTransition.prototype._startBlock = null;			// The target block to fade from
ContentCarouselTransition.prototype._fadeTimer = null;
ContentCarouselTransition.prototype.inProgress = null;
ContentCarouselTransition.prototype.controlsBlock = null;
ContentCarouselTransition.ZIndex = 65536;

ContentCarouselTransition.prototype.fade = function(startBlock)
{
	this.inProgress = true;

	this._startBlock = startBlock;
	this._startBlock.element.style.zIndex = "1";
	this._startBlock.setOpacity(1.0);
	this._startBlock.display();

	this._targetBlock.display();
	this._targetBlock.element.style.zIndex = "0";
    
    if (this.controlsBlock != null)
    {
        this.controlsBlock.active();
        this.controlsBlock.disableHover();
    }
	
	this._fadeStep();
}

ContentCarouselTransition.prototype._fadeStep = function()
{
	var me = this;
	
	// Adjust opacity
	this._CurrentOpacity -= this._fadeStepAmount;
	this._startBlock.setOpacity(this._CurrentOpacity);
	
	// Keep adjusting till we're done
	if(this._CurrentOpacity <= 0.0)
		this._finalizeTransition();
	else
		this._fadeTimer = setTimeout(function() { me._fadeStep(); }, this._fadeStepTime);
}

ContentCarouselTransition.prototype._finalizeTransition = function()
{
	// Clear stuff off of current image
	this._CurrentOpacity = 1.0;
	this._startBlock.element.style.zIndex = "0";
	this._startBlock.setOpacity(1.0);
	this._startBlock.hide();
	
	this.inProgress = false;
}

// **************************************************************************************************************************
// Doubly linked list node
// **************************************************************************************************************************
function ContentCarouselList(data)
{
	this.data = data;
}

ContentCarouselList.prototype.data = null;
ContentCarouselList.prototype.next = null;
ContentCarouselList.prototype.previous = null;