/*
---

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: 3000,
    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');
    }

});
