/**
 * add method by sevenload (christoph schmidt april 2010) slideInRangeToIndex
 * 
  * Js file for the EasySlide class.
  * Recently only tested on firefox 3.0.3 and IE 7 and webkit.
  * 
  * :IMPORTANT: At the moment this class depends on all list elements to have the same width.
  * :TODO: When introducing support for various element widths, we need to calculate the correct acceleration for each element relative to the given duration.
  *
  * @description Provides a comfortable and easy to use interface to slide through a list with several options to control behaviour.
  * @requires mootools 1.2
  * @see EasySlide.Example.html
  * @author Thorsten Schmitt-Rink <schmittrink@gmail.com>
  * @copyright 2008 Thorsten Schmitt-Rink
  */
  
/**
  * You can register callbacks for following events:
  * @see EasySlide.EVENT
  */
var EasySlide = new Class({

    Implements: [Events, Options],
    
    /**
       * @var HtmlUlElement element The root element of our slider's list.
       */
    element: null,
    
    /**
       * @var int width The width that was calculated for the slider list. 
       * :NOTICE: (The li's don't float unless the list is wide enough, otherwise they break) 
       */
    slideModifier: $H({attributeName: 'width', value: 0}),
    
    /**
       * @var bool isSliding Indicates whether we are currently in the proccess of sliding or not.
       */
    isSliding: false,
    
    /**
       * @var Array listItems Array of hashes with the following properties:
       * { HtmlLiElement:element, int:index }
       */
    listItems: [],
    
    /**
       * @var Hash current Infos on the item that is currently first in the slider's list.
       */
    current: null,
    
    /**
       * @var string direction The current direction we are sliding to.
       */
    direction: null,
    
    /**
       * @var boolean loop Indicates whether to keep on sliding after finishing the current slide.
       */
    loop: false,
    
    /**
       * @var Hash targetItem When the slideTo method is called, this property contains the target we want to reach. 
       */
    targetItem: $H(),
    
    /**
       * @var Hash eventListeners An hash holding the callback functions for all registered events.
       */
    eventListeners: $H(),
    
    /**
       * @var int lastSlideRange The last range that was slided.
       */
    lastSlideRange: 0,
    
    /**
       * @var int clonedElements An array of elements that where cloned for for sliding.
       */
    clonedElements: [],
    
    /**
       * Initialize our slider with it's base element (HtmlUlElement),
       * assign our options and set some basic values.
       *
       * @access public
       *
       * @param HtmlUlElement sliderList
       * @param Hash options
       *
       * @return void
       */
    initialize: function(sliderList, options)
    {
        this.element = $(sliderList);
        this.setOptions(options);
        this.element.set('tween', {duration: this.get('duration'), transition: Fx.Transitions.linear, onComplete: this.onComplete.bind(this)});
        this.slideModifier.set('attributeName', (EasySlide.ALIGN.HORIZONTAL === this.get('align', EasySlide.ALIGN.HORIZONTAL)) ? 'width' : 'height');
    },
       
    /**
       * Build our list, eventuelly resorting it, if we were ordered to start
       * with another item than the first.
       * Returns a reference to itself for fluent api support.
       *
       * @access public
       *
       * @return EasySlide
       */
    bootStrap: function()
    {
    	if(this.get('sliderElementsSelector')) {
    		var sliderElementsSelector = this.get('sliderElementsSelector');
    	} else {
    		var sliderElementsSelector = 'li';
    	}
    	
        var items = this.element.getElements(sliderElementsSelector);

        /**
            * Determine our first element.
            */
        var startPos = this.get('startPos', EasySlide.STARTPOS.FIRST);
        var startItemIndex = ((EasySlide.STARTPOS.LAST === startPos) ? (items.length - 1) : ((EasySlide.STARTPOS.FIRST === startPos) ? 0 : startPos));
        /**
            * Then build our slider, resorting our list if this is nessecary to get the 
            * desired first item to the top of the list.
            */
        this.buildSlider(startItemIndex, items);
        /**
            * Then inform anyone who wants to now, that we are ready for action.
            */
        this.fireEvent(EasySlide.EVENT.BUILD_COMPLETE, $H({type: 'EasySlideListEvent', name: 'onBuildComplete', event: EasySlide.EVENT.BUILD_COMPLETE, src: this, current: this.current, initialIndex: startItemIndex}));
        return this;
    },
    
    /**
       * Slide into the given direction with a range of one element.
       * 
       * @access public
       *
       * @param string direction The direction in which to slide. @see { EasySlide.DIRECTION }
       * @param int range Optional,the number of items to slide. Defaults to the slideRange the slider was initialized with. 
       *
       * @return bool
       */
    slide: function(direction, range)
    {
        /**
            * We are alreay sliding, where passed an invalid direction or don't have enough elements to fill even one screen.
            * Abort!
            */
        if (!$chk(EasySlide.DIRECTION[direction]) || (true === this.isSliding )|| (this.listItems.length <= this.get('elPerScreen'))) return false;        
        
        /**
            * Check if we have to slide or if we can save the effort.
            * When running in carousel mode, we always have to slide.
            * In single mode, we'll stop as soon as we have reached either end of the list.
            */
        if (this.isValidDirection(direction) || (EasySlide.ROTATION.CAROUSEL === this.get('rotation')))
        {
            range = range || this.get('slideRange');
            
            this.isSliding = true;
            this.direction = direction;
            /**
                 * If we are running in single rotation mode we need to calculate an eventuell correction value,
                 * to prevent sliding outof our list's boundry.
                 */  
            var slideRange = (EasySlide.ROTATION.SINGLE === this.get('rotation')) ? this.calculateSlideRangeCorrection(range) : range;
            var offset = 0; 
            /**
                 * Setup our list according to the given (and eventuell previously corrected) slide-range. 
                 */
            this.setupList(slideRange);
            /**
                 * Depending on the requested direction we have to react slightly different
                 * when settings things up to slide.
                 */
            if (EasySlide.DIRECTION.R2L === direction || EasySlide.DIRECTION.B2T === direction)
            {
                offset -= this.calculateOffsetPosition(slideRange);
            }
            else
            {
                this.element.get('tween').set((('width' === this.slideModifier.get('attributeName')) ? 'left' : 'top'), -(this.calculateOffsetPosition(slideRange)));
            }
            /**
                 * Notify everyone who's interested that we are about to start sliding.
                 */
            this.fireEvent(EasySlide.EVENT.SLIDE_START, $H({type: 'EasySlideListEvent', name: EasySlide.EVENT.SLIDE_START, src: this, direction: this.direction}));
            /**
                 * To fetch the correct position attribute name, we want to use for sliding, we check if the slider'S
                 * alignement is vertical or horizontal.
                 */
            var posAttrName = ('width' === this.slideModifier.get('attributeName')) ? 'left' : 'top';
            /**
                 * Then we slide using the calculated position offset and position attribute name. 
                 */
            this.element.get('tween').start(posAttrName, offset);
            /**
                 * Remember the amount of items we just slided, so we
                 * are able to correctly clean up/resort the list in our onComplete callback.
                 */
            this.lastSlideRange = slideRange;
            
            return true;
        }
        
        this.fireEvent(EasySlide.EVENT.SLIDE_STOP, $H({type: 'EasySlideListEvent', name: EasySlide.EVENT.SLIDE_STOP, src: this, direction: this.direction}));
        this.loop = false;
        return false;
    },
    
    /**
       * Method to use when you want to slide to a certain item in the list.
       *
       * @access public
       *
       * @param int index The initial index of the item you want to slide to.
       *
       * @return void
       */
    slideToFirst: function(index)
    {
        index = Number(index);
        if (this.current.get('index') === index || index > this.listItems.length - 1 ||  0 > index) return;
        
        var direction = this.determineDirection(this.findItemByInitialIndex(index));
        var slideRange = 1;
        
        if (EasySlide.DIRECTION.L2R === direction)
        {
            var count = this.listItems.length - 1;
            for (; 0 < count && this.listItems[count].get('index') !== index; count--, slideRange++);
        }
        else
        {
            for (; slideRange < this.listItems.length && this.listItems[slideRange].get('index') !== index; slideRange++);
        }
        
        this.slide(direction, slideRange);
    },
    
    /**
       * Method to use when you want to slide a certain item in the list just until it is visible.
       *
       * @access public
       *
       * @param int index The index of the item you want to slide to. Must be the initial list position of the desired element.
       *
       * @return void
       */
    slideIntoScreen: function(index, markAtFirstPosition)
    {
        index = Number(index);
        var markAtFirstPosition = markAtFirstPosition || false;
        if (this.isItemVisible(index) || index > this.listItems.length - 1 ||  0 > index) return;
        
        var direction = this.determineDirection(this.findItemByInitialIndex(index));
        var slideRange =  1;
        
        if (EasySlide.DIRECTION.L2R === direction)
        {
            var count = this.listItems.length - 1;
            for (; 0 < count && this.listItems[count].get('index') !== index; count--, slideRange++);
        }
        else
        {
        	if(markAtFirstPosition) {
        		var count = 1;
        	} else {
        		var count = this.get('elPerScreen');
        	}
            for (; count < this.listItems.length && this.listItems[count].get('index') !== index; count++, slideRange++);
        }
        this.slide(direction, slideRange);
    },
    
    /**
     * Method to use when you want to slide a certain item in the list but with only a certain slide range.
     *
     * @access public
     *
     * @param int index The index of the item you want to slide to. Must be the initial list position of the desired element.
     * @param int range The slide range you want to have, the method only search for the item with the given index in this range steps
     * @return void
     */
    slideInRangeToIndex: function(index, range) {
    	var index = Number(index);
    	var range = range || this.get('slideRange');
    	
    	var itemToFind = this.getItemAt(index);
    	var rangeCounter = 0;
    	for(var i=0; i<this.listItems.length; i++) {
    		if(i>=range && i%range == 0) {
    			rangeCounter = rangeCounter +range;
    		}
    		if(this.listItems[i]==itemToFind){
    			break;
    		}
    	}
    	if(rangeCounter>0) {
    		this.slide( EasySlide.DIRECTION.R2L, rangeCounter);
    	}
    },
    
    /**
       * Check if the requested direction is inside the valid list's boundry.
       *
       * @access public
       *
       * @param string direction The direction to validate.
       *
       * @return boolean
       */
    isValidDirection: function(direction)
    {
        return (direction === EasySlide.DIRECTION.L2R || direction === EasySlide.DIRECTION.T2B) ? this.hasPrev() : this.hasNext();
    },
    
    /**
       * Check if the item at the given index is currently visible or not.
       *
       * @access public
       *
       * @param int index The initial list position of the item you want to check,
       */
    isItemVisible: function(initialIndex)
    {
        var target = this.findItemByInitialIndex(initialIndex);
        return (this.listItems.indexOf(target) < (this.get('elPerScreen')));
    },
    
    /**
       * See if we have a neighbor to our right.
       *
       * @access public
       *
       * @return boolean
       */
    hasNext: function()
    {
        return ((this.current.get('index') + this.get('elPerScreen')) < this.listItems.length);
    },
    
    /**
       * See if we have a neighbor to our left.
       *
       * @access public
       *
       * @return boolean
       */
    hasPrev: function()
    {
        return (0 < this.current.get('index'));
    },
    
    /**
       * Returns the currently first positioned element of the list.
       * 
       * @access public
       *
       * @return Hash
       */
    getCurrentItem: function()
    {
        return this.current;
    },
    
    /**
       * Returns the item at the given index.
       *
       * @access public
       *
       * @param int index
       *
       * @return HtmlLiElement
       */
    getItemAt: function(index)
    {
        return $pick(this.listItems[index], null);
    },
    
    /**
       * Return the item with the initial list position matching the given index.
       *
       * @access public
       *
       * @param int index The searched elements index.
       *
       * @return object Json object containing the found item and it's current position in the list.
       */
    findItemByInitialIndex: function(index)
    {
        var checkItem = function(item)
        {
            return (item.get('index') === index); 
        }
        return $pick(this.listItems.filter(checkItem)[0], null);
    },
    
    /**
       * Return the slider's items.
       *
       * @access public
       *
       * @return Array
       */
    getItems: function()
    {
        return this.listItems;
    },
    
    /**
       * Return the value for an option by the given key.
       *
       * @access public
       *
       * @param string key
       * @param mixed defaultValue
       *
       * @return mixed Null if no value for the given key was found.
       */
    get: function(key, defaultValue)
    {
        defaultValue = $pick(defaultValue, null);
        return $pick(this.options[key], defaultValue);
    },
    
    /**
       * Return the value for an option by the given key.
       *
       * @access public
       *
       * @param string key
       * @param mixed value
       */
    set: function(key, value)
    {
        var numerics = ['duration', 'rotation', 'slideRange', 'pathMode'];
        var tweenProps = ['duration', 'transition'];
        
        if (numerics.contains(key))
        {
            value = Number(value);
        }
        
        if (tweenProps.contains(key))
        {
            this.element.get('tween').options[key] =value;
        }
        
        this.options[key] = value;
    },
    
    /**
       * Fetch all our items from the list and build the slider.
       *
       * @access protected
       *
       * @param initialItemIndex We will rebuild the list order so that the item at the given index is first in the resulting list.
       *
       * @return void
       */
    buildSlider: function(initialItemIndex, listItems)
    {
        var buffer = [];        
        /**
            * Define a function we'll use to initialize our list items...
            */
        var initializeListItem = function(element, pos)
        {
            var item = this.registerItemEvents(this.initItem(element, pos));     
            /**
                * We have to mak sure that we correctly resort the list, if we're past an initial item index, so that
                * that the at the given index is displayed as first with the others following correct order.
                */
            if ((this.get('elPerScreen') + pos < listItems.length) && pos < initialItemIndex - 1)
            {
                /**
                      * As long as the desired initial item has not been reached we buffer the list items 
                      * we have initialized in order to append them at the end.
                      */
                element.inject(this.element);
                buffer.push(item);
            }
            else
            {
                /**
                      * As soon as the desired element is reached we start to build our slider list.
                      */
                this.listItems.push(item);
                this.current = $pick(this.current, item);
            }
        }.bind(this);        
        /**
            * ... and call it on every item in order o build our list.
            */
        $each(listItems, initializeListItem);  
        /**
            * We have to set the width for the list, that we calculated while we were buildung the list,
            * in order to make sure that we won't break the floating of the list elements.
            */
        this.element.setStyle(this.slideModifier.get('attributeName'), this.slideModifier.get('value'));
        /**
            * Then make sure we append our buffered elements.
            */
        this.listItems.extend(buffer);
    },
    
    /**
      * Initialize and add the given item to our slider.
      *
      * @param HtmlLiElement The list element to init.
      * @param int The initial position of the given element.
      * 
      * @access protected
      *
      * @return Hash
      */
    initItem: function(element, pos)
    {
        var coords = element.getCoordinates();
        var slideRange = this.slideModifier.get('value');
        var styleProp = 'height' === this.slideModifier.get('attributeName') ? 'height' : 'width';
        var marginStyle = 'margin-' + (styleProp === 'height' ? 'bottom' : 'right');
        element.setStyle(marginStyle, this.get('space', 0));
        slideRange += coords[styleProp] + this.get('space', 0);
        this.slideModifier.set('value', slideRange);
        return $H({index: pos, element: element});
    },
    
    /**
       * Register some events to the given list item.
       *
       * @param Hash item The hash representing the list item to register the events on.
       *
       * @access protected
       *
       * @return Hash
       */
    registerItemEvents: function(item)
    {
        /**
           * Create a couple of callback functions,
           * we'll bind to our item events.
           * One for papa (list item was clicked)...
           */
        var propagateClick = function (event, item) 
        { 
            this.fireEvent(EasySlide.EVENT.ITEM_CLICKED, $H({ 
                    type: 'EasySlideItemEvent',
                    name: 'onMouseClicked',
                    event: EasySlide.EVENT.ITEM_CLICKED, 
                    srcEvent: event, 
                    src: item
                })
            ); 
        }.bindWithEvent(this, item);        
        /** 
           * ... one for mama (mouse over a list item).
           */
        var propagateItemEnter = function (event, item) 
        { 
            this.fireEvent(EasySlide.EVENT.ITEM_MOUSEENTER, $H({ 
                    type: 'EasySlideItemEvent',
                    name: 'onMouseEnter',
                    event: EasySlide.EVENT.ITEM_MOUSEENTER, 
                    srcEvent: event, 
                    src: item
                })
            ); 
        }.bindWithEvent(this, item);        
        /**
           * ... and one for whatever you like ^^(mouse left a list item),
           * that oughta be enough.
           */
        var propagateItemLeave = function (event, item) 
        { 
            this.fireEvent(EasySlide.EVENT.ITEM_MOUSELEAVE, $H({
                    type: 'EasySlideItemEvent',
                    name: 'onMouseLeave',
                    event: EasySlide.EVENT.ITEM_MOUSELEAVE, 
                    srcEvent: event, 
                    src: item
                }
            ));
        }.bindWithEvent(this, item);
        
        /**
           * Then register the callbacks to our desired events.
           */
        item.get('element').addEvents({ 
            'click': propagateClick, 
            'mouseenter': propagateItemEnter, 
            'mouseleave': propagateItemLeave 
        });
        
        return item;
    },
    
    /**
        * Returns the corrected slide range to use, in order to prevent that 
        * we slide out of the list's boundry when running in single mode.
        * 
        * @access protected
        *
        * @param int  slideRange The range that we are demanded to slide.
        *
        * @return int
        */
    calculateSlideRangeCorrection: function(slideRange)
    {
        var targetIndex = this.listItems.length - slideRange;
        var offsetCorrection =  0;
        
        if (EasySlide.DIRECTION.R2L === this.direction || EasySlide.DIRECTION.B2T === this.direction)
        {
            /**
                  * For R2L and B2T we have to start off with a different value for targetIndex,
                  * since we are sliding in the oposite direction.
                  */
            targetIndex = this.get('elPerScreen') + slideRange - 1;
            var correctedIndex =  targetIndex;
            
            if (targetIndex > this.listItems.length - 1)
            {
                while (correctedIndex > this.listItems.length - 1 || this.listItems[correctedIndex].get('index') < this.current.get('index') && 0 < correctedIndex)
                {
                    correctedIndex--;
                }
            }
            else
            {
                while (this.listItems[correctedIndex].get('index') < this.listItems[this.get('elPerScreen')].get('index'))
                {
                    correctedIndex--;
                }
            }
            offsetCorrection = targetIndex - correctedIndex;
            return slideRange - offsetCorrection;
        }
                  
        if (0 >= targetIndex)
        {
            /**
                 * If the target-index for the given slide-range is out of our item-list's boundry,
                 * we set it to point at the element with the initial index 0 (the initially first element in our list).
                 */
            var rightIndex = this.listItems.length - 1;
            var leftIndex = rightIndex - 1;
            
            while (this.listItems[leftIndex].get('index') < this.listItems[rightIndex].get('index'))
            {
                rightIndex--;
                leftIndex--;
            }
            
            offsetCorrection = slideRange - (this.listItems.length - rightIndex);
        }
        else
        {
            /**
                   * If the the target is an item with an initial index greater than the initial index
                   * of the currently active item, then we know we have to decrease our slide-range
                   * untill we have reached the (initial) first item.
                  */
            var correctedIndex = targetIndex;
        
            while (correctedIndex < this.listItems.length && this.listItems[correctedIndex].get('index') > this.listItems[0].get('index'))
            {
                correctedIndex++;
            }
            offsetCorrection =  correctedIndex - targetIndex;
        }
        return slideRange - offsetCorrection;
    },
    
   /**
      * Prepares the list for sliding and returns the slide-range for the next slide. 
      * 
      * @access protected
      *
      * @param int  slideRange The range that we are demanded to slide.
      *
      * @return void
      */
    setupList: function(slideRange)
    {
        var styleProp = ('height' === this.slideModifier.get('attributeName')) ? 'height' : 'width';
        var toRightOrDown = (EasySlide.DIRECTION.L2R === this.direction || EasySlide.DIRECTION.T2B === this.direction);
        
        for (itemsToSlide = slideRange, itemsAvailable = this.listItems.length - this.get('elPerScreen');  0 < itemsToSlide; itemsToSlide--, itemsAvailable--)
        {
            if (toRightOrDown)
            {
                this.current = this.listItems.pop();
                this.listItems.unshift(this.current);
            }
            
            if (itemsAvailable <= 0)
            {
                if (toRightOrDown)
                {
                    var element = this.current.get('element');
                    newElement = element.clone();
                    this.clonedElements.push(element);
                    this.current.set('element', newElement);
                    newElement.inject(this.element, 'top');
                    this.element.setStyle(styleProp, this.element.getCoordinates().width + newElement.getCoordinates().width + this.get('space', 0));
                }
                else
                {
                    var curItem = this.listItems[Math.abs(itemsAvailable)];
                    var element = curItem.get('element');
                    var newElement = element.clone();
                    curItem.set('element', newElement);
                    this.clonedElements.push(element);
                    newElement.inject(this.element, 'bottom');
                    this.element.setStyle(styleProp, this.element.getCoordinates().width + newElement.getCoordinates().width + this.get('space', 0));
                }
            }
            else
            {
                this.current.get('element').inject(this.element, 'top');
            }
        }
    },
    
    /**
       * Calculate the width/height we would slide to, for the given slide-range.
       *
       * @access protected
       *
       * @param int slideRange
       *
       * @return int
       */
    calculateOffsetPosition: function(slideRange)
    {
        var coords = this.current.get('element').getCoordinates();
        return (coords[this.slideModifier.get('attributeName')] * slideRange) + (this.get('space', 0) * (slideRange));
    },
    
    /**
       * Method called each time an item has finished sliding through the screen.
       * We resort our listItems, if the direction was right to left, in order to provide a clean initial state for the next call to slide.
       * Then we check, if we are currently sliding towards a target and if necessary, check if we have already reached it.
       * Finally we unlock our slider for upcoming slide requests and check if we are in a loop to continue sliding if reuqired.
       * 
       * @access protected
       *
       * @return void
       */
    onComplete: function()
    {
        var styleProp = 'height' === this.slideModifier.get('attributeName') ? 'height' : 'width';
        /**
           * Destroy all elements we might have cloned, while setting up the slide, 
           * which's complete event we are now handling.
           */
        while (element = this.clonedElements.pop())
        {
            this.element.setStyle(styleProp, this.element.getCoordinates().width - (element.getCoordinates().width + this.get('space', 0)));
            element.destroy();
        }
        /**
            * Depending on the direction we where sliding, we need to do some final cleaning up,
            */          
        if  (EasySlide.DIRECTION.R2L === this.direction || EasySlide.DIRECTION.B2T === this.direction)
        {
            /** 
                 * If we were slinding from right to left, we will have  {this.lastSlideRange} number of not visible items
                 * left to the currently first displayed in the list. We need to pack these guys to the end of the list,
                 * so everythings nice 'n tidy when we are demanded the next slide.
                 */
            for (count = this.lastSlideRange;  0 < count; count--)
            {
                var item = this.listItems.shift();
                item.get('element').inject(this.element);
                this.listItems.push(item);
            }
            
            this.current = this.listItems[0];
            var attribute = ('width' === this.slideModifier.get('attributeName')) ? 'left' : 'top';
            /**
                 * Now that those guys have been moved to the end,we set our list position to 0, 
                 * we set our our first item as current and have everything setup for our next slide.
                 */
            this.element.get('tween').set(attribute, 0);
            this.current = this.listItems[0];
        }
        
        this.isSliding = false;
        this.fireEvent(EasySlide.EVENT.SLIDE_PROGRESS, $H({type: 'EasySlideListEvent', name: EasySlide.EVENT.SLIDE_PROGRESS, src: this.current, direction: this.direction}));
            
        if (true === this.loop)
        {
            this.slide(this.direction);
            return;
        }
        this.fireEvent(EasySlide.EVENT.SLIDE_STOP, $H({type: 'EasySlideListEvent', name: EasySlide.EVENT.SLIDE_STOP, src: this, direction: this.direction}));
    },
    
    /**
       * Find out what direction we need to slide to in order to reach the given element.
       * Depending on the value set for options.pathMode the direction following will be returned:
       * - initialOrder: The direction will be determined considering the initial order of the list items.
       * - fastestPath: The direction will be returned by checking the fastest way to reach target. 
       * 
       * @access protected
       *
       * @param object The element we want to reach.
       *
       * @return string The direction to take.
       */
    determineDirection: function(targetItem)
    {
        var direction = (this.current.get('index') >= targetItem.get('index')) ? EasySlide.DIRECTION.L2R : EasySlide.DIRECTION.R2L;
        
        if (this.get('rotation') === EasySlide.ROTATION.CAROUSEL && this.get('pathMode') === EasySlide.PATHMODE.FASTPATH)
        {
            var leftDiff = this.listItems.length - this.listItems.indexOf(targetItem);            
            direction = (leftDiff < this.listItems.indexOf(targetItem)) ? EasySlide.DIRECTION.L2R : EasySlide.DIRECTION.R2L;
        }
        
        if (EasySlide.ALIGN.VERTICAL === this.get('align'))
        {
            direction = (direction === EasySlide.DIRECTION.L2R) ? EasySlide.DIRECTION.T2B : EasySlide.DIRECTION.B2T;
        }
        return direction;
    }
});
/**
  * Options you can use to influence the slide behaviour.
  */
