/* jQuery CCSlider Plugin 1.1.1
 * Copyright 2011, Nilok Bose  
 * http://codecanyon.net/user/cosmocoder
*/


(function($) {
    $.fn.ccslider = function(options) {
        // Extend the defaults, over-writing them with anything passed by the caller
        var opts = $.extend(true, {}, $.fn.ccslider.defaults, options);
		
        // Iterate over each element
        return this.each(function() {
            var $slider = $(this),
				$wrapper = $slider.wrapInner('<div class="slider-innerWrapper"/>').find('.slider-innerWrapper'),
				$controls = $('<div class="slider-controls"/>').appendTo($slider),
				width = $slider.width(),
				height = $slider.height(),
				cWidth = width,
				cHeight = height,
				$images = $slider.find('img'),
				slideNum = $images.length,
				effect,
				effectType,
				randanim = false,
				imageWidth = opts._3dOptions.imageWidth,
				imageHeight = opts._3dOptions.imageHeight,
				innerSideColor = opts._3dOptions.innerSideColor,
				makeShadow = opts._3dOptions.makeShadow,
				shadowColor = opts._3dOptions.shadowColor,				
				slices = opts._3dOptions.slices,
				delay = opts._3dOptions.delay,
				easing = opts._3dOptions.easing,
				fallBack = opts._3dOptions.fallBack,
				animSpeed,
				index = opts.startSlide,
				pause = false,
				animating = false,
				hover = false,
				clock;			
			

			
			// set up the correct effect
			if( opts.effectType === '3d' ) {				
				if( document.createElement('canvas').getContext ) {
					effectType = '3d';
					effect = opts.effect;
					animSpeed = opts.animSpeed;
				}
				else {
					effectType = '2d';
					effect = fallBack;
					animSpeed = opts._3dOptions.fallBackSpeed;
				}
			}
			else {
				effectType = '2d';
				effect = opts.effect;
				animSpeed = opts.animSpeed;
			}
			
			
			//set class="ccslider" on the slider div
			$slider.addClass('ccslider');
			
			
			//add next/prev buttons
			if( opts.directionNav ) {
				var $prev = $('<a class="slider-nav prev"/>').appendTo($controls),
					$next = $('<a class="slider-nav next"/>').appendTo($controls);
				
				$prev.click(function(){						
					if( clock ) {
						stopClock();
					}
					
					if( effectType === '3d' ) {
						_3dAnimate('prev');
					}
					else {
						_2dAnimate('prev');
					}
				});
				
				$next.click(function(){					
					if( clock ) {
						stopClock();
					}
						
					if( effectType === '3d' ) {
						_3dAnimate('next');
					}
					else {
						_2dAnimate('next');
					}
				});
					
			}
			
			
			//set up control links
			if( opts.controlLinks ) {
				var $links = $('<ul class="control-links" />').appendTo($slider),
					linksHtml = '',
					i;
				
				for( i = 0; i < slideNum; i++ ) {
					if( opts.controlLinkThumbs ) {
						linksHtml += '<li class="linkThumb" data-index="'+ i +'"><img src="'+ opts.controlThumbLocation + $images.eq(i).data('thumbname') +'" /></li>';
					}
					else {
						linksHtml += '<li data-index="'+ i +'">'+ (i+1) +'</li>';
					}
				}
				
				$links.append(linksHtml);
				
				$links.delegate('li', 'click', function(){					
					if( effectType === '3d' ) {
						_3dAnimate( $(this).data('index') );
					}
					else {
						_2dAnimate( $(this).data('index') );
					}
				});				
			}
			
			//highlight current control link
			function setActiveLink(){
				if( opts.controlLinks ) {
					$links.find('li').removeClass('active').eq(index).addClass('active');	
				}
			}
			
			setActiveLink();
			
			
			//set up autplay
			function startClock() {
				if( !hover && !animating ) {
					clock = setInterval( function() {
						if( effectType === '3d' ) {
							_3dAnimate('next');
						}
						else {
							_2dAnimate('next');
						}
					}, opts.pauseTime);
				}
			}
			
			function stopClock() {
				clearInterval(clock);
				clock = '';
			}
			
			if( opts.autoPlay ) {				
				startClock();
			}
			
			
			//set up autplay pause on hover
			if( opts.pauseOnHover ) {
				$slider.hover(function() {
					hover = true;					
					stopClock();									
				}, function() {
					hover = false;
					if( clock === '' && opts.autoPlay && !pause ) {
						startClock();
					}
				});
			}
			
			//set up play/pause buttons
			if( opts.autoPlay ) {
				var $timer = $('<div class="slider-timer pause"/>').appendTo($controls);
					
				$timer.click(function(){
					if( $timer.hasClass('pause') ) {
						$timer.removeClass('pause').addClass('play');
						stopClock();
						pause = true;
					}
					else {
						$timer.removeClass('play').addClass('pause');
						startClock();
						pause = false;
					}
				});
				
			}
			
			
			//function to unlock animations and start timer
			function unlock() {
				animating = false;
				opts.afterSlideChange.call( $slider[0], index );
				if( opts.autoPlay && !pause ) {
					startClock();
				}
			}
			
			
			//setup captions
			if( opts.captions ) {
				var $caption = $('<div class="slider-caption"/>').appendTo($slider);
				if( effectType === '3d' ) {
					$caption.width( imageWidth - parseInt($caption.css('padding-left'), 10) - parseInt($caption.css('padding-right'), 10) );
					$caption.css({ left: (width - imageWidth)/2, bottom: (height - imageHeight)/2, right: 'auto' });
				}
			}
			
				
			function showCaption() {
				if( opts.captions ) {
					var curImg = $images.eq(index),
						captionElem = '',
						captionHtml = '';
					
					if( curImg.data('captionelem') ) {
						captionElem = curImg.data('captionelem');
						captionHtml = $(captionElem)[0].innerHTML;
					}
					else if( curImg[0].alt ) {
						captionHtml = curImg[0].alt;						
					}
					
					if( captionHtml ) {
						$caption[0].innerHTML = captionHtml;
							
						if( opts.captionAnimation === 'none' ) {
							$caption.show();
						}
						else if( opts.captionAnimation === 'fade' ) {
							$caption.fadeIn( opts.captionAnimationSpeed );
						}
						else if( opts.captionAnimation === 'slide' ) {
							$caption.slideDown( opts.captionAnimationSpeed );
						}
					}					
				}
			}
			
			showCaption();
			
			function hideCaption() {
				if( opts.captions ) {
					if( opts.captionAnimation === 'none' ) {
						$caption.hide();
					}
					else if( opts.captionAnimation === 'fade' ) {
						$caption.fadeOut( opts.captionAnimationSpeed );
					}
					else if( opts.captionAnimation === 'slide' ) {
						$caption.slideUp( opts.captionAnimationSpeed );
					}
				}
			}
			
			
			//setup for randomly changing effects
			randanim = effect === 'random' ? true : false;
			
			function randomEffect() {				
				var effectArray = [];
				
				if( effectType === '3d' ) {
					if( makeShadow ) {
						effectArray = ['cubeUp', 'cubeDown', 'cubeRight', 'cubeLeft'];
					}
					else {
						effectArray = ['cubeUp', 'cubeDown', 'cubeRight', 'cubeLeft', 'flipUp', 'flipDown', 'flipRight', 'flipLeft', 'blindsVertical', 'blindsHorizontal'];
					}
				}
				else {
					effectArray = ['fade', 'horizontalOverlap', 'verticalOverlap', 'horizontalSlide', 'verticalSlide', 'horizontalWipe', 'verticalWipe', 'horizontalSplit', 'verticalSplit', 'fadeSlide'];						
				}
				
				effect = effectArray[ Math.floor( Math.random()*(effectArray.length + 1) )];
				if( effect === undefined ) {
					effect = effectArray[0];
				}				
			}
			
			if( randanim ) {
				randomEffect();
			}
			
			
			
			/**********************************  Code for 3d effects **********************************/
			
			if( effectType === '3d' ) {
			
				//hide the images
				$wrapper.hide();
				
				//make slider background transparent
				$slider.css('background','transparent none');
				
				//adjust width, height, position of slider controls
				$controls.width( imageWidth );
				$controls.height( imageHeight );
				$controls.css({ left: (width - imageWidth)/2, top: (height - imageHeight)/2 });				
				
				
				//get the width, height, depth, focal length of the actual 3d boxes based on the chosen effect and also of dummy canvas elements
				var objWidth,
					objHeight,
					objDepth,
					objFocalLength,
					cubeCanvas = [],
					cubeCtx = [],
					cubes = [],
					srcCanvasCur = [],
					srcCanvasNew = [],
					srcCtxCur = [],
					srcCtxNew = [];
					
				
				function setup3d() {
					if( effect === 'cubeLeft' || effect === 'cubeRight' ) {				
						objWidth = imageWidth;
						objHeight = Math.round(imageHeight/slices);
						objDepth = imageWidth;
					}					
					else if( effect === 'cubeUp' || effect === 'cubeDown' ) {
						objWidth = Math.round(imageWidth/slices);
						objHeight = imageHeight;
						objDepth = imageHeight;
					}					
					else if( effect === 'flipLeft' || effect === 'flipRight' || effect === 'blindsHorizontal' ) {				
						objWidth = imageWidth;
						objHeight = Math.round(imageHeight/slices);
						objDepth = 0;
					}
					else if( effect === 'flipUp' || effect === 'flipDown' || effect === 'blindsVertical' ) {
						objWidth = Math.round(imageWidth/slices);
						objHeight = imageHeight;
						objDepth = 0;
					}
					
					objFocalLength = objDepth === 0 ? 2*imageWidth : objDepth + 300;
					
					//find the number of the central segment, i.e, median
					var median;
					if( slices % 2 === 0 ) {
						median = slices / 2;
					}
					else {
						median = (slices + 1) / 2;
					}
					
					var i = slices,
						z;
					
					while(i--) {				
						if( i <=  median - 1) {
							z = 2 + i;
						}
						else {
							z = 2 + slices - 1 - i;
						}
						
						if( cubeCanvas[i] ) {
							cubeCanvas[i].remove();
						}
						
						cubeCanvas[i] = $('<canvas class="draw"/>').appendTo($slider).css({position: 'absolute', zIndex: z});
						cubeCtx[i] = cubeCanvas[i][0].getContext('2d');
						cubeCanvas[i][0].width = cWidth;
						cubeCanvas[i][0].height = cHeight;
						
						if( !srcCanvasCur[i] ) {
							srcCanvasCur[i] = document.createElement('canvas');
							srcCtxCur[i] = srcCanvasCur[i].getContext('2d');
						}
						
						if( !srcCanvasNew[i] ) {
							srcCanvasNew[i] = document.createElement('canvas');
							srcCtxNew[i] = srcCanvasNew[i].getContext('2d');
						}
						
						srcCanvasCur[i].width = srcCanvasNew[i].width = objWidth;
						srcCanvasCur[i].height = srcCanvasNew[i].height = objHeight;
						
						cubes[i] = new Cube(objWidth, objHeight, objDepth, objFocalLength, cubeCtx[i], innerSideColor, []);				
						
						//determine the position of the cubes based on effect
						if( effect.indexOf('Left') !== -1 || effect.indexOf('Right') !== -1 || effect === 'blindsHorizontal' ) {
							cubes[i].position.y = imageHeight/2 - objHeight/2 - i*objHeight;
						}
						else if( effect.indexOf('Up') !== -1 || effect.indexOf('Down') !== -1 || effect === 'blindsVertical' ) {
							cubes[i].position.x = -imageWidth/2 + objWidth/2 + i*objWidth;						
						}
						
						//plot the current image on the dummy canvases
						drawImgStrip(srcCtxCur[i], $images[index], i);
						
						
						//set the dummy canvas as image src for cube
						cubes[i].images[0] = srcCanvasCur[i];
						cubes[i].render();
					}					
					
				}    // end setup3d()
				
				setup3d();
				
				//declare a shadow
				if( makeShadow && effect.indexOf('cube') === 0 ) {
					var shadowCanvas = $('<canvas class="shadow"/>').appendTo($slider).css({position: 'absolute', zIndex: '1'}),
						sctx = shadowCanvas[0].getContext('2d');
						
					sctx.canvas.width = cWidth;
					sctx.canvas.height = cHeight + 30;			
					
					var	shadow = new Plane(imageWidth - 100, imageHeight + 200, imageHeight + 300, sctx, '#666', '', shadowColor);			
					
					shadow.position.y = -imageHeight/2 + 60;
					shadow.position.z = imageHeight/2;
					shadow.rotation.x = Math.PI/2;			
					shadow.render();
				}				
			
			} 

			
			//function to handle 3d transition animations
			function _3dAnimate(navDir) {
				if( !animating ) {				
					if( !hover && clock ) {
						stopClock();
					}
					
						
					var curIndex = index,
						curSlide = $images[index],
						newFace,
						direction,
						animAxis,
						angle,
						i = slices;
					
					//determine the new slide based on navigation direction, i.e. next/prev
					if( navDir === 'next' ) {
						index++;
						if( index === slideNum ) {
							index = 0;
						}
					}
					else if( navDir === 'prev' ) {
						index--;
						if( index < 0 ) {
							index = slideNum - 1;
						}
					}
					else {
						index = navDir;
						if( curIndex < index ) {
							navDir = 'next';
						}
						else {
							navDir = 'prev';
						}
					}
					
					var newSlide = $images[index];					
					
					
					//call beforeChange function
					opts.beforeSlideChange.call( $slider[0], index );
					
					//hide caption
					hideCaption();	
					
					//highlight new active link
					setActiveLink();

					//set the animation variable
					animating = true;
					
					
					//set the rotation direction and axis for each transition effect
					switch( effect ) {						
						case 'cubeLeft':
							if( navDir === 'next') {
								newFace = 1;
								direction = -1;
							}
							else { 
								newFace = 3;
								direction = 1;
							}
								
							animAxis = 'y';
							break;							
						
						case 'cubeRight':
							if( navDir === 'next') { 
								newFace = 3;
								direction = 1;
							}
							else {								
								newFace = 1;
								direction = -1;
							}
						
							animAxis = 'y';
							break;
						
						case 'cubeUp':							
							if( navDir === 'next') {
								newFace = 5;
								direction = 1;
							}
							else { 
								newFace = 4;
								direction = -1;
							}
							
							animAxis = 'x';						
							break;							
						
						case 'cubeDown':
							if( navDir === 'next') {
								newFace = 4;
								direction = -1;
							}
							else {
								newFace = 5;
								direction = 1;
							}
							
							animAxis = 'x';
							break;				
						
						case 'flipLeft':
							if( navDir === 'next') {
								direction = -1;
							}
							else {
								direction = 1;
							}
							
							newFace = 2;
							animAxis = 'y';
							break;
						
						case 'flipRight':
							if( navDir === 'next') {
								direction = 1;
							}
							else {
								direction = -1;
							}
							
							newFace = 2;
							animAxis = 'y';
							break;
						
						case 'flipUp':
							if( navDir === 'next') {
								direction = 1;
							}
							else {
								direction = -1;
							}
							
							newFace = 2;
							animAxis = 'x';
							break;
						
						case 'flipDown':
							if( navDir === 'next') {
								direction = -1;
							}							
							else {
								direction = 1;
							}
							
							newFace = 2;
							animAxis = 'x';
							break;
						
						case 'blindsVertical':
							if( navDir === 'next') {
								direction = 1;
							}
							else {
								direction = -1;
							}
							
							newFace = 2;
							animAxis = 'y';
							break;
						
						case 'blindsHorizontal':
							if( navDir === 'next') {
								direction = -1;
							}
							else {
								direction = 1;
							}
							
							newFace = 2;
							animAxis = 'x';
							break;
					}					
					
					
					//Map the current image and new image on proper faces of the boxes
					while(i--) {
						drawImgStrip(srcCtxCur[i], curSlide, i);
						drawImgStrip(srcCtxNew[i], newSlide, i);
						
						cubes[i].images[0] = srcCanvasCur[i];
						cubes[i].images[newFace] = srcCanvasNew[i];
					}
					
					
					//rotate by 90deg for cube effect and 180deg for flip/blinds effect
					if( effect.indexOf('cube') === 0 ) {
						angle = Math.PI/2;
					}
					else {
						angle = Math.PI;
					}
					
					
					//preform rotation
					var rotationAnim = animAxis === 'y' ? {y: direction*angle} : {x: direction*angle} ;
					i = slices;
					
					while(i--) {
						cubes[i].rotation[animAxis] = 0;
						
						$(cubes[i].rotation)
								.delay(i*delay)
								.animate( rotationAnim,
										   {
											duration: animSpeed,
											easing: easing,
											step: function(val) {
												this.parent.render();
											}
										});
					}
					
					
					//unlock animations and setup next effect
					setTimeout( function() {
						showCaption();
						unlock();
						if( randanim ) {
							randomEffect();
							setup3d();
						}
					}, animSpeed + (slices-1)*delay);
					
				}
				
			}
			
			
			
			//function to draw image strips on dummy canvases based on effect
			function drawImgStrip(ctx, img, stripNum) {
				if( effect.indexOf('Up') !== -1 || effect.indexOf('Down') !== -1 || effect === 'blindsVertical' ) {
					var calc1	= stripNum*img.width/slices;
					//console.log('c1: ' + calc1);
					//console.log(img.width/slices);
					ctx.drawImage(img, calc1, 0, img.width/slices, img.height, 0, 0, objWidth, objHeight);
				}
				else if( effect.indexOf('Left') !== -1 || effect.indexOf('Right') !== -1 || effect === 'blindsHorizontal' ) {
					var calc2	= stripNum*img.height/slices;
					//console.log('c2: ' + calc2);
					//console.log(img.height/slices);
					ctx.drawImage(img, 0, stripNum*img.height/slices, objWidth, objHeight/slices, 0, 0, objWidth, objHeight);
				}
			}
			
			
			
			/**********************************  Code for 2d effects **********************************/
			if( effectType === '2d' ) {	
				//set the slider width and height to that of the largest image
				$slider.width(1);
				$slider.height(1);
				
				$images.each(function(){
					var thisImg = $(this);
					if( $slider.width() < thisImg.width() ) {
						$slider.width( thisImg.width() );
						width = $slider.width();
					}
					
					if( $slider.height() < thisImg.height() ) {
						$slider.height( thisImg.height() );
						height = $slider.height();
					}
				});
			
			
				//set the z-index of the starting image and fade it into view
				$images.eq(index).css('z-index','3')
							 .fadeIn( 600, function(){
								$images.show();  // this makes other images visible
							});
							
				
				var $wipeDiv,
					$split1,
					$split2;
				
				function setup2d() {
					//create dummy element for 'wipe' effect
					if( effect.indexOf('Wipe') !== -1 ) {
						if( !$wipeDiv ) {							
							$wipeDiv = $('<div class="wipe-div"/>').appendTo($slider);
							$wipeDiv.css({
								position: 'absolute',
								top: 0,
								left: 0,
								width: 0,
								height: 0,
								zIndex: 3
								});
						}
					}					
					
					//create dummy elements for 'split' effect
					if( effect.indexOf('Split') !== -1 ) {
						if( !$split1 ) {							
							$split1 = $('<div class="split1-div"/>').appendTo($slider);
							$split2 = $('<div class="split2-div"/>').appendTo($slider); 
							
							$split1.css({ position: 'absolute', zIndex: 4 });
							$split2.css({ position: 'absolute', zIndex: 4 });
						}
					}
				}    // end setup2d()
				
				setup2d();
			}			
			
			
			
			//2d animation function
			function _2dAnimate( navDir ) {
				if( !animating ) {
					if( !hover && clock ) {
						stopClock();
					}
					
					var curIndex = index,
						curSlide = $images.eq(index),
						newSlide;
					
					//find the new image
					if( navDir === 'next' ) {
						index++;
						if( index === slideNum ) {
							index = 0;
						}
					}
					else if( navDir === 'prev' ) {
						index--;
						if( index < 0 ) {
							index = slideNum - 1;
						}
					}
					else {
						index = navDir;
						if( curIndex < index ) {
							navDir = 'next';
						}
						else {
							navDir = 'prev';
						}
					}
					
					newSlide = $images.eq(index);
					
					//call beforeChange function
					opts.beforeSlideChange.call( $slider[0], index );
					
					//hide the caption
					hideCaption();
					
					//highlight new active link
					setActiveLink();
					
					//set the animation variable
					animating = true;
					
					//first reset the z-index of all images
					$images.css('z-index','1');
					
					//then set the z-index of the current image
					curSlide.css('z-index','2');
					
					
					//now animate the new image
					switch( effect ) {
						case 'fade':
							newSlide.css({ opacity: 0, zIndex: 3 })
									.animate({ opacity: 1 }, animSpeed, function(){ 
										showCaption(); 
										unlock();
										if( randanim ) {
											randomEffect();
											setup2d();
										}
									});
							
							break;
							
						case 'horizontalOverlap':
							if( navDir === 'next' ) {
								newSlide.css({ left: width, zIndex: 3 })
									.animate({ left: 0 }, animSpeed, function(){ 
										showCaption(); 
										unlock();
										if( randanim ) {
											randomEffect();
											setup2d();
										}
									});
							}
							else {
								newSlide.css({ left: -width, zIndex: 3 })
									.animate({ left: 0 }, animSpeed, function(){ 
										showCaption(); 
										unlock();
										if( randanim ) {
											randomEffect();
											setup2d();
										}
									});
							}
							
							break;
							
						case 'verticalOverlap':
							if( navDir === 'next' ) {
								newSlide.css({ top: -height, zIndex: 3 })
									.animate({ top: 0 }, animSpeed, function(){ 
										showCaption(); 
										unlock();
										if( randanim ) {
											randomEffect();
											setup2d();
										}
									});
							}
							else {
								newSlide.css({ top: height, zIndex: 3 })
									.animate({ top: 0 }, animSpeed, function(){
										showCaption();
										unlock();
										if( randanim ) {
											randomEffect();
											setup2d();
										}
									});
							}
							
							break;
							
						case 'horizontalSlide':
							if( navDir === 'next' ) {
								newSlide.css({ left: width, zIndex: 3 })
									.animate({ left: 0 }, animSpeed, function(){
										showCaption();
										unlock();
										if( randanim ) {
											randomEffect();
											setup2d();
										}
									});
									
								curSlide.animate({ left: -width }, animSpeed, function(){curSlide.css('left', '0');});
							}
							else {
								newSlide.css({ left: -width, zIndex: 3 })
									.animate({ left: 0 }, opts.animSpeed, function(){
										showCaption();
										unlock();
										if( randanim ) {
											randomEffect();
											setup2d();
										}
									});
									
								curSlide.animate({ left: width }, animSpeed, function(){curSlide.css('left', '0');});
							}
							
							break;
							
						case 'verticalSlide':
							if( navDir === 'next' ) {
								newSlide.css({ top: -height, zIndex: 3 })
									.animate({ top: 0 }, animSpeed, function(){ 
										showCaption(); 
										unlock();
										if( randanim ) {
											randomEffect();
											setup2d();
										}
									});
									
								curSlide.animate({ top: height }, animSpeed, function(){curSlide.css('top', '0');});
							}
							else {
								newSlide.css({ top: height, zIndex: 3 })
									.animate({ top: 0 }, animSpeed, function(){ 
										showCaption();
										unlock();
										if( randanim ) {
											randomEffect();
											setup2d();
										}
									});
									
								curSlide.animate({ top: -height }, animSpeed, function(){curSlide.css('top', '0');});
							}
							
							break;
							
						case 'horizontalWipe':
							newSlide.hide();
							$wipeDiv.css({ background: 'url('+ newSlide[0].src + ') no-repeat', height: height });
							
							$wipeDiv.animate({ width: width }, animSpeed, function(){ 
								$wipeDiv.width(0);
								newSlide.css('z-index','3').show();
								showCaption();
								unlock();
								if( randanim ) {
									randomEffect();
									setup2d();
								}
							});
							
							break;
							
						case 'verticalWipe':
							newSlide.hide();
							$wipeDiv.css({ background: 'url('+ newSlide[0].src + ') no-repeat', width: width });
							
							$wipeDiv.animate({ height: height }, animSpeed, function(){ 
								$wipeDiv.height(0);
								newSlide.css('z-index','3').show();
								showCaption();
								unlock();
								if( randanim ) {
									randomEffect();
									setup2d();
								}
							});
							
							break;
							
						case 'verticalSplit':
							curSlide.css({ opacity: 0 });
							newSlide.css({ zIndex: 3 });
							$split1.css({ width: width/2, height: height, top: 0, left: 0, background: 'url('+ curSlide[0].src + ') no-repeat' });
							$split2.css({ width: width/2, height: height, top: 0, right: 0, background: 'url('+ curSlide[0].src + ') -50% 0 no-repeat' });
							
							$split1.animate({ width: 0 }, animSpeed);
							$split2.animate({ width: 0 }, 
											{ duration: animSpeed,
											  step: function(val){
											      $split2.css('background-position', val - width +'px 0');
											  },
											  complete: function(){
												curSlide.css('opacity','1');
												showCaption();
												unlock();
												if( randanim ) {
													randomEffect();
													setup2d();
												}
												}
											});							
							
							break;
							
						case 'horizontalSplit':							
							curSlide.css({ opacity: 0 });
							newSlide.css({ zIndex: 3 });
							$split1.css({ width: width, height: height/2, top: 0, left: 0, background: 'url('+ curSlide[0].src + ') no-repeat' });
							$split2.css({ width: width, height: height/2, bottom: 0, left: 0, background: 'url('+ curSlide[0].src + ') 0 -50% no-repeat' });
							
							$split1.animate({ height: 0 }, animSpeed);
							$split2.animate({ height: 0 }, 
											{ duration: animSpeed,
											  step: function(val){
											      $split2.css('background-position', '0' + (val - height) +'px');
											  },
											  complete: function(){
												curSlide.css('opacity','1');
												showCaption();
												unlock();
												if( randanim ) {
													randomEffect();
													setup2d();
												}
												}
											});
							
							break;
							
						case 'fadeSlide':
							newSlide.css('z-index', '3');
							curSlide.css('z-index', '4');
							
							if( navDir === 'next' ) {
								curSlide.animate({ left: - width, opacity: 0 }, animSpeed, function(){
											curSlide.css({ left: 0, opacity: 1, zIndex: 1 });
											showCaption();
											unlock();
											if( randanim ) {
												randomEffect();
												setup2d();
											}
											});
							}
							else {
								curSlide.animate({ left: width, opacity: 0 }, animSpeed, function(){
											curSlide.css({ left: 0, opacity: 1, zIndex: 1 });
											showCaption();
											unlock();
											if( randanim ) {
												randomEffect();
												setup2d();
											}
											});
							}
							
							break;
					}
				}				
			}   // end _2dAnimate()
			
			
        });
    };
	
    
	
    // Plugin Defaults
    $.fn.ccslider.defaults = {
		effectType: '3d',
		effect: 'cubeUp',
		_3dOptions: { imageWidth: 600,
					  imageHeight: 300,
					  innerSideColor: '#444',
					  makeShadow: true,
					  shadowColor: 'rgba(0, 0, 0, 0.7)',					  
					  slices: 3,
					  delay: 200,
					  easing: 'easeInOutBack',
					  fallBack: 'fadeSlide',
					  fallBackSpeed: 1200
					},		
		animSpeed: 2200,
		startSlide: 0,
		directionNav: true,
		controlLinks: true,
		controlLinkThumbs: false,
		controlThumbLocation: '',
		autoPlay: true,
		pauseTime: 3000,
		pauseOnHover: true,
		captions: true,
		captionAnimation: 'slide',
		captionAnimationSpeed: 600,
		beforeSlideChange: function(index){},
		afterSlideChange: function(index){}
    };
	
		
})(jQuery);    // Closure Closed




