/**
 * @license 
 * jQuery Tools @VERSION Scrollable - New wave UI design
 * 
 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
 * 
 * http://flowplayer.org/tools/scrollable.html
 *
 * Since: March 2008
 * Date: @DATE 
 */
(function($) { 

	// static constructs
	$.tools = $.tools || {version: '@VERSION'};
	
	$.tools.scrollable = {
		
		conf: {	
			activeClass: 'active',
			circular: false,
			clonedClass: 'cloned',
			disabledClass: 'disabled',
			easing: 'swing',
			initialIndex: 0,
			item: null,
			items: '.items',
			keyboard: true,
			mousewheel: false,
			next: '.next',   
			prev: '.prev', 
			speed: 400,
			vertical: false,
			wheelSpeed: 0
		} 
	};
					
	// get hidden element's width or height even though it's hidden
	function dim(el, key) {
		var v = parseInt(el.css(key), 10);
		if (v) { return v; }
		var s = el[0].currentStyle; 
		return s && s.width && parseInt(s.width, 10);	
	}

	function find(root, query) { 
		var el = $(query);
		return el.length < 2 ? el : root.parent().find(query);
	}
	
	var current;		
	
	// constructor
	function Scrollable(root, conf) {   
		
		// current instance
		var self = this, 
			 fire = root.add(self),
			 itemWrap = root.children(),
			 index = 0,
			 vertical = conf.vertical;
				
		if (!current) { current = self; } 
		if (itemWrap.length > 1) { itemWrap = $(conf.items, root); }
		
		// methods
		$.extend(self, {
				
			getConf: function() {
				return conf;	
			},			
			
			getIndex: function() {
				return index;	
			}, 

			getSize: function() {
				return self.getItems().size();	
			},

			getNaviButtons: function() {
				return prev.add(next);	
			},
			
			getRoot: function() {
				return root;	
			},
			
			getItemWrap: function() {
				return itemWrap;	
			},
			
			getItems: function() {
				return itemWrap.children(conf.item).not("." + conf.clonedClass);	
			},
							
			move: function(offset, time) {
				return self.seekTo(index + offset, time);
			},
			
			next: function(time) {
				return self.move(1, time);	
			},
			
			prev: function(time) {
				return self.move(-1, time);	
			},
			
			begin: function(time) {
				return self.seekTo(0, time);	
			},
			
			end: function(time) {
				return self.seekTo(self.getSize() -1, time);	
			},	
			
			focus: function() {
				current = self;
				return self;
			},
			
			addItem: function(item) {
				item = $(item);
				
				if (!conf.circular)  {
					itemWrap.append(item);
				} else {
					$(".cloned:last").before(item);
					$(".cloned:first").replaceWith(item.clone().addClass(conf.clonedClass)); 						
				}
				
				fire.trigger("onAddItem", [item]);
				return self;
			},
			
			
			/* all seeking functions depend on this */		
			seekTo: function(i, time, fn) {	
				
				// avoid seeking from end clone to the beginning
				if (conf.circular && i === 0 && index == -1 && time !== 0) { return self; }
				
				// check that index is sane
				if (!conf.circular && i < 0 || i > self.getSize() || i < -1) { return self; }
				
				var item = i;
			
				if (i.jquery) {
					i = self.getItems().index(i);	
				} else {
					item = self.getItems().eq(i);
				}  
				
				// onBeforeSeek
				var e = $.Event("onBeforeSeek"); 
				if (!fn) {
					fire.trigger(e, [i, time]);				
					if (e.isDefaultPrevented() || !item.length) { return self; }			
				}  
	
				var props = vertical ? {top: -item.position().top} : {left: -item.position().left};  
				
				index = i;
				current = self;  
				if (time === undefined) { time = conf.speed; }   
				
				itemWrap.animate(props, time, conf.easing, fn || function() { 
					fire.trigger("onSeek", [i]);		
				});	 
				
				return self; 
			}					
			
		});
				
		// callbacks	
		$.each(['onBeforeSeek', 'onSeek', 'onAddItem'], function(i, name) {
				
			// configuration
			if ($.isFunction(conf[name])) { 
				$(self).bind(name, conf[name]); 
			}
			
			self[name] = function(fn) {
				$(self).bind(name, fn);
				return self;
			};
		});  
		
		// circular loop
		if (conf.circular) {
			
			var cloned1 = self.getItems().slice(-1).clone().prependTo(itemWrap),
				 cloned2 = self.getItems().eq(1).clone().appendTo(itemWrap);
				
			cloned1.add(cloned2).addClass(conf.clonedClass);
			
			self.onBeforeSeek(function(e, i, time) {

				
				if (e.isDefaultPrevented()) { return; }
				
				/*
					1. animate to the clone without event triggering
					2. seek to correct position with 0 speed
				*/
				if (i == -1) {
					self.seekTo(cloned1, time, function()  {
						self.end(0);		
					});          
					return e.preventDefault();
					
				} else if (i == self.getSize()) {
					self.seekTo(cloned2, time, function()  {
						self.begin(0);		
					});	
				}
				
			});
			
			// seek over the cloned item
			self.seekTo(0, 0);
		}
		
		// next/prev buttons
		var prev = find(root, conf.prev).click(function() { self.prev(); }),
			 next = find(root, conf.next).click(function() { self.next(); });	
		
		if (!conf.circular && self.getSize() >= 1) {
			
			self.onBeforeSeek(function(e, i) {
				prev.toggleClass(conf.disabledClass, i <= 0);
				next.toggleClass(conf.disabledClass, i >= self.getSize() -1);
			}); 
		}
			
		// mousewheel support
		if (conf.mousewheel && $.fn.mousewheel) {
			root.mousewheel(function(e, delta)  {
				if (conf.mousewheel) {
					self.move(delta < 0 ? 1 : -1, conf.wheelSpeed || 50);
					return false;
				}
			});			
		}
		
		if (conf.keyboard)  {
			
			$(document).bind("keydown.scrollable", function(evt) {

				// skip certain conditions
				if (!conf.keyboard || evt.altKey || evt.ctrlKey || $(evt.target).is(":input")) { return; }
				
				// does this instance have focus?
				if (conf.keyboard != 'static' && current != self) { return; }
					
				var key = evt.keyCode;
			
				if (vertical && (key == 38 || key == 40)) {
					self.move(key == 38 ? -1 : 1);
					return evt.preventDefault();
				}
				
				if (!vertical && (key == 37 || key == 39)) {					
					self.move(key == 37 ? -1 : 1);
					return evt.preventDefault();
				}	  
				
			});  
		}
		
		// initial index
		$(self).trigger("onBeforeSeek", [conf.initialIndex]);
	} 

		
	// jQuery plugin implementation
	$.fn.scrollable = function(conf) { 
			
		// already constructed --> return API
		var el = this.data("scrollable");
		if (el) { return el; }		 

		conf = $.extend({}, $.tools.scrollable.conf, conf); 
		
		this.each(function() {
			el = new Scrollable($(this), conf);
			$(this).data("scrollable", el);	
		});
		
		return conf.api ? el: this; 
		
	};
			
	
})(jQuery);



