// JavaScript Document
// mooStack v0.1 by Martino di Filippo
//
// you're free to do whatever you want with this code
// but please in case you modify it, send me a copy at
// puntodifuga@gmail.com. Thanks, I hope this helps you!
 
Stack = new Class({
 
    Implements: [Events, Options],
   
    options: {
        scattering: 50,
        speed: 700,
        wrapperStyles: {},
        onChange: $empty
    },
   
    initialize: function(container, options) {
        this.container = $(container);
        this.setOptions(options);
        this.current = 0;
        this.currentOrder;
        this.reindexing = false;
        this.rightEdge = 0;
        this.stack = this.container.getChildren();
       
        this.container.setStyle('position', 'absolute');
       
        var wrapper = new Element('div', {
            'class': 'stackWrapper',
            'styles': {
                'position': 'absolute'
            }
        });
        wrapper.setStyles(this.options.wrapperStyles);
 
        var index = 0, z = this.stack.length * 2;
 
        this.stack.each((function(el){
            el.setStyle('z-index', z--);
            wrapper.clone()
                .setStyles({
                    'height': el.getSize().y,
                    'margin-top': $random(0, this.options.scattering),
                    'margin-left': $random(0, this.options.scattering),
                    'width': el.getSize().x,
                    'z-index': z--
                })
                .store('stack:index', index++)
                .wraps(el);
           
            if(this.rightEdge < el.scrollWidth)
                this.rightEdge = el.scrollWidth;
        }).bind(this));
 
        wrapper.dispose();
 
        this.stack = this.container.getChildren();
        this.rightEdge += this.options.scattering;
       
        this.stack[0].addEvent('click', this.swap.bind(this));
        this.update();
    },
   
    goTo: function(index) {
        if($type(index) == 'element')
            index = $pick(index.retrieve('stack:index'), index.parent.retrieve('stack:index'));
 
        if($type(index) != 'number')
            return this;
 
        if(this.current == index)
            return this;
       
        var forward = this.current < index ? index - this.current : this.stack.length - this.current + index;
        var backward = this.current > index ? this.current - index : this.current + this.stack.length - index;
        return this.swapMany((Math.abs(forward) <= Math.abs(backward)) ? forward : -backward);
    },
   
    reindex: function() {
        var z = this.stack.length * 2;
        this.stack.each(function(wrapper){
            wrapper.getChildren()[0].setStyle('z-index', z--);
            wrapper.setStyle('z-index', z--);
        });
 
        return this;
    },
   
    swap: function(direction) {
        var current = this.stack[0];
 
        switch(direction) {
            case 'prev':
                var next = this.stack.getLast();
                this.stack.erase(next);
                this.stack = [next].extend(this.stack);
                break;
            case 'next':
            default:
                direction = 'next';
                var next = this.stack[1];
                this.stack.erase(current).push(current);
        }
 
        current.removeEvents('click');
        next.addEvent('click', this.swap.bind(this));
 
        if(this.reindexing)
            $clear(this.reindexing);
 
        this.reindexing = this.reindex.delay((this.options.speed / 2).toInt(), this);
 
        var out = [$random((-this.options.scattering * 2).toInt(), 0), $random(this.rightEdge, this.rightEdge + (this.options.scattering * 2))];
        var final = [$random(0, this.options.scattering), $random(0, this.options.scattering)]
 
        new Fx.Morph(direction == 'next' ? current : next, { duration: (this.options.speed / 2).toInt(), link: 'chain' }).start({
            'margin-top': out[0],
            'margin-left': out[1]
        }).start({
            'margin-top': final[0],
            'margin-left': final[1]
        });
       
        this.update();
        this.fireEvent('onChange', this.current);
 
        return this;
    },
 
    swapMany: function(many) {
        direction = many < 0 ? 'prev' : 'next';
        many = Math.abs(many);
 
        while(many--)
            this.swap(direction);
 
        return this;
    },
   
    toElement: function() {
        return this.container;
    },
 
    update: function() {
        this.currentOrder = this.stack.map(function(el){ return el.retrieve('stack:index'); });
        this.current = this.currentOrder[0];
    }
});