//Defining the 3d objects and required 3d math

//define the Cube object
function Cube(width, height, depth, focalLength, ctx, color, images) {
	this.width = width;
	this.height = height;
	this.depth = depth;
	this.focalLength = focalLength;
	this.ctx = ctx;
	this.color = color;
	this.images = images;	
	
	this.rotation = {
		x: 0, 
		y: 0,
		z: 0,
		parent: this
	};
	
	this.position = {
		x : 0,
		y : 0,
		z : 0,
		parent: this
	};
	
	var canvas = this.ctx.canvas,
		cwidth = canvas.width,
		cheight = canvas.height,	
		centerx = cwidth/2,
		centery = cheight/2;	
	
	
	var vertexPoints = [
		make3DPoint(-this.width/2, this.height/2, -this.depth/2), 
		make3DPoint(this.width/2, this.height/2, -this.depth/2),  
		make3DPoint(this.width/2, -this.height/2, -this.depth/2),   
		make3DPoint(-this.width/2, -this.height/2, -this.depth/2),   
		make3DPoint(-this.width/2, this.height/2, this.depth/2), 
		make3DPoint(this.width/2, this.height/2, this.depth/2),  
		make3DPoint(this.width/2, -this.height/2, this.depth/2),  
		make3DPoint(-this.width/2, -this.height/2, this.depth/2)   
	];
	
	this.position.z += this.depth/2;
	
	//render the cube on the canvas
	this.render = function() {
		var points = Transform3DTo2D(vertexPoints, this.rotation, this.position, this.focalLength, centerx, centery);				
		
		//refresh the canvas before drawing
		canvas.width = 1;
		canvas.width = cwidth;
		
		var ptarray;		
		
		if( isVisible( points[3], points[0], points[1] ) ) {
			ptarray = [ points[0], points[1], points[3], points[2] ];
			mapTexture(ctx, ptarray, this.images[0]);					
		}		
		
		if( isVisible( points[6], points[5], points[4] ) ) {
			if( this.rotation.x === 0) {
				ptarray = [ points[5], points[4], points[6], points[7] ];
			}
			else {
				ptarray = [ points[7], points[6], points[4], points[5] ]; 
			}
				
			mapTexture(ctx, ptarray, this.images[2]);					
		}		
		
		if( isVisible( points[2], points[1], points[5] ) && this.depth !== 0 ) {
			if( this.images[1] ) {
				ptarray = [ points[1], points[5], points[2], points[6] ];
				mapTexture(ctx, ptarray, this.images[1]);
			}
			else {
				ctx.fillStyle = this.color;
				drawPlane(ctx, points[1], points[5], points[6], points[2]);				
				ctx.fill();
			}
		}		
		
		if( isVisible( points[7], points[4], points[0] ) && this.depth !== 0 ) {
			if( this.images[3] ) {
				ptarray = [ points[4], points[0], points[7], points[3] ];
				mapTexture(ctx, ptarray, this.images[3]);
			}
			else {
				ctx.fillStyle = this.color;
				drawPlane(ctx, points[4], points[0], points[3], points[7]);				
				ctx.fill();
			}
		}		
		
		if( isVisible( points[0], points[4], points[5] ) && this.depth !== 0 ) {
			if( this.images[4] ) {
				ptarray = [ points[4], points[5], points[0], points[1] ];
				mapTexture(ctx, ptarray, this.images[4]);
			}
			else {
				ctx.fillStyle = this.color;
				drawPlane(ctx, points[4], points[5], points[1], points[0]);				
				ctx.fill();
			}
		}		
		
		if( isVisible( points[7], points[3], points[2] ) && this.depth !== 0 ) {
			if( this.images[5] ) {
				ptarray = [ points[3], points[2], points[7], points[6] ];
				mapTexture(ctx, ptarray, this.images[5]);
			}
			else {
				ctx.fillStyle = this.color;
				drawPlane(ctx, points[3], points[2], points[6], points[7]);				
				ctx.fill();
			}
		}
	};				
}



