/**
 * jQuery TOOLS plugin :: scrollable.circular 0.5.0
 * 
 * Copyright (c) 2009 Tero Piirainen
 * http://flowplayer.org/tools/scrollable.html#circular
 *
 * Dual licensed under MIT and GPL 2+ licenses
 * http://www.opensource.org/licenses
 *
 * Launch  : September 2009
 * Date: ${date}
 * Revision: ${revision} 
 */
(function($) {

	// version number
	var t = $.tools.scrollable; 
	t.plugins = t.plugins || {};
	
	t.plugins.circular = {
		version: '0.5.0', 
		conf: { 
			api: false,
			clonedClass: 'cloned'
		} 		
	};

	
	$.fn.circular = function(opts)  {
	
		var config = $.extend({}, t.plugins.circular.conf), ret;
		$.extend(config, opts);
		
		this.each(function() {			
 
			var api = $(this).scrollable(),
				 items = api.getItems(), 
				 conf = api.getConf(), 
				 wrap = api.getItemWrap(), 
				 index = 0;
				 
			if (api) { ret = api; }
				
			// too few items. no need for this plugin.
			if (items.length < conf.size) { return false; }
			

			// clone first visible elements and append them to the end			
			items.slice(0, conf.size).each(function(i) {
  				$(this).clone().appendTo(wrap).click(function()  {
					api.click(items.length + i);
					
				}).addClass(config.clonedClass);			
			});			
			
			// clone last set of elements to the beginning in reversed order
			var tail = $.makeArray(items.slice(-conf.size)).reverse();
			
  			$(tail).each(function(i) {
				$(this).clone().prependTo(wrap).click(function()  {
					api.click(-i -1);			
					
				}).addClass(config.clonedClass);				
			});
			
			var allItems = wrap.children(conf.item);
			
			// skip the clones at the beginning
			api.seekTo(conf.size, 0);	

			function seek(i) {
				
				var item = allItems.eq(i);
				
				if (conf.vertical) {						
					wrap.css({top: -item.position().top});
				} else {
					wrap.css({left: -item.position().left});							
				}					
			}

			// overridden scrollable API methods
			$.extend(api, {

				move: function(offset, time, fn, click) {
					
					var to = index + offset + conf.size;				
					var exceed = to > api.getSize() - conf.size;
					
					if (to < 0 || exceed) {
						var fix = index + conf.size + (exceed ? -items.length : items.length);
						seek(fix);
						to = fix + offset;
					} 

					if (click) {
						allItems.removeClass(conf.activeClass)
							.eq(to + Math.floor(conf.size / 2)).addClass(conf.activeClass);
					}
								
					return api.seekTo(to, time, fn);
				},			
				
				begin: function(time, fn) {
					return this.seekTo(conf.size, time, fn);	
				},
				
				end: function(time, fn) {				
					return this.seekTo(items.length, time, fn);	
				},
	
				click: function(i, time, fn) {		
					
					if (!conf.clickable) { return self; }
					if (conf.size == 1) { return this.next(); }
					
					var to = i - index, klass = conf.activeClass;				
					to -= Math.floor(conf.size / 2);				
					
					return this.move(to, time, fn, true);
				},
	
				getIndex: function() {
					return index;	
				},
				
				getVisibleItems: function() {
					var i = index + conf.size;
					return allItems.slice(i, i + conf.size);	
				},
	
				getPageAmount: function()  {
					return Math.ceil(items.length / conf.size);		
				},
				
				getPageIndex: function()  {
					var i = api.getIndex();
					return Math.ceil((i - i % conf.size) / conf.size);		
				}	
				
			});  
			
			// navi buttons are never disabled 
			api.onSeek(function(i) {		
				index = i - conf.size;
				
				// navi buttons are never disabled
				api.getNaviButtons().removeClass(conf.disabledClass);
			});	
			
				
		});
		
		return config.api ? ret : this;
		
	};

		
})(jQuery);
		
