/*
---

script: NivooSlider.js

description: A nice image slider for MooTools.

license: MIT-style license

authors:
- Johannes Fischer

requires:
- core/1.3: '*'

provides:
- NivooSlider

...
*/

var NivooSlider = new Class({

    Implements: [Events, Options],

    caption: null,
children: null,
    containerSize: 0,
count: 0,
    currentSlide: 0,
    currentImage: '',
    effects: {
// used for random effects
common: ['fade', 'fold'],
horizontal: ['sliceLeftUp', 'sliceLeftDown', 'sliceLeftRightDown', 'sliceLeftRightUp', 'sliceRightDown', 'sliceRightUp', 'wipeDown', 'wipeUp'],
vertical: ['sliceDownLeft', 'sliceDownRight', 'sliceUpDownLeft', 'sliceUpDownRight', 'sliceUpLeft', 'sliceUpRight', 'wipeLeft', 'wipeRight']
},
holder: null,
hover: false,
interval: null,
orientation: '',
    paused: false,
    running: false,
slices: null,
sliceSize: null,
    totalSlides: 0,

    options: {
        animSpeed: 500,
        autoPlay: true,
directionNav: true,
directionNavHide: false,
directionNavWidth: '20%',
        effect: 'sliceDown', // TODO allow to pass an array with multiple effects
interval: 6000,
orientation: 'vertical',
pauseOnHover: true,
slices: 15,

// not implemented yet
directionNavPosition: 'inside|outside',
preLoadImages: false

//onFinish: function () {}
//onLastSlide: function () {}
        //onStart: function () {}
    },

    initialize: function (container, options)
    {
this.container = $(container);

this.setOptions(options);
this.orientation = this.options.orientation;

this.effects.horizontal.combine(this.effects.common);
this.effects.vertical.combine(this.effects.common);

this.initSlider();
this.createSlices();
if (this.options.autoPlay)
{
this.play();
}
    },

animate: function (slice, fxStyles, last)
    {
        var fx = slice.retrieve('fxInstance'),
            isLast = last !== undefined && last === true;

        fx.start(fxStyles).chain(function () {
this.count += 1;
if (this.count === this.options.slices || isLast)
{
this.running = false;

// fire onFinish function
this.finish();

this.setBackgroundImage();

this.count = 0;

if (this.currentSlide === (this.totalSlides - 1))
{
this.lastSlide();
}
}
}.bind(this));
    },

arrangeSlices: function (orientation)
{
var height,
            position,
            sliceSize,
            width;

this.slices.each(function (el, i) {

position = {
left: orientation === 'vertical' ? this.sliceSize.x * i : 0,
top: orientation === 'horizontal' ? this.sliceSize.y * i : 0
};

// set size & position
if (orientation === 'horizontal')
{
height = i === this.options.slices - 1 ? this.containerSize.y - (this.sliceSize.y * i) : this.sliceSize.y;
width = '100%';

el.setStyles({
height: height,
                    top: position.top,
                    width: width
                });
}
// if vertical
else
{
height = 0;
width = i === this.options.slices - 1 ? this.containerSize.x - (this.sliceSize.x * i) : this.sliceSize.x;

el.setStyles({
height: height,
left: position.left,
top: '',
                    width: width
                });
}

            el.store('fxInstance', new Fx.Morph(el, {
                duration: this.options.animSpeed
            })).store('coordinates', Object.merge(position, {height: height, width: width}));
        }, this);
},

createCaption: function ()
{
this.caption = new Element('p', {
styles: {
opacity: 0
}
}).inject(this.holder);

this.caption.store('fxInstance', new Fx.Morph(this.caption, {
duration: 200,
wait: false
}));
},

createDirectionNav: function ()
{
var directionNavStyles,
            leftContainer,
            rightContainer,
            width;

width = this.options.directionNavWidth;

directionNavStyles = {
height: this.containerSize.y,
width: width
};

// create container
leftContainer = new Element('div.direction-nav-left', {
styles: directionNavStyles
}).inject(this.holder);

rightContainer = new Element('div.direction-nav-right', {
styles: directionNavStyles
}).inject(this.holder);

// create controls

this.leftNav = new Element('a', {
events: {
'click': function (e) {
e.stop();
if (this.options.autoPlay)
{
this.pause();
if (!this.options.pauseOnHover)
{
this.play();
}
}
this.previous();
}.bind(this)
},
href: '#',
styles: {
height: directionNavStyles.height
}
}).inject(leftContainer);

this.rightNav = new Element('a', {
events: {
'click': function (e) {
e.stop();
if (this.options.autoPlay)
{
this.pause();
if (!this.options.pauseOnHover)
{
this.play();
}
}
this.next();
}.bind(this)
},
href: '#',
styles: {
height: directionNavStyles.height
}
}).inject(rightContainer);

if (this.options.directionNavHide && this.options.directionNav)
{
$$(this.leftNav, this.rightNav).setStyle('opacity', 0);
this.holder.addEvents({
'mouseout': function () {
$$(this.leftNav, this.rightNav).fade(0);
}.bind(this),
'mouseover': function () {
$$(this.leftNav, this.rightNav).fade(1);
}.bind(this)
});
}
},
    
createLinkHolder: function ()
{
this.linkHolder = new Element('a.nivoo-link', {
href: '#'
}).inject(this.holder);
},

    createSlices: function ()
    {
this.sliceSize = {
x: (this.containerSize.x / this.options.slices).round(),
y: (this.containerSize.y / this.options.slices).round()
};

// effects that need one slice only
if (['fade', 'wipeLeft', 'wipeRight'].contains(this.options.effect))
{
this.options.slices = 1;
}

        this.options.slices.each(function (i) {
            new Element('div.nivoo-slice').inject(this.holder);
        }, this);

this.slices = this.getSlices();

this.arrangeSlices(this.options.orientation);
    },
    
getImages: function ()
{
return this.holder.getElements('img');
},

    getSlices: function ()
    {
        return this.holder.getElements('.nivoo-slice');
    },

    initSlider: function ()
    {
// wrap child elements
this.holder = new Element('div.nivoo-slider-holder').adopt(this.container.getChildren()).inject(this.container);

        this.containerSize = this.holder.getSize();

        // Find our slider children
        this.children = this.getImages();

        this.totalSlides = this.children.length;

        this.children.setStyle('display', 'none');

        this.currentImage = this.children[0];

// init LinkHolderand set link
this.createLinkHolder();
this.setLink();

        // Set first background
this.holder.setStyle('background-image', 'url(' + this.currentImage.get('src') + ')');

this.createCaption();

this.showCaption();

// attach pauseOnHover
if (this.options.pauseOnHover && this.options.autoPlay)
{
this.holder.addEvents({
'mouseenter': function () {
this.pause();
}.bind(this),
'mouseleave': function () {
this.play();
}.bind(this)
});
}

// create directional navigation
if (this.options.directionNav)
{
this.createDirectionNav();
}
    },

hideCaption: function ()
{
this.caption.retrieve('fxInstance').start({
bottom: this.caption.getHeight() * -1,
opacity: 0.5
});
},

next: function ()
{
this.currentSlide += 1;

if (this.currentSlide === this.totalSlides)
{
this.currentSlide = 0;
}

this.slide();
},

pause: function ()
{
window.clearInterval(this.interval);
},

play: function ()
{
this.interval = this.next.periodical(this.options.interval, this);
},

previous: function ()
{
if (this.options.autoPlay)
{
this.pause();
if (!this.options.pauseOnHover)
{
this.play();
}
}

this.currentSlide -= 1;

        if (this.currentSlide < 0)
{
this.currentSlide = (this.totalSlides - 1);
}

this.slide();
},

showCaption: function ()
{
var title = this.currentImage.get('title');

if (!title)
{
this.hideCaption();
return;
}

this.setCaptionText(title);

this.caption.retrieve('fxInstance').start({
bottom: 0,
opacity: 1
});
},

    slide: function (slideNo)
    {
var coordinates,
            effect,
            orientation,
            slice,
            slices,
            styles,
            timeBuff;

if (this.running)
{
return;
}

if (this.options.orientation === 'random')
{
orientation = ['horizontal', 'vertical'].getRandom();
}
else
{
orientation = this.options.orientation;
}

if (orientation !== this.orientation)
{
this.arrangeSlices(orientation);
this.orientation = orientation;
}

if (slideNo !== undefined)
{
this.currentSlide = slideNo;
}

        // Set currentImage
        this.currentImage = this.children[this.currentSlide];

        this.setLink();

        // Process caption
this.showCaption();

// TODO use this.slices within each loop instead
        slices = this.slices;
timeBuff = 0;

// reset slices
        this.slices.each(function (slice) {

coordinates = slice.retrieve('coordinates');

            slice.setStyles({
                background: 'url(' + this.currentImage.get('src') + ') no-repeat -' + coordinates.left + 'px ' + coordinates.top * -1 + 'px',
bottom: '',
height: coordinates.height,
left: coordinates.left,
                opacity: 0,
right: '',
top: coordinates.top,
width: coordinates.width
            });

var property = orientation === 'horizontal' ? 'width' : 'height';

slice.setStyle(property, 0);
        }, this);
    
// fire onStart function
        this.start();

        // Run effects
        this.running = true;

effect = this.options.effect;

if (effect === 'random')
        {
            effect = this.effects[orientation].getRandom();
        }

// vertical effects
        if (['sliceDownRight', 'sliceDownLeft'].contains(effect))
        {
            if (effect === 'sliceDownLeft')
            {
                slices = slices.reverse();
            }

            slices.each(function (slice) {
                slice.setStyle('top', 0);

                this.animate.delay(100 + timeBuff, this, [slice, {height: this.containerSize.y, opacity: 1}]);

                timeBuff += 50;
            }, this);
        }
        else if (['sliceUpRight', 'sliceUpLeft'].contains(effect))
        {
            if (effect === 'sliceUpLeft')
            {
                slices = slices.reverse();
            }

            slices.each(function (slice) {
                var fx = slice.retrieve('fxInstance');
                
                slice.setStyle('bottom', 0);

                this.animate.delay(100 + timeBuff, this, [slice, {height: this.containerSize.y, opacity: 1}]);

                timeBuff += 50;
            }, this);
        }
        else if (['sliceUpDownRight', 'sliceUpDownLeft'].contains(effect))
        {
            if (effect === 'sliceUpDownLeft')
            {
                slices = slices.reverse();
            }

            slices.each(function (slice, i) {
                if (i % 2 === 0)
                {
                    slice.setStyle('top', 0);
                }
                else
                {
                    slice.setStyles({
bottom: 0,
top: ''
});
                }

                this.animate.delay(100 + timeBuff, this, [slice, {height: this.containerSize.y, opacity: 1}]);

                timeBuff += 50;
            }, this);
        }
else if (['wipeLeft', 'wipeRight'].contains(effect))
        {
styles = {
height: this.containerSize.y,
opacity: 1,
width: 0
};

if (effect === 'wipeRight')
{
Object.append(styles, {
backgroundPosition: 'top right',
left: '',
right: 0
});
}

slice = slices[0];

slice.setStyles(styles);
this.animate(slice, {width: this.containerSize.x}, true);
        }

// horizontal effects
else if (['sliceLeftUp', 'sliceLeftDown', 'sliceRightDown', 'sliceRightUp'].contains(effect))
{
if (effect === 'sliceLeftUp' || effect === 'sliceRightUp')
            {
                slices = slices.reverse();
            }

if (effect === 'sliceRightDown' || effect === 'sliceRightUp')
{
slices.setStyles({
left: '',
right: 0
});
}
else
{
slices.setStyles({
left: 0,
right: ''
});
}

            slices.each(function (slice) {
                this.animate.delay(100 + timeBuff, this, [slice, {opacity: 1, width: this.containerSize.x}]);

                timeBuff += 50;
            }, this);
}
else if (['sliceLeftRightDown', 'sliceLeftRightUp'].contains(effect))
        {
            if (effect === 'sliceLeftRightUp')
            {
                slices = slices.reverse();
            }

            slices.each(function (slice, i) {
                if (i % 2 === 0)
                {
                    slice.setStyles({
left: 0,
right: ''
});
                }
                else
                {
                    slice.setStyles({
left: '',
right: 0
});
                }

                this.animate.delay(100 + timeBuff, this, [slice, {opacity: 1, width: this.containerSize.x}]);

                timeBuff += 50;
            }, this);
        }
else if (['wipeDown', 'wipeUp'].contains(effect))
        {
styles = {
height: 0,
opacity: 1,
width: this.containerSize.x
};

if (effect === 'wipeUp')
{
Object.append(styles, {
backgroundPosition: 'bottom left',
bottom: 0,
top: ''
});
}

slice = slices[0];

slice.setStyles(styles);
this.animate(slice, {height: this.containerSize.y}, true);
        }

// horizontal or vertical
        else if (effect === 'fold')
        {
            slices.each(function (slice) {
var fxStyles = {
opacity: 1
};

if (orientation === 'horizontal')
{
fxStyles.height = slice.getHeight();

slice.setStyles({
height: 0,
width: this.containerSize.x
});
}
else
{
fxStyles.width = slice.getWidth();

slice.setStyles({
height: this.containerSize.y,
top: 0,
width: 0
});
}

this.animate.delay(100 + timeBuff, this, [slice, fxStyles]);
                timeBuff += 50;
            }, this);
        }
        else // if (effect == 'fade')
        {
slice = slices[0];

slice.setStyles({
height: this.containerSize.y,
width: this.containerSize.x
});

            this.animate(slice, {opacity: 1}, true);
        }
    },
   
setBackgroundImage: function ()
{
this.holder.setStyle('background-image', 'url(' + this.currentImage.get('src') + ')');
},

setCaptionText: function (text)
{
this.caption.set('text', text);
},

setLink: function ()
{
//Set active link
var clone,
            imageParent = this.currentImage.getParent();

        if (imageParent.get('tag') === 'a')
{
clone = imageParent.clone(false).cloneEvents(imageParent);
clone.replaces(this.linkHolder);
this.linkHolder = clone;
this.linkHolder.addClass('nivoo-link').setStyle('display', 'block');
}
else
{
this.linkHolder.setStyle('display', 'none');
}
},
    
    /**
* Events
*/
    
    finish: function ()
    {
        this.fireEvent('finish');
    },

lastSlide: function ()
    {
        this.fireEvent('lastSlide');
    },

    start: function ()
    {
        this.fireEvent('start');
    }

});

window.addEvent('domready',function(){

    // The simple way
    new NivooSlider($('Slider'));

    // The more advanced way
    new NivooSlider($('Slider'), {
        animSpeed: 1250,
        effect: 'sliceDown',
        interval: 30000,
        orientation: 'vertical',
        slices: 20
    }).addEvents({
        'onFinish': function(){
            // fired after each transition
        },
        'onStart': function(){
            // fired right before each transition
        }
    });

});