//define the Plane object		
function Plane(width, height, focalLength, ctx, color, img, makeShadow, shadowColor) {
	this.width = width;
	this.height = height;
	this.focalLength = focalLength;
	this.ctx = ctx;
	this.color = color;
				
	this.rotation = {
		x: 0, 
		y: 0,
		z: 0
	};
	
	this.position = {
		x : 0,
		y : 0,
		z : 0
	};
	
	var canvas = this.ctx.canvas,
		cwidth = canvas.width,
		cheight = canvas.height,	
		centerx = cwidth/2,
		centery = cheight/2;
		
	
	var vertexPoints = [
		make3DPoint(-this.width/2, this.height/2, 0), 
		make3DPoint(this.width/2, this.height/2, 0),  
		make3DPoint(this.width/2, -this.height/2, 0),   
		make3DPoint(-this.width/2, -this.height/2, 0)  
	];			
	
	
	//render the plane on the canvas
	this.render = function() {
		var points = Transform3DTo2D(vertexPoints, this.rotation, this.position, this.focalLength, centerx, centery);
		
		//refresh the canvas before drawing
		canvas.width = 1;
		canvas.width = cwidth;		
		
		drawPlane(ctx, make2DPoint(0, cHeight), make2DPoint(cWidth, cHeight), make2DPoint(cWidth, cHeight - 100), make2DPoint(0, cHeight - 100));
		ctx.clip();	
		
		ctx.shadowOffsetX = 0;
		ctx.shadowOffsetY = 50;
		ctx.shadowBlur    = 30;
		ctx.shadowColor   = shadowColor;
		
		ctx.fillStyle = this.color;
		drawPlane(ctx, points[0], points[1], points[2], points[3]);				
		ctx.fill();		
	};	
}



