/**
 * jQuery cachingSlider plugin
 * @name jquery.cachingSlider.js
 * @author Marco Nitsche - http://www.germaniasoft.de
 * @version 1.3
 * @date January 28, 2011
 * @category jQuery plugin
 * @copyright (c) 2011 Marco Nitsche, GermaniaSoft
 * @license Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * @example http://www.germaniasoft.de/demo/cachingSlider/
 */

var Methods = {

	init: function (options) {
		var Settings = {
			'auto': true, // Autostarts the slider, after initialization
			'cacheCount': 2, // The amount of objects to be Cached
			'cached': function (obj) {}, // A function called after caching each object, 'this' refers to the Container
			'complete': function () {}, // A function called after caching all objects, 'this' refers to the Container
			'displayCount': 1, // The amount of objects to be displayed
			'easing': 'swing', // The Type of the animation. Possible values are 'swing', 'linear'. Will be extended by jQueryUI
			'elements': [], // An Array of objects to be slided
			'pause': 2800, // The time in miliseconds, between the slides
			'speed': 800, // A string or number determining the speed of the slide
			'slideTo': 'left' // The direction, in wich will be slided. Possible values are 'left', 'right', 'top', 'bottom'
		};

		if (options) jQuery.extend(Settings, options);
		Settings.animating = false;
		Settings.cache = new Array();
		Settings.interval = null;

		return this.each(function () {
			var $this = jQuery(this),
					Data = Settings;

			Data.animating = true;
			$this.data('cachingSlider', Data);
			if (Data.elements.length < (Data.cacheCount + Data.displayCount)) Data.cacheCount = Data.elements.length - Data.displayCount;

			// Die ersten n Objekte einfügen
			for (var i = 0; i < Data.displayCount + Data.cacheCount && Data.elements.length > 0; i++) {
				var Ele = jQuery(Data.elements.shift());
				$this.append(Ele);
				Data.cached.call($this, Ele);
			}

			if (Data.elements.length == 0) Data.complete.call($this);
			Data.animating = false;

			if (Data.auto) {

				Data.interval = setInterval(function () {
					Methods.next.call($this);
				}, Data.pause);

			}

		});

	},

	next: function () {

		return this.each(function () {
			var $this = jQuery(this),
					Data = $this.data('cachingSlider');

			switch (Data.slideTo) {
				case 'bottom':
				case 'right':
					Methods.slideB.call($this, Data);
					break;

				default:
					Methods.slideF.call($this, Data);
			}

		});

	},

	previous: function () {

		return this.each(function () {
			var $this = jQuery(this),
					Data = $this.data('cachingSlider');

			switch (Data.slideTo) {
				case 'bottom':
				case 'right':
					Methods.slideF.call($this, Data);
					break;

				default:
					Methods.slideB.call($this, Data);
			}

		});

	},

	slideB: function (data) {
		var $this = this,
				Last = null;

		if (!data.animating) {
			data.animating = true;

			if (data.elements.length > 0) {
				Last = jQuery(data.elements.pop());
				$this.append(Last);
				data.cached.call($this, Last);
				if (data.elements.length == 0) data.complete.call($this);
			} else if ((data.displayCount + data.cacheCount) > 0) {
				Last = $this.children().last();
				data.cacheCount--;
			} else {
				Last = data.cache.pop();
			}

			var Ini = 0,
					Props = {};

			if (data.slideTo == 'bottom') {
				Ini = Last.css('marginTop');
				Last.css('marginTop', (parseFloat(Last.outerHeight(true)) - parseFloat(Ini)) * -1);
				Props = {marginTop: Ini};
			} else {
				Ini = Last.css('marginLeft');
				Last.css('marginLeft', (parseFloat(Last.outerWidth(true)) - parseFloat(Ini)) * -1);
				Props = {marginLeft: Ini};
			}

			$this.prepend(Last);
			data.cache.unshift(Last);

			Last.animate(Props, {

				complete: function () {
					data.animating = false;
				},

				duration:data.speed,
				easing:data.easing,
				queue:false
			});

		}

	},

	slideF: function (data) {
		var $this = this,
				Ele = null,
				First = null;

		if (!data.animating) {
			data.animating = true;

			if (data.elements.length > 0) {
				First = $this.children().first();
				Ele = jQuery(data.elements.shift());
			} else if ((data.displayCount + data.cacheCount) > 0) {
				First = $this.children().first();
				data.cacheCount--;
			} else {
				First = data.cache.shift();
			}

			var Ini = 0,
					Props = {};

			if (data.slideTo == 'top') {
				Ini = First.css('marginTop');
				Props = {marginTop: ('-=' + First.outerHeight(true))};
			} else {
				Ini = First.css('marginLeft');
				Props = {marginLeft: ('-=' + First.outerWidth(true))};
			}

			First.animate(Props, {
				complete: function () {
					$this.append(First);
					data.cache.push(First);

					if (data.slideTo == 'top') {
						First.css('marginTop', Ini);
					} else {
						First.css('marginLeft', Ini);
					}

					if (Ele) {
						Ele.insertBefore(data.cache[0]);
						data.cached.call($this, Ele);
						if (data.elements.length == 0) data.complete.call($this);
					}

					data.animating = false;
				},
				duration:data.speed,
				easing:data.easing,
				queue:false
			});

		}

	},

	start: function () {

		return this.each(function () {
			var $this = jQuery(this),
					Data = $this.data('cachingSlider');

			clearInterval(Data.interval);

			Data.interval = setInterval(function () {
				Methods.next.call($this);
			}, Data.pause);

		});

	},

	stop: function () {

		return this.each(function () {
			var $this = jQuery(this),
					Data = $this.data('cachingSlider');

			clearInterval(Data.interval);
		});

	}

};

jQuery.fn.cachingSlider = function (method) {

	// Method calling logic
	if (Methods[method]) {
		return Methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
	} else if (typeof method === 'object' || !method) {
		return Methods.init.apply(this, arguments);
	} else {
		jQuery.error('Method ' +  method + ' does not exist on jQuery.cachingSlider');
		return this;
	}

};
