/*
	Image Cross Fade Redux, Version 1.0, Last revision: 02.15.2006, steve@slayeroffice.com
	Based upon v2 from:  http://slayeroffice.com/code/imageCrossFade/xfade2.html
	[2009-02-10] NathanLocke.com
		Repackaged with heavy revisions and more exposed setting variables / features.
		The psuedo-class container architecture now supports multiple cross-fading areas.
	NOTE: someday, should convert the xfaders_add to accept a hash as a parameter
*/


// these are the defaults used when a parameter wasn't specified
// note: maybe these shouldn't be in the global scope, but whatever
var xfader_default = {
	'div':                 'imageContainer',
	'time_before_start':   2000,
	'time_display':        2000,
	'time_fade':           2000, // ignored when time_fade_step and opacity_increment are specified
	'time_fade_step':      50,
	'opacity_increment':   0.05,
	'num_cycles':          0,  // how many times to show each image (0 = infinite)
	'cycle_num':           0,  // track the number of cycles
};


// item constructor: returns a cross-fader item (use with 'new')
function xfader( div, time_display, time_fade, time_before_start, time_fade_step, opacity_increment, num_cycles )
{
	// utils for determining initial values from parameters of default
	function u_null( variable )
	{
		return (typeof(variable)=="undefined" || variable==null) ? true : false;
	}
	function param_or_default( name, value )
	{
		return u_null(value) ? xfader_default[name] : value;
	}

	// parameters
	this.div = param_or_default('div', div);
	this.time_before_start =    param_or_default( 'time_before_start',    time_before_start );
	this.time_fade =            param_or_default( 'time_fade',            time_fade );
	this.time_fade_step =       param_or_default( 'time_fade_step',       time_fade_step );
	this.time_display =         param_or_default( 'time_display',         time_display );
	this.opacity_increment =    param_or_default( 'opacity_increment',    opacity_increment );
	this.num_cycles =           param_or_default( 'num_cycles',           num_cycles );

	// values
	this.index_current = 0;
	this.index_next = 0;
	this.cycle_num = 0;
	this.images = new Array();

	// methods
	this.init = xfader_init;
	this.fade = xfader_fadestep;
	this.set_opacity = xfader_set_opacity;
}


// item method: initialize this xfader (after the doc loads)
function xfader_init()
{
	// when to bail
	if( !document.getElementById || !document.createElement )
	{
		return;
	}

	// get a list of the images we'll be cross-fading
	div = document.getElementById(this.div);
	if( !div ) return;  // nothing to init
	this.images = div.getElementsByTagName("img"); // probably should use prototype for this someday
	if( !this.images || this.images.length < 1 ) return;  // nothing to init

	// add an extra memory property; starting at zero
	for( var i=1, num=this.images.length; i<num; i++ )
	{
		this.images[i]._opacity = 0;
	}

	// except the first image, that one we should be able to see right off
	if( this.images.length > 1 )
	{
		this.images[0].style.display = "block";
		this.images[0]._opacity = .99;
		this.index_next = 1;

		// actually kick off the fading(s)
		window.setTimeout( this.fade, this.time_before_start, this );
	}
}


// item method - one timer callback here per increment of the cross-fade
function xfader_fadestep( xf )
{
	// determine the opacities for the images being faded in and out
	if( !xf.images || xf.images.length < 2 || xf.index_current == xf.index_next )
	{
		return;
	}

	// set the new fade levels
	fadeout_opacity = xf.images[xf.index_current]._opacity;
	fadein_opacity = xf.images[xf.index_next]._opacity;
	fadeout_opacity -= xf.opacity_increment;
	fadein_opacity += xf.opacity_increment;
	xf.images[xf.index_current]._opacity = fadeout_opacity;
	xf.images[xf.index_next]._opacity = fadein_opacity;

	// do the opacity set
	if( xf.images[xf.index_next].style.display != "block" )
	{
		xf.images[xf.index_next].style.display = "block"; // be sure it isn't still 'none'
	}
	xf.set_opacity( xf.images[xf.index_current] );
	xf.set_opacity( xf.images[xf.index_next] );

	// start the next fade
	if( fadeout_opacity <= 0 )
	{
		xf.images[xf.index_current].style.display = "none";
		xf.index_current = xf.index_next;
		xf.index_next = xf.images[xf.index_current+1] ? xf.index_current+1 : 0;
		if( xf.num_cycles > 0 && xf.index_next == 0 )
		{
			xf.cycle_num += 1; // don't always increment to avoid overflowing this counter when there's no limit
		}
		if( xf.cycle_num < 1 || xf.cycle_num < xf.num_cycles )
		{
			setTimeout( xf.fade, xf.index_next==0 ? xf.time_before_start : xf.time_display, xf );
		}

	}

	// quick delay and then contine the next fade step
	else
	{
		setTimeout( xf.fade, xf.time_fade_step, xf );
	}
}


// item method to do the actual opacity/fade element style adjustment
function xfader_set_opacity( obj )
{
	// correct for over-adjustments
	if( obj._opacity > .99 ) {
		obj._opacity = .99;
		return;
	}
	if( obj._opacity < 0 ) {
		obj._opacity = 0;
		return;
	}

	// do the style twiddling
	if( obj.style )
	{
		obj.style.opacity = obj._opacity;
		obj.style.MozOpacity = obj._opacity;
		obj.style.filter = "alpha(opacity=" + (obj._opacity*100) + ")";
	}
}


// collection method: managed add
// note: really should upgrade this to accept a hash instead so that one doesn't need to get everything in a certain order, etc.
function xfaders_add( div, time_display, time_fade, time_before_start, time_fade_step, opacity_increment, num_cycles )
{
	this.items[this.items.length] = new xfader( div, time_display, time_fade, time_before_start, time_fade_step, opacity_increment, num_cycles );
	this.num = this.items.length;
}


// collection method: managed remove
function xfaders_remove( index )
{
	// mark this item for omission
	this.items[index] = null;

	// rebuild the items array (of refs), omitting the index
	items = new Array();
	for( var i=0, num=this.items.length; i<num; i++ )
	{
		if( this.items[i] != null )
		{
			items[items.length] = this.items[i];
		}
	}

	// reassign items to the new list
	this.items = items;
}


// collection utility routine: init each item - not a method, but explicitly needs the collection passed in
function xfaders_init( xfs )
{
	if( typeof(xfs)=="undefined" )
	{
		if( !window.xfaders )
		{
			alert("Notice: Unable to initialize the crossfader(s).");
			return false;
		}
		xfs = window.xfaders;
	}
	for( var i=0, num=xfs.num; i<num; i++ )
	{
		xfs.items[i].init();
	}
	return true;
}


// collection constructor
function xfaders_setup( bQueueInit )
{
	// setup
	this.items = new Array();
	this.num = 0;
	this.add = xfaders_add;
	this.init = xfaders_init;
	this.queue_init = xfaders_queue_init
	this.remove = xfaders_remove;

	// put the collection init util into the queue of things to do after the doc loads
	if( typeof(bQueueInit)=="undefined" || bQueueInit != false )
	{
		this.queue_init();
	}
}


// for the collection
function xfaders_queue_init()
{
	// keep a separate reference or the 'this' in the callback be the window object
	xfs = this;

	// bind the collection init to the doc loaded event
	if( window.addEventListener )
	{
		window.addEventListener("load", function(){ xfs.init(xfs); }, false);
	}
	else
	{
		window.attachEvent("onload", function(){ xfs.init(xfs); } );
	}
}

