/************************ -- Std obj extension ************************/
// Standard indexOf method
if (!Array.prototype.indexOf)
{
	Array.prototype.indexOf = function(elt /*, from*/)
	{
		var len = this.length;
	
		var from = Number(arguments[1]) || 0;
		from = (from < 0)
			? Math.ceil(from)
			: Math.floor(from);
		if (from < 0)
			from += len;
	
		for (; from < len; from++)
		{
			if (from in this &&
					this[from] === elt)
				return from;
		}
		return -1;
	};
}
/************************ Std obj extension -- ************************/


/*******************************************
*
*  Slide show library.
*
*  Taihao Zhang
*  Australian National University
*
*******************************************/


/*
 * Slideshow manager
 * Initializes the slide collection and preloads the images.
 */
function Slideshow (id, width, height, delayms) {
	this.slides = new Array();
	
	this.loader = new SlideLoader(this);
	this.animator = new SlideAnimator(id, width, height, delayms);
}
Slideshow.prototype.useDescription = function (classes) {
	// Specify to display a description and set the "slide", "image", and "description" class names.
	// The "slide" is used as a container for fading.
	// The "image" and "description" respectively contain the image and description text.
    // Object keys:
    //   slide, img, desc, link
	this.animator.useDescription(classes);
}
Slideshow.prototype.addSlide = function (slide) {
	if (slide instanceof Slide)
		this.slides.push(slide);
}
Slideshow.prototype.loadJsonObjects = function (jsonObjects) {
	for (var i = 0; i < jsonObjects.length; i++)
		this.addSlide(new Slide(jsonObjects[i].name, jsonObjects[i].url, jsonObjects[i].description, jsonObjects[i].link));
}
Slideshow.prototype.execute = function () {
	// Load slides
	this.loader.load(this.slides);
}
Slideshow.prototype.slidesLoaded = function (readySlides) {
	// Animate the loaded slides
	this.animator.animate(readySlides);
}
Slideshow.prototype.play = function () { if (this.loader.loadedCount > 1) this.animator.play(); }
Slideshow.prototype.stop = function () { if (this.loader.loadedCount > 1) this.animator.stop(); }
Slideshow.prototype.prev = function () { if (this.loader.loadedCount > 1) this.animator.prevSlide(false); }
Slideshow.prototype.next = function () { if (this.loader.loadedCount > 1) this.animator.nextSlide(false); }


/*
 * Slide
 * Contains information about a specific slide.
 */
function Slide (name, url, description, link) {
	this.name = name;
	this.url = url;
	this.description = description;
	this.link = link;
}


/*
 * Slide loader
 * Loads the slide collection.
 */
function SlideLoader (slideshow) {
	this.slideshow = slideshow;
	
	this.loadingSlides = new Array();
	this.readySlides = new Array();
    this.loadedCount = 0;
	this.failedCount = 0;
}
//var debug = document.getElementById("debug");
SlideLoader.prototype.load = function (slides) {
	// Abort if no slides
	if (!(slides instanceof Array) || slides.length == 0) return;
	
	// Copy the slides to load
	this.loadingSlides = slides.slice();
	
	// Setup dynamic reference to self
	var loader = this;
	
	// Load each slide
	for (var i = 0; i < slides.length; i++) {
		// Setup image
		var img = new Image();
		img.slide = slides[i];
        slides[i].img = img;
		
		// Wrap events
		img.onload = function () { loader.imgLoaded(this); }
		img.onerror = function () { loader.imgFailed(this); }
		img.onabort = img.onerror;  // Treat abort as error
		
		// Preload image
		img.src = slides[i].url;
        //debug.innerHTML += "Loading " + img.src + "<br>";
	}
}
SlideLoader.prototype.imgLoaded = function (img) {
	this.readySlides.push(img.slide);
    this.loadedCount++;
    //debug.innerHTML += img.src + " loaded<br>";
	this.checkSequence();
}
SlideLoader.prototype.imgFailed = function (img) {
	this.failedCount++;
    //debug.innerHTML += img.src + " failed<br>";
	this.checkSequence();
}
SlideLoader.prototype.checkSequence = function () {
	// Check loading sequence
	if (this.loadingSlides.length > 0 &&
			this.loadingSlides.length == this.loadedCount + this.failedCount)
		this.slideshow.slidesLoaded(this.readySlides);
}


