﻿/*
* Crossfade
* Version 4.1 30/03/2007
* 
* Copyright (c) 2007 Millstream Web Software http://www.millstream.com.au
* 
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* * 
*/

var Crossfade = Class.create();

Crossfade.prototype = {
    loaded: false,
    initialize: function(elm, options) {
        var me = this, next, prev;
        this.elm = $(elm);
        this.counter = 0;
        this.prevSlide = null;
        var t_opt = {};
        for (t in Crossfade.Transition) {
            var trans = Crossfade.Transition[t];
            if (trans.className && this.elm.hasClassName(trans.className)) {
                t_opt = { transition: trans };
                break;
            }
        }
        this.options = Object.extend(Object.clone(Crossfade.defaults), Object.extend(options || {}, t_opt));
        this.options.interval = Math.max(2, this.options.interval);
        this.elm.makePositioned();
        this.slides = this.elm.immediateDescendants();
        if (this.options.random || this.elm.hasClassName(this.options.randomClassName)) {
            this.slides.sort(function(a, b) {
                return me.rndm(-1, 1);
            });
        }
        if (this.elm.id) {
            next = $(this.elm.id + '-next');
            prev = $(this.elm.id + '-previous');
            if (next) { Event.observe(next, 'click', this.next.bind(this)); }
            if (prev) { Event.observe(prev, 'click', this.previous.bind(this)); }
        }

        this.loadSlide(this.slides[0], function() {
            me.options.transition.prepare(me);
        });
        this.loadSlide(this.slides[1]);

        if (this.options.autoStart) { setTimeout(this.start.bind(this), this.rndm((this.options.interval - 1) * 1000, (this.options.interval + 1) * 1000)); }
    },
    start: function() {
        this.ready = true;
        this.cycle()
        return this.timer = new PeriodicalExecuter(this.cycle.bind(this), this.options.interval);
    },
    stop: function() {
        this.options.transition.cancel(this);
        this.timer.stop();
    },
    next: function() {
        this.options.transition.cancel(this);
        this.cycle();
    },
    previous: function() {
        this.options.transition.cancel(this);
        this.cycle(-1);
    },
    cycle: function(dir) {
        if (!this.ready) { return; }
        this.ready = false;
        dir = (dir === -1) ? dir : 1;
        var me = this, prevSlide, nextSlide, opt, fade;
        prevSlide = this.slides[this.counter];
        this.counter = this.loopCount(this.counter + dir);
        if (this.counter == 0) {
            this.loaded = true;
        }
        nextSlide = this.slides[this.counter];
        this.loadSlide(nextSlide, me.options.transition.cycle(prevSlide, nextSlide, me));
        if (!this.loaded) {
            this.loadSlide(this.slides[this.loopCount(this.counter + 1)]);
        }
    },
    loadSlide: function(slide, onload) {
        var loaders = [], me = this, img, pnode, onloadFunction;
        onload = typeof onload === 'function' ? onload : function() { };
        onloadFunction = function() {
            onload();
            me.ready = true;
        };
        slide = $(slide);
        loaders = Selector.findChildElements(slide, [this.options.imageLoadSelector]);
        if (loaders.length && loaders[0].href !== '') {
            img = document.createElement('img');
            img.className = 'loadimage';
            img.onload = onloadFunction;
            img.src = loaders[0].href;
            loaders[0].parentNode.replaceChild(img, loaders[0]);
        } else {
            loaders = [];
            loaders = Selector.findChildElements(slide, [this.options.ajaxLoadSelector]);
            if (loaders.length && loaders[0].href !== '') {
                new Ajax.Updater(slide, loaders[0].href, { method: 'get', onComplete: onloadFunction });
            } else {
                onloadFunction();
            }
        }
    },
    loopCount: function(c) {
        if (c >= this.slides.length) {
            c = 0;
        } else if (c < 0) {
            c = this.slides.length - 1
        }
        return c;
    },
    rndm: function(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
    },
    timer: null, effect: null, ready: false
};
Crossfade.Transition = {};
Crossfade.Transition.Switch = {
    className: 'transition-switch',
    cycle: function(prev, next, show) {
        show.slides.without(next).each(function(s) {
            $(s).hide();
        })
        $(next).show();
    },
    cancel: function(show) { },
    prepare: function(show) {
        show.slides.each(function(s, i) {
            $(s).setStyle({ display: (i === 0 ? 'block' : 'none') });
        });
    }
};
Crossfade.Transition.Crossfade = {
    className: 'transition-crossfade',
    cycle: function(prev, next, show) {
        var opt = show.options;
        show.effect = new Effect.Parallel([new Effect.Fade(prev, { sync: true }),
			new Effect.Appear(next, { sync: true })],
			{ duration: opt.duration, queue: 'Crossfade', afterFinish: function() {
			    show.slides.without(next).each(function(s) {
			        $(s).setStyle({ opacity: 0 });
			    })
			} 
			}
		);
    },
    cancel: function(show) {
        if (show.effect) { show.effect.cancel(); }
    },
    prepare: function(show) {
        show.slides.each(function(s, i) {
            $(s).setStyle({ opacity: (i === 0 ? 1 : 0), visibility: 'visible' });
        });
    }
};
Crossfade.Transition.FadeOutFadeIn = {
    className: 'transition-fadeoutfadein',
    cycle: function(prev, next, show) {
        var opt = show.options;
        show.effect = new Effect.Fade(prev, {
            duration: opt.duration / 2,
            afterFinish: function() {
                show.effect = new Effect.Appear(next, { duration: opt.duration / 2 });
                show.slides.without(next).each(function(s) {
                    $(s).setStyle({ opacity: 0 });
                })
            }
        });
    },
    cancel: function(show) {
        if (show.effect) { show.effect.cancel(); }
    },
    prepare: function(show) {
        show.slides.each(function(s, i) {
            $(s).setStyle({ opacity: (i === 0 ? 1 : 0), visibility: 'visible' });
        });
    }
};

