(function($) {
	
	$.fn.lemFlow = function(settings) {
		
		var config = {
			
			prev:        '.prev',		 // The element that will trigger the moveToPrev action
			next:        '.next',		 // The element that will trigger the moveToNext action
			vertical:    0,              // If movement is vertical, set to 1. If horizontal, leave at 0
			width:       0,              // (mandatory if horizontal) The width in pixels of a single element (if groups are used, enter the width of the group)
			height:      0,              // (mandatory if vertical) The height value in pixels of the holder and tray
			numVisible:  1,              // (optional) The number of visible elements at a time (if groups are used, enter the number of groups)
			group:       1,              // (optional) Group that number of elements together per transition (it will wrap them in a div if the value is greater than 1)
			groupClass:  '',             // (optional) The class that will get a group, if used (do not write the period (.) before the class name)
			current:     '',             // (optional) The element that will display the current position -- ex: '.current'
			total:       '',             // (optional) The element that will display the total number of elements -- ex: '.total'
			imageTitle:  '',             // (optional) The element that will display the title of the image (that is set in the title of the image) -- ex: '.current'
			imageAlt:    '',             // (optional) The element that will display the description of the image (that is set in the alt of the image) -- ex: '.total'
			duration:    300,            // (optional) A number (milliseconds) determining how long the animation will run.
			easing:      null,           // (optional) A string indicating which easing function to use for the transition.
			pos:         1,              // (optional) The starting position
			loop:        false,          // (optional) If true, the flow will loop when it reaches the end
			linkset:     '',             // (optional) A class of links that calls a specific element
			autoplay:    false,          // (optional) If true, it will flow all by itself (no prev/next necessary)
			continuous:  true,           // (optional) If true, clicking next or previous won't cancel the autoplay. If false, a user action will cancel the autoplay.
			delay:       5000            // (optional) Delay in milliseconds before the autoplay goes to the next element
			
		};
		
		if (settings) {
		    
            $.extend(config, settings);
            
        }
		
		var mThis = this;			//This
		var holder;					//The div that's holding the elements
		var elements;				//The elements in the Holder
		var numElements;			//The number of elements in the Holder
		var tray;					//The tray that will be used to move the elements
		var moving;					//If true, the tray is currently moving
		var timeout;				//Used for the timeout
		var gap;                    //The distance (in px) to move (set according to config.width if horizontal, and config.height if vertical)
		var opposite;               //The width of height of the holder element (opposite to the movement direction)
		var strGapType;             //The string 'width' if horizontal, 'height' if vertical
		var strOppositeType;        //The string 'height' if horizontal, 'width' if vertical
		var strPosRelDirection;     //The string 'right' if horizontal, 'bottom' if vertical
		
		/* ------ INIT ------------------------------------------------------------------------ */
		var init = function(){
			
			config.pos -= 1;
			holder = '#'+$(mThis).attr('id');
			
			setDirection();
			
			if( config.group > 1 ){
				
				$( holder ).children().each( groupElements );
				
			}
			
			elements = $( holder ).children();
			numElements = elements.length;
			tray = holder + 'Tray' ;
			moving = false;
			
			$( config.prev ).click( beforePrev );
			$( config.next ).click( beforeNext );
			
			if( config.linkset !== '' ){
				
				$( config.linkset ).click( moveToSpecific );
				
			}
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ SET DIRECTION ------------------------------------------------------------------ */
		var setDirection = function(){
			
			
			if( config.vertical ){
				
				gap =                 config.height;
				opposite =            config.width;
				strGapType =          'height';
				strOppositeType =     'width';
				strPosRelDirection =  'bottom';
				
			} else {
                
                gap =                 config.width;
				opposite =            config.height;
				strGapType =          'width';
				strOppositeType =     'height';
				strPosRelDirection =  'right';
                
			}
			
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ GROUP ELEMENTS ----------------------------------------------------------------- */
		var groupElements = function( i ){
			
			var theClass = "";
			
			if( config.groupClass != "" ){
			    
				theClass = ' class="' + config.groupClass + '"' ;
				
			}
			$( holder ).children().slice( i , i + config.group ).wrapAll( '<div' + theClass + '>' );
			
		}
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ WRITE TOTAL -------------------------------------------------------- */
		var writeTotal = function(){
			
			if( config.total !== '' ){
			
				$( config.total ).html( numElements );
				
			}
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ MANIPULATE ELEMENTS -------------------------------------- */
		var manipulateElements = function(){
			
			for( var i = 0; i < elements.length; i++ ){
				
				if( !config.vertical ){
				    
				    elements.eq( i ).css( 'float', 'left' );
				    
				}
				
				elements.eq( i ).css( 'position', 'relative' );
			}
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ MANIPULATE HOLDERS ---------------------------------------- */
		var manipulateHolders = function(){
			
			if( config.loop ){
				
				for( var i = 0; i < config.numVisible; i++ ){
					
					elements.eq( i ).clone().appendTo( holder );
					
				}
				
			}
			$( holder ).children().wrapAll( '<div id="' + tray.substring( 1 ) +'">' );
			$( holder ).css( 'overflow', 'hidden' );
			$( holder ).css( 'position', 'relative' );
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ MANIPULATE TRAY ----------------------------------------------- */
		var manipulateTray = function(){
			
			$( tray ).append( '<div style="clear:both;"></div>' );
			$( tray ).css( 'position', 'relative' );
			$( tray ).css( strPosRelDirection, '0' );
			
			if( config.loop ){
				
				$( tray ).css( strGapType, gap * ( numElements + config.numVisible ) + 'px' );
				
			} else {
				
				$( tray ).css( strGapType, gap * numElements + 'px' );
				
			}
			
			makeSelected( config.pos );
			teleport( config.pos );
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ SET HEIGHT ------------------------------------------- */
		var setOpposite = function(){
			
			if(opposite !== 0){
				
				$(holder).css( strOppositeType, opposite + 'px' );
				$(tray).css( strOppositeType, opposite + 'px' );
				
			}
						
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ MOVE TRAY ----------------------------------------------------------- */
		var moveTray = function(){
			
			if( config.vertical ){
                
                $( tray ).animate( {
                    
    				bottom: gap * config.pos + 'px'
    				
    			}, config.duration, config.easing, afterMove );
    			
			} else {
                
                $( tray ).animate( {
                    
    				right: gap * config.pos + 'px'
    				
    			}, config.duration, config.easing, afterMove );
    			
			}
		
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ AFTER MOVE ----------------------------------------------------------- */
		var afterMove = function(){
			
			if( config.loop && config.pos === numElements ){
			    
				teleport( 0 );
				
			} else {
			    
				setCurrents();
				
			}
			
			moving = false;
			autoplay();
		
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ TELEPORT ------------------------------------------------------------- */
		var teleport = function( target ){
			
			config.pos = target;
			$( tray ).css( strPosRelDirection, gap * config.pos + 'px' );
			
			if(config.pos !== numElements){
			    
				setCurrents();
				
			}
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ BEFORE PREV -------------------------------------------------------- */
		var beforePrev = function( e ){
			
			resetTimeout();
			moveToPrev();
			
			return false;
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ BEFORE NEXT -------------------------------------------------------- */
		var beforeNext = function(){
			
			resetTimeout();
			moveToNext();
			
			return false;
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ BEFORE SPECIFIC -------------------------------------------------------- */
		var beforeSpecific = function(){
			
			resetTimeout();
			moveToSpecific();
			
			return false;
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ MOVE TO PREV ----------------------------------------------------- */
		var moveToPrev = function(){
		
			if( !moving ){
				
				moving = true;
				
				if( config.pos > 0 ){
				    
					config.pos -= 1;
					
				} else if( config.loop && config ){
				    
					teleport( numElements );
					config.pos = numElements - 1;
					
				} else {
				    
					config.pos = numElements - ( 1 * config.numVisible );
					
				}
				
				makeSelected( config.pos );
				moveTray();
				
			}
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ MOVE TO NEXT -------------------------------------------------------- */
		var moveToNext = function(){
		
			var limit;
			
			if( !moving ){
				
				moving = true;
				
				if( config.loop ){
				    
					limit = numElements;
					
				} else {
				    
					limit = numElements - config.numVisible;
					
				}
				
				if( config.pos < limit ){
				    
					config.pos += 1;
					
				} else {
				    
					config.pos = 0;
					
				}
				
				//In a case of loop, to have the right element selected when going back to the first...
				var target = config.pos;
				
				if( target === numElements ){
				    
					target = 0;
					
				}
				
				makeSelected( target );
				moveTray();
			
			}
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ MOVE TO SPECIFIC ----------------------------------------------------- */
		var moveToSpecific = function(){
			
			if( !moving ){
				
				resetTimeout();
				
				moving = true;
				
				var target = $( this ).attr( 'rel' ) - 1;
				
				makeSelected(target);
				
				if( target > numElements - ( 1 * config.numVisible ) ){
				    
					target = numElements - ( 1 * config.numVisible );
					
				}
				
				config.pos = target;
								
				moveTray();
				
			}
			
			return false;
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ AUTOPLAY ------------------------------------------------------------ */
		var autoplay = function(){
			
			if( config.autoplay === true ){
			    
				timeout = setTimeout( moveToNext, config.delay );
				
			}
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ RESET TIMEOUT ---------------------------------------------------- */
		var resetTimeout = function(){
			
			if( config.autoplay === true ){
			    
				clearTimeout( timeout );
				
				if( config.continuous === false ){
				    
					config.autoplay = false;
					
				}
				
			}
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ MAKE SELECTED ----------------------------------------------------- */
		var makeSelected = function( target ){
			
			$( config.linkset ).removeClass( 'lemSelected' );
			
			if( config.linkset !== '' ){
			    
				$( config.linkset ).eq( target ).addClass( 'lemSelected' );
				
			}
			
		};
		/* --------------------------------------------------------------------------------------- */
		
		/* ------ SET CURRENTS ------------------------------------------------------- */
		var setCurrents = function(){
		
			if( config.current !== '' ) {
				
				$( config.current ).html( config.pos + 1 );
				
			}
			
			if( config.imageTitle !== '' ){
				
				var newTitle = null;
				
				if( config.group === 1 ){
				    
					newTitle = $( tray ).children().eq( config.pos ).attr( 'title' );
					
				} else {
				    
					newTitle = $( tray ).children().eq( config.pos ).children().eq( 0 ).attr( 'title' );
					
				}
				
				if( newTitle !== null ){
				    
					$( config.imageTitle ).html( newTitle );
					
				}
			}
			
			if( config.imageAlt !== '' ){
				
				var newAlt = null;
				
				if( config.group === 1 ){
				    
					newAlt = $( tray ).children().eq( config.pos ).attr( 'alt' );
					
				} else {
				    
					newAlt = $( tray ).children().eq( config.pos ).children().eq( 0 ).attr( 'alt' );
					
				}
				
				if( newAlt !== null ){
				    
					$( config.imageAlt ).html( newAlt );
					
				}
				
			}
		
		}
		/* --------------------------------------------------------------------------------------- */
		
		
		init();
		writeTotal();
		manipulateElements();
		manipulateHolders();
		manipulateTray();
		setOpposite();
		autoplay();
		
		return this;
		
	};
	
})(jQuery);