/*
 * Slide animator
 * Animates the slideshow.
 */
function SlideAnimator (id, width, height, delayms) {
	this.width = width;
	this.height = height;
	this.delay = 15000;
	
	this.slides = new Array();
	
	this.stage = document.getElementById(id);
	this.slideElems = [];
    this.classes = {};
	
	this.playing = false;
	this.currentSlide = 0;
	this.numSlides = 0;
	
    this.fade = !checkIE6();   /* Do not fade if IE6 */
	this.fading = false;
	this.fadeFrame = 0;
	this.fadeMax = 24;
	this.fadeDelay = 50;
}
SlideAnimator.prototype.useDescription = function (classes) {
    if (typeof(classes) != "object") return;
	this.usingDescription = true;
	this.classes = classes;
}
SlideAnimator.prototype.animate = function (slides) {
	// Abort if no slides
	if (!(slides instanceof Array) || slides.length == 0) return;
	
	// Initialize state
	this.slides = slides;
	this.numSlides = slides.length;
	
	this.start();
}
SlideAnimator.prototype.start = function () {
	if (!this.stage) return;
	
	// Setup slides for animation
	
	var slideElem;
	if (this.usingDescription)
		slideElem = this.getElementByClass(this.stage, this.classes.slide);
	else
		slideElem = this.stage.getElementsByTagName("img")[0];
	
	slideElem.style.position = "absolute";
	
	// Setup custom formatting functions
	var setOpacity = function (opacity) {
		this.style.opacity = opacity;
		this.style.MozOpacity = opacity;
		this.style.filter = "alpha(opacity=" + Math.round(opacity*100) + ")";
	}
    var setVisible;
    if (this.fade) {
        setVisible = function (visible) {
            this.style.visibility = (visible ? "visible" : "hidden");
            this.setOpacity(visible ? 1 : 0);
        }
    }
    else {
        setVisible = function (visible) {
            this.style.visibility = (visible ? "visible" : "hidden");
        }
    }
	
	slideElem.setOpacity = setOpacity;
	slideElem.setVisible = setVisible;
	
	// Populate hidden slide elements
	for (var i = 0; i < this.slides.length; i++) {
		var elem;
		if (this.usingDescription) {
			// Deep-copy the existing slide element
			elem = slideElem.cloneNode(true);
			// Retrieve the new children
			var imgElem = this.getElementByClass(elem, this.classes.img);
			var descElem = this.getElementByClass(elem, this.classes.desc);
			var linkElem = this.getElementByClass(elem, this.classes.link);
			if (imgElem == null || descElem == null || linkElem == null) return;  // Sanity check
			//Update the children to reflect the current slide
			imgElem.src = this.slides[i].url;
			descElem.innerHTML = this.slides[i].description;
			linkElem.href = this.slides[i].link;
            // Wrap link around image
            if (this.classes.linkmask) {
                var linkMaskElem = this.getElementByClass(elem, this.classes.linkmask);
                if (linkMaskElem) {
                    // Change link on the mask too
                    linkMaskElem.href = this.slides[i].link;
                }
            }

		} else {
			// Just an image
			elem = document.createElement("a");
			elem.setAttribute("href", this.slides[i].link);
			elemImg = document.createElement("img");
            var imgElem = this.slides[i].img;
			imgElem.setAttribute("width", this.width);
			imgElem.setAttribute("height", this.height);
			imgElem.setAttribute("alt", this.slides[i].name);
			imgElem.setAttribute("title", this.slides[i].description);
			
			elem.appendChild(imgElem);
		}
		
		elem.setOpacity = setOpacity;
		elem.setVisible = setVisible;
		
		elem.seqNo = i;
		elem.style.position = "absolute";
		elem.setVisible(false);
		
		this.slideElems.push(elem);
		this.stage.appendChild(elem);
	}

	// Show first one and hide original image
	this.currentSlide = 0;
	this.slideElems[0].setVisible(true);
	slideElem.setVisible(false);
	
	// Begin
	this.play();
}
SlideAnimator.prototype.play = function () {
	// Abort if no slides to animate
	if (this.numSlides < 2) return;
	
	this.playing = true;
	
	if (!this.fading) {
		// Wait until next slide
		var self = this;
		var advanceSlide = function () {
			if (self.fade)
                self.nextSlide();
            else
                self.nextSlide(false);
		}
        this._slideTimeout = window.setTimeout(advanceSlide, this.delay);
	}
}
SlideAnimator.prototype.stop = function () {
	if (this._slideTimeout) window.clearTimeout(this._slideTimeout);
	this.playing = false;
	
	if (this.fading) {
		this.fadeEnded();
	}
}
SlideAnimator.prototype.prevSlide = function (fade) {
	if (this._slideTimeout) window.clearTimeout(this._slideTimeout);
	if (this.fading) {
		// Figure out which of the fading slides to skip to
		if (this.fadeElemNext.seqNo == this.getPrevSlideNo(this.fadeElemCurr.seqNo))
			this.finishFading();
		else
			this.undoFading();
		return;
	}
	
	if (fade === undefined) fade = true;
	
	var prevSlideNo = this.getPrevSlideNo();
	// Gather the two slides
	this.fadeElemCurr = this.slideElems[this.currentSlide];
	this.fadeElemNext = this.slideElems[prevSlideNo];
	// Put the current picture right behind the next one
	//this.fadeElemCurr.style.zIndex = 2;
	//this.fadeElemNext.style.zIndex = 3;
	this.currentSlide = prevSlideNo;
	// Do the fading
	if (fade) {
		this.fadeElemNext.style.visibility = "visible";
		this.fading = true;
		this.fadeSlide();
	} else {
		this.finishFading();
	}
}
SlideAnimator.prototype.nextSlide = function (fade) {
	if (this._slideTimeout) window.clearTimeout(this._slideTimeout);
	if (this.fading) {
		// Figure out which of the fading slides to skip to
		if (this.fadeElemNext.seqNo == this.getNextSlideNo(this.fadeElemCurr.seqNo))
			this.finishFading();
		else
			this.undoFading();
		return;
	}
	
	if (fade === undefined) fade = true;
	
	var nextSlideNo = this.getNextSlideNo();
	// Gather the two slides
	this.fadeElemCurr = this.slideElems[this.currentSlide];
	this.fadeElemNext = this.slideElems[nextSlideNo];
	// Put the current picture right behind the next one
	//this.fadeElemCurr.style.zIndex = 2;
	//this.fadeElemNext.style.zIndex = 3;
	this.currentSlide = nextSlideNo;
	// Do the fading
	if (fade) {
		this.fadeElemNext.style.visibility = "visible";
		this.fading = true;
		this.fadeSlide();
	} else {
		this.finishFading();
	}
}
SlideAnimator.prototype.fadeSlide = function () {
	if (this._fadeTimeout) window.clearTimeout(this._fadeTimeout);
	if (!this.fading) return;
	
	if (this.fadeFrame < this.fadeMax) {
		// Continue fading
		var opacity = this.fadeFrame / this.fadeMax;
		this.fadeElemNext.setOpacity(opacity);
		this.fadeElemCurr.setOpacity(1 - opacity);
		// Next opacity level
		var self = this;
		var continueFade = function () {
			self.fadeFrame++;
			self.fadeSlide();
		}
		this._fadeTimeout = window.setTimeout(continueFade, this.fadeDelay);
	} else {
		// Fading end reached
		this.fadeEnded();
	}
}
SlideAnimator.prototype.finishFading = function () {
	this.fadeFrame = this.fadeMax;
	this.currentSlide = this.fadeElemNext.seqNo;
	this.fadeEnded();
}
SlideAnimator.prototype.undoFading = function () {
	this.fadeFrame = 0;
	this.currentSlide = this.fadeElemCurr.seqNo;
	this.fadeEnded();
}
SlideAnimator.prototype.fadeEnded = function () {
	// Transition intent guessing
	if (this.fadeFrame / this.fadeMax < 0.75) {
		// Cancel the fade
		this.fadeElemNext.setVisible(false);
		this.fadeElemCurr.setVisible(true);
		//this.fadeElemCurr.style.zIndex = 2;
		this.currentSlide = this.fadeElemCurr.seqNo;
	} else {
		// Finish the fade
		this.fadeElemCurr.setVisible(false);
		this.fadeElemNext.setVisible(true);
		//this.fadeElemNext.style.zIndex = 2;
	}
	// Reset state
	this.fadeElemNext = this.fadeElemCurr = null;
	this.fading = false;
	this.fadeFrame = 0;
	// Continue to play if specified
	if (this.playing)
		this.play();
}
SlideAnimator.prototype.getPrevSlideNo = function (num) {
	if (num === undefined) num = this.currentSlide;
	return (num + this.numSlides - 1) % this.numSlides;
}
SlideAnimator.prototype.getNextSlideNo = function (num) {
	if (num === undefined) num = this.currentSlide;
	return (num + 1) % this.numSlides;
}
SlideAnimator.prototype.getElementByClass = function (root, cls) {
	// Find the first descendant by the given class
	
	// Root must be a DOM element
	if (!root.childNodes) return null;
	
	// Skip base element
	if (root.childNodes.length == 0)
		return null;
	
	// For each child element
	for (var i = 0; i < root.childNodes.length; i++) {
		// Return the element if belongs to class
		if (root.childNodes[i].className && root.childNodes[i].className.match(new RegExp("(^|\\s+)"+cls+"(\\s+|$)"))) return root.childNodes[i];
		// Search children if not
		var child = this.getElementByClass(root.childNodes[i], cls);
		if (child != null) return child;
	}
}