Effect.DoNothing = Class.create();
Object.extend(Object.extend(Effect.DoNothing.prototype, Effect.Base.prototype), {
    initialize: function() {
        this.start({ duration: 0 });
    },
    update: Prototype.emptyFunction
});
Crossfade.Transition.FadeOutResizeFadeIn = {
    className: 'transition-fadeoutresizefadein',
    cycle: function(prev, next, show) {
        var opt = show.options;
        show.effect = new Effect.Fade(prev, {
            duration: (opt.duration - 1) / 2,
            afterFinish: function() {
                show.slides.without(next).each(function(s) {
                    $(s).setStyle({ opacity: 0 });
                })
                var slideDims = [next.getWidth(), next.getHeight()];
                var loadimg = Selector.findChildElements(next, ['img.loadimage']);
                if (loadimg.length && loadimg[0].offsetWidth && loadimg[0].offsetHeight) {
                    slideDims[0] += slideDims[0] < loadimg[0].offsetWidth ? loadimg[0].offsetWidth : 0;
                    slideDims[1] += slideDims[1] < loadimg[0].offsetHeight ? loadimg[0].offsetHeight : 0;
                }
                var showDims = [show.elm.getWidth(), show.elm.getHeight()];
                var scale = [(showDims[0] > 0 && slideDims[0] > 0 ? slideDims[0] / showDims[0] : 1) * 100, (showDims[1] > 0 && slideDims[1] > 0 ? slideDims[1] / showDims[1] : 1) * 100];
                show.effect = new Effect.Parallel([
						(scale[0] === 100 ? new Effect.DoNothing() : new Effect.Scale(show.elm, scale[0], { sync: true, scaleY: false, scaleContent: false })),
						(scale[1] === 100 ? new Effect.DoNothing() : new Effect.Scale(show.elm, scale[1], { sync: true, scaleX: false, scaleContent: false }))
					],
					{
					    duration: 1,
					    queue: 'FadeOutResizeFadeIn',
					    afterFinish: function() {
					        show.effect = new Effect.Appear(next, { duration: (opt.duration - 1) / 2 });
					    }
					}
				);
            }
        });
    },
    cancel: function(show) {
        if (show.effect) { show.effect.cancel(); }
    },
    prepare: function(show) {
        var slideDims = [$(show.slides[0]).getWidth(), $(show.slides[0]).getHeight()];
        show.elm.setStyle({ width: slideDims[0] + 'px', height: slideDims[1] + 'px' });
        show.slides.each(function(s, i) {
            $(s).setStyle({ opacity: (i === 0 ? 1 : 0), visibility: 'visible' });
        });
    }
};
Crossfade.defaults = {
    autoLoad: true,
    autoStart: true,
    random: false,
    randomClassName: 'random',
    selectors: ['.crossfade'],
    imageLoadSelector: 'a.loadimage',
    ajaxLoadSelector: 'a.load',
    interval: 5,
    duration: 2,
    transition: Crossfade.Transition.Crossfade
};
Crossfade.setup = function(options) {
    Object.extend(Crossfade.defaults, options);
};
Crossfade.load = function() {
    if (Crossfade.defaults.autoLoad) {
        Crossfade.defaults.selectors.each(function(s) {
            $$(s).each(function(c) {
                return new Crossfade(c);
            });
        });
    }
};

if (window.FastInit) {
    FastInit.addOnLoad(Crossfade.load);
} else {
    Event.observe(window, 'load', Crossfade.load);
}