function make3DPoint(x,y,z) {
	var point = {
		x : x,
		y : y,
		z : z
	};
	return point;
}


function make2DPoint(x,y) {
	var point = {
		x : x,
		y : y		
	}
	return point;
}


function Transform3DTo2D(points, axisRotations, position, focalLength, centerx, centery) {	
	var TransformedPoints = [],
		Mathsin = Math.sin,
		Mathcos = Math.cos,
		sx = Mathsin(axisRotations.x),
		cx = Mathcos(axisRotations.x),
		sy = Mathsin(axisRotations.y),
		cy = Mathcos(axisRotations.y),
		sz = Mathsin(axisRotations.z),
		cz = Mathcos(axisRotations.z),
		x,y,z, xy,xz, yx,yz, zx,zy, scaleFactor;

	var i = points.length;
	
	while (i--) {
		x = points[i].x;
		y = points[i].y;
		z = points[i].z;
		
		xy = cx*y - sx*z;
		xz = sx*y + cx*z;		
		
		yz = cy*xz + sy*x;
		yx = -sy*xz + cy*x;		
		
		zx = cz*yx - sz*xy;
		zy = sz*yx + cz*xy;
		
		x = zx + position.x;
		y = zy + position.y;
		z = yz + position.z;
		
		scaleFactor = focalLength/(focalLength + z);
		x = x*scaleFactor + centerx;
		y = -(y*scaleFactor) + centery;	

		TransformedPoints[i] = make2DPoint(x, y);
	}	
	return TransformedPoints;
}


