/*
 * jQuery Slideshow plugin
 * @author	Display:inline <contact@display-inline.fr>
 * @url		http://display-inline.fr
 * @version	1.0
 */

/*
 * Enables slideshow on selected elements
 *
 * Elements structure must be:
 * <ul class="slideshow">
 *	 <li><a><img /><span>Caption</span></a></li>
 *	 ...
 * </ul>
 * 
 * Global parameters
 * @param	int			height				height of slideshow block (default: 200)
 * @param	int			visibleSlides		max number of pulltabs visible at the same time (default: 4)
 * @param	int			duration			slide pause (default: 7000)
 * @param	int			restartDuration		time before slideshow restarts after hover (default: 11000)
 * @param	int|string	slidingDuration		duration of the sliding movement: 'fast', 'normal', 'slow' or a number of milliseconds (default: 'normal')
 * @param	string		easing				the name of the easing effect (default: 'swing')
 * 											- jQuery core: 'linear' or 'swing'
 * 											- include plugin to use custom effects (ie: http://plugins.jquery.com/project/Easing)
 * @param	boolean		showNumbers			true to show slides number, false to hide (default: true)
 * @param	boolean		showButtons			true to show previous and next buttons, false to hide (default: true)
 * @param	boolean		autoHideButtons		true to hide buttons when not needed, false to show always (default: true)
 * @param	string		prevButtonText		title (hover tooltip) for previous slide button (default: 'Show previous slide')
 * @param	string		nextButtonText		title (hover tooltip) for next slide button (default: 'Show next slide')
 * @param	boolean		showForthcoming		true to show forthcoming slides when available, false to disable (default: true)
 * 
 * CSS parameters, edit if you change the related CSS styles
 * @param	int			pulltabWidth		width of pulltab (default: 50)
 * @param	int			buttonWidth			width of previous/next buttons (default: 8)
 * @param	int			leftShadowWidth		width of pulltab lefdt shadow (default: 10)
 * @param	int			spanHPaddings		total of left and right paddings of caption span (default: 30)
 */
