/*
 * Written by Ronan Cashell (c) 2010 Symfonip.com
 * Version 1.4
 */
(function($) {
	$.fn.extend({
		symCanvasSlider: function(options) {
		
			// Private functions
			// =================
			// This function takes an image and renders
			// a version of it similar to the Overlay blending
			// mode in Photoshop.
			function URLDecode(encoded)
			{
			   // Replace + with ' '
			   // Replace %xx with equivalent character
			   // Put [ERROR] in output if %xx is invalid.
			   var HEXCHARS = "0123456789ABCDEFabcdef"; 
			   var plaintext = "";
			   var i = 0;
			   while (i < encoded.length) {
			       var ch = encoded.charAt(i);
				   if (ch == "+") {
				       plaintext += " ";
					   i++;
				   } else if (ch == "%") {
						if (i < (encoded.length-2) 
								&& HEXCHARS.indexOf(encoded.charAt(i+1)) != -1 
								&& HEXCHARS.indexOf(encoded.charAt(i+2)) != -1 ) {
							plaintext += unescape( encoded.substr(i,3) );
							i += 3;
						} else {
							alert( 'Bad escape combination near ...' + encoded.substr(i) );
							plaintext += "%[ERROR]";
							i++;
						}
					} else {
					   plaintext += ch;
					   i++;
					}
				} // while
			   return plaintext;
			};
			
			function setCaption(fromHere) {
				var stitle = $("img", fromHere).attr("stitle");
				if(stitle && stitle != "") {
					var title = URLDecode(stitle);
					if(defaults.showCaptions && title != "") {
						caption.text(title);
						caption.parent().stop().animate({bottom:0}, defaults.captionAnimationInterval);
					}
				}
			}

			function createCanvasOverlay(image){
		
				var canvas			= document.createElement('canvas'),
					canvasContext	= canvas.getContext("2d");
				
				// Make it the same size as the image
				canvas.width = image.width;
				canvas.height = image.height;
				
				// Drawing the default version of the image on the canvas:
				canvasContext.drawImage(image,0,0);
				
		
				// Taking the image data and storing it in the imageData array:
				var imageData	= canvasContext.getImageData(0,0,canvas.width,canvas.height),
					data		= imageData.data,
					len			= imageData.data.length;
				
				// Loop through all the pixels in the imageData array, and modify
				// the red, green, and blue color values.
				
				for(var i = 0,z=len;i<z;i++){
					
					// The values for red, green and blue are consecutive elements
					// in the imageData array. We modify the three of them at once:
					
					data[i] = ((data[i] < 128) ? (2*data[i]*data[i] / 255) : (255 - 2 * (255 - data[i]) * (255 - data[i]) / 255));
					data[++i] = ((data[i] < 128) ? (2*data[i]*data[i] / 255) : (255 - 2 * (255 - data[i]) * (255 - data[i]) / 255));
					data[++i] = ((data[i] < 128) ? (2*data[i]*data[i] / 255) : (255 - 2 * (255 - data[i]) * (255 - data[i]) / 255));
					
					
					// After the RGB elements is the alpha value, but we leave it the same.
					++i;
				}
				
				// Putting the modified imageData back to the canvas.
				canvasContext.putImageData(imageData,0,0);
				
				// Inserting the canvas in the DOM, before the image:
				image.parentNode.insertBefore(canvas,image);
				$(canvas).css({"position":"absolute","left":$(image).css("left"),"top":$(image).css("top")});
			}
			
			// We should note:
			// if autoAdvance is false, then showNavButtons is true by default
			var defaults = {
				pauseOnHover: false,
				canvasTransition: true,
				autoAdvance: false,
				slideInterval: 5000,
				showNavButtons: true,
				autoHideNavButtons: true,
				showCaptions: true,
				captionAnimationInterval: 500,
				transitionType: "fade",
				animationSpeed: 1000,
				width:400,
				height:250,
				border:10,
				canvasFadeSpeed:500,
				navFadeSpeed:500
				};
			var intervalID = 0;
			var $this = $(this);
				
			if(options) {
				$.extend(defaults, options);
			}
			defaults.left = defaults.width;//+2*defaults.border;
			defaults.top = defaults.height;//+2*defaults.border;
			
			// Parameter validations
			if(defaults.slideInterval < 1000) {
				// Minimum slide Interval is 1 second
				defaults.slideInterval = 1000;
			}
			if(!defaults.autoAdvance && !defaults.showNavButtons) {
				defaults.showNavButtons = true;
			}
			
			// Hide navigation buttons
			if(!defaults.showNavButtons) {
				$('.arrow', $this).css("display","none");
			}
			
			// Set off the interval
			if(defaults.autoAdvance) {
				if(!intervalID) {
					intervalID = setInterval(function() { $(".next", $this).click(); }, defaults.slideInterval);
				}
			}
			
			// Testing wether the current browser supports the canvas element:
			var supportCanvas = 'getContext' in document.createElement('canvas');

			// The canvas manipulations of the images are CPU intensive,
			// this is why we are using setTimeout to make them asynchronous
			// and improve the responsiveness of the page.
			
			var slides = $('.slide', $this),
				current = 0,
				slideshow = {width:0,height:0},
				caption = $('.caption p', $this);
				
			// This includes functionality of pauseOnHover and captions
			$this.hover(
				function() {
					if(defaults.autoAdvance && defaults.pauseOnHover) {
						if(intervalID) {
							clearInterval(intervalID);
							intervalID = 0;
						}
					}
					if(defaults.autoHideNavButtons) {
						$('.arrow', $this).fadeIn(defaults.navFadeSpeed);
					}
				},
				function() {
					if(defaults.autoAdvance && defaults.pauseOnHover) {
						if(!intervalID) {
							intervalID = setInterval(function() { $(".next", $this).click(); }, defaults.slideInterval);
						}
					}
					if(defaults.autoHideNavButtons) {
						$('.arrow', $this).fadeOut(defaults.navFadeSpeed);
					}
				}
			);
			
			setCaption(slides.eq(current));
			
	
			setTimeout(function(){
			
				if(defaults.canvasTransition && supportCanvas){
					$('img', $this).each(function(){
			
						if(!slideshow.width){
							// Taking the dimensions of the first image:
							slideshow.width = this.width;
							slideshow.height = this.height;
						}
			
						// Rendering the modified versions of the images:
						createCanvasOverlay(this);
					});
				}
		
				$('.arrow', $this).click(function(){
					var li			= slides.eq(current),
						canvas		= li.find('canvas'),
						nextIndex	= 0,
						arrow 		= $(this);
				
					// Depending on whether this is the next or previous
					// arrow, calculate the index of the next slide accordingly.
				
					if($(this).hasClass('next')){
						nextIndex = current >= slides.length-1 ? 0 : current+1;
					} else {
						nextIndex = current <= 0 ? slides.length-1 : current-1;
					}
				
					var next = slides.eq(nextIndex);
			
					if(defaults.canvasTransition && supportCanvas) {

						// This browser supports canvas, fade it into view:
						canvas.fadeIn(defaults.canvasFadeSpeed, function(){
							current = nextIndex;
							
							li.removeClass('slideActive').css({"zIndex":1});
							caption.parent().stop().animate({bottom:-(caption.height() + 20)}, defaults.captionAnimationInterval);
							
							
							// The next set of code is for fading in
							if(defaults.transitionType == "fade") {
								li.fadeOut(defaults.animationSpeed, function() { $(this).css({"zIndex" : 2});});
								next
									.css({"opacity" : 0, "zIndex" : 3})
									.show()
									.animate({"opacity" : 1}, defaults.animationSpeed, 
										function() { 
											li.css({"zIndex":1}).hide(); 
											canvas.hide();
											setCaption(next);
										}
									);
								
							}
							// The next set of code is for up down movement
							li.css({"zIndex":2});
							if(defaults.transitionType == "vertical") {
								if(arrow.hasClass("next")) {
									li.fadeOut(defaults.animationSpeed);
									next.
										addClass('slideActive')
										.css({"top":"-" + defaults.height + "px", "zIndex":3})
										.show()
										.animate({"top":"0px"}, defaults.animationSpeed, 
											function() { 
												li.css({"zIndex":1}); 
												canvas.hide();
												setCaption(next);
											}
										);
								} else {
									next
										.addClass('slideActive')
										.fadeIn(defaults.animationSpeed)
										.css({"zIndex":2})
										.show();
									li
										.css({"zIndex":3, top:"0px"})
										.animate({top:"-" + defaults.height + "px"}, defaults.animationSpeed, 
											function() { 
												li.css({"zIndex":1, top:"0px"}).hide(); 
												next.css({"zIndex":3}); 
												canvas.hide();
												setCaption(next);
											}
										);
								}
							}
					
							if(defaults.transitionType == "horizontal") {
								// The next set of code is for left and right movement.
								if(arrow.hasClass("next")) {
									li.fadeOut(defaults.animationSpeed).css({"zIndex":2});
									next
										.addClass('slideActive')
										.css({"left":defaults.left + "px", "zIndex":3})
										.show()
										.animate({"left":"0px"}, defaults.animationSpeed, 
											function() { 
												canvas.hide();
												li.css({"zIndex":1});
												setCaption(next);
									  		}
										);
								} else {
									next.css({"zIndex":2, left:"0px"}).show();
									li
										.css({"zIndex":3})
										.show()
										.animate({left:defaults.left + "px"}, defaults.animationSpeed, 
											function() { 
												canvas.hide();
												li.css({"zIndex":1}); 
												next.css({"zIndex":3}); 
												setCaption(next);
											}
										);
								}
							}
					
						});
					} else {
				
						// This browser does not support canvas.
						// Use the plain version of the slideshow.
						caption.parent().stop().animate({bottom:-(caption.height() + 20)}, defaults.captionAnimationInterval);
				
						current=nextIndex;
						li.removeClass('slideActive').css({"zIndex":2});
						
						// The next set of code is for fading in
						if(defaults.transitionType == "fade") {
							li.css({"z-index" : 2});
							next
								.css({"opacity" : 0, "z-index" : 3})
								.show()
								.animate({"opacity" : 1}, defaults.animationSpeed, 
									function() { 
										li.css({"zIndex":1});
										setCaption(next);
									}
								);
							
						}
						// The next set of code is for up down movement
						if(defaults.transitionType == "vertical") {
							if($(this).hasClass("next")) {
								li.fadeOut(defaults.animationSpeed);
								next
									.addClass('slideActive')
									.css({"top":"-" + defaults.height + "px", "zIndex":3})
									.show()
									.animate({"top":"0px"}, defaults.animationSpeed, 
										function() { 
											li.css({"zIndex":1});
											setCaption(next);
										}
									);
							} else {
								next.fadeIn(defaults.animationSpeed).css({"zIndex":2}).show();
								li
									.css({"zIndex":3})
									.animate({top:"-" + defaults.height + "px"}, defaults.animationSpeed, 
										function() { 
											li.css({"zIndex":1, top:"0px"}).hide(); 
											next.css({"zIndex":3});
											setCaption(next);
										}
									);
							}
						}
						if(defaults.transitionType == "horizontal") {
							// The next set of code is for left and right movement.
							if($(this).hasClass("next")) {
								li.fadeOut(defaults.animationSpeed);
								next
									.addClass('slideActive')
									.css({"left":defaults.left + "px", "zIndex":3})
									.show()
									.animate({"left":"0px"}, defaults.animationSpeed, 
										function() { 
											li.css({"zIndex":1});
											setCaption(next);
										}
									);
							} else {
								next.fadeIn(defaults.animationSpeed).css({"zIndex":2});
								li
									.css({"zIndex":3})
									.animate({left:defaults.left + "px"}, defaults.animationSpeed, 
										function() { 
											li.css({"zIndex":1, left:"0px"}).hide(); 
											next.css({"zIndex":3});
											setCaption(next);
										}
									);
							}
						}
					}
				});
		
			},100);
		}
	});
})(jQuery);

