/*!
 * PanSlide jQuery plugin v0.1
 *
 * Copyright 2010 by Alberto Arena <arena.alberto@gmail.com>
 *
 * 0.1    2010-10-06  Initial release
 */

(function() {
	var $ = jQuery,
		animate = ($.fn.startAnimation ? 'startAnimation' : 'animate'),
		pause_missing = 'pause plugin missing.';

	// utility to format a string with {0}, {1}... placeholders
	function format(str) {
		for (var i = 1; i < arguments.length; i++)
			str = str.replace(new RegExp('\\{' + (i-1) + '}', 'g'), arguments[i]);
		return str;
	}

	// utility to abort with a message to the error console
	function abort() {
		arguments[0] = 'PanSlide: ' + arguments[0];
		throw new Error(format.apply(null, arguments));
	}

	$.fn.panSlide = function(opts, callback)
	{
		var self = this,
		self_width = this.width(),
		self_height = this.height();

		// must be called on exactly 1 element
		if (self.length != 1)
			abort('panSlide() must be called on exactly 1 element')

		// gets images
		var plan = $(this).find('img').clone();
		$(this).find('img').remove();
		if ( plan.length == 0 )
			abort('no image to slide');

		// saving params for panSlide.restart
		self.get(0).panSlideArgs = [ opts, plan, callback ];
		
		self.pause = false;

		// make working copy of plan
		plan = $.map(plan, function(p) {
			return $.extend({}, p);
		});

		// options with default values
		if (! opts.easing)
			opts.easing = opts.variant ? 'swing' : 'linear';
		if (! callback)
			callback = function() {};

		// first preload all the images, while getting their actual width and height
		(function(proceed) {
			var n_loaded = 0;
			function loop(i, img) {
				// this loop is a for (i = 0; i < plan.length; i++)
				// with independent var i, img (for the onload closures)
				img.onload = function(e) {
					n_loaded++;
					plan[i].width = img.width;
					plan[i].height = img.height;
					if (n_loaded == plan.length)
						proceed();
				}
				img.src = plan[i].src;
				$(img).load(function(){
					if (i + 1 < plan.length)
						loop(i + 1, new Image());
					else {
						proceed2();
					}
				})
			}
			loop(0, new Image());

			function proceed2() {
				// create progress area
				$progressArea = $(opts.progressArea);
				if ( $progressArea.length == 1 ) {
					var p = "<ul>";
					for (var i = 0; i < plan.length; ++i) {
						p += "<li></li>";
					}
					p += "</ul>";
					$progressArea.append(p);
					
					var ulW = $progressArea.find('ul').width();
					var pW = $progressArea.width();
					$progressArea.find('ul').css('left',
						parseInt( ( pW - ulW ) / 2 )+'px'
					)
				}			
			}

		})(function() { // then proceed

			// check global params
			if (! opts.fade)
				abort('missing fade parameter.');
			if (! opts.time)
				abort('missing time interval parameter.');
			if (opts.speed && opts.sleep)
				abort('you cannot set both speed and sleep at the same time.');
			if (! opts.titles)
				opts.titles = [ "Click to stop" , "Click to resume" ];

			opts.time *= 1000;

			// conversion from sec to ms; from px/sec to px/ms
			var fade_ms = Math.round(opts.fade * 1000);
			if (opts.sleep)
				var sleep = Math.round(opts.sleep * 1000);
			if (opts.speed)
				var speed = opts.speed / 1000,
						fade_px = Math.round(fade_ms * speed);

			// set container css
			self.empty().css({
				overflow: 'hidden',
				padding: 0
			});
			if (! /^(absolute|relative|fixed)$/.test(self.css('position')))
				self.css({ position: 'relative' });
			if (! self.width() || ! self.height())
				abort('container element does not have its own width and height');

			// random sorting
			if (opts.shuffle)
				plan.sort(function() {
					return Math.random() - 0.5;
				});
	
			// prepare each image
			for (var i = 0; i < plan.length; ++i) {

				var p = plan[i];
				if (! p.src)
					abort('missing src parameter in picture {0}.', i + 1);

				// append the image (or anchor) element to the container
				var img, elm;
				elm = img = $(format('<img src="{0}"/>', p.src));
				if (p.href)
					elm = $(format('<a href="{0}"></a>', p.href)).append(img);
				if (p.onclick)
					elm.click(p.onclick);
				if (p.alt)
					img.attr('alt', p.alt);
				if (p.rel)
					elm.attr('rel', p.rel);
				if (p.href && p.target)
					elm.attr('target', p.target);
				elm.appendTo(self);
			}

			// find images to animate and set initial css attributes
			var imgs = self.find('img').css({
				position: 'absolute',
				display: 'none',
				top: 0,
				left: 0,
				border: 0,
				//cursor: "pointer"
			})
			/*.attr('title',opts.titles[0])*/
			;

			// show first image
			imgs.eq(0).fadeIn(300);

			
			// events
			imgs.click(function(){
				return false;
			});

			// start animation			
			self.curIndex = 0;
			self.progressArea = $(opts.progressArea);
			self.plan = plan;
			self.imgs = imgs;
			self.time = opts.time;
			self.fade_ms = fade_ms;
			self.animation_index = 0;
			self.animations = [
				{
					from: {"width":"120%","top": "-50px","left": "-50px"},
					to: { "top": "+=50px","left": "+=50px","width": "-=20%"}
				},
				{
					from: {"width":"130%"},
					to: { "width": "-=30%"}
				},
				{
					from: {"width":"100%"},
					to: { "width": "+=20%"}
				}
			];
			self.panSlideNextItem();
		});
		return self;
	};
	
	$.fn.panSlideNextItem = function() {
		var self = this;
		if ( self.pause )
			return;

		// select current animation
		var anim = self.animations[ self.animation_index ];
		
		// selects item in progress bar, if available
		if ( self.progressArea.length == 1 ) {
			self.progressArea.find('ul li').removeClass('selected');
			self.progressArea.find('ul li:eq('+self.curIndex+')').addClass('selected');
		}

		var p = self.plan[self.curIndex];
		var tm = self.time;
		
		var lastIndex = ( self.curIndex > 0 ? self.curIndex - 1 : self.plan.length - 1 );
		
		self.imgs.eq(lastIndex).fadeOut(self.fade_ms);
		self.imgs.eq(self.curIndex)
			//.css(position_to_css(plan[curIndex], opts.variant ? 0 : 1))
			.css(anim['from'])
			.fadeIn(self.fade_ms)
			.animate(anim['to'],{
				queue: false,
				duration: tm/5*3
			});
		
		self.animation_index++;
		if ( self.animation_index >= self.animations.length )
			self.animation_index = 0;
		
		self.curIndex++;
		if ( self.curIndex >= self.plan.length )
			self.curIndex = 0;
			
		setTimeout( function() {
			self.panSlideNextItem()
		}, tm );
	}

	$.fn.panSlidePause = function()
	{
		this.pause = true;
		return;
	}

	$.fn.panSlideResume = function()
	{
		this.pause = false;
		this.panSlideNextItem();
		return;
	}

})();

