/*
 * MooTools based slideshow
 * Copyright (C) Nick Wellnhofer, 2009
 * wellnhofer@aevum.de
 */

var AESlideshow = new Class({
    Implements: [ Options, Events ],
    options: {
        transition: {
            type: 'slide'
        },
        createLinks: true,
        autoDelay: 0,
        autoSize: false
    },
    transitions: {
        slide: new Class({
            Implements: [ Options ],
            options: {
                fps: 50,
                duration: 1000,
                transition: Fx.Transitions.Quad.easeInOut,
                direction: 'left'
            },
            initialize: function(slideshow, options) {
                this.slideshow = slideshow;
                this.setOptions(options);

                var dir = this.options.direction;

                if(dir == 'top' || dir == 'bottom') {
                    this.fxProperty = 'top';
                    this.size = slideshow.size.y;
                    this.switchMode = dir == 'bottom' ? 1 : 0;
                }
                else {
                    this.fxProperty = 'left';
                    this.size = slideshow.size.x;
                    this.switchMode = dir == 'right' ? 1 : 0;
                }

                this.fx = new Fx.Tween(slideshow.container, {
                    property: this.fxProperty,
                    unit: 'px',
                    fps: this.options.fps,
                    duration: this.options.duration,
                    transition: this.options.transition,
                    onComplete: this.complete.bind(this)
                });
            },
            start: function(mode, oldItem, newItem) {
                var newPos = this.size;
                if((mode == 'prev' ? 1 : 0) ^ this.switchMode)
                    newPos = -newPos;

                newItem.setStyle(this.fxProperty, newPos + 'px');
                this.fx.start(0, -newPos);

                this.oldItem = oldItem;
                this.newItem = newItem;
            },
            complete: function() {
                var slideshow = this.slideshow;

                this.oldItem.dispose();
                this.newItem.setStyle(this.fxProperty, '0');
                slideshow.container.setStyle(this.fxProperty, '0');

                slideshow.endAnimation();
            }
        }),
        fade: new Class({
            Implements: [ Options ],
            options: {
                fps: 50,
                duration: 1000,
                transition: Fx.Transitions.Sine.easeInOut
            },
            initialize: function(slideshow, options) {
                this.slideshow = slideshow;
                this.setOptions(options);
            },
            start: function(mode, oldItem, newItem) {
                newItem.setStyle('opacity', '0');

                new Fx.Tween(newItem, {
                    property: 'opacity',
                    fps: this.options.fps,
                    duration: this.options.duration,
                    transition: this.options.transition,
                    onComplete: this.complete.bind(this)
                }).start(0, 1);

                this.oldItem = oldItem;
            },
            complete: function() {
                this.oldItem.dispose();
                this.slideshow.endAnimation();
            }
        })
    },
    initialize: function(slideshow, options) {
        this.slideshow = $(slideshow);
        this.setOptions(options);

        this.slideshow.getElement('.noscript').setStyle('display', 'none');

        this.items = [];
        this.itemSpecs = this.slideshow.getElements('.item-spec');
        this.allowInput = true;
        this.pos = 0;

        if (options.autoSize) {
            var size = { x: 0, y: 0 };

            this.itemSpecs.each(function(item, idx) {
                var itemSize = item.getDimensions();
                if (itemSize.x > size.x) size.x = itemSize.x;
                if (itemSize.y > size.y) size.y = itemSize.y;
            });

            this.size = size;
            this.slideshow.setStyles({
                width:  size.x,
                height: size.y
            });
        }
        else {
            this.size = this.slideshow.getSize();
        }

        this.container = new Element('div', {
            styles: {
                position: 'absolute'
            }
        }).inject(this.slideshow);

        if(this.options.createLinks) {
            new Element('a', {
                'class': 'left',
                events: {
                    mousedown: this.prev.bind(this)
                }
            }).inject(this.slideshow);

            new Element('a', {
                'class': 'right',
                events: {
                    mousedown: this.next.bind(this)
                }
            }).inject(this.slideshow);
        }

        var transOptions = this.options.transition;
        var tclass = this.transitions[transOptions.type];
        this.transition = new tclass(this, transOptions);

        this.load(0);
        this.items[0].inject(this.container);

        this.startTimer();
    },
    normalizePos: function(pos) {
        var len = this.itemSpecs.length;

        if(pos < 0) pos += len;
        else if(pos >= len) pos -= len;

        return pos;
    },
    load: function(pos) {
        pos = this.normalizePos(pos);
        if(this.items[pos]) return;

        var item = new Element('div', {
            'class': 'item',
            styles: {
                position: 'absolute',
                left: '0',
                top: '0',
                width: this.size.x + 'px',
                height: this.size.y + 'px'
            }
        });
        this.populateItem(item, this.itemSpecs[pos]);
        this.items[pos] = item;
    },
    populateItem: function(item, spec) {
        spec.getChildren().inject(item);
    },
    prev: function(event) {
        if(event) event.stop();
        this.jump(this.pos - 1, 'prev');
    },
    next: function(event) {
        if(event) event.stop();
        this.jump(this.pos + 1, 'next');
    },
    jump: function(newPos, mode) {
        if(!this.allowInput) return;
        this.allowInput = false;
        this.animationCount = 1;
        clearTimeout(this.autoTimer);

        newPos = this.normalizePos(newPos);
        this.load(newPos);

        /* Firefox 4 loses the img element of the final item and sometimes
         * other items for some reason. Reading all img elements here seems
         * to help. */

        if (1) {
            var count = 0;

            for (var i = 0; i < this.items.length; ++i) {
                var item = this.items[i];

                if (item && item.getElement('img'))
                    ++count;
            }
        }

        var oldItem = this.items[this.pos];
        var newItem = this.items[newPos];
        newItem.inject(this.container);
        this.transition.start(mode, oldItem, newItem);

        this.fireEvent('transition', [ mode, newPos ]);

        this.pos = newPos;
    },
    beginAnimation: function() {
        ++this.animationCount;
    },
    endAnimation: function() {
        --this.animationCount;

        if(this.animationCount == 0) {
            this.allowInput = true;
            this.startTimer();
        }
    },
    startTimer: function() {
        var autoDelay = this.options.autoDelay;
        if(autoDelay)
            this.autoTimer = this.next.delay(autoDelay, this, 0);
    }
});

