
if(typeof Widget == "undefined") Widget = {};
Widget.Fader = Class.create();
Widget.Fader.prototype = {
    /**
     * The Widget.Fader class constructor. <br />
     * @constructor Widget.Fader
     * @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} [fadeInDuration] The time in seconds of the fade in. (default: 2.5)
         * @param {float} [fadeOutDuration] The time in seconds of the fade out. (default: 1.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 {object} [attributes] An associative array of attributes given to the image. (default: {})
         * @param {string} [dir] The directory that all images reside in. Used as a prefix for the image src. (default: null)
         * @param {function} [beforeFade] A function that is called before the image is faded. 2 parameters are passed: 1. the image; 2. a boolean indicating if the image is being faded in (true) or out (false) (default: null)
         * @param {int} [startIndex] The index of the first new image to be shown. (default: 0)
         */
        this.options = Object.extend({
            id: this.img.id,
            fadeInDuration: 0.5,
            fadeOutDuration: 0.5,
            displayDuration: 1.0,
            autoSize: false,
            autoStart: true,
            attributes: {},
            dir: "",
            beforeFade: null,
            startIndex: 0
        }, options || {});
        this.options.attributes["id"] = this.options.id;

        this.index = this.options.startIndex;
        this.container = $(this.img.parentNode);
        this.loadedObserver = this.loaded.bind(this);
        this.fadeInObserver = this.fadeIn.bind(this);
        this.nextObserver = this.next.bind(this);

        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() {
        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.img = this.oldImg;
            --this.index;
        }
        Element.setOpacity(this.img, 1);
    },
    /**
     * Loads the next image in list
     * @private
     */
    next: function() {
        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 = new Element("img", this.options.attributes);
        Event.observe(this.img, "load", this.loadedObserver);
        this.img.src = this.options.dir + this.list[this.index];
    },
    /**
     * Event listener for image loaded
     * @private
     */
    loaded: function() {
        Event.stopObserving(this.img, "load", this.loadedObserver);
        if(typeof this.options.beforeFade == "function") {
            this.options.beforeFade(this.oldImg, false);
        }
        new Effect.Opacity(this.oldImg, { duration: this.options.fadeOutDuration, from: 1.0, to: 0.0, queue: { scope: this.options.id } });
        this.timeout = setTimeout(this.fadeInObserver, this.options.fadeOutDuration * 1000);
    },
    /**
     * Event listener for fadeIn
     * @private
     */
    fadeIn: function() {
        if(typeof this.options.beforeFade == "function") {
            this.options.beforeFade(this.img, true);
        }
        this.img.id = this.id;
        Element.setOpacity(this.img, 0);
        if(this.options.autoSize) {
            this.resize(this.img);
        }
        this.container.replaceChild(this.img, this.oldImg);
        this.oldImg = null;
        new Effect.Opacity(this.img, { duration: this.options.fadeInDuration, from: 0.0, to: 1.0, queue: { scope: this.options.id } });
        this.timeout = setTimeout(this.nextObserver, (this.options.fadeInDuration + 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;
        }
    }
};
/**
 * @deprecated
 **/
var Fader = Widget.Fader;