(function($){$.fn.jMyCarousel=function(o){o=$.extend({btnPrev:null,btnNext:null,mouseWheel:true,auto:false,speed:500,easing:'linear',vertical:false,circular:true,visible:'4',start:0,scroll:1,step:50,eltByElt:false,evtStart:'mouseover',evtStop:'mouseout',beforeStart:null,afterEnd:null},o||{});return this.each(function(){var running=false,animCss=o.vertical?"top":"left",sizeCss=o.vertical?"height":"width";var div=$(this),ul=$("ul",div),tLi=$("li",ul),tl=tLi.size(),v=o.visible;var mousewheelN=0;var defaultBtn=(o.btnNext===null&&o.btnPrev===null)?true:false;var cssU=(v.toString().indexOf("%")!=-1?'%':(v.toString().indexOf("px")!=-1)?'px':'el');var direction=null;if(o.circular){var imgSet=tLi.clone();ul.prepend(imgSet).append(imgSet.clone());}
var li=$("li",ul);div.css("visibility","visible");li.css("overflow","hidden").css("float",o.vertical?"none":"left").children().css("overflow","hidden");if(!o.vertical){li.css("display","inline");}
if(li.children().get(0).tagName.toLowerCase()=='a'&&!o.vertical){li.children().css('float','left');}
if(o.vertical&&jQuery.browser.msie){li.css('line-height','4px').children().css('margin-bottom','-4px');}
ul.css("margin","0").css("padding","0").css("position","relative").css("list-style-type","none").css("z-index","1");div.css("overflow","hidden").css("position","relative").css("z-index","2").css("left","0px");var liSize=o.vertical?height(li):width(li);var liSizeV=o.vertical?elHeight(li):height(li);var curr=o.start;var nbAllElts=li.size();var ulSize=liSize*nbAllElts;var nbElts=tl;var eltsSize=nbElts*liSize;var allEltsSize=nbAllElts*liSize;var step=o.step=='default'?liSize:o.step;o.btnPrev=defaultBtn?$('<input type="button" class="'+(o.vertical?'up':'prev')+'" />'):$(o.btnPrev);o.btnNext=defaultBtn?$('<input type="button" class="'+(o.vertical?'down':'next')+'" />'):$(o.btnNext);var prev=o.btnPrev;var next=o.btnNext;if(defaultBtn&&o.auto!==true){prev.css({'opacity':'0.6'});next.css({'opacity':'0.6'});div.prepend(prev);div.prepend(next);o.btnPrev=prev;o.btnNext=next;}
if(o.eltByElt){step=liSize;if(o.start%liSize!==0){var imgStart=parseInt(o.start/liSize);curr=o.start=(imgStart*liSize);}}
if(o.circular){o.start+=(liSize*tl);curr+=(liSize*tl);}
var divSize,cssSize,cssUnity;if(cssU=='%'){divSize=0;cssSize=parseInt(v);cssUnity="%";}
else if(cssU=='px'){divSize=parseInt(v);cssSize=parseInt(v);cssUnity="px";}
else{divSize=liSize*parseInt(v);cssSize=liSize*parseInt(v);cssUnity="px";}
ul.css(sizeCss,ulSize+"%").css(animCss,-(o.start));div.css(sizeCss,cssSize+cssUnity);if(o.vertical&&cssUnity=='%'){var pxsize=((liSize*nbElts)*(parseInt(v)/100));div.css(sizeCss,pxsize+'px');}
if(divSize===0){divSize=div.width();}
if(o.vertical){div.css("width",liSizeV+'px');ul.css("width",liSizeV+'px');li.css('margin-bottom',(parseInt(li.css('margin-bottom'))*2)+'px');li.eq(li.size()-1).css('margin-bottom',li.css('margin-top'));}else{div.css('height',liSizeV+'px');ul.css('height',liSizeV+'px');}
if(cssU=='%'){v=divSize/li.width();if(v%1!==0){v+=1;}
v=parseInt(v);}
var divVSize=div.height();if(defaultBtn){next.css({'z-index':200,'position':'absolute'});prev.css({'z-index':200,'position':'absolute'});if(o.vertical){prev.css({'width':prev.width(),'height':prev.height(),'top':'0px','left':parseInt(liSizeV/2)-parseInt(prev.width()/2)+'px'});next.css({'width':prev.width(),'height':prev.height(),'top':(divVSize-prev.height())+'px','left':parseInt(liSizeV/2)-parseInt(prev.width()/2)+'px'});}
else{prev.css({'left':'0px','top':parseInt(liSizeV/2)-parseInt(prev.height()/2)+'px'});next.css({'right':'0px','top':parseInt(liSizeV/2)-parseInt(prev.height()/2)+'px'});}}
if(o.btnPrev){            
            $(o.btnPrev).bind(o.evtStart, function() {
                if(defaultBtn){ o.btnPrev.css('opacity',0.9); }
                running = true;
                direction = 'backward';
                return backward(); 
            });
            
            $(o.btnPrev).bind(o.evtStop, function() {
            	if(defaultBtn){ o.btnPrev.css('opacity',0.6); }
            	running = false; 
            	direction = null;
                return stop(); 
            });
        }
        
        
        // Bind the events with the "next" button
        if(o.btnNext){
			$(o.btnNext).bind(o.evtStart, function() {
				if(defaultBtn){ o.btnNext.css('opacity',0.9); }
				running = true;
				 direction = 'forward';
                return forward(); 
            });
			$(o.btnNext).bind(o.evtStop,function() {
				if(defaultBtn){ o.btnNext.css('opacity',0.6); }
				running = false;
				direction = null;
                return stop(); 
            });
        }
        
       // auto scroll management (auto = true). => launch the animation
	   if(o.auto === true){
	   	 running = true;	
	   	 forward();	
	   }		

		// Mousewheel management	
        if(o.mouseWheel && div.mousewheel){
            div.mousewheel(function(e, d) { 
                if(!o.circular && (d > 0 ? (curr + divSize < ulSize) : (curr > 0)) || o.circular){ //prevents the mouse events to occur in case of circular mode
	                mousewheelN += 1; 				//one more step to do, store it.
	                if(running === false){
	                	if(d > 0){ forward(step, true); }
	                	else { backward(step, true); }
	                	running = true;
	                }
                }
            });
        }
		
		/**
		 * Animate the track by moving it forward according to the step size and the speed
		 * @param stepsize, the size of the step (optional)
		 * @param once, shall the animation continue endlessly until we set running to false ? (optional)
		 */
        function forward(stepsize, once){
    		var s = (stepsize ? stepsize : step);

			if(running === true && direction === "backward"){ return; }
			
    		//If not circular, no need to animate endlessly
    		if(!o.circular){
    			//will the next step overtake the last  image ?
    			if(curr + s + (o.vertical ? divVSize : divSize) > eltsSize){
    				s = eltsSize - (curr + (o.vertical ? divVSize : divSize));
    			}
    		}
    		
    		ul.animate(
                animCss == "left" ? { left: -(curr + s) } : { top: -(curr + s) } , o.speed, o.easing,
                function() {
                	curr += s; //Add step size
                	//Calculate whether we cross the limit,
                	//if so, put the carousel one time backward
                	if(o.circular){
	                	if(curr + (o.vertical ? divVSize : divSize) + liSize >= allEltsSize){
	                    	ul.css(o.vertical ? 'top' : 'left', -curr + eltsSize);
	                    	curr -= eltsSize;
	                    }
                	}
    				
                    if(!once && running){
                    	 forward();
                    }
                    else if(once){
                    	if(--mousewheelN > 0){
                    		this.forward(step, true);
                    	}
                    	else{
                    		 running = false;
                    		 direction = null;
                    	}
                    }
                }
            );
        }
        
        /**
         * Animate the track by moving it backward according to the step size and the speed
         * @param stepsize, the size of the step (optional)
		 * @param once, shall the animation continue endlessly until we set running to false ? (optional)
         */
        function backward(stepsize, once){
    		var s = (stepsize ? stepsize : step);
    		
    		if(running === true && direction === "forward"){ return; } 
    		
    		//If not circular, no need to animate endlessly
    		if(!o.circular){
    			//will the next step overtake the first image ?
    			if(curr - s  < 0){
    				 s = curr - 0;
    			}
    		}
    		
    		ul.animate(
                animCss == "left" ? { left: -(curr - s) } : { top: -(curr - s) } , o.speed, o.easing,
                function() {
                	curr -= s;
                	//Calculate if we cross the limit,
                	//if so, put the carousel one time backward
                    if(o.circular){
	                	if(curr <= liSize){
	                    	ul.css(o.vertical ? 'top' : 'left', -(curr + eltsSize));
	                    	curr += eltsSize;
	                    }
                    }
                    
					if(!once  && running){
						backward();
					}
					else if(once){
	                	if(--mousewheelN > 0){
	                		backward(step, true);
	                	}
	                	else{
	                		 running = false;
	                		 direction = null;
	                	}
                	}
                }
            );
        }
         /**
          * Stops the animation
          * Basically, tells the animation not to continue
          */
        function stop(){
        	if(!o.eltByElt){ 	//If we don't move elements by elements, then we can stop immediately
        		ul.stop(); 		// stop the animation straight
        		curr = 0 - parseInt(ul.css(animCss));	// We stopped suddenly, so the curr variable is not refreshed. We refresh it with the true value
        	}
        	running = false; 	// default value and in case we proceed element by element (eltByElt = true)
        	direction = null;
        }
        
        /**
         * Return the size of the carousel, everything included (height or length depending on o.vertical)
         */
        /*function jmcSize(){
        	var img = $('ul li img', div);
        	var sizeLi = (o.vertical ? img.width() : img.height());
        	var elt = img;
        	while(elt.parent().get(0).tagName.toLowerCase() != 'div'){
        		sizeLi += (o.vertical ? (parseInt(elt.css('marginLeft')) + parseInt(elt.css('marginRight')) + parseInt(elt.css('paddingRight')) + parseInt(elt.css('paddingLeft'))) : (parseInt(elt.css('marginTop')) + parseInt(elt.css('marginBottom')) + parseInt(elt.css('paddingTop')) + parseInt(elt.css('paddingBottom'))));
        		elt = elt.parent();
        	} 
        	return sizeLi;
        }*/
        
        /**
         * Calculate and return the size of the image in the element
         * @param el, the element
         * @param dimension, 'width' or 'height'.
         * @return the requested size in pixels.
         */
        function imgSize(el, dimension){
			if(dimension == 'width'){
				return el.find('img').width();
			}
			else {
				return el.find('img').height();
			}
		}
		
		/**
		 * Size of an element li with its margin calculated from scratch (without any call to width except for the image size)
		 * usefull in case of vertical carousel, when the size of each element is 100%.
		 * @param el, the element
		 * @return the size of the element in pixels
		 */
		function elHeight(el){
			var elImg = el.find('img');
			if(o.vertical){
		    	return parseInt(el.css('margin-left')) + parseInt(el.css('margin-right')) + parseInt(elImg.width()) + parseInt(el.css('border-left-width')) + parseInt(el.css('border-right-width')) + parseInt(el.css('padding-right')) + parseInt(el.css('padding-left'));
			}	
			else{
				return parseInt(el.css('margin-top')) + parseInt(el.css('margin-bottom')) + parseInt(elImg.width()) + parseInt(el.css('border-top-height')) + parseInt(el.css('border-bottom-height')) + parseInt(el.css('padding-top')) + parseInt(el.css('padding-bottom'));
			}
		}
        
        function debug(html){
        	$('#debug').html($('#debug').html() + html + "<br/>");
        } 
        
    });
};

function css(el, prop) {
    return parseInt($.css(el[0], prop)) || 0;
}

function width(el) {
    	return el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
}

function height(el) {
    return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
}

})(jQuery);
