/************************************************************************************************
* Rotater v1.1
* February 16, 2009
*
* Author: Byron McGregor
* Website: http://randomous.com/tools/floatbox/
*************************************************************************************************/

function Rotater() {
// set preferences
	this.swapInterval = 10;  // seconds between subsequent image swaps
	this.fadeSpeed = 5;  // 0 to 10, 10 is slowest
	this.className = 'rotater';  // class name of divs containing our rotation sets
	this.useFloatbox = true;  // floatbox-enable the rotater images
	this.groupBy = 'all';  // 'div' creates a fb gallery for each rotation set.  'all' gathers all rotation sets in one big gallery.
	this.fbOptions = 'doSlideshow:true';  // see floatbox docs for details
// end set preferences

	if (document.all && !window.opera) {
		var version = navigator.appVersion;
		this.ieOld = parseInt(version.substr(version.indexOf('MSIE') + 5), 10) < 8;  // less than v8.0
	}

// get preference overrides from page
	if (window.rotPreferences) {
		if (rotPreferences.initialWait) this.initialWait = rotPreferences.initialWait;
		if (rotPreferences.swapInterval) this.swapInterval = rotPreferences.swapInterval;
		if (rotPreferences.fadeSpeed) this.fadeSpeed = rotPreferences.fadeSpeed;
		if (rotPreferences.className) this.className = rotPreferences.className;
		if (rotPreferences.useFloatbox) this.useFloatbox = rotPreferences.useFloatbox;
		if (rotPreferences.groupBy) this.groupBy = rotPreferences.groupBy;
		if (rotPreferences.fbOptions) this.fbOptions = rotPreferences.fbOptions;
	}

	this.divs = [];  // our main data repository
	var classNodes = this.getElementsByClassName(this.className),  // all nodes (of any type) with our className
		divNodes = [];  // for div nodes (and only div nodes) with our className on them
// gather all divs with our className into the divNodes[] array
	for (var i = 0; i < classNodes.length; i++) {
		if (classNodes[i].tagName.toLowerCase() === 'div') {
			divNodes.push(classNodes[i]);
		}
	}

// gather all classy divs that have more than one anchor with a thumb into the divs[] array
	for (var i = 0; i < divNodes.length; i++) {
		var div = divNodes[i],
			anchorNodes = div.getElementsByTagName('a'),
			anchors = [],
			thumbs = [],
			showing;
		// gather anchor/thumb pairs that live inside this div node
		for (var j = 0; j < anchorNodes.length; j++) {
			var anchor = anchorNodes[j],
				img = (anchor.getElementsByTagName('img') || [])[0];  // the first thumbnail image in the anchor
			if (img) {
				if (anchor.offsetWidth) showing = anchors.length;  // should be only one image that's not hidden
				anchors.push(anchor);
				thumbs.push(img);
			}
		}
		// if we've got more than one anchor/img pair, save this div record to the data repository
		if (anchors.length > 1) {
			var guid = this.groupBy === 'all' ? this.className : (Math.random() + '').substring(2);
			for (var j = 0; j < anchors.length; j++) {
				var anchor = anchors[j];
				if (this.useFloatbox) {  // set up attributes for floatbox
					anchor.className = (anchor.className ? anchor.className + ' ' : '') + 'floatbox.' + guid;
					anchor.rev = this.fbOptions +
						' beforeBoxStart:`rot.stop();`' +
						' onItemStart:`rot.preload(' + this.divs.length + ',' + j + ');`' +
						' beforeBoxEnd:`rot.show(' + this.divs.length + ',' + j + ',true);`' +
						' afterBoxEnd:`rot.go();`';
				}
			}
			if (this.useFloatbox && window.fb) {
				fb.preloadAll = false;  // turn off fb's aggressive preloading
				fb.tagAnchors(div);  // register the anchors in this div
			}
			this.divs.push({
				anchors: anchors,  // all the usable anchors in this div
				thumbs: thumbs,  // the thumbs inside the usable anchors
				showing: showing,  // index of the currently displayed anchor
				preloader: new Image()  // for preloading anchor reference at each display
			});
		}
	}
	this.preloadNext();  // prep for the first swap
}

