/***********************************************************************
*
* Coda Slider 3
* Kevin Batdorf
*
* http://kevinbatdorf.github.com/codaslider
*
* GPL license & MIT license
*
************************************************************************/
// Utility for creating objects in older browsers
if ( typeof Object.create !== 'function' ) {
Object.create = function( obj ) {
function F() {}
F.prototype = obj;
return new F();
};
}
(function( $, window, document, undefined ) {
var Slider = {
//initialize
init: function( options, elem ) {
var self = this;
//remove no JavaScript warning
$("body").removeClass("coda-slider-no-js");
//add preloader class (backwards compatible)
$('.coda-slider').prepend('
Loading...
');
// Cache the element
self.elem = elem;
self.$elem = $( elem );
// Cache the ID and class. This allows for multiple instances with any ID name supplied
self.sliderId = '#' + ( self.$elem ).attr('id');
// Set the options
self.options = $.extend( {}, $.fn.codaSlider.options, options );
// Cache the ID and class. This allows for multiple instances with any ID name supplied
self.sliderId = '#' + ( self.$elem ).attr('id');
// Build the tabs and navigation
self.build();
// Start auto slider
if (self.options.autoSlide) {self.autoSlide();}
self.events();
// Test the preloader (image doesn't load)
//alert("Testing preloader");
// Kill the preloader
$("p.loading").remove();
},
build: function() {
var self = this;
// Wrap the entire slider (backwards compatible)
if ( $(self.sliderId).parent().attr('class') != 'coda-slider-wrapper' ) {$(self.sliderId).wrap(''); }
// Add the .panel class to the individual panels (backwards compatable)
$(self.sliderId + " > div").addClass('panel');
self.panelClass = self.sliderId + ' .panel';
// Wrap all panels in a div, and wrap inner content in a div (backwards compatible)
$(self.panelClass).wrapAll('');
if ( $(self.panelClass).children().attr('class') != 'panel-wrapper' ) { $(self.panelClass).wrapInner(''); }
self.panelContainer = ($(self.panelClass).parent());
// Store hash Links
if (self.options.hashLinking) {
self.hash = (window.location.hash);
self.hashPanel = (self.hash).replace('#', '');
}
// Store current tab
self.currentTab = (self.options.hashLinking && self.hash) ? self.hashPanel - 1 : self.options.firstPanelToLoad - 1;
// Apply starting height to the container
if (self.options.autoHeight) { $(self.sliderId).css('height', $($(self.panelContainer).children()[self.currentTab]).height() + $(self.sliderId + '-wrapper .coda-nav-right').height()); }
// Build navigation tabs
if (self.options.dynamicTabs) { self.addNavigation(); }
// Build navigation arrows
if (self.options.dynamicArrows) { self.addArrows(); }
// Create a container width to allow for a smooth float right.
self.totalSliderWidth = $(self.sliderId).outerWidth(true) + $($(self.sliderId).parent()).children('[class^=coda-nav-left]').outerWidth(true) + $($(self.sliderId).parent()).children('[class^=coda-nav-right]').outerWidth(true);
$($(self.sliderId).parent()).css('width', self.totalSliderWidth);
// Align navigation tabs
if (self.options.dynamicTabs) { self.alignNavigation(); }
// Clone panels if continuous is enabled
if (self.options.continuous) {
$(self.panelContainer).prepend($(self.panelContainer).children().last().clone());
$(self.panelContainer).append($(self.panelContainer).children().eq(1).clone());
}
// Allow the slider to be clicked
self.clickable = true;
// Count the number of panels and get the combined width
self.panelCount = $(self.panelClass).length;
self.panelWidth = $(self.panelClass).outerWidth();
self.totalWidth = self.panelCount * self.panelWidth;
// Variable for the % sign if needed (responsive), otherwise px
self.pSign = 'px';
self.slideWidth = $(self.sliderId).width();
// Puts the margin at the starting point with no animation. Made for both continuous and firstPanelToLoad features.
// ~~(self.options.continuous) will equal 1 if true, otherwise 0
$(self.panelContainer).css('margin-left', ( -self.slideWidth * ~~(self.options.continuous)) + (-self.slideWidth * self.currentTab) );
// Configure the current tab
self.setCurrent(self.currentTab);
// Apply the width to the panel container
$(self.sliderId + ' .panel-container').css('width', self.totalWidth);
},
addNavigation: function(){
var self = this;
// The id is assigned here to allow for the responsive setting
var dynamicTabs = '';
// Add basic frame
if (self.options.dynamicTabsPosition === 'bottom') { $(self.sliderId).after(dynamicTabs); }
else{ $(self.sliderId).before(dynamicTabs); }
// Add labels
$.each(
(self.$elem).find(self.options.panelTitleSelector), function(n) {
$($(self.sliderId).parent()).find('.coda-nav ul').append('' + $(this).text() + '');
}
);
},
alignNavigation: function() {
var self = this;
self.totalNavWidth = 0;
var arrow = '';
if (self.options.dynamicArrowsGraphical) {arrow = '-arrow';}
// Set the alignment
if (self.options.dynamicTabsAlign != 'center') {
$($(self.sliderId).parent()).find('.coda-nav ul').css(
'margin-' + self.options.dynamicTabsAlign,
// Finds the width of the arrows and the margin
$($(self.sliderId).parent()).find(
'.coda-nav-' +
self.options.dynamicTabsAlign +
arrow
).outerWidth(true) + parseInt($(self.sliderId).css('margin-'+ self.options.dynamicTabsAlign), 10)
);
$($(self.sliderId).parent()).find('.coda-nav ul').css('float', self.options.dynamicTabsAlign); // couldn't combine this .css() with the previous??
}
else {
// Get total width of the navigation tabs and center it
$($(self.sliderId).parent()).find('.coda-nav li a').each(function(){self.totalNavWidth += $(this).outerWidth(true); });
$($(self.sliderId).parent()).find('.coda-nav ul').css('width', self.totalNavWidth + 1);
}
},
addArrows: function(){
var self = this;
$(self.sliderId).parent().addClass("arrows");
if(self.options.dynamicArrowsGraphical){
$(self.sliderId).before('');
$(self.sliderId).after('');
}
else{
$(self.sliderId).before('');
$(self.sliderId).after('');
}
},
events: function(){
var self = this;
// CLick arrows
$($(self.sliderId).parent()).find('[class^=coda-nav-]').on('click', function(e){
// These prevent clicking when in continuous mode, which would break it otherwise.
if (!self.clickable && self.options.continuous) {return false;}
self.setCurrent($(this).attr('class').split('-')[2]);
if (self.options.continuous) {self.clickable = false;}
return false;
});
// Click tabs
$($(self.sliderId).parent()).find('[class^=coda-nav] li').on('click', function(e){
if (!self.clickable && self.options.continuous) {return false;}
self.setCurrent(parseInt( $(this).attr('class').split('tab')[1], 10) - 1 );
if (self.options.continuous) {self.clickable = false;}
return false;
});
// Click cross links
$('[data-ref*=' + (self.sliderId).split('#')[1] + ']').on('click', function(e){
if (!self.clickable && self.options.continuous) {return false;}
// Stop and Play controls
if (self.options.autoSlideControls) {
if ($(this).attr('name') === 'stop') {
$(this).html(self.options.autoSlideStartText).attr('name', 'start');
clearTimeout(self.autoslideTimeout);
return false;
}
if ($(this).attr('name') === 'start') {
$(this).html(self.options.autoSlideStopText).attr('name', 'stop');
self.setCurrent(self.currentTab + 1);
self.autoSlide();
return false;
}
}
self.setCurrent( parseInt( $(this).attr('href').split('#')[1] -1, 10 ) );
if (self.options.continuous) {self.clickable = false;}
if (self.options.autoSlideStopWhenClicked) { clearTimeout(self.autoslideTimeout); }
return false;
});
// Click to stop autoslider
$($(self.sliderId).parent()).find('*').on('click', function(e){
// AutoSlide controls.
if (self.options.autoSlideControls && autoSlideStopWhenClicked) {
$('body').find('[data-ref*=' + (self.sliderId).split('#')[1] + '][name=stop]').html(self.options.autoSlideStartText);
clearTimeout(self.autoslideTimeout);
}
if (!self.clickable && self.options.continuous) {
if (self.options.autoSlideStopWhenClicked) { clearTimeout(self.autoslideTimeout); }
return false;
}
if (self.options.autoSlide) {
// Clear the timeout
if (self.options.autoSlideStopWhenClicked) { clearTimeout(self.autoslideTimeout); }
else {
self.autoSlide(clearTimeout(self.autoslideTimeout));
self.clickable = true;
}
}
// Stops from speedy clicking for continuous sliding.
if (self.options.continuous) {clearTimeout(self.continuousTimeout);}
});
},
setCurrent: function( direction ){
var self = this;
if (self.clickable) {
if (typeof direction == 'number') { self.currentTab = direction; }
else {
// "left" = -1; "right" = 1;
self.currentTab += ( ~~( direction === 'right' ) || -1 );
// If not continuous, slide back at the last or first panel
if (!self.options.continuous){
self.currentTab = (self.currentTab < 0) ? this.panelCount - 1 : (self.currentTab % this.panelCount);
}
}
// This is so the height will match the current panel, ignoring the clones.
// It also adjusts the count for the "currrent" class that's applied
if (self.options.continuous) {
self.panelHeightCount = self.currentTab + 1;
if (self.currentTab === self.panelCount - 2){self.setTab = 0;}
else if (self.currentTab === -1) {self.setTab = self.panelCount - 3;}
else {self.setTab = self.currentTab;}
}
else{
self.panelHeightCount = self.currentTab;
self.setTab = self.currentTab;
}
// Add and remove current class.
$($(self.sliderId).parent()).find('.tab' + (self.setTab + 1) + ' a:first')
.addClass('current')
.parent().siblings().children().removeClass('current');
// Update Hash Tags
if (self.options.hashLinking) {
//console.log( ((self.$elem).find(self.options.hashTitleSelector)[self.currentTab] ));
if (self.options.continuous) {
if (self.currentTab === self.panelCount - 2) {
window.location.hash = 1;
} else if (self.currentTab === -1) {
window.location.hash = self.panelCount - 2;
} else {
window.location.hash = self.currentTab + 1;
}
} else { window.location.hash = self.currentTab + 1; }
}
this.transition();
}
},
transition: function(){
var self = this;
// Adjust the height
if (self.options.autoHeight) {
$(self.panelContainer).parent().animate({
'height': $($(self.panelContainer).children()[self.panelHeightCount]).height()
}, {
easing: self.options.autoHeightEaseFunction,
duration: self.options.autoHeightEaseDuration,
queue: false
});
}
// Adjust the margin for continuous sliding
if (self.options.continuous) {self.marginLeft = -(self.currentTab * self.slideWidth ) - self.slideWidth;}
// Otherwise adjust as normal
else {self.marginLeft = -(self.currentTab * self.slideWidth ); }
// Animate the slider
(self.panelContainer).animate({
'margin-left': self.marginLeft + self.pSign
}, {
easing: self.options.slideEaseFunction,
duration: self.options.slideEaseDuration,
queue: false,
complete: self.continuousSlide(self.options.slideEaseDuration + 50)
});
},
autoSlide: function(){
var self = this;
// Can't set the autoslide slower than the easing ;-)
if (self.options.autoSlideInterval < self.options.slideEaseDuration) {
self.options.autoSlideInterval = (self.options.slideEaseDuration > self.options.autoHeightEaseDuration) ? self.options.slideEaseDuration : self.options.autoHeightEaseDuration;
}
if (self.options.continuous) {self.clickable = false;}
self.autoslideTimeout = setTimeout(function() {
// Slide left or right
self.setCurrent( self.options.autoSliderDirection );
self.autoSlide();
}, self.options.autoSlideInterval);
},
continuousSlide: function (delay){
var self = this;
if (self.options.continuous) {
self.continuousTimeout = setTimeout(function() {
// If on the last panel (the clone of panel 1), set the margin to the original.
if (self.currentTab === self.panelCount - 2){
$(self.panelContainer).css('margin-left', -self.slideWidth + self.pSign);
self.currentTab = 0;
}
// If on the first panel the clone of the last panel), set the margin to the original.
else if (self.currentTab === -1){
$(self.panelContainer).css('margin-left', -( ((self.slideWidth * self.panelCount) - (self.slideWidth * 2))) + self.pSign );
self.currentTab = (self.panelCount - 3);
}
self.clickable = true;
}, delay);
}
else{self.clickable = true;}
}
};
$.fn.codaSlider = function( options ) {
return this.each(function() {
var slider = Object.create( Slider );
slider.init( options, this );
});
};
$.fn.codaSlider.options = {
autoHeight: true,
autoHeightEaseDuration: 1500,
autoHeightEaseFunction: "easeInOutExpo",
autoSlide: false,
autoSliderDirection: 'right',
autoSlideInterval: 7000,
autoSlideControls: false,
autoSlideStartText: 'Start',
autoSlideStopText: 'Stop',
autoSlideStopWhenClicked: true,
continuous: true,
crossLinking: true, // No longer used
dynamicArrows: true,
dynamicArrowsGraphical: false,
dynamicArrowLeftText: "« left",
dynamicArrowRightText: "right »",
dynamicTabs: true,
dynamicTabsAlign: "center",
dynamicTabsPosition: "top",
externalTriggerSelector: "a.xtrig", //shouldnt need any more
firstPanelToLoad: 1,
hashLinking: false,
panelTitleSelector: "h2.title",
slideEaseDuration: 1500,
slideEaseFunction: "easeInOutExpo"
};
})( jQuery, window, document );