/**
 * @fileoverview Blender.js <br />
 * Loads an array of images and blends them in sequence. <br />
 * <br />
 * Requires Prototype (http://www.prototypejs.org) 1.5 or later <br />
 * and Script.aculo.us (http://script.aculo.us) 1.7 or later. <br />
 * <br />
 * by Marc Heiligers (marc@eternal.co.za) http://www.eternal.co.za<br />
 * <br />
 * You may use this class in any way you like, but don't blame me if things go <br />
 * pear shaped. If you use this library I'd like a mention on your website, but <br />
 * it's not required. If you like it, send me an email. If you find bugs, send <br />
 * me an email. If you don't like it, don't tell me: you'll hurt my feelings. <br />
 * <br />
 * Change History:<br />
 * Version 1.0.2: 19 Aug 2007<br />
 * - Updated DOM creation to use Builder.node in all instances<br />
 * - Updated DOM traversal to use Element.up() for parent nodes<br />
 * Version 1.0.1: 15 Aug 2007<br />
 * - Added attributes option<br />
 * - Now requires builder.js from Scriptaculous<br />
 * - Now delays the start call by displayDuration<br />
 * Version 1.0.0: 12 Aug 2007<br />
 * - First version<br />
 * @class Blender
 * @version 1.0.2
 * @author Marc Heiligers marc@eternal.co.za http://www.eternal.co.za
 */
var Blender = Class.create();
Blender.prototype = {
	/**
	 * The Blender class constructor. <br />
	 * @constructor Blender
	 * @param {string|img element} img The id of or actual image element to be faded
	 * @param {array(string)} list  An array of paths (relative or absolute) of the images
	 * @param {object} [options] An object of options.
	 */
	initialize: function(img, list, options) {
		this.img = $(img);
		this.list = list;

		/**
		 * The default options object.
		 * @class
		 * @param {string} [id] The id used as queue scope. (default: img.id)
		 * @param {float} [fadeDuration] The time in seconds of the fade in/out. (default: 2.5)
		 * @param {float} [displayDuration] The time in seconds that the image is not faded out after being faded in. (default: 2.5)
		 * @param {bool} [autoSize] Set true if the image should be sized to it's container. Maintains aspect ratio. (default: false)
		 * @param {bool} [autoStart] If false the Blender will not start until Blender#start is called. (default: true)
		 * @param {bool} [noWrap] If false the Blender will not wrap the image in a div and use the image's parent container. (default: false)
		 * @param {array} [attributes] The attributes given to the image. (default: [])
		 */
		this.options = Object.extend({
			id: this.img.id,
			fadeDuration: 2.5,
			displayDuration: 2.5,
			autoSize: false,
			autoStart: true,
			noWrap: false,
			attributes: []
		}, options || {});

		this.index = 0;
		if(this.options.noWrap) {
			this.container = this.img.up();
		} else {
			this.container = Element.extend(Builder.node("div")).setStyle({ position: "relative", width: this.img.width + "px", height: this.img.height + "px" });
			this.img.up().replaceChild(this.container, this.img);
			this.container.appendChild(this.img);
		}
		this.loadedObserver = this.loaded.bind(this);
		this.nextObserver = this.next.bind(this);

		this.stopped = true;
		if(this.options.autoStart) {
			setTimeout(this.start.bind(this), this.options.displayDuration * 1000);
		}
	},
	/**
	 * Starts the fading if the autoStart option was set to false or after a call to stop.
	 */
	start: function() {
		if(!this.stopped) {
			return;
		}
		this.stopped = false;
		this.next();
	},
	/**
	 * Stops the fading and sets the opacity of the current image to 100%.
	 */
	stop: function() {
		this.stopped = true;
		try { clearTimeout(this.timeout); } catch(ex) { }
		try { Effect.Queues.get(this.options.id).each(function(effect) { effect.cancel() }) } catch(ex) { }
		if(this.oldImg) {
			this.container.removeChild(this.oldImg);
			this.oldImg = null;
		}
		Element.setOpacity(this.img, 1);
	},
	/**
	 * Loads the next image in list
	 * @private
	 */
	next: function() {
		if(this.oldImg) {
			this.container.removeChild(this.oldImg);
		}
		this.oldImg = this.img;
		if(this.stopped || this.list.length == 0) {
			return;
		}
		++this.index;
		if(this.index >= this.list.length) {
			this.index = 0;
		}
		this.img = Builder.node("img", this.options.attributes);
		Event.observe(this.img, "load", this.loadedObserver);
		this.img.src = this.list[this.index];
	},
	/**
	 * Event listener for image loaded
	 * @private
	 */
	loaded: function() {
		Event.stopObserving(this.img, "load", this.loadedObserver);
		if(this.options.autoSize) {
			this.resize(this.img);
		}
		this.img.setOpacity(0);
		this.container.appendChild(this.img);
		this.img.setStyle({ position: "absolute", top: this.container.getStyle("padding-top"), left: this.container.getStyle("padding-left") });
		new Effect.Opacity(this.oldImg, { duration: this.options.fadeDuration, from: 1.0, to: 0.0, queue: { scope: this.options.id } });
		new Effect.Opacity(this.img, { duration: this.options.fadeDuration, from: 0.0, to: 1.0, queue: { scope: this.options.id } });
		this.timeout = setTimeout(this.nextObserver, (this.options.fadeDuration + this.options.displayDuration) * 1000);
	},
	/**
	 * Resize the image to the container while maintaining aspect ratio
	 * @private
	 */
	resize: function(img) {
		var dim = this.container.getDimensions();
		dim.width -= parseInt(this.container.getStyle("padding-left")) +
			parseInt(this.container.getStyle("padding-right")) +
			parseInt(this.container.getStyle("border-left-width")) +
			parseInt(this.container.getStyle("border-right-width"));
		dim.height -= parseInt(this.container.getStyle("padding-top")) +
			parseInt(this.container.getStyle("padding-bottom")) +
			parseInt(this.container.getStyle("border-top-width")) +
			parseInt(this.container.getStyle("border-bottom-width"));

		var dw = dim.width / img.width;
		var dh = dim.height / img.height;
		var w1 = img.width * dh;
		var h1 = img.height * dw;

		if(dw > dh) {
			img.width = w1;
			img.height = dim.height;
		} else {
			img.width = dim.width;
			img.height = h1;
		}
	}
};