AESlideshow.Preloaded = new Class({
    Extends: AESlideshow,
    options: {
        preloadCount: 1
    },
    initialize: function(slideshow, options) {
        this.parent(slideshow, options);
        this.setOptions(options);

        this.preloadSiblings(null, this.pos);
        this.addEvent('transition', this.preloadSiblings.bind(this));
    },
    preloadSiblings: function(mode, newPos) {
        for(var i=1; i<=this.options.preloadCount; ++i) {
            this.load(newPos + i);
            this.load(newPos - i);
        }
    },
    populateItem: function(item, spec) {
        var a = spec.getElement('a.image');

        if (a) {
            // for backwards compatibility
            new Asset.image(a.href, {
                onload: function(img) {
                    item.setStyle('background-image', 'none');
                }
            }).inject(item);
        }
        else {
            spec.getChildren().inject(item);
            item.getElements('img.preload').each(function(img) {
                new Asset.image(img.getProperty('longdesc'), {
                    onload: function(img) {
                        item.setStyle('background-image', 'none');
                    }
                }).replaces(img);
            });
        }
    }
});

AESlideshow.Slave = new Class({
    Extends: AESlideshow,
    setMaster: function(master) {
        this.master = master;
        master.addEvent('transition', this.masterTransition.bind(this));
    },
    masterTransition: function(mode, newPos) {
        this.master.beginAnimation();
        if(mode == 'prev') this.prev();
        else if(mode == 'next') this.next();
    },
    endAnimation: function() {
        this.parent();
        this.master.endAnimation();
    }
});