/**
* @license 
* jQuery Tools @VERSION / Scrollable Autoscroll
* 
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
* 
* http://flowplayer.org/tools/scrollable/autoscroll.html
*
* Since: September 2009
* Date: @DATE 
*/
(function ($) {

    var t = $.tools.scrollable;

    t.autoscroll = {

        conf: {
            autoplay: true,
            interval: 3000,
            autopause: true
        }
    };

    // jQuery plugin implementation
    $.fn.autoscroll = function (conf) {

        if (typeof conf == 'number') {
            conf = { interval: conf };
        }

        var opts = $.extend({}, t.autoscroll.conf, conf), ret;

        this.each(function () {

            var api = $(this).data("scrollable");
            if (api) { ret = api; }

            // interval stuff
            var timer, hoverTimer, stopped = true;

            api.play = function () {

                // do not start additional timer if already exists
                if (timer) { return; }

                stopped = false;

                // construct new timer
                timer = setInterval(function () {
                    api.next();
                }, opts.interval);

                api.next();
            };

            api.pause = function () {
                timer = clearInterval(timer);
            };

            // when stopped - mouseover won't restart 
            api.stop = function () {
                api.pause();
                stopped = true;
            };

            /* when mouse enters, autoscroll stops */
            if (opts.autopause) {
                api.getRoot().add(api.getNaviButtons()).hover(function () {
                    api.pause();
                    clearInterval(hoverTimer);

                }, function () {
                    if (!stopped) {
                        hoverTimer = setTimeout(api.play, opts.interval);
                    }
                });
            }

            if (opts.autoplay) {
                setTimeout(api.play, opts.interval);
            }

        });

        return opts.api ? ret : this;

    };

})(jQuery);