function drawPlane(ctx, a, b, c, d){			
	ctx.beginPath();
	ctx.moveTo(a.x, a.y);
	ctx.lineTo(b.x, b.y);
	ctx.lineTo(c.x, c.y);
	ctx.lineTo(d.x, d.y);
	ctx.closePath();			
}


function isVisible(a, b, c) {
	if ( ( (b.y - a.y)/(b.x - a.x) - (c.y - a.y)/(c.x - a.x) < 0 ) ^ (a.x <= b.x === a.x > c.x)) {
		return true;
	} else {
		return false;
	}
}



/*
 * Projective texturing using Canvas.
 * (c) Steven Wittens 2008
 * http://www.acko.net/
 */
 
/*
 * Modified by Nilok Bose, (c) 2011   
 * http://codecanyon.net/user/cosmocoder
 */


/**
 * Update the display to match a new point configuration.
 */
function mapTexture(ctx, points, img) {   
  var subdivisionLimit = 5,
	  patchSize = 64,	
	  transform = getProjectiveTransform(points);
 
  var ptl = transform.transformProjectiveVector([0, 0, 1]),
	  ptr = transform.transformProjectiveVector([1, 0, 1]),
	  pbl = transform.transformProjectiveVector([0, 1, 1]),
	  pbr = transform.transformProjectiveVector([1, 1, 1]);

  
  ctx.save();  
  
  ctx.beginPath();
  ctx.moveTo(ptl[0], ptl[1]);
  ctx.lineTo(ptr[0], ptr[1]);
  ctx.lineTo(pbr[0], pbr[1]);
  ctx.lineTo(pbl[0], pbl[1]);
  ctx.closePath();
  ctx.clip();

  divide(0, 0, 1, 1, ptl, ptr, pbl, pbr, transform, subdivisionLimit, patchSize, ctx, img);  
 
  ctx.restore();
}

