/**

 * 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: 5,

			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 {

			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);