$.fn.slideshow = function(options)
{
	// Options
	settings = $.extend({
		height:		 		250,
		visibleSlides: 		6,
		duration: 			7000,
		restartDuration: 	11000,
		slidingDuration: 	'normal',
		easing: 			'swing',
		showNumbers: 		false,
		showButtons: 		true,
		autoHideButtons: 	true,
		prevButtonText: 	'Show previous slide',
		nextButtonText: 	'Show next slide',
		showForthcoming: 	true,
		pulltabWidth:		50,
		buttonWidth:		8,
		leftShadowWidth:	10,
		spanHPaddings:		30
	}, options);
	
	this.each(function(i)
	{
		// Init
		this._settings = settings;
		var slideshow = $(this);
		slideshow.addClass('slideshow-enabled').height(settings.height);
		var width = slideshow.width();
		var blocks = slideshow.children('li');
		var blocksCount = blocks.length;
		if (this.id == undefined || this.id == '')
		{
			this.id = 'slideshow'+Math.round(Math.random()*10000);	// Generate random id (if none) to handle timeout
		}
		
		// Visible range
		var range = {
			total: blocksCount,
			width: Math.min(blocksCount, this._settings.visibleSlides),
			start: 1,
			end: Math.min(blocksCount, this._settings.visibleSlides)
		};
		this._range = range;
		
		// If showing buttons
		if (settings.showButtons)
		{
			// Wrapping to enable scroll buttons
			var slideshowWrapper = slideshow.wrap('<div class="slideshow-wrapper"></div>').parent().height(settings.height);;
			slideshow.after('<a class="slideshow-prev-button" style="left:'+parseInt(slideshow.css('border-left-width'))+'" href="javascript:void(0)" onclick="$(this).parent().children().eq(0).slideshowPrev(false)" title="'+settings.prevButtonText+'"></a>');
			slideshow.after('<a class="slideshow-next-button" style="right:'+parseInt(slideshow.css('border-right-width'))+'" href="javascript:void(0)" onclick="$(this).parent().children().eq(0).slideshowNext(false)" title="'+settings.nextButtonText+'"></a>');
			var buttons = slideshow.parent().children('a');
			buttons.hover(function()
			{
				// Stop timeout
				clearTimeout($(this).parent().children(':first').get(0)._timeout);
			}, function ()
			{
				// Restart timeout
				var slideshow = $(this).parent().children(':first').get(0);
				clearTimeout(slideshow._timeout);
				slideshow._timeout = setTimeout('$(\'#'+slideshow.id+'\').slideshowRestart()', 50);
			});
			
			// Move margin attributes to wrapper div
			var margins = ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'];
			for (var i = 0; i < margins.length; ++i)
			{
				slideshowWrapper.css(margins[i], slideshow.css(margins[i]));
				slideshow.css(margins[i], 0);
			}
			
			// Hiding effect
			if (settings.autoHideButtons)
			{
				buttons.css('width', '0');
			}
		}
		
		// Setup of slides
		blocks.each(function(i)
		{
			// Initial position
			var shadowHide = ((i+1) > range.end) ? settings.leftShadowWidth : 0;	// If out of range, add shadow width to prevent it to be visible
			var position = (i > 0) ? (width-((range.end-i)*settings.pulltabWidth)+shadowHide+1) : 0;
			$(this).css('left', position+'px');
			
			// Title and number
			var link = $(this).children('a');
			if (settings.showNumbers)
			{
				link.prepend('<b class="slide-number">'+(i+1)+'</b>');
			}
			//link.prepend('<b class="slide-title"></b>');
		}).not(':first').find('span').each(function(i)
		{
			$(this).css('bottom', -$(this).height()+'px');
		});
		
		// Current slide
		this._current = blocks.get(0);
		
		// Width of legends
		blocks.find('span').css('right', 'auto').width(width-(range.end*settings.pulltabWidth)-settings.spanHPaddings);
		
		// Behaviour
		blocks.hover(function()
		{
			// Opens slide
			$(this).slideshowShow(false, false);
			
			// Stop timeout
			clearTimeout($(this).parent().get(0)._timeout);
		}, function ()
		{
			// Restart timeout
			// Timeout is needed to avoid calling restart when the user hovers from this slide to another
			var slideshow = $(this).parent().get(0);
			clearTimeout(slideshow._timeout);
			slideshow._timeout = setTimeout('$(\'#'+slideshow.id+'\').slideshowRestart()', 50);
		});
		
		// Timeout for next slide
		this._timeout = setTimeout('$(\'#'+this.id+'\').slideshowNext(true)', this._settings.duration);
	});
	
	return this;
};

/**
 * Restart slideshow after hover
 */
$.fn.slideshowRestart = function()
{
	this.each(function(i)
	{
		// Refresh visible range
		$(this._current).slideshowShow(true, true);
		
		// Restart
		clearTimeout(this._timeout);
		this._timeout = setTimeout('$(\'#'+this.id+'\').slideshowNext(true)', this._settings.restartDuration);
	});
	
	return this;
}

/**
 * Show previous slide
 * @option boolean	showNext		true will make the slideshow reveal forthcoming slides if needed, so the user can hover on them.
 */
$.fn.slideshowPrev = function(showNext)
{
	this.each(function(i)
	{
		var prev = $(this._current).prev();
		if (prev.length == 0)
		{
			prev = $(this).children(':last');
		}
		prev.slideshowShow(true, showNext);
		
		// Timeout
		clearTimeout(this._timeout);
		this._timeout = setTimeout('$(\'#'+this.id+'\').slideshowNext(true)', this._settings.duration);
	});
	
	return this;
}

/**
 * Show next slide
 * @option boolean	showNext		true will make the slideshow reveal forthcoming slides if needed, so the user can hover on them.
 */
$.fn.slideshowNext = function(showNext)
{
	this.each(function(i)
	{
		var next = $(this._current).next();
		if (next.length == 0)
		{
			next = $(this).children(':first');
		}
		next.slideshowShow(true, showNext);
		
		// Timeout
		clearTimeout(this._timeout);
		this._timeout = setTimeout('$(\'#'+this.id+'\').slideshowNext(true)', this._settings.duration);
	});
	
	return this;
}