/**
* @license 
* jQuery Tools @VERSION / Scrollable Navigator
* 
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
*
* http://flowplayer.org/tools/scrollable/navigator.html
*
* Since: September 2009
* Date: @DATE 
*/
(function ($) {

    var t = $.tools.scrollable;

    t.navigator = {

        conf: {
            navi: '.navi',
            naviItem: null,
            activeClass: 'active',
            indexed: false,
            idPrefix: null,

            // 1.2
            history: false
        }
    };

    function find(root, query) {
        var el = $(query);
        return el.length < 2 ? el : root.parent().find(query);
    }

    // jQuery plugin implementation
    $.fn.navigator = function (conf) {

        // configuration
        if (typeof conf == 'string') { conf = { navi: conf }; }
        conf = $.extend({}, t.navigator.conf, conf);

        var ret;

        this.each(function () {

            var api = $(this).data("scrollable"),
				 navi = find(api.getRoot(), conf.navi),
				 buttons = api.getNaviButtons(),
				 cls = conf.activeClass,
				 history = conf.history && $.fn.history;

            // @deprecated stuff
            if (api) { ret = api; }

            api.getNaviButtons = function () {
                return buttons.add(navi);
            };


            function doClick(el, i, e) {
                api.seekTo(i);
                if (history) {
                    if (location.hash) {
                        location.hash = el.attr("href").replace("#", "");
                    }
                } else {
                    return e.preventDefault();
                }
            }

            function els() {
                return navi.find(conf.naviItem || '> *');
            }

            function addItem(i) {

                var item = $("<" + (conf.naviItem || 'a') + "/>").click(function (e) {
                    doClick($(this), i, e);

                }).attr("href", "#" + i);

                // index number / id attribute
                if (i === 0) { item.addClass(cls); }
                if (conf.indexed) { item.text(i + 1); }
                if (conf.idPrefix) { item.attr("id", conf.idPrefix + i); }

                return item.appendTo(navi);
            }


            // generate navigator
            if (els().length) {
                els().each(function (i) {
                    $(this).click(function (e) {
                        doClick($(this), i, e);
                    });
                });

            } else {
                $.each(api.getItems(), function (i) {
                    addItem(i);
                });
            }

            // activate correct entry
            api.onBeforeSeek(function (e, index) {
                var el = els().eq(index);
                if (!e.isDefaultPrevented() && el.length) {
                    els().removeClass(cls).eq(index).addClass(cls);
                }
            });

            function doHistory(evt, hash) {
                var el = els().eq(hash.replace("#", ""));
                if (!el.length) {
                    el = els().filter("[href=" + hash + "]");
                }
                el.click();
            }

            // new item being added
            api.onAddItem(function (e, item) {
                item = addItem(api.getItems().index(item));
                if (history) { item.history(doHistory); }
            });

            if (history) { els().history(doHistory); }

        });

        return conf.api ? ret : this;

    };

})(jQuery);			