/**
 * Render a projective patch.
 */
function divide(u1, v1, u4, v4, p1, p2, p3, p4, transform, limit, patchSize, ctx, img) {
  var Mathabs = Math.abs,
	  Mathmax = Math.max,
	  Mathmin = Math.min,
	  Mathsqrt = Math.sqrt;
  
  
  if (limit) {    
    var d1 = [p2[0] + p3[0] - 2 * p1[0], p2[1] + p3[1] - 2 * p1[1]],
        d2 = [p2[0] + p3[0] - 2 * p4[0], p2[1] + p3[1] - 2 * p4[1]],
        d3 = [d1[0] + d2[0], d1[1] + d2[1]],
        r = Mathabs((d3[0] * d3[0] + d3[1] * d3[1]) / (d1[0] * d2[0] + d1[1] * d2[1]));
    
    d1 = [p2[0] - p1[0] + p4[0] - p3[0], p2[1] - p1[1] + p4[1] - p3[1]];
    d2 = [p3[0] - p1[0] + p4[0] - p2[0], p3[1] - p1[1] + p4[1] - p2[1]];
    var area = Mathabs(d1[0] * d2[1] - d1[1] * d2[0]);

   
    if ((u1 === 0 && u4 === 1) || ((.25 + r * 5) * area > (patchSize * patchSize))) {      
      var umid = (u1 + u4) / 2,
          vmid = (v1 + v4) / 2,
          pmid = transform.transformProjectiveVector([umid, vmid, 1]),
          pt = transform.transformProjectiveVector([umid, v1, 1]),
          pb = transform.transformProjectiveVector([umid, v4, 1]),
          pl = transform.transformProjectiveVector([u1, vmid, 1]),
          pr = transform.transformProjectiveVector([u4, vmid, 1]);
      
      --limit;
      divide(u1, v1, umid, vmid, p1, pt, pl, pmid, transform, limit, patchSize, ctx, img);
      divide(umid, v1, u4, vmid, pt, p2, pmid, pr, transform, limit, patchSize, ctx, img);
      divide(u1, vmid, umid, v4, pl, pmid, p3, pb, transform, limit, patchSize, ctx, img);
      divide(umid, vmid, u4, v4, pmid, pr, pb, p4, transform, limit, patchSize, ctx, img);
    
      return;
    }
  }
  
  ctx.save();
  
  ctx.beginPath();
  ctx.moveTo(p1[0], p1[1]);
  ctx.lineTo(p2[0], p2[1]);
  ctx.lineTo(p4[0], p4[1]);
  ctx.lineTo(p3[0], p3[1]);
  ctx.closePath();  
  
 
  var d12 = [p2[0] - p1[0], p2[1] - p1[1]],
      d24 = [p4[0] - p2[0], p4[1] - p2[1]],
      d43 = [p3[0] - p4[0], p3[1] - p4[1]],
      d31 = [p1[0] - p3[0], p1[1] - p3[1]];
  
   var a1 = Mathabs(d12[0] * d31[1] - d12[1] * d31[0]),
       a2 = Mathabs(d24[0] * d12[1] - d24[1] * d12[0]),
       a4 = Mathabs(d43[0] * d24[1] - d43[1] * d24[0]),
       a3 = Mathabs(d31[0] * d43[1] - d31[1] * d43[0]),
       amax = Mathmax(Mathmax(a1, a2), Mathmax(a3, a4)),
       dx = 0, dy = 0, padx = 0, pady = 0;  
  
  switch (amax) {
    case a1:
      ctx.transform(d12[0], d12[1], -d31[0], -d31[1], p1[0], p1[1]);      
      if (u4 !== 1) padx = 1.05 / Mathsqrt(d12[0] * d12[0] + d12[1] * d12[1]);
      if (v4 !== 1) pady = 1.05 / Mathsqrt(d31[0] * d31[0] + d31[1] * d31[1]);
      break;
    case a2:
      ctx.transform(d12[0], d12[1],  d24[0],  d24[1], p2[0], p2[1]);      
      if (u4 !== 1) padx = 1.05 / Mathsqrt(d12[0] * d12[0] + d12[1] * d12[1]);
      if (v4 !== 1) pady = 1.05 / Mathsqrt(d24[0] * d24[0] + d24[1] * d24[1]);
      dx = -1;
      break;
    case a4:
      ctx.transform(-d43[0], -d43[1], d24[0], d24[1], p4[0], p4[1]);     
      if (u4 !== 1) padx = 1.05 / Mathsqrt(d43[0] * d43[0] + d43[1] * d43[1]);
      if (v4 !== 1) pady = 1.05 / Mathsqrt(d24[0] * d24[0] + d24[1] * d24[1]);
      dx = -1;
      dy = -1;
      break;
    case a3:      
      ctx.transform(-d43[0], -d43[1], -d31[0], -d31[1], p3[0], p3[1]);
      if (u4 !== 1) padx = 1.05 / Mathsqrt(d43[0] * d43[0] + d43[1] * d43[1]);
      if (v4 !== 1) pady = 1.05 / Mathsqrt(d31[0] * d31[0] + d31[1] * d31[1]);
      dy = -1;
      break;
  }
  
  
  var du = (u4 - u1),
      dv = (v4 - v1),
      padu = padx * du,
      padv = pady * dv;
  
   
  var iw = img.width,
	  ih = img.height; 
  
  ctx.drawImage(
    img,
    u1 * iw,
    v1 * ih,
    Mathmin(u4 - u1 + padu, 1) * iw,
    Mathmin(v4 - v1 + padv, 1) * ih,
    dx, dy,
    1 + padx, 1 + pady
  );
  ctx.restore();
}


