(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 : 30, // image margin right
border : 0, // 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 );
//
this.$slider = this.$el.find('ul');
// -
this.$items = this.$slider.children('li');
// total number of elements / images
this.itemscount = this.$items.length;
// cache the
'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
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 width
this.$slider.css({
width : this.sliderw
});
},
_setdim : function( elw ) {
// - style
this.$items.css({
marginright : this.options.margin,
width : ( elw ) ? elw : this.options.imagew + 2 * this.options.border
}).children('a').css({ // 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 /
// 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 = $('next');
this.$navprev = $('previous');
$('')
.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.css("opacity", "1") : this.$navprev.css("opacity", "1");
else
( dir === 'right' ) ? this.$navnext.css("opacity", "0.5") : this.$navprev.css("opacity", "0.5");
}
else if( this.current === this.itemscount - 1 || this.fitcount >= this.itemscount )
this.$navnext.css("opacity", "0.5");
},
_initevents : function() {
var instance = this;
// window resize
$(window).bind('resize.elastislide', function( event ) {
// 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 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; //eazzy fix / do links
});
// touch events
instance.$slider.touchwipe({
wipeleft : function() {
instance._slide('right');
},
wiperight : function() {
instance._slide('left');
}
});
},
_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.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 );
},
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 );