/**
 * Show selected slides
 * @option boolean	refreshRange	refresh range to include current slide
 * @option boolean	showNext		true will make the slideshow reveal forthcoming slides if needed, so the user can hover on them.
 * 									use false when hover to avoid next slide to appear under mouse when hovering last visible slice.
 * 									(ignored if refreshRange is false)
 */
$.fn.slideshowShow = function(refreshRange, showNext)
{
	this.each(function(i)
	{
		// Init
		var slideshow = $(this).parent();
		var slideshowNode = slideshow.get(0);
		var settings = slideshowNode._settings;
		var range = slideshowNode._range;
		var width = slideshow.width();
		var block = $(this);
		
		// Siblings
		var previous = block.prevAll();
		var previousCount = previous.length;
		var next = block.nextAll();
		var nextCount = next.length;
		var currentNumber = previousCount+1;
		
		// If in current range (prevent hidden elements to show when hovering left shadow)
		if (refreshRange || (currentNumber >= range.start && currentNumber <= range.end))
		{
			// Check if a change is needed
			var change = (slideshowNode._current != this);
			
			// Updating range if needed
			if (refreshRange)
			{
				var visibleNext = (showNext && settings.showForthcoming) ? 1 : 0;
				if (currentNumber > range.end-visibleNext)
				{
					range.end = currentNumber+visibleNext;
					if (range.end > range.total)
					{
						range.end = range.total;
					}
					range.start = range.end-range.width+1;
					change = true;
				}
				else if (currentNumber < range.start+visibleNext)
				{
					range.start = currentNumber-visibleNext;
					if (range.start < 1)
					{
						range.start = 1;
					}
					range.end = range.start+range.width-1;
					change = true;
				}
			}
			
			// If something changed
			if (change)
			{
				// Store current slide
				slideshowNode._current = this;
				
				// Previous elements
				previous.each(function(i)
				{
					var span = $(this).stop().animate({left:((previousCount-i-range.start)*settings.pulltabWidth)+'px'}, settings.slidingDuration, settings.easing).find('span');
					span.stop().animate({bottom:-span.outerHeight()+'px'});
				});
				
				// Selected slide
				var span = block.stop().animate({left:((previousCount+1-range.start)*settings.pulltabWidth)+'px'}, settings.slidingDuration, settings.easing).find('span');
				span.stop().animate({bottom:0});
				
				// Next elements
				next.each(function(i)
				{
					// Future position
					var leftOffset = width-((nextCount-(range.total-range.end)-i)*settings.pulltabWidth)+1;
					
					// If out of range
					if ((currentNumber+i+1) > range.end)
					{
						// Add shadow width to prevent it to from being visible
						if (parseInt($(this).css('left')) > width)
						{
							// Out of visible frame, slide straight to final position
							$(this).stop().animate({left:(leftOffset+settings.leftShadowWidth)+'px'}, settings.slidingDuration, settings.easing);
						}
						else
						{
							// Slide visible, slide to normal position then add shadow width
							$(this).stop().animate({left:leftOffset+'px'}, settings.slidingDuration, settings.easing)
										  .animate({left:(leftOffset+settings.leftShadowWidth)+'px'}, 50);
						}
					}
					else
					{
						$(this).stop().animate({left:leftOffset+'px'}, settings.slidingDuration, settings.easing);
					}
					$(this).find('span').stop().animate({bottom:-span.outerHeight()+'px'});
				});
				
				// Buttons
				if (settings.showButtons && settings.autoHideButtons)
				{
					// Left button show/hide
					if (currentNumber == range.start && range.start > 1)
					{
						slideshow.parent().children('.slideshow-prev-button').animate({width: settings.buttonWidth+'px'}, 'fast');
					}
					else
					{
						slideshow.parent().children('.slideshow-prev-button').animate({width: 0}, 'fast');
					}
					
					// Right button show/hide
					if (currentNumber == range.end && range.end < range.total)
					{
						slideshow.parent().children('.slideshow-next-button').animate({width: settings.buttonWidth+'px'}, 'fast');
					}
					else
					{
						slideshow.parent().children('.slideshow-next-button').animate({width: 0}, 'fast');
					}
				}
			}
		}
	});
	
	return this;
};