/**
 * Calculate a projective transform that maps [0,1]x[0,1] onto the given set of points.
 */
function getProjectiveTransform(points) {
  var eqMatrix = new Matrix(9, 8, [
    [ 1, 1, 1,   0, 0, 0, -points[3].x,-points[3].x,-points[3].x ], 
    [ 0, 1, 1,   0, 0, 0,  0,-points[2].x,-points[2].x ],
    [ 1, 0, 1,   0, 0, 0, -points[1].x, 0,-points[1].x ],
    [ 0, 0, 1,   0, 0, 0,  0, 0,-points[0].x ],

    [ 0, 0, 0,  -1,-1,-1,  points[3].y, points[3].y, points[3].y ],
    [ 0, 0, 0,   0,-1,-1,  0, points[2].y, points[2].y ],
    [ 0, 0, 0,  -1, 0,-1,  points[1].y, 0, points[1].y ],
    [ 0, 0, 0,   0, 0,-1,  0, 0, points[0].y ]

  ]);
  
  var kernel = eqMatrix.rowEchelon().values;
  var transform = new Matrix(3, 3, [
    [-kernel[0][8], -kernel[1][8], -kernel[2][8]],
    [-kernel[3][8], -kernel[4][8], -kernel[5][8]],
    [-kernel[6][8], -kernel[7][8],             1]
  ]);
  return transform;
}



