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