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