 /*
**
**
** File:         	jquery.kaldeera.js (module: SlideShow)
** Version:			1.0
** Description:     Adds Kaldeera plugins to jQuery.
** Author:          Pedro Castro -- pedro.castro@kaldeera.com
** Created: 		18/Dic/2009
** Last modified:   18/Dic/2009
**
**
** (c) Copyright 2009 Kaldeera, S.L. - All rights reserved.
*/


(function($) {
	// static constructs
	$.kaldeera = $.kaldeera || {};

	$.kaldeera.SlideShow = {
		version: '1.0',
		
		config: {
			interval: 5000,
			speed: 1500,
			keyboard: true,
			disabledClass: 'disabled',
			hoverClass: null,
			clickable: true,
			activeClass: 'active',
			items: '.items',
			item: null,
			api: false
			
			// CALLBACKS: onBeforeSlide, onStart, onSlide, onReload
		}
	};
		
	var current;
	
	// Constructor
	function SlideShow(root, config) {
		
		// current instance
		var self = this, 
			$self = $(this),
			wrap = root.children(),
			index = 0,
			timer = null,
			isHover = false;
		
		if (!current) { current = self; }
		
		// bind all callbacks from configuration
		$.each(config, function(name, fn) {
			if ($.isFunction(fn)) { $self.bind(name, fn); }
		});
		
		if (wrap.length > 1) { wrap = $(config.items, root); }
		
		// navigational items can be anywhere when globalNav = true
		function find(query) {
			var els = $(query);
			return config.globalNav ? els : root.parent().find(query);
		}
		
		// to be used by plugins
		root.data("finder", find);

		// methods
		$.extend(self, {
			
			getIndex: function() {
				return index;
			},
			
			getClickIndex: function() {
				var items = self.getItems();
				return items.index(items.filter("." + config.activeClass));
			},
	
			getConf: function() {
				return config;
			},
			
			getSize: function() {
				return self.getItems().size();
			},
			
			getRoot: function() {
				return root;
			},
			
			getItemWrap: function() {
				return wrap;
			},
			
			getItems: function() {
				return wrap.children(config.item);
			},
			
			/* all sliding functions depend on this */
			slideSwitch: function(i, time, fn) {
				clearTimeout(self.timer);

				if (i < 0) { i = self.getSize() - 1 ; }
				
				// nothing happens
				if (index === i) { return self; }
				
				// function given as second argument
				if ($.isFunction(time)) {
					fn = time;
				}

				// sliding exceeds the end
				if (i > self.getSize() - 1) { return self.begin(); }

				var item = self.getItems().eq(i);
				if (!item.length) { return self; }
				
				// onBeforeSlide
				var e = $.Event("onBeforeSlide");
				$self.trigger(e, [i]);
				if (e.isDefaultPrevented()) { return self; }

				// get the (possibly altered) speed
				if (time === undefined || $.isFunction(time)) { time = config.speed; }
				
				function callback() {
					if (fn) { fn.call(self, i); }
					$self.trigger("onSlide", [i]);
				}
				
				self.getItems().stop(true, true);
				var active = self.getItems().filter('.active');
				if (active.length) { active.removeClass(config.activeClass).addClass(config.disabledClass); }
				
				item.css({opacity: 0.0})
					.addClass(config.activeClass)
					.animate({opacity: 1.0}, config.speed, function() {
						active.removeClass(config.activeClass + ' ' + config.disabledClass);
					}
				);
				
				current = self;
				index = i;
				
				// onStart
				e = $.Event("onStart");
				$self.trigger(e, [i]);
				if (e.isDefaultPrevented()) { return self; }
				
				if (!self.isHover) { 
					self.timer = setTimeout(function() { self.next(); } , config.interval); 
				}
				
				return self;
			},
			
				
			goTo: function(index, time, fn) {
				return this.slideSwitch(index, time, fn);
			},
			
			move: function(offset, time, fn) {
				return this.slideSwitch(index + offset, time, fn);
			},
			
			next: function(time, fn) {
				return this.move(1, time, fn);
			},
			
			prev: function(time, fn) {
				return this.move(-1, time, fn);
			},
			
			begin: function(time, fn) {
				return this.slideSwitch(0, time, fn);
			},
			
			end: function(time, fn) {
				var to = this.getSize() - 1;
				return to > 0 ? this.slideSwitch(to, time, fn) : self;
			},
			
			reload: function() {
				$self.trigger("onReload");
				return self;
			},
			
			focus: function() {
				current = self;
				return self;
			},
			
			click: function(i) {
				var item = self.getItems().eq(i);
				if (!item.hasClass(config.activeClass)) { return self; }
				return self.next();
			},
			
			// bind / unbind
			bind: function(name, fn) {
				$self.bind(name, fn);
				return self;
			},
			
			unbind: function(name) {
				$self.unbind(name);
				return self;
			},
			
			play: function() {
				self.timer = setTimeout(function() { self.next(); }, config.interval);
			},
			
			pause: function() {
				clearTimeout(self.timer);
			}
			
		});

		
		// callbacks
		$.each("onBeforeSlide,onStart,onSlide,onReload".split(","), function(i, ev) {
			self[ev] = function(fn) {
				return self.bind(ev, fn);
			};
		});


		// hover
		var hc = config.hoverClass, keyId = "keydown." + Math.random().toString().substring(10);

		
		self.onReload(function() {
			
			// hovering
			self.getItems().hover(
				function() {
					if (hc) { $(this).addClass(hc); }
					self.pause();
					self.isHover = true;
				}, 
				function() {
					if (hc) { $(this).removeClass(hc); }
					self.play();
					self.isHover = false;
				}
			);
			
			// clickable
			if (config.clickable) {
				self.getItems().each(function(i) {
					$(this).unbind("click.SlideShow").bind("click.SlideShow", function(e) {
						if ($(e.target).is("a")) { return; }
						return self.click(i);
					});
				});
			}
			
			// keyboard
			if (config.keyboard) {
				
				// keyboard works on one instance at the time. thus we need to unbind first
				$(document).unbind(keyId).bind(keyId, function(evt) {
					
					// do nothing with CTRL / ALT buttons
					if (evt.altKey || evt.ctrlKey) { return; }
					
					if (evt.keyCode == 37 || evt.keyCode == 39) {
						self.move(evt.keyCode == 37 ? -1 : 1);
						return evt.preventDefault();
					}
					
					return true;
					
				});
				
			} else  {
				$(document).unbind(keyId);
			}
			
			// Select correct index (current active item)
			var i = self.getClickIndex();
			if (i == -1) { 
				self.getItems().eq(0).addClass(config.activeClass);
				i = 0;
			}
			index = i;

			// Starts animation
			self.play();
			
		});
		
		self.reload();
		
	};
	
	
	// jQuery plugin implementation
	$.fn.SlideShow = function(config) {
		
		// already constructed --> return API
		var el = this.eq(typeof config == 'number' ? config : 0).data("SlideShow");
		if (el) { return el; }
		
		var globals = $.extend({}, $.kaldeera.SlideShow.config);
		config = $.extend(globals, config);
		
		this.each(function() {
			el = new SlideShow($(this), config);
			$(this).data("SlideShow", el);
		});
		
		return config.api ? el : this;
	};
	
})(jQuery);


