/*
 * ----------------------------------------------------------------------------
 * $Id: blender.js,v 1.6 2008/01/13 22:10:27 stephan Exp $
 * ----------------------------------------------------------------------------
 * $Log: blender.js,v $
 * Revision 1.6  2008/01/13 22:10:27  stephan
 * set background of container to no-repeat
 *
 * Revision 1.5  2008/01/13 19:00:05  stephan
 * added copyright
 * removed blenderDetect()
 *
 * Revision 1.4  2007/12/28 19:15:39  stephan
 * added usage comment
 *
 * Revision 1.3  2007/12/26 12:09:54  stephan
 * show first image for the pause duration and start blending the
 * next picture after that, instead of start blending directly
 *
 * Revision 1.2  2007/12/26 11:33:31  stephan
 * addedd private error function
 *
 * Revision 1.1  2007/12/26 10:58:37  stephan
 * Initial revision
 *
 * ----------------------------------------------------------------------------
 * Copyright 2008 Stephan Leemburg at recursive dot nl
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ----------------------------------------------------------------------------
 * Object oriented blender
 *
 * Usage:
 * - create a <div> with an unique id, for example:
 *   <div id="DivId">
 * - create <img>'s inside this <div> either statically or by javascript
 * - run a javascript that calls:
 *
 *     var timerExpiration = 60;
 *     var timerSleep = 2000;
 *
 *     var b = new Blender("DivId", timerExpiration, timerSleep);
 *
 *     b.start();
 *
 * - to stop the slideshow, run:
 *
 *     b.stop();
 */

function thisClosure(o, f)
{
	if(!o || !f)
		return null;

	/*
	 * call object o's function f with o itself as parameter
	 * this would be simular to this.f(this) and is needed
	 * for use in timers with setTimeout and the like
	 */
	return (function(){
		return o[f](o);
	});
}

function Blender(tag, speed, pause) 
{
	/* public properties */

	this.version = "$Revision: 1.6 $";

	this.speed = speed ? speed : 120;
	this.pause = pause ? pause : 1500;
	this.timeout = this.speed;

	this.timer = null;
	this.timerId = null;
	this.running = false;
	this.step = 2;

	/* private properties */
	
	var me = this;
	var tag = tag;
	var count = 0;
	var container = null;
	var image = null;
	var opacity = 0;
	var engine = "Gecko";
	var width = 0;
	var height = 0;
	var first = true;
	var instantiated = false;

	/* private methods */

	function error(msg)
	{
		alert("ERROR [Blender " + me.version + "]\n\n" + msg);
	}

	function firstImage()
	{
		if(!instantiated)
			return;

    		for(var o = container.firstChild; o; o = o.nextSibling)
			if(o.tagName == 'IMG')
				return o;

		error(container.nodeName + " has no images");
    		return null;
	}

	function nextImage(o) 
	{
		if(!instantiated)
			return;

    		for(o = o.nextSibling; o; o = o.nextSibling)
			if(o.tagName == 'IMG')
				return o;

		return firstImage();
	}

	function setOpacity(o, v) 
	{
		if(!instantiated)
			return;

		if(v < 0)
			v = 0;

		if(v > 100)
			v = 100;

    		o.style.opacity = (v / 100);
    		o.style.MozOpacity = (v / 100);
    		o.style.KhtmlOpacity = (v / 100);
    		o.style.filter = 'alpha(opacity=' + v + ')';
	}

	function init()
	{
		if(instantiated)
		{
			error("Blender: entering initializer more than once");
			return;
		}

		container = document.getElementById(tag);
		if(!container)
		{
			error("Cannot get node " + tag);
			return;
		}
	
		first = true;

		count = 0;
    		for(var o = container.firstChild; o; o = o.nextSibling)
		{
			if(o.tagName != 'IMG')
				continue;

			setOpacity(o, 0);
			o.style.display = 'none';

			if(first)
			{
				image = o;
				o.style.zIndex = 100;

				first = false;	
			}
			else
				o.style.zIndex = 0;

			count++;
		}

		if(!count)
		{
			error("no images in container '" + tag + "'");
			return;
		}
		container.style.backgroundRepeat = 'no-repeat';

		width = image.width;
		height = image.height;

		instantiated = true;

		if(count == 1)
			return;

		/* Focus first, fade on next */
		image.parentNode.style.backgroundImage = "url("+image.src+")";
		setNext();
	}

	function setNext() 
	{
		if(!instantiated)
			return;

		var next = nextImage(image);

		image.style.display = 'none';
		image.style.zIndex = 0;
		setOpacity(image, 0);

		setOpacity((image = next), (opacity = 0));
		image.style.zIndex = 100;
		image.style.display = 'block';
	}

	/* priviliged methods */

	this.getImage = function()
	{
		return image;
	}

	this.render = function()
	{
		if(!instantiated || !this.running)
			return;

		this.timerId = null;

    		opacity += this.step;

    		if (opacity <= 100) 
		{
			this.timeout = this.speed;
			setOpacity(image, opacity);
		}
		else
		{
			// ready with blending 1 image
			if(count <= 1)
			{
				this.timer = null; // release closure
				return; 
			}

			this.timeout = this.pause;
			image.parentNode.style.backgroundImage = "url("+image.src+")";
	
			setNext();
    		}
		this.timerId = setTimeout(this.timer, this.timeout);
    	}

	this.defined = function()
	{
		return instantiated;
	}

	/* setup object instance */

	init();
}

/* public methods */

Blender.prototype.start = function() {

		if(!this.defined())
		{
			alert("Blender::init(): object not fully defined");
			return;
		}

		if(this.timerId)
			this.stop();

		/* show first image */
		var image = this.getImage();
    		image.style.zindex = 100;
    		image.style.display = 'block';

		this.running = true;
		this.timer = thisClosure(this, "render");
		this.timerId = setTimeout(this.timer, this.pause);
}

Blender.prototype.stop = function() {

		if(!this.defined())
		{
			alert("Blender::stop(): object not fully defined");
			return;
		}

		this.running = false;

		if(this.timerId)
		{
			clearTimeout(this.timerId);
			this.timerId = null;
		}
		this.timer = null;

		var image = this.getImage();
		image.style.display = 'none';
		image.parentNode.style.backgroundImage = "";
}