EasySlide.ROTATION = {
    /**
      * Endless rotation in both directions.
      */
    CAROUSEL: 1, 
    /**
      * The slider will use the first and last item as boundries for sliding.
      */
    SINGLE: 2
};
/**
  * Controlls the way we decide which direction to take when told to slide to an
  * item that is not directly next to us.
  */
EasySlide.PATHMODE = {
    /**
      * Requires EasySlide.ROTATION.CAROUSEL
      * Prefer the shorter path prior to the correct order. 
      */
    FASTPATH: 4, 
    /**
      * Always choose the direction so that we slide the items in the correct order.
      */
    ORDERED: 8
};
/**
 * Directions you can pass to EasySlide when using slide().
 */
EasySlide.DIRECTION = { L2R: 'L2R', R2L: 'R2L', T2B: 'T2B', B2T: 'B2T' };
/**
  * Determines whether we are running a horizontal or vertically aligned slider.
  */
EasySlide.ALIGN = { VERTICAL: 'vertical', HORIZONTAL: 'horizontal' };
/**
 * Tell EasySlide to start on the first or the last item.
 */
EasySlide.STARTPOS = { FIRST: 'first', LAST: 'last' };
/**
  * Events you can register for.
  */
EasySlide.EVENT = {
    /**
      * Fired when an item was clicked.
      */
    ITEM_CLICKED: 'onItemClicked', 
    /**
      * Fired when the mouse enters a slider item.
      */
    ITEM_MOUSEENTER: 'onItemMouseEnter', 
    /**
      * Fired when the mouse leaves a slider item.
      */
    ITEM_MOUSELEAVE: 'onItemMouseLeave', 
    /**
      * Fired when the slider finished building.
      */
    BUILD_COMPLETE: 'onBuildComplete', 
    /**
      * Fired everytime the slider starts to slide.
      */
    SLIDE_START: 'onSlideStart', 
    /**
      * Fired every time the slider stops to slide.
      */
    SLIDE_STOP: 'onSlideStop', 
    /**
      * Fired everytime an item has finished sliding.
      */
    SLIDE_PROGRESS: 'onSlideProgress',
    /**
      * Fired when an item is reached that was selected by a call to slideTo() or slideIntoScreen().
      */
    TARGET_REACHED: 'onTargetReached'
};