function checkIE6() {
    if (navigator.appName == 'Microsoft Internet Explorer') {
        var regex = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
        if (regex.exec(navigator.userAgent) != null)
            return parseFloat( RegExp.$1 ) == 6;
    }
    return false;
}
var slideshow;
function createSlideshow () {
	slideshow = new Slideshow("slideshow", 800, 170, 12000);
	slideshow.useDescription({"slide":"slide","img":"slideimg","desc":"slidedesc","link":"slidelink","linkmask":"linkmask"});
	slideshow.loadJsonObjects([{"name":"Gecko","url":"\/Slideshow\/Images\/gecko.jpg","description":"Asexual reproduction in reptiles.","link":"http:\/\/sciencewise.anu.edu.au\/articles\/mitzy_gecko"},{"name":"RetinaCones","url":"\/Slideshow\/Images\/RetinaCones.jpg","description":"Preventing vision loss.","link":"http:\/\/sciencewise.anu.edu.au\/articles\/myopia"},{"name":"Plant","url":"\/Slideshow\/Images\/plantPetri.jpg","description":"Programs in plant sciences.","link":"http:\/\/science.anu.edu.au\/Plants\/"},{"name":"KowalczukiAbsorption","url":"\/Slideshow\/Images\/KowalczukiAbsorption.jpg","description":"Protein absorption","link":"http:\/\/sciencewise.anu.edu.au\/articles\/bambi-assembly-line"},{"name":"Fish","url":"\/Slideshow\/Images\/Fish.jpg","description":"Interested in marine science?","link":"http:\/\/science.anu.edu.au\/Marine\/"},{"name":"Snowgum","url":"\/Slideshow\/Images\/Snowgum.jpg","description":"The physiology of drought and freezing.","link":"http:\/\/sciencewise.anu.edu.au\/articles\/sob_leafveins"},{"name":"PSI","url":"\/Slideshow\/Images\/PSI.jpg","description":"Towards artificial photosynthesis.","link":"http:\/\/sciencewise.anu.edu.au\/articles\/Ultimate_green_energy"},{"name":"Crab","url":"\/Slideshow\/Images\/Crab.jpg","description":"Strategies of the Fiddler crab.","link":"http:\/\/sciencewise.anu.edu.au\/articles\/sob_fiddlercrabview"}]);
	slideshow.execute();
}
function slideshowPlay () { slideshow.play(); }
function slideshowStop () { slideshow.stop(); }
function slideshowNext () { slideshow.next(); }
function slideshowPrev () { slideshow.prev(); }

createSlideshow();