/*
**
**
** File:         	jquery.kaldeera.js (plugin: thumbnails)
** Version:			1.0
** Description:     Adds Kaldeera plugins to jQuery.
** Author:          Pedro Castro -- pedro.castro@kaldeera.com
** Created: 		18/Dic/2009
** Last modified:   18/Dic/2009
**
**
** (c) Copyright 2009 Kaldeera, S.L. - All rights reserved.
*/
(function($) {
	
	var k = $.kaldeera.SlideShow;
	k.plugins = k.plugins || {};
	
	k.plugins.thumbnails = { // thumbnails/navigator
		version: '1.0',
		
		config: {
			thumbs: '.thumbs',
			thumbItem: null,
			activeClass: 'active',
			indexed: false,
			api: false,
			idPrefix: null
		}
	};
	
	// jQuery plugin implementation
	$.fn.thumbnails = function(config) {
		var globals = $.extend({}, k.plugins.thumbnails.config), ret;
		if (typeof config == 'string') { config = {thumbs: config}; }
		
		config = $.extend(globals, config);
		
		this.each(function() {
			var api = $(this).SlideShow(),
				root = api.getRoot(),
				thumbs = root.data("finder").call(null, config.thumbs),
				els = null;
			
			if (api) { ret = api; }
			
			// generate new entries
			function reload() {
				if (!thumbs.children().length || thumbs.data("thumbs") == api) {
					
					thumbs.empty();
					thumbs.data("thumbs", api);
					
					for (var i = 0; i < api.getSize(); i++) {
						thumbs.append($("<" + (config.thumbItem || 'a') + "/>"));
					}
					
					els = thumbs.children().each(function(i) {
						var el = $(this);
						
						el.click(function(e) {
							api.goTo(i);
							return e.preventDefault();
						});
						
						// possible index number
						if (config.indexed)  { el.text(i); }
						if (config.idPrefix) { el.attr("id", config.idPrefix + i); }
					});
					
				// assign onClick events to existing entries
				} else {

					// find a entries first -> syntaxically correct
					els = config.thumbItem ? thumbs.find(config.thumbItem) : thumbs.children();
					
					els.each(function(i) {
						var el = $(this);
						
						el.click(function(e) {
							api.goTo(i);
							return e.preventDefault();
						});
					});
				}

				// Select correct active thumbnail
				var cls = config.activeClass;
				els.removeClass(cls).eq(api.getClickIndex()).addClass(cls);
			}
			
			// activate correct entry
			api.onStart(function(e, index) {
				var cls = config.activeClass;
				els.removeClass(cls).eq(index).addClass(cls);
			});
			
			api.onReload(function() {
				reload();
			});
			
			reload();
			
			/*
			// look for correct thumb item from location.hash
			var el = els.filter("[href=" + location.hash + "]");
			if (el.length) { api.move(els.index(el)); }
			*/
		});
		
		return config.api ? ret : this;
	};
	
})(jQuery);
