/** * jQuery bxSliderVD v3.0 * http://bxSliderVD.com * * Copyright 2011, Steven Wanderski * http://bxcreative.com * * Free to use and abuse under the MIT license. * http://www.opensource.org/licenses/mit-license.php * */ (function($){ $.fn.bxSliderVD = function(options){ var defaults = { mode: 'horizontal', // 'horizontal', 'vertical', 'fade' childSelector: '', // jQuery selector - elements to be used as slides infiniteLoop: false, // true, false - display first slide after last hideControlOnEnd: true, // true, false - if true, will hide 'next' control on last slide and 'prev' control on first controls: true, // true, false - previous and next controls speed: 500, // integer - in ms, duration of time slide transitions will occupy easing: 'swing', // used with jquery.easing.1.3.js - see http://gsgd.co.uk/sandbox/jquery/easing/ for available options pager: false, // true / false - display a pager pagerSelector: null, // jQuery selector - element to contain the pager. ex: '#pager' pagerType: 'full', // 'full', 'short' - if 'full' pager displays 1,2,3... if 'short' pager displays 1 / 4 pagerLocation: 'bottom', // 'bottom', 'top' - location of pager pagerShortSeparator: '/', // string - ex: 'of' pager would display 1 of 4 pagerActiveClass: 'pager-active', // string - classname attached to the active pager link nextText: 'next', // string - text displayed for 'next' control nextImage: '', // string - filepath of image used for 'next' control. ex: 'images/next.jpg' nextSelector: null, // jQuery selector - element to contain the next control. ex: '#next' prevText: 'prev', // string - text displayed for 'previous' control prevImage: '', // string - filepath of image used for 'previous' control. ex: 'images/prev.jpg' prevSelector: null, // jQuery selector - element to contain the previous control. ex: '#next' captions: false, // true, false - display image captions (reads the image 'title' tag) captionsSelector: null, // jQuery selector - element to contain the captions. ex: '#captions' auto: false, // true, false - make slideshow change automatically autoDirection: 'next', // 'next', 'prev' - direction in which auto show will traverse autoControls: false, // true, false - show 'start' and 'stop' controls for auto show autoControlsSelector: null, // jQuery selector - element to contain the auto controls. ex: '#auto-controls' autoStart: true, // true, false - if false show will wait for 'start' control to activate autoHover: false, // true, false - if true show will pause on mouseover autoDelay: 0, // integer - in ms, the amount of time before starting the auto show pause: 3000, // integer - in ms, the duration between each slide transition startText: 'start', // string - text displayed for 'start' control startImage: '', // string - filepath of image used for 'start' control. ex: 'images/start.jpg' stopText: 'stop', // string - text displayed for 'stop' control stopImage: '', // string - filepath of image used for 'stop' control. ex: 'images/stop.jpg' ticker: false, // true, false - continuous motion ticker mode (think news ticker) // note: autoControls, autoControlsSelector, and autoHover apply to ticker! tickerSpeed: 5000, // float - use value between 1 and 5000 to determine ticker speed - the smaller the value the faster the ticker speed tickerDirection: 'next', // 'next', 'prev' - direction in which ticker show will traverse tickerHover: false, // true, false - if true ticker will pause on mouseover wrapperClass: 'bx-wrap', // string - classname attached to the slider wraper startingSlide: 0, // integer - show will start on specified slide. note: slides are zero based! displaySlideQty: 3, // integer - number of slides to display at once moveSlideQty: 3, // integer - number of slides to move at once randomStart: false, // true, false - if true show will start on a random slide onBeforeSlide: function(){}, // function(currentSlideNumber, totalSlideQty, currentSlideHtmlObject) - advanced use only! see the tutorial here: http://bxSliderVD.com/custom-pager onAfterSlide: function(){}, // function(currentSlideNumber, totalSlideQty, currentSlideHtmlObject) - advanced use only! see the tutorial here: http://bxSliderVD.com/custom-pager onLastSlide: function(){}, // function(currentSlideNumber, totalSlideQty, currentSlideHtmlObject) - advanced use only! see the tutorial here: http://bxSliderVD.com/custom-pager onFirstSlide: function(){}, // function(currentSlideNumber, totalSlideQty, currentSlideHtmlObject) - advanced use only! see the tutorial here: http://bxSliderVD.com/custom-pager onNextSlide: function(){}, // function(currentSlideNumber, totalSlideQty, currentSlideHtmlObject) - advanced use only! see the tutorial here: http://bxSliderVD.com/custom-pager onPrevSlide: function(){}, // function(currentSlideNumber, totalSlideQty, currentSlideHtmlObject) - advanced use only! see the tutorial here: http://bxSliderVD.com/custom-pager buildPager: null, // function(slideIndex, slideHtmlObject){ return string; } - advanced use only! see the tutorial here: http://bxSliderVD.com/custom-pager } var options = $.extend(defaults, options); // cache the base element var base = this; // initialize (and localize) all variables var $parent = ''; var $origElement = ''; var $children = ''; var $outerWrapper = ''; var $firstChild = ''; var childrenWidth = ''; var childrenOuterWidth = ''; var wrapperWidth = ''; var wrapperHeight = ''; var $pager = ''; var interval = ''; var $autoControls = ''; var $stopHtml = ''; var $startContent = ''; var $stopContent = ''; var autoPlaying = true; var loaded = false; var childrenMaxWidth = 0; var childrenMaxHeight = 0; var currentSlide = 0; var origLeft = 0; var origTop = 0; var origShowWidth = 0; var origShowHeight = 0; var tickerLeft = 0; var tickerTop = 0; var isWorking = false; var firstSlide = 0; var lastSlide = $children.length - 1; // PUBLIC FUNCTIONS /** * Go to specified slide */ this.goToSlide = function(number, stopAuto){ if(!isWorking){ isWorking = true; // set current slide to argument currentSlide = number; options.onBeforeSlide(currentSlide, $children.length, $children.eq(currentSlide)); // check if stopAuto argument is supplied if(typeof(stopAuto) == 'undefined'){ var stopAuto = true; } if(stopAuto){ // if show is auto playing, stop it if(options.auto){ base.stopShow(true); } } slide = number; // check for first slide callback if(slide == firstSlide){ options.onFirstSlide(currentSlide, $children.length, $children.eq(currentSlide)); } // check for last slide callback if(slide == lastSlide){ options.onLastSlide(currentSlide, $children.length, $children.eq(currentSlide)); } // horizontal if(options.mode == 'horizontal'){ $parent.animate({'left': '-'+getSlidePosition(slide, 'left')+'px'}, options.speed, options.easing, function(){ isWorking = false; // perform the callback function options.onAfterSlide(currentSlide, $children.length, $children.eq(currentSlide)); }); // vertical }else if(options.mode == 'vertical'){ $parent.animate({'top': '-'+getSlidePosition(slide, 'top')+'px'}, options.speed, options.easing, function(){ isWorking = false; // perform the callback function options.onAfterSlide(currentSlide, $children.length, $children.eq(currentSlide)); }); // fade }else if(options.mode == 'fade'){ setChildrenFade(); } // check to remove controls on last/first slide checkEndControls(); // accomodate multi slides if(options.moveSlideQty > 1){ number = Math.floor(number / options.moveSlideQty); } // make the current slide active makeSlideActive(number); // display the caption showCaptions(); } } /** * Go to next slide */ this.goToNextSlide = function(stopAuto){ // check if stopAuto argument is supplied if(typeof(stopAuto) == 'undefined'){ var stopAuto = true; } if(stopAuto){ // if show is auto playing, stop it if(options.auto){ base.stopShow(true); } } // makes slideshow finite if(!options.infiniteLoop){ if(!isWorking){ var slideLoop = false; // make current slide the old value plus moveSlideQty currentSlide = (currentSlide + (options.moveSlideQty)); // if current slide has looped on itself if(currentSlide <= lastSlide){ checkEndControls(); // next slide callback options.onNextSlide(currentSlide, $children.length, $children.eq(currentSlide)); // move to appropriate slide base.goToSlide(currentSlide); }else{ currentSlide -= options.moveSlideQty; } } // end if(!isWorking) }else{ if(!isWorking){ isWorking = true; var slideLoop = false; // make current slide the old value plus moveSlideQty currentSlide = (currentSlide + options.moveSlideQty); // if current slide has looped on itself if(currentSlide > lastSlide){ currentSlide = currentSlide % $children.length; slideLoop = true; } // next slide callback options.onNextSlide(currentSlide, $children.length, $children.eq(currentSlide)); // slide before callback options.onBeforeSlide(currentSlide, $children.length, $children.eq(currentSlide)); if(options.mode == 'horizontal'){ // get the new 'left' property for $parent var parentLeft = (options.moveSlideQty * childrenOuterWidth); // animate to the new 'left' $parent.animate({'left': '-='+parentLeft+'px'}, options.speed, options.easing, function(){ isWorking = false; // if its time to loop, reset the $parent if(slideLoop){ $parent.css('left', '-'+getSlidePosition(currentSlide, 'left')+'px'); } // perform the callback function options.onAfterSlide(currentSlide, $children.length, $children.eq(currentSlide)); }); }else if(options.mode == 'vertical'){ // get the new 'left' property for $parent var parentTop = (options.moveSlideQty * childrenMaxHeight); // animate to the new 'left' $parent.animate({'top': '-='+parentTop+'px'}, options.speed, options.easing, function(){ isWorking = false; // if its time to loop, reset the $parent if(slideLoop){ $parent.css('top', '-'+getSlidePosition(currentSlide, 'top')+'px'); } // perform the callback function options.onAfterSlide(currentSlide, $children.length, $children.eq(currentSlide)); }); }else if(options.mode == 'fade'){ setChildrenFade(); } // make the current slide active if(options.moveSlideQty > 1){ makeSlideActive(Math.ceil(currentSlide / options.moveSlideQty)); }else{ makeSlideActive(currentSlide); } // display the caption showCaptions(); } // end if(!isWorking) } } // end function /** * Go to previous slide */ this.goToPreviousSlide = function(stopAuto){ // check if stopAuto argument is supplied if(typeof(stopAuto) == 'undefined'){ var stopAuto = true; } if(stopAuto){ // if show is auto playing, stop it if(options.auto){ base.stopShow(true); } } // makes slideshow finite if(!options.infiniteLoop){ if(!isWorking){ var slideLoop = false; // make current slide the old value plus moveSlideQty currentSlide = currentSlide - options.moveSlideQty; // if current slide has looped on itself if(currentSlide < 0){ currentSlide = 0; // if specified, hide the control on the last slide if(options.hideControlOnEnd){ $outerWrapper.children('.bx-prev').hide(); } } checkEndControls(); // next slide callback options.onPrevSlide(currentSlide, $children.length, $children.eq(currentSlide)); // move to appropriate slide base.goToSlide(currentSlide); } }else{ if(!isWorking){ isWorking = true; var slideLoop = false; // make current slide the old value plus moveSlideQty currentSlide = (currentSlide - (options.moveSlideQty)); // if current slide has looped on itself if(currentSlide < 0){ negativeOffset = (currentSlide % $children.length); if(negativeOffset == 0){ currentSlide = 0; }else{ currentSlide = ($children.length) + negativeOffset; } slideLoop = true; } // next slide callback options.onPrevSlide(currentSlide, $children.length, $children.eq(currentSlide)); // slide before callback options.onBeforeSlide(currentSlide, $children.length, $children.eq(currentSlide)); if(options.mode == 'horizontal'){ // get the new 'left' property for $parent var parentLeft = (options.moveSlideQty * childrenOuterWidth); // animate to the new 'left' $parent.animate({'left': '+='+parentLeft+'px'}, options.speed, options.easing, function(){ isWorking = false; // if its time to loop, reset the $parent if(slideLoop){ $parent.css('left', '-'+getSlidePosition(currentSlide, 'left')+'px'); } // perform the callback function options.onAfterSlide(currentSlide, $children.length, $children.eq(currentSlide)); }); }else if(options.mode == 'vertical'){ // get the new 'left' property for $parent var parentTop = (options.moveSlideQty * childrenMaxHeight); // animate to the new 'left' $parent.animate({'top': '+='+parentTop+'px'}, options.speed, options.easing, function(){ isWorking = false; // if its time to loop, reset the $parent if(slideLoop){ $parent.css('top', '-'+getSlidePosition(currentSlide, 'top')+'px'); } // perform the callback function options.onAfterSlide(currentSlide, $children.length, $children.eq(currentSlide)); }); }else if(options.mode == 'fade'){ setChildrenFade(); } // make the current slide active if(options.moveSlideQty > 1){ makeSlideActive(Math.ceil(currentSlide / options.moveSlideQty)); }else{ makeSlideActive(currentSlide); } // display the caption showCaptions(); } // end if(!isWorking) } } // end function /** * Go to first slide */ this.goToFirstSlide = function(stopAuto){ // check if stopAuto argument is supplied if(typeof(stopAuto) == 'undefined'){ var stopAuto = true; } base.goToSlide(firstSlide, stopAuto); } /** * Go to last slide */ this.goToLastSlide = function(){ // check if stopAuto argument is supplied if(typeof(stopAuto) == 'undefined'){ var stopAuto = true; } base.goToSlide(lastSlide, stopAuto); } /** * Get the current slide */ this.getCurrentSlide = function(){ return currentSlide; } /** * Get the total slide count */ this.getSlideCount = function(){ return $children.length; } /** * Suspend the slideshow */ this.suspendShow = function(changeText){ clearInterval(interval); // check if changeText argument is supplied if(typeof(changeText) == 'undefined'){ var changeText = true; } if(changeText && options.autoControls){ $autoControls.html($startContent).removeClass('stop').addClass('start'); } } /** * Restart the suspended slideshow */ this.restartShow = function(changeText){ // check if changeText argument is supplied if(typeof(changeText) == 'undefined'){ var changeText = true; } if(autoPlaying){ setAutoInterval(); } if(changeText && options.autoControls){ $autoControls.html($stopContent).removeClass('start').addClass('stop'); } } /** * Stop the slideshow permanently */ this.stopShow = function(changeText){ autoPlaying = false; this.suspendShow(changeText); } /** * Start the slideshow */ this.startShow = function(changeText){ autoPlaying = true; this.restartShow(changeText); } /** * Stops the ticker */ this.stopTicker = function(changeText){ $parent.stop(); // check if changeText argument is supplied if(typeof(changeText) == 'undefined'){ var changeText = true; } if(changeText && options.ticker){ $autoControls.html($startContent).removeClass('stop').addClass('start'); autoPlaying = false; } } /** * Starts the ticker */ this.startTicker = function(changeText){ if(options.mode == 'horizontal'){ if(options.tickerDirection == 'next'){ // get the 'left' property where the ticker stopped var stoppedLeft = parseInt($parent.css('left')); // calculate the remaining distance the show must travel until the loop var remainingDistance = (origShowWidth + stoppedLeft) + $children.eq(0).width(); }else if(options.tickerDirection == 'prev'){ // get the 'left' property where the ticker stopped var stoppedLeft = -parseInt($parent.css('left')); // calculate the remaining distance the show must travel until the loop var remainingDistance = (stoppedLeft) - $children.eq(0).width(); } // calculate the speed ratio to seamlessly finish the loop var finishingSpeed = (remainingDistance * options.tickerSpeed) / origShowWidth; // call the show moveTheShow(tickerLeft, remainingDistance, finishingSpeed); }else if(options.mode == 'vertical'){ if(options.tickerDirection == 'next'){ // get the 'top' property where the ticker stopped var stoppedTop = parseInt($parent.css('top')); // calculate the remaining distance the show must travel until the loop var remainingDistance = (origShowHeight + stoppedTop) + $children.eq(0).height(); }else if(options.tickerDirection == 'prev'){ // get the 'left' property where the ticker stopped var stoppedTop = -parseInt($parent.css('top')); // calculate the remaining distance the show must travel until the loop var remainingDistance = (stoppedTop) - $children.eq(0).height(); } // calculate the speed ratio to seamlessly finish the loop var finishingSpeed = (remainingDistance * options.tickerSpeed) / origShowHeight; // call the show moveTheShow(tickerTop, remainingDistance, finishingSpeed); // check if changeText argument is supplied if(typeof(changeText) == 'undefined'){ var changeText = true; } if(changeText && options.ticker){ $autoControls.html($stopContent).removeClass('start').addClass('stop'); autoPlaying = true; } } } /** * Initialize a new slideshow */ this.initShow = function(){ // reinitialize all variables // base = this; $parent = $(this); $origElement = $parent.clone(); $children = $parent.children(options.childSelector); $outerWrapper = ''; $firstChild = $parent.children(options.childSelector + ':first'); childrenWidth = $firstChild.width(); childrenMaxWidth = 0; childrenOuterWidth = $firstChild.outerWidth(); childrenMaxHeight = 0; wrapperWidth = getWrapperWidth(); wrapperHeight = getWrapperHeight(); isWorking = false; $pager = ''; currentSlide = 0; origLeft = 0; origTop = 0; interval = ''; $autoControls = ''; $stopHtml = ''; $startContent = ''; $stopContent = ''; autoPlaying = true; loaded = false; origShowWidth = 0; origShowHeight = 0; tickerLeft = 0; tickerTop = 0; firstSlide = 0; lastSlide = $children.length - 1; // get the largest child's height and width $children.each(function(index) { if($(this).outerHeight() > childrenMaxHeight){ childrenMaxHeight = $(this).outerHeight(); } if($(this).outerWidth() > childrenMaxWidth){ childrenMaxWidth = $(this).outerWidth(); } }); // get random slide number if(options.randomStart){ var randomNumber = Math.floor(Math.random() * $children.length); currentSlide = randomNumber; origLeft = childrenOuterWidth * (options.moveSlideQty + randomNumber); origTop = childrenMaxHeight * (options.moveSlideQty + randomNumber); // start show at specific slide }else{ currentSlide = options.startingSlide; origLeft = childrenOuterWidth * (options.moveSlideQty + options.startingSlide); origTop = childrenMaxHeight * (options.moveSlideQty + options.startingSlide); } // set initial css initCss(); // check to show pager if(options.pager && !options.ticker){ if(options.pagerType == 'full'){ showPager('full'); }else if(options.pagerType == 'short'){ showPager('short'); } } // check to show controls if(options.controls && !options.ticker && $children.length > 1){ setControlsVars(); } // check if auto if(options.auto || options.ticker){ // check if auto controls are displayed if(options.autoControls){ setAutoControlsVars(); } // check if show should auto start if(options.autoStart){ // check if autostart should delay autoPlaying = false; // prevent playing during the deplay setTimeout(function(){ base.startShow(true); }, options.autoDelay); }else{ base.stopShow(true); } // check if show should pause on hover if(options.autoHover && !options.ticker){ setAutoHover(); } } // make the starting slide active if(options.moveSlideQty > 1){ makeSlideActive(Math.ceil(currentSlide / options.moveSlideQty)); }else{ makeSlideActive(currentSlide); } // check for finite show and if controls should be hidden checkEndControls(); // show captions if(options.captions){ showCaptions(); } // perform the callback function options.onAfterSlide(currentSlide, $children.length, $children.eq(currentSlide)); } /** * Destroy the current slideshow */ this.destroyShow = function(){ // stop the auto show clearInterval(interval); // remove any controls / pagers that have been appended $outerWrapper.children('.bx-next, .bx-prev, .bx-pager, .bx-auto').remove(); // unwrap all bx-wrappers $parent.unwrap().unwrap().removeAttr('style'); // remove any styles that were appended $parent.children(options.childSelector).removeAttr('style').not('.bx-child').remove(); // remove any childrent that were appended $children.removeClass('bx-child'); } /** * Reload the current slideshow */ this.reloadShow = function(){ base.destroyShow(); base.initShow(); } // PRIVATE FUNCTIONS /** * Creates all neccessary styling for the slideshow */ function initCss(){ // layout the children setChildrenLayout(options.startingSlide); // CSS for horizontal mode if(options.mode == 'horizontal'){ // wrap the