/** * miSlider * Version: 0.1.11 * URL: private * Description: A multi-item slider for displaying one or more items per slide * Requires: jQuery * Optional: Modernizr * Author: jbowyers * Copyright: 2014-2015 jbowyers * License: This file is part of miSlider. * miSlider is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * miSlider is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/ */ ;(function( $, window, document, Math, undefined ) { "use strict"; var MiSlider = function( stageEl, options ) { // Clone miSlider object var o = this; // Initialize option defaults ============================================== o.optionsInit = { // The speed of the slide transition in milliseconds. Options: positive integer. speed: 700, // slide pause time between transitions in milliseconds. // Options: false, positive integer. false = Autoplay off. pause: 4000, // The number of slides to increment with Autoplay and Nav Buttons. // Options: positive or negative integer. Positive integer = Slide left. // Negative integer = Slide right. increment: 1, // The height of the stage in px. Options: false or positive integer. // false = height is calculated using maximum slide heights. stageHeight: false, // Number of slides visible at one time. Options: false or positive integer. // false = Fit as many as possible. slidesOnStage: 1, // Continuous motion - Boolean. true = slides loop in one direction if possible. slidesContinuous: true, // The location of the current slide on the stage. Options: "left", "right", "center". slidePosition: "left", // The slide to start on. Options: "beg", "mid", "end" // or slide number starting at 1 - "1","2","3", etc. slideStart: "beg", // The width of the current slide in px. Options: false or positive integer. // false = width is the maximum of the existing slide widths. slideWidth: false, // The relative percentage scaling factor of the current slide // other slides are scaled down. Options: positive number 100 or higher. 100 = No scaling. slideScaling: 100, // The vertical offset of the slide center as a percentage of slide height. // Options: positive or negative number. Neg value = up. Pos value = down. 0 = No offset. offsetV: 0, // Center slide contents vertically - Boolean. centerV: false, // Enable numbered list navigation - Boolean. navList: true, // Enable prev and next button navigation - Boolean. navButtons: true, // Always show prev and next button navigation except when transitioning - Boolean. navButtonsShow: false, // Opacity of the prev and next button navigation when not transitioning. // Options: Number between 0 and 1. 0 (transparent) - 1 (opaque). navButtonsOpacity: 1, // Randomize the order of the slides - Boolean. randomize: false, // The slides loaded call back function - called when slides have loaded. slidesLoaded: false, // The slide transition before call back function - called before the slide transition. beforeTrans: false, // The slide transition complete call back function // called at the end of a slide transition. afterTrans: false, // The CSS class that will be applied to the stage element. classStage: "mis-stage", // The CSS class that will be applied to the slider element. classSlider: "mis-slider", // The CSS class that will be applied to each slide element. classSlide: "mis-slide", // The CSS class that will be applied to the parent of the // prev and next button navigation elements. classNavButtons: "mis-nav-buttons", // The CSS class that will be applied to the parent of the // numbered list navigation elements classNavList: "mis-nav-list", // The selector used to select the slider element - Descendant of the stage selectorSlider: "ol", // The selector used to select each slide element - Descendant of the slider selectorSlide: "li" }; // Define objects and vars ================================================= // Objects ----------------------------------------------------------------- // Merged options o.options = {}; // slider container element o.stage = false; // slider element - container for slides+ o.slider = false; // The collection of slides o.slides = false; // The nav buttons element o.navButtons = false; // The "previous" nav button o.prev = false; // The "next" nav button o.next = false; // The nav list element o.navList = false; // The nav list items collection o.navListItems = false; // Current slide o.slideCurrent = false; // A collection of all the elements that animate o.animatedElements = $(); // Calculated Widths and Heights ------------------------------------------- // The existing width of the stage o.stageWidth = 0; // The calculated height of stage o.stageHeight = 0; // The calculated width of slider o.sliderWidth = 0; // Slide calculated width of non-current slides o.slideWidth = 0; // Slide calculated width of current slide o.slideWidthCurrent = 0; // Calculated Scaling vars ------------------------------------------------- // The calculated relative percentage scaling factor of the current slide o.slideScaling = o.optionsInit.slideScaling; // The calculated scaling width o.scalingWidth = 0; // The calculated scaling margin o.scalingMargin = 0; // The vertical offset of the slide center o.offsetV = o.optionsInit.offsetV; // Slide counts ------------------------------------------------------------ // The original number of slides in collection o.slidesLengthOrig = 0; // The number of slides in collection including cloned slides o.slidesLength = 0; // Current slide index o.indexCurrent = 0; // The index of the first unique slide o.indexFirst = 0; // The index of the last unique slide o.indexLast = 0; // The calculated number of slides to increment with Autoplay and Nav Buttons o.increment = o.optionsInit.increment; // The calculated number of slides on stage o.slidesOnStage = o.optionsInit.slidesOnStage; // Slider settings -------------------------------------------------------- // The calculated speed o.speed = o.optionsInit.speed; // The calculted Nav Buttons opacity o.navButtonsOpacity = o.optionsInit.navButtonsOpacity; // The calculated prev and next button navigation fade boolean o.navButtonsFade = false; // The calculated continuous motion Boolean. o.slidesContinuous = o.optionsInit.slidesContinuous; // The normalized pause value o.pause = o.optionsInit.pause; // Functions -------------------------------------------------------------- // Interval timer function o.timer = false; // Window resize timer function o.resizeTimer = false; // Temporary after transition callback function o.after = false; // Classes ---------------------------------------------------------------- // Class applied to the cloned slides o.classSlideClone = "mis-clone"; // Class of slide container - inserted around the contents of each slide o.classSlideContainer = "mis-container"; // Class applied to the the current slides and current nav list item o.classCurrent = "mis-current"; // Class applied to the the "previous" button o.classPrev = "mis-prev"; // Class applied to the the "next" button o.classNext = "mis-next"; // Initiate slider ======================================================== o.init = function( stageEl, options ) { // Must only be called once // Set options o.options = $.extend( {}, o.optionsInit, options ); // Initiate elements o.stage = $( stageEl ); // Hide everything while we get setup o.stage.fadeTo( 0, 0 ); // Only one slider per stage o.slider = o.stage.children( o.options.selectorSlider ).first(); o.slides = o.slider.children( o.options.selectorSlide ); o.slidesLengthOrig = o.slides.length; o.animatedElements = o.animatedElements.add( o.slider ).add( o.slides ); // Initiate current index if ( String( o.options.slideStart ) === "beg" ) { o.indexCurrent = 0; } else if ( String( o.options.slideStart ) === "mid" ) { o.indexCurrent = Math.ceil( o.slidesLengthOrig / 2 ) - 1; } else if ( String( o.options.slideStart ) === "end" ) { o.indexCurrent = o.slidesLengthOrig - 1; } else if ($.isNumeric( o.options.slideStart ) && parseInt( o.options.slideStart, 10 ) <= o.slidesLengthOrig && parseInt( o.options.slideStart, 10 ) > 0) { o.indexCurrent = parseInt( o.options.slideStart, 10 ) - 1; } else { o.indexCurrent = 0; } // Randomize slides if ( o.options.randomize ) { o.randomize(); } // Add classes to stage and slider if ( !o.stage.hasClass( o.options.classStage ) ) { o.stage.addClass( o.options.classStage ); } if ( !o.slider.hasClass( o.options.classSlider ) ) { o.slider.addClass( o.options.classSlider ); } // Normalize static options if ( o.options.speed && $.isNumeric( o.options.speed ) ) { o.speed = Math.abs( parseInt( o.options.speed, 10 ) ); } if ( o.options.pause === false ) { // Note: 0 must return true o.pause = false; } else if ( $.isNumeric( o.options.pause ) ) { o.pause = Math.abs( parseInt( o.options.pause, 10 ) ); } if ( $.isNumeric( o.options.offsetV ) ) { o.offsetV = Number( o.options.offsetV ); } if ($.isNumeric(o.options.navButtonsOpacity) && Number(o.options.navButtonsOpacity) <= 1) { o.navButtonsOpacity = Number( o.options.navButtonsOpacity ); } // Initiate calculated scaling factor if ( $.isNumeric( o.options.slideScaling ) && Number( o.options.slideScaling ) >= 100 ) { o.slideScaling = Number( o.options.slideScaling ); } // if CSS transforms are not supported if ( !supportTransform( stageEl ) ) { o.slideScaling = 100; } o.optionsInit.slideScaling = o.slideScaling; // Initiate the calculated increment if ( $.isNumeric( o.options.increment ) && parseInt( o.options.increment, 10 ) !== 0 ) { o.increment = parseInt( o.options.increment, 10 ); o.optionsInit.increment = o.increment; } // Add previous and next nav buttons if ( o.options.navButtons ) { o.addNavButtons( o.stage ); o.animatedElements = o.animatedElements.add( o.navButtons ); if ( !o.options.navButtonsShow ) { o.navButtonsFade = true; } } // Add numbered list navigation if ( o.options.navList ) { o.addNavList(); } // Set up the slider o.setup(); // Add event handlers --------------------------------------------------- // Add click events to slides if ( o.slidesOnStage > 1 ) { o.slider.on( "click", o.options.selectorSlide, function( e ) { if ( $( this ).index() !== o.indexCurrent ) { e.preventDefault(); o.autoplayOff(); o.transition( $( this ).index(), false, o.autoplayOn( o.increment ) ); } }); } // Add hover events for controling Autoplay and Nav Button opacity if ( o.pause !== false || o.navButtonsFade ) { // Note: 0 must return true for o.pause o.stage.on({ "mouseenter": function() { if ( o.pause !== false ) { o.autoplayOff(); } if ( o.navButtonsFade ) { if ( !o.animatedElements.is( ":animated" ) ) { o.navButtons.fadeTo( 400, o.navButtonsOpacity ); } else { if ( $.isFunction( o.after ) ) { var after = o.after; o.after = function() { after(); o.navButtons.fadeTo( 400, o.navButtonsOpacity ); }; } else { o.after = function() { o.navButtons.fadeTo( 400, o.navButtonsOpacity ); }; } } } }, "mouseleave": function() { if ( o.pause !== false ) { o.autoplayOn( o.increment ); } if ( o.navButtonsFade ) { o.navButtons.fadeTo( 100, 100 ); } } }); } // Window events $( window ).on({ // Wait for slides to load before setting up slides and nav buttons "load": function() { // Setup slides and nav buttons o.slideSetup(); o.updateNavButtons(); // Fade in everything o.stage.fadeTo( 600, 1 ); // Autoplay slides if enabled o.autoplayOn( o.increment ); // Slides loaded callBack if ( $.isFunction( o.options.slidesLoaded ) ) { o.options.slidesLoaded(); } }, // Reset Slider on screen resize "resize": function() { o.autoplayOff(); clearTimeout( o.resizeTimer ); o.resizeTimer = setTimeout(o.resetSlider, 500 ); } }); return this; }; // Setup slider ============================================================ o.setup = function() { var slidesMaxNum, incrementTest, slidesOffStage; // Set Slides length o.slidesLength = o.slidesLengthOrig; o.indexLast = o.slidesLength - 1; // Get widths, heights, add slide class and container o.slides.each( function() { var width, height, slide = $(this); // Add slide class to slide if ( !slide.hasClass( o.options.classSlide ) ) { slide.addClass( o.options.classSlide ); } // Add slide container to slide if ( !slide.children().hasClass( o.classSlideContainer ) ) { slide.wrapInner( "
" ); } // Get widths and heights width = slide.outerWidth(); height = slide.outerHeight(); if ( width > o.slideWidthCurrent ) { o.slideWidthCurrent = width; } if ( height > o.stageHeight ) { o.stageHeight = height; } }); // Apply presets if they exist if ( $.isNumeric( o.options.slideWidth ) && parseInt( o.options.slideWidth, 10 ) > 0 ) { o.slideWidthCurrent = parseInt( o.options.slideWidth, 10 ); } if ( $.isNumeric( o.options.stageHeight ) && parseInt( o.options.stageHeight, 10 ) > 0 ) { o.stageHeight = parseInt( o.options.stageHeight, 10 ); } // Use modulus hack to ensure current index is within range o.indexCurrent = normalizeIndex( o.indexCurrent ); // Set the stage ------------------------------------------------------- // Set CSS o.stage.css({ "height": o.stageHeight }); // Get Stage width - must do this after setting height o.stageWidth = o.stage.outerWidth(); // Calculate slide scaling, widths, increment and slides on and off stage // Determine the maximum number of slides that fit on the stage slidesMaxNum = Math.floor( ( o.stageWidth - o.slideWidthCurrent ) / ( o.slideWidthCurrent * 100 / o.slideScaling ) ) + 1; // Must have at least 1 slidesMaxNum = ( slidesMaxNum < 1 ) ? 1 : slidesMaxNum; // Calculate the number of slides visible on the stage o.slidesOnStage = slidesMaxNum; // Fit as many as possible if ($.isNumeric( o.options.slidesOnStage ) && parseInt( o.options.slidesOnStage, 10 ) >= 1 && parseInt( o.options.slidesOnStage, 10 ) <= slidesMaxNum) { // Use existing options value o.slidesOnStage = parseInt( o.options.slidesOnStage, 10 ); } if ( o.options.slidePosition === "center" ) { // need odd number for centered layout o.slidesOnStage = ( Math.ceil( o.slidesOnStage / 2 ) * 2 ) - 1; } // The absolute increment number should not be greater than slides on stage incrementTest = ( o.increment + o.slidesOnStage ) / 2; if ( incrementTest > o.slidesOnStage ) { // increment is positive and more than slides on stage o.increment = o.slidesOnStage; } else if ( incrementTest < 0 ) { // increment is negative and more than slides on stage o.increment = -( o.slidesOnStage ); } // Calculate the current and non-current slide widths if ( o.slidesOnStage > 1 ) { // modify non-current slide width to accommodate correct number of slides on the stage o.slideWidth = ( o.stageWidth - o.slideWidthCurrent ) / ( o.slidesOnStage - 1 ); if ( o.slideWidthCurrent < o.slideWidth && !o.options.slideWidth ) { // Set slideWidth and slideWidthCurrent to be the same o.slideWidth = o.stageWidth / o.slidesOnStage; o.slideWidthCurrent = o.slideWidth; } } else { // Make slide widths full width of stage o.slideWidth = o.stageWidth; o.slideWidthCurrent = o.slideWidth; o.slideScaling = 100; } // Set scaling width and margin o.scalingWidth = o.slideWidth * o.slideScaling / 100; o.scalingMargin = ( o.slideWidth - o.scalingWidth ) / 2; // Determine the number of slides off stage slidesOffStage = o.slidesLengthOrig - o.slidesOnStage; // Clone last slidesToClone slides to beginning and first slidesToClone slides to end if ( slidesOffStage >= 0 && o.options.slidesContinuous ) { // Set calculated continuous motion boolean to true o.slidesContinuous = true; // Determine the number of slides to clone o.slidesToClone = o.slidesOnStage + Math.abs( o.increment ) - 1; // Prepend last slidesToClone slides o.slides .slice( o.slidesLength - o.slidesToClone ) .clone() .addClass( o.classSlideClone ) .removeAttr( "id" ) .prependTo( o.slider ) .find( "*" ) .removeAttr( "id" ); // Append first slidesToClone slides o.slides .slice( 0, o.slidesToClone ) .clone() .addClass( o.classSlideClone ) .removeAttr( "id" ) .appendTo( o.slider ) .find( "*" ) .removeAttr( "id" ); // Adjust indexes o.indexFirst = o.slidesToClone; o.indexLast = o.slidesLength + o.slidesToClone - 1; o.indexCurrent = o.indexCurrent + o.slidesToClone; // Refresh slides o.slides = o.slider.children( o.options.selectorSlide ); o.slidesLength = o.slides.length; } else { o.slidesContinuous = false; } // Update current slide o.slideCurrent = o.slides.eq( o.indexCurrent ); // Set the horizontal position, width and other CSS of the slider ------ // Calculate the width of the slider o.sliderWidth = o.slideWidthCurrent + ( o.slideWidth * ( o.slidesLength - 1 ) ) + 1; // Set CSS of slider o.slider .css({ "left": leftOffsetter( o.indexCurrent ), "width": o.sliderWidth }) ; // update navList o.updateNavList( o.indexCurrent ); return this; }; // Transition control function ============================================= o.transition = function( indexTo, beforeTrans, afterTrans, navButtonsFadeIn ) { // If slider is not animated and indexTo != current index - continue if ( !o.animatedElements.is( ":animated" ) && indexTo !== o.indexCurrent ) { // Define indexes that might be adjusted var indexDiff, indexToAdjusted = indexTo, indexCurrentAdjusted = o.indexCurrent; // Update indexes and slides if continuous and slides are out of bounds if ( o.slidesContinuous ) { // Get adjusted indexTo if slides are out of bounds if ( indexTo < o.indexFirst ) { indexToAdjusted = indexTo + o.slidesLengthOrig; } else if ( indexTo > o.indexLast ) { indexToAdjusted = indexTo - o.slidesLengthOrig; } if ( indexToAdjusted !== indexTo ) { // indexTo is out of bounds // Adjust current index indexCurrentAdjusted = o.indexCurrent + o.slidesLengthOrig; if ( indexToAdjusted < indexTo ) { indexCurrentAdjusted = o.indexCurrent - o.slidesLengthOrig; } } } else { // Use modulus hack to ensure adjusted index is within range indexToAdjusted = normalizeIndex( indexTo ); } // Get the normalized difference between indexes indexDiff = normalizeIndex( indexToAdjusted ) - normalizeIndex( indexCurrentAdjusted ); // If adjusted indexTo != adjusted current index - do move --------- if ( indexDiff ) { // Define after transition function var var after; // Call "before transition" functions if ( $.isFunction( beforeTrans ) ) { beforeTrans(); } if ( $.isFunction( o.options.beforeTrans ) ) { o.options.beforeTrans(); } // Construct after transition function after = function() { // Call afterTrans local function if ( $.isFunction( afterTrans ) ) { afterTrans(); } // Call afterTrans pre-defined function if ( $.isFunction( o.options.afterTrans ) ) { o.options.afterTrans(); } // Call temporary callback function if ( $.isFunction( o.after ) ) { o.after(); o.after = false; } }; // Swap current slide if continuous and current index needs to be adjusted if ( o.slidesContinuous && indexCurrentAdjusted !== o.indexCurrent ) { var slideCurrentAdjusted = o.slides.eq( indexCurrentAdjusted ); // Prepare Current slide's clone if ( o.slideScaling !== 100 ) { // Scale slide slideCurrentAdjusted.css({ "transform": "scale(1)", "width": o.slideWidthCurrent, "marginLeft": "0", "marginRight": "0", // value placeholder used with step function to animate tranform "borderSpacing": "100px" }); // Vertically center slide if enabled if ( o.options.centerV ) { slideCurrentAdjusted.children().first().css({ "marginTop": slideCurrentAdjusted.data( "slideMarginTopCurrent" ) }); } } // Add current class to current slide slideCurrentAdjusted .addClass( o.classCurrent ) .siblings() .removeClass( o.classCurrent ); // Jump to Clone o.slider.css( "left", leftOffsetter( indexCurrentAdjusted ) ); // Reset original if ( o.slideScaling !== 100 ) { // Scale slide o.slideCurrent.css({ "transform": "scale(" + ( 100 / o.slideScaling ) + ")", "width": o.scalingWidth, "marginLeft": o.scalingMargin, "marginRight": o.scalingMargin, // value placeholder used with step function to animate tranform "borderSpacing": o.slideScaling }); // Vertically center slide if enabled if ( o.options.centerV ) { o.slideCurrent.children().first().css({ "marginTop": o.slideCurrent.data( "slideMarginTop" ) }); } } // Update current index and slide o.indexCurrent = indexCurrentAdjusted; o.slideCurrent = o.slides.eq( o.indexCurrent ); } // Transition -------------------------------------------------- if ( o.navButtons ) { // FadeIn Nav Buttons before move if fadeIn bool true o.navButtons.fadeTo( 100, ( navButtonsFadeIn ) ? o.navButtonsOpacity : 1, function() { // FadeOut Nav Buttons before move o.navButtons.fadeTo( 100, 100, function() { // Do transition o.animateSlides( indexToAdjusted, function() { if ( o.stage.find( ":hover" ).length || o.options.navButtonsShow ) { // FadeIn Nav Buttons after move o.navButtons.fadeTo( 400, o.navButtonsOpacity, after ); } else { after(); } }); }); } ); } else { // Do transition o.animateSlides( indexToAdjusted, after ); } } } return this; }; // Animate slide transition ================================================ o.animateSlides = function( indexTo, afterTrans ) { // Remove current class from current slide o.slideCurrent.removeClass( o.classCurrent ); // Define the slide to move to var slideTo = o.slides.eq( indexTo ); if ( o.slideScaling !== 100 ) { // only scale if needed // Scale New Slide ------------------------------------------------- slideTo .animate({ "marginLeft": "0", "marginRight": "0", "width": o.slideWidthCurrent }, { duration: o.speed, queue: false }) .animate({ "borderSpacing": "100px" }, { // Must use step function to animate scaling step: function ( now ) { $( this ).css({ "transform": "scale(" + 100 / now + ")" }); }, duration: o.speed, queue: false }) ; // Scale Current slide --------------------------------------------- o.slideCurrent .animate({ "marginLeft": o.scalingMargin, "marginRight": o.scalingMargin, "width": o.scalingWidth }, { duration: o.speed, queue: false }) .animate({ "borderSpacing": o.slideScaling }, { // Must use step function to animate scaling step: function ( now ) { $( this ).css({ "transform": "scale(" + 100 / now + ")" }); }, duration: o.speed, queue: false }) ; // Animate slide contents margin if vertically center slide is enabled if ( o.options.centerV ) { // Animate New Slide content slideTo .children() .first() .animate({ "marginTop": slideTo.data( "slideMarginTopCurrent" ) }, { duration: o.speed, queue: false }); // Animate Current Slide content o.slideCurrent .children() .first() .animate({ "marginTop": o.slideCurrent.data( "slideMarginTop" ) }, { duration: o.speed, queue: false }); } } // Move to new slide --------------------------------------------------- o.slider.animate({ "left": leftOffsetter( indexTo ) }, { duration: o.speed, queue: false, complete: function() { // transition complete // Update current index and slide o.indexCurrent = indexTo; o.slideCurrent = slideTo; // update navList o.updateNavList( indexTo ); // Add current class to current slide o.slideCurrent .addClass( o.classCurrent ) .siblings() .removeClass( o.classCurrent ) ; // Execute callbacks if ( $.isFunction( afterTrans ) ) { afterTrans(); } } }); return this; }; // Playback control functions ============================================== // Autoplay on ------------------------------------------------------------- o.autoplayOn = function( incr ) { if ( o.pause !== false ) { // autoplay is enabled // Reset timer clearInterval( o.timer ); if ( !o.stage.find( ":hover" ).length ) { // slider is not in hover state o.timer = setInterval(function() { // if not transitioning do transition if ( !o.animatedElements.is( ":animated" ) ) { o.transition( o.indexCurrent + incr ); } }, o.pause ); } } return this; }; // Autoplay off ------------------------------------------------------------ o.autoplayOff = function() { clearInterval( o.timer ); return this; }; // Add navButtons ----------------------------------------------------------- o.addNavButtons = function( element ) { // Create Nav Buttons object and Parent element to append nav buttons to var navButtons, $el = $( element ); // Construct HTML navButtons = $( "
PrevNext
" ); // Apply CSS and click events to buttons navButtons .css({ "opacity": ( ( o.options.navButtonsShow ) ? 1 : 1 ) }) .children( "a" ) .on( "click", function( e ) { e.preventDefault(); if ( this.className === o.classPrev ) { o.autoplayOff(); o.transition( o.indexCurrent - Math.abs( o.increment ), false, o.autoplayOn( o.increment ), true ); } else if ( this.className === o.classNext ) { o.autoplayOff(); o.transition( o.indexCurrent + Math.abs( o.increment ), false, o.autoplayOn( o.increment ), true ); } }); // Append buttons to stage $el.append( navButtons ); // Define cached objects o.navButtons = $el.find( "." + o.options.classNavButtons ); o.prev = o.navButtons.find( "." + o.classPrev ); o.next = o.navButtons.find( "." + o.classNext ); return this; }; // Update Nav Buttons ------------------------------------------------------ o.updateNavButtons = function() { if ( o.navButtons ) { // Apply CSS to buttons o.navButtons .css({ "width": 1126, "left": 0, }) .children( "a" ) .css({ "height": o.stageHeight, "paddingTop": ( 50 + o.offsetV ) * o.stageHeight / 100 }) ; } }; // Add navList -------------------------------------------------------------- o.addNavList = function() { // Create temporary list object and list HTML var var navList, listHtml = "
    "; // Create each List item o.slides.each(function( index ) { // Create caption and list item text vars var caption, itemText = index + 1; // Use header, figcaption, or img title as list item text instead caption = $( this ).find( ":header" ).sort(function( a, b ) { var aTag = $( a ).prop( "tagName" ), bTag = $( b ).prop( "tagName" ); return parseInt( aTag.match( /\d+/ ), 10 ) - parseInt( bTag.match( /\d+/ ), 10 ); }).eq( 0 ).html(); if ( caption ) { itemText = caption; } else { caption = $( this ).find( "figcaption" ).eq( 0 ).html(); if ( caption ) { itemText = caption; } else { caption = $( this ).find( "img" ).eq( 0 ).attr( "title" ); if ( caption ) { itemText = caption; } } } // Add list item to list listHtml += "
  1. " + itemText + "
  2. "; }); // Close list listHtml += "
"; // Define temporary list object and add click events navList = $( listHtml ) .on( "click", "li", function( e ) { e.preventDefault(); if ( $( this ).index() !== ( o.indexCurrent - o.indexFirst ) ) { o.autoplayOff(); o.transition( $( this ).index() + o.indexFirst, false, o.autoplayOn( o.increment ) ); } }) ; // Prepend list to slider and update cached objects o.stage.prepend( navList ); o.navList = o.stage.children().first(); o.navListItems = o.navList.children( "li" ); return this; }; // Update navList ----------------------------------------------------------- o.updateNavList = function( index ) { if ( o.navListItems.length ) { o.navListItems .eq( index - o.indexFirst ) .addClass( o.classCurrent ) .siblings() .removeClass( o.classCurrent ) ; } }; // Assorted functions ====================================================== // Randomizer -------------------------------------------------------------- o.randomize = function() { // Randomize cached slides o.slides.sort(function() { return ( 0.5 - Math.random() ); }); // Apply randomized slides to page o.slides.detach().appendTo( o.slider ); return this; }; // Reset Slider ----------------------------------------------------------- o.resetSlider = function() { if ( o.animatedElements.is( ":animated" ) ) { // Reset slider after transition has finished if ( $.isFunction( o.after ) ) { var after = o.after; o.after = function() { after(); o.resetSlider(); }; } else { o.after = o.resetSlider; } } else { // Resets o.autoplayOff(); o.stage.removeAttr( "style" ); o.slider.removeAttr( "style" ); o.slides.removeAttr( "style" ); o.slides.filter( "." + o.classSlideClone ).remove(); o.slides = o.slider.children( o.options.selectorSlide ); o.stageHeight = 0; o.slideWidthCurrent = 0; o.slideScaling = o.optionsInit.slideScaling; o.indexCurrent -= o.slidesToClone; o.indexFirst = 0; o.increment = o.optionsInit.increment; o.after = false; // Setup o.setup(); o.slideSetup(); o.updateNavButtons(); o.autoplayOn( o.increment ); } return this; }; // Setup slides function ---------------------------------------------------- o.slideSetup = function() { o.slides.each(function( i ) { var slide = $( this ); // Set transform origin and current width slide.css({ "transform-origin": "50% " + String( 50 + o.offsetV ) + "%", "width": o.slideWidthCurrent }); // Get vertical slide offset if enabled if ( o.options.centerV ) { getMarginTop( slide, "slideMarginTopCurrent" ); } // Set non-current slides slide.css({ "width": o.scalingWidth }); if ( o.slideScaling !== 100 ) { slide.css({ // compensation for scaling transform "marginLeft": o.scalingMargin, // compensation for scaling transform "marginRight": o.scalingMargin, "transform": "scale(" + ( 100 / o.slideScaling ) + ")", // value placeholder used with step function to animate tranform "borderSpacing": o.slideScaling }); } // Vertically center slide if enabled if ( o.options.centerV ) { slide.children().first().css({ "marginTop": getMarginTop( slide, "slideMarginTop" ) }); } // Set current slides if ( i === o.indexCurrent ) { slide .css({ // value placeholder used with step function to animate tranform "borderSpacing": "100px", "width": o.slideWidthCurrent, // compensation for scaling transform "marginLeft": 0, // compensation for scaling transform "marginRight": 0, "transform": "scale(1)" }) .addClass( o.classCurrent ) .siblings() .removeClass( o.classCurrent ) ; // Vertically center slide if enabled if ( o.options.centerV ) { slide.children().first().css({ "marginTop": getMarginTop( slide, "slideMarginTopCurrent" ) }); } } }); }; // Slider left offset calculator ------------------------------------------ function leftOffsetter( index ) { var indexOffset = o.slideWidth * index * -1, leftOffset = indexOffset; // Slide position = Left if ( o.options.slidePosition === "center" ) { leftOffset = indexOffset + ( Math.floor( o.slidesOnStage / 2 ) * o.slideWidth ); } else if (o.options.slidePosition === "right") { leftOffset = ( indexOffset + ( ( o.slidesOnStage - 1) * o.slideWidth ) ); } return leftOffset; } // Get offset for centering slide contents vertically within stage // Must be called after contents (images) have loaded to get correct height function getMarginTop( slide, dataKey ) { // Get height of slide container var height = slide.children().first().outerHeight(), slideMarginTop = 0; // Add negative top margin if slide height is bigger than stage height if (height > o.stageHeight) { slideMarginTop = ( o.stageHeight - height ) / 2; } // Store slide's top margin value for re-use slide.data( dataKey, slideMarginTop ); return slideMarginTop; } // Use modulus hack to make sure index is within range ---------------------- function normalizeIndex( index ) { index = ( ( index % o.slidesLengthOrig ) + o.slidesLengthOrig ) % o.slidesLengthOrig; return index; } // Test transform feature support function supportTransform( element ) { var test = false; // Use Modernizr if it exists if ( typeof Modernizr !== 'undefined' ) { if ( Modernizr.csstransforms ) { test = true; } } else { var style = element.style; if (typeof style.transform !== "undefined" || typeof style.WebkitTransform !== "undefined" || typeof style.msTransform !== "undefined") { test = true; } } return test; } // initialize ---------------------------------------------------------------- o.init( stageEl, options ); return this; }; // Create a jQuery plugin ====================================================== $.fn.miSlider = function( options ) { // Enable multiple-slider support return this.each(function() { var stage = $( this ); if ( !stage.data( "miSlider" ) ) { stage.data( "miSlider", new MiSlider( this, options ) ); } }); }; })( jQuery, window, document, Math );