Rotater.prototype = {

getElementsByClassName: function(name) {
	if (document.getElementsByClassName) {
		return document.getElementsByClassName(name);
	} else {
	    var all = document.all || document.getElementsByTagName('*'),
	    	rex = new RegExp('(^|\\s)' + name + '(\\s|$)', 'i'),
	    	nodes = [];
	    for (var i = 0, len = all.length; i < len; i++) {
	    	if (rex.test(all[i].className)) nodes.push(all[i]);
	    }
	    return nodes;
	}
},

fadeOpacity: function(el, startOp, endOp, duration, callback) {
	var duration = duration || 0;
// fading in?
	var fadeIn = (startOp <= endOp && endOp > 0);
// calc the % increment for each iteration
	if (duration > 10) duration = 10;
	if (duration < 0) duration = 0;
	if (duration === 0) {
		startOp = endOp;
		var incr = 1;  // won't get used, but keep it here for cleanliness
	} else {
// magic log math that yields nice increments.  duration=1 -> incr=50%, 5 -> 9%, 10 -> 1%
		var root = Math.pow(100, 0.1),
			power = duration + ((10 - duration)/9) * (Math.log(2)/Math.log(root) - 1),
			incr = 1/Math.pow(root, power);
	}
	incr /= 8;  // small images need to be slowed down
	if (!fadeIn) incr = -incr;
// set initial endOp values and next timer event
	this.stepFade(el, startOp, endOp, incr, fadeIn, callback);
},  // end fadeOpacity

stepFade: function(el, thisOp, finishOp, incr, fadeIn, callback) {
// Worker bee function for fadeOpacity()
	if (!el) return;  // safety for mid-fade ends
	var that = this;
// don't go beyond the finish state
	if ((fadeIn && thisOp >= finishOp) || (!fadeIn && thisOp <= finishOp)) thisOp = finishOp;
// set opacity styles
	el.style.opacity = thisOp + '';
	if (el.style.removeAttribute) el.style.filter = 'alpha(opacity=' + thisOp*100 + ')';
	if (thisOp === finishOp) {
// we're done. clear ie filter and run on-complete code
		if (finishOp >= 1 && el.style.removeAttribute) el.style.removeAttribute('filter');  // fix for IE alpha opacity filter bug (from lytebox)
		if (callback) callback();
	} else {
// set timer for next step of opacity fade
		setTimeout(function() { that.stepFade(el, thisOp + incr, finishOp, incr, fadeIn, callback); }, 20);
	}
},  // end stepFade

// Hidden thumbs are referenced in the img's "longdesc" attribute
// so that they don't all have to load at page start time.
// Move the path into the src attribute before lighting up the next anchor.
preload: function(iDiv, iAnchor) {
	var div = this.divs[iDiv];
	var img = div.thumbs[iAnchor],
		longdesc = img.getAttribute('longdesc');
	if (longdesc) {  // update the src attribute if not already done
		img.src = longdesc;  // will start loading now
		img.setAttribute('longdesc', '');  // update once only
	}
	// also preload the image pointed to by the this anchor
	div.preloader.src = div.anchors[div.showing].href;
},

preloadNext: function() {
	for (var i = 0; i < this.divs.length; i++) {  // each collection
		var next = this.divs[i].showing + 1;
		if (next >= this.divs[i].thumbs.length) next = 0;  // wrap
		this.preload(i, next);
	}
},

// Display (unhide) the iAnchor'th anchor in the iDiv'th div, fading unless asked not to
show: function(iDiv, iAnchor, noFade) {
	var div = this.divs[iDiv],
		speed = noFade ? 0 : this.fadeSpeed;
	if (document.all) speed--;  // IE and Opera are kinda slow at fades
	if (iAnchor >= div.anchors.length) iAnchor = 0;  // handle wrapping so the caller doesn't have to
	if (iAnchor < 0) iAnchor = div.anchors.length - 1;
	if (iAnchor === div.showing) return;  // don't bother if this one is already showing
	var lastAnchor = div.anchors[div.showing],  // the anchor currently displayed
		nextAnchor = div.anchors[iAnchor],  // the anchor requested for display
		lastText = (lastAnchor.getElementsByTagName('span') || [])[0],
		nextText = (nextAnchor.getElementsByTagName('span') || [])[0],
		lastStyle = lastAnchor.style,  // save some typing
		nextStyle = nextAnchor.style;
	this.fadeOpacity(nextAnchor, 0, 0, 0);  // make sure the new anchor starts fully transparent
	if (nextText) {
		this.fadeOpacity(nextText, 1, 1, 0);  // text was faded to transparent on last rotation
		if (this.ieOld) nextText.style.visibility = 'visible';  // or hidden for IE pre v8
	}
	nextStyle.display = 'block';  // turn off display:none for the next image
	lastStyle.zIndex = '10';  // move the current image below the transparent next image
	nextStyle.zIndex = '20';
	this.fadeOpacity(nextAnchor, 0, 1, speed, function() {  // fade in the next anchor (both image and text)
		lastStyle.display = 'none';
	});
	if (lastText) {
		this.fadeOpacity(lastText, 1, 0, speed - .5);  // fade out the old text a little faster than the new fade in
		if (this.ieOld) lastText.style.visibility = 'hidden';  // IE pre v.8 can't opacity fade text
	}
	div.showing = iAnchor;  // update index
	this.preloadNext();  // prep for the next swap
},

// Cycle to the next anchor in all divs
showNext: function() {
	for (var i = 0; i < this.divs.length; i++) {
		this.show(i, this.divs[i].showing + 1);
	}
},

go: function() {
	var that = this;
	this.stop();
	this.timer = setInterval(function() {
		that.showNext();
	}, that.swapInterval * 1000);
},

stop: function() {
	clearInterval(this.timer);
}

};  // end prototype

// initialize rotater on window.onload
(function () {
	var rotInit = function() { (rot = new Rotater()).go(); };
	if (window.addEventListener) {
		window.addEventListener('load', rotInit, false);
	} else if (window.attachEvent) { 
		window.attachEvent('onload', rotInit);
	}
})();
