/**
 * jquery.scrollable 0.13. Put your HTML scroll.
 * 
 * http://flowplayer.org/tools/scrollable.html
 *
 * Copyright (c) 2008 Tero Piirainen (support@flowplayer.org)
 *
 * Released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 * 
 * >> Basically you can do anything you want but leave this header as is <<
 *
 * Since  : 0.10 - 03/01/2008
 * Version: 0.13 - Wed Nov 05 2008 12:04:04 GMT-0000 (GMT+00:00)
 */
(function($) {
                
        // constructor
        function Scrollable(el, config) {   
                
                // current instance
                var self = this;  
                
                if (!Scrollable.current) {
                        Scrollable.current = this;      
                }
                
                var opts = {                                                            
                        size: 3,
                        vertical:false,                         
                        activeClass:'active',
                        speed: 300,
                        onSeek: null,
                        clickable: true,
                        
                        // jquery selectors
                        items: '.items',
                        prev:'.prev',
                        next:'.next',
                        navi:'.navi',
                        naviItem:'span', 
                        loop: false
                }; 

                
                this.opts = $.extend(opts, config);  
                this.opts.horizontal = !opts.vertical;
                
                // root / itemRoot
                this.root = $(el);
                var root = this.root;
                var itemRoot = $(opts.items, root);                     
                if (!itemRoot.length)  { itemRoot = root; }  
                
                
                // wrap itemRoot.children() inside container
                itemRoot.css({position:'relative', overflow:'hidden', visibility:'visible'});
                itemRoot.children().wrapAll('<div class="__scrollable" style="position:absolute"/>'); 
                
                this.wrap = itemRoot.find(":first");
                this.wrap.css(opts.horizontal ? "width" : "height", "200000em").after('<br clear="all" />');            
                this.items = this.wrap.children();
                this.index = 0;

                
                // set dimensions based on offsets of the two first elements
                if (opts.horizontal) {
                        itemRoot.width(opts.size * (this.items.eq(1).offset().left - this.items.eq(0).offset().left) -2);       
                } else {
/*r                        itemRoot.height(opts.size * (this.items.eq(1).offset().top - this.items.eq(0).offset().top) -2);        */
                } 

                // mousewheel
                if ($.isFunction($.fn.mousewheel)) { 
                        root.bind("mousewheel.scrollable", function(e, delta)  { 
                                self.move(-delta, 50);          
                                return false;
                        });
                }  
                
                // item.click()
                if (opts.clickable) {
                        this.items.each(function(index, arg) {
                                $(this).bind("click.scrollable", function() {
                                        self.click(index);              
                                });
                        });                             
                }


                this.activeIndex = 0;
                
                // prev
                $(opts.prev, root).click(function() { 
                        self.prev(); 
                });
                

                // next
                $(opts.next, root).click(function() { 
                        self.next(); 
                });
                

                // navi                         
                $(opts.navi, root).each(function() {                            
                        var navi = $(this);
                        
                        var status = self.getStatus();
                        
                        // generate new entries
                        if (navi.is(":empty")) {
                                for (var i = 0; i < status.pages; i++) {                
                                        
                                        var item = $("<" + opts.naviItem + "/>").attr("page", i).click(function(e) {                                                    
                                                var el = $(this);
                                                el.parent().children().removeClass(opts.activeClass);
                                                el.addClass(opts.activeClass);
                                                self.setPage(el.attr("page"));
                                                e.preventDefault();
                                        });
                                        
                                        if (i === 0) { item.addClass(opts.activeClass); }
                                        navi.append(item);                                      
                                }
                                
                        // assign onClick events to existing entries
                        } else {
                                
                                // find a entries first -> syntaxically correct
                                var els = navi.find("a");
                                
                                if (!els.length) { 
                                        els = navi.children(); 
                                }
                                
                                els.each(function(i)  {
                                        var item = $(this);
                                        item.attr("page", i);
                                        if (i === 0) { item.addClass(opts.activeClass); }
                                        
                                        item.click(function() {
                                                navi.find("." + opts.activeClass).removeClass(opts.activeClass);
                                                item.addClass(opts.activeClass);
                                                self.setPage(item.attr("page"));
                                        });
                                        
                                });
                        }
                        
                });                     
        } 
        
        
        // methods
        $.extend(Scrollable.prototype, {  
                        
                        
                getVersion: function() {
                        return '@VERSION';      
                },

                click: function(index) {
                        
                        var item = this.items.eq(index);
                        var klass = this.opts.activeClass;                      
                        
                        if (!item.hasClass(klass) && (index >= 0 || index < this.items.size())) {                               
                                this.items.removeClass(klass);
                                item.addClass(klass);
                                var delta = Math.floor(this.opts.size / 2);
                                var to = index - delta;

                                if (to !== this.activeIndex) {
                                        this.seekTo(to);                
                                }                                
                        } 
                },
                
                
                getStatus: function() {
                        var len =  this.items.size();
                        return {
                                size: this.opts.size,
                                total: len, 
                                index: this.index,  
                                pages: Math.ceil(len / this.opts.size),
                                page: Math.ceil(this.index / this.opts.size)
                        };
                }, 

                
                // all other seeking functions depend on this generic seeking function          
                seekTo: function(index, time) {
                        
                        if (index < 0) { index = 0; }
                        var max = Math.min(index, this.items.length - this.opts.size);                          
                        
                        if (index <= max) { 
                                
                                var item = this.items.eq(index);                        
                                this.index = index;     

                                if (this.opts.horizontal) {
                                        var left = this.wrap.offset().left - item.offset().left;                                
                                        this.wrap.animate({left: left}, time || this.opts.speed);
                                        
                                } else {
                                        var top = this.wrap.offset().top - item.offset().top;                                   
                                        this.wrap.animate({top: top}, time || this.opts.speed);                                                 
                                }
                                
                                Scrollable.current = this; 
                        } 
                        

                        // custom onSeek callback
                        if ($.isFunction(this.opts.onSeek)) {
                                this.opts.onSeek.call(this);
                        }
                        
                        // navi status update
                        var navi = $(this.opts.navi, this.root);
                        
                        if (navi.length) {
                                var klass = this.opts.activeClass;
                                var page = Math.ceil(index / this.opts.size);
                                page = Math.min(page, navi.children().length - 1);
                                navi.children().removeClass(klass).eq(page).addClass(klass);
                        } 
                        
                        this.activeIndex = index;                       
                        return true; 
                },
                
                        
                move: function(offset, time) {
                        var to = this.index + offset;
                        if (this.opts.loop && to > (this.items.length - this.opts.size)) {
                                to = 0; 
                        }
                        this.seekTo(to, time);
                },
                
                next: function(time) {
                        this.move(1, time);     
                },
                
                prev: function(time) {
                        this.move(-1, time);    
                },
                
                movePage: function(offset, time) {
                        this.move(this.opts.size * offset, time);               
                },
                
                setPage: function(page, time) {
                        var size = this.opts.size;
                        var index = size * page;
                        var lastPage = index + size >= this.items.size(); 
                        if (lastPage) {
                                index = this.items.size() - this.opts.size;
                        }
                        this.seekTo(index, time);
                },
                
                prevPage: function(time) {
                        this.setPage(this.getStatus().page - 1, time);
                },  

                nextPage: function(time) {
                        this.setPage(this.getStatus().page + 1, time);
                }, 
                
                begin: function(time) {
                        this.seekTo(0, time);   
                },
                
                end: function(time) {
                        this.seekTo(this.items.size() - this.opts.size, time);  
                }
                
        });  
        
        
        
        // keyboard
        $(window).bind("keypress.scrollable", function(evt) {
                
                var el = Scrollable.current;    
                if (!el) { return; }
                        
                if (el.opts.horizontal && (evt.keyCode == 37 || evt.keyCode == 39)) {
                        el.move(evt.keyCode == 37 ? -1 : 1);
                        return evt.preventDefault();
                }       
                
                if (!el.opts.horizontal && (evt.keyCode == 38 || evt.keyCode == 40)) {
                        el.move(evt.keyCode == 38 ? -1 : 1);
                        return evt.preventDefault();
                }
                
                return true;
                
        });     
                
        // jQuery plugin implementation
        jQuery.prototype.scrollable = function(opts, arg0, arg1) { 
                        
                // return API associated with this instance
                if (!opts || typeof opts == 'number') {
                        var index = opts || 0;
                        var el = $.data(this.get()[index], "scrollable");
                        if (el) { return el; }
                }
                
                this.each(function() {
                                
                        // @deprecated way of accessing API
                        if (typeof opts == "string") {
                                var el = $.data(this, "scrollable");
                                el[opts].apply(el, [arg0, arg1]);
                                
                        // create new Scrollable instance
                        } else { 
                                var instance = new Scrollable(this, opts);      
                                $.data(this, "scrollable", instance);
                        }
                });
                
                return this;
        };
                        
        
})(jQuery);




