1/*! 2* - v1.2.5 3* Homepage: http://bqworks.com/slider-pro/ 4* Author: bqworks 5* Author URL: http://bqworks.com/ 6*/ 7;(function( window, $ ) { 8 9 "use strict"; 10 11 // Static methods for Slider Pro 12 $.SliderPro = { 13 14 // List of added modules 15 modules: [], 16 17 // Add a module by extending the core prototype 18 addModule: function( name, module ) { 19 this.modules.push( name ); 20 $.extend( SliderPro.prototype, module ); 21 } 22 }; 23 24 // namespace 25 var NS = $.SliderPro.namespace = 'SliderPro'; 26 27 var SliderPro = function( instance, options ) { 28 29 // Reference to the slider instance 30 this.instance = instance; 31 32 // Reference to the slider jQuery element 33 this.$slider = $( this.instance ); 34 35 // Reference to the slides (sp-slides) jQuery element 36 this.$slides = null; 37 38 // Reference to the mask (sp-mask) jQuery element 39 this.$slidesMask = null; 40 41 // Reference to the slides (sp-slides-container) jQuery element 42 this.$slidesContainer = null; 43 44 // Array of SliderProSlide objects, ordered by their DOM index 45 this.slides = []; 46 47 // Array of SliderProSlide objects, ordered by their left/top position in the slider. 48 // This will be updated continuously if the slider is loopable. 49 this.slidesOrder = []; 50 51 // Holds the options passed to the slider when it was instantiated 52 this.options = options; 53 54 // Holds the final settings of the slider after merging the specified 55 // ones with the default ones. 56 this.settings = {}; 57 58 // Another reference to the settings which will not be altered by breakpoints or by other means 59 this.originalSettings = {}; 60 61 // Reference to the original 'gotoSlide' method 62 this.originalGotoSlide = null; 63 64 // The index of the currently selected slide (starts with 0) 65 this.selectedSlideIndex = 0; 66 67 // The index of the previously selected slide 68 this.previousSlideIndex = 0; 69 70 // Indicates the position of the slide considered to be in the middle. 71 // If there are 5 slides (0, 1, 2, 3, 4) the middle position will be 2. 72 // If there are 6 slides (0, 1, 2, 3, 4, 5) the middle position will be approximated to 2. 73 this.middleSlidePosition = 0; 74 75 // Indicates the type of supported transition (CSS3 2D, CSS3 3D or JavaScript) 76 this.supportedAnimation = null; 77 78 // Indicates the required vendor prefix for CSS (i.e., -webkit, -moz, etc.) 79 this.vendorPrefix = null; 80 81 // Indicates the name of the CSS transition's complete event (i.e., transitionend, webkitTransitionEnd, etc.) 82 this.transitionEvent = null; 83 84 // Indicates the 'left' or 'top' position 85 this.positionProperty = null; 86 87 // Indicates if the current browser is IE 88 this.isIE = null; 89 90 // The position of the slides container 91 this.slidesPosition = 0; 92 93 // The width of the individual slide 94 this.slideWidth = 0; 95 96 // The height of the individual slide 97 this.slideHeight = 0; 98 99 // The width or height, depending on the orientation, of the individual slide 100 this.slideSize = 0; 101 102 // Reference to the old slide width, used to check if the width has changed 103 this.previousSlideWidth = 0; 104 105 // Reference to the old slide height, used to check if the height has changed 106 this.previousSlideHeight = 0; 107 108 // Reference to the old window width, used to check if the window width has changed 109 this.previousWindowWidth = 0; 110 111 // Reference to the old window height, used to check if the window height has changed 112 this.previousWindowHeight = 0; 113 114 // The distance from the margin of the slider to the left/top of the selected slide. 115 // This is useful in calculating the position of the selected slide when there are 116 // more visible slides. 117 this.visibleOffset = 0; 118 119 // Property used for deferring the resizing of the slider 120 this.allowResize = true; 121 122 // Unique ID to be used for event listening 123 this.uniqueId = new Date().valueOf(); 124 125 // Stores size breakpoints 126 this.breakpoints = []; 127 128 // Indicates the current size breakpoint 129 this.currentBreakpoint = -1; 130 131 // An array of shuffled indexes, based on which the slides will be shuffled 132 this.shuffledIndexes = []; 133 134 // Initialize the slider 135 this._init(); 136 }; 137 138 SliderPro.prototype = { 139 140 // The starting place for the slider 141 _init: function() { 142 var that = this; 143 144 this.supportedAnimation = SliderProUtils.getSupportedAnimation(); 145 this.vendorPrefix = SliderProUtils.getVendorPrefix(); 146 this.transitionEvent = SliderProUtils.getTransitionEvent(); 147 this.isIE = SliderProUtils.checkIE(); 148 149 // Remove the 'sp-no-js' when the slider's JavaScript code starts running 150 this.$slider.removeClass( 'sp-no-js' ); 151 152 // Add the 'ios' class if it's an iOS device 153 if ( window.navigator.userAgent.match( /(iPad|iPhone|iPod)/g ) ) { 154 this.$slider.addClass( 'ios' ); 155 } 156 157 // Check if IE (older than 11) is used and add the version number as a class to the slider since 158 // older IE versions might need CSS tweaks. 159 var rmsie = /(msie) ([\w.]+)/, 160 ieVersion = rmsie.exec( window.navigator.userAgent.toLowerCase() ); 161 162 if ( this.isIE ) { 163 this.$slider.addClass( 'ie' ); 164 } 165 166 if ( ieVersion !== null ) { 167 this.$slider.addClass( 'ie' + parseInt( ieVersion[2], 10 ) ); 168 } 169 170 // Set up the slides containers 171 // slider-pro > sp-slides-container > sp-mask > sp-slides > sp-slide 172 this.$slidesContainer = $( '<div class="sp-slides-container"></div>' ).appendTo( this.$slider ); 173 this.$slidesMask = $( '<div class="sp-mask"></div>' ).appendTo( this.$slidesContainer ); 174 this.$slides = this.$slider.find( '.sp-slides' ).appendTo( this.$slidesMask ); 175 this.$slider.find( '.sp-slide' ).appendTo( this.$slides ); 176 177 var modules = $.SliderPro.modules; 178 179 // Merge the modules' default settings with the core's default settings 180 if ( typeof modules !== 'undefined' ) { 181 for ( var i = 0; i < modules.length; i++ ) { 182 var defaults = modules[ i ].substring( 0, 1 ).toLowerCase() + modules[ i ].substring( 1 ) + 'Defaults'; 183 184 if ( typeof this[ defaults ] !== 'undefined' ) { 185 $.extend( this.defaults, this[ defaults ] ); 186 } 187 } 188 } 189 190 // Merge the specified setting with the default ones 191 this.settings = $.extend( {}, this.defaults, this.options ); 192 193 // Initialize the modules 194 if ( typeof modules !== 'undefined' ) { 195 for ( var j = 0; j < modules.length; j++ ) { 196 if ( typeof this[ 'init' + modules[ j ] ] !== 'undefined' ) { 197 this[ 'init' + modules[ j ] ](); 198 } 199 } 200 } 201 202 // Keep a reference of the original settings and use it 203 // to restore the settings when the breakpoints are used. 204 this.originalSettings = $.extend( {}, this.settings ); 205
206 // Get the reference to the 'gotoSlide' method 207 this.originalGotoSlide = this.gotoSlide; 208 209 // Parse the breakpoints object and store the values into an array, 210 // sorting them in ascending order based on the specified size. 211 if ( this.settings.breakpoints !== null ) { 212 for ( var sizes in this.settings.breakpoints ) { 213 this.breakpoints.push({ size: parseInt( sizes, 10 ), properties:this.settings.breakpoints[ sizes ] }); 214 } 215 216 this.breakpoints = this.breakpoints.sort(function( a, b ) { 217 return a.size >= b.size ? 1: -1; 218 }); 219 } 220 221 // Set which slide should be selected initially 222 this.selectedSlideIndex = this.settings.startSlide; 223 224 // Shuffle/randomize the slides 225 if ( this.settings.shuffle === true ) { 226 var slides = this.$slides.find( '.sp-slide' ), 227 shuffledSlides = []; 228 229 // Populate the 'shuffledIndexes' with index numbers 230 slides.each(function( index ) { 231 that.shuffledIndexes.push( index ); 232 }); 233 234 for ( var k = this.shuffledIndexes.length - 1; k > 0; k-- ) { 235 var l = Math.floor( Math.random() * ( k + 1 ) ), 236 temp = this.shuffledIndexes[ k ]; 237 238 this.shuffledIndexes[ k ] = this.shuffledIndexes[ l ]; 239 this.shuffledIndexes[ l ] = temp; 240 } 241 242 // Reposition the slides based on the order of the indexes in the 243 // 'shuffledIndexes' array 244 $.each( this.shuffledIndexes, function( index, element ) { 245 shuffledSlides.push( slides[ element ] ); 246 }); 247 248 // Append the sorted slides to the slider 249 this.$slides.empty().append( shuffledSlides ) ; 250 } 251 252 // Resize the slider when the browser window resizes. 253 // Also, deffer the resizing in order to not allow multiple 254 // resizes in a 200 milliseconds interval. 255 $( window ).on( 'resize.' + this.uniqueId + '.' + NS, function() { 256 257 // Get the current width and height of the window 258 var newWindowWidth = $( window ).width(), 259 newWindowHeight = $( window ).height(); 260 261 // If the resize is not allowed yet or if the window size hasn't changed (this needs to be verified 262 // because in IE8 and lower the resize event is triggered whenever an element from the page changes 263 // its size) return early. 264 if ( that.allowResize === false || 265 ( that.previousWindowWidth === newWindowWidth && that.previousWindowHeight === newWindowHeight ) ) { 266 return; 267 } 268 269 // Asign the new values for the window width and height 270 that.previousWindowWidth = newWindowWidth; 271 that.previousWindowHeight = newWindowHeight; 272 273 that.allowResize = false; 274 275 setTimeout(function() { 276 that.resize(); 277 that.allowResize = true; 278 }, 200 ); 279 }); 280 281 // Resize the slider when the 'update' method is called. 282 this.on( 'update.' + NS, function() { 283 284 // Reset the previous slide width 285 that.previousSlideWidth = 0; 286 287 // Some updates might require a resize 288 that.resize(); 289 }); 290 291 this.update(); 292 293 // add the 'sp-selected' class to the initially selected slide 294 this.$slides.find( '.sp-slide' ).eq( this.selectedSlideIndex ).addClass( 'sp-selected' ); 295 296 // Fire the 'init' event 297 this.trigger({ type: 'init' }); 298 if ( $.isFunction( this.settings.init ) ) { 299 this.settings.init.call( this, { type: 'init' }); 300 } 301 }, 302 303 // Update the slider by checking for setting changes and for slides 304 // that weren't initialized yet. 305 update: function() { 306 var that = this; 307 308 // Check the current slider orientation and reset CSS that might have been 309 // added for a different orientation, since the orientation can be changed 310 // at runtime. 311 if ( this.settings.orientation === 'horizontal' ) { 312 this.$slider.removeClass( 'sp-vertical' ).addClass( 'sp-horizontal' ); 313 this.$slider.css({ 'height': '', 'max-height': '' }); 314 this.$slides.find( '.sp-slide' ).css( 'top', '' ); 315 } else if ( this.settings.orientation === 'vertical' ) { 316 this.$slider.removeClass( 'sp-horizontal' ).addClass( 'sp-vertical' ); 317 this.$slides.find( '.sp-slide' ).css( 'left', '' ); 318 } 319 320 // Set the position that will be used to arrange elements, like the slides, 321 // based on the orientation. 322 this.positionProperty = this.settings.orientation === 'horizontal' ? 'left' : 'top'; 323
324 // Reset the 'gotoSlide' method 325 this.gotoSlide = this.originalGotoSlide; 326 327 // Loop through the array of SliderProSlide objects and if a stored slide is found 328 // which is not in the DOM anymore, destroy that slide. 329 for ( var i = this.slides.length - 1; i >= 0; i-- ) { 330 if ( this.$slider.find( '.sp-slide[data-index="' + i + '"]' ).length === 0 ) { 331 var slide = this.slides[ i ]; 332 333 slide.destroy(); 334 this.slides.splice( i, 1 ); 335 } 336 } 337 338 this.slidesOrder.length = 0; 339 340 // Loop through the list of slides and initialize newly added slides if any, 341 // and reset the index of each slide. 342 this.$slider.find( '.sp-slide' ).each(function( index ) { 343 var $slide = $( this ); 344 345 if ( typeof $slide.attr( 'data-init' ) === 'undefined' ) { 346 that._createSlide( index, $slide ); 347 } else { 348 that.slides[ index ].setIndex( index ); 349 } 350 351 that.slidesOrder.push( index ); 352 }); 353 354 // Calculate the position/index of the middle slide 355 this.middleSlidePosition = parseInt( ( that.slidesOrder.length - 1 ) / 2, 10 ); 356 357 // Arrange the slides in a loop 358 if ( this.settings.loop === true ) { 359 this._updateSlidesOrder(); 360 } 361 362 // Fire the 'update' event 363 this.trigger({ type: 'update' }); 364 if ( $.isFunction( this.settings.update ) ) { 365 this.settings.update.call( this, { type: 'update' } ); 366 } 367 }, 368 369 // Create a SliderProSlide instance for the slide passed as a jQuery element 370 _createSlide: function( index, element ) { 371 var that = this, 372 slide = new SliderProSlide( $( element ), index, this.settings ); 373 374 this.slides.splice( index, 0, slide ); 375 }, 376 377 // Arrange the slide elements in a loop inside the 'slidesOrder' array 378 _updateSlidesOrder: function() { 379 var slicedItems, 380 i, 381 382 // Calculate the distance between the selected element and the middle position 383 distance = $.inArray( this.selectedSlideIndex, this.slidesOrder ) - this.middleSlidePosition; 384 385 // If the distance is negative it means that the selected slider is before the middle position, so 386 // slides from the end of the array will be added at the beginning, in order to shift the selected slide 387 // forward. 388 // 389 // If the distance is positive, slides from the beginning of the array will be added at the end. 390 if ( distance < 0 ) { 391 slicedItems = this.slidesOrder.splice( distance, Math.abs( distance ) ); 392 393 for ( i = slicedItems.length - 1; i >= 0; i-- ) { 394 this.slidesOrder.unshift( slicedItems[ i ] ); 395 } 396 } else if ( distance > 0 ) { 397 slicedItems = this.slidesOrder.splice( 0, distance ); 398 399 for ( i = 0; i <= slicedItems.length - 1; i++ ) { 400 this.slidesOrder.push( slicedItems[ i ] ); 401 } 402 } 403 }, 404 405 // Set the left/top position of the slides based on their position in the 'slidesOrder' array 406 _updateSlidesPosition: function() { 407 var selectedSlidePixelPosition = parseInt( this.$slides.find( '.sp-slide' ).eq( this.selectedSlideIndex ).css( this.positionProperty ), 10 ); 408 409 for ( var slideIndex = 0; slideIndex < this.slidesOrder.length; slideIndex++ ) { 410 var slide = this.$slides.find( '.sp-slide' ).eq( this.slidesOrder[ slideIndex ] ); 411 slide.css( this.positionProperty, selectedSlidePixelPosition + ( slideIndex - this.middleSlidePosition ) * ( this.slideSize + this.settings.slideDistance ) ); 412 } 413 }, 414 415 // Set the left/top position of the slides based on their position in the 'slidesOrder' array, 416 // and also set the position of the slides container. 417 _resetSlidesPosition: function() { 418 for ( var slideIndex = 0; slideIndex < this.slidesOrder.length; slideIndex++ ) { 419 var slide = this.$slides.find( '.sp-slide' ).eq( this.slidesOrder[ slideIndex ] ); 420 slide.css( this.positionProperty, slideIndex * ( this.slideSize + this.settings.slideDistance ) ); 421 } 422 423 var newSlidesPosition = - parseInt( this.$slides.find( '.sp-slide' ).eq( this.selectedSlideIndex ).css( this.positionProperty ), 10 ) + this.visibleOffset; 424 this._moveTo( newSlidesPosition, true ); 425 }, 426 427 // Called when the slider needs to resize 428 resize: function() { 429 var that = this; 430 431 // Check if the current window width is bigger than the biggest breakpoint 432 // and if necessary reset the properties to the original settings. 433 // 434 // If the window width is smaller than a certain breakpoint, apply the settings specified 435 // for that breakpoint but only after merging them with the original settings 436 // in order to make sure that only the specified settings for the breakpoint are applied 437 if ( this.settings.breakpoints !== null && this.breakpoints.length > 0 ) { 438 if ( $( window ).width() > this.breakpoints[ this.breakpoints.length - 1 ].size && this.currentBreakpoint !== -1 ) { 439 this.currentBreakpoint = -1; 440 this._setProperties( this.originalSettings, false ); 441 } else { 442 for ( var i = 0, n = this.breakpoints.length; i < n; i++ ) { 443 if ( $( window ).width() <= this.breakpoints[ i ].size ) { 444 if ( this.currentBreakpoint !== this.breakpoints[ i ].size ) { 445 var eventObject = { type: 'breakpointReach', size: this.breakpoints[ i ].size, settings: this.breakpoints[ i ].properties }; 446 this.trigger( eventObject ); 447 if ( $.isFunction( this.settings.breakpointReach ) ) 448 this.settings.breakpointReach.call( this, eventObject ); 449 450 this.currentBreakpoint = this.breakpoints[ i ].size; 451 var settings = $.extend( {}, this.originalSettings, this.breakpoints[ i ].properties ); 452 this._setProperties( settings, false ); 453 454 return; 455 } 456 457 break; 458 } 459 } 460 } 461 } 462 463 // Set the width of the main slider container based on whether or not the slider is responsive, 464 // full width or full size 465 if ( this.settings.responsive === true ) { 466 if ( ( this.settings.forceSize === 'fullWidth' || this.settings.forceSize === 'fullWindow' ) && 467 ( this.settings.visibleSize === 'auto' || this.settings.visibleSize !== 'auto' && this.settings.orientation === 'vertical' ) 468 ) { 469 this.$slider.css( 'margin', 0 ); 470 this.$slider.css({ 'width': $( window ).width(), 'max-width': '', 'marginLeft': - this.$slider.offset().left }); 471 } else { 472 this.$slider.css({ 'width': '100%', 'max-width': this.settings.width, 'marginLeft': '' }); 473 } 474 } else { 475 this.$slider.css({ 'width': this.settings.width }); 476 } 477 478 // Calculate the aspect ratio of the slider 479 if ( this.settings.aspectRatio === -1 ) { 480 this.settings.aspectRatio = this.settings.width / this.settings.height; 481 } 482 483 // Initially set the slide width to the size of the slider. 484 // Later, this will be set to less if there are multiple visible slides. 485 this.slideWidth = this.$slider.width(); 486 487 // Set the height to the same size as the browser window if the slider is set to be 'fullWindow', 488 // or calculate the height based on the width and the aspect ratio. 489 if ( this.settings.forceSize === 'fullWindow' ) { 490 this.slideHeight = $( window ).height(); 491 } else { 492 this.slideHeight = isNaN( this.settings.aspectRatio ) ? this.settings.height : this.slideWidth / this.settings.aspectRatio; 493 } 494 495 // Resize the slider only if the size of the slider has changed 496 // If it hasn't, return. 497 if ( this.previousSlideWidth !== this.slideWidth || 498 this.previousSlideHeight !== this.slideHeight || 499 this.settings.visibleSize !== 'auto' || 500 this.$slider.outerWidth() > this.$slider.parent().width() || 501 this.$slider.width() !== this.$slidesMask.width() 502 ) { 503 this.previousSlideWidth = this.slideWidth; 504 this.previousSlideHeight = this.slideHeight; 505 } else { 506 return; 507 } 508 509 // The slide width or slide height is needed for several calculation, so create a reference to it 510 // based on the current orientation. 511 this.slideSize = this.settings.orientation === 'horizontal' ? this.slideWidth : this.slideHeight; 512 513 // Initially set the visible size of the slides and the offset of the selected slide as if there is only 514 // on visible slide. 515 // If there will be multiple visible slides (when 'visibleSize' is different than 'auto'), these will 516 // be updated accordingly. 517 this.visibleSlidesSize = this.slideSize; 518 this.visibleOffset = 0; 519
520 // Loop through the existing slides and reset their size. 521 $.each( this.slides, function( index, element ) { 522 element.setSize( that.slideWidth, that.slideHeight ); 523 }); 524 525 // Set the initial size of the mask container to the size of an individual slide 526 this.$slidesMask.css({ 'width': this.slideWidth, 'height': this.slideHeight }); 527 528 // Adjust the height if it's set to 'auto' 529 if ( this.settings.autoHeight === true ) { 530 531 // Delay the resizing of the height to allow for other resize handlers 532 // to execute first before calculating the final height of the slide 533 setTimeout( function() { 534 that._resizeHeight(); 535 }, 1 ); 536 } else { 537 this.$slidesMask.css( this.vendorPrefix + 'transition', '' ); 538 } 539 540 // The 'visibleSize' option can be set to fixed or percentage size to make more slides 541 // visible at a time. 542 // By default it's set to 'auto'. 543 if ( this.settings.visibleSize !== 'auto' ) { 544 if ( this.settings.orientation === 'horizontal' ) { 545 546 // If the size is forced to full width or full window, the 'visibleSize' option will be 547 // ignored and the slider will become as wide as the browser window. 548 if ( this.settings.forceSize === 'fullWidth' || this.settings.forceSize === 'fullWindow' ) { 549 this.$slider.css( 'margin', 0 ); 550 this.$slider.css({ 'width': $( window ).width(), 'max-width': '', 'marginLeft': - this.$slider.offset().left }); 551 } else { 552 this.$slider.css({ 'width': this.settings.visibleSize, 'max-width': '100%', 'marginLeft': 0 }); 553 } 554 555 this.$slidesMask.css( 'width', this.$slider.width() ); 556 557 this.visibleSlidesSize = this.$slidesMask.width(); 558 this.visibleOffset = Math.round( ( this.$slider.width() - this.slideWidth ) / 2 ); 559 } else { 560 561 // If the size is forced to full window, the 'visibleSize' option will be 562 // ignored and the slider will become as high as the browser window. 563 if ( this.settings.forceSize === 'fullWindow' ) { 564 this.$slider.css({ 'height': $( window ).height(), 'max-height': '' }); 565 } else { 566 this.$slider.css({ 'height': this.settings.visibleSize, 'max-height': '100%' }); 567 } 568 569 this.$slidesMask.css( 'height', this.$slider.height() ); 570 571 this.visibleSlidesSize = this.$slidesMask.height(); 572 this.visibleOffset = Math.round( ( this.$slider.height() - this.slideHeight ) / 2 ); 573 } 574 } 575 576 this._resetSlidesPosition(); 577 578 // Fire the 'sliderResize' event 579 this.trigger({ type: 'sliderResize' }); 580 if ( $.isFunction( this.settings.sliderResize ) ) { 581 this.settings.sliderResize.call( this, { type: 'sliderResize' }); 582 } 583 }, 584 585 // Resize the height of the slider to the height of the selected slide. 586 // It's used when the 'autoHeight' option is set to 'true'. 587 _resizeHeight: function() { 588 var that = this, 589 selectedSlide = this.getSlideAt( this.selectedSlideIndex ), 590 size = selectedSlide.getSize(); 591 592 selectedSlide.off( 'imagesLoaded.' + NS ); 593 selectedSlide.on( 'imagesLoaded.' + NS, function( event ) { 594 if ( event.index === that.selectedSlideIndex ) { 595 var size = selectedSlide.getSize(); 596 that._resizeHeightTo( size.height ); 597 } 598 }); 599 600 // If the selected slide contains images which are still loading, 601 // wait for the loading to complete and then request the size again. 602 if ( size !== 'loading' ) { 603 this._resizeHeightTo( size.height ); 604 } 605 }, 606 607 // Open the slide at the specified index 608 gotoSlide: function( index ) { 609 if ( index === this.selectedSlideIndex || typeof this.slides[ index ] === 'undefined' ) { 610 return; 611 } 612 613 var that = this; 614 615 this.previousSlideIndex = this.selectedSlideIndex; 616 this.selectedSlideIndex = index; 617 618 // Re-assign the 'sp-selected' class to the currently selected slide 619 this.$slides.find( '.sp-selected' ).removeClass( 'sp-selected' ); 620 this.$slides.find( '.sp-slide' ).eq( this.selectedSlideIndex ).addClass( 'sp-selected' ); 621 622 // If the slider is loopable reorder the slides to have the selected slide in the middle 623 // and update the slides' position. 624 if ( this.settings.loop === true ) { 625 this._updateSlidesOrder(); 626 this._updateSlidesPosition(); 627 } 628 629 // Adjust the height of the slider 630 if ( this.settings.autoHeight === true ) { 631 this._resizeHeight(); 632 } 633 634 // Calculate the new position that the slides container need to take 635 var newSlidesPosition = - parseInt( this.$slides.find( '.sp-slide' ).eq( this.selectedSlideIndex ).css( this.positionProperty ), 10 ) + this.visibleOffset; 636 637 // Move the slides container to the new position 638 this._moveTo( newSlidesPosition, false, function() { 639 if ( that.settings.loop === true ) { 640 that._resetSlidesPosition(); 641 } 642
643 // Fire the 'gotoSlideComplete' event 644 that.trigger({ type: 'gotoSlideComplete', index: index, previousIndex: that.previousSlideIndex }); 645 if ( $.isFunction( that.settings.gotoSlideComplete ) ) { 646 that.settings.gotoSlideComplete.call( that, { type: 'gotoSlideComplete', index: index, previousIndex: that.previousSlideIndex } ); 647 } 648 }); 649 650 // Fire the 'gotoSlide' event 651 this.trigger({ type: 'gotoSlide', index: index, previousIndex: this.previousSlideIndex }); 652 if ( $.isFunction( this.settings.gotoSlide ) ) { 653 this.settings.gotoSlide.call( this, { type: 'gotoSlide', index: index, previousIndex: this.previousSlideIndex } ); 654 } 655 }, 656 657 // Open the next slide 658 nextSlide: function() { 659 var index = ( this.selectedSlideIndex >= this.getTotalSlides() - 1 ) ? 0 : ( this.selectedSlideIndex + 1 ); 660 this.gotoSlide( index ); 661 }, 662 663 // Open the previous slide 664 previousSlide: function() { 665 var index = this.selectedSlideIndex <= 0 ? ( this.getTotalSlides() - 1 ) : ( this.selectedSlideIndex - 1 ); 666 this.gotoSlide( index ); 667 }, 668 669 // Move the slides container to the specified position. 670 // The movement can be instant or animated. 671 _moveTo: function( position, instant, callback ) { 672 var that = this, 673 css = {}; 674 675 if ( position === this.slidesPosition ) { 676 return; 677 } 678 679 this.slidesPosition = position; 680 681 if ( ( this.supportedAnimation === 'css-3d' || this.supportedAnimation === 'css-2d' ) && this.isIE === false ) { 682 var transition, 683 left = this.settings.orientation === 'horizontal' ? position : 0, 684 top = this.settings.orientation === 'horizontal' ? 0 : position; 685 686 if ( this.supportedAnimation === 'css-3d' ) { 687 css[ this.vendorPrefix + 'transform' ] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; 688 } else { 689 css[ this.vendorPrefix + 'transform' ] = 'translate(' + left + 'px, ' + top + 'px)'; 690 } 691 692 if ( typeof instant !== 'undefined' && instant === true ) { 693 transition = ''; 694 } else { 695 this.$slides.addClass( 'sp-animated' ); 696 transition = this.vendorPrefix + 'transform ' + this.settings.slideAnimationDuration / 1000 + 's'; 697 698 this.$slides.on( this.transitionEvent, function( event ) { 699 if ( event.target !== event.currentTarget ) { 700 return; 701 } 702 703 that.$slides.off( that.transitionEvent ); 704 that.$slides.removeClass( 'sp-animated' ); 705 706 if ( typeof callback === 'function' ) { 707 callback(); 708 } 709 }); 710 } 711 712 css[ this.vendorPrefix + 'transition' ] = transition; 713 714 this.$slides.css( css ); 715 } else { 716 css[ 'margin-' + this.positionProperty ] = position; 717 718 if ( typeof instant !== 'undefined' && instant === true ) { 719 this.$slides.css( css ); 720 } else { 721 this.$slides.addClass( 'sp-animated' ); 722 this.$slides.animate( css, this.settings.slideAnimationDuration, function() { 723 that.$slides.removeClass( 'sp-animated' ); 724 725 if ( typeof callback === 'function' ) { 726 callback(); 727 } 728 }); 729 } 730 } 731 }, 732 733 // Stop the movement of the slides 734 _stopMovement: function() { 735 var css = {}; 736 737 if ( ( this.supportedAnimation === 'css-3d' || this.supportedAnimation === 'css-2d' ) && this.isIE === false) { 738 739 // Get the current position of the slides by parsing the 'transform' property 740 var matrixString = this.$slides.css( this.vendorPrefix + 'transform' ), 741 matrixType = matrixString.indexOf( 'matrix3d' ) !== -1 ? 'matrix3d' : 'matrix', 742 matrixArray = matrixString.replace( matrixType, '' ).match( /-?[0-9\.]+/g ), 743 left = matrixType === 'matrix3d' ? parseInt( matrixArray[ 12 ], 10 ) : parseInt( matrixArray[ 4 ], 10 ), 744 top = matrixType === 'matrix3d' ? parseInt( matrixArray[ 13 ], 10 ) : parseInt( matrixArray[ 5 ], 10 ); 745 746 // Set the transform property to the value that the transform had when the function was called 747 if ( this.supportedAnimation === 'css-3d' ) { 748 css[ this.vendorPrefix + 'transform' ] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; 749 } else { 750 css[ this.vendorPrefix + 'transform' ] = 'translate(' + left + 'px, ' + top + 'px)'; 751 } 752 753 css[ this.vendorPrefix + 'transition' ] = ''; 754 755 this.$slides.css( css ); 756 this.$slides.off( this.transitionEvent ); 757 this.slidesPosition = this.settings.orientation === 'horizontal' ? left : top; 758 } else { 759 this.$slides.stop(); 760 this.slidesPosition = parseInt( this.$slides.css( 'margin-' + this.positionProperty ), 10 ); 761 } 762 763 this.$slides.removeClass( 'sp-animated' ); 764 }, 765 766 // Resize the height of the slider to the specified value 767 _resizeHeightTo: function( height ) { 768 var that = this, 769 css = { 'height': height }; 770 771 if ( this.supportedAnimation === 'css-3d' || this.supportedAnimation === 'css-2d' ) { 772 css[ this.vendorPrefix + 'transition' ] = 'height ' + this.settings.heightAnimationDuration / 1000 + 's'; 773 774 this.$slidesMask.off( this.transitionEvent ); 775 this.$slidesMask.on( this.transitionEvent, function( event ) { 776 if ( event.target !== event.currentTarget ) { 777 return; 778 } 779 780 that.$slidesMask.off( that.transitionEvent ); 781 782 // Fire the 'resizeHeightComplete' event 783 that.trigger({ type: 'resizeHeightComplete' }); 784 if ( $.isFunction( that.settings.resizeHeightComplete ) ) { 785 that.settings.resizeHeightComplete.call( that, { type: 'resizeHeightComplete' } ); 786 } 787 }); 788 789 this.$slidesMask.css( css ); 790 } else { 791 this.$slidesMask.stop().animate( css, this.settings.heightAnimationDuration, function( event ) { 792 // Fire the 'resizeHeightComplete' event 793 that.trigger({ type: 'resizeHeightComplete' }); 794 if ( $.isFunction( that.settings.resizeHeightComplete ) ) { 795 that.settings.resizeHeightComplete.call( that, { type: 'resizeHeightComplete' } ); 796 } 797 }); 798 } 799 }, 800 801 // Destroy the slider instance 802 destroy: function() { 803 // Remove the stored reference to this instance 804 this.$slider.removeData( 'sliderPro' ); 805 806 // Clean the CSS 807 this.$slider.removeAttr( 'style' ); 808 this.$slides.removeAttr( 'style' ); 809 810 // Remove event listeners 811 this.off( 'update.' + NS ); 812 $( window ).off( 'resize.' + this.uniqueId + '.' + NS ); 813 814 // Destroy modules 815 var modules = $.SliderPro.modules; 816 817 if ( typeof modules !== 'undefined' ) { 818 for ( var i = 0; i < modules.length; i++ ) { 819 if ( typeof this[ 'destroy' + modules[ i ] ] !== 'undefined' ) { 820 this[ 'destroy' + modules[ i ] ](); 821 } 822 } 823 } 824 825 // Destroy all slides 826 $.each( this.slides, function( index, element ) { 827 element.destroy(); 828 }); 829 830 this.slides.length = 0; 831 832 // Move the slides to their initial position in the DOM and 833 // remove the container elements created dynamically. 834 this.$slides.prependTo( this.$slider ); 835 this.$slidesContainer.remove(); 836 }, 837 838 // Set properties on runtime 839 _setProperties: function( properties, store ) { 840 // Parse the properties passed as an object 841 for ( var prop in properties ) { 842 this.settings[ prop ] = properties[ prop ]; 843 844 // Alter the original settings as well unless 'false' is passed to the 'store' parameter 845 if ( store !== false ) { 846 this.originalSettings[ prop ] = properties[ prop ]; 847 } 848 } 849 850 this.update(); 851 }, 852 853 // Attach an event handler to the slider 854 on: function( type, callback ) { 855 return this.$slider.on( type, callback ); 856 }, 857 858 // Detach an event handler 859 off: function( type ) { 860 return this.$slider.off( type ); 861 }, 862 863 // Trigger an event on the slider 864 trigger: function( data ) { 865 return this.$slider.triggerHandler( data ); 866 }, 867 868 // Return the slide at the specified index 869 getSlideAt: function( index ) { 870 return this.slides[ index ]; 871 }, 872 873 // Return the index of the currently opened slide 874 getSelectedSlide: function() { 875 return this.selectedSlideIndex; 876 }, 877 878 // Return the total amount of slides 879 getTotalSlides: function() { 880 return this.slides.length; 881 }, 882 883 // The default options of the slider 884 defaults: { 885 // Width of the slide 886 width: 500, 887 888 // Height of the slide 889 height: 300, 890 891 // Indicates if the slider is responsive 892 responsive: true, 893 894 // The aspect ratio of the slider (width/height) 895 aspectRatio: -1, 896 897 // The scale mode for images (cover, contain, exact and none) 898 imageScaleMode: 'cover', 899 900 // Indicates if the image will be centered 901 centerImage: true, 902 903 // Indicates if the image can be scaled up more than its original size 904 allowScaleUp: true, 905 906 // Indicates if height of the slider will be adjusted to the
907 // height of the selected slide 908 autoHeight: false, 909 910 // Indicates the initially selected slide 911 startSlide: 0, 912 913 // Indicates if the slides will be shuffled 914 shuffle: false, 915 916 // Indicates whether the slides will be arranged horizontally 917 // or vertically. Can be set to 'horizontal' or 'vertical'. 918 orientation: 'horizontal', 919 920 // Indicates if the size of the slider will be forced to 'fullWidth' or 'fullWindow' 921 forceSize: 'none', 922 923 // Indicates if the slider will be loopable 924 loop: true, 925 926 // The distance between slides 927 slideDistance: 10, 928 929 // The duration of the slide animation 930 slideAnimationDuration: 700, 931 932 // The duration of the height animation 933 heightAnimationDuration: 700, 934 935 // Sets the size of the visible area, allowing the increase of it in order 936 // to make more slides visible. 937 // By default, only the selected slide will be visible. 938 visibleSize: 'auto', 939 940 // Breakpoints for allowing the slider's options to be changed 941 // based on the size of the window. 942 breakpoints: null, 943 944 // Called when the slider is initialized 945 init: function() {}, 946 947 // Called when the slider is updates 948 update: function() {}, 949 950 // Called when the slider is resized 951 sliderResize: function() {}, 952 953 // Called when a new slide is selected 954 gotoSlide: function() {}, 955 956 // Called when the navigation to the newly selected slide is complete 957 gotoSlideComplete: function() {}, 958 959 // Called when the height animation of the slider is complete 960 resizeHeightComplete: function() {}, 961 962 // Called when a breakpoint is reached 963 breakpointReach: function() {} 964 } 965 }; 966 967 var SliderProSlide = function( slide, index, settings ) { 968 969 // Reference to the slide jQuery element 970 this.$slide = slide; 971 972 // Reference to the main slide image 973 this.$mainImage = null; 974 975 // Reference to the container that will hold the main image 976 this.$imageContainer = null; 977 978 // Indicates whether the slide has a main image 979 this.hasMainImage = false; 980 981 // Indicates whether the main image is loaded 982 this.isMainImageLoaded = false; 983 984 // Indicates whether the main image is in the process of being loaded 985 this.isMainImageLoading = false; 986 987 // Indicates whether the slide has any image. There could be other images (i.e., in layers) 988 // besides the main slide image. 989 this.hasImages = false; 990 991 // Indicates if all the images in the slide are loaded 992 this.areImagesLoaded = false; 993 994 // The width and height of the slide 995 this.width = 0; 996 this.height = 0; 997 998 // Reference to the global settings of the slider 999 this.settings = settings; 1000 1001 // Set the index of the slide 1002 this.setIndex( index ); 1003 1004 // Initialize the slide 1005 this._init(); 1006 }; 1007 1008 SliderProSlide.prototype = { 1009 1010 // The starting point for the slide 1011 _init: function() { 1012 var that = this; 1013 1014 // Mark the slide as initialized 1015 this.$slide.attr( 'data-init', true ); 1016 1017 // Get the main slide image if there is one 1018 this.$mainImage = this.$slide.find( '.sp-image' ).length !== 0 ? this.$slide.find( '.sp-image' ) : null; 1019 1020 // If there is a main slide image, create a container for it and add the image to this container. 1021 // The container will allow the isolation of the image from the rest of the slide's content. This is 1022 // helpful when you want to show some content below the image and not cover it. 1023 if ( this.$mainImage !== null ) { 1024 this.hasMainImage = true; 1025 1026 this.$imageContainer = $( '<div class="sp-image-container"></div>' ).prependTo( this.$slide ); 1027 1028 if ( this.$mainImage.parent( 'a' ).length !== 0 ) { 1029 this.$mainImage.parent( 'a' ).appendTo( this.$imageContainer ); 1030 } else { 1031 this.$mainImage.appendTo( this.$imageContainer ); 1032 } 1033 } 1034 1035 this.hasImages = this.$slide.find( 'img' ).length !== 0 ? true : false; 1036 }, 1037 1038 // Set the size of the slide 1039 setSize: function( width, height ) { 1040 var that = this; 1041 1042 this.width = width; 1043 this.height = this.settings.autoHeight === true ? 'auto' : height; 1044 1045 this.$slide.css({ 1046 'width': this.width, 1047 'height': this.height 1048 }); 1049 1050 if ( this.hasMainImage === true ) { 1051 this.$imageContainer.css({ 1052 'width': this.width, 1053 'height': this.height 1054 }); 1055 1056 // Resize the main image if it's loaded. If the 'data-src' attribute is present it means 1057 // that the image will be lazy-loaded 1058 if ( typeof this.$mainImage.attr( 'data-src' ) === 'undefined' ) { 1059 this.resizeMainImage(); 1060 } 1061 } 1062 }, 1063 1064 // Get the size (width and height) of the slide 1065 getSize: function() { 1066 var that = this, 1067 size; 1068 1069 // Check if all images have loaded, and if they have, return the size, else, return 'loading' 1070 if ( this.hasImages === true && this.areImagesLoaded === false && typeof this.$slide.attr( 'data-loading' ) === 'undefined' ) { 1071 this.$slide.attr( 'data-loading', true ); 1072 1073 var status = SliderProUtils.checkImagesComplete( this.$slide, function() { 1074 that.areImagesLoaded = true; 1075 that.$slide.removeAttr( 'data-loading' ); 1076 that.trigger({ type: 'imagesLoaded.' + NS, index: that.index }); 1077 }); 1078 1079 if ( status === 'complete' ) { 1080 size = this.calculateSize(); 1081 1082 return { 1083 'width': size.width, 1084 'height': size.height 1085 }; 1086 } else { 1087 return 'loading'; 1088 } 1089 } else { 1090 size = this.calculateSize(); 1091 1092 return { 1093 'width': size.width, 1094 'height': size.height 1095 }; 1096 } 1097 }, 1098 1099 // Calculate the width and height of the slide by going 1100 // through all the child elements and measuring their 'bottom' 1101 // and 'right' properties. The element with the biggest 1102 // 'right'/'bottom' property will determine the slide's 1103 // width/height. 1104 calculateSize: function() { 1105 var width = this.$slide.width(), 1106 height = this.$slide.height(); 1107 1108 this.$slide.children().each(function( index, element ) { 1109 var child = $( element ); 1110 1111 if ( child.is( ':hidden' ) === true ) { 1112 return; 1113 } 1114 1115 var rect = element.getBoundingClientRect(), 1116 bottom = child.position().top + ( rect.bottom - rect.top ), 1117 right = child.position().left + ( rect.right - rect.left ); 1118 1119 if ( bottom > height ) { 1120 height = bottom; 1121 } 1122 1123 if ( right > width ) { 1124 width = right; 1125 } 1126 }); 1127 1128 return { width: width, height: height }; 1129 }, 1130 1131 // Resize the main image. 1132 // 1133 // Call this when the slide resizes or when the main image has changed to a different image. 1134 resizeMainImage: function( isNewImage ) { 1135 var that = this; 1136 1137 // If the main image has changed, reset the 'flags' 1138 if ( isNewImage === true ) { 1139 this.isMainImageLoaded = false;
1140 this.isMainImageLoading = false; 1141 } 1142 1143 // If the image was not loaded yet and it's not in the process of being loaded, load it 1144 if ( this.isMainImageLoaded === false && this.isMainImageLoading === false ) { 1145 this.isMainImageLoading = true; 1146 1147 SliderProUtils.checkImagesComplete( this.$mainImage, function() { 1148 that.isMainImageLoaded = true; 1149 that.isMainImageLoading = false; 1150 that.resizeMainImage(); 1151 that.trigger({ type: 'imagesLoaded.' + NS, index: that.index }); 1152 }); 1153 1154 return; 1155 } 1156 1157 if ( this.settings.allowScaleUp === false ) { 1158 this.$mainImage.css({ 'width': '', 'height': '', 'maxWidth': '', 'maxHeight': '' }); 1159 1160 var naturalWidth = this.$mainImage.width(), 1161 naturalHeight = this.$mainImage.height(); 1162 1163 this.$mainImage.css({ 'maxWidth': naturalWidth, 'maxHeight': naturalHeight }); 1164 } 1165 1166 // After the main image has loaded, resize it 1167 if ( this.settings.autoHeight === true ) { 1168 this.$mainImage.css({ width: '100%', height: 'auto' }); 1169 } else { 1170 if ( this.settings.imageScaleMode === 'cover' ) { 1171 if ( this.$mainImage.width() / this.$mainImage.height() <= this.width / this.height ) { 1172 this.$mainImage.css({ width: '100%', height: 'auto' }); 1173 } else { 1174 this.$mainImage.css({ width: 'auto', height: '100%' }); 1175 } 1176 } else if ( this.settings.imageScaleMode === 'contain' ) { 1177 if ( this.$mainImage.width() / this.$mainImage.height() >= this.width / this.height ) { 1178 this.$mainImage.css({ width: '100%', height: 'auto' }); 1179 } else { 1180 this.$mainImage.css({ width: 'auto', height: '100%' }); 1181 } 1182 } else if ( this.settings.imageScaleMode === 'exact' ) { 1183 this.$mainImage.css({ width: '100%', height: '100%' }); 1184 } 1185 } 1186 1187 if ( this.settings.centerImage === true ) { 1188 this.$mainImage.css({ 'marginLeft': ( this.$imageContainer.width() - this.$mainImage.width() ) * 0.5, 'marginTop': ( this.$imageContainer.height() - this.$mainImage.height() ) * 0.5 }); 1189 } 1190 }, 1191 1192 // Destroy the slide 1193 destroy: function() { 1194 // Clean the slide element from attached styles and data 1195 this.$slide.removeAttr( 'style' ); 1196 this.$slide.removeAttr( 'data-init' ); 1197 this.$slide.removeAttr( 'data-index' ); 1198 this.$slide.removeAttr( 'data-loaded' ); 1199 1200 // If there is a main image, remove its container 1201 if ( this.hasMainImage === true ) { 1202 this.$slide.find( '.sp-image' ) 1203 .removeAttr( 'style' ) 1204 .appendTo( this.$slide ); 1205 1206 this.$slide.find( '.sp-image-container' ).remove(); 1207 } 1208 }, 1209 1210 // Return the index of the slide 1211 getIndex: function() { 1212 return this.index; 1213 }, 1214 1215 // Set the index of the slide 1216 setIndex: function( index ) { 1217 this.index = index; 1218 this.$slide.attr( 'data-index', this.index ); 1219 }, 1220 1221 // Attach an event handler to the slide 1222 on: function( type, callback ) { 1223 return this.$slide.on( type, callback ); 1224 }, 1225 1226 // Detach an event handler to the slide 1227 off: function( type ) { 1228 return this.$slide.off( type ); 1229 }, 1230 1231 // Trigger an event on the slide 1232 trigger: function( data ) { 1233 return this.$slide.triggerHandler( data ); 1234 } 1235 }; 1236 1237 window.SliderPro = SliderPro; 1238 window.SliderProSlide = SliderProSlide; 1239 1240 $.fn.sliderPro = function( options ) { 1241 var args = Array.prototype.slice.call( arguments, 1 ); 1242 1243 return this.each(function() { 1244 // Instantiate the slider or alter it 1245 if ( typeof $( this ).data( 'sliderPro' ) === 'undefined' ) { 1246 var newInstance = new SliderPro( this, options ); 1247 1248 // Store a reference to the instance created 1249 $( this ).data( 'sliderPro', newInstance ); 1250 } else if ( typeof options !== 'undefined' ) { 1251 var currentInstance = $( this ).data( 'sliderPro' ); 1252 1253 // Check the type of argument passed 1254 if ( typeof currentInstance[ options ] === 'function' ) { 1255 currentInstance[ options ].apply( currentInstance, args ); 1256 } else if ( typeof currentInstance.settings[ options ] !== 'undefined' ) { 1257 var obj = {}; 1258 obj[ options ] = args[ 0 ]; 1259 currentInstance._setProperties( obj ); 1260 } else if ( typeof options === 'object' ) { 1261 currentInstance._setProperties( options ); 1262 } else { 1263 $.error( options + ' does not exist in sliderPro.' ); 1264 } 1265 } 1266 }); 1267 }; 1268 1269 // Contains useful utility functions 1270 var SliderProUtils = { 1271 1272 // Indicates what type of animations are supported in the current browser 1273 // Can be CSS 3D, CSS 2D or JavaScript 1274 supportedAnimation: null, 1275 1276 // Indicates the required vendor prefix for the current browser 1277 vendorPrefix: null, 1278 1279 // Indicates the name of the transition's complete event for the current browser 1280 transitionEvent: null, 1281 1282 // Indicates if the current browser is Internet Explorer (any version) 1283 isIE: null, 1284 1285 // Check whether CSS3 3D or 2D transforms are supported. If they aren't, use JavaScript animations 1286 getSupportedAnimation: function() { 1287 if ( this.supportedAnimation !== null ) { 1288 return this.supportedAnimation; 1289 } 1290 1291 var element = document.body || document.documentElement, 1292 elementStyle = element.style, 1293 isCSSTransitions = typeof elementStyle.transition !== 'undefined' || 1294 typeof elementStyle.WebkitTransition !== 'undefined' || 1295 typeof elementStyle.MozTransition !== 'undefined' || 1296 typeof elementStyle.OTransition !== 'undefined'; 1297 1298 if ( isCSSTransitions === true ) { 1299 var div = document.createElement( 'div' ); 1300 1301 // Check if 3D transforms are supported 1302 if ( typeof div.style.WebkitPerspective !== 'undefined' || typeof div.style.perspective !== 'undefined' ) { 1303 this.supportedAnimation = 'css-3d'; 1304 } 1305 1306 // Additional checks for Webkit 1307 if ( this.supportedAnimation === 'css-3d' && typeof div.styleWebkitPerspective !== 'undefined' ) { 1308 var style = document.createElement( 'style' ); 1309 style.textContent = '@media (transform-3d),(-webkit-transform-3d){#test-3d{left:9px;position:absolute;height:5px;margin:0;padding:0;border:0;}}'; 1310 document.getElementsByTagName( 'head' )[0].appendChild( style ); 1311 1312 div.id = 'test-3d'; 1313 document.body.appendChild( div ); 1314 1315 if ( ! ( div.offsetLeft === 9 && div.offsetHeight === 5 ) ) { 1316 this.supportedAnimation = null; 1317 } 1318 1319 style.parentNode.removeChild( style ); 1320 div.parentNode.removeChild( div ); 1321 } 1322 1323 // If CSS 3D transforms are not supported, check if 2D transforms are supported 1324 if ( this.supportedAnimation === null && ( typeof div.style['-webkit-transform'] !== 'undefined' || typeof div.style.transform !== 'undefined' ) ) { 1325 this.supportedAnimation = 'css-2d'; 1326 } 1327 } else {
1328 this.supportedAnimation = 'javascript'; 1329 } 1330 1331 return this.supportedAnimation; 1332 }, 1333 1334 // Check what vendor prefix should be used in the current browser 1335 getVendorPrefix: function() { 1336 if ( this.vendorPrefix !== null ) { 1337 return this.vendorPrefix; 1338 } 1339 1340 var div = document.createElement( 'div' ), 1341 prefixes = [ 'Webkit', 'Moz', 'ms', 'O' ]; 1342 1343 if ( 'transform' in div.style ) { 1344 this.vendorPrefix = ''; 1345 return this.vendorPrefix; 1346 } 1347 1348 for ( var i = 0; i < prefixes.length; i++ ) { 1349 if ( ( prefixes[ i ] + 'Transform' ) in div.style ) { 1350 this.vendorPrefix = '-' + prefixes[ i ].toLowerCase() + '-'; 1351 break; 1352 } 1353 } 1354 1355 return this.vendorPrefix; 1356 }, 1357 1358 // Check the name of the transition's complete event in the current browser 1359 getTransitionEvent: function() { 1360 if ( this.transitionEvent !== null ) { 1361 return this.transitionEvent; 1362 } 1363 1364 var div = document.createElement( 'div' ), 1365 transitions = { 1366 'transition': 'transitionend', 1367 'WebkitTransition': 'webkitTransitionEnd', 1368 'MozTransition': 'transitionend', 1369 'OTransition': 'oTransitionEnd' 1370 }; 1371 1372 for ( var transition in transitions ) { 1373 if ( transition in div.style ) { 1374 this.transitionEvent = transitions[ transition ]; 1375 break; 1376 } 1377 } 1378 1379 return this.transitionEvent; 1380 }, 1381 1382 // If a single image is passed, check if it's loaded. 1383 // If a different element is passed, check if there are images 1384 // inside it, and check if these images are loaded. 1385 checkImagesComplete: function( target, callback ) { 1386 var that = this, 1387 1388 // Check the initial status of the image(s) 1389 status = this.checkImagesStatus( target ); 1390 1391 // If there are loading images, wait for them to load. 1392 // If the images are loaded, call the callback function directly. 1393 if ( status === 'loading' ) { 1394 var checkImages = setInterval(function() { 1395 status = that.checkImagesStatus( target ); 1396 1397 if ( status === 'complete' ) { 1398 clearInterval( checkImages ); 1399 1400 if ( typeof callback === 'function' ) { 1401 callback(); 1402 } 1403 } 1404 }, 100 ); 1405 } else if ( typeof callback === 'function' ) { 1406 callback(); 1407 } 1408 1409 return status; 1410 }, 1411 1412 checkImagesStatus: function( target ) { 1413 var status = 'complete'; 1414 1415 if ( target.is( 'img' ) && target[0].complete === false ) { 1416 status = 'loading'; 1417 } else { 1418 target.find( 'img' ).each(function( index ) { 1419 var image = $( this )[0]; 1420 1421 if ( image.complete === false ) { 1422 status = 'loading'; 1423 } 1424 }); 1425 } 1426 1427 return status; 1428 }, 1429 1430 checkIE: function() { 1431 if ( this.isIE !== null ) { 1432 return this.isIE; 1433 } 1434 1435 var userAgent = window.navigator.userAgent, 1436 msie = userAgent.indexOf( 'MSIE' ); 1437 1438 if ( userAgent.indexOf( 'MSIE' ) !== -1 || userAgent.match( /Trident.*rv\:11\./ ) ) { 1439 this.isIE = true; 1440 } else { 1441 this.isIE = false; 1442 } 1443 1444 return this.isIE; 1445 } 1446 }; 1447 1448 window.SliderProUtils = SliderProUtils; 1449 1450})( window, jQuery ); 1451 1452// Thumbnails module for Slider Pro. 1453// 1454// Adds the possibility to create a thumbnail scroller, each thumbnail 1455// corresponding to a slide. 1456;(function( window, $ ) { 1457 1458 "use strict"; 1459 1460 var NS = 'Thumbnails.' + $.SliderPro.namespace; 1461 1462 var Thumbnails = { 1463 1464 // Reference to the thumbnail scroller 1465 $thumbnails: null, 1466 1467 // Reference to the container of the thumbnail scroller 1468 $thumbnailsContainer: null, 1469 1470 // List of Thumbnail objects 1471 thumbnails: null, 1472 1473 // Index of the selected thumbnail 1474 selectedThumbnailIndex: 0, 1475 1476 // Total size (width or height, depending on the orientation) of the thumbnails 1477 thumbnailsSize: 0, 1478 1479 // Size of the thumbnail's container 1480 thumbnailsContainerSize: 0, 1481 1482 // The position of the thumbnail scroller inside its container 1483 thumbnailsPosition: 0, 1484 1485 // Orientation of the thumbnails 1486 thumbnailsOrientation: null, 1487 1488 // Indicates the 'left' or 'top' position based on the orientation of the thumbnails 1489 thumbnailsPositionProperty: null, 1490 1491 // Indicates if there are thumbnails in the slider 1492 isThumbnailScroller: false, 1493 1494 initThumbnails: function() { 1495 var that = this; 1496 1497 this.thumbnails = []; 1498 1499 this.on( 'update.' + NS, $.proxy( this._thumbnailsOnUpdate, this ) ); 1500 this.on( 'sliderResize.' + NS, $.proxy( this._thumbnailsOnResize, this ) );
1501 this.on( 'gotoSlide.' + NS, function( event ) { 1502 that._gotoThumbnail( event.index ); 1503 }); 1504 }, 1505 1506 // Called when the slider is updated 1507 _thumbnailsOnUpdate: function() { 1508 var that = this; 1509 1510 if ( this.$slider.find( '.sp-thumbnail' ).length === 0 && this.thumbnails.length === 0 ) { 1511 this.isThumbnailScroller = false; 1512 return; 1513 } 1514 1515 this.isThumbnailScroller = true; 1516 1517 // Create the container of the thumbnail scroller, if it wasn't created yet 1518 if ( this.$thumbnailsContainer === null ) { 1519 this.$thumbnailsContainer = $( '<div class="sp-thumbnails-container"></div>' ).insertAfter( this.$slidesContainer ); 1520 } 1521 1522 // If the thumbnails' main container doesn't exist, create it, and get a reference to it 1523 if ( this.$thumbnails === null ) { 1524 if ( this.$slider.find( '.sp-thumbnails' ).length !== 0 ) { 1525 this.$thumbnails = this.$slider.find( '.sp-thumbnails' ).appendTo( this.$thumbnailsContainer ); 1526 1527 // Shuffle/randomize the thumbnails 1528 if ( this.settings.shuffle === true ) { 1529 var thumbnails = this.$thumbnails.find( '.sp-thumbnail' ), 1530 shuffledThumbnails = []; 1531 1532 // Reposition the thumbnails based on the order of the indexes in the 1533 // 'shuffledIndexes' array 1534 $.each( this.shuffledIndexes, function( index, element ) { 1535 var $thumbnail = $( thumbnails[ element ] ); 1536 1537 if ( $thumbnail.parent( 'a' ).length !== 0 ) { 1538 $thumbnail = $thumbnail.parent( 'a' ); 1539 } 1540 1541 shuffledThumbnails.push( $thumbnail ); 1542 }); 1543 1544 // Append the sorted thumbnails to the thumbnail scroller 1545 this.$thumbnails.empty().append( shuffledThumbnails ) ; 1546 } 1547 } else { 1548 this.$thumbnails = $( '<div class="sp-thumbnails"></div>' ).appendTo( this.$thumbnailsContainer ); 1549 } 1550 } 1551 1552 // Check if there are thumbnails inside the slides and move them in the thumbnails container 1553 this.$slides.find( '.sp-thumbnail' ).each( function( index ) { 1554 var $thumbnail = $( this ), 1555 thumbnailIndex = $thumbnail.parents( '.sp-slide' ).index(), 1556 lastThumbnailIndex = that.$thumbnails.find( '.sp-thumbnail' ).length - 1; 1557 1558 if ( $thumbnail.parent( 'a' ).length !== 0 ) { 1559 $thumbnail = $thumbnail.parent( 'a' ); 1560 } 1561 1562 // If the index of the slide that contains the thumbnail is greater than the total number 1563 // of thumbnails from the thumbnails container, position the thumbnail at the end. 1564 // Otherwise, add the thumbnails at the corresponding position. 1565 if ( thumbnailIndex > lastThumbnailIndex ) { 1566 $thumbnail.appendTo( that.$thumbnails ); 1567 } else { 1568 $thumbnail.insertBefore( that.$thumbnails.find( '.sp-thumbnail' ).eq( thumbnailIndex ) ); 1569 } 1570 }); 1571 1572 // Loop through the Thumbnail objects and if a corresponding element is not found in the DOM, 1573 // it means that the thumbnail might have been removed. In this case, destroy that Thumbnail instance. 1574 for ( var i = this.thumbnails.length - 1; i >= 0; i-- ) { 1575 if ( this.$thumbnails.find( '.sp-thumbnail[data-index="' + i + '"]' ).length === 0 ) { 1576 var thumbnail = this.thumbnails[ i ]; 1577 1578 thumbnail.destroy(); 1579 this.thumbnails.splice( i, 1 ); 1580 } 1581 } 1582 1583 // Loop through the thumbnails and if there is any uninitialized thumbnail, 1584 // initialize it, else update the thumbnail's index. 1585 this.$thumbnails.find( '.sp-thumbnail' ).each(function( index ) { 1586 var $thumbnail = $( this ); 1587 1588 if ( typeof $thumbnail.attr( 'data-init' ) === 'undefined' ) { 1589 that._createThumbnail( $thumbnail, index ); 1590 } else { 1591 that.thumbnails[ index ].setIndex( index ); 1592 } 1593 }); 1594 1595 // Remove the previous class that corresponds to the position of the thumbnail scroller 1596 this.$thumbnailsContainer.removeClass( 'sp-top-thumbnails sp-bottom-thumbnails sp-left-thumbnails sp-right-thumbnails' ); 1597 1598 // Check the position of the thumbnail scroller and assign it the appropriate class and styling 1599 if ( this.settings.thumbnailsPosition === 'top' ) { 1600 this.$thumbnailsContainer.addClass( 'sp-top-thumbnails' ); 1601 this.thumbnailsOrientation = 'horizontal'; 1602 } else if ( this.settings.thumbnailsPosition === 'bottom' ) { 1603 this.$thumbnailsContainer.addClass( 'sp-bottom-thumbnails' ); 1604 this.thumbnailsOrientation = 'horizontal'; 1605 } else if ( this.settings.thumbnailsPosition === 'left' ) { 1606 this.$thumbnailsContainer.addClass( 'sp-left-thumbnails' ); 1607 this.thumbnailsOrientation = 'vertical'; 1608 } else if ( this.settings.thumbnailsPosition === 'right' ) { 1609 this.$thumbnailsContainer.addClass( 'sp-right-thumbnails' ); 1610 this.thumbnailsOrientation = 'vertical'; 1611 } 1612 1613 // Check if the pointer needs to be created 1614 if ( this.settings.thumbnailPointer === true ) { 1615 this.$thumbnailsContainer.addClass( 'sp-has-pointer' ); 1616 } else { 1617 this.$thumbnailsContainer.removeClass( 'sp-has-pointer' ); 1618 } 1619
1620 // Mark the thumbnail that corresponds to the selected slide 1621 this.selectedThumbnailIndex = this.selectedSlideIndex; 1622 this.$thumbnails.find( '.sp-thumbnail-container' ).eq( this.selectedThumbnailIndex ).addClass( 'sp-selected-thumbnail' ); 1623 1624 // Calculate the total size of the thumbnails 1625 this.thumbnailsSize = 0; 1626 1627 $.each( this.thumbnails, function( index, thumbnail ) { 1628 thumbnail.setSize( that.settings.thumbnailWidth, that.settings.thumbnailHeight ); 1629 that.thumbnailsSize += that.thumbnailsOrientation === 'horizontal' ? thumbnail.getSize().width : thumbnail.getSize().height; 1630 }); 1631 1632 // Set the size of the thumbnails 1633 if ( this.thumbnailsOrientation === 'horizontal' ) { 1634 this.$thumbnails.css({ 'width': this.thumbnailsSize, 'height': this.settings.thumbnailHeight }); 1635 this.$thumbnailsContainer.css( 'height', '' ); 1636 this.thumbnailsPositionProperty = 'left'; 1637 } else { 1638 this.$thumbnails.css({ 'width': this.settings.thumbnailWidth, 'height': this.thumbnailsSize }); 1639 this.$thumbnailsContainer.css( 'width', '' ); 1640 this.thumbnailsPositionProperty = 'top'; 1641 } 1642 1643 // Fire the 'thumbnailsUpdate' event 1644 this.trigger({ type: 'thumbnailsUpdate' }); 1645 if ( $.isFunction( this.settings.thumbnailsUpdate ) ) { 1646 this.settings.thumbnailsUpdate.call( this, { type: 'thumbnailsUpdate' } ); 1647 } 1648 }, 1649 1650 // Create an individual thumbnail 1651 _createThumbnail: function( element, index ) { 1652 var that = this, 1653 thumbnail = new Thumbnail( element, this.$thumbnails, index ); 1654 1655 // When the thumbnail is clicked, navigate to the corresponding slide 1656 thumbnail.on( 'thumbnailClick.' + NS, function( event ) { 1657 that.gotoSlide( event.index ); 1658 }); 1659 1660 // Add the thumbnail at the specified index 1661 this.thumbnails.splice( index, 0, thumbnail ); 1662 }, 1663 1664 // Called when the slider is resized. 1665 // Resets the size and position of the thumbnail scroller container. 1666 _thumbnailsOnResize: function() { 1667 if ( this.isThumbnailScroller === false ) { 1668 return; 1669 } 1670 1671 var that = this, 1672 newThumbnailsPosition; 1673 1674 if ( this.thumbnailsOrientation === 'horizontal' ) { 1675 this.thumbnailsContainerSize = Math.min( this.$slidesMask.width(), this.thumbnailsSize ); 1676 this.$thumbnailsContainer.css( 'width', this.thumbnailsContainerSize ); 1677 1678 // Reduce the slide mask's height, to make room for the thumbnails 1679 if ( this.settings.forceSize === 'fullWindow' ) { 1680 this.$slidesMask.css( 'height', this.$slidesMask.height() - this.$thumbnailsContainer.outerHeight( true ) ); 1681 1682 // Resize the slide 1683 this.slideHeight = this.$slidesMask.height(); 1684 1685 $.each( this.slides, function( index, element ) { 1686 element.setSize( that.slideWidth, that.slideHeight ); 1687 }); 1688 } 1689 } else if ( this.thumbnailsOrientation === 'vertical' ) { 1690 1691 // Check if the width of the slide mask plus the width of the thumbnail scroller is greater than 1692 // the width of the slider's container and if that's the case, reduce the slides container width 1693 // in order to make the entire slider fit inside the slider's container. 1694 if ( this.$slidesMask.width() + this.$thumbnailsContainer.outerWidth( true ) > this.$slider.parent().width() ) { 1695 // Reduce the slider's width, to make room for the thumbnails 1696 if ( this.settings.forceSize === 'fullWidth' || this.settings.forceSize === 'fullWindow' ) { 1697 this.$slider.css( 'max-width', $( window ).width() - this.$thumbnailsContainer.outerWidth( true ) ); 1698 } else { 1699 this.$slider.css( 'max-width', this.$slider.parent().width() - this.$thumbnailsContainer.outerWidth( true ) ); 1700 } 1701 1702 this.$slidesMask.css( 'width', this.$slider.width() ); 1703 1704 // If the slides are horizontally oriented, update the visible size and the offset 1705 // of the selected slide, since the slider's size was reduced to make room for the thumbnails. 1706 // 1707 // If the slides are vertically oriented, update the width and height (to maintain the aspect ratio) 1708 // of the slides. 1709 if ( this.settings.orientation === 'horizontal' ) { 1710 this.visibleOffset = Math.round( ( this.$slider.width() - this.slideSize ) / 2 ); 1711 this.visibleSlidesSize = this.$slidesMask.width(); 1712 } else if ( this.settings.orientation === 'vertical' ) { 1713 this.slideWidth = this.$slider.width(); 1714 1715 $.each( this.slides, function( index, element ) { 1716 element.setSize( that.slideWidth, that.slideHeight ); 1717 }); 1718 } 1719 1720 // Re-arrange the slides 1721 this._resetSlidesPosition(); 1722 } 1723 1724 this.thumbnailsContainerSize = Math.min( this.$slidesMask.height(), this.thumbnailsSize ); 1725 this.$thumbnailsContainer.css( 'height', this.thumbnailsContainerSize ); 1726 } 1727 1728 // If the total size of the thumbnails is smaller than the thumbnail scroller' container (which has 1729 // the same size as the slides container), it means that all the thumbnails will be visible, so set 1730 // the position of the thumbnail scroller to 0. 1731 // 1732 // If that's not the case, the thumbnail scroller will be positioned based on which thumbnail is selected. 1733 if ( this.thumbnailsSize <= this.thumbnailsContainerSize || this.$thumbnails.find( '.sp-selected-thumbnail' ).length === 0 ) { 1734 newThumbnailsPosition = 0; 1735 } else { 1736 newThumbnailsPosition = Math.max( - this.thumbnails[ this.selectedThumbnailIndex ].getPosition()[ this.thumbnailsPositionProperty ], this.thumbnailsContainerSize - this.thumbnailsSize ); 1737 } 1738 1739 // Add a padding to the slider, based on the thumbnail scroller's orientation, to make room 1740 // for the thumbnails. 1741 if ( this.settings.thumbnailsPosition === 'top' ) { 1742 this.$slider.css({ 'paddingTop': this.$thumbnailsContainer.outerHeight( true ), 'paddingLeft': '', 'paddingRight': '' }); 1743 } else if ( this.settings.thumbnailsPosition === 'bottom' ) { 1744 this.$slider.css({ 'paddingTop': '', 'paddingLeft': '', 'paddingRight': '' }); 1745 } else if ( this.settings.thumbnailsPosition === 'left' ) { 1746 this.$slider.css({ 'paddingTop': '', 'paddingLeft': this.$thumbnailsContainer.outerWidth( true ), 'paddingRight': '' }); 1747 } else if ( this.settings.thumbnailsPosition === 'right' ) { 1748 this.$slider.css({ 'paddingTop': '', 'paddingLeft': '', 'paddingRight': this.$thumbnailsContainer.outerWidth( true ) }); 1749 } 1750
1751 this._moveThumbnailsTo( newThumbnailsPosition, true ); 1752 }, 1753 1754 // Selects the thumbnail at the indicated index and moves the thumbnail scroller 1755 // accordingly. 1756 _gotoThumbnail: function( index ) { 1757 if ( this.isThumbnailScroller === false || typeof this.thumbnails[ index ] === 'undefined' ) { 1758 return; 1759 } 1760 1761 var previousIndex = this.selectedThumbnailIndex, 1762 newThumbnailsPosition = this.thumbnailsPosition; 1763 1764 this.selectedThumbnailIndex = index; 1765 1766 // Set the 'selected' class to the appropriate thumbnail 1767 this.$thumbnails.find( '.sp-selected-thumbnail' ).removeClass( 'sp-selected-thumbnail' ); 1768 this.$thumbnails.find( '.sp-thumbnail-container' ).eq( this.selectedThumbnailIndex ).addClass( 'sp-selected-thumbnail' ); 1769 1770 // Calculate the new position that the thumbnail scroller needs to go to. 1771 // 1772 // If the selected thumbnail has a higher index than the previous one, make sure that the thumbnail 1773 // that comes after the selected thumbnail will be visible, if the selected thumbnail is not the 1774 // last thumbnail in the list. 1775 // 1776 // If the selected thumbnail has a lower index than the previous one, make sure that the thumbnail 1777 // that's before the selected thumbnail will be visible, if the selected thumbnail is not the 1778 // first thumbnail in the list. 1779 if ( this.selectedThumbnailIndex >= previousIndex ) { 1780 var nextThumbnailIndex = this.selectedThumbnailIndex === this.thumbnails.length - 1 ? this.selectedThumbnailIndex : this.selectedThumbnailIndex + 1, 1781 nextThumbnail = this.thumbnails[ nextThumbnailIndex ], 1782 nextThumbnailPosition = this.thumbnailsOrientation === 'horizontal' ? nextThumbnail.getPosition().right : nextThumbnail.getPosition().bottom, 1783 thumbnailsRightPosition = - this.thumbnailsPosition + this.thumbnailsContainerSize; 1784 1785 if ( nextThumbnailPosition > thumbnailsRightPosition ) { 1786 newThumbnailsPosition = this.thumbnailsPosition - ( nextThumbnailPosition - thumbnailsRightPosition ); 1787 } 1788 } else if ( this.selectedThumbnailIndex < previousIndex ) { 1789 var previousThumbnailIndex = this.selectedThumbnailIndex === 0 ? this.selectedThumbnailIndex : this.selectedThumbnailIndex - 1, 1790 previousThumbnail = this.thumbnails[ previousThumbnailIndex ], 1791 previousThumbnailPosition = this.thumbnailsOrientation === 'horizontal' ? previousThumbnail.getPosition().left : previousThumbnail.getPosition().top; 1792 1793 if ( previousThumbnailPosition < - this.thumbnailsPosition ) { 1794 newThumbnailsPosition = - previousThumbnailPosition; 1795 } 1796 } 1797 1798 // Move the thumbnail scroller to the calculated position 1799 this._moveThumbnailsTo( newThumbnailsPosition ); 1800 1801 // Fire the 'gotoThumbnail' event 1802 this.trigger({ type: 'gotoThumbnail' }); 1803 if ( $.isFunction( this.settings.gotoThumbnail ) ) { 1804 this.settings.gotoThumbnail.call( this, { type: 'gotoThumbnail' }); 1805 } 1806 }, 1807 1808 // Move the thumbnail scroller to the indicated position 1809 _moveThumbnailsTo: function( position, instant, callback ) { 1810 var that = this, 1811 css = {}; 1812 1813 // Return if the position hasn't changed 1814 if ( position === this.thumbnailsPosition ) { 1815 return; 1816 } 1817 1818 this.thumbnailsPosition = position; 1819 1820 // Use CSS transitions if they are supported. If not, use JavaScript animation 1821 if ( this.supportedAnimation === 'css-3d' || this.supportedAnimation === 'css-2d' ) { 1822 var transition, 1823 left = this.thumbnailsOrientation === 'horizontal' ? position : 0, 1824 top = this.thumbnailsOrientation === 'horizontal' ? 0 : position; 1825 1826 if ( this.supportedAnimation === 'css-3d' ) { 1827 css[ this.vendorPrefix + 'transform' ] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; 1828 } else { 1829 css[ this.vendorPrefix + 'transform' ] = 'translate(' + left + 'px, ' + top + 'px)'; 1830 } 1831 1832 if ( typeof instant !== 'undefined' && instant === true ) { 1833 transition = ''; 1834 } else { 1835 this.$thumbnails.addClass( 'sp-animated' ); 1836 transition = this.vendorPrefix + 'transform ' + 700 / 1000 + 's'; 1837 1838 this.$thumbnails.on( this.transitionEvent, function( event ) { 1839 if ( event.target !== event.currentTarget ) { 1840 return; 1841 } 1842 1843 that.$thumbnails.off( that.transitionEvent ); 1844 that.$thumbnails.removeClass( 'sp-animated' ); 1845 1846 if ( typeof callback === 'function' ) { 1847 callback(); 1848 } 1849 1850 // Fire the 'thumbnailsMoveComplete' event 1851 that.trigger({ type: 'thumbnailsMoveComplete' }); 1852 if ( $.isFunction( that.settings.thumbnailsMoveComplete ) ) { 1853 that.settings.thumbnailsMoveComplete.call( that, { type: 'thumbnailsMoveComplete' }); 1854 } 1855 }); 1856 } 1857 1858 css[ this.vendorPrefix + 'transition' ] = transition; 1859 1860 this.$thumbnails.css( css ); 1861 } else { 1862 css[ 'margin-' + this.thumbnailsPositionProperty ] = position; 1863 1864 if ( typeof instant !== 'undefined' && instant === true ) { 1865 this.$thumbnails.css( css ); 1866 } else { 1867 this.$thumbnails 1868 .addClass( 'sp-animated' ) 1869 .animate( css, 700, function() { 1870 that.$thumbnails.removeClass( 'sp-animated' ); 1871 1872 if ( typeof callback === 'function' ) { 1873 callback(); 1874 } 1875 1876 // Fire the 'thumbnailsMoveComplete' event 1877 that.trigger({ type: 'thumbnailsMoveComplete' }); 1878 if ( $.isFunction( that.settings.thumbnailsMoveComplete ) ) { 1879 that.settings.thumbnailsMoveComplete.call( that, { type: 'thumbnailsMoveComplete' }); 1880 } 1881 }); 1882 } 1883 } 1884 }, 1885 1886 // Stop the movement of the thumbnail scroller 1887 _stopThumbnailsMovement: function() { 1888 var css = {}; 1889 1890 if ( this.supportedAnimation === 'css-3d' || this.supportedAnimation === 'css-2d' ) { 1891 var matrixString = this.$thumbnails.css( this.vendorPrefix + 'transform' ), 1892 matrixType = matrixString.indexOf( 'matrix3d' ) !== -1 ? 'matrix3d' : 'matrix', 1893 matrixArray = matrixString.replace( matrixType, '' ).match( /-?[0-9\.]+/g ), 1894 left = matrixType === 'matrix3d' ? parseInt( matrixArray[ 12 ], 10 ) : parseInt( matrixArray[ 4 ], 10 ), 1895 top = matrixType === 'matrix3d' ? parseInt( matrixArray[ 13 ], 10 ) : parseInt( matrixArray[ 5 ], 10 ); 1896 1897 if ( this.supportedAnimation === 'css-3d' ) { 1898 css[ this.vendorPrefix + 'transform' ] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; 1899 } else { 1900 css[ this.vendorPrefix + 'transform' ] = 'translate(' + left + 'px, ' + top + 'px)'; 1901 } 1902 1903 css[ this.vendorPrefix + 'transition' ] = ''; 1904 1905 this.$thumbnails.css( css ); 1906 this.$thumbnails.off( this.transitionEvent ); 1907 this.thumbnailsPosition = this.thumbnailsOrientation === 'horizontal' ? parseInt( matrixArray[ 4 ] , 10 ) : parseInt( matrixArray[ 5 ] , 10 ); 1908 } else { 1909 this.$thumbnails.stop(); 1910 this.thumbnailsPosition = parseInt( this.$thumbnails.css( 'margin-' + this.thumbnailsPositionProperty ), 10 ); 1911 } 1912 1913 this.$thumbnails.removeClass( 'sp-animated' ); 1914 }, 1915 1916 // Destroy the module 1917 destroyThumbnails: function() { 1918 var that = this; 1919 1920 // Remove event listeners 1921 this.off( 'update.' + NS ); 1922 1923 if ( this.isThumbnailScroller === false ) { 1924 return; 1925 } 1926 1927 this.off( 'sliderResize.' + NS );
1928 this.off( 'gotoSlide.' + NS ); 1929 $( window ).off( 'resize.' + this.uniqueId + '.' + NS ); 1930 1931 // Destroy the individual thumbnails 1932 this.$thumbnails.find( '.sp-thumbnail' ).each( function() { 1933 var $thumbnail = $( this ), 1934 index = parseInt( $thumbnail.attr( 'data-index' ), 10 ), 1935 thumbnail = that.thumbnails[ index ]; 1936 1937 thumbnail.off( 'thumbnailClick.' + NS ); 1938 thumbnail.destroy(); 1939 }); 1940 1941 this.thumbnails.length = 0; 1942 1943 // Add the thumbnail scroller directly in the slider and 1944 // remove the thumbnail scroller container 1945 this.$thumbnails.appendTo( this.$slider ); 1946 this.$thumbnailsContainer.remove(); 1947 1948 // Remove any created padding 1949 this.$slider.css({ 'paddingTop': '', 'paddingLeft': '', 'paddingRight': '' }); 1950 }, 1951 1952 thumbnailsDefaults: { 1953 1954 // Sets the width of the thumbnail 1955 thumbnailWidth: 100, 1956 1957 // Sets the height of the thumbnail 1958 thumbnailHeight: 80, 1959 1960 // Sets the position of the thumbnail scroller (top, bottom, right, left) 1961 thumbnailsPosition: 'bottom', 1962 1963 // Indicates if a pointer will be displayed for the selected thumbnail 1964 thumbnailPointer: false, 1965 1966 // Called when the thumbnails are updated 1967 thumbnailsUpdate: function() {}, 1968 1969 // Called when a new thumbnail is selected 1970 gotoThumbnail: function() {}, 1971 1972 // Called when the thumbnail scroller has moved 1973 thumbnailsMoveComplete: function() {} 1974 } 1975 }; 1976 1977 var Thumbnail = function( thumbnail, thumbnails, index ) { 1978 1979 // Reference to the thumbnail jQuery element 1980 this.$thumbnail = thumbnail; 1981 1982 // Reference to the thumbnail scroller 1983 this.$thumbnails = thumbnails; 1984 1985 // Reference to the thumbnail's container, which will be 1986 // created dynamically. 1987 this.$thumbnailContainer = null; 1988 1989 // The width and height of the thumbnail 1990 this.width = 0; 1991 this.height = 0; 1992 1993 // Indicates whether the thumbnail's image is loaded 1994 this.isImageLoaded = false; 1995 1996 // Set the index of the slide 1997 this.setIndex( index ); 1998 1999 // Initialize the thumbnail 2000 this._init(); 2001 }; 2002 2003 Thumbnail.prototype = { 2004 2005 _init: function() { 2006 var that = this; 2007 2008 // Mark the thumbnail as initialized 2009 this.$thumbnail.attr( 'data-init', true ); 2010 2011 // Create a container for the thumbnail and add the original thumbnail to this container. 2012 // Having a container will help crop the thumbnail image if it's too large. 2013 this.$thumbnailContainer = $( '<div class="sp-thumbnail-container"></div>' ).appendTo( this.$thumbnails ); 2014 2015 if ( this.$thumbnail.parent( 'a' ).length !== 0 ) { 2016 this.$thumbnail.parent( 'a' ).appendTo( this.$thumbnailContainer ); 2017 } else { 2018 this.$thumbnail.appendTo( this.$thumbnailContainer ); 2019 } 2020 2021 // When the thumbnail container is clicked, fire an event 2022 this.$thumbnailContainer.on( 'click.' + NS, function() { 2023 that.trigger({ type: 'thumbnailClick.' + NS, index: that.index }); 2024 }); 2025 }, 2026 2027 // Set the width and height of the thumbnail 2028 setSize: function( width, height ) { 2029 this.width = width; 2030 this.height = height; 2031 2032 // Apply the width and height to the thumbnail's container 2033 this.$thumbnailContainer.css({ 'width': this.width, 'height': this.height }); 2034 2035 // If there is an image, resize it to fit the thumbnail container 2036 if ( this.$thumbnail.is( 'img' ) && typeof this.$thumbnail.attr( 'data-src' ) === 'undefined' ) { 2037 this.resizeImage(); 2038 } 2039 }, 2040 2041 // Return the width and height of the thumbnail 2042 getSize: function() { 2043 return { 2044 width: this.$thumbnailContainer.outerWidth( true ), 2045 height: this.$thumbnailContainer.outerHeight( true ) 2046 }; 2047 }, 2048 2049 // Return the top, bottom, left and right position of the thumbnail 2050 getPosition: function() { 2051 return { 2052 left: this.$thumbnailContainer.position().left + parseInt( this.$thumbnailContainer.css( 'marginLeft' ) , 10 ), 2053 right: this.$thumbnailContainer.position().left + parseInt( this.$thumbnailContainer.css( 'marginLeft' ) , 10 ) + this.$thumbnailContainer.outerWidth(), 2054 top: this.$thumbnailContainer.position().top + parseInt( this.$thumbnailContainer.css( 'marginTop' ) , 10 ), 2055 bottom: this.$thumbnailContainer.position().top + parseInt( this.$thumbnailContainer.css( 'marginTop' ) , 10 ) + this.$thumbnailContainer.outerHeight() 2056 }; 2057 }, 2058 2059 // Set the index of the thumbnail 2060 setIndex: function( index ) { 2061 this.index = index; 2062 this.$thumbnail.attr( 'data-index', this.index ); 2063 }, 2064 2065 // Resize the thumbnail's image 2066 resizeImage: function() { 2067 var that = this; 2068 2069 // If the image is not loaded yet, load it 2070 if ( this.isImageLoaded === false ) { 2071 SliderProUtils.checkImagesComplete( this.$thumbnailContainer , function() { 2072 that.isImageLoaded = true; 2073 that.resizeImage(); 2074 }); 2075 2076 return; 2077 } 2078 2079 // Get the reference to the thumbnail image again because it was replaced by 2080 // another img element during the loading process
2081 this.$thumbnail = this.$thumbnailContainer.find( '.sp-thumbnail' ); 2082 2083 // Calculate whether the image should stretch horizontally or vertically 2084 var imageWidth = this.$thumbnail.width(), 2085 imageHeight = this.$thumbnail.height(); 2086 2087 if ( imageWidth / imageHeight <= this.width / this.height ) { 2088 this.$thumbnail.css({ width: '100%', height: 'auto' }); 2089 } else { 2090 this.$thumbnail.css({ width: 'auto', height: '100%' }); 2091 } 2092 2093 this.$thumbnail.css({ 'marginLeft': ( this.$thumbnailContainer.width() - this.$thumbnail.width() ) * 0.5, 'marginTop': ( this.$thumbnailContainer.height() - this.$thumbnail.height() ) * 0.5 }); 2094 }, 2095 2096 // Destroy the thumbnail 2097 destroy: function() { 2098 this.$thumbnailContainer.off( 'click.' + NS ); 2099 2100 // Remove added attributes 2101 this.$thumbnail.removeAttr( 'data-init' ); 2102 this.$thumbnail.removeAttr( 'data-index' ); 2103 2104 // Remove the thumbnail's container and add the thumbnail 2105 // back to the thumbnail scroller container 2106 if ( this.$thumbnail.parent( 'a' ).length !== 0 ) { 2107 this.$thumbnail.parent( 'a' ).insertBefore( this.$thumbnailContainer ); 2108 } else { 2109 this.$thumbnail.insertBefore( this.$thumbnailContainer ); 2110 } 2111 2112 this.$thumbnailContainer.remove(); 2113 }, 2114 2115 // Attach an event handler to the slide 2116 on: function( type, callback ) { 2117 return this.$thumbnailContainer.on( type, callback ); 2118 }, 2119 2120 // Detach an event handler to the slide 2121 off: function( type ) { 2122 return this.$thumbnailContainer.off( type ); 2123 }, 2124 2125 // Trigger an event on the slide 2126 trigger: function( data ) { 2127 return this.$thumbnailContainer.triggerHandler( data ); 2128 } 2129 }; 2130 2131 $.SliderPro.addModule( 'Thumbnails', Thumbnails ); 2132 2133})( window, jQuery ); 2134 2135// ConditionalImages module for Slider Pro. 2136// 2137// Adds the possibility to specify multiple sources for each image and 2138// load the image that's the most appropriate for the size of the slider. 2139// For example, instead of loading a large image even if the slider will be small 2140// you can specify a smaller image that will be loaded instead. 2141;(function( window, $ ) { 2142 2143 "use strict"; 2144 2145 var NS = 'ConditionalImages.' + $.SliderPro.namespace; 2146 2147 var ConditionalImages = { 2148 2149 // Reference to the previous size 2150 previousImageSize: null, 2151 2152 // Reference to the current size 2153 currentImageSize: null, 2154 2155 // Indicates if the current display supports high PPI 2156 isRetinaScreen: false, 2157 2158 initConditionalImages: function() { 2159 this.currentImageSize = this.previousImageSize = 'default'; 2160 this.isRetinaScreen = ( typeof this._isRetina !== 'undefined' ) && ( this._isRetina() === true ); 2161 2162 this.on( 'update.' + NS, $.proxy( this._conditionalImagesOnUpdate, this ) ); 2163 this.on( 'sliderResize.' + NS, $.proxy( this._conditionalImagesOnResize, this ) ); 2164 }, 2165 2166 // Loop through all the existing images and specify the original path of the image 2167 // inside the 'data-default' attribute. 2168 _conditionalImagesOnUpdate: function() { 2169 $.each( this.slides, function( index, element ) { 2170 var $slide = element.$slide; 2171 2172 $slide.find( 'img:not([ data-default ])' ).each(function() { 2173 var $image = $( this ); 2174 2175 if ( typeof $image.attr( 'data-src' ) !== 'undefined' ) { 2176 $image.attr( 'data-default', $image.attr( 'data-src' ) ); 2177 } else { 2178 $image.attr( 'data-default', $image.attr( 'src' ) ); 2179 } 2180 }); 2181 }); 2182 }, 2183 2184 // When the window resizes, identify the applyable image size based on the current size of the slider 2185 // and apply it to all images that have a version of the image specified for this size. 2186 _conditionalImagesOnResize: function() { 2187 if ( this.slideWidth <= this.settings.smallSize ) { 2188 this.currentImageSize = 'small'; 2189 } else if ( this.slideWidth <= this.settings.mediumSize ) { 2190 this.currentImageSize = 'medium'; 2191 } else if ( this.slideWidth <= this.settings.largeSize ) { 2192 this.currentImageSize = 'large'; 2193 } else { 2194 this.currentImageSize = 'default'; 2195 } 2196 2197 if ( this.previousImageSize !== this.currentImageSize ) { 2198 var that = this; 2199 2200 $.each( this.slides, function( index, element ) { 2201 var $slide = element.$slide; 2202
2203 $slide.find( 'img' ).each(function() { 2204 var $image = $( this ), 2205 imageSource = ''; 2206 2207 // Check if the current display supports high PPI and if a retina version of the current size was specified 2208 if ( that.isRetinaScreen === true && typeof $image.attr( 'data-retina' + that.currentImageSize ) !== 'undefined' ) { 2209 imageSource = $image.attr( 'data-retina' + that.currentImageSize ); 2210 2211 // If the retina image was not loaded yet, replace the default image source with the one 2212 // that corresponds to the current slider size 2213 if ( typeof $image.attr( 'data-retina' ) !== 'undefined' && $image.attr( 'data-retina' ) !== imageSource ) { 2214 $image.attr( 'data-retina', imageSource ); 2215 } 2216 } else if ( ( that.isRetinaScreen === false || that.isRetinaScreen === true && typeof $image.attr( 'data-retina' ) === 'undefined' ) && typeof $image.attr( 'data-' + that.currentImageSize ) !== 'undefined' ) { 2217 imageSource = $image.attr( 'data-' + that.currentImageSize ); 2218 2219 // If the image is set to lazy load, replace the image source with the one 2220 // that corresponds to the current slider size 2221 if ( typeof $image.attr( 'data-src' ) !== 'undefined' && $image.attr( 'data-src' ) !== imageSource ) { 2222 $image.attr( 'data-src', imageSource ); 2223 } 2224 } 2225 2226 // If a new image was found 2227 if ( imageSource !== '' ) { 2228 2229 // The existence of the 'data-src' attribute indicates that the image 2230 // will be lazy loaded, so don't load the new image yet 2231 if ( typeof $image.attr( 'data-src' ) === 'undefined' && $image.attr( 'src' ) !== imageSource ) { 2232 that._loadConditionalImage( $image, imageSource, function( newImage ) { 2233 if ( newImage.hasClass( 'sp-image' ) ) { 2234 element.$mainImage = newImage; 2235 element.resizeMainImage( true ); 2236 } 2237 }); 2238 } 2239 } 2240 }); 2241 }); 2242 2243 this.previousImageSize = this.currentImageSize; 2244 } 2245 }, 2246 2247 // Replace the target image with a new image 2248 _loadConditionalImage: function( image, source, callback ) { 2249 2250 // Create a new image element 2251 var newImage = $( new Image() ); 2252 2253 // Copy the class(es) and inline style 2254 newImage.attr( 'class', image.attr( 'class' ) ); 2255 newImage.attr( 'style', image.attr( 'style' ) ); 2256 2257 // Copy the data attributes 2258 $.each( image.data(), function( name, value ) { 2259 newImage.attr( 'data-' + name, value ); 2260 }); 2261 2262 // Copy the width and height attributes if they exist 2263 if ( typeof image.attr( 'width' ) !== 'undefined') { 2264 newImage.attr( 'width', image.attr( 'width' ) ); 2265 } 2266 2267 if ( typeof image.attr( 'height' ) !== 'undefined') { 2268 newImage.attr( 'height', image.attr( 'height' ) ); 2269 } 2270 2271 if ( typeof image.attr( 'alt' ) !== 'undefined' ) { 2272 newImage.attr( 'alt', image.attr( 'alt' ) ); 2273 } 2274 2275 if ( typeof image.attr( 'title' ) !== 'undefined' ) { 2276 newImage.attr( 'title', image.attr( 'title' ) ); 2277 } 2278 2279 newImage.attr( 'src', source ); 2280 2281 // Add the new image in the same container and remove the older image 2282 newImage.insertAfter( image ); 2283 image.remove(); 2284 image = null; 2285 2286 if ( typeof callback === 'function' ) { 2287 callback( newImage ); 2288 } 2289 }, 2290 2291 // Destroy the module 2292 destroyConditionalImages: function() { 2293 this.off( 'update.' + NS ); 2294 this.off( 'sliderResize.' + NS ); 2295 }, 2296 2297 conditionalImagesDefaults: { 2298 2299 // If the slider size is below this size, the small version of the images will be used 2300 smallSize: 480, 2301 2302 // If the slider size is below this size, the small version of the images will be used 2303 mediumSize: 768, 2304 2305 // If the slider size is below this size, the small version of the images will be used 2306 largeSize: 1024 2307 } 2308 }; 2309 2310 $.SliderPro.addModule( 'ConditionalImages', ConditionalImages ); 2311 2312})( window, jQuery ); 2313 2314// Retina module for Slider Pro. 2315// 2316// Adds the possibility to load a different image when the slider is 2317// viewed on a retina screen. 2318;(function( window, $ ) { 2319 2320 "use strict"; 2321 2322 var NS = 'Retina.' + $.SliderPro.namespace; 2323 2324 var Retina = { 2325 2326 initRetina: function() { 2327 var that = this; 2328 2329 // Return if it's not a retina screen 2330 if ( this._isRetina() === false ) { 2331 return; 2332 } 2333 2334 this.on( 'update.' + NS, $.proxy( this._checkRetinaImages, this ) ); 2335
2336 if ( this.$slider.find( '.sp-thumbnail' ).length !== 0 ) { 2337 this.on( 'update.Thumbnails.' + NS, $.proxy( this._checkRetinaThumbnailImages, this ) ); 2338 } 2339 }, 2340 2341 // Checks if the current display supports high PPI 2342 _isRetina: function() { 2343 if ( window.devicePixelRatio >= 2 ) { 2344 return true; 2345 } 2346 2347 if ( window.matchMedia && ( window.matchMedia( "(-webkit-min-device-pixel-ratio: 2),(min-resolution: 2dppx)" ).matches ) ) { 2348 return true; 2349 } 2350 2351 return false; 2352 }, 2353 2354 // Loop through the slides and replace the images with their retina version 2355 _checkRetinaImages: function() { 2356 var that = this; 2357 2358 $.each( this.slides, function( index, element ) { 2359 var $slide = element.$slide; 2360 2361 if ( typeof $slide.attr( 'data-retina-loaded' ) === 'undefined' ) { 2362 $slide.attr( 'data-retina-loaded', true ); 2363 2364 $slide.find( 'img[data-retina]' ).each(function() { 2365 var $image = $( this ); 2366 2367 if ( typeof $image.attr( 'data-src' ) !== 'undefined' ) { 2368 $image.attr( 'data-src', $image.attr( 'data-retina' ) ); 2369 } else { 2370 that._loadRetinaImage( $image, function( newImage ) { 2371 if ( newImage.hasClass( 'sp-image' ) ) { 2372 element.$mainImage = newImage; 2373 element.resizeMainImage( true ); 2374 } 2375 }); 2376 } 2377 }); 2378 } 2379 }); 2380 }, 2381 2382 // Loop through the thumbnails and replace the images with their retina version 2383 _checkRetinaThumbnailImages: function() { 2384 var that = this; 2385 2386 $.each( this.thumbnails, function( index, element ) { 2387 var $thumbnail = element.$thumbnailContainer; 2388 2389 if ( typeof $thumbnail.attr( 'data-retina-loaded' ) === 'undefined' ) { 2390 $thumbnail.attr( 'data-retina-loaded', true ); 2391 2392 $thumbnail.find( 'img[data-retina]' ).each(function() { 2393 var $image = $( this ); 2394 2395 if ( typeof $image.attr( 'data-src' ) !== 'undefined' ) { 2396 $image.attr( 'data-src', $image.attr( 'data-retina' ) ); 2397 } else { 2398 that._loadRetinaImage( $image, function( newImage ) { 2399 if ( newImage.hasClass( 'sp-thumbnail' ) ) { 2400 element.resizeImage(); 2401 } 2402 }); 2403 } 2404 }); 2405 } 2406 }); 2407 }, 2408 2409 // Load the retina image 2410 _loadRetinaImage: function( image, callback ) { 2411 var retinaFound = false, 2412 newImagePath = ''; 2413 2414 // Check if there is a retina image specified 2415 if ( typeof image.attr( 'data-retina' ) !== 'undefined' ) { 2416 retinaFound = true; 2417 2418 newImagePath = image.attr( 'data-retina' ); 2419 } 2420 2421 // Check if there is a lazy loaded, non-retina, image specified 2422 if ( typeof image.attr( 'data-src' ) !== 'undefined' ) { 2423 if ( retinaFound === false ) { 2424 newImagePath = image.attr( 'data-src') ; 2425 } 2426 2427 image.removeAttr('data-src'); 2428 } 2429 2430 // Return if there isn't a retina or lazy loaded image 2431 if ( newImagePath === '' ) { 2432 return; 2433 } 2434 2435 // Create a new image element 2436 var newImage = $( new Image() ); 2437 2438 // Copy the class(es) and inline style 2439 newImage.attr( 'class', image.attr('class') ); 2440 newImage.attr( 'style', image.attr('style') ); 2441 2442 // Copy the data attributes 2443 $.each( image.data(), function( name, value ) { 2444 newImage.attr( 'data-' + name, value ); 2445 }); 2446 2447 // Copy the width and height attributes if they exist 2448 if ( typeof image.attr( 'width' ) !== 'undefined' ) { 2449 newImage.attr( 'width', image.attr( 'width' ) ); 2450 } 2451 2452 if ( typeof image.attr( 'height' ) !== 'undefined' ) { 2453 newImage.attr( 'height', image.attr( 'height' ) ); 2454 } 2455 2456 if ( typeof image.attr( 'alt' ) !== 'undefined' ) { 2457 newImage.attr( 'alt', image.attr( 'alt' ) ); 2458 } 2459 2460 if ( typeof image.attr( 'title' ) !== 'undefined' ) { 2461 newImage.attr( 'title', image.attr( 'title' ) ); 2462 } 2463 2464 // Add the new image in the same container and remove the older image 2465 newImage.insertAfter( image ); 2466 image.remove(); 2467 image = null; 2468 2469 // Assign the source of the image 2470 newImage.attr( 'src', newImagePath ); 2471 2472 if ( typeof callback === 'function' ) { 2473 callback( newImage ); 2474 } 2475 }, 2476 2477 // Destroy the module 2478 destroyRetina: function() { 2479 this.off( 'update.' + NS ); 2480 this.off( 'update.Thumbnails.' + NS ); 2481 } 2482 }; 2483 2484 $.SliderPro.addModule( 'Retina', Retina ); 2485 2486})( window, jQuery ); 2487 2488// Lazy Loading module for Slider Pro. 2489// 2490// Adds the possibility to delay the loading of the images until the slides/thumbnails 2491// that contain them become visible. This technique improves the initial loading 2492// performance. 2493;(function( window, $ ) { 2494 2495 "use strict"; 2496 2497 var NS = 'LazyLoading.' + $.SliderPro.namespace; 2498 2499 var LazyLoading = { 2500 2501 allowLazyLoadingCheck: true, 2502 2503 initLazyLoading: function() { 2504 var that = this; 2505 2506 // The 'resize' event is fired after every update, so it's possible to use it for checking 2507 // if the update made new slides become visible 2508 // 2509 // Also, resizing the slider might make new slides or thumbnails visible 2510 this.on( 'sliderResize.' + NS, $.proxy( this._lazyLoadingOnResize, this ) ); 2511 2512 // Check visible images when a new slide is selected
2513 this.on( 'gotoSlide.' + NS, $.proxy( this._checkAndLoadVisibleImages, this ) ); 2514 2515 // Check visible thumbnail images when the thumbnails are updated because new thumbnail 2516 // might have been added or the settings might have been changed so that more thumbnail 2517 // images become visible 2518 // 2519 // Also, check visible thumbnail images after the thumbnails have moved because new thumbnails might 2520 // have become visible 2521 this.on( 'thumbnailsUpdate.' + NS + ' ' + 'thumbnailsMoveComplete.' + NS, $.proxy( this._checkAndLoadVisibleThumbnailImages, this ) ); 2522 }, 2523 2524 _lazyLoadingOnResize: function() { 2525 var that = this; 2526 2527 if ( this.allowLazyLoadingCheck === false ) { 2528 return; 2529 } 2530 2531 this.allowLazyLoadingCheck = false; 2532 2533 this._checkAndLoadVisibleImages(); 2534 2535 if ( this.$slider.find( '.sp-thumbnail' ).length !== 0 ) { 2536 this._checkAndLoadVisibleThumbnailImages(); 2537 } 2538 2539 // Use a timer to deffer the loading of images in order to prevent too many 2540 // checking attempts 2541 setTimeout(function() { 2542 that.allowLazyLoadingCheck = true; 2543 }, 500 ); 2544 }, 2545 2546 // Check visible slides and load their images 2547 _checkAndLoadVisibleImages: function() { 2548 if ( this.$slider.find( '.sp-slide:not([ data-loaded ])' ).length === 0 ) { 2549 return; 2550 } 2551 2552 var that = this, 2553 2554 // Use either the middle position or the index of the selected slide as a reference, depending on 2555 // whether the slider is loopable 2556 referencePosition = this.settings.loop === true ? this.middleSlidePosition : this.selectedSlideIndex, 2557 2558 // Calculate how many slides are visible at the sides of the selected slide 2559 visibleOnSides = Math.ceil( ( this.visibleSlidesSize - this.slideSize ) / 2 / this.slideSize ), 2560 2561 // Calculate the indexes of the first and last slide that will be checked 2562 from = referencePosition - visibleOnSides - 1 > 0 ? referencePosition - visibleOnSides - 1 : 0, 2563 to = referencePosition + visibleOnSides + 1 < this.getTotalSlides() - 1 ? referencePosition + visibleOnSides + 1 : this.getTotalSlides() - 1, 2564 2565 // Get all the slides that need to be checked 2566 slidesToCheck = this.slidesOrder.slice( from, to + 1 ); 2567 2568 // Loop through the selected slides and if the slide is not marked as having 2569 // been loaded yet, loop through its images and load them. 2570 $.each( slidesToCheck, function( index, element ) { 2571 var slide = that.slides[ element ], 2572 $slide = slide.$slide; 2573 2574 if ( typeof $slide.attr( 'data-loaded' ) === 'undefined' ) { 2575 $slide.attr( 'data-loaded', true ); 2576 2577 $slide.find( 'img[ data-src ]' ).each(function() { 2578 var image = $( this ); 2579 that._loadImage( image, function( newImage ) { 2580 if ( newImage.hasClass( 'sp-image' ) ) { 2581 slide.$mainImage = newImage; 2582 slide.resizeMainImage( true ); 2583 } 2584 }); 2585 }); 2586 } 2587 }); 2588 }, 2589 2590 // Check visible thumbnails and load their images 2591 _checkAndLoadVisibleThumbnailImages: function() { 2592 if ( this.$slider.find( '.sp-thumbnail-container:not([ data-loaded ])' ).length === 0 ) { 2593 return; 2594 } 2595 2596 var that = this, 2597 thumbnailSize = this.thumbnailsSize / this.thumbnails.length, 2598 2599 // Calculate the indexes of the first and last thumbnail that will be checked 2600 from = Math.floor( Math.abs( this.thumbnailsPosition / thumbnailSize ) ), 2601 to = Math.floor( ( - this.thumbnailsPosition + this.thumbnailsContainerSize ) / thumbnailSize ), 2602 2603 // Get all the thumbnails that need to be checked 2604 thumbnailsToCheck = this.thumbnails.slice( from, to + 1 ); 2605 2606 // Loop through the selected thumbnails and if the thumbnail is not marked as having 2607 // been loaded yet, load its image. 2608 $.each( thumbnailsToCheck, function( index, element ) { 2609 var $thumbnailContainer = element.$thumbnailContainer; 2610 2611 if ( typeof $thumbnailContainer.attr( 'data-loaded' ) === 'undefined' ) { 2612 $thumbnailContainer.attr( 'data-loaded', true ); 2613 2614 $thumbnailContainer.find( 'img[ data-src ]' ).each(function() { 2615 var image = $( this ); 2616 2617 that._loadImage( image, function() { 2618 element.resizeImage(); 2619 }); 2620 }); 2621 } 2622 }); 2623 }, 2624 2625 // Load an image 2626 _loadImage: function( image, callback ) { 2627 // Create a new image element 2628 var newImage = $( new Image() ); 2629 2630 // Copy the class(es) and inline style 2631 newImage.attr( 'class', image.attr( 'class' ) ); 2632 newImage.attr( 'style', image.attr( 'style' ) ); 2633 2634 // Copy the data attributes 2635 $.each( image.data(), function( name, value ) { 2636 newImage.attr( 'data-' + name, value ); 2637 }); 2638 2639 // Copy the width and height attributes if they exist 2640 if ( typeof image.attr( 'width' ) !== 'undefined') { 2641 newImage.attr( 'width', image.attr( 'width' ) ); 2642 } 2643 2644 if ( typeof image.attr( 'height' ) !== 'undefined') { 2645 newImage.attr( 'height', image.attr( 'height' ) ); 2646 } 2647 2648 if ( typeof image.attr( 'alt' ) !== 'undefined' ) { 2649 newImage.attr( 'alt', image.attr( 'alt' ) ); 2650 } 2651 2652 if ( typeof image.attr( 'title' ) !== 'undefined' ) { 2653 newImage.attr( 'title', image.attr( 'title' ) ); 2654 } 2655 2656 // Assign the source of the image 2657 newImage.attr( 'src', image.attr( 'data-src' ) ); 2658 newImage.removeAttr( 'data-src' ); 2659 2660 // Add the new image in the same container and remove the older image 2661 newImage.insertAfter( image ); 2662 image.remove(); 2663 image = null; 2664 2665 if ( typeof callback === 'function' ) { 2666 callback( newImage ); 2667 } 2668 }, 2669 2670 // Destroy the module 2671 destroyLazyLoading: function() { 2672 this.off( 'update.' + NS );
2673 this.off( 'gotoSlide.' + NS ); 2674 this.off( 'sliderResize.' + NS ); 2675 this.off( 'thumbnailsUpdate.' + NS ); 2676 this.off( 'thumbnailsMoveComplete.' + NS ); 2677 } 2678 }; 2679 2680 $.SliderPro.addModule( 'LazyLoading', LazyLoading ); 2681 2682})( window, jQuery ); 2683 2684// Layers module for Slider Pro. 2685// 2686// Adds support for animated and static layers. The layers can contain any content, 2687// from simple text for video elements. 2688;(function( window, $ ) { 2689 2690 "use strict"; 2691 2692 var NS = 'Layers.' + $.SliderPro.namespace; 2693 2694 var Layers = { 2695 2696 // Reference to the original 'gotoSlide' method 2697 layersGotoSlideReference: null, 2698 2699 // Reference to the timer that will delay the overriding 2700 // of the 'gotoSlide' method 2701 waitForLayersTimer: null, 2702 2703 initLayers: function() { 2704 this.on( 'update.' + NS, $.proxy( this._layersOnUpdate, this ) ); 2705 this.on( 'sliderResize.' + NS, $.proxy( this._layersOnResize, this ) ); 2706 this.on( 'gotoSlide.' + NS, $.proxy( this._layersOnGotoSlide, this ) ); 2707 }, 2708 2709 // Loop through the slides and initialize all layers 2710 _layersOnUpdate: function( event ) { 2711 var that = this; 2712 2713 $.each( this.slides, function( index, element ) { 2714 var $slide = element.$slide; 2715 2716 // Initialize the layers 2717 this.$slide.find( '.sp-layer:not([ data-layer-init ])' ).each(function() { 2718 var layer = new Layer( $( this ) ); 2719 2720 // Add the 'layers' array to the slide objects (instance of SliderProSlide) 2721 if ( typeof element.layers === 'undefined' ) { 2722 element.layers = []; 2723 } 2724 2725 element.layers.push( layer ); 2726 2727 if ( $( this ).hasClass( 'sp-static' ) === false ) { 2728 2729 // Add the 'animatedLayers' array to the slide objects (instance of SliderProSlide) 2730 if ( typeof element.animatedLayers === 'undefined' ) { 2731 element.animatedLayers = []; 2732 } 2733 2734 element.animatedLayers.push( layer ); 2735 } 2736 }); 2737 }); 2738 2739 // If the 'waitForLayers' option is enabled, the slider will not move to another slide 2740 // until all the layers from the previous slide will be hidden. To achieve this, 2741 // replace the current 'gotoSlide' function with another function that will include the 2742 // required functionality. 2743 // 2744 // Since the 'gotoSlide' method might be overridden by other modules as well, delay this 2745 // override to make sure it's the last override. 2746 if ( this.settings.waitForLayers === true ) { 2747 clearTimeout( this.waitForLayersTimer ); 2748 2749 this.waitForLayersTimer = setTimeout(function() { 2750 that.layersGotoSlideReference = that.gotoSlide; 2751 that.gotoSlide = that._layersGotoSlide; 2752 }, 1 ); 2753 } 2754 2755 // Show the layers for the initial slide 2756 // Delay the call in order to make sure the layers 2757 // are scaled properly before displaying them 2758 setTimeout(function() { 2759 that.showLayers( that.selectedSlideIndex ); 2760 }, 1); 2761 }, 2762 2763 // When the slider resizes, try to scale down the layers proportionally. The automatic scaling 2764 // will make use of an option, 'autoScaleReference', by comparing the current width of the slider 2765 // with the reference width. So, if the reference width is 1000 pixels and the current width is 2766 // 500 pixels, it means that the layers will be scaled down to 50% of their size. 2767 _layersOnResize: function() { 2768 var that = this, 2769 autoScaleReference, 2770 useAutoScale = this.settings.autoScaleLayers, 2771 scaleRatio; 2772 2773 if ( this.settings.autoScaleLayers === false ) { 2774 return; 2775 } 2776 2777 // If there isn't a reference for how the layers should scale down automatically, use the 'width' 2778 // option as a reference, unless the width was set to a percentage. If there isn't a set reference and 2779 // the width was set to a percentage, auto scaling will not be used because it's not possible to 2780 // calculate how much should the layers scale. 2781 if ( this.settings.autoScaleReference === -1 ) { 2782 if ( typeof this.settings.width === 'string' && this.settings.width.indexOf( '%' ) !== -1 ) { 2783 useAutoScale = false; 2784 } else { 2785 autoScaleReference = parseInt( this.settings.width, 10 ); 2786 } 2787 } else { 2788 autoScaleReference = this.settings.autoScaleReference; 2789 } 2790 2791 if ( useAutoScale === true && this.slideWidth < autoScaleReference ) { 2792 scaleRatio = that.slideWidth / autoScaleReference; 2793 } else { 2794 scaleRatio = 1; 2795 } 2796 2797 $.each( this.slides, function( index, slide ) { 2798 if ( typeof slide.layers !== 'undefined' ) { 2799 $.each( slide.layers, function( index, layer ) { 2800 layer.scale( scaleRatio ); 2801 }); 2802 } 2803 }); 2804 }, 2805
2806 // Replace the 'gotoSlide' method with this one, which makes it possible to 2807 // change the slide only after the layers from the previous slide are hidden. 2808 _layersGotoSlide: function( index ) { 2809 var that = this, 2810 animatedLayers = this.slides[ this.selectedSlideIndex ].animatedLayers; 2811 2812 // If the slider is dragged, don't wait for the layer to hide 2813 if ( this.$slider.hasClass( 'sp-swiping' ) || typeof animatedLayers === 'undefined' || animatedLayers.length === 0 ) { 2814 this.layersGotoSlideReference( index ); 2815 } else { 2816 this.on( 'hideLayersComplete.' + NS, function() { 2817 that.off( 'hideLayersComplete.' + NS ); 2818 that.layersGotoSlideReference( index ); 2819 }); 2820 2821 this.hideLayers( this.selectedSlideIndex ); 2822 } 2823 }, 2824 2825 // When a new slide is selected, hide the layers from the previous slide 2826 // and show the layers from the current slide. 2827 _layersOnGotoSlide: function( event ) { 2828 if ( this.previousSlideIndex !== this.selectedSlideIndex && this.settings.waitForLayers === false ) { 2829 this.hideLayers( this.previousSlideIndex ); 2830 } 2831 2832 this.showLayers( this.selectedSlideIndex ); 2833 }, 2834 2835 // Show the animated layers from the slide at the specified index, 2836 // and fire an event when all the layers from the slide become visible. 2837 showLayers: function( index ) { 2838 var that = this, 2839 animatedLayers = this.slides[ index ].animatedLayers, 2840 layerCounter = 0; 2841 2842 if ( typeof animatedLayers === 'undefined' ) { 2843 return; 2844 } 2845 2846 $.each( animatedLayers, function( index, element ) { 2847 2848 // If the layer is already visible, increment the counter directly, else wait 2849 // for the layer's showing animation to complete. 2850 if ( element.isVisible() === true ) { 2851 layerCounter++; 2852 2853 if ( layerCounter === animatedLayers.length ) { 2854 that.trigger({ type: 'showLayersComplete', index: index }); 2855 if ( $.isFunction( that.settings.showLayersComplete ) ) { 2856 that.settings.showLayersComplete.call( that, { type: 'showLayersComplete', index: index }); 2857 } 2858 } 2859 } else { 2860 element.show(function() { 2861 layerCounter++; 2862 2863 if ( layerCounter === animatedLayers.length ) { 2864 that.trigger({ type: 'showLayersComplete', index: index }); 2865 if ( $.isFunction( that.settings.showLayersComplete ) ) { 2866 that.settings.showLayersComplete.call( that, { type: 'showLayersComplete', index: index }); 2867 } 2868 } 2869 }); 2870 } 2871 }); 2872 }, 2873 2874 // Hide the animated layers from the slide at the specified index, 2875 // and fire an event when all the layers from the slide become invisible. 2876 hideLayers: function( index ) { 2877 var that = this, 2878 animatedLayers = this.slides[ index ].animatedLayers, 2879 layerCounter = 0; 2880 2881 if ( typeof animatedLayers === 'undefined' ) { 2882 return; 2883 } 2884 2885 $.each( animatedLayers, function( index, element ) { 2886 2887 // If the layer is already invisible, increment the counter directly, else wait 2888 // for the layer's hiding animation to complete. 2889 if ( element.isVisible() === false ) { 2890 layerCounter++; 2891 2892 if ( layerCounter === animatedLayers.length ) { 2893 that.trigger({ type: 'hideLayersComplete', index: index }); 2894 if ( $.isFunction( that.settings.hideLayersComplete ) ) { 2895 that.settings.hideLayersComplete.call( that, { type: 'hideLayersComplete', index: index }); 2896 } 2897 } 2898 } else { 2899 element.hide(function() { 2900 layerCounter++; 2901 2902 if ( layerCounter === animatedLayers.length ) { 2903 that.trigger({ type: 'hideLayersComplete', index: index }); 2904 if ( $.isFunction( that.settings.hideLayersComplete ) ) { 2905 that.settings.hideLayersComplete.call( that, { type: 'hideLayersComplete', index: index }); 2906 } 2907 } 2908 }); 2909 } 2910 }); 2911 }, 2912 2913 // Destroy the module 2914 destroyLayers: function() { 2915 this.off( 'update.' + NS ); 2916 this.off( 'resize.' + NS ); 2917 this.off( 'gotoSlide.' + NS ); 2918 this.off( 'hideLayersComplete.' + NS ); 2919 }, 2920 2921 layersDefaults: { 2922 2923 // Indicates whether the slider will wait for the layers to disappear before 2924 // going to a new slide 2925 waitForLayers: false, 2926 2927 // Indicates whether the layers will be scaled automatically 2928 autoScaleLayers: true, 2929 2930 // Sets a reference width which will be compared to the current slider width 2931 // in order to determine how much the layers need to scale down. By default, 2932 // the reference width will be equal to the slide width. However, if the slide width 2933 // is set to a percentage value, then it's necessary to set a specific value for 'autoScaleReference'. 2934 autoScaleReference: -1, 2935 2936 // Called when all animated layers become visible 2937 showLayersComplete: function() {}, 2938 2939 // Called when all animated layers become invisible 2940 hideLayersComplete: function() {} 2941 } 2942 }; 2943 2944 // Override the slide's 'destroy' method in order to destroy the 2945 // layers that where added to the slide as well. 2946 var slideDestroy = window.SliderProSlide.prototype.destroy; 2947 2948 window.SliderProSlide.prototype.destroy = function() { 2949 if ( typeof this.layers !== 'undefined' ) { 2950 $.each( this.layers, function( index, element ) { 2951 element.destroy(); 2952 }); 2953 2954 this.layers.length = 0; 2955 } 2956 2957 if ( typeof this.animatedLayers !== 'undefined' ) { 2958 this.animatedLayers.length = 0; 2959 } 2960 2961 slideDestroy.apply( this ); 2962 }; 2963 2964 var Layer = function( layer ) { 2965 2966 // Reference to the layer jQuery element 2967 this.$layer = layer; 2968 2969 // Indicates whether a layer is currently visible or hidden 2970 this.visible = false;
2971 2972 // Indicates whether the layer was styled 2973 this.styled = false; 2974 2975 // Holds the data attributes added to the layer 2976 this.data = null; 2977 2978 // Indicates the layer's reference point (topLeft, bottomLeft, topRight or bottomRight) 2979 this.position = null; 2980 2981 // Indicates which CSS property (left or right) will be used for positioning the layer 2982 this.horizontalProperty = null; 2983 2984 // Indicates which CSS property (top or bottom) will be used for positioning the layer 2985 this.verticalProperty = null; 2986 2987 // Indicates the value of the horizontal position 2988 this.horizontalPosition = null; 2989 2990 // Indicates the value of the vertical position 2991 this.verticalPosition = null; 2992 2993 // Indicates how much the layers needs to be scaled 2994 this.scaleRatio = 1; 2995 2996 // Indicates the type of supported transition (CSS3 2D, CSS3 3D or JavaScript) 2997 this.supportedAnimation = SliderProUtils.getSupportedAnimation(); 2998 2999 // Indicates the required vendor prefix for CSS (i.e., -webkit, -moz, etc.) 3000 this.vendorPrefix = SliderProUtils.getVendorPrefix(); 3001 3002 // Indicates the name of the CSS transition's complete event (i.e., transitionend, webkitTransitionEnd, etc.) 3003 this.transitionEvent = SliderProUtils.getTransitionEvent(); 3004 3005 // Reference to the timer that will be used to hide the layers automatically after a given time interval 3006 this.stayTimer = null; 3007 3008 this._init(); 3009 }; 3010 3011 Layer.prototype = { 3012 3013 // Initialize the layers 3014 _init: function() { 3015 this.$layer.attr( 'data-layer-init', true ); 3016 3017 if ( this.$layer.hasClass( 'sp-static' ) ) { 3018 this._setStyle(); 3019 } else { 3020 this.$layer.css({ 'visibility': 'hidden' }); 3021 } 3022 }, 3023 3024 // Set the size and position of the layer 3025 _setStyle: function() { 3026 this.styled = true; 3027 3028 // Get the data attributes specified in HTML 3029 this.data = this.$layer.data(); 3030 3031 if ( typeof this.data.width !== 'undefined' ) { 3032 this.$layer.css( 'width', this.data.width ); 3033 } 3034 3035 if ( typeof this.data.height !== 'undefined' ) { 3036 this.$layer.css( 'height', this.data.height ); 3037 } 3038 3039 if ( typeof this.data.depth !== 'undefined' ) { 3040 this.$layer.css( 'z-index', this.data.depth ); 3041 } 3042 3043 this.position = this.data.position ? ( this.data.position ).toLowerCase() : 'topleft'; 3044 3045 if ( this.position.indexOf( 'right' ) !== -1 ) { 3046 this.horizontalProperty = 'right'; 3047 } else if ( this.position.indexOf( 'left' ) !== -1 ) { 3048 this.horizontalProperty = 'left'; 3049 } else { 3050 this.horizontalProperty = 'center'; 3051 } 3052 3053 if ( this.position.indexOf( 'bottom' ) !== -1 ) { 3054 this.verticalProperty = 'bottom'; 3055 } else if ( this.position.indexOf( 'top' ) !== -1 ) { 3056 this.verticalProperty = 'top'; 3057 } else { 3058 this.verticalProperty = 'center'; 3059 } 3060 3061 this._setPosition(); 3062 3063 this.scale( this.scaleRatio ); 3064 }, 3065 3066 // Set the position of the layer 3067 _setPosition: function() { 3068 var inlineStyle = this.$layer.attr( 'style' ); 3069 3070 this.horizontalPosition = typeof this.data.horizontal !== 'undefined' ? this.data.horizontal : 0; 3071 this.verticalPosition = typeof this.data.vertical !== 'undefined' ? this.data.vertical : 0; 3072 3073 // Set the horizontal position of the layer based on the data set 3074 if ( this.horizontalProperty === 'center' ) { 3075 3076 // prevent content wrapping while setting the width 3077 if ( this.$layer.is( 'img' ) === false && ( typeof inlineStyle === 'undefined' || ( typeof inlineStyle !== 'undefined' && inlineStyle.indexOf( 'width' ) === -1 ) ) ) { 3078 this.$layer.css( 'white-space', 'nowrap' ); 3079 this.$layer.css( 'width', this.$layer.outerWidth( true ) ); 3080 } 3081 3082 this.$layer.css({ 'marginLeft': 'auto', 'marginRight': 'auto', 'left': this.horizontalPosition, 'right': 0 }); 3083 } else { 3084 this.$layer.css( this.horizontalProperty, this.horizontalPosition ); 3085 } 3086 3087 // Set the vertical position of the layer based on the data set 3088 if ( this.verticalProperty === 'center' ) { 3089 3090 // prevent content wrapping while setting the height 3091 if ( this.$layer.is( 'img' ) === false && ( typeof inlineStyle === 'undefined' || ( typeof inlineStyle !== 'undefined' && inlineStyle.indexOf( 'height' ) === -1 ) ) ) { 3092 this.$layer.css( 'white-space', 'nowrap' ); 3093 this.$layer.css( 'height', this.$layer.outerHeight( true ) ); 3094 } 3095 3096 this.$layer.css({ 'marginTop': 'auto', 'marginBottom': 'auto', 'top': this.verticalPosition, 'bottom': 0 }); 3097 } else { 3098 this.$layer.css( this.verticalProperty, this.verticalPosition ); 3099 } 3100 }, 3101 3102 // Scale the layer 3103 scale: function( ratio ) { 3104 3105 // Return if the layer is set to be unscalable 3106 if ( this.$layer.hasClass( 'sp-no-scale' ) ) { 3107 return; 3108 } 3109 3110 // Store the ratio (even if the layer is not ready to be scaled yet) 3111 this.scaleRatio = ratio; 3112 3113 // Return if the layer is not styled yet 3114 if ( this.styled === false ) { 3115 return; 3116 } 3117 3118 var horizontalProperty = this.horizontalProperty === 'center' ? 'left' : this.horizontalProperty, 3119 verticalProperty = this.verticalProperty === 'center' ? 'top' : this.verticalProperty, 3120 css = {}; 3121 3122 // Apply the scaling 3123 css[ this.vendorPrefix + 'transform-origin' ] = this.horizontalProperty + ' ' + this.verticalProperty; 3124 css[ this.vendorPrefix + 'transform' ] = 'scale(' + this.scaleRatio + ')'; 3125 3126 // If the position is not set to a percentage value, apply the scaling to the position 3127 if ( typeof this.horizontalPosition !== 'string' ) { 3128 css[ horizontalProperty ] = this.horizontalPosition * this.scaleRatio; 3129 } 3130 3131 // If the position is not set to a percentage value, apply the scaling to the position 3132 if ( typeof this.verticalPosition !== 'string' ) { 3133 css[ verticalProperty ] = this.verticalPosition * this.scaleRatio; 3134 } 3135 3136 // If the width or height is set to a percentage value, increase the percentage in order to 3137 // maintain the same layer to slide proportions. This is necessary because otherwise the scaling 3138 // transform would minimize the layers more than intended. 3139 if ( typeof this.data.width === 'string' && this.data.width.indexOf( '%' ) !== -1 ) { 3140 css.width = ( parseInt( this.data.width, 10 ) / this.scaleRatio ).toString() + '%'; 3141 } 3142 3143 if ( typeof this.data.height === 'string' && this.data.height.indexOf( '%' ) !== -1 ) { 3144 css.height = ( parseInt( this.data.height, 10 ) / this.scaleRatio ).toString() + '%'; 3145 } 3146 3147 this.$layer.css( css ); 3148 }, 3149 3150 // Show the layer 3151 show: function( callback ) { 3152 if ( this.visible === true ) { 3153 return; 3154 } 3155 3156 this.visible = true; 3157 3158 // First, style the layer if it's not already styled 3159 if ( this.styled === false ) { 3160 this._setStyle(); 3161 } 3162 3163 var that = this, 3164 offset = typeof this.data.showOffset !== 'undefined' ? this.data.showOffset : 50, 3165 duration = typeof this.data.showDuration !== 'undefined' ? this.data.showDuration / 1000 : 0.4,
3166 delay = typeof this.data.showDelay !== 'undefined' ? this.data.showDelay : 10, 3167 stayDuration = typeof that.data.stayDuration !== 'undefined' ? parseInt( that.data.stayDuration, 10 ) : -1; 3168 3169 // Animate the layers with CSS3 or with JavaScript 3170 if ( this.supportedAnimation === 'javascript' ) { 3171 this.$layer 3172 .stop() 3173 .delay( delay ) 3174 .css({ 'opacity': 0, 'visibility': 'visible' }) 3175 .animate( { 'opacity': 1 }, duration * 1000, function() { 3176 3177 // Hide the layer after a given time interval 3178 if ( stayDuration !== -1 ) { 3179 that.stayTimer = setTimeout(function() { 3180 that.hide(); 3181 that.stayTimer = null; 3182 }, stayDuration ); 3183 } 3184 3185 if ( typeof callback !== 'undefined' ) { 3186 callback(); 3187 } 3188 }); 3189 } else { 3190 var start = { 'opacity': 0, 'visibility': 'visible' }, 3191 target = { 'opacity': 1 }, 3192 transformValues = ''; 3193 3194 start[ this.vendorPrefix + 'transform' ] = 'scale(' + this.scaleRatio + ')'; 3195 target[ this.vendorPrefix + 'transform' ] = 'scale(' + this.scaleRatio + ')'; 3196 target[ this.vendorPrefix + 'transition' ] = 'opacity ' + duration + 's'; 3197 3198 if ( typeof this.data.showTransition !== 'undefined' ) { 3199 if ( this.data.showTransition === 'left' ) { 3200 transformValues = offset + 'px, 0'; 3201 } else if ( this.data.showTransition === 'right' ) { 3202 transformValues = '-' + offset + 'px, 0'; 3203 } else if ( this.data.showTransition === 'up' ) { 3204 transformValues = '0, ' + offset + 'px'; 3205 } else if ( this.data.showTransition === 'down') { 3206 transformValues = '0, -' + offset + 'px'; 3207 } 3208 3209 start[ this.vendorPrefix + 'transform' ] += this.supportedAnimation === 'css-3d' ? ' translate3d(' + transformValues + ', 0)' : ' translate(' + transformValues + ')'; 3210 target[ this.vendorPrefix + 'transform' ] += this.supportedAnimation === 'css-3d' ? ' translate3d(0, 0, 0)' : ' translate(0, 0)'; 3211 target[ this.vendorPrefix + 'transition' ] += ', ' + this.vendorPrefix + 'transform ' + duration + 's'; 3212 } 3213 3214 // Listen when the layer animation is complete 3215 this.$layer.on( this.transitionEvent, function( event ) { 3216 if ( event.target !== event.currentTarget ) { 3217 return; 3218 } 3219 3220 that.$layer 3221 .off( that.transitionEvent ) 3222 .css( that.vendorPrefix + 'transition', '' ); 3223 3224 // Hide the layer after a given time interval 3225 if ( stayDuration !== -1 ) { 3226 that.stayTimer = setTimeout(function() { 3227 that.hide(); 3228 that.stayTimer = null; 3229 }, stayDuration ); 3230 } 3231 3232 if ( typeof callback !== 'undefined' ) { 3233 callback(); 3234 } 3235 }); 3236 3237 this.$layer.css( start ); 3238 3239 setTimeout( function() { 3240 that.$layer.css( target ); 3241 }, delay ); 3242 } 3243 }, 3244 3245 // Hide the layer 3246 hide: function( callback ) { 3247 if ( this.visible === false ) { 3248 return; 3249 } 3250 3251 var that = this, 3252 offset = typeof this.data.hideOffset !== 'undefined' ? this.data.hideOffset : 50, 3253 duration = typeof this.data.hideDuration !== 'undefined' ? this.data.hideDuration / 1000 : 0.4, 3254 delay = typeof this.data.hideDelay !== 'undefined' ? this.data.hideDelay : 10; 3255 3256 this.visible = false; 3257 3258 // If the layer is hidden before it hides automatically, clear the timer 3259 if ( this.stayTimer !== null ) { 3260 clearTimeout( this.stayTimer ); 3261 } 3262 3263 // Animate the layers with CSS3 or with JavaScript 3264 if ( this.supportedAnimation === 'javascript' ) { 3265 this.$layer 3266 .stop() 3267 .delay( delay ) 3268 .animate({ 'opacity': 0 }, duration * 1000, function() { 3269 $( this ).css( 'visibility', 'hidden' ); 3270 3271 if ( typeof callback !== 'undefined' ) { 3272 callback(); 3273 } 3274 }); 3275 } else { 3276 var transformValues = '', 3277 target = { 'opacity': 0 }; 3278 3279 target[ this.vendorPrefix + 'transform' ] = 'scale(' + this.scaleRatio + ')'; 3280 target[ this.vendorPrefix + 'transition' ] = 'opacity ' + duration + 's'; 3281 3282 if ( typeof this.data.hideTransition !== 'undefined' ) { 3283 if ( this.data.hideTransition === 'left' ) { 3284 transformValues = '-' + offset + 'px, 0'; 3285 } else if ( this.data.hideTransition === 'right' ) { 3286 transformValues = offset + 'px, 0'; 3287 } else if ( this.data.hideTransition === 'up' ) { 3288 transformValues = '0, -' + offset + 'px'; 3289 } else if ( this.data.hideTransition === 'down' ) { 3290 transformValues = '0, ' + offset + 'px'; 3291 } 3292 3293 target[ this.vendorPrefix + 'transform' ] += this.supportedAnimation === 'css-3d' ? ' translate3d(' + transformValues + ', 0)' : ' translate(' + transformValues + ')'; 3294 target[ this.vendorPrefix + 'transition' ] += ', ' + this.vendorPrefix + 'transform ' + duration + 's'; 3295 } 3296 3297 // Listen when the layer animation is complete 3298 this.$layer.on( this.transitionEvent, function( event ) { 3299 if ( event.target !== event.currentTarget ) { 3300 return; 3301 } 3302 3303 that.$layer 3304 .off( that.transitionEvent ) 3305 .css( that.vendorPrefix + 'transition', '' ); 3306 3307 // Hide the layer after transition 3308 if ( that.visible === false ) { 3309 that.$layer.css( 'visibility', 'hidden' ); 3310 } 3311 3312 if ( typeof callback !== 'undefined' ) { 3313 callback(); 3314 } 3315 }); 3316 3317 setTimeout( function() { 3318 that.$layer.css( target ); 3319 }, delay ); 3320 } 3321 }, 3322 3323 isVisible: function() { 3324 if ( this.visible === false || this.$layer.is( ':hidden' ) ) { 3325 return false;
3326 } 3327 3328 return true; 3329 }, 3330 3331 // Destroy the layer 3332 destroy: function() { 3333 this.$layer.removeAttr( 'style' ); 3334 this.$layer.removeAttr( 'data-layer-init' ); 3335 } 3336 }; 3337 3338 $.SliderPro.addModule( 'Layers', Layers ); 3339 3340})( window, jQuery ); 3341 3342// Fade module for Slider Pro. 3343// 3344// Adds the possibility to navigate through slides using a cross-fade effect. 3345;(function( window, $ ) { 3346 3347 "use strict"; 3348 3349 var NS = 'Fade.' + $.SliderPro.namespace; 3350 3351 var Fade = { 3352 3353 // Reference to the original 'gotoSlide' method 3354 fadeGotoSlideReference: null, 3355 3356 initFade: function() { 3357 this.on( 'update.' + NS, $.proxy( this._fadeOnUpdate, this ) ); 3358 }, 3359 3360 // If fade is enabled, store a reference to the original 'gotoSlide' method 3361 // and then assign a new function to 'gotoSlide'. 3362 _fadeOnUpdate: function() { 3363 if ( this.settings.fade === true ) { 3364 this.fadeGotoSlideReference = this.gotoSlide; 3365 this.gotoSlide = this._fadeGotoSlide; 3366 } 3367 }, 3368 3369 // Will replace the original 'gotoSlide' function by adding a cross-fade effect 3370 // between the previous and the next slide. 3371 _fadeGotoSlide: function( index ) { 3372 if ( index === this.selectedSlideIndex ) { 3373 return; 3374 } 3375 3376 // If the slides are being swiped/dragged, don't use fade, but call the original method instead. 3377 // If not, which means that a new slide was selected through a button, arrows or direct call, then 3378 // use fade. 3379 if ( this.$slider.hasClass( 'sp-swiping' ) ) { 3380 this.fadeGotoSlideReference( index ); 3381 } else { 3382 var that = this, 3383 $nextSlide, 3384 $previousSlide, 3385 newIndex = index; 3386 3387 // Loop through all the slides and overlap the the previous and next slide, 3388 // and hide the other slides. 3389 $.each( this.slides, function( index, element ) { 3390 var slideIndex = element.getIndex(), 3391 $slide = element.$slide; 3392 3393 if ( slideIndex === newIndex ) { 3394 $slide.css({ 'opacity': 0, 'left': 0, 'top': 0, 'z-index': 20 }); 3395 $nextSlide = $slide; 3396 } else if ( slideIndex === that.selectedSlideIndex ) { 3397 $slide.css({ 'opacity': 1, 'left': 0, 'top': 0, 'z-index': 10 }); 3398 $previousSlide = $slide; 3399 } else { 3400 $slide.css( 'visibility', 'hidden' ); 3401 } 3402 }); 3403 3404 // Set the new indexes for the previous and selected slides 3405 this.previousSlideIndex = this.selectedSlideIndex; 3406 this.selectedSlideIndex = index; 3407 3408 // Re-assign the 'sp-selected' class to the currently selected slide 3409 this.$slides.find( '.sp-selected' ).removeClass( 'sp-selected' ); 3410 this.$slides.find( '.sp-slide' ).eq( this.selectedSlideIndex ).addClass( 'sp-selected' ); 3411 3412 // Rearrange the slides if the slider is loopable 3413 if ( that.settings.loop === true ) { 3414 that._updateSlidesOrder(); 3415 } 3416 3417 // Move the slides container so that the cross-fading slides (which now have the top and left 3418 // position set to 0) become visible and in the center of the slider. 3419 this._moveTo( this.visibleOffset, true ); 3420 3421 // Fade out the previous slide, if indicated, in addition to fading in the next slide 3422 if ( this.settings.fadeOutPreviousSlide === true ) { 3423 this._fadeSlideTo( $previousSlide, 0 ); 3424 } 3425 3426 // Fade in the selected slide 3427 this._fadeSlideTo( $nextSlide, 1, function() { 3428 3429 // After the animation is over, make all the slides visible again 3430 $.each( that.slides, function( index, element ) { 3431 var $slide = element.$slide; 3432 $slide.css({ 'visibility': '', 'opacity': '', 'z-index': '' }); 3433 }); 3434 3435 // Reset the position of the slides and slides container 3436 that._resetSlidesPosition(); 3437 3438 // Fire the 'gotoSlideComplete' event 3439 that.trigger({ type: 'gotoSlideComplete', index: index, previousIndex: that.previousSlideIndex }); 3440 if ( $.isFunction( that.settings.gotoSlideComplete ) ) { 3441 that.settings.gotoSlideComplete.call( that, { type: 'gotoSlideComplete', index: index, previousIndex: that.previousSlideIndex } ); 3442 } 3443 }); 3444 3445 if ( this.settings.autoHeight === true ) { 3446 this._resizeHeight(); 3447 } 3448 3449 // Fire the 'gotoSlide' event 3450 this.trigger({ type: 'gotoSlide', index: index, previousIndex: this.previousSlideIndex }); 3451 if ( $.isFunction( this.settings.gotoSlide ) ) {
3452 this.settings.gotoSlide.call( this, { type: 'gotoSlide', index: index, previousIndex: this.previousSlideIndex }); 3453 } 3454 } 3455 }, 3456 3457 // Fade the target slide to the specified opacity (0 or 1) 3458 _fadeSlideTo: function( target, opacity, callback ) { 3459 var that = this; 3460 3461 // Use CSS transitions if they are supported. If not, use JavaScript animation. 3462 if ( this.supportedAnimation === 'css-3d' || this.supportedAnimation === 'css-2d' ) { 3463 3464 // There needs to be a delay between the moment the opacity is set 3465 // and the moment the transitions starts. 3466 setTimeout(function(){ 3467 var css = { 'opacity': opacity }; 3468 css[ that.vendorPrefix + 'transition' ] = 'opacity ' + that.settings.fadeDuration / 1000 + 's'; 3469 target.css( css ); 3470 }, 100 ); 3471 3472 target.on( this.transitionEvent, function( event ) { 3473 if ( event.target !== event.currentTarget ) { 3474 return; 3475 } 3476 3477 target.off( that.transitionEvent ); 3478 target.css( that.vendorPrefix + 'transition', '' ); 3479 3480 if ( typeof callback === 'function' ) { 3481 callback(); 3482 } 3483 }); 3484 } else { 3485 target.stop().animate({ 'opacity': opacity }, this.settings.fadeDuration, function() { 3486 if ( typeof callback === 'function' ) { 3487 callback(); 3488 } 3489 }); 3490 } 3491 }, 3492 3493 // Destroy the module 3494 destroyFade: function() { 3495 this.off( 'update.' + NS ); 3496 3497 if ( this.fadeGotoSlideReference !== null ) { 3498 this.gotoSlide = this.fadeGotoSlideReference; 3499 } 3500 }, 3501 3502 fadeDefaults: { 3503 3504 // Indicates if fade will be used 3505 fade: false, 3506 3507 // Indicates if the previous slide will be faded out (in addition to the next slide being faded in) 3508 fadeOutPreviousSlide: true, 3509 3510 // Sets the duration of the fade effect
3511 fadeDuration: 500 3512 } 3513 }; 3514 3515 $.SliderPro.addModule( 'Fade', Fade ); 3516 3517})( window, jQuery ); 3518 3519// Touch Swipe module for Slider Pro. 3520// 3521// Adds touch-swipe functionality for slides. 3522;(function( window, $ ) { 3523 3524 "use strict"; 3525 3526 var NS = 'TouchSwipe.' + $.SliderPro.namespace; 3527 3528 var TouchSwipe = { 3529 3530 // The x and y coordinates of the pointer/finger's starting position 3531 touchStartPoint: {x: 0, y: 0}, 3532 3533 // The x and y coordinates of the pointer/finger's end position 3534 touchEndPoint: {x: 0, y: 0}, 3535 3536 // The distance from the starting to the end position on the x and y axis 3537 touchDistance: {x: 0, y: 0}, 3538 3539 // The position of the slides when the touch swipe starts 3540 touchStartPosition: 0, 3541 3542 // Indicates if the slides are being swiped 3543 isTouchMoving: false, 3544 3545 // Stores the names of the events 3546 touchSwipeEvents: { startEvent: '', moveEvent: '', endEvent: '' }, 3547 3548 initTouchSwipe: function() { 3549 var that = this; 3550 3551 // check if touch swipe is enabled 3552 if ( this.settings.touchSwipe === false ) { 3553 return; 3554 } 3555 3556 this.touchSwipeEvents.startEvent = 'touchstart' + '.' + NS + ' mousedown' + '.' + NS; 3557 this.touchSwipeEvents.moveEvent = 'touchmove' + '.' + NS + ' mousemove' + '.' + NS; 3558 this.touchSwipeEvents.endEvent = 'touchend' + '.' + this.uniqueId + '.' + NS + ' mouseup' + '.' + this.uniqueId + '.' + NS; 3559 3560 // Listen for touch swipe/mouse move events 3561 this.$slidesMask.on( this.touchSwipeEvents.startEvent, $.proxy( this._onTouchStart, this ) ); 3562 this.$slidesMask.on( 'dragstart.' + NS, function( event ) { 3563 event.preventDefault(); 3564 }); 3565 3566 // Add the grabbing icon 3567 this.$slidesMask.addClass( 'sp-grab' ); 3568 }, 3569 3570 // Called when the slides starts being dragged 3571 _onTouchStart: function( event ) { 3572 3573 // Disable dragging if the element is set to allow selections 3574 if ( $( event.target ).closest( '.sp-selectable' ).length >= 1 ) { 3575 return; 3576 } 3577 3578 var that = this, 3579 eventObject = typeof event.originalEvent.touches !== 'undefined' ? event.originalEvent.touches[0] : event.originalEvent; 3580 3581 // Prevent default behavior only for mouse events 3582 if ( typeof event.originalEvent.touches === 'undefined' ) { 3583 event.preventDefault(); 3584 } 3585 3586 // Disable click events on links 3587 $( event.target ).parents( '.sp-slide' ).find( 'a' ).one( 'click.' + NS, function( event ) { 3588 event.preventDefault(); 3589 }); 3590 3591 // Get the initial position of the mouse pointer and the initial position 3592 // of the slides' container 3593 this.touchStartPoint.x = eventObject.pageX || eventObject.clientX; 3594 this.touchStartPoint.y = eventObject.pageY || eventObject.clientY; 3595 this.touchStartPosition = this.slidesPosition; 3596 3597 // Clear the previous distance values 3598 this.touchDistance.x = this.touchDistance.y = 0; 3599 3600 // If the slides are being grabbed while they're still animating, stop the 3601 // current movement 3602 if ( this.$slides.hasClass( 'sp-animated' ) ) { 3603 this.isTouchMoving = true; 3604 this._stopMovement(); 3605 this.touchStartPosition = this.slidesPosition; 3606 } 3607 3608 // Listen for move and end events 3609 this.$slidesMask.on( this.touchSwipeEvents.moveEvent, $.proxy( this._onTouchMove, this ) ); 3610 $( document ).on( this.touchSwipeEvents.endEvent, $.proxy( this._onTouchEnd, this ) ); 3611 3612 // Swap grabbing icons 3613 this.$slidesMask.removeClass( 'sp-grab' ).addClass( 'sp-grabbing' ); 3614 3615 // Add 'sp-swiping' class to indicate that the slides are being swiped 3616 this.$slider.addClass( 'sp-swiping' ); 3617 }, 3618 3619 // Called during the slides' dragging 3620 _onTouchMove: function( event ) { 3621 var eventObject = typeof event.originalEvent.touches !== 'undefined' ? event.originalEvent.touches[0] : event.originalEvent; 3622 3623 // Indicate that the move event is being fired 3624 this.isTouchMoving = true; 3625 3626 // Get the current position of the mouse pointer 3627 this.touchEndPoint.x = eventObject.pageX || eventObject.clientX; 3628 this.touchEndPoint.y = eventObject.pageY || eventObject.clientY; 3629 3630 // Calculate the distance of the movement on both axis 3631 this.touchDistance.x = this.touchEndPoint.x - this.touchStartPoint.x; 3632 this.touchDistance.y = this.touchEndPoint.y - this.touchStartPoint.y; 3633 3634 // Calculate the distance of the swipe that takes place in the same direction as the orientation of the slides 3635 // and calculate the distance from the opposite direction. 3636 // 3637 // For a swipe to be valid there should more distance in the same direction as the orientation of the slides. 3638 var distance = this.settings.orientation === 'horizontal' ? this.touchDistance.x : this.touchDistance.y, 3639 oppositeDistance = this.settings.orientation === 'horizontal' ? this.touchDistance.y : this.touchDistance.x; 3640 3641 // If the movement is in the same direction as the orientation of the slides, the swipe is valid 3642 if ( Math.abs( distance ) > Math.abs( oppositeDistance ) ) { 3643 event.preventDefault(); 3644 } else { 3645 return; 3646 } 3647 3648 if ( this.settings.loop === false ) { 3649 // Make the slides move slower if they're dragged outside its bounds 3650 if ( ( this.slidesPosition > this.touchStartPosition && this.selectedSlideIndex === 0 ) || 3651 ( this.slidesPosition < this.touchStartPosition && this.selectedSlideIndex === this.getTotalSlides() - 1 ) 3652 ) { 3653 distance = distance * 0.2; 3654 } 3655 } 3656 3657 this._moveTo( this.touchStartPosition + distance, true ); 3658 }, 3659 3660 // Called when the slides are released 3661 _onTouchEnd: function( event ) { 3662 var that = this, 3663 touchDistance = this.settings.orientation === 'horizontal' ? this.touchDistance.x : this.touchDistance.y; 3664 3665 // Remove the move and end listeners 3666 this.$slidesMask.off( this.touchSwipeEvents.moveEvent ); 3667 $( document ).off( this.touchSwipeEvents.endEvent ); 3668 3669 // Swap grabbing icons 3670 this.$slidesMask.removeClass( 'sp-grabbing' ).addClass( 'sp-grab' ); 3671 3672 // Check if there is intention for a tap 3673 if ( this.isTouchMoving === false || this.isTouchMoving === true && Math.abs( this.touchDistance.x ) < 10 && Math.abs( this.touchDistance.y ) < 10 ) { 3674 // Re-enable click events on links 3675 $( event.target ).parents( '.sp-slide' ).find( 'a' ).off( 'click.' + NS ); 3676 this.$slider.removeClass( 'sp-swiping' ); 3677 } 3678 3679 // Remove the 'sp-swiping' class but with a delay 3680 // because there might be other event listeners that check 3681 // the existence of this class, and this class should still be 3682 // applied for those listeners, since there was a swipe event 3683 setTimeout(function() { 3684 that.$slider.removeClass( 'sp-swiping' ); 3685 }, 1 ); 3686 3687 // Return if the slides didn't move 3688 if ( this.isTouchMoving === false ) { 3689 return; 3690 } 3691 3692 this.isTouchMoving = false;
3693 3694 $( event.target ).parents( '.sp-slide' ).one( 'click', function( event ) { 3695 event.preventDefault(); 3696 }); 3697 3698 // Calculate the old position of the slides in order to return to it if the swipe 3699 // is below the threshold 3700 var oldSlidesPosition = - parseInt( this.$slides.find( '.sp-slide' ).eq( this.selectedSlideIndex ).css( this.positionProperty ), 10 ) + this.visibleOffset; 3701 3702 if ( Math.abs( touchDistance ) < this.settings.touchSwipeThreshold ) { 3703 this._moveTo( oldSlidesPosition ); 3704 } else { 3705 3706 // Calculate by how many slides the slides container has moved 3707 var slideArrayDistance = touchDistance / ( this.slideSize + this.settings.slideDistance ); 3708 3709 // Floor the obtained value and add or subtract 1, depending on the direction of the swipe 3710 slideArrayDistance = parseInt( slideArrayDistance, 10 ) + ( slideArrayDistance > 0 ? 1 : - 1 ); 3711 3712 // Get the index of the currently selected slide and subtract the position index in order to obtain 3713 // the new index of the selected slide. 3714 var nextSlideIndex = this.slidesOrder[ $.inArray( this.selectedSlideIndex, this.slidesOrder ) - slideArrayDistance ]; 3715 3716 if ( this.settings.loop === true ) { 3717 this.gotoSlide( nextSlideIndex ); 3718 } else { 3719 if ( typeof nextSlideIndex !== 'undefined' ) { 3720 this.gotoSlide( nextSlideIndex ); 3721 } else { 3722 this._moveTo( oldSlidesPosition ); 3723 } 3724 } 3725 } 3726 }, 3727 3728 // Destroy the module 3729 destroyTouchSwipe: function() { 3730 this.$slidesMask.off( this.touchSwipeEvents.startEvent ); 3731 this.$slidesMask.off( this.touchSwipeEvents.moveEvent ); 3732 this.$slidesMask.off( 'dragstart.' + NS ); 3733 $( document ).off( this.touchSwipeEvents.endEvent ); 3734 this.$slidesMask.removeClass( 'sp-grab' ); 3735 }, 3736 3737 touchSwipeDefaults: { 3738 3739 // Indicates whether the touch swipe will be enabled 3740 touchSwipe: true, 3741 3742 // Sets the minimum amount that the slides should move 3743 touchSwipeThreshold: 50 3744 } 3745 }; 3746 3747 $.SliderPro.addModule( 'TouchSwipe', TouchSwipe ); 3748 3749})( window, jQuery ); 3750 3751// Caption module for Slider Pro. 3752// 3753// Adds a corresponding caption for each slide. The caption 3754// will appear and disappear with the slide. 3755;(function( window, $ ) { 3756 3757 "use strict"; 3758 3759 var NS = 'Caption.' + $.SliderPro.namespace; 3760 3761 var Caption = { 3762 3763 // Reference to the container element that will hold the caption 3764 $captionContainer: null, 3765 3766 // The caption content/text 3767 captionContent: '', 3768 3769 initCaption: function() { 3770 this.on( 'update.' + NS, $.proxy( this._captionOnUpdate, this ) );
3771 this.on( 'gotoSlide.' + NS, $.proxy( this._updateCaptionContent, this ) ); 3772 }, 3773 3774 // Create the caption container and hide the captions inside the slides 3775 _captionOnUpdate: function() { 3776 this.$captionContainer = this.$slider.find( '.sp-caption-container' ); 3777 3778 if ( this.$slider.find( '.sp-caption' ).length && this.$captionContainer.length === 0 ) { 3779 this.$captionContainer = $( '<div class="sp-caption-container"></div>' ).appendTo( this.$slider ); 3780 3781 // Show the caption for the selected slide 3782 this._updateCaptionContent(); 3783 } 3784 3785 // Hide the captions inside the slides 3786 this.$slides.find( '.sp-caption' ).each(function() { 3787 $( this ).css( 'display', 'none' ); 3788 }); 3789 }, 3790 3791 // Show the caption content for the selected slide 3792 _updateCaptionContent: function() { 3793 var that = this, 3794 newCaptionField = this.$slider.find( '.sp-slide' ).eq( this.selectedSlideIndex ).find( '.sp-caption' ), 3795 newCaptionContent = newCaptionField.length !== 0 ? newCaptionField.html() : ''; 3796 3797 // Either use a fade effect for swapping the captions or use an instant change 3798 if ( this.settings.fadeCaption === true ) { 3799 3800 // If the previous slide had a caption, fade out that caption first and when the animation is over 3801 // fade in the current caption. 3802 // If the previous slide didn't have a caption, fade in the current caption directly. 3803 if ( this.captionContent !== '' ) { 3804 3805 // If the caption container has 0 opacity when the fade out transition starts, set it 3806 // to 1 because the transition wouldn't work if the initial and final values are the same, 3807 // and the callback functions wouldn't fire in this case. 3808 if ( parseFloat( this.$captionContainer.css( 'opacity' ), 10 ) === 0 ) { 3809 this.$captionContainer.css( this.vendorPrefix + 'transition', '' ); 3810 this.$captionContainer.css( 'opacity', 1 ); 3811 } 3812 3813 this._fadeCaptionTo( 0, function() { 3814 that.captionContent = newCaptionContent; 3815 3816 if ( newCaptionContent !== '' ) { 3817 that.$captionContainer.html( that.captionContent ); 3818 that._fadeCaptionTo( 1 ); 3819 } else { 3820 that.$captionContainer.empty(); 3821 } 3822 }); 3823 } else { 3824 this.captionContent = newCaptionContent; 3825 this.$captionContainer.html( this.captionContent ); 3826 this.$captionContainer.css( 'opacity', 0 ); 3827 this._fadeCaptionTo( 1 ); 3828 } 3829 } else { 3830 this.captionContent = newCaptionContent; 3831 this.$captionContainer.html( this.captionContent ); 3832 } 3833 }, 3834 3835 // Fade the caption container to the specified opacity 3836 _fadeCaptionTo: function( opacity, callback ) { 3837 var that = this; 3838 3839 // Use CSS transitions if they are supported. If not, use JavaScript animation. 3840 if ( this.supportedAnimation === 'css-3d' || this.supportedAnimation === 'css-2d' ) { 3841 3842 // There needs to be a delay between the moment the opacity is set 3843 // and the moment the transitions starts. 3844 setTimeout(function(){ 3845 var css = { 'opacity': opacity }; 3846 css[ that.vendorPrefix + 'transition' ] = 'opacity ' + that.settings.captionFadeDuration / 1000 + 's'; 3847 that.$captionContainer.css( css ); 3848 }, 1 ); 3849 3850 this.$captionContainer.on( this.transitionEvent, function( event ) { 3851 if ( event.target !== event.currentTarget ) { 3852 return; 3853 } 3854 3855 that.$captionContainer.off( that.transitionEvent ); 3856 that.$captionContainer.css( that.vendorPrefix + 'transition', '' ); 3857 3858 if ( typeof callback === 'function' ) { 3859 callback(); 3860 } 3861 }); 3862 } else { 3863 this.$captionContainer.stop().animate({ 'opacity': opacity }, this.settings.captionFadeDuration, function() { 3864 if ( typeof callback === 'function' ) { 3865 callback(); 3866 } 3867 }); 3868 } 3869 }, 3870 3871 // Destroy the module 3872 destroyCaption: function() { 3873 this.off( 'update.' + NS ); 3874 this.off( 'gotoSlide.' + NS ); 3875 3876 this.$captionContainer.remove(); 3877 3878 this.$slider.find( '.sp-caption' ).each(function() { 3879 $( this ).css( 'display', '' ); 3880 }); 3881 }, 3882 3883 captionDefaults: { 3884 3885 // Indicates whether or not the captions will be faded 3886 fadeCaption: true, 3887 3888 // Sets the duration of the fade animation 3889 captionFadeDuration: 500 3890 } 3891 }; 3892 3893 $.SliderPro.addModule( 'Caption', Caption ); 3894 3895})( window, jQuery ); 3896 3897// Deep Linking module for Slider Pro. 3898// 3899// Updates the hash of the URL as the user navigates through the slides. 3900// Also, allows navigating to a specific slide by indicating it in the hash. 3901;(function( window, $ ) { 3902 3903 "use strict"; 3904 3905 var NS = 'DeepLinking.' + $.SliderPro.namespace; 3906 3907 var DeepLinking = { 3908 3909 initDeepLinking: function() { 3910 var that = this; 3911 3912 // Parse the initial hash 3913 this.on( 'init.' + NS, function() { 3914 that._gotoHash( window.location.hash ); 3915 }); 3916 3917 // Update the hash when a new slide is selected
3918 this.on( 'gotoSlide.' + NS, function( event ) { 3919 if ( that.settings.updateHash === true ) { 3920 3921 // get the 'id' attribute of the slide 3922 var slideId = that.$slider.find( '.sp-slide' ).eq( event.index ).attr( 'id' ); 3923 3924 // if the slide doesn't have an 'id' attribute, use the slide index 3925 if ( typeof slideId === 'undefined' ) { 3926 slideId = event.index; 3927 } 3928 3929 window.location.hash = that.$slider.attr( 'id' ) + '/' + slideId; 3930 } 3931 }); 3932 3933 // Check when the hash changes and navigate to the indicated slide 3934 $( window ).on( 'hashchange.' + this.uniqueId + '.' + NS, function() { 3935 that._gotoHash( window.location.hash ); 3936 }); 3937 }, 3938 3939 // Parse the hash and return the slider id and the slide id 3940 _parseHash: function( hash ) { 3941 if ( hash !== '' ) { 3942 // Eliminate the # symbol 3943 hash = hash.substring(1); 3944 3945 // Get the specified slider id and slide id 3946 var values = hash.split( '/' ), 3947 slideId = values.pop(), 3948 sliderId = hash.slice( 0, - slideId.toString().length - 1 ); 3949 3950 if ( this.$slider.attr( 'id' ) === sliderId ) { 3951 return { 'sliderID': sliderId, 'slideId': slideId }; 3952 } 3953 } 3954 3955 return false; 3956 }, 3957 3958 // Navigate to the appropriate slide, based on the specified hash 3959 _gotoHash: function( hash ) { 3960 var result = this._parseHash( hash ); 3961 3962 if ( result === false ) { 3963 return; 3964 } 3965 3966 var slideId = result.slideId, 3967 slideIdNumber = parseInt( slideId, 10 ); 3968 3969 // check if the specified slide id is a number or string 3970 if ( isNaN( slideIdNumber ) ) { 3971 // get the index of the slide based on the specified id 3972 var slideIndex = this.$slider.find( '.sp-slide#' + slideId ).index(); 3973 3974 if ( slideIndex !== -1 && slideIndex !== this.selectedSlideIndex ) { 3975 this.gotoSlide( slideIndex ); 3976 } 3977 } else if ( slideIdNumber !== this.selectedSlideIndex ) { 3978 this.gotoSlide( slideIdNumber ); 3979 } 3980 }, 3981 3982 // Destroy the module 3983 destroyDeepLinking: function() { 3984 this.off( 'init.' + NS ); 3985 this.off( 'gotoSlide.' + NS ); 3986 $( window ).off( 'hashchange.' + this.uniqueId + '.' + NS ); 3987 }, 3988 3989 deepLinkingDefaults: { 3990 3991 // Indicates whether the hash will be updated when a new slide is selected 3992 updateHash: false 3993 } 3994 }; 3995 3996 $.SliderPro.addModule( 'DeepLinking', DeepLinking ); 3997 3998})( window, jQuery ); 3999 4000// Autoplay module for Slider Pro. 4001// 4002// Adds automatic navigation through the slides by calling the 4003// 'nextSlide' or 'previousSlide' methods at certain time intervals. 4004;(function( window, $ ) { 4005 4006 "use strict"; 4007 4008 var NS = 'Autoplay.' + $.SliderPro.namespace; 4009 4010 var Autoplay = { 4011 4012 autoplayTimer: null, 4013 4014 isTimerRunning: false, 4015 4016 isTimerPaused: false, 4017 4018 initAutoplay: function() { 4019 this.on( 'update.' + NS, $.proxy( this._autoplayOnUpdate, this ) ); 4020 }, 4021 4022 // Start the autoplay if it's enabled, or stop it if it's disabled but running 4023 _autoplayOnUpdate: function( event ) { 4024 if ( this.settings.autoplay === true ) {
4025 this.on( 'gotoSlide.' + NS, $.proxy( this._autoplayOnGotoSlide, this ) ); 4026 this.on( 'mouseenter.' + NS, $.proxy( this._autoplayOnMouseEnter, this ) ); 4027 this.on( 'mouseleave.' + NS, $.proxy( this._autoplayOnMouseLeave, this ) ); 4028 4029 this.startAutoplay(); 4030 } else { 4031 this.off( 'gotoSlide.' + NS ); 4032 this.off( 'mouseenter.' + NS ); 4033 this.off( 'mouseleave.' + NS ); 4034 4035 this.stopAutoplay(); 4036 } 4037 }, 4038 4039 // Restart the autoplay timer when a new slide is selected 4040 _autoplayOnGotoSlide: function( event ) { 4041 // stop previous timers before starting a new one 4042 if ( this.isTimerRunning === true ) { 4043 this.stopAutoplay(); 4044 } 4045 4046 if ( this.isTimerPaused === false ) { 4047 this.startAutoplay(); 4048 } 4049 }, 4050 4051 // Pause the autoplay when the slider is hovered 4052 _autoplayOnMouseEnter: function( event ) { 4053 if ( this.isTimerRunning && ( this.settings.autoplayOnHover === 'pause' || this.settings.autoplayOnHover === 'stop' ) ) { 4054 this.stopAutoplay(); 4055 this.isTimerPaused = true; 4056 } 4057 }, 4058 4059 // Start the autoplay when the mouse moves away from the slider 4060 _autoplayOnMouseLeave: function( event ) { 4061 if ( this.settings.autoplay === true && this.isTimerRunning === false && this.settings.autoplayOnHover !== 'stop' ) { 4062 this.startAutoplay(); 4063 this.isTimerPaused = false; 4064 } 4065 }, 4066 4067 // Starts the autoplay 4068 startAutoplay: function() { 4069 var that = this; 4070 4071 this.isTimerRunning = true; 4072 4073 this.autoplayTimer = setTimeout(function() { 4074 if ( that.settings.autoplayDirection === 'normal' ) { 4075 that.nextSlide(); 4076 } else if ( that.settings.autoplayDirection === 'backwards' ) { 4077 that.previousSlide(); 4078 } 4079 }, this.settings.autoplayDelay ); 4080 }, 4081 4082 // Stops the autoplay 4083 stopAutoplay: function() { 4084 this.isTimerRunning = false; 4085 this.isTimerPaused = false; 4086 4087 clearTimeout( this.autoplayTimer ); 4088 }, 4089 4090 // Destroy the module 4091 destroyAutoplay: function() { 4092 clearTimeout( this.autoplayTimer ); 4093 4094 this.off( 'update.' + NS ); 4095 this.off( 'gotoSlide.' + NS ); 4096 this.off( 'mouseenter.' + NS ); 4097 this.off( 'mouseleave.' + NS ); 4098 }, 4099 4100 autoplayDefaults: { 4101 // Indicates whether or not autoplay will be enabled 4102 autoplay: true, 4103 4104 // Sets the delay/interval at which the autoplay will run 4105 autoplayDelay: 5000, 4106 4107 // Indicates whether autoplay will navigate to the next slide or previous slide 4108 autoplayDirection: 'normal', 4109 4110 // Indicates if the autoplay will be paused or stopped when the slider is hovered. 4111 // Possible values are 'pause', 'stop' or 'none'. 4112 autoplayOnHover: 'pause' 4113 } 4114 }; 4115 4116 $.SliderPro.addModule( 'Autoplay', Autoplay ); 4117 4118})(window, jQuery); 4119 4120// Keyboard module for Slider Pro. 4121// 4122// Adds the possibility to navigate through slides using the keyboard arrow keys, or 4123// open the link attached to the main slide image by using the Enter key. 4124;(function( window, $ ) { 4125 4126 "use strict"; 4127 4128 var NS = 'Keyboard.' + $.SliderPro.namespace; 4129 4130 var Keyboard = { 4131 4132 initKeyboard: function() { 4133 var that = this, 4134 hasFocus = false; 4135 4136 if ( this.settings.keyboard === false ) { 4137 return; 4138 } 4139 4140 // Detect when the slide is in focus and when it's not, and, optionally, make it 4141 // responsive to keyboard input only when it's in focus 4142 this.$slider.on( 'focus.' + NS, function() { 4143 hasFocus = true; 4144 }); 4145 4146 this.$slider.on( 'blur.' + NS, function() { 4147 hasFocus = false; 4148 }); 4149 4150 $( document ).on( 'keydown.' + this.uniqueId + '.' + NS, function( event ) { 4151 if ( that.settings.keyboardOnlyOnFocus === true && hasFocus === false ) { 4152 return; 4153 } 4154 4155 // If the left arrow key is pressed, go to the previous slide. 4156 // If the right arrow key is pressed, go to the next slide. 4157 // If the Enter key is pressed, open the link attached to the main slide image. 4158 if ( event.which === 37 ) { 4159 that.previousSlide(); 4160 } else if ( event.which === 39 ) { 4161 that.nextSlide(); 4162 } else if ( event.which === 13 ) { 4163 that.$slider.find( '.sp-slide' ).eq( that.selectedSlideIndex ).find( '.sp-image-container a' )[0].click(); 4164 } 4165 }); 4166 }, 4167 4168 // Destroy the module 4169 destroyKeyboard: function() { 4170 this.$slider.off( 'focus.' + NS ); 4171 this.$slider.off( 'blur.' + NS ); 4172 $( document ).off( 'keydown.' + this.uniqueId + '.' + NS ); 4173 }, 4174 4175 keyboardDefaults: { 4176 4177 // Indicates whether keyboard navigation will be enabled 4178 keyboard: true, 4179 4180 // Indicates whether the slider will respond to keyboard input only when
4181 // the slider is in focus. 4182 keyboardOnlyOnFocus: false 4183 } 4184 }; 4185 4186 $.SliderPro.addModule( 'Keyboard', Keyboard ); 4187 4188})( window, jQuery ); 4189 4190// Full Screen module for Slider Pro. 4191// 4192// Adds the possibility to open the slider full-screen, using the HMTL5 FullScreen API. 4193;(function( window, $ ) { 4194 4195 "use strict"; 4196 4197 var NS = 'FullScreen.' + $.SliderPro.namespace; 4198 4199 var FullScreen = { 4200 4201 // Indicates whether the slider is currently in full-screen mode 4202 isFullScreen: false, 4203 4204 // Reference to the full-screen button 4205 $fullScreenButton: null, 4206 4207 // Reference to a set of settings that influence the slider's size 4208 // before it goes full-screen 4209 sizeBeforeFullScreen: {}, 4210 4211 initFullScreen: function() { 4212 if ( ! ( document.fullscreenEnabled || 4213 document.webkitFullscreenEnabled || 4214 document.mozFullScreenEnabled || 4215 document.msFullscreenEnabled ) ) { 4216 return; 4217 } 4218 4219 this.on( 'update.' + NS, $.proxy( this._fullScreenOnUpdate, this ) ); 4220 }, 4221 4222 // Create or remove the full-screen button depending on the value of the 'fullScreen' option 4223 _fullScreenOnUpdate: function() { 4224 if ( this.settings.fullScreen === true && this.$fullScreenButton === null ) { 4225 this._addFullScreen(); 4226 } else if ( this.settings.fullScreen === false && this.$fullScreenButton !== null ) { 4227 this._removeFullScreen(); 4228 } 4229 4230 if ( this.settings.fullScreen === true ) { 4231 if ( this.settings.fadeFullScreen === true ) { 4232 this.$fullScreenButton.addClass( 'sp-fade-full-screen' ); 4233 } else if ( this.settings.fadeFullScreen === false ) { 4234 this.$fullScreenButton.removeClass( 'sp-fade-full-screen' ); 4235 } 4236 } 4237 }, 4238 4239 // Create the full-screen button 4240 _addFullScreen: function() { 4241 this.$fullScreenButton = $('<div class="sp-full-screen-button"></div>').appendTo( this.$slider ); 4242 this.$fullScreenButton.on( 'click.' + NS, $.proxy( this._onFullScreenButtonClick, this ) ); 4243 4244 document.addEventListener( 'fullscreenchange', $.proxy( this._onFullScreenChange, this ) ); 4245 document.addEventListener( 'mozfullscreenchange', $.proxy( this._onFullScreenChange, this ) ); 4246 document.addEventListener( 'webkitfullscreenchange', $.proxy( this._onFullScreenChange, this ) ); 4247 document.addEventListener( 'MSFullscreenChange', $.proxy( this._onFullScreenChange, this ) ); 4248 }, 4249 4250 // Remove the full-screen button 4251 _removeFullScreen: function() { 4252 if ( this.$fullScreenButton !== null ) { 4253 this.$fullScreenButton.off( 'click.' + NS ); 4254 this.$fullScreenButton.remove(); 4255 this.$fullScreenButton = null; 4256 document.removeEventListener( 'fullscreenchange', this._onFullScreenChange ); 4257 document.removeEventListener( 'mozfullscreenchange', this._onFullScreenChange ); 4258 document.removeEventListener( 'webkitfullscreenchange', this._onFullScreenChange ); 4259 document.removeEventListener( 'MSFullscreenChange', this._onFullScreenChange ); 4260 } 4261 }, 4262 4263 // When the full-screen button is clicked, put the slider into full-screen mode, and 4264 // take it out of the full-screen mode when it's clicked again. 4265 _onFullScreenButtonClick: function() { 4266 if ( this.isFullScreen === false ) { 4267 if ( this.instance.requestFullScreen ) { 4268 this.instance.requestFullScreen(); 4269 } else if ( this.instance.mozRequestFullScreen ) { 4270 this.instance.mozRequestFullScreen(); 4271 } else if ( this.instance.webkitRequestFullScreen ) { 4272 this.instance.webkitRequestFullScreen(); 4273 } else if ( this.instance.msRequestFullscreen ) { 4274 this.instance.msRequestFullscreen(); 4275 } 4276 } else { 4277 if ( document.exitFullScreen ) { 4278 document.exitFullScreen(); 4279 } else if ( document.mozCancelFullScreen ) { 4280 document.mozCancelFullScreen(); 4281 } else if ( document.webkitCancelFullScreen ) { 4282 document.webkitCancelFullScreen(); 4283 } else if ( document.msExitFullscreen ) { 4284 document.msExitFullscreen(); 4285 } 4286 } 4287 }, 4288 4289 // This will be called whenever the full-screen mode changes. 4290 // If the slider is in full-screen mode, set it to 'full window', and if it's 4291 // not in full-screen mode anymore, set it back to the original size. 4292 _onFullScreenChange: function() { 4293 this.isFullScreen = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement ? true : false;
4294 4295 if ( this.isFullScreen === true ) { 4296 this.sizeBeforeFullScreen = { forceSize: this.settings.forceSize, autoHeight: this.settings.autoHeight }; 4297 this.$slider.addClass( 'sp-full-screen' ); 4298 this.settings.forceSize = 'fullWindow'; 4299 this.settings.autoHeight = false; 4300 } else { 4301 this.$slider.css( 'margin', '' ); 4302 this.$slider.removeClass( 'sp-full-screen' ); 4303 this.settings.forceSize = this.sizeBeforeFullScreen.forceSize; 4304 this.settings.autoHeight = this.sizeBeforeFullScreen.autoHeight; 4305 } 4306 4307 this.resize(); 4308 }, 4309 4310 // Destroy the module 4311 destroyFullScreen: function() { 4312 this.off( 'update.' + NS ); 4313 this._removeFullScreen(); 4314 }, 4315 4316 fullScreenDefaults: { 4317 4318 // Indicates whether the full-screen button is enabled 4319 fullScreen: false, 4320 4321 // Indicates whether the button will fade in only on hover 4322 fadeFullScreen: true 4323 } 4324 }; 4325 4326 $.SliderPro.addModule( 'FullScreen', FullScreen ); 4327 4328})( window, jQuery ); 4329 4330// Buttons module for Slider Pro. 4331// 4332// Adds navigation buttons at the bottom of the slider. 4333;(function( window, $ ) { 4334 4335 "use strict"; 4336 4337 var NS = 'Buttons.' + $.SliderPro.namespace; 4338 4339 var Buttons = { 4340 4341 // Reference to the buttons container 4342 $buttons: null, 4343 4344 initButtons: function() { 4345 this.on( 'update.' + NS, $.proxy( this._buttonsOnUpdate, this ) ); 4346 }, 4347 4348 _buttonsOnUpdate: function() { 4349 this.$buttons = this.$slider.find('.sp-buttons'); 4350 4351 // If there is more that one slide but the buttons weren't created yet, create the buttons. 4352 // If the buttons were created but their number differs from the total number of slides, re-create the buttons. 4353 // If the buttons were created but there are less than one slide, remove the buttons.s 4354 if ( this.settings.buttons === true && this.getTotalSlides() > 1 && this.$buttons.length === 0 ) { 4355 this._createButtons(); 4356 } else if ( this.settings.buttons === true && this.getTotalSlides() !== this.$buttons.find( '.sp-button' ).length && this.$buttons.length !== 0 ) { 4357 this._adjustButtons(); 4358 } else if ( this.settings.buttons === false || ( this.getTotalSlides() <= 1 && this.$buttons.length !== 0 ) ) { 4359 this._removeButtons(); 4360 } 4361 }, 4362 4363 // Create the buttons 4364 _createButtons: function() { 4365 var that = this; 4366 4367 // Create the buttons' container 4368 this.$buttons = $( '<div class="sp-buttons"></div>' ).appendTo( this.$slider ); 4369 4370 // Create the buttons 4371 for ( var i = 0; i < this.getTotalSlides(); i++ ) { 4372 $( '<div class="sp-button"></div>' ).appendTo( this.$buttons ); 4373 } 4374 4375 // Listen for button clicks 4376 this.$buttons.on( 'click.' + NS, '.sp-button', function() { 4377 that.gotoSlide( $( this ).index() ); 4378 }); 4379 4380 // Set the initially selected button 4381 this.$buttons.find( '.sp-button' ).eq( this.selectedSlideIndex ).addClass( 'sp-selected-button' ); 4382 4383 // Select the corresponding button when the slide changes
4384 this.on( 'gotoSlide.' + NS, function( event ) { 4385 that.$buttons.find( '.sp-selected-button' ).removeClass( 'sp-selected-button' ); 4386 that.$buttons.find( '.sp-button' ).eq( event.index ).addClass( 'sp-selected-button' ); 4387 }); 4388 4389 // Indicate that the slider has buttons 4390 this.$slider.addClass( 'sp-has-buttons' ); 4391 }, 4392 4393 // Re-create the buttons. This is calles when the number of slides changes. 4394 _adjustButtons: function() { 4395 this.$buttons.empty(); 4396 4397 // Create the buttons 4398 for ( var i = 0; i < this.getTotalSlides(); i++ ) { 4399 $( '<div class="sp-button"></div>' ).appendTo( this.$buttons ); 4400 } 4401 4402 // Change the selected the buttons 4403 this.$buttons.find( '.sp-selected-button' ).removeClass( 'sp-selected-button' ); 4404 this.$buttons.find( '.sp-button' ).eq( this.selectedSlideIndex ).addClass( 'sp-selected-button' ); 4405 }, 4406 4407 // Remove the buttons 4408 _removeButtons: function() { 4409 this.$buttons.off( 'click.' + NS, '.sp-button' ); 4410 this.off( 'gotoSlide.' + NS ); 4411 this.$buttons.remove(); 4412 this.$slider.removeClass( 'sp-has-buttons' ); 4413 }, 4414 4415 destroyButtons: function() { 4416 this._removeButtons(); 4417 this.off( 'update.' + NS ); 4418 }, 4419 4420 buttonsDefaults: { 4421 4422 // Indicates whether the buttons will be created 4423 buttons: true 4424 } 4425 }; 4426 4427 $.SliderPro.addModule( 'Buttons', Buttons ); 4428 4429})( window, jQuery ); 4430 4431// Arrows module for Slider Pro. 4432// 4433// Adds arrows for navigating to the next or previous slide. 4434;(function( window, $ ) { 4435 4436 "use strict"; 4437 4438 var NS = 'Arrows.' + $.SliderPro.namespace; 4439 4440 var Arrows = { 4441 4442 // Reference to the arrows container 4443 $arrows: null, 4444 4445 // Reference to the previous arrow 4446 $previousArrow: null, 4447 4448 // Reference to the next arrow 4449 $nextArrow: null, 4450 4451 initArrows: function() { 4452 this.on( 'update.' + NS, $.proxy( this._arrowsOnUpdate, this ) );
4453 this.on( 'gotoSlide.' + NS, $.proxy( this._checkArrowsVisibility, this ) ); 4454 }, 4455 4456 _arrowsOnUpdate: function() { 4457 var that = this; 4458 4459 // Create the arrows if the 'arrows' option is set to true 4460 if ( this.settings.arrows === true && this.$arrows === null ) { 4461 this.$arrows = $( '<div class="sp-arrows"></div>' ).appendTo( this.$slidesContainer ); 4462 4463 this.$previousArrow = $( '<div class="sp-arrow sp-previous-arrow"></div>' ).appendTo( this.$arrows ); 4464 this.$nextArrow = $( '<div class="sp-arrow sp-next-arrow"></div>' ).appendTo( this.$arrows ); 4465 4466 this.$previousArrow.on( 'click.' + NS, function() { 4467 that.previousSlide(); 4468 }); 4469 4470 this.$nextArrow.on( 'click.' + NS, function() { 4471 that.nextSlide(); 4472 }); 4473 4474 this._checkArrowsVisibility(); 4475 } else if ( this.settings.arrows === false && this.$arrows !== null ) { 4476 this._removeArrows(); 4477 } 4478 4479 if ( this.settings.arrows === true ) { 4480 if ( this.settings.fadeArrows === true ) { 4481 this.$arrows.addClass( 'sp-fade-arrows' ); 4482 } else if ( this.settings.fadeArrows === false ) { 4483 this.$arrows.removeClass( 'sp-fade-arrows' ); 4484 } 4485 } 4486 }, 4487 4488 // Show or hide the arrows depending on the position of the selected slide 4489 _checkArrowsVisibility: function() { 4490 if ( this.settings.arrows === false || this.settings.loop === true ) { 4491 return; 4492 } 4493 4494 if ( this.selectedSlideIndex === 0 ) { 4495 this.$previousArrow.css( 'display', 'none' ); 4496 } else { 4497 this.$previousArrow.css( 'display', 'block' ); 4498 } 4499 4500 if ( this.selectedSlideIndex === this.getTotalSlides() - 1 ) { 4501 this.$nextArrow.css( 'display', 'none' ); 4502 } else { 4503 this.$nextArrow.css( 'display', 'block' ); 4504 } 4505 }, 4506 4507 _removeArrows: function() { 4508 if ( this.$arrows !== null ) { 4509 this.$previousArrow.off( 'click.' + NS ); 4510 this.$nextArrow.off( 'click.' + NS ); 4511 this.$arrows.remove(); 4512 this.$arrows = null; 4513 } 4514 }, 4515 4516 destroyArrows: function() { 4517 this._removeArrows(); 4518 this.off( 'update.' + NS ); 4519 this.off( 'gotoSlide.' + NS ); 4520 }, 4521 4522 arrowsDefaults: { 4523 4524 // Indicates whether the arrow buttons will be created 4525 arrows: false, 4526 4527 // Indicates whether the arrows will fade in only on hover 4528 fadeArrows: true 4529 } 4530 }; 4531 4532 $.SliderPro.addModule( 'Arrows', Arrows ); 4533 4534})( window, jQuery ); 4535 4536// Thumbnail Touch Swipe module for Slider Pro. 4537// 4538// Adds touch-swipe functionality for thumbnails. 4539;(function( window, $ ) { 4540 4541 "use strict"; 4542 4543 var NS = 'ThumbnailTouchSwipe.' + $.SliderPro.namespace; 4544 4545 var ThumbnailTouchSwipe = { 4546 4547 // The x and y coordinates of the pointer/finger's starting position 4548 thumbnailTouchStartPoint: { x: 0, y: 0 }, 4549 4550 // The x and y coordinates of the pointer/finger's end position 4551 thumbnailTouchEndPoint: { x: 0, y: 0 }, 4552 4553 // The distance from the starting to the end position on the x and y axis 4554 thumbnailTouchDistance: { x: 0, y: 0 }, 4555 4556 // The position of the thumbnail scroller when the touch swipe starts 4557 thumbnailTouchStartPosition: 0, 4558 4559 // Indicates if the thumbnail scroller is being swiped 4560 isThumbnailTouchMoving: false, 4561 4562 // Indicates if the touch swipe was initialized 4563 isThumbnailTouchSwipe: false, 4564 4565 // Stores the names of the events 4566 thumbnailTouchSwipeEvents: { startEvent: '', moveEvent: '', endEvent: '' }, 4567 4568 initThumbnailTouchSwipe: function() { 4569 this.on( 'update.' + NS, $.proxy( this._thumbnailTouchSwipeOnUpdate, this ) ); 4570 }, 4571 4572 _thumbnailTouchSwipeOnUpdate: function() { 4573 4574 // Return if there are no thumbnails 4575 if ( this.isThumbnailScroller === false ) { 4576 return; 4577 } 4578 4579 // Initialize the touch swipe functionality if it wasn't initialized yet 4580 if ( this.settings.thumbnailTouchSwipe === true && this.isThumbnailTouchSwipe === false ) { 4581 this.isThumbnailTouchSwipe = true; 4582 4583 this.thumbnailTouchSwipeEvents.startEvent = 'touchstart' + '.' + NS + ' mousedown' + '.' + NS; 4584 this.thumbnailTouchSwipeEvents.moveEvent = 'touchmove' + '.' + NS + ' mousemove' + '.' + NS; 4585 this.thumbnailTouchSwipeEvents.endEvent = 'touchend' + '.' + this.uniqueId + '.' + NS + ' mouseup' + '.' + this.uniqueId + '.' + NS; 4586 4587 // Listen for touch swipe/mouse move events 4588 this.$thumbnails.on( this.thumbnailTouchSwipeEvents.startEvent, $.proxy( this._onThumbnailTouchStart, this ) ); 4589 this.$thumbnails.on( 'dragstart.' + NS, function( event ) { 4590 event.preventDefault(); 4591 }); 4592 4593 // Add the grabbing icon 4594 this.$thumbnails.addClass( 'sp-grab' ); 4595 } 4596 4597 // Remove the default thumbnailClick 4598 $.each( this.thumbnails, function( index, thumbnail ) { 4599 thumbnail.off( 'thumbnailClick' ); 4600 }); 4601 }, 4602 4603 // Called when the thumbnail scroller starts being dragged 4604 _onThumbnailTouchStart: function( event ) { 4605 // Disable dragging if the element is set to allow selections 4606 if ( $( event.target ).closest( '.sp-selectable' ).length >= 1 ) { 4607 return; 4608 } 4609 4610 var that = this, 4611 eventObject = typeof event.originalEvent.touches !== 'undefined' ? event.originalEvent.touches[0] : event.originalEvent; 4612 4613 // Prevent default behavior for mouse events 4614 if ( typeof event.originalEvent.touches === 'undefined' ) { 4615 event.preventDefault(); 4616 } 4617 4618 // Disable click events on links
4619 $( event.target ).parents( '.sp-thumbnail-container' ).find( 'a' ).one( 'click.' + NS, function( event ) { 4620 event.preventDefault(); 4621 }); 4622 4623 // Get the initial position of the mouse pointer and the initial position 4624 // of the thumbnail scroller 4625 this.thumbnailTouchStartPoint.x = eventObject.pageX || eventObject.clientX; 4626 this.thumbnailTouchStartPoint.y = eventObject.pageY || eventObject.clientY; 4627 this.thumbnailTouchStartPosition = this.thumbnailsPosition; 4628 4629 // Clear the previous distance values 4630 this.thumbnailTouchDistance.x = this.thumbnailTouchDistance.y = 0; 4631 4632 // If the thumbnail scroller is being grabbed while it's still animating, stop the 4633 // current movement 4634 if ( this.$thumbnails.hasClass( 'sp-animated' ) ) { 4635 this.isThumbnailTouchMoving = true; 4636 this._stopThumbnailsMovement(); 4637 this.thumbnailTouchStartPosition = this.thumbnailsPosition; 4638 } 4639 4640 // Listen for move and end events 4641 this.$thumbnails.on( this.thumbnailTouchSwipeEvents.moveEvent, $.proxy( this._onThumbnailTouchMove, this ) ); 4642 $( document ).on( this.thumbnailTouchSwipeEvents.endEvent, $.proxy( this._onThumbnailTouchEnd, this ) ); 4643 4644 // Swap grabbing icons 4645 this.$thumbnails.removeClass( 'sp-grab' ).addClass( 'sp-grabbing' ); 4646 4647 // Add 'sp-swiping' class to indicate that the thumbnail scroller is being swiped 4648 this.$thumbnailsContainer.addClass( 'sp-swiping' ); 4649 }, 4650 4651 // Called during the thumbnail scroller's dragging 4652 _onThumbnailTouchMove: function( event ) { 4653 var eventObject = typeof event.originalEvent.touches !== 'undefined' ? event.originalEvent.touches[0] : event.originalEvent; 4654 4655 // Indicate that the move event is being fired 4656 this.isThumbnailTouchMoving = true; 4657 4658 // Get the current position of the mouse pointer 4659 this.thumbnailTouchEndPoint.x = eventObject.pageX || eventObject.clientX; 4660 this.thumbnailTouchEndPoint.y = eventObject.pageY || eventObject.clientY; 4661 4662 // Calculate the distance of the movement on both axis 4663 this.thumbnailTouchDistance.x = this.thumbnailTouchEndPoint.x - this.thumbnailTouchStartPoint.x; 4664 this.thumbnailTouchDistance.y = this.thumbnailTouchEndPoint.y - this.thumbnailTouchStartPoint.y; 4665 4666 // Calculate the distance of the swipe that takes place in the same direction as the orientation of the thumbnails 4667 // and calculate the distance from the opposite direction. 4668 // 4669 // For a swipe to be valid there should more distance in the same direction as the orientation of the thumbnails. 4670 var distance = this.thumbnailsOrientation === 'horizontal' ? this.thumbnailTouchDistance.x : this.thumbnailTouchDistance.y, 4671 oppositeDistance = this.thumbnailsOrientation === 'horizontal' ? this.thumbnailTouchDistance.y : this.thumbnailTouchDistance.x; 4672 4673 // If the movement is in the same direction as the orientation of the thumbnails, the swipe is valid 4674 if ( Math.abs( distance ) > Math.abs( oppositeDistance ) ) { 4675 event.preventDefault(); 4676 } else { 4677 return; 4678 } 4679 4680 // Make the thumbnail scroller move slower if it's dragged outside its bounds 4681 if ( this.thumbnailsPosition >= 0 ) { 4682 var infOffset = - this.thumbnailTouchStartPosition; 4683 distance = infOffset + ( distance - infOffset ) * 0.2; 4684 } else if ( this.thumbnailsPosition <= - this.thumbnailsSize + this.thumbnailsContainerSize ) { 4685 var supOffset = this.thumbnailsSize - this.thumbnailsContainerSize + this.thumbnailTouchStartPosition; 4686 distance = - supOffset + ( distance + supOffset ) * 0.2; 4687 } 4688 4689 this._moveThumbnailsTo( this.thumbnailTouchStartPosition + distance, true ); 4690 }, 4691 4692 // Called when the thumbnail scroller is released 4693 _onThumbnailTouchEnd: function( event ) { 4694 var that = this, 4695 thumbnailTouchDistance = this.thumbnailsOrientation === 'horizontal' ? this.thumbnailTouchDistance.x : this.thumbnailTouchDistance.y; 4696 4697 // Remove the move and end listeners 4698 this.$thumbnails.off( this.thumbnailTouchSwipeEvents.moveEvent ); 4699 $( document ).off( this.thumbnailTouchSwipeEvents.endEvent ); 4700 4701 // Swap grabbing icons 4702 this.$thumbnails.removeClass( 'sp-grabbing' ).addClass( 'sp-grab' ); 4703
4704 // Check if there is intention for a tap/click 4705 if ( this.isThumbnailTouchMoving === false || 4706 this.isThumbnailTouchMoving === true && 4707 Math.abs( this.thumbnailTouchDistance.x ) < 10 && 4708 Math.abs( this.thumbnailTouchDistance.y ) < 10 4709 ) { 4710 var targetThumbnail = $( event.target ).hasClass( 'sp-thumbnail-container' ) ? $( event.target ) : $( event.target ).parents( '.sp-thumbnail-container' ), 4711 index = targetThumbnail.index(); 4712 4713 // If a link is cliked, navigate to that link, else navigate to the slide that corresponds to the thumbnail 4714 if ( $( event.target ).parents( 'a' ).length !== 0 ) { 4715 $( event.target ).parents( 'a' ).off( 'click.' + NS ); 4716 this.$thumbnailsContainer.removeClass( 'sp-swiping' ); 4717 } else if ( index !== this.selectedThumbnailIndex && index !== -1 ) { 4718 this.gotoSlide( index ); 4719 } 4720 4721 return; 4722 } 4723 4724 this.isThumbnailTouchMoving = false; 4725 4726 $( event.target ).parents( '.sp-thumbnail' ).one( 'click', function( event ) { 4727 event.preventDefault(); 4728 }); 4729 4730 // Remove the 'sp-swiping' class but with a delay 4731 // because there might be other event listeners that check 4732 // the existence of this class, and this class should still be 4733 // applied for those listeners, since there was a swipe event 4734 setTimeout(function() { 4735 that.$thumbnailsContainer.removeClass( 'sp-swiping' ); 4736 }, 1 ); 4737 4738 // Keep the thumbnail scroller inside the bounds 4739 if ( this.thumbnailsPosition > 0 ) { 4740 this._moveThumbnailsTo( 0 ); 4741 } else if ( this.thumbnailsPosition < this.thumbnailsContainerSize - this.thumbnailsSize ) { 4742 this._moveThumbnailsTo( this.thumbnailsContainerSize - this.thumbnailsSize ); 4743 } 4744 4745 // Fire the 'thumbnailsMoveComplete' event 4746 this.trigger({ type: 'thumbnailsMoveComplete' }); 4747 if ( $.isFunction( this.settings.thumbnailsMoveComplete ) ) { 4748 this.settings.thumbnailsMoveComplete.call( this, { type: 'thumbnailsMoveComplete' }); 4749 } 4750 }, 4751 4752 // Destroy the module 4753 destroyThumbnailTouchSwipe: function() { 4754 this.off( 'update.' + NS ); 4755 4756 if ( this.isThumbnailScroller === false ) { 4757 return; 4758 } 4759 4760 this.$thumbnails.off( this.thumbnailTouchSwipeEvents.startEvent ); 4761 this.$thumbnails.off( this.thumbnailTouchSwipeEvents.moveEvent ); 4762 this.$thumbnails.off( 'dragstart.' + NS ); 4763 $( document ).off( this.thumbnailTouchSwipeEvents.endEvent ); 4764 this.$thumbnails.removeClass( 'sp-grab' ); 4765 }, 4766 4767 thumbnailTouchSwipeDefaults: { 4768 4769 // Indicates whether the touch swipe will be enabled for thumbnails 4770 thumbnailTouchSwipe: true 4771 } 4772 }; 4773 4774 $.SliderPro.addModule( 'ThumbnailTouchSwipe', ThumbnailTouchSwipe ); 4775 4776})( window, jQuery ); 4777 4778// Thumbnail Arrows module for Slider Pro. 4779// 4780// Adds thumbnail arrows for moving the thumbnail scroller. 4781;(function( window, $ ) { 4782 4783 "use strict"; 4784 4785 var NS = 'ThumbnailArrows.' + $.SliderPro.namespace; 4786 4787 var ThumbnailArrows = { 4788 4789 // Reference to the arrows container 4790 $thumbnailArrows: null, 4791 4792 // Reference to the 'previous' thumbnail arrow 4793 $previousThumbnailArrow: null, 4794 4795 // Reference to the 'next' thumbnail arrow 4796 $nextThumbnailArrow: null, 4797 4798 initThumbnailArrows: function() { 4799 var that = this; 4800 4801 this.on( 'update.' + NS, $.proxy( this._thumbnailArrowsOnUpdate, this ) ); 4802 4803 // Check if the arrows need to be visible or invisible when the thumbnail scroller 4804 // resizes and when the thumbnail scroller moves. 4805 this.on( 'sliderResize.' + NS + ' ' + 'thumbnailsMoveComplete.' + NS, function() { 4806 if ( that.isThumbnailScroller === true && that.settings.thumbnailArrows === true ) { 4807 that._checkThumbnailArrowsVisibility(); 4808 } 4809 }); 4810 }, 4811 4812 // Called when the slider is updated 4813 _thumbnailArrowsOnUpdate: function() { 4814 var that = this; 4815 4816 if ( this.isThumbnailScroller === false ) { 4817 return; 4818 } 4819 4820 // Create or remove the thumbnail scroller arrows 4821 if ( this.settings.thumbnailArrows === true && this.$thumbnailArrows === null ) { 4822 this.$thumbnailArrows = $( '<div class="sp-thumbnail-arrows"></div>' ).appendTo( this.$thumbnailsContainer ); 4823
4824 this.$previousThumbnailArrow = $( '<div class="sp-thumbnail-arrow sp-previous-thumbnail-arrow"></div>' ).appendTo( this.$thumbnailArrows ); 4825 this.$nextThumbnailArrow = $( '<div class="sp-thumbnail-arrow sp-next-thumbnail-arrow"></div>' ).appendTo( this.$thumbnailArrows ); 4826 4827 this.$previousThumbnailArrow.on( 'click.' + NS, function() { 4828 var previousPosition = Math.min( 0, that.thumbnailsPosition + that.thumbnailsContainerSize ); 4829 that._moveThumbnailsTo( previousPosition ); 4830 }); 4831 4832 this.$nextThumbnailArrow.on( 'click.' + NS, function() { 4833 var nextPosition = Math.max( that.thumbnailsContainerSize - that.thumbnailsSize, that.thumbnailsPosition - that.thumbnailsContainerSize ); 4834 that._moveThumbnailsTo( nextPosition ); 4835 }); 4836 } else if ( this.settings.thumbnailArrows === false && this.$thumbnailArrows !== null ) { 4837 this._removeThumbnailArrows(); 4838 } 4839 4840 // Add fading functionality and check if the arrows need to be visible or not 4841 if ( this.settings.thumbnailArrows === true ) { 4842 if ( this.settings.fadeThumbnailArrows === true ) { 4843 this.$thumbnailArrows.addClass( 'sp-fade-thumbnail-arrows' ); 4844 } else if ( this.settings.fadeThumbnailArrows === false ) { 4845 this.$thumbnailArrows.removeClass( 'sp-fade-thumbnail-arrows' ); 4846 } 4847 4848 this._checkThumbnailArrowsVisibility(); 4849 } 4850 }, 4851 4852 // Checks if the 'next' or 'previous' arrows need to be visible or hidden, 4853 // based on the position of the thumbnail scroller 4854 _checkThumbnailArrowsVisibility: function() { 4855 if ( this.thumbnailsPosition === 0 ) { 4856 this.$previousThumbnailArrow.css( 'display', 'none' ); 4857 } else { 4858 this.$previousThumbnailArrow.css( 'display', 'block' ); 4859 } 4860 4861 if ( this.thumbnailsPosition === this.thumbnailsContainerSize - this.thumbnailsSize ) { 4862 this.$nextThumbnailArrow.css( 'display', 'none' ); 4863 } else { 4864 this.$nextThumbnailArrow.css( 'display', 'block' ); 4865 } 4866 }, 4867 4868 // Remove the thumbnail arrows 4869 _removeThumbnailArrows: function() { 4870 if ( this.$thumbnailArrows !== null ) { 4871 this.$previousThumbnailArrow.off( 'click.' + NS ); 4872 this.$nextThumbnailArrow.off( 'click.' + NS ); 4873 this.$thumbnailArrows.remove(); 4874 this.$thumbnailArrows = null; 4875 } 4876 }, 4877 4878 // Destroy the module 4879 destroyThumbnailArrows: function() { 4880 this._removeThumbnailArrows(); 4881 this.off( 'update.' + NS ); 4882 this.off( 'sliderResize.' + NS ); 4883 this.off( 'thumbnailsMoveComplete.' + NS ); 4884 }, 4885 4886 thumbnailArrowsDefaults: { 4887 4888 // Indicates whether the thumbnail arrows will be enabled 4889 thumbnailArrows: false, 4890 4891 // Indicates whether the thumbnail arrows will be faded 4892 fadeThumbnailArrows: true 4893 } 4894 }; 4895 4896 $.SliderPro.addModule( 'ThumbnailArrows', ThumbnailArrows ); 4897 4898})( window, jQuery ); 4899 4900// Video module for Slider Pro 4901// 4902// Adds automatic control for several video players and providers 4903;(function( window, $ ) { 4904 4905 "use strict"; 4906 4907 var NS = 'Video.' + $.SliderPro.namespace; 4908 4909 var Video = { 4910 4911 firstInit: false, 4912 4913 initVideo: function() { 4914 this.on( 'update.' + NS, $.proxy( this._videoOnUpdate, this ) );
4915 this.on( 'gotoSlideComplete.' + NS, $.proxy( this._videoOnGotoSlideComplete, this ) ); 4916 }, 4917 4918 _videoOnUpdate: function() { 4919 var that = this; 4920 4921 // Find all the inline videos and initialize them 4922 this.$slider.find( '.sp-video' ).not( 'a, [data-video-init]' ).each(function() { 4923 var video = $( this ); 4924 that._initVideo( video ); 4925 }); 4926 4927 // Find all the lazy-loaded videos and preinitialize them. They will be initialized 4928 // only when their play button is clicked. 4929 this.$slider.find( 'a.sp-video' ).not( '[data-video-preinit]' ).each(function() { 4930 var video = $( this ); 4931 that._preinitVideo( video ); 4932 }); 4933 4934 // call the 'gotoSlideComplete' method in case the first slide contains a video that 4935 // needs to play automatically 4936 if ( this.firstInit === false ) { 4937 this.firstInit = true; 4938 this._videoOnGotoSlideComplete({ index: this.selectedSlideIndex, previousIndex: -1 }); 4939 } 4940 }, 4941 4942 // Initialize the target video 4943 _initVideo: function( video ) { 4944 var that = this; 4945 4946 video.attr( 'data-video-init', true ) 4947 .videoController(); 4948 4949 // When the video starts playing, pause the autoplay if it's running 4950 video.on( 'videoPlay.' + NS, function() { 4951 if ( that.settings.playVideoAction === 'stopAutoplay' && typeof that.stopAutoplay !== 'undefined' ) { 4952 that.stopAutoplay(); 4953 that.settings.autoplay = false; 4954 } 4955 4956 // Fire the 'videoPlay' event 4957 var eventObject = { type: 'videoPlay', video: video }; 4958 that.trigger( eventObject ); 4959 if ( $.isFunction( that.settings.videoPlay ) ) { 4960 that.settings.videoPlay.call( that, eventObject ); 4961 } 4962 }); 4963 4964 // When the video is paused, restart the autoplay 4965 video.on( 'videoPause.' + NS, function() { 4966 if ( that.settings.pauseVideoAction === 'startAutoplay' && typeof that.startAutoplay !== 'undefined' ) { 4967 that.startAutoplay(); 4968 that.settings.autoplay = true; 4969 } 4970 4971 // Fire the 'videoPause' event 4972 var eventObject = { type: 'videoPause', video: video }; 4973 that.trigger( eventObject ); 4974 if ( $.isFunction( that.settings.videoPause ) ) { 4975 that.settings.videoPause.call( that, eventObject ); 4976 } 4977 }); 4978 4979 // When the video ends, restart the autoplay (which was paused during the playback), or 4980 // go to the next slide, or replay the video 4981 video.on( 'videoEnded.' + NS, function() { 4982 if ( that.settings.endVideoAction === 'startAutoplay' && typeof that.startAutoplay !== 'undefined' ) { 4983 that.startAutoplay(); 4984 that.settings.autoplay = true; 4985 } else if ( that.settings.endVideoAction === 'nextSlide' ) { 4986 that.nextSlide(); 4987 } else if ( that.settings.endVideoAction === 'replayVideo' ) { 4988 video.videoController( 'replay' ); 4989 } 4990 4991 // Fire the 'videoEnd' event 4992 var eventObject = { type: 'videoEnd', video: video }; 4993 that.trigger( eventObject ); 4994 if ( $.isFunction(that.settings.videoEnd ) ) { 4995 that.settings.videoEnd.call( that, eventObject ); 4996 } 4997 }); 4998 }, 4999 5000 // Pre-initialize the video. This is for lazy loaded videos. 5001 _preinitVideo: function( video ) { 5002 var that = this; 5003 5004 video.attr( 'data-video-preinit', true ); 5005 5006 // When the video poster is clicked, remove the poster and create 5007 // the inline video 5008 video.on( 'click.' + NS, function( event ) { 5009 5010 // If the video is being dragged, don't start the video 5011 if ( that.$slider.hasClass( 'sp-swiping' ) ) { 5012 return; 5013 } 5014 5015 event.preventDefault(); 5016 5017 var href = video.attr( 'href' ), 5018 iframe, 5019 provider, 5020 regExp, 5021 match, 5022 id, 5023 src, 5024 videoAttributes, 5025 videoWidth = video.children( 'img' ).attr( 'width' ) || video.children( 'img' ).width(), 5026 videoHeight = video.children( 'img' ).attr( 'height') || video.children( 'img' ).height(); 5027 5028 // Check if it's a youtube or vimeo video 5029 if ( href.indexOf( 'youtube' ) !== -1 || href.indexOf( 'youtu.be' ) !== -1 ) { 5030 provider = 'youtube'; 5031 } else if ( href.indexOf( 'vimeo' ) !== -1 ) { 5032 provider = 'vimeo'; 5033 } 5034 5035 // Get the id of the video 5036 regExp = provider === 'youtube' ? /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/ : /http:\/\/(www\.)?vimeo.com\/(\d+)/; 5037 match = href.match( regExp ); 5038 id = match[2]; 5039 5040 // Get the source of the iframe that will be created 5041 src = provider === 'youtube' ? '//www.youtube.com/embed/' + id + '?enablejsapi=1&wmode=opaque' : '//player.vimeo.com/video/'+ id +'?api=1'; 5042 5043 // Get the attributes passed to the video link and then pass them to the iframe's src 5044 videoAttributes = href.split( '?' )[ 1 ]; 5045 5046 if ( typeof videoAttributes !== 'undefined' ) { 5047 videoAttributes = videoAttributes.split( '&' ); 5048 5049 $.each( videoAttributes, function( index, value ) { 5050 if ( value.indexOf( id ) === -1 ) { 5051 src += '&' + value; 5052 } 5053 }); 5054 } 5055 5056 // Create the iframe 5057 iframe = $( '<iframe></iframe>' ) 5058 .attr({ 5059 'src': src, 5060 'width': videoWidth, 5061 'height': videoHeight, 5062 'class': video.attr( 'class' ), 5063 'frameborder': 0, 5064 'allowfullscreen': 'allowfullscreen' 5065 }).insertBefore( video ); 5066 5067 // Initialize the video and play it 5068 that._initVideo( iframe ); 5069 iframe.videoController( 'play' ); 5070 5071 // Hide the video poster 5072 video.css( 'display', 'none' ); 5073 }); 5074 }, 5075 5076 // Called when a new slide is selected 5077 _videoOnGotoSlideComplete: function( event ) { 5078 5079 // Get the video from the previous slide 5080 var previousVideo = this.$slides.find( '.sp-slide' ).eq( event.previousIndex ).find( '.sp-video[data-video-init]' ); 5081 5082 // Handle the video from the previous slide by stopping it, or pausing it, 5083 // or remove it, depending on the value of the 'leaveVideoAction' option. 5084 if ( event.previousIndex !== -1 && previousVideo.length !== 0 ) { 5085 if ( this.settings.leaveVideoAction === 'stopVideo' ) { 5086 previousVideo.videoController( 'stop' ); 5087 } else if ( this.settings.leaveVideoAction === 'pauseVideo' ) { 5088 previousVideo.videoController( 'pause' ); 5089 } else if ( this.settings.leaveVideoAction === 'removeVideo' ) { 5090 // If the video was lazy-loaded, remove it and show the poster again. If the video 5091 // was not lazy-loaded, but inline, stop the video. 5092 if ( previousVideo.siblings( 'a.sp-video' ).length !== 0 ) { 5093 previousVideo.siblings( 'a.sp-video' ).css( 'display', '' ); 5094 previousVideo.videoController( 'destroy' ); 5095 previousVideo.remove(); 5096 } else { 5097 previousVideo.videoController( 'stop' ); 5098 } 5099 } 5100 } 5101 5102 // Handle the video from the selected slide 5103 if ( this.settings.reachVideoAction === 'playVideo' ) { 5104 var loadedVideo = this.$slides.find( '.sp-slide' ).eq( event.index ).find( '.sp-video[data-video-init]' ), 5105 unloadedVideo = this.$slides.find( '.sp-slide' ).eq( event.index ).find( '.sp-video[data-video-preinit]' ); 5106 5107 // If the video was already initialized, play it. If it's not initialized (because 5108 // it's lazy loaded) initialize it and play it. 5109 if ( loadedVideo.length !== 0 ) { 5110 loadedVideo.videoController( 'play' ); 5111 } else if ( unloadedVideo.length !== 0 ) { 5112 unloadedVideo.trigger( 'click.' + NS ); 5113 } 5114 } 5115 }, 5116 5117 // Destroy the module 5118 destroyVideo: function() { 5119 this.$slider.find( '.sp-video[ data-video-preinit ]' ).each(function() { 5120 var video = $( this );
5121 video.removeAttr( 'data-video-preinit' ); 5122 video.off( 'click.' + NS ); 5123 }); 5124 5125 // Loop through the all the videos and destroy them 5126 this.$slider.find( '.sp-video[ data-video-init ]' ).each(function() { 5127 var video = $( this ); 5128 video.removeAttr( 'data-video-init' ); 5129 video.off( 'Video' ); 5130 video.videoController( 'destroy' ); 5131 }); 5132 5133 this.off( 'update.' + NS ); 5134 this.off( 'gotoSlideComplete.' + NS ); 5135 }, 5136 5137 videoDefaults: { 5138 5139 // Sets the action that the video will perform when its slide container is selected 5140 // ( 'playVideo' and 'none' ) 5141 reachVideoAction: 'none', 5142 5143 // Sets the action that the video will perform when another slide is selected 5144 // ( 'stopVideo', 'pauseVideo', 'removeVideo' and 'none' ) 5145 leaveVideoAction: 'pauseVideo', 5146 5147 // Sets the action that the slider will perform when the video starts playing 5148 // ( 'stopAutoplay' and 'none' ) 5149 playVideoAction: 'stopAutoplay', 5150 5151 // Sets the action that the slider will perform when the video is paused 5152 // ( 'startAutoplay' and 'none' ) 5153 pauseVideoAction: 'none', 5154 5155 // Sets the action that the slider will perform when the video ends 5156 // ( 'startAutoplay', 'nextSlide', 'replayVideo' and 'none' ) 5157 endVideoAction: 'none', 5158 5159 // Called when the video starts playing 5160 videoPlay: function() {}, 5161 5162 // Called when the video is paused 5163 videoPause: function() {}, 5164 5165 // Called when the video ends 5166 videoEnd: function() {} 5167 } 5168 }; 5169 5170 $.SliderPro.addModule( 'Video', Video ); 5171 5172})( window, jQuery ); 5173 5174// Video Controller jQuery plugin 5175// Creates a universal controller for multiple video types and providers 5176;(function( $ ) { 5177 5178 "use strict"; 5179 5180// Check if an iOS device is used. 5181// This information is important because a video can not be 5182// controlled programmatically unless the user has started the video manually. 5183var isIOS = window.navigator.userAgent.match( /(iPad|iPhone|iPod)/g ) ? true : false; 5184 5185var VideoController = function( instance, options ) { 5186 this.$video = $( instance ); 5187 this.options = options; 5188 this.settings = {}; 5189 this.player = null; 5190 5191 this._init(); 5192}; 5193 5194VideoController.prototype = { 5195 5196 _init: function() { 5197 this.settings = $.extend( {}, this.defaults, this.options ); 5198 5199 var that = this, 5200 players = $.VideoController.players, 5201 videoID = this.$video.attr( 'id' ); 5202 5203 // Loop through the available video players 5204 // and check if the targeted video element is supported by one of the players. 5205 // If a compatible type is found, store the video type. 5206 for ( var name in players ) { 5207 if ( typeof players[ name ] !== 'undefined' && players[ name ].isType( this.$video ) ) { 5208 this.player = new players[ name ]( this.$video ); 5209 break; 5210 } 5211 } 5212 5213 // Return if the player could not be instantiated 5214 if ( this.player === null ) { 5215 return; 5216 } 5217 5218 // Add event listeners 5219 var events = [ 'ready', 'start', 'play', 'pause', 'ended' ]; 5220 5221 $.each( events, function( index, element ) { 5222 var event = 'video' + element.charAt( 0 ).toUpperCase() + element.slice( 1 ); 5223 5224 that.player.on( element, function() { 5225 that.trigger({ type: event, video: videoID }); 5226 if ( $.isFunction( that.settings[ event ] ) ) { 5227 that.settings[ event ].call( that, { type: event, video: videoID } ); 5228 } 5229 }); 5230 }); 5231 }, 5232 5233 play: function() { 5234 if ( isIOS === true && this.player.isStarted() === false || this.player.getState() === 'playing' ) { 5235 return; 5236 } 5237 5238 this.player.play(); 5239 }, 5240 5241 stop: function() { 5242 if ( isIOS === true && this.player.isStarted() === false || this.player.getState() === 'stopped' ) { 5243 return; 5244 } 5245 5246 this.player.stop(); 5247 }, 5248 5249 pause: function() { 5250 if ( isIOS === true && this.player.isStarted() === false || this.player.getState() === 'paused' ) { 5251 return; 5252 } 5253 5254 this.player.pause(); 5255 }, 5256 5257 replay: function() { 5258 if ( isIOS === true && this.player.isStarted() === false ) { 5259 return; 5260 } 5261 5262 this.player.replay(); 5263 }, 5264 5265 on: function( type, callback ) { 5266 return this.$video.on( type, callback ); 5267 }, 5268 5269 off: function( type ) { 5270 return this.$video.off( type ); 5271 }, 5272 5273 trigger: function( data ) { 5274 return this.$video.triggerHandler( data ); 5275 }, 5276 5277 destroy: function() { 5278 if ( this.player.isStarted() === true ) { 5279 this.stop(); 5280 } 5281 5282 this.player.off( 'ready' ); 5283 this.player.off( 'start' ); 5284 this.player.off( 'play' ); 5285 this.player.off( 'pause' ); 5286 this.player.off( 'ended' ); 5287 5288 this.$video.removeData( 'videoController' ); 5289 }, 5290 5291 defaults: { 5292 videoReady: function() {}, 5293 videoStart: function() {}, 5294 videoPlay: function() {}, 5295 videoPause: function() {}, 5296 videoEnded: function() {} 5297 } 5298}; 5299 5300$.VideoController = { 5301 players: {}, 5302 5303 addPlayer: function( name, player ) { 5304 this.players[ name ] = player; 5305 } 5306}; 5307 5308$.fn.videoController = function( options ) { 5309 var args = Array.prototype.slice.call( arguments, 1 ); 5310 5311 return this.each(function() { 5312 // Instantiate the video controller or call a function on the current instance 5313 if ( typeof $( this ).data( 'videoController' ) === 'undefined' ) { 5314 var newInstance = new VideoController( this, options ); 5315 5316 // Store a reference to the instance created 5317 $( this ).data( 'videoController', newInstance ); 5318 } else if ( typeof options !== 'undefined' ) { 5319 var currentInstance = $( this ).data( 'videoController' ); 5320 5321 // Check the type of argument passed 5322 if ( typeof currentInstance[ options ] === 'function' ) { 5323 currentInstance[ options ].apply( currentInstance, args ); 5324 } else { 5325 $.error( options + ' does not exist in videoController.' ); 5326 } 5327 } 5328 }); 5329}; 5330 5331// Base object for the video players 5332var Video = function( video ) { 5333 this.$video = video; 5334 this.player = null; 5335 this.ready = false;
5336 this.started = false; 5337 this.state = ''; 5338 this.events = $({}); 5339 5340 this._init(); 5341}; 5342 5343Video.prototype = { 5344 _init: function() {}, 5345 5346 play: function() {}, 5347 5348 pause: function() {}, 5349 5350 stop: function() {}, 5351 5352 replay: function() {}, 5353 5354 isType: function() {}, 5355 5356 isReady: function() { 5357 return this.ready; 5358 }, 5359 5360 isStarted: function() { 5361 return this.started; 5362 }, 5363 5364 getState: function() { 5365 return this.state; 5366 }, 5367 5368 on: function( type, callback ) { 5369 return this.events.on( type, callback ); 5370 }, 5371 5372 off: function( type ) { 5373 return this.events.off( type ); 5374 }, 5375 5376 trigger: function( data ) { 5377 return this.events.triggerHandler( data ); 5378 } 5379}; 5380 5381// YouTube video 5382var YoutubeVideoHelper = { 5383 youtubeAPIAdded: false, 5384 youtubeVideos: [] 5385}; 5386 5387var YoutubeVideo = function( video ) { 5388 this.init = false; 5389 var youtubeAPILoaded = window.YT && window.YT.Player; 5390 5391 if ( typeof youtubeAPILoaded !== 'undefined' ) { 5392 Video.call( this, video ); 5393 } else { 5394 YoutubeVideoHelper.youtubeVideos.push({ 'video': video, 'scope': this }); 5395 5396 if ( YoutubeVideoHelper.youtubeAPIAdded === false ) { 5397 YoutubeVideoHelper.youtubeAPIAdded = true; 5398 5399 var tag = document.createElement( 'script' ); 5400 tag.src = "//www.youtube.com/player_api"; 5401 var firstScriptTag = document.getElementsByTagName( 'script' )[0]; 5402 firstScriptTag.parentNode.insertBefore( tag, firstScriptTag ); 5403 5404 window.onYouTubePlayerAPIReady = function() { 5405 $.each( YoutubeVideoHelper.youtubeVideos, function( index, element ) { 5406 Video.call( element.scope, element.video ); 5407 }); 5408 }; 5409 } 5410 } 5411}; 5412 5413YoutubeVideo.prototype = new Video(); 5414YoutubeVideo.prototype.constructor = YoutubeVideo; 5415$.VideoController.addPlayer( 'YoutubeVideo', YoutubeVideo ); 5416 5417YoutubeVideo.isType = function( video ) { 5418 if ( video.is( 'iframe' ) ) { 5419 var src = video.attr( 'src' ); 5420 5421 if ( src.indexOf( 'youtube.com' ) !== -1 || src.indexOf( 'youtu.be' ) !== -1 ) { 5422 return true; 5423 } 5424 } 5425 5426 return false; 5427}; 5428 5429YoutubeVideo.prototype._init = function() { 5430 this.init = true; 5431 this._setup(); 5432}; 5433 5434YoutubeVideo.prototype._setup = function() { 5435 var that = this; 5436 5437 // Get a reference to the player 5438 this.player = new YT.Player( this.$video[0], { 5439 events: { 5440 'onReady': function() { 5441 that.trigger({ type: 'ready' }); 5442 that.ready = true; 5443 }, 5444 5445 'onStateChange': function( event ) { 5446 switch ( event.data ) { 5447 case YT.PlayerState.PLAYING: 5448 if (that.started === false) { 5449 that.started = true; 5450 that.trigger({ type: 'start' }); 5451 } 5452 5453 that.state = 'playing'; 5454 that.trigger({ type: 'play' }); 5455 break; 5456 5457 case YT.PlayerState.PAUSED: 5458 that.state = 'paused'; 5459 that.trigger({ type: 'pause' }); 5460 break; 5461 5462 case YT.PlayerState.ENDED: 5463 that.state = 'ended'; 5464 that.trigger({ type: 'ended' }); 5465 break; 5466 } 5467 } 5468 } 5469 }); 5470}; 5471 5472YoutubeVideo.prototype.play = function() { 5473 var that = this; 5474 5475 if ( this.ready === true ) { 5476 this.player.playVideo(); 5477 } else { 5478 var timer = setInterval(function() { 5479 if ( that.ready === true ) { 5480 clearInterval( timer ); 5481 that.player.playVideo(); 5482 } 5483 }, 100 ); 5484 } 5485}; 5486 5487YoutubeVideo.prototype.pause = function() { 5488 // On iOS, simply pausing the video can make other videos unresponsive 5489 // so we stop the video instead. 5490 if ( isIOS === true ) { 5491 this.stop(); 5492 } else { 5493 this.player.pauseVideo(); 5494 } 5495}; 5496 5497YoutubeVideo.prototype.stop = function() { 5498 this.player.seekTo( 1 ); 5499 this.player.stopVideo(); 5500 this.state = 'stopped'; 5501}; 5502 5503YoutubeVideo.prototype.replay = function() { 5504 this.player.seekTo( 1 ); 5505 this.player.playVideo(); 5506}; 5507 5508YoutubeVideo.prototype.on = function( type, callback ) { 5509 var that = this; 5510 5511 if ( this.init === true ) { 5512 Video.prototype.on.call( this, type, callback ); 5513 } else { 5514 var timer = setInterval(function() { 5515 if ( that.init === true ) { 5516 clearInterval( timer ); 5517 Video.prototype.on.call( that, type, callback ); 5518 } 5519 }, 100 ); 5520 } 5521}; 5522 5523// Vimeo video 5524var VimeoVideoHelper = { 5525 vimeoAPIAdded: false, 5526 vimeoVideos: [] 5527}; 5528 5529var VimeoVideo = function( video ) { 5530 this.init = false; 5531 5532 if ( typeof window.Froogaloop !== 'undefined' ) { 5533 Video.call( this, video ); 5534 } else { 5535 VimeoVideoHelper.vimeoVideos.push({ 'video': video, 'scope': this }); 5536 5537 if ( VimeoVideoHelper.vimeoAPIAdded === false ) { 5538 VimeoVideoHelper.vimeoAPIAdded = true; 5539 5540 var tag = document.createElement('script'); 5541 tag.src = "//a.vimeocdn.com/js/froogaloop2.min.js"; 5542 var firstScriptTag = document.getElementsByTagName( 'script' )[0]; 5543 firstScriptTag.parentNode.insertBefore( tag, firstScriptTag ); 5544 5545 var checkVimeoAPITimer = setInterval(function() { 5546 if ( typeof window.Froogaloop !== 'undefined' ) { 5547 clearInterval( checkVimeoAPITimer ); 5548 5549 $.each( VimeoVideoHelper.vimeoVideos, function( index, element ) { 5550 Video.call( element.scope, element.video ); 5551 }); 5552 } 5553 }, 100 ); 5554 } 5555 } 5556}; 5557 5558VimeoVideo.prototype = new Video(); 5559VimeoVideo.prototype.constructor = VimeoVideo; 5560$.VideoController.addPlayer( 'VimeoVideo', VimeoVideo ); 5561 5562VimeoVideo.isType = function( video ) { 5563 if ( video.is( 'iframe' ) ) { 5564 var src = video.attr('src'); 5565 5566 if ( src.indexOf( 'vimeo.com' ) !== -1 ) { 5567 return true; 5568 } 5569 } 5570 5571 return false;
5572}; 5573 5574VimeoVideo.prototype._init = function() { 5575 this.init = true; 5576 this._setup(); 5577}; 5578 5579VimeoVideo.prototype._setup = function() { 5580 var that = this; 5581 5582 // Get a reference to the player 5583 this.player = $f( this.$video[0] ); 5584 5585 this.player.addEvent( 'ready', function() { 5586 that.ready = true; 5587 that.trigger({ type: 'ready' }); 5588 5589 that.player.addEvent( 'play', function() { 5590 if ( that.started === false ) { 5591 that.started = true; 5592 that.trigger({ type: 'start' }); 5593 } 5594 5595 that.state = 'playing'; 5596 that.trigger({ type: 'play' }); 5597 }); 5598 5599 that.player.addEvent( 'pause', function() { 5600 that.state = 'paused'; 5601 that.trigger({ type: 'pause' }); 5602 }); 5603 5604 that.player.addEvent( 'finish', function() { 5605 that.state = 'ended'; 5606 that.trigger({ type: 'ended' }); 5607 }); 5608 }); 5609}; 5610 5611VimeoVideo.prototype.play = function() { 5612 var that = this; 5613 5614 if ( this.ready === true ) { 5615 this.player.api( 'play' ); 5616 } else { 5617 var timer = setInterval(function() { 5618 if ( that.ready === true ) { 5619 clearInterval( timer ); 5620 that.player.api( 'play' ); 5621 } 5622 }, 100 ); 5623 } 5624}; 5625 5626VimeoVideo.prototype.pause = function() { 5627 this.player.api( 'pause' ); 5628}; 5629 5630VimeoVideo.prototype.stop = function() { 5631 this.player.api( 'seekTo', 0 ); 5632 this.player.api( 'pause' ); 5633 this.state = 'stopped'; 5634}; 5635 5636VimeoVideo.prototype.replay = function() { 5637 this.player.api( 'seekTo', 0 ); 5638 this.player.api( 'play' ); 5639}; 5640 5641VimeoVideo.prototype.on = function( type, callback ) { 5642 var that = this; 5643 5644 if ( this.init === true ) { 5645 Video.prototype.on.call( this, type, callback ); 5646 } else { 5647 var timer = setInterval(function() { 5648 if ( that.init === true ) { 5649 clearInterval( timer ); 5650 Video.prototype.on.call( that, type, callback ); 5651 } 5652 }, 100 ); 5653 } 5654}; 5655 5656// HTML5 video 5657var HTML5Video = function( video ) { 5658 Video.call( this, video ); 5659}; 5660 5661HTML5Video.prototype = new Video(); 5662HTML5Video.prototype.constructor = HTML5Video; 5663$.VideoController.addPlayer( 'HTML5Video', HTML5Video ); 5664 5665HTML5Video.isType = function( video ) { 5666 if ( video.is( 'video' ) && video.hasClass( 'video-js' ) === false && video.hasClass( 'sublime' ) === false ) { 5667 return true; 5668 } 5669 5670 return false; 5671}; 5672 5673HTML5Video.prototype._init = function() { 5674 var that = this; 5675 5676 // Get a reference to the player 5677 this.player = this.$video[0]; 5678 5679 var checkVideoReady = setInterval(function() { 5680 if ( that.player.readyState === 4 ) { 5681 clearInterval( checkVideoReady ); 5682 5683 that.ready = true; 5684 that.trigger({ type: 'ready' }); 5685 5686 that.player.addEventListener( 'play', function() { 5687 if ( that.started === false ) { 5688 that.started = true; 5689 that.trigger({ type: 'start' }); 5690 } 5691 5692 that.state = 'playing'; 5693 that.trigger({ type: 'play' }); 5694 }); 5695 5696 that.player.addEventListener( 'pause', function() { 5697 that.state = 'paused'; 5698 that.trigger({ type: 'pause' }); 5699 }); 5700 5701 that.player.addEventListener( 'ended', function() { 5702 that.state = 'ended'; 5703 that.trigger({ type: 'ended' }); 5704 }); 5705 } 5706 }, 100 ); 5707}; 5708 5709HTML5Video.prototype.play = function() { 5710 var that = this; 5711 5712 if ( this.ready === true ) { 5713 this.player.play(); 5714 } else { 5715 var timer = setInterval(function() { 5716 if ( that.ready === true ) { 5717 clearInterval( timer ); 5718 that.player.play(); 5719 } 5720 }, 100 ); 5721 } 5722}; 5723 5724HTML5Video.prototype.pause = function() { 5725 this.player.pause(); 5726}; 5727 5728HTML5Video.prototype.stop = function() { 5729 this.player.currentTime = 0; 5730 this.player.pause(); 5731 this.state = 'stopped'; 5732}; 5733 5734HTML5Video.prototype.replay = function() { 5735 this.player.currentTime = 0; 5736 this.player.play(); 5737}; 5738 5739// VideoJS video 5740var VideoJSVideo = function( video ) { 5741 Video.call( this, video ); 5742}; 5743 5744VideoJSVideo.prototype = new Video(); 5745VideoJSVideo.prototype.constructor = VideoJSVideo; 5746$.VideoController.addPlayer( 'VideoJSVideo', VideoJSVideo ); 5747 5748VideoJSVideo.isType = function( video ) { 5749 if ( ( typeof video.attr( 'data-videojs-id' ) !== 'undefined' || video.hasClass( 'video-js' ) ) && typeof videojs !== 'undefined' ) { 5750 return true; 5751 } 5752 5753 return false; 5754}; 5755 5756VideoJSVideo.prototype._init = function() { 5757 var that = this, 5758 videoID = this.$video.hasClass( 'video-js' ) ? this.$video.attr( 'id' ) : this.$video.attr( 'data-vi
5758deojs-id' ); 5759 5760 this.player = videojs( videoID ); 5761 5762 this.player.ready(function() { 5763 that.ready = true; 5764 that.trigger({ type: 'ready' }); 5765 5766 that.player.on( 'play', function() { 5767 if ( that.started === false ) { 5768 that.started = true; 5769 that.trigger({ type: 'start' }); 5770 } 5771 5772 that.state = 'playing'; 5773 that.trigger({ type: 'play' }); 5774 }); 5775 5776 that.player.on( 'pause', function() { 5777 that.state = 'paused'; 5778 that.trigger({ type: 'pause' }); 5779 }); 5780 5781 that.player.on( 'ended', function() { 5782 that.state = 'ended'; 5783 that.trigger({ type: 'ended' }); 5784 }); 5785 }); 5786}; 5787 5788VideoJSVideo.prototype.play = function() { 5789 this.player.play(); 5790}; 5791 5792VideoJSVideo.prototype.pause = function() { 5793 this.player.pause(); 5794}; 5795 5796VideoJSVideo.prototype.stop = function() { 5797 this.player.currentTime( 0 ); 5798 this.player.pause(); 5799 this.state = 'stopped'; 5800}; 5801 5802VideoJSVideo.prototype.replay = function() { 5803 this.player.currentTime( 0 ); 5804 this.player.play(); 5805}; 5806 5807// Sublime video 5808var SublimeVideo = function( video ) { 5809 Video.call( this, video ); 5810}; 5811 5812SublimeVideo.prototype = new Video(); 5813SublimeVideo.prototype.constructor = SublimeVideo; 5814$.VideoController.addPlayer( 'SublimeVideo', SublimeVideo ); 5815 5816SublimeVideo.isType = function( video ) { 5817 if ( video.hasClass( 'sublime' ) && typeof sublime !== 'undefined' ) { 5818 return true; 5819 } 5820 5821 return false; 5822}; 5823 5824SublimeVideo.prototype._init = function() { 5825 var that = this; 5826 5827 sublime.ready(function() { 5828 // Get a reference to the player 5829 that.player = sublime.player( that.$video.attr( 'id' ) ); 5830 5831 that.ready = true; 5832 that.trigger({ type: 'ready' }); 5833 5834 that.player.on( 'play', function() { 5835 if ( that.started === false ) { 5836 that.started = true; 5837 that.trigger({ type: 'start' }); 5838 } 5839 5840 that.state = 'playing'; 5841 that.trigger({ type: 'play' }); 5842 }); 5843 5844 that.player.on( 'pause', function() { 5845 that.state = 'paused'; 5846 that.trigger({ type: 'pause' }); 5847 }); 5848 5849 that.player.on( 'stop', function() { 5850 that.state = 'stopped'; 5851 that.trigger({ type: 'stop' }); 5852 }); 5853 5854 that.player.on( 'end', function() { 5855 that.state = 'ended'; 5856 that.trigger({ type: 'ended' }); 5857 }); 5858 }); 5859}; 5860 5861SublimeVideo.prototype.play = function() { 5862 this.player.play(); 5863}; 5864 5865SublimeVideo.prototype.pause = function() { 5866 this.player.pause(); 5867}; 5868 5869SublimeVideo.prototype.stop = function() { 5870 this.player.stop(); 5871}; 5872 5873SublimeVideo.prototype.replay = function() { 5874 this.player.stop(); 5875 this.player.play(); 5876}; 5877 5878// JWPlayer video 5879var JWPlayerVideo = function( video ) { 5880 Video.call( this, video ); 5881}; 5882 5883JWPlayerVideo.prototype = new Video(); 5884JWPlayerVideo.prototype.constructor = JWPlayerVideo; 5885$.VideoController.addPlayer( 'JWPlayerVideo', JWPlayerVideo ); 5886 5887JWPlayerVideo.isType = function( video ) { 5888 if ( ( typeof video.attr( 'data-jwplayer-id' ) !== 'undefined' || video.hasClass( 'jwplayer' ) || video.find( "object[data*='jwplayer']" ).length !== 0 ) && 5889 typeof jwplayer !== 'undefined') { 5890 return true; 5891 } 5892 5893 return false; 5894}; 5895 5896JWPlayerVideo.prototype._init = function() { 5897 var that = this, 5898 videoID; 5899 5900 if ( this.$video.hasClass( 'jwplayer' ) ) { 5901 videoID = this.$video.attr( 'id' ); 5902 } else if ( typeof this.$video.attr( 'data-jwplayer-id' ) !== 'undefined' ) { 5903 videoID = this.$video.attr( 'data-jwplayer-id'); 5904 } else if ( this.$video.find( "object[data*='jwplayer']" ).length !== 0 ) { 5905 videoID = this.$video.find( 'object' ).attr( 'id' ); 5906 } 5907 5908 // Get a reference to the player 5909 this.player = jwplayer( videoID ); 5910 5911 this.player.onReady(function() { 5912 that.ready = true; 5913 that.trigger({ type: 'ready' }); 5914 5915 that.player.onPlay(function() { 5916 if ( that.started === false ) { 5917 that.started = true; 5918 that.trigger({ type: 'start' }); 5919 } 5920 5921 that.state = 'playing'; 5922 that.trigger({ type: 'play' }); 5923 }); 5924 5925 that.player.onPause(function() { 5926 that.state = 'paused'; 5927 that.trigger({ type: 'pause' }); 5928 }); 5929 5930 that.player.onComplete(function() { 5931 that.state = 'ended'; 5932 that.trigger({ type: 'ended' }); 5933 }); 5934 }); 5935}; 5936 5937JWPlayerVideo.prototype.play = function() { 5938 this.player.play( true ); 5939}; 5940 5941JWPlayerVideo.prototype.pause = function() { 5942 this.player.pause( true ); 5943}; 5944 5945JWPlayerVideo.prototype.stop = function() { 5946 this.player.stop(); 5947 this.state = 'stopped'; 5948}; 5949 5950JWPlayerVideo.prototype.replay = function() { 5951 this.player.seek( 0 ); 5952 this.player.play( true ); 5953}; 5954 5955})( jQuery );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.