/* 
 * Generic matrix class.
 * (c) Steven Wittens 2008
 * http://www.acko.net/
 */
 
/*
 * Modified by Nilok Bose, (c) 2011  
 * http://codecanyon.net/user/cosmocoder
 */

 
var Matrix = function (w, h, values) {
  this.w = w;
  this.h = h;
  this.values = values || Matrix.allocate(h);
};

Matrix.allocate = function (w, h) {
  var values = [],
	  i = h,
	  j = w;
	  
  while(i--) {
    values[i] = [];
    while(j--) {
      values[i][j] = 0;
    }
  } 
  return values; 
}

Matrix.cloneValues = function (values) {
  clone = [];
  for (var i = 0, len = values.length; i < len; ++i) {
    clone[i] = [].concat(values[i]);
  } 
  return clone; 
}

Matrix.prototype.transformProjectiveVector = function (operand) {
  var out = [];
  for (var y = 0; y < this.h; ++y) {
    out[y] = 0;
    for (var x = 0; x < this.w; ++x) {
      out[y] += this.values[y][x] * operand[x];
    }
  }
  var iz = 1 / (out[out.length - 1]);
  for (var y = 0; y < this.h; ++y) {
    out[y] *= iz;
  }
  return out;
}


Matrix.prototype.rowEchelon = function () {
  if (this.w <= this.h) {
    throw "Matrix rowEchelon size mismatch";
  }
  
  var temp = Matrix.cloneValues(this.values);

  // Do Gauss-Jordan algorithm.
  for (var yp = 0; yp < this.h; ++yp) {
    // Look up pivot value.
    var pivot = temp[yp][yp];
    while (pivot == 0) {
      // If pivot is zero, find non-zero pivot below.
      for (var ys = yp + 1; ys < this.h; ++ys) {
        if (temp[ys][yp] != 0) {
          // Swap rows.
          var tmpRow = temp[ys];
          temp[ys] = temp[yp];
          temp[yp] = tmpRow;
          break;
        }
      }
      if (ys == this.h) {
        // No suitable pivot found. Abort.
        return new Matrix(this.w, this.h, temp);
      }
      else {
        pivot = temp[yp][yp];        
      }
    };
    // Normalize this row.
    var scale = 1 / pivot;
    for (var x = yp; x < this.w; ++x) {
      temp[yp][x] *= scale;
    }
    // Subtract this row from all other rows (scaled).
    for (var y = 0; y < this.h; ++y) {
      if (y == yp) continue;
      var factor = temp[y][yp];
      temp[y][yp] = 0;
      for (var x = yp + 1; x < this.w; ++x) {
        temp[y][x] -= factor * temp[yp][x];
      }
    }
  }  

  return new Matrix(this.w, this.h, temp);
}
