text: (function( window, $, undefined ) {
  
  	// http://www.netcu.de/jquery-touchwipe-iphone-ipad-library
  	$.fn.touchwipe 				= function(settings) {
  
  		var config = {
  			min_move_x: 20,
  			min_move_y: 20,
  			wipeLeft: function() { },
  			wipeRight: function() { },
  			wipeUp: function() { },
  			wipeDown: function() { },
  			preventDefaultEvents: true
  		};
  
  		if (settings) $.extend(config, settings);
  
  		this.each(function() {
  			var startX;
  			var startY;
  			var isMoving = false;
  
  			function cancelTouch() {
  				this.removeEventListener('touchmove', onTouchMove);
  				startX = null;
  				isMoving = false;
  			}
  
  			function onTouchMove(e) {
  				if(config.preventDefaultEvents) {
  					e.preventDefault();
  				}
  				if(isMoving) {
  					var x = e.touches[0].pageX;
  					var y = e.touches[0].pageY;
  					var dx = startX - x;
  					var dy = startY - y;
  					if(Math.abs(dx) >= config.min_move_x) {
  						cancelTouch();
  						if(dx > 0) {
  							config.wipeLeft();
  						}
  						else {
  							config.wipeRight();
  						}
  					}
  					else if(Math.abs(dy) >= config.min_move_y) {
  						cancelTouch();
  						if(dy > 0) {
  							config.wipeDown();
  						}
  						else {
  							config.wipeUp();
  						}
  					}
  				}
  			}
  
  			function onTouchStart(e)
  			{
  				if (e.touches.length == 1) {
  					startX = e.touches[0].pageX;
  					startY = e.touches[0].pageY;
  					isMoving = true;
  					this.addEventListener('touchmove', onTouchMove, false);
  				}
  			}
  			if ('ontouchstart' in document.documentElement) {
  				this.addEventListener('touchstart', onTouchStart, false);
  			}
  		});
  
  		return this;
  	};
  
  	$.elastislide 				= function( options, element ) {
  		this.$el	= $( element );
  		this._init( options );
  	};
  
  	$.elastislide.defaults 		= {
  		speed		: 450,	// animation speed
  		easing		: '',	// animation easing effect
  		imageW		: 190,	// the images width
  		margin		: 3,	// image margin right
  		border		: 2,	// image border
  		minItems	: 1,	// the minimum number of items to show.
  							// when we resize the window, this will make sure minItems are always shown
  							// (unless of course minItems is higher than the total number of elements)
  		current		: 0,	// index of the current item
  							// when we resize the window, the carousel will make sure this item is visible
  		onClick		: function() { return false; } // click item callback
      };
  
  	$.elastislide.prototype 	= {
  		_init 				: function( options ) {
  
  			this.options 		= $.extend( true, {}, $.elastislide.defaults, options );
  
  			// <ul>
  			this.$slider		= this.$el.find('ul');
  
  			// <li>
  			this.$items			= this.$slider.children('li');
  
  			// total number of elements / images
  			this.itemsCount		= this.$items.length;
  
  			// cache the <ul>'s parent, since we will eventually need to recalculate its width on window resize
  			this.$esCarousel	= this.$slider.parent();
  
  			// validate options
  			this._validateOptions();
  
  			// set sizes and initialize some vars...
  			this._configure();
  
  			// add navigation buttons
  			this._addControls();
  
  			// initialize the events
  			this._initEvents();
  
  			// show the <ul>
  			this.$slider.show();
  
  			// slide to current's position
  			this._slideToCurrent( false );
  
  		},
  		_validateOptions	: function() {
  
  			if( this.options.speed < 0 )
  				this.options.speed = 450;
  			if( this.options.margin < 0 )
  				this.options.margin = 4;
  			if( this.options.border < 0 )
  				this.options.border = 1;
  			if( this.options.minItems < 1 || this.options.minItems > this.itemsCount )
  				this.options.minItems = 1;
  			if( this.options.current > this.itemsCount - 1 )
  				this.options.current = 0;
  
  		},
  		_configure			: function() {
  
  			// current item's index
  			this.current		= this.options.current;
  
  			// the ul's parent's (div.es-carousel) width is the "visible" width
  			this.visibleWidth	= this.$esCarousel.width();
  
  			// test to see if we need to initially resize the items
  			if( this.visibleWidth < this.options.minItems * ( this.options.imageW + 2 * this.options.border ) + ( this.options.minItems - 1 ) * this.options.margin ) {
  				this._setDim( ( this.visibleWidth - ( this.options.minItems - 1 ) * this.options.margin ) / this.options.minItems );
  				this._setCurrentValues();
  				// how many items fit with the current width
  				this.fitCount	= this.options.minItems;
  			}
  			else {
  				this._setDim();
  				this._setCurrentValues();
  			}
  
  			// set the <ul> width
  			this.$slider.css({
  				width	: this.sliderW
  			});
  
  		},
  		_setDim				: function( elW ) {
  
  			// <li> style
  			this.$items.css({
  				marginRight	: this.options.margin,
  				width		: ( elW ) ? elW : this.options.imageW + 2 * this.options.border
  			}).children('a').css({ // <a> style
  				borderWidth		: this.options.border
  			});
  
  		},
  		_setCurrentValues	: function() {
  
  			// the total space occupied by one item
  			this.itemW			= this.$items.outerWidth(true);
  
  			// total width of the slider / <ul>
  			// this will eventually change on window resize
  			this.sliderW		= this.itemW * this.itemsCount;
  
  			// the ul parent's (div.es-carousel) width is the "visible" width
  			this.visibleWidth	= this.$esCarousel.width();
  
  			// how many items fit with the current width
  			this.fitCount		= Math.floor( this.visibleWidth / this.itemW );
  
  		},
  		_addControls		: function() {
  
  			this.$navNext	= $('<span class="es-nav-next">Next</span>');
  			this.$navPrev	= $('<span class="es-nav-prev">Previous</span>');
  			$('<div class="es-nav"/>')
  			.append( this.$navPrev )
  			.append( this.$navNext )
  			.appendTo( this.$el );
  
  			//this._toggleControls();
  
  		},
  		_toggleControls		: function( dir, status ) {
  
  			// show / hide navigation buttons
  			if( dir && status ) {
  				if( status === 1 )
  					( dir === 'right' ) ? this.$navNext.show() : this.$navPrev.show();
  				else
  					( dir === 'right' ) ? this.$navNext.hide() : this.$navPrev.hide();
  			}
  			else if( this.current === this.itemsCount - 1 || this.fitCount >= this.itemsCount )
  					this.$navNext.hide();
  
  		},
  		_initEvents			: function() {
  
  			var instance	= this;
  
  			// window resize
  			$(window).bind('resize.elastislide', function( event ) {
  
  				instance._reload();
  
  				// slide to the current element
  				clearTimeout( instance.resetTimeout );
  				instance.resetTimeout	= setTimeout(function() {
  					instance._slideToCurrent();
  				}, 200);
  
  			});
  
  			// navigation buttons events
  			this.$navNext.bind('click.elastislide', function( event ) {
  				instance._slide('right');
  			});
  
  			this.$navPrev.bind('click.elastislide', function( event ) {
  				instance._slide('left');
  			});
  
  			// item click event
  			this.$items.bind('click.elastislide', function( event ) {
  				instance.options.onClick( $(this) );
  				return false;
  			});
  
  			// touch events
  			instance.$slider.touchwipe({
  				wipeLeft			: function() {
  					instance._slide('right');
  				},
  				wipeRight			: function() {
  					instance._slide('left');
  				}
  			});
  
  		},
  		reload				: function( callback ) {
  			this._reload();
  			if ( callback ) callback.call();
  
  		},
  		_reload				: function() {
  
  			var instance	= this;
  
  			// set values again
  			instance._setCurrentValues();
  
  			// need to resize items
  			if( instance.visibleWidth < instance.options.minItems * ( instance.options.imageW + 2 * instance.options.border ) + ( instance.options.minItems - 1 ) * instance.options.margin ) {
  				instance._setDim( ( instance.visibleWidth - ( instance.options.minItems - 1 ) * instance.options.margin ) / instance.options.minItems );
  				instance._setCurrentValues();
  				instance.fitCount	= instance.options.minItems;
  			}
  			else{
  				instance._setDim();
  				instance._setCurrentValues();
  			}
  
  			instance.$slider.css({
  				width	: instance.sliderW + 10 // TODO: +10px seems to solve a firefox "bug" :S
  			});
  
  		},
  		_slide				: function( dir, val, anim, callback ) {
  
  			// if animating return
  			//if( this.$slider.is(':animated') )
  				//return false;
  
  			// current margin left
  			var ml		= parseFloat( this.$slider.css('margin-left') );
  
  			// val is just passed when we want an exact value for the margin left (used in the _slideToCurrent function)
  			if( val === undefined ) {
  
  				// how much to slide?
  				var amount	= this.fitCount * this.itemW, val;
  
  				if( amount < 0 ) return false;
  
  				// make sure not to leave a space between the last item / first item and the end / beggining of the slider available width
  				if( dir === 'right' && this.sliderW - ( Math.abs( ml ) + amount ) < this.visibleWidth ) {
  					amount	= this.sliderW - ( Math.abs( ml ) + this.visibleWidth ) - this.options.margin; // decrease the margin left
  					// show / hide navigation buttons
  					this._toggleControls( 'right', -1 );
  					this._toggleControls( 'left', 1 );
  				}
  				else if( dir === 'left' && Math.abs( ml ) - amount < 0 ) {
  					amount	= Math.abs( ml );
  					// show / hide navigation buttons
  					this._toggleControls( 'left', -1 );
  					this._toggleControls( 'right', 1 );
  				}
  				else {
  					var fml; // future margin left
  					( dir === 'right' )
  						? fml = Math.abs( ml ) + this.options.margin + Math.abs( amount )
  						: fml = Math.abs( ml ) - this.options.margin - Math.abs( amount );
  
  					// show / hide navigation buttons
  					if( fml > 0 )
  						this._toggleControls( 'left', 1 );
  					else
  						this._toggleControls( 'left', -1 );
  
  					if( fml < this.sliderW - this.visibleWidth )
  						this._toggleControls( 'right', 1 );
  					else
  						this._toggleControls( 'right', -1 );
  
  				}
  
  				( dir === 'right' ) ? val = '-=' + amount : val = '+=' + amount
  
  			}
  			else {
  				var fml		= Math.abs( val ); // future margin left
  
  				if( Math.max( this.sliderW, this.visibleWidth ) - fml < this.visibleWidth ) {
  					val	= - ( Math.max( this.sliderW, this.visibleWidth ) - this.visibleWidth );
  					if( val !== 0 )
  						val += this.options.margin;	// decrease the margin left if not on the first position
  
  					// show / hide navigation buttons
  					this._toggleControls( 'right', -1 );
  					fml	= Math.abs( val );
  				}
  
  				// show / hide navigation buttons
  				if( fml > 0 )
  					this._toggleControls( 'left', 1 );
  				else
  					this._toggleControls( 'left', -1 );
  
  				if( Math.max( this.sliderW, this.visibleWidth ) - this.visibleWidth > fml + this.options.margin )
  					this._toggleControls( 'right', 1 );
  				else
  					this._toggleControls( 'right', -1 );
  
  			}
  
  			$.fn.applyStyle = ( anim === undefined ) ? $.fn.animate : $.fn.css;
  
  			var sliderCSS	= { marginLeft : val };
  
  			var instance	= this;
  
  			this.$slider.stop().applyStyle( sliderCSS, $.extend( true, [], { duration : this.options.speed, easing : this.options.easing, complete : function() {
  				if( callback ) callback.call();
  			} } ) );
  
  		},
  		_slideToCurrent		: function( anim ) {
  
  			// how much to slide?
  			var amount	= this.current * this.itemW;
  			this._slide('', -amount, anim );
  
  		},
  		add					: function( $newelems, callback ) {
  
  			// adds new items to the carousel
  			this.$items 		= this.$items.add( $newelems );
  			this.itemsCount		= this.$items.length;
  			this._setDim();
  			this._setCurrentValues();
  			this.$slider.css({
  				width	: this.sliderW
  			});
  			this._slideToCurrent();
  
  			if ( callback ) callback.call( $newelems );
  
  		},
  		setCurrent			: function( idx, callback ) {
  
  			this.current = idx;
  
  			var ml		= Math.abs( parseFloat( this.$slider.css('margin-left') ) ),
  				posR	= ml + this.visibleWidth,
  				fml		= Math.abs( this.current * this.itemW );
  
  			if( fml + this.itemW > posR || fml < ml ) {
  				this._slideToCurrent();
  			}
  
  			if ( callback ) callback.call();
  
  		},
  		destroy				: function( callback ) {
  
  			this._destroy( callback );
  
  		},
  		_destroy 			: function( callback ) {
  			this.$el.unbind('.elastislide').removeData('elastislide');
  			$(window).unbind('.elastislide');
  			if ( callback ) callback.call();
  		}
  	};
  
  	var logError 				= function( message ) {
  		if ( this.console ) {
  			console.error( message );
  		}
  	};
  
  	$.fn.elastislide 				= function( options ) {
  		if ( typeof options === 'string' ) {
  			var args = Array.prototype.slice.call( arguments, 1 );
  
  			this.each(function() {
  				var instance = $.data( this, 'elastislide' );
  				if ( !instance ) {
  					logError( "cannot call methods on elastislide prior to initialization; " +
  					"attempted to call method '" + options + "'" );
  					return;
  				}
  				if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
  					logError( "no such method '" + options + "' for elastislide instance" );
  					return;
  				}
  				instance[ options ].apply( instance, args );
  			});
  		}
  		else {
  			this.each(function() {
  				var instance = $.data( this, 'elastislide' );
  				if ( !instance ) {
  					$.data( this, 'elastislide', new $.elastislide( options, this ) );
  				}
  			});
  		}
  		return this;
  	};
  
  })( window, jQuery );