/** * BxSlider v4.1.2 - Fully loaded, responsive content slider * http://bxslider.com * * Copyright 2014, Steven Wanderski - http://stevenwanderski.com - http://bxcreative.com * Written while drinking Belgian ales and listening to jazz * * Released under the MIT license - http://opensource.org/licenses/MIT */ ; (function ($) { var plugin = {}; var defaults = { // GENERAL mode: 'horizontal', slideSelector: '', infiniteLoop: true, hideControlOnEnd: false, speed: 500, easing: null, slideMargin: 0, startSlide: 0, randomStart: false, captions: false, ticker: false, tickerHover: false, adaptiveHeight: false, scale: '', adaptiveHeightSpeed: 500, video: false, useCSS: true, preloadImages: 'visible', responsive: true, slideZIndex: 50, wrapperClass: 'bx-wrapper', // TOUCH touchEnabled: true, swipeThreshold: 50, oneToOneTouch: true, preventDefaultSwipeX: true, preventDefaultSwipeY: false, // PAGER pager: true, pagerType: 'full', pagerShortSeparator: ' / ', pagerSelector: null, buildPager: null, pagerCustom: null, // CONTROLS controls: true, nextText: 'Next', prevText: 'Prev', nextSelector: null, prevSelector: null, autoControls: false, startText: 'Start', stopText: 'Stop', autoControlsCombine: false, autoControlsSelector: null, // AUTO auto: true, pause: 5000, autoStart: true, autoDirection: 'next', autoHover: false, autoDelay: 0, autoSlideForOnePage: false, // CAROUSEL minSlides: 1, maxSlides: 1, moveSlides: 0, slideWidth: 0, // CALLBACKS onSliderLoad: function () {}, onSlideBefore: function () {}, onSlideAfter: function () {}, onSlideNext: function () {}, onSlidePrev: function () {}, onSliderResize: function () {} } $.fn.bxSlider = function (options) { if (this.length == 0) return this; // support mutltiple elements if (this.length > 1) { this.each(function () { $(this).bxSlider(options) }); return this; } // create a namespace to be used throughout the plugin var slider = {}; // set a reference to our slider element var el = this; plugin.el = this; /** * Makes slideshow responsive */ // first get the original window dimens (thanks alot IE) var windowWidth = $(window).width(); var windowHeight = $(window).height(); /** * =================================================================================== * = PRIVATE FUNCTIONS * =================================================================================== */ /** * Initializes namespace settings to be used throughout plugin */ var init = function () { // merge user-supplied options with the defaults slider.settings = $.extend({}, defaults, options); // parse slideWidth setting slider.settings.slideWidth = parseInt(slider.settings.slideWidth); // store the original children slider.children = el.children(slider.settings.slideSelector); // check if actual number of slides is less than minSlides / maxSlides if (slider.children.length < slider.settings.minSlides) slider.settings.minSlides = slider.children.length; if (slider.children.length < slider.settings.maxSlides) slider.settings.maxSlides = slider.children.length; // if random start, set the startSlide setting to random number if (slider.settings.randomStart) slider.settings.startSlide = Math.floor(Math.random() * slider.children.length); // store active slide information slider.active = {index: slider.settings.startSlide} // store if the slider is in carousel mode (displaying / moving multiple slides) slider.carousel = slider.settings.minSlides > 1 || slider.settings.maxSlides > 1; // if carousel, force preloadImages = 'all' if (slider.carousel) slider.settings.preloadImages = 'all'; // calculate the min / max width thresholds based on min / max number of slides // used to setup and update carousel slides dimensions slider.minThreshold = (slider.settings.minSlides * slider.settings.slideWidth) + ((slider.settings.minSlides - 1) * slider.settings.slideMargin); slider.maxThreshold = (slider.settings.maxSlides * slider.settings.slideWidth) + ((slider.settings.maxSlides - 1) * slider.settings.slideMargin); // store the current state of the slider (if currently animating, working is true) slider.working = false; // initialize the controls object slider.controls = {}; // initialize an auto interval slider.interval = null; // determine which property to use for transitions slider.animProp = slider.settings.mode == 'vertical' ? 'top' : 'left'; // determine if hardware acceleration can be used slider.usingCSS = slider.settings.useCSS && slider.settings.mode != 'fade' && (function () { // create our test div element var div = document.createElement('div'); // css transition properties var props = ['WebkitPerspective', 'MozPerspective', 'OPerspective', 'msPerspective']; // test for each property for (var i in props) { if (div.style[props[i]] !== undefined) { slider.cssPrefix = props[i].replace('Perspective', '').toLowerCase(); slider.animProp = '-' + slider.cssPrefix + '-transform'; return true; } } return false; }()); // if vertical mode always make maxSlides and minSlides equal if (slider.settings.mode == 'vertical') slider.settings.maxSlides = slider.settings.minSlides; // save original style data el.data("origStyle", el.attr("style")); el.children(slider.settings.slideSelector).each(function () { $(this).data("origStyle", $(this).attr("style")); }); // perform all DOM / CSS modifications setup(); } /** * Performs all DOM and CSS modifications */ var setup = function () { // wrap el in a wrapper el.wrap('
'); // store a namspace reference to .bx-viewport slider.viewport = el.parent(); // add a loading div to display while images are loading slider.loader = $(''); slider.viewport.prepend(slider.loader); // set el to a massive width, to hold any needed slides // also strip any margin and padding from el el.css({ width: slider.settings.mode == 'horizontal' ? (slider.children.length * 100 + 215) + '%' : 'auto', position: 'relative' }); // if using CSS, add the easing property if (slider.usingCSS && slider.settings.easing) { el.css('-' + slider.cssPrefix + '-transition-timing-function', slider.settings.easing); // if not using CSS and no easing value was supplied, use the default JS animation easing (swing) } else if (!slider.settings.easing) { slider.settings.easing = 'swing'; } var slidesShowing = getNumberSlidesShowing(); // make modifications to the viewport (.bx-viewport) slider.viewport.css({ width: '100%', overflow: 'hidden', position: 'relative' }); slider.viewport.parent().css({ maxWidth: getViewportMaxWidth() }); // make modification to the wrapper (.bx-wrapper) if (!slider.settings.pager) { slider.viewport.parent().css({ margin: '0 auto 0px' }); } // apply css to all slider children slider.children.css({ 'float': slider.settings.mode == 'horizontal' ? 'left' : 'none', listStyle: 'none', position: 'relative' }); // apply the calculated width after the float is applied to prevent scrollbar interference slider.children.css('width', getSlideWidth()); // if slideMargin is supplied, add the css if (slider.settings.mode == 'horizontal' && slider.settings.slideMargin > 0) slider.children.css('marginRight', slider.settings.slideMargin); if (slider.settings.mode == 'vertical' && slider.settings.slideMargin > 0) slider.children.css('marginBottom', slider.settings.slideMargin); // if "fade" mode, add positioning and z-index CSS if (slider.settings.mode == 'fade') { slider.children.css({ position: 'absolute', left: 0, zIndex: 0, display: 'none' }); // prepare the z-index on the showing element slider.children.eq(slider.settings.startSlide).css({zIndex: slider.settings.slideZIndex, display: 'block'}); } // create an element to contain all slider controls (pager, start / stop, etc) slider.controls.el = $(''); // if captions are requested, add them if (slider.settings.captions) appendCaptions(); // check if startSlide is last slide slider.active.last = slider.settings.startSlide == getPagerQty() - 1; // if video is true, set up the fitVids plugin if (slider.settings.video) el.fitVids(); // set the default preload selector (visible) var preloadSelector = slider.children.eq(slider.settings.startSlide); if (slider.settings.preloadImages == "all") preloadSelector = slider.children; // only check for control addition if not in "ticker" mode if (!slider.settings.ticker) { // if pager is requested, add it if (slider.settings.pager) appendPager(); // if controls are requested, add them if (slider.settings.controls) appendControls(); // if auto is true, and auto controls are requested, add them if (slider.settings.auto && slider.settings.autoControls) appendControlsAuto(); // if any control option is requested, add the controls wrapper if (slider.settings.controls || slider.settings.autoControls || slider.settings.pager) slider.viewport.after(slider.controls.el); // if ticker mode, do not allow a pager } else { slider.settings.pager = false; } // preload all images, then perform final DOM / CSS modifications that depend on images being loaded loadElements(preloadSelector, start); } var loadElements = function (selector, callback) { var total = selector.find('img, iframe').length; if (total == 0) { callback(); return; } var count = 0; selector.find('img, iframe').each(function () { $(this).one('load', function () { if (++count == total) callback(); }).each(function () { if (this.complete) $(this).load(); }); }); } /** * Start the slider */ var start = function () { // if infinite loop, prepare additional slides if (slider.settings.infiniteLoop && slider.settings.mode != 'fade' && !slider.settings.ticker) { var slice = slider.settings.mode == 'vertical' ? slider.settings.minSlides : slider.settings.maxSlides; var sliceAppend = slider.children.slice(0, slice).clone().addClass('bx-clone'); var slicePrepend = slider.children.slice(-slice).clone().addClass('bx-clone'); el.append(sliceAppend).prepend(slicePrepend); } // remove the loading DOM element slider.loader.remove(); // set the left / top position of "el" setSlidePosition(); // if "vertical" mode, always use adaptiveHeight to prevent odd behavior if (slider.settings.mode == 'vertical') slider.settings.adaptiveHeight = true; // set the viewport height slider.viewport.height(getViewportHeight()); // make sure everything is positioned just right (same as a window resize) el.redrawSlider(); // onSliderLoad callback slider.settings.onSliderLoad(slider.active.index); // slider has been fully initialized slider.initialized = true; // bind the resize call to the window if (slider.settings.responsive) $(window).bind('resize', resizeWindow); // if auto is true and has more than 1 page, start the show if (slider.settings.auto && slider.settings.autoStart && (getPagerQty() > 1 || slider.settings.autoSlideForOnePage)) initAuto(); // if ticker is true, start the ticker if (slider.settings.ticker) initTicker(); // if pager is requested, make the appropriate pager link active if (slider.settings.pager) updatePagerActive(slider.settings.startSlide); // check for any updates to the controls (like hideControlOnEnd updates) if (slider.settings.controls) updateDirectionControls(); // if touchEnabled is true, setup the touch events if (slider.settings.touchEnabled && !slider.settings.ticker) initTouch(); } /** * Returns the calculated height of the viewport, used to determine either adaptiveHeight or the maxHeight value */ var getViewportHeight = function () { var height = 0; // first determine which children (slides) should be used in our height calculation var children = $(); // if mode is not "vertical" and adaptiveHeight is false, include all children if (slider.settings.mode != 'vertical' && !slider.settings.adaptiveHeight) { children = slider.children; } else { // if not carousel, return the single active child if (!slider.carousel) { children = slider.children.eq(slider.active.index); // if carousel, return a slice of children } else { // get the individual slide index var currentIndex = slider.settings.moveSlides == 1 ? slider.active.index : slider.active.index * getMoveBy(); // add the current slide to the children children = slider.children.eq(currentIndex); // cycle through the remaining "showing" slides for (i = 1; i <= slider.settings.maxSlides - 1; i++) { // if looped back to the start if (currentIndex + i >= slider.children.length) { children = children.add(slider.children.eq(i - 1)); } else { children = children.add(slider.children.eq(currentIndex + i)); } } } } // if "vertical" mode, calculate the sum of the heights of the children if (slider.settings.mode == 'vertical') { children.each(function (index) { height += $(this).outerHeight(); }); // add user-supplied margins if (slider.settings.slideMargin > 0) { height += slider.settings.slideMargin * (slider.settings.minSlides - 1); } // if not "vertical" mode, calculate the max height of the children } else { height = Math.max.apply(Math, children.map(function () { return $(this).outerHeight(false); }).get()); } if (slider.viewport.css('box-sizing') == 'border-box') { height += parseFloat(slider.viewport.css('padding-top')) + parseFloat(slider.viewport.css('padding-bottom')) + parseFloat(slider.viewport.css('border-top-width')) + parseFloat(slider.viewport.css('border-bottom-width')); } else if (slider.viewport.css('box-sizing') == 'padding-box') { height += parseFloat(slider.viewport.css('padding-top')) + parseFloat(slider.viewport.css('padding-bottom')); } if (slider.settings.scale != '') { var scale = slider.settings.scale.split('*'); height = (getSlideWidth() / parseInt(scale[0])) * parseInt(scale[1]); slider.children.find('img').css({'height': height}); } return height; } /** * Returns the calculated width to be used for the outer wrapper / viewport */ var getViewportMaxWidth = function () { var width = '100%'; if (slider.settings.slideWidth > 0) { if (slider.settings.mode == 'horizontal') { width = (slider.settings.maxSlides * slider.settings.slideWidth) + ((slider.settings.maxSlides - 1) * slider.settings.slideMargin); } else { width = slider.settings.slideWidth; } } return width; } /** * Returns the calculated width to be applied to each slide */ var getSlideWidth = function () { // start with any user-supplied slide width var newElWidth = slider.settings.slideWidth; // get the current viewport width var wrapWidth = slider.viewport.width(); // if slide width was not supplied, or is larger than the viewport use the viewport width if (slider.settings.slideWidth == 0 || (slider.settings.slideWidth > wrapWidth && !slider.carousel) || slider.settings.mode == 'vertical') { newElWidth = wrapWidth; // if carousel, use the thresholds to determine the width } else if (slider.settings.maxSlides > 1 && slider.settings.mode == 'horizontal') { if (wrapWidth > slider.maxThreshold) { // newElWidth = (wrapWidth - (slider.settings.slideMargin * (slider.settings.maxSlides - 1))) / slider.settings.maxSlides; } else if (wrapWidth < slider.minThreshold) { newElWidth = (wrapWidth - (slider.settings.slideMargin * (slider.settings.minSlides - 1))) / slider.settings.minSlides; } } return newElWidth; } /** * Returns the number of slides currently visible in the viewport (includes partially visible slides) */ var getNumberSlidesShowing = function () { var slidesShowing = 1; if (slider.settings.mode == 'horizontal' && slider.settings.slideWidth > 0) { // if viewport is smaller than minThreshold, return minSlides if (slider.viewport.width() < slider.minThreshold) { slidesShowing = slider.settings.minSlides; // if viewport is larger than minThreshold, return maxSlides } else if (slider.viewport.width() > slider.maxThreshold) { slidesShowing = slider.settings.maxSlides; // if viewport is between min / max thresholds, divide viewport width by first child width } else { var childWidth = slider.children.first().width() + slider.settings.slideMargin; slidesShowing = Math.floor((slider.viewport.width() + slider.settings.slideMargin) / childWidth); } // if "vertical" mode, slides showing will always be minSlides } else if (slider.settings.mode == 'vertical') { slidesShowing = slider.settings.minSlides; } return slidesShowing; } /** * Returns the number of pages (one full viewport of slides is one "page") */ var getPagerQty = function () { var pagerQty = 0; // if moveSlides is specified by the user if (slider.settings.moveSlides > 0) { if (slider.settings.infiniteLoop) { pagerQty = Math.ceil(slider.children.length / getMoveBy()); } else { // use a while loop to determine pages var breakPoint = 0; var counter = 0 // when breakpoint goes above children length, counter is the number of pages while (breakPoint < slider.children.length) { ++pagerQty; breakPoint = counter + getNumberSlidesShowing(); counter += slider.settings.moveSlides <= getNumberSlidesShowing() ? slider.settings.moveSlides : getNumberSlidesShowing(); } } // if moveSlides is 0 (auto) divide children length by sides showing, then round up } else { pagerQty = Math.ceil(slider.children.length / getNumberSlidesShowing()); } return pagerQty; } /** * Returns the number of indivual slides by which to shift the slider */ var getMoveBy = function () { // if moveSlides was set by the user and moveSlides is less than number of slides showing if (slider.settings.moveSlides > 0 && slider.settings.moveSlides <= getNumberSlidesShowing()) { return slider.settings.moveSlides; } // if moveSlides is 0 (auto) return getNumberSlidesShowing(); } /** * Sets the slider's (el) left or top position */ var setSlidePosition = function () { // if last slide, not infinite loop, and number of children is larger than specified maxSlides if (slider.children.length > slider.settings.maxSlides && slider.active.last && !slider.settings.infiniteLoop) { if (slider.settings.mode == 'horizontal') { // get the last child's position var lastChild = slider.children.last(); var position = lastChild.position(); // set the left position setPositionProperty(-(position.left - (slider.viewport.width() - lastChild.outerWidth())), 'reset', 0); } else if (slider.settings.mode == 'vertical') { // get the last showing index's position var lastShowingIndex = slider.children.length - slider.settings.minSlides; var position = slider.children.eq(lastShowingIndex).position(); // set the top position setPositionProperty(-position.top, 'reset', 0); } // if not last slide } else { // get the position of the first showing slide var position = slider.children.eq(slider.active.index * getMoveBy()).position(); // check for last slide if (slider.active.index == getPagerQty() - 1) slider.active.last = true; // set the repective position if (position != undefined) { if (slider.settings.mode == 'horizontal') setPositionProperty(-position.left, 'reset', 0); else if (slider.settings.mode == 'vertical') setPositionProperty(-position.top, 'reset', 0); } } } /** * Sets the el's animating property position (which in turn will sometimes animate el). * If using CSS, sets the transform property. If not using CSS, sets the top / left property. * * @param value (int) * - the animating property's value * * @param type (string) 'slider', 'reset', 'ticker' * - the type of instance for which the function is being * * @param duration (int) * - the amount of time (in ms) the transition should occupy * * @param params (array) optional * - an optional parameter containing any variables that need to be passed in */ var setPositionProperty = function (value, type, duration, params) { // use CSS transform if (slider.usingCSS) { // determine the translate3d value var propValue = slider.settings.mode == 'vertical' ? 'translate3d(0, ' + value + 'px, 0)' : 'translate3d(' + value + 'px, 0, 0)'; // add the CSS transition-duration el.css('-' + slider.cssPrefix + '-transition-duration', duration / 1000 + 's'); if (type == 'slide') { // set the property value el.css(slider.animProp, propValue); // bind a callback method - executes when CSS transition completes el.bind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function () { // unbind the callback el.unbind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd'); updateAfterSlideTransition(); }); } else if (type == 'reset') { el.css(slider.animProp, propValue); } else if (type == 'ticker') { // make the transition use 'linear' el.css('-' + slider.cssPrefix + '-transition-timing-function', 'linear'); el.css(slider.animProp, propValue); // bind a callback method - executes when CSS transition completes el.bind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function () { // unbind the callback el.unbind('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd'); // reset the position setPositionProperty(params['resetValue'], 'reset', 0); // start the loop again tickerLoop(); }); } // use JS animate } else { var animateObj = {}; animateObj[slider.animProp] = value; if (type == 'slide') { el.animate(animateObj, duration, slider.settings.easing, function () { updateAfterSlideTransition(); }); } else if (type == 'reset') { el.css(slider.animProp, value) } else if (type == 'ticker') { el.animate(animateObj, speed, 'linear', function () { setPositionProperty(params['resetValue'], 'reset', 0); // run the recursive loop after animation tickerLoop(); }); } } } /** * Populates the pager with proper amount of pages */ var populatePager = function () { var pagerHtml = ''; var pagerQty = getPagerQty(); // loop through each pager item for (var i = 0; i < pagerQty; i++) { var linkContent = ''; // if a buildPager function is supplied, use it to get pager link value, else use index + 1 if (slider.settings.buildPager && $.isFunction(slider.settings.buildPager)) { linkContent = slider.settings.buildPager(i); slider.pagerEl.addClass('bx-custom-pager'); } else { linkContent = i + 1; slider.pagerEl.addClass('bx-default-pager'); } // var linkContent = slider.settings.buildPager && $.isFunction(slider.settings.buildPager) ? slider.settings.buildPager(i) : i + 1; // add the markup to the string pagerHtml += ''; } ; // populate the pager element with pager links slider.pagerEl.html(pagerHtml); } /** * Appends the pager to the controls element */ var appendPager = function () { if (!slider.settings.pagerCustom) { // create the pager DOM element slider.pagerEl = $(''); // if a pager selector was supplied, populate it with the pager if (slider.settings.pagerSelector) { $(slider.settings.pagerSelector).html(slider.pagerEl); // if no pager selector was supplied, add it after the wrapper } else { slider.controls.el.addClass('bx-has-pager').append(slider.pagerEl); } // populate the pager populatePager(); } else { slider.pagerEl = $(slider.settings.pagerCustom); } // assign the pager click binding slider.pagerEl.on('click', 'a', clickPagerBind); } /** * Appends prev / next controls to the controls element */ var appendControls = function () { slider.controls.next = $('' + slider.settings.nextText + ''); slider.controls.prev = $('' + slider.settings.prevText + ''); // bind click actions to the controls slider.controls.next.bind('click', clickNextBind); slider.controls.prev.bind('click', clickPrevBind); // if nextSlector was supplied, populate it if (slider.settings.nextSelector) { $(slider.settings.nextSelector).append(slider.controls.next); } // if prevSlector was supplied, populate it if (slider.settings.prevSelector) { $(slider.settings.prevSelector).append(slider.controls.prev); } // if no custom selectors were supplied if (!slider.settings.nextSelector && !slider.settings.prevSelector) { // add the controls to the DOM slider.controls.directionEl = $(''); // add the control elements to the directionEl slider.controls.directionEl.append(slider.controls.prev).append(slider.controls.next); // slider.viewport.append(slider.controls.directionEl); slider.controls.el.addClass('bx-has-controls-direction').append(slider.controls.directionEl); } } /** * Appends start / stop auto controls to the controls element */ var appendControlsAuto = function () { slider.controls.start = $(''); slider.controls.stop = $(''); // add the controls to the DOM slider.controls.autoEl = $(''); // bind click actions to the controls slider.controls.autoEl.on('click', '.bx-start', clickStartBind); slider.controls.autoEl.on('click', '.bx-stop', clickStopBind); // if autoControlsCombine, insert only the "start" control if (slider.settings.autoControlsCombine) { slider.controls.autoEl.append(slider.controls.start); // if autoControlsCombine is false, insert both controls } else { slider.controls.autoEl.append(slider.controls.start).append(slider.controls.stop); } // if auto controls selector was supplied, populate it with the controls if (slider.settings.autoControlsSelector) { $(slider.settings.autoControlsSelector).html(slider.controls.autoEl); // if auto controls selector was not supplied, add it after the wrapper } else { slider.controls.el.addClass('bx-has-controls-auto').append(slider.controls.autoEl); } // update the auto controls updateAutoControls(slider.settings.autoStart ? 'stop' : 'start'); } /** * Appends image captions to the DOM */ var appendCaptions = function () { // cycle through each child slider.children.each(function (index) { // get the image title attribute var title = $(this).find('img:first').attr('title'); // append the caption if (title != undefined && ('' + title).length) { $(this).append('