vendor: 15,719 bytes, lines 1-650
1/*! 2 * Flickity PACKAGED v2.0.5 3 * Touch, responsive, flickable carousels 4 * 5 * Licensed GPLv3 for open source use 6 * or Flickity Commercial License for commercial use 7 * 8 * http://flickity.metafizzy.co 9 * Copyright 2016 Metafizzy 10 */ 11 12/** 13 * Bridget makes jQuery widgets 14 * v2.0.1 15 * MIT license 16 */ 17 18/* jshint browser: true, strict: true, undef: true, unused: true */ 19 20( function( window, factory ) { 21 // universal module definition 22 /*jshint strict: false */ /* globals define, module, require */ 23 if ( typeof define == 'function' && define.amd ) { 24 // AMD 25 define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) { 26 return factory( window, jQuery ); 27 }); 28 } else if ( typeof module == 'object' && module.exports ) { 29 // CommonJS 30 module.exports = factory( 31 window, 32 require('jquery') 33 ); 34 } else { 35 // browser global 36 window.jQueryBridget = factory( 37 window, 38 window.jQuery 39 ); 40 } 41 42}( window, function factory( window, jQuery ) { 43'use strict'; 44 45// ----- utils ----- // 46 47var arraySlice = Array.prototype.slice; 48 49// helper function for logging errors 50// $.error breaks jQuery chaining 51var console = window.console; 52var logError = typeof console == 'undefined' ? function() {} : 53 function( message ) { 54 console.error( message ); 55 }; 56 57// ----- jQueryBridget ----- // 58 59function jQueryBridget( namespace, PluginClass, $ ) { 60 $ = $ || jQuery || window.jQuery; 61 if ( !$ ) { 62 return; 63 } 64 65 // add option method -> $().plugin('option', {...}) 66 if ( !PluginClass.prototype.option ) { 67 // option setter 68 PluginClass.prototype.option = function( opts ) { 69 // bail out if not an object 70 if ( !$.isPlainObject( opts ) ){ 71 return; 72 } 73 this.options = $.extend( true, this.options, opts ); 74 }; 75 } 76 77 // make jQuery plugin 78 $.fn[ namespace ] = function( arg0 /*, arg1 */ ) { 79 if ( typeof arg0 == 'string' ) { 80 // method call $().plugin( 'methodName', { options } ) 81 // shift arguments by 1 82 var args = arraySlice.call( arguments, 1 ); 83 return methodCall( this, arg0, args ); 84 } 85 // just $().plugin({ options }) 86 plainCall( this, arg0 ); 87 return this; 88 }; 89 90 // $().plugin('methodName') 91 function methodCall( $elems, methodName, args ) { 92 var returnValue; 93 var pluginMethodStr = '$().' + namespace + '("' + methodName + '")'; 94 95 $elems.each( function( i, elem ) { 96 // get instance 97 var instance = $.data( elem, namespace ); 98 if ( !instance ) { 99 logError( namespace + ' not initialized. Cannot call methods, i.e. ' + 100 pluginMethodStr ); 101 return; 102 } 103 104 var method = instance[ methodName ]; 105 if ( !method || methodName.charAt(0) == '_' ) { 106 logError( pluginMethodStr + ' is not a valid method' ); 107 return; 108 } 109 110 // apply method, get return value 111 var value = method.apply( instance, args ); 112 // set return value if value is returned, use only first value 113 returnValue = returnValue === undefined ? value : returnValue; 114 }); 115 116 return returnValue !== undefined ? returnValue : $elems; 117 } 118 119 function plainCall( $elems, options ) { 120 $elems.each( function( i, elem ) { 121 var instance = $.data( elem, namespace ); 122 if ( instance ) { 123 // set options & init 124 instance.option( options ); 125 instance._init(); 126 } else { 127 // initialize new instance 128 instance = new PluginClass( elem, options ); 129 $.data( elem, namespace, instance ); 130 } 131 }); 132 } 133 134 updateJQuery( $ ); 135 136} 137 138// ----- updateJQuery ----- // 139 140// set $.bridget for v1 backwards compatibility 141function updateJQuery( $ ) { 142 if ( !$ || ( $ && $.bridget ) ) { 143 return; 144 } 145 $.bridget = jQueryBridget; 146} 147 148updateJQuery( jQuery || window.jQuery ); 149 150// ----- ----- // 151 152return jQueryBridget; 153 154})); 155 156/** 157 * EvEmitter v1.0.3 158 * Lil' event emitter 159 * MIT License 160 */ 161 162/* jshint unused: true, undef: true, strict: true */ 163 164( function( global, factory ) { 165 // universal module definition 166 /* jshint strict: false */ /* globals define, module, window */ 167 if ( typeof define == 'function' && define.amd ) { 168 // AMD - RequireJS 169 define( 'ev-emitter/ev-emitter',factory ); 170 } else if ( typeof module == 'object' && module.exports ) { 171 // CommonJS - Browserify, Webpack 172 module.exports = factory(); 173 } else { 174 // Browser globals 175 global.EvEmitter = factory(); 176 } 177 178}( typeof window != 'undefined' ? window : this, function() { 179 180 181 182function EvEmitter() {} 183 184var proto = EvEmitter.prototype; 185 186proto.on = function( eventName, listener ) { 187 if ( !eventName || !listener ) { 188 return; 189 } 190 // set events hash 191 var events = this._events = this._events || {}; 192 // set listeners array 193 var listeners = events[ eventName ] = events[ eventName ] || []; 194 // only add once 195 if ( listeners.indexOf( listener ) == -1 ) { 196 listeners.push( listener ); 197 } 198 199 return this; 200}; 201 202proto.once = function( eventName, listener ) { 203 if ( !eventName || !listener ) { 204 return; 205 } 206 // add event 207 this.on( eventName, listener ); 208 // set once flag 209 // set onceEvents hash 210 var onceEvents = this._onceEvents = this._onceEvents || {}; 211 // set onceListeners object 212 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; 213 // set flag 214 onceListeners[ listener ] = true; 215 216 return this; 217}; 218 219proto.off = function( eventName, listener ) { 220 var listeners = this._events && this._events[ eventName ]; 221 if ( !listeners || !listeners.length ) { 222 return; 223 } 224 var index = listeners.indexOf( listener ); 225 if ( index != -1 ) { 226 listeners.splice( index, 1 ); 227 } 228 229 return this; 230}; 231 232proto.emitEvent = function( eventName, args ) { 233 var listeners = this._events && this._events[ eventName ]; 234 if ( !listeners || !listeners.length ) { 235 return; 236 } 237 var i = 0; 238 var listener = listeners[i]; 239 args = args || []; 240 // once stuff 241 var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; 242 243 while ( listener ) { 244 var isOnce = onceListeners && onceListeners[ listener ]; 245 if ( isOnce ) { 246 // remove listener 247 // remove before trigger to prevent recursion 248 this.off( eventName, listener ); 249 // unset once flag 250 delete onceListeners[ listener ]; 251 } 252 // trigger listener 253 listener.apply( this, args ); 254 // get next listener 255 i += isOnce ? 0 : 1; 256 listener = listeners[i]; 257 } 258 259 return this; 260}; 261 262return EvEmitter; 263 264})); 265 266/*! 267 * getSize v2.0.2 268 * measure size of elements 269 * MIT license 270 */ 271 272/*jshint browser: true, strict: true, undef: true, unused: true */ 273/*global define: false, module: false, console: false */ 274 275( function( window, factory ) { 276 'use strict'; 277 278 if ( typeof define == 'function' && define.amd ) { 279 // AMD 280 define( 'get-size/get-size',[],function() { 281 return factory(); 282 }); 283 } else if ( typeof module == 'object' && module.exports ) { 284 // CommonJS 285 module.exports = factory(); 286 } else { 287 // browser global 288 window.getSize = factory(); 289 } 290 291})( window, function factory() { 292'use strict'; 293 294// -------------------------- helpers -------------------------- // 295 296// get a number from a string, not a percentage 297function getStyleSize( value ) { 298 var num = parseFloat( value ); 299 // not a percent like '100%', and a number 300 var isValid = value.indexOf('%') == -1 && !isNaN( num ); 301 return isValid && num; 302} 303 304function noop() {} 305 306var logError = typeof console == 'undefined' ? noop : 307 function( message ) { 308 console.error( message ); 309 }; 310 311// -------------------------- measurements -------------------------- // 312 313var measurements = [ 314 'paddingLeft', 315 'paddingRight', 316 'paddingTop', 317 'paddingBottom', 318 'marginLeft', 319 'marginRight', 320 'marginTop', 321 'marginBottom', 322 'borderLeftWidth', 323 'borderRightWidth', 324 'borderTopWidth', 325 'borderBottomWidth' 326]; 327 328var measurementsLength = measurements.length; 329 330function getZeroSize() { 331 var size = { 332 width: 0, 333 height: 0, 334 innerWidth: 0, 335 innerHeight: 0, 336 outerWidth: 0, 337 outerHeight: 0 338 }; 339 for ( var i=0; i < measurementsLength; i++ ) { 340 var measurement = measurements[i]; 341 size[ measurement ] = 0; 342 } 343 return size; 344} 345 346// -------------------------- getStyle -------------------------- // 347 348/** 349 * getStyle, get style of element, check for Firefox bug 350 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397 351 */ 352function getStyle( elem ) { 353 var style = getComputedStyle( elem ); 354 if ( !style ) { 355 logError( 'Style returned ' + style + 356 '. Are you running this code in a hidden iframe on Firefox? ' + 357 'See http://bit.ly/getsizebug1' ); 358 } 359 return style; 360} 361 362// -------------------------- setup -------------------------- // 363 364var isSetup = false; 365 366var isBoxSizeOuter; 367 368/** 369 * setup 370 * check isBoxSizerOuter 371 * do on first getSize() rather than on page load for Firefox bug 372 */ 373function setup() { 374 // setup once 375 if ( isSetup ) { 376 return; 377 } 378 isSetup = true; 379 380 // -------------------------- box sizing -------------------------- // 381 382 /** 383 * WebKit measures the outer-width on style.width on border-box elems 384 * IE & Firefox<29 measures the inner-width 385 */ 386 var div = document.createElement('div'); 387 div.style.width = '200px'; 388 div.style.padding = '1px 2px 3px 4px'; 389 div.style.borderStyle = 'solid'; 390 div.style.borderWidth = '1px 2px 3px 4px'; 391 div.style.boxSizing = 'border-box'; 392 393 var body = document.body || document.documentElement; 394 body.appendChild( div ); 395 var style = getStyle( div ); 396 397 getSize.isBoxSizeOuter = isBoxSizeOuter = getStyleSize( style.width ) == 200; 398 body.removeChild( div ); 399 400} 401 402// -------------------------- getSize -------------------------- // 403 404function getSize( elem ) { 405 setup(); 406 407 // use querySeletor if elem is string 408 if ( typeof elem == 'string' ) { 409 elem = document.querySelector( elem ); 410 } 411 412 // do not proceed on non-objects 413 if ( !elem || typeof elem != 'object' || !elem.nodeType ) { 414 return; 415 } 416 417 var style = getStyle( elem ); 418 419 // if hidden, everything is 0 420 if ( style.display == 'none' ) { 421 return getZeroSize(); 422 } 423 424 var size = {}; 425 size.width = elem.offsetWidth; 426 size.height = elem.offsetHeight; 427 428 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box'; 429 430 // get all measurements 431 for ( var i=0; i < measurementsLength; i++ ) { 432 var measurement = measurements[i]; 433 var value = style[ measurement ]; 434 var num = parseFloat( value ); 435 // any 'auto', 'medium' value will be 0 436 size[ measurement ] = !isNaN( num ) ? num : 0; 437 } 438 439 var paddingWidth = size.paddingLeft + size.paddingRight; 440 var paddingHeight = size.paddingTop + size.paddingBottom; 441 var marginWidth = size.marginLeft + size.marginRight; 442 var marginHeight = size.marginTop + size.marginBottom; 443 var borderWidth = size.borderLeftWidth + size.borderRightWidth; 444 var borderHeight = size.borderTopWidth + size.borderBottomWidth; 445 446 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; 447 448 // overwrite width and height if we can get it from style 449 var styleWidth = getStyleSize( style.width ); 450 if ( styleWidth !== false ) { 451 size.width = styleWidth + 452 // add padding and border unless it's already including it 453 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); 454 } 455 456 var styleHeight = getStyleSize( style.height ); 457 if ( styleHeight !== false ) { 458 size.height = styleHeight + 459 // add padding and border unless it's already including it 460 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); 461 } 462 463 size.innerWidth = size.width - ( paddingWidth + borderWidth ); 464 size.innerHeight = size.height - ( paddingHeight + borderHeight ); 465 466 size.outerWidth = size.width + marginWidth; 467 size.outerHeight = size.height + marginHeight; 468 469 return size; 470} 471 472return getSize; 473 474}); 475 476/** 477 * matchesSelector v2.0.1 478 * matchesSelector( element, '.selector' ) 479 * MIT license 480 */ 481 482/*jshint browser: true, strict: true, undef: true, unused: true */ 483 484( function( window, factory ) { 485 /*global define: false, module: false */ 486 'use strict'; 487 // universal module definition 488 if ( typeof define == 'function' && define.amd ) { 489 // AMD 490 define( 'desandro-matches-selector/matches-selector',factory ); 491 } else if ( typeof module == 'object' && module.exports ) { 492 // CommonJS 493 module.exports = factory(); 494 } else { 495 // browser global 496 window.matchesSelector = factory(); 497 } 498 499}( window, function factory() { 500 'use strict'; 501 502 var matchesMethod = ( function() { 503 var ElemProto = Element.prototype; 504 // check for the standard method name first 505 if ( ElemProto.matches ) { 506 return 'matches'; 507 } 508 // check un-prefixed 509 if ( ElemProto.matchesSelector ) { 510 return 'matchesSelector'; 511 } 512 // check vendor prefixes 513 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ]; 514 515 for ( var i=0; i < prefixes.length; i++ ) { 516 var prefix = prefixes[i]; 517 var method = prefix + 'MatchesSelector'; 518 if ( ElemProto[ method ] ) { 519 return method; 520 } 521 } 522 })(); 523 524 return function matchesSelector( elem, selector ) { 525 return elem[ matchesMethod ]( selector ); 526 }; 527 528})); 529 530/** 531 * Fizzy UI utils v2.0.3 532 * MIT license 533 */ 534 535/*jshint browser: true, undef: true, unused: true, strict: true */ 536 537( function( window, factory ) { 538 // universal module definition 539 /*jshint strict: false */ /*globals define, module, require */ 540 541 if ( typeof define == 'function' && define.amd ) { 542 // AMD 543 define( 'fizzy-ui-utils/utils',[ 544 'desandro-matches-selector/matches-selector' 545 ], function( matchesSelector ) { 546 return factory( window, matchesSelector ); 547 }); 548 } else if ( typeof module == 'object' && module.exports ) { 549 // CommonJS 550 module.exports = factory( 551 window, 552 require('desandro-matches-selector') 553 ); 554 } else { 555 // browser global 556 window.fizzyUIUtils = factory( 557 window, 558 window.matchesSelector 559 ); 560 } 561 562}( window, function factory( window, matchesSelector ) { 563 564 565 566var utils = {}; 567 568// ----- extend ----- // 569 570// extends objects 571utils.extend = function( a, b ) { 572 for ( var prop in b ) { 573 a[ prop ] = b[ prop ]; 574 } 575 return a; 576}; 577 578// ----- modulo ----- // 579 580utils.modulo = function( num, div ) { 581 return ( ( num % div ) + div ) % div; 582}; 583 584// ----- makeArray ----- // 585 586// turn element or nodeList into an array 587utils.makeArray = function( obj ) { 588 var ary = []; 589 if ( Array.isArray( obj ) ) { 590 // use object if already an array 591 ary = obj; 592 } else if ( obj && typeof obj.length == 'number' ) { 593 // convert nodeList to array 594 for ( var i=0; i < obj.length; i++ ) { 595 ary.push( obj[i] ); 596 } 597 } else { 598 // array of single index 599 ary.push( obj ); 600 } 601 return ary; 602}; 603 604// ----- removeFrom ----- // 605 606utils.removeFrom = function( ary, obj ) { 607 var index = ary.indexOf( obj ); 608 if ( index != -1 ) { 609 ary.splice( index, 1 ); 610 } 611}; 612 613// ----- getParent ----- // 614 615utils.getParent = function( elem, selector ) { 616 while ( elem != document.body ) { 617 elem = elem.parentNode; 618 if ( matchesSelector( elem, selector ) ) { 619 return elem; 620 } 621 } 622}; 623 624// ----- getQueryElement ----- // 625 626// use element as selector string 627utils.getQueryElement = function( elem ) { 628 if ( typeof elem == 'string' ) { 629 return document.querySelector( elem ); 630 } 631 return elem; 632}; 633 634// ----- handleEvent ----- // 635 636// enable .ontype to trigger from .addEventListener( elem, 'type' ) 637utils.handleEvent = function( event ) { 638 var method = 'on' + event.type; 639 if ( this[ method ] ) { 640 this[ method ]( event ); 641 } 642}; 643 644// ----- filterFindElements ----- // 645 646utils.filterFindElements = function( elems, selector ) { 647 // make array of elems 648 elems = utils.makeArray( elems ); 649 var ffElems = []; 650
651 elems.forEach( function( elem ) { 652 // check that elem is an actual element 653 if ( !( elem instanceof HTMLElement ) ) { 654 return; 655 } 656 // add elem if no selector 657 if ( !selector ) { 658 ffElems.push( elem ); 659 return; 660 } 661 // filter & find items if we have a selector 662 // filter 663 if ( matchesSelector( elem, selector ) ) { 664 ffElems.push( elem ); 665 } 666 // find children 667 var childElems = elem.querySelectorAll( selector ); 668 // concat childElems to filterFound array 669 for ( var i=0; i < childElems.length; i++ ) { 670 ffElems.push( childElems[i] ); 671 } 672 }); 673 674 return ffElems; 675}; 676 677// ----- debounceMethod ----- // 678 679utils.debounceMethod = function( _class, methodName, threshold ) { 680 // original method 681 var method = _class.prototype[ methodName ]; 682 var timeoutName = methodName + 'Timeout'; 683 684 _class.prototype[ methodName ] = function() { 685 var timeout = this[ timeoutName ]; 686 if ( timeout ) { 687 clearTimeout( timeout ); 688 } 689 var args = arguments; 690 691 var _this = this; 692 this[ timeoutName ] = setTimeout( function() { 693 method.apply( _this, args ); 694 delete _this[ timeoutName ]; 695 }, threshold || 100 ); 696 }; 697}; 698 699// ----- docReady ----- // 700 701utils.docReady = function( callback ) { 702 var readyState = document.readyState; 703 if ( readyState == 'complete' || readyState == 'interactive' ) { 704 // do async to allow for other scripts to run. metafizzy/flickity#441 705 setTimeout( callback ); 706 } else { 707 document.addEventListener( 'DOMContentLoaded', callback ); 708 } 709}; 710 711// ----- htmlInit ----- // 712 713// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ 714utils.toDashed = function( str ) { 715 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) { 716 return $1 + '-' + $2; 717 }).toLowerCase(); 718}; 719 720var console = window.console; 721/** 722 * allow user to initialize classes via [data-namespace] or .js-namespace class 723 * htmlInit( Widget, 'widgetName' ) 724 * options are parsed from data-namespace-options 725 */ 726utils.htmlInit = function( WidgetClass, namespace ) { 727 utils.docReady( function() { 728 var dashedNamespace = utils.toDashed( namespace ); 729 var dataAttr = 'data-' + dashedNamespace; 730 var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' ); 731 var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace ); 732 var elems = utils.makeArray( dataAttrElems ) 733 .concat( utils.makeArray( jsDashElems ) ); 734 var dataOptionsAttr = dataAttr + '-options'; 735 var jQuery = window.jQuery; 736 737 elems.forEach( function( elem ) { 738 var attr = elem.getAttribute( dataAttr ) || 739 elem.getAttribute( dataOptionsAttr ); 740 var options; 741 try { 742 options = attr && JSON.parse( attr ); 743 } catch ( error ) { 744 // log error, do not initialize 745 if ( console ) { 746 console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className + 747 ': ' + error ); 748 } 749 return; 750 } 751 // initialize 752 var instance = new WidgetClass( elem, options ); 753 // make available via $().data('namespace') 754 if ( jQuery ) { 755 jQuery.data( elem, namespace, instance ); 756 } 757 }); 758 759 }); 760}; 761 762// ----- ----- // 763 764return utils; 765 766})); 767 768// Flickity.Cell 769( function( window, factory ) { 770 // universal module definition 771 /* jshint strict: false */ 772 if ( typeof define == 'function' && define.amd ) { 773 // AMD 774 define( 'flickity/js/cell',[ 775 'get-size/get-size' 776 ], function( getSize ) { 777 return factory( window, getSize ); 778 }); 779 } else if ( typeof module == 'object' && module.exports ) { 780 // CommonJS 781 module.exports = factory( 782 window, 783 require('get-size') 784 ); 785 } else { 786 // browser global 787 window.Flickity = window.Flickity || {}; 788 window.Flickity.Cell = factory( 789 window, 790 window.getSize 791 ); 792 } 793 794}( window, function factory( window, getSize ) { 795 796 797 798function Cell( elem, parent ) { 799 this.element = elem; 800 this.parent = parent; 801 802 this.create(); 803} 804 805var proto = Cell.prototype; 806 807proto.create = function() { 808 this.element.style.position = 'absolute'; 809 this.x = 0; 810 this.shift = 0; 811}; 812 813proto.destroy = function() { 814 // reset style 815 this.element.style.position = ''; 816 var side = this.parent.originSide; 817 this.element.style[ side ] = ''; 818}; 819 820proto.getSize = function() { 821 this.size = getSize( this.element ); 822}; 823 824proto.setPosition = function( x ) { 825 this.x = x; 826 this.updateTarget(); 827 this.renderPosition( x ); 828}; 829 830// setDefaultTarget v1 method, backwards compatibility, remove in v3 831proto.updateTarget = proto.setDefaultTarget = function() { 832 var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight'; 833 this.target = this.x + this.size[ marginProperty ] + 834 this.size.width * this.parent.cellAlign; 835}; 836 837proto.renderPosition = function( x ) { 838 // render position of cell with in slider 839 var side = this.parent.originSide; 840 this.element.style[ side ] = this.parent.getPositionValue( x ); 841}; 842 843/** 844 * @param {Integer} factor - 0, 1, or -1 845**/ 846proto.wrapShift = function( shift ) { 847 this.shift = shift; 848 this.renderPosition( this.x + this.parent.slideableWidth * shift ); 849}; 850 851proto.remove = function() { 852 this.element.parentNode.removeChild( this.element ); 853}; 854 855return Cell; 856 857})); 858 859// slide 860( function( window, factory ) { 861 // universal module definition 862 /* jshint strict: false */ 863 if ( typeof define == 'function' && define.amd ) { 864 // AMD 865 define( 'flickity/js/slide',factory ); 866 } else if ( typeof module == 'object' && module.exports ) { 867 // CommonJS 868 module.exports = factory(); 869 } else { 870 // browser global 871 window.Flickity = window.Flickity || {}; 872 window.Flickity.Slide = factory(); 873 } 874 875}( window, function factory() { 876'use strict'; 877 878function Slide( parent ) { 879 this.parent = parent; 880 this.isOriginLeft = parent.originSide == 'left'; 881 this.cells = []; 882 this.outerWidth = 0; 883 this.height = 0; 884} 885 886var proto = Slide.prototype; 887 888proto.addCell = function( cell ) { 889 this.cells.push( cell ); 890 this.outerWidth += cell.size.outerWidth; 891 this.height = Math.max( cell.size.outerHeight, this.height ); 892 // first cell stuff 893 if ( this.cells.length == 1 ) { 894 this.x = cell.x; // x comes from first cell 895 var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight'; 896 this.firstMargin = cell.size[ beginMargin ]; 897 } 898}; 899 900proto.updateTarget = function() { 901 var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft'; 902 var lastCell = this.getLastCell(); 903 var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0; 904 var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin ); 905 this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign; 906}; 907 908proto.getLastCell = function() { 909 return this.cells[ this.cells.length - 1 ]; 910}; 911 912proto.select = function() { 913 this.changeSelectedClass('add'); 914}; 915 916proto.unselect = function() { 917 this.changeSelectedClass('remove'); 918}; 919 920proto.changeSelectedClass = function( method ) {
921 this.cells.forEach( function( cell ) { 922 cell.element.classList[ method ]('is-selected'); 923 }); 924}; 925 926proto.getCellElements = function() { 927 return this.cells.map( function( cell ) { 928 return cell.element; 929 }); 930}; 931 932return Slide; 933 934})); 935 936// animate 937( function( window, factory ) { 938 // universal module definition 939 /* jshint strict: false */ 940 if ( typeof define == 'function' && define.amd ) { 941 // AMD 942 define( 'flickity/js/animate',[ 943 'fizzy-ui-utils/utils' 944 ], function( utils ) { 945 return factory( window, utils ); 946 }); 947 } else if ( typeof module == 'object' && module.exports ) { 948 // CommonJS 949 module.exports = factory( 950 window, 951 require('fizzy-ui-utils') 952 ); 953 } else { 954 // browser global 955 window.Flickity = window.Flickity || {}; 956 window.Flickity.animatePrototype = factory( 957 window, 958 window.fizzyUIUtils 959 ); 960 } 961 962}( window, function factory( window, utils ) { 963 964 965 966// -------------------------- requestAnimationFrame -------------------------- // 967 968// get rAF, prefixed, if present 969var requestAnimationFrame = window.requestAnimationFrame || 970 window.webkitRequestAnimationFrame; 971 972// fallback to setTimeout 973var lastTime = 0; 974if ( !requestAnimationFrame ) { 975 requestAnimationFrame = function( callback ) { 976 var currTime = new Date().getTime(); 977 var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); 978 var id = setTimeout( callback, timeToCall ); 979 lastTime = currTime + timeToCall; 980 return id; 981 }; 982} 983 984// -------------------------- animate -------------------------- // 985 986var proto = {}; 987 988proto.startAnimation = function() { 989 if ( this.isAnimating ) { 990 return; 991 } 992 993 this.isAnimating = true; 994 this.restingFrames = 0; 995 this.animate(); 996}; 997 998proto.animate = function() { 999 this.applyDragForce(); 1000 this.applySelectedAttraction(); 1001 1002 var previousX = this.x; 1003 1004 this.integratePhysics(); 1005 this.positionSlider(); 1006 this.settle( previousX ); 1007 // animate next frame 1008 if ( this.isAnimating ) { 1009 var _this = this; 1010 requestAnimationFrame( function animateFrame() { 1011 _this.animate(); 1012 }); 1013 } 1014}; 1015 1016 1017var transformProperty = ( function () { 1018 var style = document.documentElement.style; 1019 if ( typeof style.transform == 'string' ) { 1020 return 'transform'; 1021 } 1022 return 'WebkitTransform'; 1023})(); 1024 1025proto.positionSlider = function() { 1026 var x = this.x; 1027 // wrap position around 1028 if ( this.options.wrapAround && this.cells.length > 1 ) { 1029 x = utils.modulo( x, this.slideableWidth ); 1030 x = x - this.slideableWidth; 1031 this.shiftWrapCells( x ); 1032 } 1033 1034 x = x + this.cursorPosition; 1035 // reverse if right-to-left and using transform 1036 x = this.options.rightToLeft && transformProperty ? -x : x; 1037 var value = this.getPositionValue( x ); 1038 // use 3D tranforms for hardware acceleration on iOS 1039 // but use 2D when settled, for better font-rendering 1040 this.slider.style[ transformProperty ] = this.isAnimating ? 1041 'translate3d(' + value + ',0,0)' : 'translateX(' + value + ')'; 1042 1043 // scroll event 1044 var firstSlide = this.slides[0]; 1045 if ( firstSlide ) { 1046 var positionX = -this.x - firstSlide.target; 1047 var progress = positionX / this.slidesWidth; 1048 this.dispatchEvent( 'scroll', null, [ progress, positionX ] ); 1049 } 1050}; 1051 1052proto.positionSliderAtSelected = function() { 1053 if ( !this.cells.length ) { 1054 return; 1055 } 1056 this.x = -this.selectedSlide.target; 1057 this.positionSlider(); 1058}; 1059 1060proto.getPositionValue = function( position ) { 1061 if ( this.options.percentPosition ) { 1062 // percent position, round to 2 digits, like 12.34% 1063 return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 )+ '%'; 1064 } else { 1065 // pixel positioning 1066 return Math.round( position ) + 'px'; 1067 } 1068}; 1069 1070proto.settle = function( previousX ) { 1071 // keep track of frames where x hasn't moved 1072 if ( !this.isPointerDown && Math.round( this.x * 100 ) == Math.round( previousX * 100 ) ) { 1073 this.restingFrames++; 1074 } 1075 // stop animating if resting for 3 or more frames 1076 if ( this.restingFrames > 2 ) { 1077 this.isAnimating = false; 1078 delete this.isFreeScrolling; 1079 // render position with translateX when settled 1080 this.positionSlider(); 1081 this.dispatchEvent('settle'); 1082 } 1083}; 1084 1085proto.shiftWrapCells = function( x ) { 1086 // shift before cells 1087 var beforeGap = this.cursorPosition + x; 1088 this._shiftCells( this.beforeShiftCells, beforeGap, -1 ); 1089 // shift after cells 1090 var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition ); 1091 this._shiftCells( this.afterShiftCells, afterGap, 1 ); 1092}; 1093 1094proto._shiftCells = function( cells, gap, shift ) { 1095 for ( var i=0; i < cells.length; i++ ) { 1096 var cell = cells[i]; 1097 var cellShift = gap > 0 ? shift : 0; 1098 cell.wrapShift( cellShift ); 1099 gap -= cell.size.outerWidth; 1100 } 1101}; 1102 1103proto._unshiftCells = function( cells ) { 1104 if ( !cells || !cells.length ) { 1105 return; 1106 } 1107 for ( var i=0; i < cells.length; i++ ) { 1108 cells[i].wrapShift( 0 ); 1109 } 1110}; 1111 1112// -------------------------- physics -------------------------- // 1113 1114proto.integratePhysics = function() { 1115 this.x += this.velocity; 1116 this.velocity *= this.getFrictionFactor(); 1117}; 1118 1119proto.applyForce = function( force ) { 1120 this.velocity += force; 1121}; 1122 1123proto.getFrictionFactor = function() { 1124 return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ]; 1125}; 1126 1127proto.getRestingPosition = function() { 1128 // my thanks to Steven Wittens, who simplified this math greatly 1129 return this.x + this.velocity / ( 1 - this.getFrictionFactor() ); 1130}; 1131 1132proto.applyDragForce = function() { 1133 if ( !this.isPointerDown ) { 1134 return; 1135 } 1136 // change the position to drag position by applying force 1137 var dragVelocity = this.dragX - this.x; 1138 var dragForce = dragVelocity - this.velocity; 1139 this.applyForce( dragForce ); 1140}; 1141 1142proto.applySelectedAttraction = function() { 1143 // do not attract if pointer down or no cells 1144 if ( this.isPointerDown || this.isFreeScrolling || !this.cells.length ) { 1145 return; 1146 } 1147 var distance = this.selectedSlide.target * -1 - this.x; 1148 var force = distance * this.options.selectedAttraction; 1149 this.applyForce( force ); 1150}; 1151 1152return proto; 1153 1154})); 1155 1156// Flickity main 1157( function( window, factory ) { 1158 // universal module definition 1159 /* jshint strict: false */ 1160 if ( typeof define == 'function' && define.amd ) { 1161 // AMD 1162 define( 'flickity/js/flickity',[ 1163 'ev-emitter/ev-emitter', 1164 'get-size/get-size', 1165 'fizzy-ui-utils/utils', 1166 './cell', 1167 './slide', 1168 './animate' 1169 ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) { 1170 return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype ); 1171 }); 1172 } else if ( typeof module == 'object' && module.exports ) { 1173 // CommonJS 1174 module.exports = factory( 1175 window, 1176 require('ev-emitter'), 1177 require('get-size'), 1178 require('fizzy-ui-utils'), 1179 require('./cell'), 1180 require('./slide'), 1181 require('./animate') 1182 ); 1183 } else { 1184 // browser global 1185 var _Flickity = window.Flickity; 1186 1187 window.Flickity = factory( 1188 window, 1189 window.EvEmitter, 1190 window.getSize, 1191 window.fizzyUIUtils, 1192 _Flickity.Cell, 1193 _Flickity.Slide, 1194 _Flickity.animatePrototype 1195 ); 1196 } 1197 1198}( window, function factory( window, EvEmitter, getSize, 1199 utils, Cell, Slide, animatePrototype ) { 1200 1201 1202 1203// vars 1204var jQuery = window.jQuery; 1205var getComputedStyle = window.getComputedStyle; 1206var console = window.console; 1207 1208function moveElements( elems, toElem ) { 1209 elems = utils.makeArray( elems ); 1210 while ( elems.length ) { 1211 toElem.appendChild( elems.shift() ); 1212 } 1213} 1214 1215// -------------------------- Flickity -------------------------- // 1216 1217// globally unique identifiers 1218var GUID = 0; 1219// internal store of all Flickity intances 1220var instances = {}; 1221 1222function Flickity( element, options ) { 1223 var queryElement = utils.getQueryElement( element ); 1224 if ( !queryElement ) { 1225 if ( console ) { 1226 console.error( 'Bad element for Flickity: ' + ( queryElement || element ) ); 1227 } 1228 return; 1229 } 1230 this.element = queryElement; 1231 // do not initialize twice on same element 1232 if ( this.element.flickityGUID ) { 1233 var instance = instances[ this.element.flickityGUID ]; 1234 instance.option( options ); 1235 return instance; 1236 } 1237 1238 // add jQuery 1239 if ( jQuery ) { 1240 this.$element = jQuery( this.element ); 1241 } 1242 // options 1243 this.options = utils.extend( {}, this.constructor.defaults ); 1244 this.option( options ); 1245 1246 // kick things off 1247 this._create(); 1248} 1249 1250Flickity.defaults = { 1251 accessibility: true, 1252 // adaptiveHeight: false, 1253 cellAlign: 'center', 1254 // cellSelector: undefined,
1255 // contain: false, 1256 freeScrollFriction: 0.075, // friction when free-scrolling 1257 friction: 0.28, // friction when selecting 1258 namespaceJQueryEvents: true, 1259 // initialIndex: 0, 1260 percentPosition: true, 1261 resize: true, 1262 selectedAttraction: 0.025, 1263 setGallerySize: true 1264 // watchCSS: false, 1265 // wrapAround: false 1266}; 1267 1268// hash of methods triggered on _create() 1269Flickity.createMethods = []; 1270 1271var proto = Flickity.prototype; 1272// inherit EventEmitter 1273utils.extend( proto, EvEmitter.prototype ); 1274 1275proto._create = function() { 1276 // add id for Flickity.data 1277 var id = this.guid = ++GUID; 1278 this.element.flickityGUID = id; // expando 1279 instances[ id ] = this; // associate via id 1280 // initial properties 1281 this.selectedIndex = 0; 1282 // how many frames slider has been in same position 1283 this.restingFrames = 0; 1284 // initial physics properties 1285 this.x = 0; 1286 this.velocity = 0; 1287 this.originSide = this.options.rightToLeft ? 'right' : 'left'; 1288 // create viewport & slider 1289 this.viewport = document.createElement('div'); 1290 this.viewport.className = 'flickity-viewport'; 1291 this._createSlider(); 1292 1293 if ( this.options.resize || this.options.watchCSS ) { 1294 window.addEventListener( 'resize', this ); 1295 } 1296 1297 Flickity.createMethods.forEach( function( method ) { 1298 this[ method ](); 1299 }, this ); 1300 1301 if ( this.options.watchCSS ) { 1302 this.watchCSS(); 1303 } else { 1304 this.activate(); 1305 } 1306 1307}; 1308 1309/** 1310 * set options 1311 * @param {Object} opts 1312 */ 1313proto.option = function( opts ) { 1314 utils.extend( this.options, opts ); 1315}; 1316 1317proto.activate = function() { 1318 if ( this.isActive ) { 1319 return; 1320 } 1321 this.isActive = true; 1322 this.element.classList.add('flickity-enabled'); 1323 if ( this.options.rightToLeft ) { 1324 this.element.classList.add('flickity-rtl'); 1325 } 1326 1327 this.getSize(); 1328 // move initial cell elements so they can be loaded as cells 1329 var cellElems = this._filterFindCellElements( this.element.children ); 1330 moveElements( cellElems, this.slider ); 1331 this.viewport.appendChild( this.slider ); 1332 this.element.appendChild( this.viewport ); 1333 // get cells from children 1334 this.reloadCells(); 1335 1336 if ( this.options.accessibility ) { 1337 // allow element to focusable 1338 this.element.tabIndex = 0; 1339 // listen for key presses 1340 this.element.addEventListener( 'keydown', this ); 1341 } 1342 1343 this.emitEvent('activate'); 1344 1345 var index; 1346 var initialIndex = this.options.initialIndex; 1347 if ( this.isInitActivated ) { 1348 index = this.selectedIndex; 1349 } else if ( initialIndex !== undefined ) { 1350 index = this.cells[ initialIndex ] ? initialIndex : 0; 1351 } else { 1352 index = 0; 1353 } 1354 // select instantly 1355 this.select( index, false, true ); 1356 // flag for initial activation, for using initialIndex 1357 this.isInitActivated = true; 1358}; 1359 1360// slider positions the cells 1361proto._createSlider = function() { 1362 // slider element does all the positioning 1363 var slider = document.createElement('div'); 1364 slider.className = 'flickity-slider'; 1365 slider.style[ this.originSide ] = 0; 1366 this.slider = slider; 1367}; 1368 1369proto._filterFindCellElements = function( elems ) { 1370 return utils.filterFindElements( elems, this.options.cellSelector ); 1371}; 1372 1373// goes through all children 1374proto.reloadCells = function() { 1375 // collection of item elements 1376 this.cells = this._makeCells( this.slider.children ); 1377 this.positionCells(); 1378 this._getWrapShiftCells(); 1379 this.setGallerySize(); 1380}; 1381 1382/** 1383 * turn elements into Flickity.Cells 1384 * @param {Array or NodeList or HTMLElement} elems 1385 * @returns {Array} items - collection of new Flickity Cells 1386 */ 1387proto._makeCells = function( elems ) { 1388 var cellElems = this._filterFindCellElements( elems ); 1389 1390 // create new Flickity for collection 1391 var cells = cellElems.map( function( cellElem ) { 1392 return new Cell( cellElem, this ); 1393 }, this ); 1394 1395 return cells; 1396}; 1397 1398proto.getLastCell = function() { 1399 return this.cells[ this.cells.length - 1 ]; 1400}; 1401 1402proto.getLastSlide = function() { 1403 return this.slides[ this.slides.length - 1 ]; 1404}; 1405 1406// positions all cells 1407proto.positionCells = function() { 1408 // size all cells 1409 this._sizeCells( this.cells ); 1410 // position all cells 1411 this._positionCells( 0 ); 1412}; 1413 1414/** 1415 * position certain cells 1416 * @param {Integer} index - which cell to start with 1417 */ 1418proto._positionCells = function( index ) { 1419 index = index || 0; 1420 // also measure maxCellHeight 1421 // start 0 if positioning all cells 1422 this.maxCellHeight = index ? this.maxCellHeight || 0 : 0; 1423 var cellX = 0; 1424 // get cellX 1425 if ( index > 0 ) { 1426 var startCell = this.cells[ index - 1 ]; 1427 cellX = startCell.x + startCell.size.outerWidth; 1428 } 1429 var len = this.cells.length; 1430 for ( var i=index; i < len; i++ ) { 1431 var cell = this.cells[i]; 1432 cell.setPosition( cellX ); 1433 cellX += cell.size.outerWidth; 1434 this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight ); 1435 } 1436 // keep track of cellX for wrap-around 1437 this.slideableWidth = cellX; 1438 // slides 1439 this.updateSlides(); 1440 // contain slides target 1441 this._containSlides(); 1442 // update slidesWidth 1443 this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0; 1444}; 1445 1446/** 1447 * cell.getSize() on multiple cells 1448 * @param {Array} cells 1449 */ 1450proto._sizeCells = function( cells ) {
1451 cells.forEach( function( cell ) { 1452 cell.getSize(); 1453 }); 1454}; 1455 1456// -------------------------- -------------------------- // 1457 1458proto.updateSlides = function() { 1459 this.slides = []; 1460 if ( !this.cells.length ) { 1461 return; 1462 } 1463 1464 var slide = new Slide( this ); 1465 this.slides.push( slide ); 1466 var isOriginLeft = this.originSide == 'left'; 1467 var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft'; 1468 1469 var canCellFit = this._getCanCellFit(); 1470 1471 this.cells.forEach( function( cell, i ) { 1472 // just add cell if first cell in slide 1473 if ( !slide.cells.length ) { 1474 slide.addCell( cell ); 1475 return; 1476 } 1477 1478 var slideWidth = ( slide.outerWidth - slide.firstMargin ) + 1479 ( cell.size.outerWidth - cell.size[ nextMargin ] ); 1480 1481 if ( canCellFit.call( this, i, slideWidth ) ) { 1482 slide.addCell( cell ); 1483 } else { 1484 // doesn't fit, new slide 1485 slide.updateTarget(); 1486 1487 slide = new Slide( this ); 1488 this.slides.push( slide ); 1489 slide.addCell( cell ); 1490 } 1491 }, this ); 1492 // last slide 1493 slide.updateTarget(); 1494 // update .selectedSlide 1495 this.updateSelectedSlide(); 1496}; 1497 1498proto._getCanCellFit = function() { 1499 var groupCells = this.options.groupCells; 1500 if ( !groupCells ) { 1501 return function() { 1502 return false; 1503 }; 1504 } else if ( typeof groupCells == 'number' ) { 1505 // group by number. 3 -> [0,1,2], [3,4,5], ... 1506 var number = parseInt( groupCells, 10 ); 1507 return function( i ) { 1508 return ( i % number ) !== 0; 1509 }; 1510 } 1511 // default, group by width of slide 1512 // parse '75% 1513 var percentMatch = typeof groupCells == 'string' && 1514 groupCells.match(/^(\d+)%$/); 1515 var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1; 1516 return function( i, slideWidth ) { 1517 return slideWidth <= ( this.size.innerWidth + 1 ) * percent; 1518 }; 1519}; 1520 1521// alias _init for jQuery plugin .flickity() 1522proto._init = 1523proto.reposition = function() { 1524 this.positionCells(); 1525 this.positionSliderAtSelected(); 1526}; 1527 1528proto.getSize = function() { 1529 this.size = getSize( this.element ); 1530 this.setCellAlign(); 1531 this.cursorPosition = this.size.innerWidth * this.cellAlign; 1532}; 1533 1534var cellAlignShorthands = { 1535 // cell align, then based on origin side 1536 center: { 1537 left: 0.5, 1538 right: 0.5 1539 }, 1540 left: { 1541 left: 0, 1542 right: 1 1543 }, 1544 right: { 1545 right: 0, 1546 left: 1 1547 } 1548}; 1549 1550proto.setCellAlign = function() { 1551 var shorthand = cellAlignShorthands[ this.options.cellAlign ]; 1552 this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign; 1553}; 1554 1555proto.setGallerySize = function() { 1556 if ( this.options.setGallerySize ) { 1557 var height = this.options.adaptiveHeight && this.selectedSlide ? 1558 this.selectedSlide.height : this.maxCellHeight; 1559 this.viewport.style.height = height + 'px'; 1560 } 1561}; 1562 1563proto._getWrapShiftCells = function() { 1564 // only for wrap-around 1565 if ( !this.options.wrapAround ) { 1566 return; 1567 } 1568 // unshift previous cells 1569 this._unshiftCells( this.beforeShiftCells ); 1570 this._unshiftCells( this.afterShiftCells ); 1571 // get before cells 1572 // initial gap 1573 var gapX = this.cursorPosition; 1574 var cellIndex = this.cells.length - 1; 1575 this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 ); 1576 // get after cells 1577 // ending gap between last cell and end of gallery viewport 1578 gapX = this.size.innerWidth - this.cursorPosition; 1579 // start cloning at first cell, working forwards 1580 this.afterShiftCells = this._getGapCells( gapX, 0, 1 ); 1581}; 1582 1583proto._getGapCells = function( gapX, cellIndex, increment ) { 1584 // keep adding cells until the cover the initial gap 1585 var cells = []; 1586 while ( gapX > 0 ) { 1587 var cell = this.cells[ cellIndex ]; 1588 if ( !cell ) { 1589 break; 1590 } 1591 cells.push( cell ); 1592 cellIndex += increment; 1593 gapX -= cell.size.outerWidth; 1594 } 1595 return cells; 1596}; 1597 1598// ----- contain ----- // 1599 1600// contain cell targets so no excess sliding 1601proto._containSlides = function() { 1602 if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) { 1603 return; 1604 } 1605 var isRightToLeft = this.options.rightToLeft; 1606 var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft'; 1607 var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight'; 1608 var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ]; 1609 // content is less than gallery size 1610 var isContentSmaller = contentWidth < this.size.innerWidth; 1611 // bounds 1612 var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ]; 1613 var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign ); 1614 // contain each cell target
1615 this.slides.forEach( function( slide ) { 1616 if ( isContentSmaller ) { 1617 // all cells fit inside gallery 1618 slide.target = contentWidth * this.cellAlign; 1619 } else { 1620 // contain to bounds 1621 slide.target = Math.max( slide.target, beginBound ); 1622 slide.target = Math.min( slide.target, endBound ); 1623 } 1624 }, this ); 1625}; 1626 1627// ----- ----- // 1628 1629/** 1630 * emits events via eventEmitter and jQuery events 1631 * @param {String} type - name of event 1632 * @param {Event} event - original event 1633 * @param {Array} args - extra arguments 1634 */ 1635proto.dispatchEvent = function( type, event, args ) { 1636 var emitArgs = event ? [ event ].concat( args ) : args; 1637 this.emitEvent( type, emitArgs ); 1638 1639 if ( jQuery && this.$element ) { 1640 // default trigger with type if no event 1641 type += this.options.namespaceJQueryEvents ? '.flickity' : ''; 1642 var $event = type; 1643 if ( event ) { 1644 // create jQuery event 1645 var jQEvent = jQuery.Event( event ); 1646 jQEvent.type = type; 1647 $event = jQEvent; 1648 } 1649 this.$element.trigger( $event, args ); 1650 } 1651}; 1652 1653// -------------------------- select -------------------------- // 1654 1655/** 1656 * @param {Integer} index - index of the slide 1657 * @param {Boolean} isWrap - will wrap-around to last/first if at the end 1658 * @param {Boolean} isInstant - will immediately set position at selected cell 1659 */ 1660proto.select = function( index, isWrap, isInstant ) { 1661 if ( !this.isActive ) { 1662 return; 1663 } 1664 index = parseInt( index, 10 ); 1665 this._wrapSelect( index ); 1666 1667 if ( this.options.wrapAround || isWrap ) { 1668 index = utils.modulo( index, this.slides.length ); 1669 } 1670 // bail if invalid index 1671 if ( !this.slides[ index ] ) { 1672 return; 1673 } 1674 this.selectedIndex = index; 1675 this.updateSelectedSlide(); 1676 if ( isInstant ) { 1677 this.positionSliderAtSelected(); 1678 } else { 1679 this.startAnimation(); 1680 } 1681 if ( this.options.adaptiveHeight ) { 1682 this.setGallerySize(); 1683 } 1684 1685 this.dispatchEvent('select'); 1686 // old v1 event name, remove in v3 1687 this.dispatchEvent('cellSelect'); 1688}; 1689 1690// wraps position for wrapAround, to move to closest slide. #113 1691proto._wrapSelect = function( index ) { 1692 var len = this.slides.length; 1693 var isWrapping = this.options.wrapAround && len > 1; 1694 if ( !isWrapping ) { 1695 return index; 1696 } 1697 var wrapIndex = utils.modulo( index, len ); 1698 // go to shortest 1699 var delta = Math.abs( wrapIndex - this.selectedIndex ); 1700 var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex ); 1701 var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex ); 1702 if ( !this.isDragSelect && backWrapDelta < delta ) { 1703 index += len; 1704 } else if ( !this.isDragSelect && forewardWrapDelta < delta ) { 1705 index -= len; 1706 } 1707 // wrap position so slider is within normal area 1708 if ( index < 0 ) { 1709 this.x -= this.slideableWidth; 1710 } else if ( index >= len ) { 1711 this.x += this.slideableWidth; 1712 } 1713}; 1714 1715proto.previous = function( isWrap, isInstant ) { 1716 this.select( this.selectedIndex - 1, isWrap, isInstant ); 1717}; 1718 1719proto.next = function( isWrap, isInstant ) { 1720 this.select( this.selectedIndex + 1, isWrap, isInstant ); 1721}; 1722 1723proto.updateSelectedSlide = function() { 1724 var slide = this.slides[ this.selectedIndex ]; 1725 // selectedIndex could be outside of slides, if triggered before resize() 1726 if ( !slide ) { 1727 return; 1728 } 1729 // unselect previous selected slide 1730 this.unselectSelectedSlide(); 1731 // update new selected slide 1732 this.selectedSlide = slide; 1733 slide.select(); 1734 this.selectedCells = slide.cells; 1735 this.selectedElements = slide.getCellElements(); 1736 // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility 1737 // Remove in v3? 1738 this.selectedCell = slide.cells[0]; 1739 this.selectedElement = this.selectedElements[0]; 1740}; 1741 1742proto.unselectSelectedSlide = function() { 1743 if ( this.selectedSlide ) { 1744 this.selectedSlide.unselect(); 1745 } 1746}; 1747 1748/** 1749 * select slide from number or cell element 1750 * @param {Element or Number} elem 1751 */ 1752proto.selectCell = function( value, isWrap, isInstant ) { 1753 // get cell 1754 var cell; 1755 if ( typeof value == 'number' ) { 1756 cell = this.cells[ value ]; 1757 } else { 1758 // use string as selector 1759 if ( typeof value == 'string' ) { 1760 value = this.element.querySelector( value ); 1761 } 1762 // get cell from element 1763 cell = this.getCell( value ); 1764 } 1765 // select slide that has cell 1766 for ( var i=0; cell && i < this.slides.length; i++ ) { 1767 var slide = this.slides[i]; 1768 var index = slide.cells.indexOf( cell ); 1769 if ( index != -1 ) { 1770 this.select( i, isWrap, isInstant ); 1771 return; 1772 } 1773 } 1774}; 1775 1776// -------------------------- get cells -------------------------- // 1777 1778/** 1779 * get Flickity.Cell, given an Element 1780 * @param {Element} elem 1781 * @returns {Flickity.Cell} item 1782 */ 1783proto.getCell = function( elem ) { 1784 // loop through cells to get the one that matches 1785 for ( var i=0; i < this.cells.length; i++ ) { 1786 var cell = this.cells[i]; 1787 if ( cell.element == elem ) { 1788 return cell; 1789 } 1790 } 1791}; 1792 1793/** 1794 * get collection of Flickity.Cells, given Elements 1795 * @param {Element, Array, NodeList} elems 1796 * @returns {Array} cells - Flickity.Cells 1797 */ 1798proto.getCells = function( elems ) { 1799 elems = utils.makeArray( elems ); 1800 var cells = [];
1801 elems.forEach( function( elem ) { 1802 var cell = this.getCell( elem ); 1803 if ( cell ) { 1804 cells.push( cell ); 1805 } 1806 }, this ); 1807 return cells; 1808}; 1809 1810/** 1811 * get cell elements 1812 * @returns {Array} cellElems 1813 */ 1814proto.getCellElements = function() { 1815 return this.cells.map( function( cell ) { 1816 return cell.element; 1817 }); 1818}; 1819 1820/** 1821 * get parent cell from an element 1822 * @param {Element} elem 1823 * @returns {Flickit.Cell} cell 1824 */ 1825proto.getParentCell = function( elem ) { 1826 // first check if elem is cell 1827 var cell = this.getCell( elem ); 1828 if ( cell ) { 1829 return cell; 1830 } 1831 // try to get parent cell elem 1832 elem = utils.getParent( elem, '.flickity-slider > *' ); 1833 return this.getCell( elem ); 1834}; 1835 1836/** 1837 * get cells adjacent to a slide 1838 * @param {Integer} adjCount - number of adjacent slides 1839 * @param {Integer} index - index of slide to start 1840 * @returns {Array} cells - array of Flickity.Cells 1841 */ 1842proto.getAdjacentCellElements = function( adjCount, index ) { 1843 if ( !adjCount ) { 1844 return this.selectedSlide.getCellElements(); 1845 } 1846 index = index === undefined ? this.selectedIndex : index; 1847 1848 var len = this.slides.length; 1849 if ( 1 + ( adjCount * 2 ) >= len ) { 1850 return this.getCellElements(); 1851 } 1852 1853 var cellElems = []; 1854 for ( var i = index - adjCount; i <= index + adjCount ; i++ ) { 1855 var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i; 1856 var slide = this.slides[ slideIndex ]; 1857 if ( slide ) { 1858 cellElems = cellElems.concat( slide.getCellElements() ); 1859 } 1860 } 1861 return cellElems; 1862}; 1863 1864// -------------------------- events -------------------------- // 1865 1866proto.uiChange = function() { 1867 this.emitEvent('uiChange'); 1868}; 1869 1870proto.childUIPointerDown = function( event ) { 1871 this.emitEvent( 'childUIPointerDown', [ event ] ); 1872}; 1873 1874// ----- resize ----- // 1875 1876proto.onresize = function() { 1877 this.watchCSS(); 1878 this.resize(); 1879}; 1880 1881utils.debounceMethod( Flickity, 'onresize', 150 ); 1882 1883proto.resize = function() { 1884 if ( !this.isActive ) { 1885 return; 1886 } 1887 this.getSize(); 1888 // wrap values 1889 if ( this.options.wrapAround ) { 1890 this.x = utils.modulo( this.x, this.slideableWidth ); 1891 } 1892 this.positionCells(); 1893 this._getWrapShiftCells(); 1894 this.setGallerySize(); 1895 this.emitEvent('resize'); 1896 // update selected index for group slides, instant 1897 // TODO: position can be lost between groups of various numbers 1898 var selectedElement = this.selectedElements && this.selectedElements[0]; 1899 this.selectCell( selectedElement, false, true ); 1900}; 1901 1902// watches the :after property, activates/deactivates 1903proto.watchCSS = function() { 1904 var watchOption = this.options.watchCSS; 1905 if ( !watchOption ) { 1906 return; 1907 } 1908 1909 var afterContent = getComputedStyle( this.element, ':after' ).content; 1910 // activate if :after { content: 'flickity' } 1911 if ( afterContent.indexOf('flickity') != -1 ) { 1912 this.activate(); 1913 } else { 1914 this.deactivate(); 1915 } 1916}; 1917 1918// ----- keydown ----- // 1919 1920// go previous/next if left/right keys pressed 1921proto.onkeydown = function( event ) { 1922 // only work if element is in focus 1923 if ( !this.options.accessibility || 1924 ( document.activeElement && document.activeElement != this.element ) ) { 1925 return; 1926 } 1927 1928 if ( event.keyCode == 37 ) { 1929 // go left 1930 var leftMethod = this.options.rightToLeft ? 'next' : 'previous'; 1931 this.uiChange(); 1932 this[ leftMethod ](); 1933 } else if ( event.keyCode == 39 ) { 1934 // go right 1935 var rightMethod = this.options.rightToLeft ? 'previous' : 'next'; 1936 this.uiChange(); 1937 this[ rightMethod ](); 1938 } 1939}; 1940 1941// -------------------------- destroy -------------------------- // 1942 1943// deactivate all Flickity functionality, but keep stuff available 1944proto.deactivate = function() { 1945 if ( !this.isActive ) { 1946 return; 1947 } 1948 this.element.classList.remove('flickity-enabled'); 1949 this.element.classList.remove('flickity-rtl'); 1950 // destroy cells 1951 this.cells.forEach( function( cell ) { 1952 cell.destroy(); 1953 }); 1954 this.unselectSelectedSlide(); 1955 this.element.removeChild( this.viewport ); 1956 // move child elements back into element 1957 moveElements( this.slider.children, this.element ); 1958 if ( this.options.accessibility ) { 1959 this.element.removeAttribute('tabIndex'); 1960 this.element.removeEventListener( 'keydown', this ); 1961 } 1962 // set flags 1963 this.isActive = false;
1964 this.emitEvent('deactivate'); 1965}; 1966 1967proto.destroy = function() { 1968 this.deactivate(); 1969 window.removeEventListener( 'resize', this ); 1970 this.emitEvent('destroy'); 1971 if ( jQuery && this.$element ) { 1972 jQuery.removeData( this.element, 'flickity' ); 1973 } 1974 delete this.element.flickityGUID; 1975 delete instances[ this.guid ]; 1976}; 1977 1978// -------------------------- prototype -------------------------- // 1979 1980utils.extend( proto, animatePrototype ); 1981 1982// -------------------------- extras -------------------------- // 1983 1984/** 1985 * get Flickity instance from element 1986 * @param {Element} elem 1987 * @returns {Flickity} 1988 */ 1989Flickity.data = function( elem ) { 1990 elem = utils.getQueryElement( elem ); 1991 var id = elem && elem.flickityGUID; 1992 return id && instances[ id ]; 1993}; 1994 1995utils.htmlInit( Flickity, 'flickity' ); 1996 1997if ( jQuery && jQuery.bridget ) { 1998 jQuery.bridget( 'flickity', Flickity ); 1999} 2000 2001Flickity.Cell = Cell; 2002 2003return Flickity; 2004 2005})); 2006 2007/*! 2008 * Unipointer v2.1.0 2009 * base class for doing one thing with pointer event 2010 * MIT license 2011 */ 2012 2013/*jshint browser: true, undef: true, unused: true, strict: true */ 2014 2015( function( window, factory ) { 2016 // universal module definition 2017 /* jshint strict: false */ /*global define, module, require */ 2018 if ( typeof define == 'function' && define.amd ) { 2019 // AMD 2020 define( 'unipointer/unipointer',[ 2021 'ev-emitter/ev-emitter' 2022 ], function( EvEmitter ) { 2023 return factory( window, EvEmitter ); 2024 }); 2025 } else if ( typeof module == 'object' && module.exports ) { 2026 // CommonJS 2027 module.exports = factory( 2028 window, 2029 require('ev-emitter') 2030 ); 2031 } else { 2032 // browser global 2033 window.Unipointer = factory( 2034 window, 2035 window.EvEmitter 2036 ); 2037 } 2038 2039}( window, function factory( window, EvEmitter ) { 2040 2041 2042 2043function noop() {} 2044 2045function Unipointer() {} 2046 2047// inherit EvEmitter 2048var proto = Unipointer.prototype = Object.create( EvEmitter.prototype ); 2049 2050proto.bindStartEvent = function( elem ) { 2051 this._bindStartEvent( elem, true ); 2052}; 2053 2054proto.unbindStartEvent = function( elem ) { 2055 this._bindStartEvent( elem, false ); 2056}; 2057 2058/** 2059 * works as unbinder, as you can ._bindStart( false ) to unbind 2060 * @param {Boolean} isBind - will unbind if falsey 2061 */ 2062proto._bindStartEvent = function( elem, isBind ) { 2063 // munge isBind, default to true 2064 isBind = isBind === undefined ? true : !!isBind; 2065 var bindMethod = isBind ? 'addEventListener' : 'removeEventListener'; 2066 2067 if ( window.navigator.pointerEnabled ) { 2068 // W3C Pointer Events, IE11. See https://coderwall.com/p/mfreca 2069 elem[ bindMethod ]( 'pointerdown', this ); 2070 } else if ( window.navigator.msPointerEnabled ) { 2071 // IE10 Pointer Events 2072 elem[ bindMethod ]( 'MSPointerDown', this ); 2073 } else { 2074 // listen for both, for devices like Chrome Pixel 2075 elem[ bindMethod ]( 'mousedown', this ); 2076 elem[ bindMethod ]( 'touchstart', this ); 2077 } 2078}; 2079 2080// trigger handler methods for events 2081proto.handleEvent = function( event ) { 2082 var method = 'on' + event.type; 2083 if ( this[ method ] ) { 2084 this[ method ]( event ); 2085 } 2086}; 2087 2088// returns the touch that we're keeping track of 2089proto.getTouch = function( touches ) { 2090 for ( var i=0; i < touches.length; i++ ) { 2091 var touch = touches[i]; 2092 if ( touch.identifier == this.pointerIdentifier ) { 2093 return touch; 2094 } 2095 } 2096}; 2097 2098// ----- start event ----- // 2099 2100proto.onmousedown = function( event ) { 2101 // dismiss clicks from right or middle buttons 2102 var button = event.button; 2103 if ( button && ( button !== 0 && button !== 1 ) ) { 2104 return; 2105 } 2106 this._pointerDown( event, event ); 2107}; 2108 2109proto.ontouchstart = function( event ) { 2110 this._pointerDown( event, event.changedTouches[0] ); 2111}; 2112 2113proto.onMSPointerDown = 2114proto.onpointerdown = function( event ) { 2115 this._pointerDown( event, event ); 2116}; 2117 2118/** 2119 * pointer start 2120 * @param {Event} event 2121 * @param {Event or Touch} pointer 2122 */ 2123proto._pointerDown = function( event, pointer ) { 2124 // dismiss other pointers 2125 if ( this.isPointerDown ) { 2126 return; 2127 } 2128 2129 this.isPointerDown = true; 2130 // save pointer identifier to match up touch events 2131 this.pointerIdentifier = pointer.pointerId !== undefined ? 2132 // pointerId for pointer events, touch.indentifier for touch events 2133 pointer.pointerId : pointer.identifier; 2134 2135 this.pointerDown( event, pointer ); 2136}; 2137 2138proto.pointerDown = function( event, pointer ) { 2139 this._bindPostStartEvents( event ); 2140 this.emitEvent( 'pointerDown', [ event, pointer ] ); 2141}; 2142 2143// hash of events to be bound after start event 2144var postStartEvents = {
2145 mousedown: [ 'mousemove', 'mouseup' ], 2146 touchstart: [ 'touchmove', 'touchend', 'touchcancel' ], 2147 pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ], 2148 MSPointerDown: [ 'MSPointerMove', 'MSPointerUp', 'MSPointerCancel' ] 2149}; 2150 2151proto._bindPostStartEvents = function( event ) { 2152 if ( !event ) { 2153 return; 2154 } 2155 // get proper events to match start event 2156 var events = postStartEvents[ event.type ]; 2157 // bind events to node 2158 events.forEach( function( eventName ) { 2159 window.addEventListener( eventName, this ); 2160 }, this ); 2161 // save these arguments 2162 this._boundPointerEvents = events; 2163}; 2164 2165proto._unbindPostStartEvents = function() { 2166 // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug) 2167 if ( !this._boundPointerEvents ) { 2168 return; 2169 } 2170 this._boundPointerEvents.forEach( function( eventName ) { 2171 window.removeEventListener( eventName, this ); 2172 }, this ); 2173 2174 delete this._boundPointerEvents; 2175}; 2176 2177// ----- move event ----- // 2178 2179proto.onmousemove = function( event ) { 2180 this._pointerMove( event, event ); 2181}; 2182 2183proto.onMSPointerMove = 2184proto.onpointermove = function( event ) { 2185 if ( event.pointerId == this.pointerIdentifier ) { 2186 this._pointerMove( event, event ); 2187 } 2188}; 2189 2190proto.ontouchmove = function( event ) { 2191 var touch = this.getTouch( event.changedTouches ); 2192 if ( touch ) { 2193 this._pointerMove( event, touch ); 2194 } 2195}; 2196 2197/** 2198 * pointer move 2199 * @param {Event} event 2200 * @param {Event or Touch} pointer 2201 * @private 2202 */ 2203proto._pointerMove = function( event, pointer ) { 2204 this.pointerMove( event, pointer ); 2205}; 2206 2207// public 2208proto.pointerMove = function( event, pointer ) { 2209 this.emitEvent( 'pointerMove', [ event, pointer ] ); 2210}; 2211 2212// ----- end event ----- // 2213 2214 2215proto.onmouseup = function( event ) { 2216 this._pointerUp( event, event ); 2217}; 2218 2219proto.onMSPointerUp = 2220proto.onpointerup = function( event ) { 2221 if ( event.pointerId == this.pointerIdentifier ) { 2222 this._pointerUp( event, event ); 2223 } 2224}; 2225 2226proto.ontouchend = function( event ) { 2227 var touch = this.getTouch( event.changedTouches ); 2228 if ( touch ) { 2229 this._pointerUp( event, touch ); 2230 } 2231}; 2232 2233/** 2234 * pointer up 2235 * @param {Event} event 2236 * @param {Event or Touch} pointer 2237 * @private 2238 */ 2239proto._pointerUp = function( event, pointer ) { 2240 this._pointerDone(); 2241 this.pointerUp( event, pointer ); 2242}; 2243 2244// public 2245proto.pointerUp = function( event, pointer ) { 2246 this.emitEvent( 'pointerUp', [ event, pointer ] ); 2247}; 2248 2249// ----- pointer done ----- // 2250 2251// triggered on pointer up & pointer cancel 2252proto._pointerDone = function() { 2253 // reset properties 2254 this.isPointerDown = false; 2255 delete this.pointerIdentifier; 2256 // remove events 2257 this._unbindPostStartEvents(); 2258 this.pointerDone(); 2259}; 2260 2261proto.pointerDone = noop; 2262 2263// ----- pointer cancel ----- // 2264 2265proto.onMSPointerCancel = 2266proto.onpointercancel = function( event ) { 2267 if ( event.pointerId == this.pointerIdentifier ) { 2268 this._pointerCancel( event, event ); 2269 } 2270}; 2271 2272proto.ontouchcancel = function( event ) { 2273 var touch = this.getTouch( event.changedTouches ); 2274 if ( touch ) { 2275 this._pointerCancel( event, touch ); 2276 } 2277}; 2278 2279/** 2280 * pointer cancel 2281 * @param {Event} event 2282 * @param {Event or Touch} pointer 2283 * @private 2284 */ 2285proto._pointerCancel = function( event, pointer ) { 2286 this._pointerDone(); 2287 this.pointerCancel( event, pointer ); 2288}; 2289 2290// public 2291proto.pointerCancel = function( event, pointer ) { 2292 this.emitEvent( 'pointerCancel', [ event, pointer ] ); 2293}; 2294 2295// ----- ----- // 2296 2297// utility function for getting x/y coords from event 2298Unipointer.getPointerPoint = function( pointer ) { 2299 return { 2300 x: pointer.pageX, 2301 y: pointer.pageY 2302 }; 2303}; 2304 2305// ----- ----- // 2306 2307return Unipointer; 2308 2309})); 2310 2311/*! 2312 * Unidragger v2.1.0 2313 * Draggable base class 2314 * MIT license 2315 */ 2316 2317/*jshint browser: true, unused: true, undef: true, strict: true */ 2318 2319( function( window, factory ) { 2320 // universal module definition 2321 /*jshint strict: false */ /*globals define, module, require */ 2322 2323 if ( typeof define == 'function' && define.amd ) { 2324 // AMD 2325 define( 'unidragger/unidragger',[ 2326 'unipointer/unipointer' 2327 ], function( Unipointer ) { 2328 return factory( window, Unipointer ); 2329 }); 2330 } else if ( typeof module == 'object' && module.exports ) { 2331 // CommonJS 2332 module.exports = factory( 2333 window, 2334 require('unipointer') 2335 ); 2336 } else { 2337 // browser global 2338 window.Unidragger = factory( 2339 window, 2340 window.Unipointer 2341 ); 2342 } 2343 2344}( window, function factory( window, Unipointer ) { 2345 2346 2347 2348// ----- ----- // 2349 2350function noop() {} 2351 2352// -------------------------- Unidragger -------------------------- // 2353 2354function Unidragger() {} 2355 2356// inherit Unipointer & EvEmitter 2357var proto = Unidragger.prototype = Object.create( Unipointer.prototype ); 2358 2359// ----- bind start ----- // 2360 2361proto.bindHandles = function() { 2362 this._bindHandles( true ); 2363}; 2364 2365proto.unbindHandles = function() { 2366 this._bindHandles( false ); 2367}; 2368 2369var navigator = window.navigator; 2370/** 2371 * works as unbinder, as you can .bindHandles( false ) to unbind 2372 * @param {Boolean} isBind - will unbind if falsey 2373 */ 2374proto._bindHandles = function( isBind ) { 2375 // munge isBind, default to true 2376 isBind = isBind === undefined ? true : !!isBind; 2377 // extra bind logic 2378 var binderExtra; 2379 if ( navigator.pointerEnabled ) { 2380 binderExtra = function( handle ) { 2381 // disable scrolling on the element 2382 handle.style.touchAction = isBind ? 'none' : ''; 2383 }; 2384 } else if ( navigator.msPointerEnabled ) { 2385 binderExtra = function( handle ) { 2386 // disable scrolling on the element 2387 handle.style.msTouchAction = isBind ? 'none' : ''; 2388 }; 2389 } else { 2390 binderExtra = noop; 2391 } 2392 // bind each handle 2393 var bindMethod = isBind ? 'addEventListener' : 'removeEventListener'; 2394 for ( var i=0; i < this.handles.length; i++ ) { 2395 var handle = this.handles[i]; 2396 this._bindStartEvent( handle, isBind ); 2397 binderExtra( handle ); 2398 handle[ bindMethod ]( 'click', this ); 2399 } 2400}; 2401 2402// ----- start event ----- // 2403 2404/** 2405 * pointer start 2406 * @param {Event} event 2407 * @param {Event or Touch} pointer 2408 */ 2409proto.pointerDown = function( event, pointer ) { 2410 // dismiss range sliders 2411 if ( event.target.nodeName == 'INPUT' && event.target.type == 'range' ) { 2412 // reset pointerDown logic 2413 this.isPointerDown = false;
2414 delete this.pointerIdentifier; 2415 return; 2416 } 2417 2418 this._dragPointerDown( event, pointer ); 2419 // kludge to blur focused inputs in dragger 2420 var focused = document.activeElement; 2421 if ( focused && focused.blur ) { 2422 focused.blur(); 2423 } 2424 // bind move and end events 2425 this._bindPostStartEvents( event ); 2426 this.emitEvent( 'pointerDown', [ event, pointer ] ); 2427}; 2428 2429// base pointer down logic 2430proto._dragPointerDown = function( event, pointer ) { 2431 // track to see when dragging starts 2432 this.pointerDownPoint = Unipointer.getPointerPoint( pointer ); 2433 2434 var canPreventDefault = this.canPreventDefaultOnPointerDown( event, pointer ); 2435 if ( canPreventDefault ) { 2436 event.preventDefault(); 2437 } 2438}; 2439 2440// overwriteable method so Flickity can prevent for scrolling 2441proto.canPreventDefaultOnPointerDown = function( event ) { 2442 // prevent default, unless touchstart or <select> 2443 return event.target.nodeName != 'SELECT'; 2444}; 2445 2446// ----- move event ----- // 2447 2448/** 2449 * drag move 2450 * @param {Event} event 2451 * @param {Event or Touch} pointer 2452 */ 2453proto.pointerMove = function( event, pointer ) { 2454 var moveVector = this._dragPointerMove( event, pointer ); 2455 this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] ); 2456 this._dragMove( event, pointer, moveVector ); 2457}; 2458 2459// base pointer move logic 2460proto._dragPointerMove = function( event, pointer ) { 2461 var movePoint = Unipointer.getPointerPoint( pointer ); 2462 var moveVector = { 2463 x: movePoint.x - this.pointerDownPoint.x, 2464 y: movePoint.y - this.pointerDownPoint.y 2465 }; 2466 // start drag if pointer has moved far enough to start drag 2467 if ( !this.isDragging && this.hasDragStarted( moveVector ) ) { 2468 this._dragStart( event, pointer ); 2469 } 2470 return moveVector; 2471}; 2472 2473// condition if pointer has moved far enough to start drag 2474proto.hasDragStarted = function( moveVector ) { 2475 return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3; 2476}; 2477 2478 2479// ----- end event ----- // 2480 2481/** 2482 * pointer up 2483 * @param {Event} event 2484 * @param {Event or Touch} pointer 2485 */ 2486proto.pointerUp = function( event, pointer ) { 2487 this.emitEvent( 'pointerUp', [ event, pointer ] ); 2488 this._dragPointerUp( event, pointer ); 2489}; 2490 2491proto._dragPointerUp = function( event, pointer ) { 2492 if ( this.isDragging ) { 2493 this._dragEnd( event, pointer ); 2494 } else { 2495 // pointer didn't move enough for drag to start 2496 this._staticClick( event, pointer ); 2497 } 2498}; 2499 2500// -------------------------- drag -------------------------- // 2501 2502// dragStart 2503proto._dragStart = function( event, pointer ) { 2504 this.isDragging = true; 2505 this.dragStartPoint = Unipointer.getPointerPoint( pointer ); 2506 // prevent clicks
2507 this.isPreventingClicks = true; 2508 2509 this.dragStart( event, pointer ); 2510}; 2511 2512proto.dragStart = function( event, pointer ) { 2513 this.emitEvent( 'dragStart', [ event, pointer ] ); 2514}; 2515 2516// dragMove 2517proto._dragMove = function( event, pointer, moveVector ) { 2518 // do not drag if not dragging yet 2519 if ( !this.isDragging ) { 2520 return; 2521 } 2522 2523 this.dragMove( event, pointer, moveVector ); 2524}; 2525 2526proto.dragMove = function( event, pointer, moveVector ) { 2527 event.preventDefault(); 2528 this.emitEvent( 'dragMove', [ event, pointer, moveVector ] ); 2529}; 2530 2531// dragEnd 2532proto._dragEnd = function( event, pointer ) { 2533 // set flags 2534 this.isDragging = false; 2535 // re-enable clicking async 2536 setTimeout( function() { 2537 delete this.isPreventingClicks; 2538 }.bind( this ) ); 2539 2540 this.dragEnd( event, pointer ); 2541}; 2542 2543proto.dragEnd = function( event, pointer ) { 2544 this.emitEvent( 'dragEnd', [ event, pointer ] ); 2545}; 2546 2547// ----- onclick ----- // 2548 2549// handle all clicks and prevent clicks when dragging 2550proto.onclick = function( event ) { 2551 if ( this.isPreventingClicks ) { 2552 event.preventDefault(); 2553 } 2554}; 2555 2556// ----- staticClick ----- // 2557 2558// triggered after pointer down & up with no/tiny movement 2559proto._staticClick = function( event, pointer ) { 2560 // ignore emulated mouse up clicks 2561 if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) { 2562 return; 2563 } 2564 2565 // allow click in <input>s and <textarea>s 2566 var nodeName = event.target.nodeName; 2567 if ( nodeName == 'INPUT' || nodeName == 'TEXTAREA' ) { 2568 event.target.focus(); 2569 } 2570 this.staticClick( event, pointer ); 2571 2572 // set flag for emulated clicks 300ms after touchend 2573 if ( event.type != 'mouseup' ) { 2574 this.isIgnoringMouseUp = true; 2575 // reset flag after 300ms 2576 setTimeout( function() { 2577 delete this.isIgnoringMouseUp; 2578 }.bind( this ), 400 ); 2579 } 2580}; 2581 2582proto.staticClick = function( event, pointer ) { 2583 this.emitEvent( 'staticClick', [ event, pointer ] ); 2584}; 2585 2586// ----- utils ----- // 2587 2588Unidragger.getPointerPoint = Unipointer.getPointerPoint; 2589 2590// ----- ----- // 2591 2592return Unidragger; 2593 2594})); 2595 2596// drag 2597( function( window, factory ) { 2598 // universal module definition 2599 /* jshint strict: false */ 2600 if ( typeof define == 'function' && define.amd ) { 2601 // AMD 2602 define( 'flickity/js/drag',[ 2603 './flickity', 2604 'unidragger/unidragger', 2605 'fizzy-ui-utils/utils' 2606 ], function( Flickity, Unidragger, utils ) { 2607 return factory( window, Flickity, Unidragger, utils ); 2608 }); 2609 } else if ( typeof module == 'object' && module.exports ) { 2610 // CommonJS 2611 module.exports = factory( 2612 window, 2613 require('./flickity'), 2614 require('unidragger'), 2615 require('fizzy-ui-utils') 2616 ); 2617 } else { 2618 // browser global 2619 window.Flickity = factory( 2620 window, 2621 window.Flickity, 2622 window.Unidragger, 2623 window.fizzyUIUtils 2624 ); 2625 } 2626 2627}( window, function factory( window, Flickity, Unidragger, utils ) { 2628 2629 2630 2631// ----- defaults ----- // 2632 2633utils.extend( Flickity.defaults, { 2634 draggable: true, 2635 dragThreshold: 3, 2636}); 2637 2638// ----- create ----- // 2639 2640Flickity.createMethods.push('_createDrag'); 2641 2642// -------------------------- drag prototype -------------------------- // 2643 2644var proto = Flickity.prototype; 2645utils.extend( proto, Unidragger.prototype ); 2646 2647// -------------------------- -------------------------- // 2648 2649var isTouch = 'createTouch' in document; 2650var isTouchmoveScrollCanceled = false; 2651 2652proto._createDrag = function() { 2653 this.on( 'activate', this.bindDrag ); 2654 this.on( 'uiChange', this._uiChangeDrag ); 2655 this.on( 'childUIPointerDown', this._childUIPointerDownDrag ); 2656 this.on( 'deactivate', this.unbindDrag ); 2657 // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior 2658 // #457, RubaXa/Sortable#973 2659 if ( isTouch && !isTouchmoveScrollCanceled ) { 2660 window.addEventListener( 'touchmove', function() {}); 2661 isTouchmoveScrollCanceled = true; 2662 } 2663}; 2664 2665proto.bindDrag = function() { 2666 if ( !this.options.draggable || this.isDragBound ) { 2667 return; 2668 } 2669 this.element.classList.add('is-draggable'); 2670 this.handles = [ this.viewport ]; 2671 this.bindHandles(); 2672 this.isDragBound = true; 2673}; 2674 2675proto.unbindDrag = function() { 2676 if ( !this.isDragBound ) { 2677 return; 2678 } 2679 this.element.classList.remove('is-draggable'); 2680 this.unbindHandles(); 2681 delete this.isDragBound; 2682}; 2683 2684proto._uiChangeDrag = function() { 2685 delete this.isFreeScrolling; 2686}; 2687 2688proto._childUIPointerDownDrag = function( event ) { 2689 event.preventDefault(); 2690 this.pointerDownFocus( event ); 2691}; 2692 2693// -------------------------- pointer events -------------------------- // 2694 2695// nodes that have text fields 2696var cursorNodes = { 2697 TEXTAREA: true, 2698 INPUT: true, 2699 OPTION: true, 2700}; 2701 2702// input types that do not have text fields 2703var clickTypes = { 2704 radio: true, 2705 checkbox: true, 2706 button: true, 2707 submit: true, 2708 image: true, 2709 file: true, 2710}; 2711 2712proto.pointerDown = function( event, pointer ) { 2713 // dismiss inputs with text fields. #403, #404 2714 var isCursorInput = cursorNodes[ event.target.nodeName ] && 2715 !clickTypes[ event.target.type ]; 2716 if ( isCursorInput ) { 2717 // reset pointerDown logic 2718 this.isPointerDown = false;
2719 delete this.pointerIdentifier; 2720 return; 2721 } 2722 2723 this._dragPointerDown( event, pointer ); 2724 2725 // kludge to blur focused inputs in dragger 2726 var focused = document.activeElement; 2727 if ( focused && focused.blur && focused != this.element && 2728 // do not blur body for IE9 & 10, #117 2729 focused != document.body ) { 2730 focused.blur(); 2731 } 2732 this.pointerDownFocus( event ); 2733 // stop if it was moving 2734 this.dragX = this.x; 2735 this.viewport.classList.add('is-pointer-down'); 2736 // bind move and end events 2737 this._bindPostStartEvents( event ); 2738 // track scrolling 2739 this.pointerDownScroll = getScrollPosition(); 2740 window.addEventListener( 'scroll', this ); 2741 2742 this.dispatchEvent( 'pointerDown', event, [ pointer ] ); 2743}; 2744 2745var touchStartEvents = { 2746 touchstart: true, 2747 MSPointerDown: true 2748}; 2749 2750var focusNodes = { 2751 INPUT: true, 2752 SELECT: true 2753}; 2754 2755proto.pointerDownFocus = function( event ) { 2756 // focus element, if not touch, and its not an input or select 2757 if ( !this.options.accessibility || touchStartEvents[ event.type ] || 2758 focusNodes[ event.target.nodeName ] ) { 2759 return; 2760 } 2761 var prevScrollY = window.pageYOffset; 2762 this.element.focus(); 2763 // hack to fix scroll jump after focus, #76 2764 if ( window.pageYOffset != prevScrollY ) { 2765 window.scrollTo( window.pageXOffset, prevScrollY ); 2766 } 2767}; 2768 2769proto.canPreventDefaultOnPointerDown = function( event ) { 2770 // prevent default, unless touchstart or <select> 2771 var isTouchstart = event.type == 'touchstart'; 2772 var targetNodeName = event.target.nodeName; 2773 return !isTouchstart && targetNodeName != 'SELECT'; 2774}; 2775 2776// ----- move ----- // 2777 2778proto.hasDragStarted = function( moveVector ) { 2779 return Math.abs( moveVector.x ) > this.options.dragThreshold; 2780}; 2781 2782// ----- up ----- // 2783 2784proto.pointerUp = function( event, pointer ) { 2785 delete this.isTouchScrolling; 2786 this.viewport.classList.remove('is-pointer-down'); 2787 this.dispatchEvent( 'pointerUp', event, [ pointer ] ); 2788 this._dragPointerUp( event, pointer ); 2789}; 2790 2791proto.pointerDone = function() { 2792 window.removeEventListener( 'scroll', this ); 2793 delete this.pointerDownScroll; 2794}; 2795 2796// -------------------------- dragging -------------------------- // 2797 2798proto.dragStart = function( event, pointer ) { 2799 this.dragStartPosition = this.x; 2800 this.startAnimation(); 2801 window.removeEventListener( 'scroll', this ); 2802 this.dispatchEvent( 'dragStart', event, [ pointer ] ); 2803}; 2804 2805proto.pointerMove = function( event, pointer ) { 2806 var moveVector = this._dragPointerMove( event, pointer ); 2807 this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] ); 2808 this._dragMove( event, pointer, moveVector ); 2809}; 2810 2811proto.dragMove = function( event, pointer, moveVector ) { 2812 event.preventDefault(); 2813 2814 this.previousDragX = this.dragX; 2815 // reverse if right-to-left 2816 var direction = this.options.rightToLeft ? -1 : 1; 2817 var dragX = this.dragStartPosition + moveVector.x * direction; 2818 2819 if ( !this.options.wrapAround && this.slides.length ) { 2820 // slow drag 2821 var originBound = Math.max( -this.slides[0].target, this.dragStartPosition ); 2822 dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX; 2823 var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition ); 2824 dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX; 2825 } 2826 2827 this.dragX = dragX; 2828 2829 this.dragMoveTime = new Date(); 2830 this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] ); 2831}; 2832 2833proto.dragEnd = function( event, pointer ) { 2834 if ( this.options.freeScroll ) { 2835 this.isFreeScrolling = true; 2836 } 2837 // set selectedIndex based on where flick will end up 2838 var index = this.dragEndRestingSelect(); 2839 2840 if ( this.options.freeScroll && !this.options.wrapAround ) { 2841 // if free-scroll & not wrap around 2842 // do not free-scroll if going outside of bounding slides 2843 // so bounding slides can attract slider, and keep it in bounds 2844 var restingX = this.getRestingPosition(); 2845 this.isFreeScrolling = -restingX > this.slides[0].target && 2846 -restingX < this.getLastSlide().target; 2847 } else if ( !this.options.freeScroll && index == this.selectedIndex ) { 2848 // boost selection if selected index has not changed 2849 index += this.dragEndBoostSelect(); 2850 } 2851 delete this.previousDragX; 2852 // apply selection 2853 // TODO refactor this, selecting here feels weird 2854 // HACK, set flag so dragging stays in correct direction 2855 this.isDragSelect = this.options.wrapAround; 2856 this.select( index ); 2857 delete this.isDragSelect; 2858 this.dispatchEvent( 'dragEnd', event, [ pointer ] ); 2859}; 2860 2861proto.dragEndRestingSelect = function() { 2862 var restingX = this.getRestingPosition(); 2863 // how far away from selected slide
2864 var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) ); 2865 // get closet resting going up and going down 2866 var positiveResting = this._getClosestResting( restingX, distance, 1 ); 2867 var negativeResting = this._getClosestResting( restingX, distance, -1 ); 2868 // use closer resting for wrap-around 2869 var index = positiveResting.distance < negativeResting.distance ? 2870 positiveResting.index : negativeResting.index; 2871 return index; 2872}; 2873 2874/** 2875 * given resting X and distance to selected cell 2876 * get the distance and index of the closest cell 2877 * @param {Number} restingX - estimated post-flick resting position 2878 * @param {Number} distance - distance to selected cell 2879 * @param {Integer} increment - +1 or -1, going up or down 2880 * @returns {Object} - { distance: {Number}, index: {Integer} } 2881 */ 2882proto._getClosestResting = function( restingX, distance, increment ) { 2883 var index = this.selectedIndex; 2884 var minDistance = Infinity; 2885 var condition = this.options.contain && !this.options.wrapAround ? 2886 // if contain, keep going if distance is equal to minDistance 2887 function( d, md ) { return d <= md; } : function( d, md ) { return d < md; }; 2888 while ( condition( distance, minDistance ) ) { 2889 // measure distance to next cell 2890 index += increment; 2891 minDistance = distance; 2892 distance = this.getSlideDistance( -restingX, index ); 2893 if ( distance === null ) { 2894 break; 2895 } 2896 distance = Math.abs( distance ); 2897 } 2898 return { 2899 distance: minDistance, 2900 // selected was previous index 2901 index: index - increment 2902 }; 2903}; 2904 2905/** 2906 * measure distance between x and a slide target 2907 * @param {Number} x 2908 * @param {Integer} index - slide index 2909 */ 2910proto.getSlideDistance = function( x, index ) { 2911 var len = this.slides.length; 2912 // wrap around if at least 2 slides 2913 var isWrapAround = this.options.wrapAround && len > 1; 2914 var slideIndex = isWrapAround ? utils.modulo( index, len ) : index; 2915 var slide = this.slides[ slideIndex ]; 2916 if ( !slide ) { 2917 return null; 2918 } 2919 // add distance for wrap-around slides 2920 var wrap = isWrapAround ? this.slideableWidth * Math.floor( index / len ) : 0; 2921 return x - ( slide.target + wrap ); 2922}; 2923 2924proto.dragEndBoostSelect = function() { 2925 // do not boost if no previousDragX or dragMoveTime 2926 if ( this.previousDragX === undefined || !this.dragMoveTime || 2927 // or if drag was held for 100 ms 2928 new Date() - this.dragMoveTime > 100 ) { 2929 return 0; 2930 } 2931 2932 var distance = this.getSlideDistance( -this.dragX, this.selectedIndex ); 2933 var delta = this.previousDragX - this.dragX; 2934 if ( distance > 0 && delta > 0 ) { 2935 // boost to next if moving towards the right, and positive velocity 2936 return 1; 2937 } else if ( distance < 0 && delta < 0 ) { 2938 // boost to previous if moving towards the left, and negative velocity 2939 return -1; 2940 } 2941 return 0; 2942}; 2943 2944// ----- staticClick ----- // 2945 2946proto.staticClick = function( event, pointer ) { 2947 // get clickedCell, if cell was clicked 2948 var clickedCell = this.getParentCell( event.target ); 2949 var cellElem = clickedCell && clickedCell.element; 2950 var cellIndex = clickedCell && this.cells.indexOf( clickedCell ); 2951 this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] ); 2952}; 2953 2954// ----- scroll ----- // 2955 2956proto.onscroll = function() { 2957 var scroll = getScrollPosition(); 2958 var scrollMoveX = this.pointerDownScroll.x - scroll.x; 2959 var scrollMoveY = this.pointerDownScroll.y - scroll.y; 2960 // cancel click/tap if scroll is too much 2961 if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) { 2962 this._pointerDone(); 2963 } 2964}; 2965 2966// ----- utils ----- // 2967 2968function getScrollPosition() { 2969 return { 2970 x: window.pageXOffset, 2971 y: window.pageYOffset 2972 }; 2973} 2974 2975// ----- ----- // 2976 2977return Flickity; 2978 2979})); 2980 2981/*! 2982 * Tap listener v2.0.0 2983 * listens to taps 2984 * MIT license 2985 */ 2986 2987/*jshint browser: true, unused: true, undef: true, strict: true */ 2988 2989( function( window, factory ) { 2990 // universal module definition 2991 /*jshint strict: false*/ /*globals define, module, require */ 2992 2993 if ( typeof define == 'function' && define.amd ) { 2994 // AMD 2995 define( 'tap-listener/tap-listener',[
2996 'unipointer/unipointer' 2997 ], function( Unipointer ) { 2998 return factory( window, Unipointer ); 2999 }); 3000 } else if ( typeof module == 'object' && module.exports ) { 3001 // CommonJS 3002 module.exports = factory( 3003 window, 3004 require('unipointer') 3005 ); 3006 } else { 3007 // browser global 3008 window.TapListener = factory( 3009 window, 3010 window.Unipointer 3011 ); 3012 } 3013 3014}( window, function factory( window, Unipointer ) { 3015 3016 3017 3018// -------------------------- TapListener -------------------------- // 3019 3020function TapListener( elem ) { 3021 this.bindTap( elem ); 3022} 3023 3024// inherit Unipointer & EventEmitter 3025var proto = TapListener.prototype = Object.create( Unipointer.prototype ); 3026 3027/** 3028 * bind tap event to element 3029 * @param {Element} elem 3030 */ 3031proto.bindTap = function( elem ) { 3032 if ( !elem ) { 3033 return; 3034 } 3035 this.unbindTap(); 3036 this.tapElement = elem; 3037 this._bindStartEvent( elem, true ); 3038}; 3039 3040proto.unbindTap = function() { 3041 if ( !this.tapElement ) { 3042 return; 3043 } 3044 this._bindStartEvent( this.tapElement, true ); 3045 delete this.tapElement; 3046}; 3047 3048/** 3049 * pointer up 3050 * @param {Event} event 3051 * @param {Event or Touch} pointer 3052 */ 3053proto.pointerUp = function( event, pointer ) { 3054 // ignore emulated mouse up clicks 3055 if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) { 3056 return; 3057 } 3058 3059 var pointerPoint = Unipointer.getPointerPoint( pointer ); 3060 var boundingRect = this.tapElement.getBoundingClientRect(); 3061 var scrollX = window.pageXOffset; 3062 var scrollY = window.pageYOffset; 3063 // calculate if pointer is inside tapElement 3064 var isInside = pointerPoint.x >
3064= boundingRect.left + scrollX && 3065 pointerPoint.x <= boundingRect.right + scrollX && 3066 pointerPoint.y >= boundingRect.top + scrollY && 3067 pointerPoint.y <= boundingRect.bottom + scrollY; 3068 // trigger callback if pointer is inside element 3069 if ( isInside ) { 3070 this.emitEvent( 'tap', [ event, pointer ] ); 3071 } 3072 3073 // set flag for emulated clicks 300ms after touchend 3074 if ( event.type != 'mouseup' ) { 3075 this.isIgnoringMouseUp = true; 3076 // reset flag after 300ms 3077 var _this = this; 3078 setTimeout( function() { 3079 delete _this.isIgnoringMouseUp; 3080 }, 400 ); 3081 } 3082}; 3083 3084proto.destroy = function() { 3085 this.pointerDone(); 3086 this.unbindTap(); 3087}; 3088 3089// ----- ----- // 3090 3091return TapListener; 3092 3093})); 3094 3095// prev/next buttons 3096( function( window, factory ) { 3097 // universal module definition 3098 /* jshint strict: false */ 3099 if ( typeof define == 'function' && define.amd ) { 3100 // AMD 3101 define( 'flickity/js/prev-next-button',[ 3102 './flickity', 3103 'tap-listener/tap-listener', 3104 'fizzy-ui-utils/utils' 3105 ], function( Flickity, TapListener, utils ) { 3106 return factory( window, Flickity, TapListener, utils ); 3107 }); 3108 } else if ( typeof module == 'object' && module.exports ) { 3109 // CommonJS 3110 module.exports = factory( 3111 window, 3112 require('./flickity'), 3113 require('tap-listener'), 3114 require('fizzy-ui-utils') 3115 ); 3116 } else { 3117 // browser global 3118 factory( 3119 window, 3120 window.Flickity, 3121 window.TapListener, 3122 window.fizzyUIUtils 3123 ); 3124 } 3125 3126}( window, function factory( window, Flickity, TapListener, utils ) { 3127'use strict'; 3128 3129var svgURI = 'http://www.w3.org/2000/svg'; 3130 3131// -------------------------- PrevNextButton -------------------------- // 3132 3133function PrevNextButton( direction, parent ) { 3134 this.direction = direction; 3135 this.parent = parent; 3136 this._create(); 3137} 3138 3139PrevNextButton.prototype = new TapListener(); 3140 3141PrevNextButton.prototype._create = function() { 3142 // properties 3143 this.isEnabled = true; 3144 this.isPrevious = this.direction == -1; 3145 var leftDirection = this.parent.options.rightToLeft ? 1 : -1; 3146 this.isLeft = this.direction == leftDirection; 3147 3148 var element = this.element = document.createElement('button'); 3149 element.className = 'flickity-prev-next-button'; 3150 element.className += this.isPrevious ? ' previous' : ' next'; 3151 // prevent button from submitting form http://stackoverflow.com/a/10836076/182183 3152 element.setAttribute( 'type', 'button' ); 3153 // init as disabled 3154 this.disable(); 3155 3156 element.setAttribute( 'aria-label', this.isPrevious ? 'previous' : 'next' ); 3157 3158 // create arrow 3159 var svg = this.createSVG(); 3160 element.appendChild( svg ); 3161 // events 3162 this.on( 'tap', this.onTap ); 3163 this.parent.on( 'select', this.update.bind( this ) ); 3164 this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) ); 3165}; 3166 3167PrevNextButton.prototype.activate = function() { 3168 this.bindTap( this.element ); 3169 // click events from keyboard 3170 this.element.addEventListener( 'click', this ); 3171 // add to DOM 3172 this.parent.element.appendChild( this.element ); 3173}; 3174 3175PrevNextButton.prototype.deactivate = function() { 3176 // remove from DOM 3177 this.parent.element.removeChild( this.element ); 3178 // do regular TapListener destroy 3179 TapListener.prototype.destroy.call( this ); 3180 // click events from keyboard 3181 this.element.removeEventListener( 'click', this ); 3182}; 3183 3184PrevNextButton.prototype.createSVG = function() { 3185 var svg = document.createElementNS( svgURI, 'svg'); 3186 svg.setAttribute( 'viewBox', '0 0 100 100' ); 3187 var path = document.createElementNS( svgURI, 'path'); 3188 var pathMovements = getArrowMovements( this.parent.options.arrowShape ); 3189 path.setAttribute( 'd', pathMovements ); 3190 path.setAttribute( 'class', 'arrow' ); 3191 // rotate arrow 3192 if ( !this.isLeft ) { 3193 path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' ); 3194 } 3195 svg.appendChild( path ); 3196 return svg; 3197}; 3198 3199// get SVG path movmement 3200function getArrowMovements( shape ) { 3201 // use shape as movement if string 3202 if ( typeof shape == 'string' ) { 3203 return shape; 3204 } 3205 // create movement string 3206 return 'M ' + shape.x0 + ',50' + 3207 ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) + 3208 ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) + 3209 ' L ' + shape.x3 + ',50 ' + 3210 ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) + 3211 ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) + 3212 ' Z'; 3213} 3214 3215PrevNextButton.prototype.onTap = function() { 3216 if ( !this.isEnabled ) { 3217 return; 3218 } 3219 this.parent.uiChange(); 3220 var method = this.isPrevious ? 'previous' : 'next'; 3221 this.parent[ method ](); 3222}; 3223 3224PrevNextButton.prototype.handleEvent = utils.handleEvent; 3225 3226PrevNextButton.prototype.onclick = function() { 3227 // only allow clicks from keyboard 3228 var focused = document.activeElement;
3229 if ( focused && focused == this.element ) { 3230 this.onTap(); 3231 } 3232}; 3233 3234// ----- ----- // 3235 3236PrevNextButton.prototype.enable = function() { 3237 if ( this.isEnabled ) { 3238 return; 3239 } 3240 this.element.disabled = false; 3241 this.isEnabled = true; 3242}; 3243 3244PrevNextButton.prototype.disable = function() { 3245 if ( !this.isEnabled ) { 3246 return; 3247 } 3248 this.element.disabled = true; 3249 this.isEnabled = false; 3250}; 3251 3252PrevNextButton.prototype.update = function() { 3253 // index of first or last slide, if previous or next 3254 var slides = this.parent.slides; 3255 // enable is wrapAround and at least 2 slides 3256 if ( this.parent.options.wrapAround && slides.length > 1 ) { 3257 this.enable(); 3258 return; 3259 } 3260 var lastIndex = slides.length ? slides.length - 1 : 0; 3261 var boundIndex = this.isPrevious ? 0 : lastIndex; 3262 var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable'; 3263 this[ method ](); 3264}; 3265 3266PrevNextButton.prototype.destroy = function() { 3267 this.deactivate(); 3268}; 3269 3270// -------------------------- Flickity prototype -------------------------- // 3271 3272utils.extend( Flickity.defaults, { 3273 prevNextButtons: true, 3274 arrowShape: { 3275 x0: 10, 3276 x1: 60, y1: 50, 3277 x2: 70, y2: 40, 3278 x3: 30 3279 } 3280}); 3281 3282Flickity.createMethods.push('_createPrevNextButtons'); 3283var proto = Flickity.prototype; 3284 3285proto._createPrevNextButtons = function() { 3286 if ( !this.options.prevNextButtons ) { 3287 return; 3288 } 3289 3290 this.prevButton = new PrevNextButton( -1, this ); 3291 this.nextButton = new PrevNextButton( 1, this ); 3292 3293 this.on( 'activate', this.activatePrevNextButtons ); 3294}; 3295 3296proto.activatePrevNextButtons = function() { 3297 this.prevButton.activate(); 3298 this.nextButton.activate(); 3299 this.on( 'deactivate', this.deactivatePrevNextButtons ); 3300}; 3301 3302proto.deactivatePrevNextButtons = function() { 3303 this.prevButton.deactivate(); 3304 this.nextButton.deactivate(); 3305 this.off( 'deactivate', this.deactivatePrevNextButtons ); 3306}; 3307 3308// -------------------------- -------------------------- // 3309 3310Flickity.PrevNextButton = PrevNextButton; 3311 3312return Flickity; 3313 3314})); 3315 3316// page dots 3317( function( window, factory ) { 3318 // universal module definition 3319 /* jshint strict: false */ 3320 if ( typeof define == 'function' && define.amd ) { 3321 // AMD 3322 define( 'flickity/js/page-dots',[ 3323 './flickity', 3324 'tap-listener/tap-listener', 3325 'fizzy-ui-utils/utils' 3326 ], function( Flickity, TapListener, utils ) { 3327 return factory( window, Flickity, TapListener, utils ); 3328 }); 3329 } else if ( typeof module == 'object' && module.exports ) { 3330 // CommonJS 3331 module.exports = factory( 3332 window, 3333 require('./flickity'), 3334 require('tap-listener'), 3335 require('fizzy-ui-utils') 3336 ); 3337 } else { 3338 // browser global 3339 factory( 3340 window, 3341 window.Flickity, 3342 window.TapListener, 3343 window.fizzyUIUtils 3344 ); 3345 } 3346 3347}( window, function factory( window, Flickity, TapListener, utils ) { 3348 3349// -------------------------- PageDots -------------------------- // 3350 3351 3352 3353function PageDots( parent ) { 3354 this.parent = parent; 3355 this._create(); 3356} 3357 3358PageDots.prototype = new TapListener(); 3359 3360PageDots.prototype._create = function() { 3361 // create holder element 3362 this.holder = document.createElement('ol'); 3363 this.holder.className = 'flickity-page-dots'; 3364 // create dots, array of elements 3365 this.dots = []; 3366 // events 3367 this.on( 'tap', this.onTap ); 3368 this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) ); 3369}; 3370 3371PageDots.prototype.activate = function() { 3372 this.setDots(); 3373 this.bindTap( this.holder ); 3374 // add to DOM 3375 this.parent.element.appendChild( this.holder ); 3376}; 3377 3378PageDots.prototype.deactivate = function() { 3379 // remove from DOM 3380 this.parent.element.removeChild( this.holder ); 3381 TapListener.prototype.destroy.call( this ); 3382}; 3383 3384PageDots.prototype.setDots = function() { 3385 // get difference between number of slides and number of dots 3386 var delta = this.parent.slides.length - this.dots.length; 3387 if ( delta > 0 ) { 3388 this.addDots( delta ); 3389 } else if ( delta < 0 ) { 3390 this.removeDots( -delta ); 3391 } 3392}; 3393 3394PageDots.prototype.addDots = function( count ) { 3395 var fragment = document.createDocumentFragment(); 3396 var newDots = []; 3397 while ( count ) { 3398 var dot = document.createElement('li'); 3399 dot.className = 'dot'; 3400 fragment.appendChild( dot ); 3401 newDots.push( dot ); 3402 count--; 3403 } 3404 this.holder.appendChild( fragment ); 3405 this.dots = this.dots.concat( newDots ); 3406}; 3407 3408PageDots.prototype.removeDots = function( count ) { 3409 // remove from this.dots collection 3410 var removeDots = this.dots.splice( this.dots.length - count, count ); 3411 // remove from DOM
3412 removeDots.forEach( function( dot ) { 3413 this.holder.removeChild( dot ); 3414 }, this ); 3415}; 3416 3417PageDots.prototype.updateSelected = function() { 3418 // remove selected class on previous 3419 if ( this.selectedDot ) { 3420 this.selectedDot.className = 'dot'; 3421 } 3422 // don't proceed if no dots 3423 if ( !this.dots.length ) { 3424 return; 3425 } 3426 this.selectedDot = this.dots[ this.parent.selectedIndex ]; 3427 this.selectedDot.className = 'dot is-selected'; 3428}; 3429 3430PageDots.prototype.onTap = function( event ) { 3431 var target = event.target; 3432 // only care about dot clicks 3433 if ( target.nodeName != 'LI' ) { 3434 return; 3435 } 3436 3437 this.parent.uiChange(); 3438 var index = this.dots.indexOf( target ); 3439 this.parent.select( index ); 3440}; 3441 3442PageDots.prototype.destroy = function() { 3443 this.deactivate(); 3444}; 3445 3446Flickity.PageDots = PageDots; 3447 3448// -------------------------- Flickity -------------------------- // 3449 3450utils.extend( Flickity.defaults, { 3451 pageDots: true 3452}); 3453 3454Flickity.createMethods.push('_createPageDots'); 3455 3456var proto = Flickity.prototype; 3457 3458proto._createPageDots = function() { 3459 if ( !this.options.pageDots ) { 3460 return; 3461 } 3462 this.pageDots = new PageDots( this ); 3463 // events 3464 this.on( 'activate', this.activatePageDots ); 3465 this.on( 'select', this.updateSelectedPageDots ); 3466 this.on( 'cellChange', this.updatePageDots ); 3467 this.on( 'resize', this.updatePageDots ); 3468 this.on( 'deactivate', this.deactivatePageDots ); 3469}; 3470 3471proto.activatePageDots = function() { 3472 this.pageDots.activate(); 3473}; 3474 3475proto.updateSelectedPageDots = function() { 3476 this.pageDots.updateSelected(); 3477}; 3478 3479proto.updatePageDots = function() { 3480 this.pageDots.setDots(); 3481}; 3482 3483proto.deactivatePageDots = function() { 3484 this.pageDots.deactivate(); 3485}; 3486 3487// ----- ----- // 3488 3489Flickity.PageDots = PageDots; 3490 3491return Flickity; 3492 3493})); 3494 3495// player & autoPlay 3496( function( window, factory ) { 3497 // universal module definition 3498 /* jshint strict: false */ 3499 if ( typeof define == 'function' && define.amd ) { 3500 // AMD 3501 define( 'flickity/js/player',[ 3502 'ev-emitter/ev-emitter', 3503 'fizzy-ui-utils/utils', 3504 './flickity' 3505 ], function( EvEmitter, utils, Flickity ) { 3506 return factory( EvEmitter, utils, Flickity ); 3507 }); 3508 } else if ( typeof module == 'object' && module.exports ) { 3509 // CommonJS 3510 module.exports = factory( 3511 require('ev-emitter'), 3512 require('fizzy-ui-utils'), 3513 require('./flickity') 3514 ); 3515 } else { 3516 // browser global 3517 factory( 3518 window.EvEmitter, 3519 window.fizzyUIUtils, 3520 window.Flickity 3521 ); 3522 } 3523 3524}( window, function factory( EvEmitter, utils, Flickity ) { 3525 3526 3527 3528// -------------------------- Page Visibility -------------------------- // 3529// https://developer.mozilla.org/en-US/docs/Web/Guide/User_experience/Using_the_Page_Visibility_API 3530 3531var hiddenProperty, visibilityEvent; 3532if ( 'hidden' in document ) { 3533 hiddenProperty = 'hidden'; 3534 visibilityEvent = 'visibilitychange'; 3535} else if ( 'webkitHidden' in document ) { 3536 hiddenProperty = 'webkitHidden'; 3537 visibilityEvent = 'webkitvisibilitychange'; 3538} 3539 3540// -------------------------- Player -------------------------- // 3541 3542function Player( parent ) { 3543 this.parent = parent; 3544 this.state = 'stopped'; 3545 // visibility change event handler 3546 if ( visibilityEvent ) { 3547 this.onVisibilityChange = function() { 3548 this.visibilityChange(); 3549 }.bind( this ); 3550 this.onVisibilityPlay = function() { 3551 this.visibilityPlay(); 3552 }.bind( this ); 3553 } 3554} 3555 3556Player.prototype = Object.create( EvEmitter.prototype ); 3557 3558// start play 3559Player.prototype.play = function() { 3560 if ( this.state == 'playing' ) { 3561 return; 3562 } 3563 // do not play if page is hidden, start playing when page is visible 3564 var isPageHidden = document[ hiddenProperty ]; 3565 if ( visibilityEvent && isPageHidden ) { 3566 document.addEventListener( visibilityEvent, this.onVisibilityPlay ); 3567 return; 3568 } 3569 3570 this.state = 'playing'; 3571 // listen to visibility change 3572 if ( visibilityEvent ) { 3573 document.addEventListener( visibilityEvent, this.onVisibilityChange ); 3574 } 3575 // start ticking 3576 this.tick(); 3577}; 3578 3579Player.prototype.tick = function() { 3580 // do not tick if not playing 3581 if ( this.state != 'playing' ) { 3582 return; 3583 } 3584 3585 var time = this.parent.options.autoPlay; 3586 // default to 3 seconds 3587 time = typeof time == 'number' ? time : 3000; 3588 var _this = this; 3589 // HACK: reset ticks if stopped and started within interval 3590 this.clear(); 3591 this.timeout = setTimeout( function() { 3592 _this.parent.next( true ); 3593 _this.tick(); 3594 }, time ); 3595}; 3596 3597Player.prototype.stop = function() { 3598 this.state = 'stopped'; 3599 this.clear(); 3600 // remove visibility change event 3601 if ( visibilityEvent ) { 3602 document.removeEventListener( visibilityEvent, this.onVisibilityChange ); 3603 } 3604}; 3605 3606Player.prototype.clear = function() { 3607 clearTimeout( this.timeout ); 3608}; 3609 3610Player.prototype.pause = function() { 3611 if ( this.state == 'playing' ) { 3612 this.state = 'paused'; 3613 this.clear(); 3614 } 3615}; 3616 3617Player.prototype.unpause = function() { 3618 // re-start play if paused 3619 if ( this.state == 'paused' ) { 3620 this.play(); 3621 } 3622}; 3623 3624// pause if page visibility is hidden, unpause if visible 3625Player.prototype.visibilityChange = function() { 3626 var isPageHidden = document[ hiddenProperty ]; 3627 this[ isPageHidden ? 'pause' : 'unpause' ](); 3628}; 3629 3630Player.prototype.visibilityPlay = function() { 3631 this.play(); 3632 document.removeEventListener( visibilityEvent, this.onVisibilityPlay ); 3633}; 3634 3635// -------------------------- Flickity -------------------------- // 3636 3637utils.extend( Flickity.defaults, { 3638 pauseAutoPlayOnHover: true 3639}); 3640 3641Flickity.createMethods.push('_createPlayer'); 3642var proto = Flickity.prototype; 3643 3644proto._createPlayer = function() { 3645 this.player = new Player( this ); 3646 3647 this.on( 'activate', this.activatePlayer ); 3648 this.on( 'uiChange', this.stopPlayer ); 3649 this.on( 'pointerDown', this.stopPlayer ); 3650 this.on( 'deactivate', this.deactivatePlayer ); 3651}; 3652 3653proto.activatePlayer = function() { 3654 if ( !this.options.autoPlay ) { 3655 return; 3656 } 3657 this.player.play(); 3658 this.element.addEventListener( 'mouseenter', this ); 3659}; 3660 3661// Player API, don't hate the ... thanks I know where the door is 3662 3663proto.playPlayer = function() { 3664 this.player.play(); 3665}; 3666 3667proto.stopPlayer = function() { 3668 this.player.stop(); 3669}; 3670 3671proto.pausePlayer = function() { 3672 this.player.pause(); 3673}; 3674 3675proto.unpausePlayer = function() { 3676 this.player.unpause(); 3677}; 3678 3679proto.deactivatePlayer = function() { 3680 this.player.stop(); 3681 this.element.removeEventListener( 'mouseenter', this ); 3682}; 3683 3684// ----- mouseenter/leave ----- // 3685 3686// pause auto-play on hover 3687proto.onmouseenter = function() { 3688 if ( !this.options.pauseAutoPlayOnHover ) { 3689 return; 3690 } 3691 this.player.pause(); 3692 this.element.addEventListener( 'mouseleave', this ); 3693}; 3694 3695// resume auto-play on hover off 3696proto.onmouseleave = function() { 3697 this.player.unpause(); 3698 this.element.removeEventListener( 'mouseleave', this ); 3699}; 3700 3701// ----- ----- // 3702 3703Flickity.Player = Player; 3704 3705return Flickity; 3706 3707})); 3708 3709// add, remove cell 3710( function( window, factory ) { 3711 // universal module definition 3712 /* jshint strict: false */ 3713 if ( typeof define == 'function' && define.amd ) { 3714 // AMD 3715 define( 'flickity/js/add-remove-cell',[ 3716 './flickity', 3717 'fizzy-ui-utils/utils' 3718 ], function( Flickity, utils ) { 3719 return factory( window, Flickity, utils ); 3720 }); 3721 } else if ( typeof module == 'object' && module.exports ) { 3722 // CommonJS 3723 module.exports = factory( 3724 window, 3725 require('./flickity'), 3726 require('fizzy-ui-utils') 3727 ); 3728 } else { 3729 // browser global 3730 factory( 3731 window, 3732 window.Flickity, 3733 window.fizzyUIUtils 3734 ); 3735 } 3736 3737}( window, function factory( window, Flickity, utils ) { 3738 3739 3740 3741// append cells to a document fragment 3742function getCellsFragment( cells ) { 3743 var fragment = document.createDocumentFragment();
3744 cells.forEach( function( cell ) { 3745 fragment.appendChild( cell.element ); 3746 }); 3747 return fragment; 3748} 3749 3750// -------------------------- add/remove cell prototype -------------------------- // 3751 3752var proto = Flickity.prototype; 3753 3754/** 3755 * Insert, prepend, or append cells 3756 * @param {Element, Array, NodeList} elems 3757 * @param {Integer} index 3758 */ 3759proto.insert = function( elems, index ) { 3760 var cells = this._makeCells( elems ); 3761 if ( !cells || !cells.length ) { 3762 return; 3763 } 3764 var len = this.cells.length; 3765 // default to append 3766 index = index === undefined ? len : index; 3767 // add cells with document fragment 3768 var fragment = getCellsFragment( cells ); 3769 // append to slider 3770 var isAppend = index == len; 3771 if ( isAppend ) { 3772 this.slider.appendChild( fragment ); 3773 } else { 3774 var insertCellElement = this.cells[ index ].element; 3775 this.slider.insertBefore( fragment, insertCellElement ); 3776 } 3777 // add to this.cells 3778 if ( index === 0 ) { 3779 // prepend, add to start 3780 this.cells = cells.concat( this.cells ); 3781 } else if ( isAppend ) { 3782 // append, add to end 3783 this.cells = this.cells.concat( cells ); 3784 } else { 3785 // insert in this.cells 3786 var endCells = this.cells.splice( index, len - index ); 3787 this.cells = this.cells.concat( cells ).concat( endCells ); 3788 } 3789 3790 this._sizeCells( cells ); 3791 3792 var selectedIndexDelta = index > this.selectedIndex ? 0 : cells.length; 3793 this._cellAddedRemoved( index, selectedIndexDelta ); 3794}; 3795 3796proto.append = function( elems ) { 3797 this.insert( elems, this.cells.length ); 3798}; 3799 3800proto.prepend = function( elems ) { 3801 this.insert( elems, 0 ); 3802}; 3803 3804/** 3805 * Remove cells 3806 * @param {Element, Array, NodeList} elems 3807 */ 3808proto.remove = function( elems ) { 3809 var cells = this.getCells( elems ); 3810 var selectedIndexDelta = 0; 3811 var len = cells.length; 3812 var i, cell; 3813 // calculate selectedIndexDelta, easier if done in seperate loop 3814 for ( i=0; i < len; i++ ) { 3815 cell = cells[i]; 3816 var wasBefore = this.cells.indexOf( cell ) < this.selectedIndex; 3817 selectedIndexDelta -= wasBefore ? 1 : 0; 3818 } 3819 3820 for ( i=0; i < len; i++ ) { 3821 cell = cells[i]; 3822 cell.remove(); 3823 // remove item from collection 3824 utils.removeFrom( this.cells, cell ); 3825 } 3826 3827 if ( cells.length ) { 3828 // update stuff 3829 this._cellAddedRemoved( 0, selectedIndexDelta ); 3830 } 3831}; 3832 3833// updates when cells are added or removed 3834proto._cellAddedRemoved = function( changedCellIndex, selectedIndexDelta ) { 3835 // TODO this math isn't perfect with grouped slides 3836 selectedIndexDelta = selectedIndexDelta || 0; 3837 this.selectedIndex += selectedIndexDelta; 3838 this.selectedIndex = Math.max( 0, Math.min( this.slides.length - 1, this.selectedIndex ) ); 3839 3840 this.cellChange( changedCellIndex, true ); 3841 // backwards compatibility 3842 this.emitEvent( 'cellAddedRemoved', [ changedCellIndex, selectedIndexDelta ] ); 3843}; 3844 3845/** 3846 * logic to be run after a cell's size changes 3847 * @param {Element} elem - cell's element 3848 */ 3849proto.cellSizeChange = function( elem ) { 3850 var cell = this.getCell( elem ); 3851 if ( !cell ) { 3852 return; 3853 } 3854 cell.getSize(); 3855 3856 var index = this.cells.indexOf( cell ); 3857 this.cellChange( index ); 3858}; 3859 3860/** 3861 * logic any time a cell is changed: added, removed, or size changed 3862 * @param {Integer} changedCellIndex - index of the changed cell, optional 3863 */ 3864proto.cellChange = function( changedCellIndex, isPositioningSlider ) { 3865 var prevSlideableWidth = this.slideableWidth; 3866 this._positionCells( changedCellIndex ); 3867 this._getWrapShiftCells(); 3868 this.setGallerySize(); 3869 this.emitEvent( 'cellChange', [ changedCellIndex ] ); 3870 // position slider 3871 if ( this.options.freeScroll ) { 3872 // shift x by change in slideableWidth 3873 // TODO fix position shifts when prepending w/ freeScroll 3874 var deltaX = prevSlideableWidth - this.slideableWidth; 3875 this.x += deltaX * this.cellAlign; 3876 this.positionSlider(); 3877 } else { 3878 // do not position slider after lazy load 3879 if ( isPositioningSlider ) { 3880 this.positionSliderAtSelected(); 3881 } 3882 this.select( this.selectedIndex ); 3883 } 3884}; 3885 3886// ----- ----- // 3887 3888return Flickity; 3889 3890})); 3891 3892// lazyload 3893( function( window, factory ) { 3894 // universal module definition 3895 /* jshint strict: false */ 3896 if ( typeof define == 'function' && define.amd ) { 3897 // AMD 3898 define( 'flickity/js/lazyload',[ 3899 './flickity', 3900 'fizzy-ui-utils/utils' 3901 ], function( Flickity, utils ) { 3902 return factory( window, Flickity, utils ); 3903 }); 3904 } else if ( typeof module == 'object' && module.exports ) { 3905 // CommonJS 3906 module.exports = factory( 3907 window, 3908 require('./flickity'), 3909 require('fizzy-ui-utils') 3910 ); 3911 } else { 3912 // browser global 3913 factory( 3914 window, 3915 window.Flickity, 3916 window.fizzyUIUtils 3917 ); 3918 } 3919 3920}( window, function factory( window, Flickity, utils ) { 3921'use strict'; 3922 3923Flickity.createMethods.push('_createLazyload'); 3924var proto = Flickity.prototype; 3925 3926proto._createLazyload = function() { 3927 this.on( 'select', this.lazyLoad ); 3928}; 3929 3930proto.lazyLoad = function() { 3931 var lazyLoad = this.options.lazyLoad; 3932 if ( !lazyLoad ) { 3933 return; 3934 } 3935 // get adjacent cells, use lazyLoad option for adjacent count 3936 var adjCount = typeof lazyLoad == 'number' ? lazyLoad : 0; 3937 var cellElems = this.getAdjacentCellElements( adjCount ); 3938 // get lazy images in those cells 3939 var lazyImages = [];
3940 cellElems.forEach( function( cellElem ) { 3941 var lazyCellImages = getCellLazyImages( cellElem ); 3942 lazyImages = lazyImages.concat( lazyCellImages ); 3943 }); 3944 // load lazy images 3945 lazyImages.forEach( function( img ) { 3946 new LazyLoader( img, this ); 3947 }, this ); 3948}; 3949 3950function getCellLazyImages( cellElem ) { 3951 // check if cell element is lazy image 3952 if ( cellElem.nodeName == 'IMG' && 3953 cellElem.getAttribute('data-flickity-lazyload') ) { 3954 return [ cellElem ]; 3955 } 3956 // select lazy images in cell 3957 var imgs = cellElem.querySelectorAll('img[data-flickity-lazyload]'); 3958 return utils.makeArray( imgs ); 3959} 3960 3961// -------------------------- LazyLoader -------------------------- // 3962 3963/** 3964 * class to handle loading images 3965 */ 3966function LazyLoader( img, flickity ) { 3967 this.img = img; 3968 this.flickity = flickity; 3969 this.load(); 3970} 3971 3972LazyLoader.prototype.handleEvent = utils.handleEvent; 3973 3974LazyLoader.prototype.load = function() { 3975 this.img.addEventListener( 'load', this ); 3976 this.img.addEventListener( 'error', this ); 3977 // load image 3978 this.img.src = this.img.getAttribute('data-flickity-lazyload'); 3979 // remove attr 3980 this.img.removeAttribute('data-flickity-lazyload'); 3981}; 3982 3983LazyLoader.prototype.onload = function( event ) { 3984 this.complete( event, 'flickity-lazyloaded' ); 3985}; 3986 3987LazyLoader.prototype.onerror = function( event ) { 3988 this.complete( event, 'flickity-lazyerror' ); 3989}; 3990 3991LazyLoader.prototype.complete = function( event, className ) { 3992 // unbind events 3993 this.img.removeEventListener( 'load', this ); 3994 this.img.removeEventListener( 'error', this ); 3995 3996 var cell = this.flickity.getParentCell( this.img ); 3997 var cellElem = cell && cell.element; 3998 this.flickity.cellSizeChange( cellElem ); 3999 4000 this.img.classList.add( className ); 4001 this.flickity.dispatchEvent( 'lazyLoad', event, cellElem ); 4002}; 4003 4004// ----- ----- // 4005 4006Flickity.LazyLoader = LazyLoader; 4007 4008return Flickity; 4009 4010})); 4011 4012/*! 4013 * Flickity v2.0.5 4014 * Touch, responsive, flickable carousels 4015 * 4016 * Licensed GPLv3 for open source use 4017 * or Flickity Commercial License for commercial use 4018 * 4019 * http://flickity.metafizzy.co 4020 * Copyright 2016 Metafizzy 4021 */ 4022 4023( function( window, factory ) { 4024 // universal module definition 4025 /* jshint strict: false */ 4026 if ( typeof define == 'function' && define.amd ) { 4027 // AMD 4028 define( 'flickity/js/index',[ 4029 './flickity', 4030 './drag', 4031 './prev-next-button', 4032 './page-dots', 4033 './player', 4034 './add-remove-cell', 4035 './lazyload' 4036 ], factory ); 4037 } else if ( typeof module == 'object' && module.exports ) { 4038 // CommonJS 4039 module.exports = factory( 4040 require('./flickity'), 4041 require('./drag'), 4042 require('./prev-next-button'), 4043 require('./page-dots'), 4044 require('./player'), 4045 require('./add-remove-cell'), 4046 require('./lazyload') 4047 ); 4048 } 4049 4050})( window, function factory( Flickity ) { 4051 /*jshint strict: false*/ 4052 return Flickity; 4053}); 4054 4055/*! 4056 * Flickity asNavFor v2.0.1 4057 * enable asNavFor for Flickity 4058 */ 4059 4060/*jshint browser: true, undef: true, unused: true, strict: true*/ 4061 4062( function( window, factory ) { 4063 // universal module definition 4064 /*jshint strict: false */ /*globals define, module, require */ 4065 if ( typeof define == 'function' && define.amd ) { 4066 // AMD 4067 define( 'flickity-as-nav-for/as-nav-for',[ 4068 'flickity/js/index', 4069 'fizzy-ui-utils/utils' 4070 ], factory ); 4071 } else if ( typeof module == 'object' && module.exports ) { 4072 // CommonJS 4073 module.exports = factory( 4074 require('flickity'), 4075 require('fizzy-ui-utils') 4076 ); 4077 } else { 4078 // browser global 4079 window.Flickity = factory( 4080 window.Flickity, 4081 window.fizzyUIUtils 4082 ); 4083 } 4084 4085}( window, function factory( Flickity, utils ) { 4086 4087 4088 4089// -------------------------- asNavFor prototype -------------------------- // 4090 4091// Flickity.defaults.asNavFor = null; 4092 4093Flickity.createMethods.push('_createAsNavFor'); 4094 4095var proto = Flickity.prototype; 4096 4097proto._createAsNavFor = function() { 4098 this.on( 'activate', this.activateAsNavFor ); 4099 this.on( 'deactivate', this.deactivateAsNavFor ); 4100 this.on( 'destroy', this.destroyAsNavFor ); 4101 4102 var asNavForOption = this.options.asNavFor; 4103 if ( !asNavForOption ) { 4104 return; 4105 } 4106 // HACK do async, give time for other flickity to be initalized 4107 var _this = this; 4108 setTimeout( function initNavCompanion() { 4109 _this.setNavCompanion( asNavForOption ); 4110 }); 4111}; 4112 4113proto.setNavCompanion = function( elem ) { 4114 elem = utils.getQueryElement( elem ); 4115 var companion = Flickity.data( elem ); 4116 // stop if no companion or companion is self 4117 if ( !companion || companion == this ) { 4118 return; 4119 } 4120 4121 this.navCompanion = companion; 4122 // companion select 4123 var _this = this; 4124 this.onNavCompanionSelect = function() { 4125 _this.navCompanionSelect(); 4126 }; 4127 companion.on( 'select', this.onNavCompanionSelect ); 4128 // click 4129 this.on( 'staticClick', this.onNavStaticClick ); 4130 4131 this.navCompanionSelect( true ); 4132}; 4133 4134proto.navCompanionSelect = function( isInstant ) { 4135 if ( !this.navCompanion ) { 4136 return; 4137 } 4138 // select slide that matches first cell of slide 4139 var selectedCell = this.navCompanion.selectedCells[0]; 4140 var firstIndex = this.navCompanion.cells.indexOf( selectedCell ); 4141 var lastIndex = firstIndex + this.navCompanion.selectedCells.length - 1; 4142 var selectIndex = Math.floor( lerp( firstIndex, lastIndex, 4143 this.navCompanion.cellAlign ) ); 4144 this.selectCell( selectIndex, false, isInstant ); 4145 // set nav selected class 4146 this.removeNavSelectedElements();
4147 // stop if companion has more cells than this one 4148 if ( selectIndex >= this.cells.length ) { 4149 return; 4150 } 4151 4152 var selectedCells = this.cells.slice( firstIndex, lastIndex + 1 ); 4153 this.navSelectedElements = selectedCells.map( function( cell ) { 4154 return cell.element; 4155 }); 4156 this.changeNavSelectedClass('add'); 4157}; 4158 4159function lerp( a, b, t ) { 4160 return ( b - a ) * t + a; 4161} 4162 4163proto.changeNavSelectedClass = function( method ) { 4164 this.navSelectedElements.forEach( function( navElem ) { 4165 navElem.classList[ method ]('is-nav-selected'); 4166 }); 4167}; 4168 4169proto.activateAsNavFor = function() { 4170 this.navCompanionSelect( true ); 4171}; 4172 4173proto.removeNavSelectedElements = function() { 4174 if ( !this.navSelectedElements ) { 4175 return; 4176 } 4177 this.changeNavSelectedClass('remove'); 4178 delete this.navSelectedElements; 4179}; 4180 4181proto.onNavStaticClick = function( event, pointer, cellElement, cellIndex ) { 4182 if ( typeof cellIndex == 'number' ) { 4183 this.navCompanion.selectCell( cellIndex ); 4184 } 4185}; 4186 4187proto.deactivateAsNavFor = function() { 4188 this.removeNavSelectedElements(); 4189}; 4190 4191proto.destroyAsNavFor = function() { 4192 if ( !this.navCompanion ) { 4193 return; 4194 } 4195 this.navCompanion.off( 'select', this.onNavCompanionSelect ); 4196 this.off( 'staticClick', this.onNavStaticClick ); 4197 delete this.navCompanion; 4198}; 4199 4200// ----- ----- // 4201 4202return Flickity; 4203 4204})); 4205 4206/*! 4207 * imagesLoaded v4.1.1 4208 * JavaScript is all like "You images are done yet or what?" 4209 * MIT License 4210 */ 4211 4212( function( window, factory ) { 'use strict'; 4213 // universal module definition 4214 4215 /*global define: false, module: false, require: false */ 4216 4217 if ( typeof define == 'function' && define.amd ) { 4218 // AMD 4219 define( 'imagesloaded/imagesloaded',[ 4220 'ev-emitter/ev-emitter' 4221 ], function( EvEmitter ) { 4222 return factory( window, EvEmitter ); 4223 }); 4224 } else if ( typeof module == 'object' && module.exports ) { 4225 // CommonJS 4226 module.exports = factory( 4227 window, 4228 require('ev-emitter') 4229 ); 4230 } else { 4231 // browser global 4232 window.imagesLoaded = factory( 4233 window, 4234 window.EvEmitter 4235 ); 4236 } 4237 4238})( window, 4239 4240// -------------------------- factory -------------------------- // 4241 4242function factory( window, EvEmitter ) { 4243 4244 4245 4246var $ = window.jQuery; 4247var console = window.console; 4248 4249// -------------------------- helpers -------------------------- // 4250 4251// extend objects 4252function extend( a, b ) { 4253 for ( var prop in b ) { 4254 a[ prop ] = b[ prop ]; 4255 } 4256 return a; 4257} 4258 4259// turn element or nodeList into an array 4260function makeArray( obj ) { 4261 var ary = []; 4262 if ( Array.isArray( obj ) ) { 4263 // use object if already an array 4264 ary = obj; 4265 } else if ( typeof obj.length == 'number' ) { 4266 // convert nodeList to array 4267 for ( var i=0; i < obj.length; i++ ) { 4268 ary.push( obj[i] ); 4269 } 4270 } else { 4271 // array of single index 4272 ary.push( obj ); 4273 } 4274 return ary; 4275} 4276 4277// -------------------------- imagesLoaded -------------------------- // 4278 4279/** 4280 * @param {Array, Element, NodeList, String} elem 4281 * @param {Object or Function} options - if function, use as callback 4282 * @param {Function} onAlways - callback function 4283 */ 4284function ImagesLoaded( elem, options, onAlways ) { 4285 // coerce ImagesLoaded() without new, to be new ImagesLoaded() 4286 if ( !( this instanceof ImagesLoaded ) ) { 4287 return new ImagesLoaded( elem, options, onAlways ); 4288 } 4289 // use elem as selector string 4290 if ( typeof elem == 'string' ) { 4291 elem = document.querySelectorAll( elem ); 4292 } 4293 4294 this.elements = makeArray( elem ); 4295 this.options = extend( {}, this.options ); 4296 4297 if ( typeof options == 'function' ) { 4298 onAlways = options; 4299 } else { 4300 extend( this.options, options ); 4301 } 4302 4303 if ( onAlways ) { 4304 this.on( 'always', onAlways ); 4305 } 4306 4307 this.getImages(); 4308 4309 if ( $ ) { 4310 // add jQuery Deferred object 4311 this.jqDeferred = new $.Deferred(); 4312 } 4313 4314 // HACK check async to allow time to bind listeners 4315 setTimeout( function() { 4316 this.check(); 4317 }.bind( this )); 4318} 4319 4320ImagesLoaded.prototype = Object.create( EvEmitter.prototype ); 4321 4322ImagesLoaded.prototype.options = {}; 4323 4324ImagesLoaded.prototype.getImages = function() { 4325 this.images = []; 4326 4327 // filter & find items if we have an item selector
vendor: 4,347 bytes, lines 4328-4484
4328 this.elements.forEach( this.addElementImages, this ); 4329}; 4330 4331/** 4332 * @param {Node} element 4333 */ 4334ImagesLoaded.prototype.addElementImages = function( elem ) { 4335 // filter siblings 4336 if ( elem.nodeName == 'IMG' ) { 4337 this.addImage( elem ); 4338 } 4339 // get background image on element 4340 if ( this.options.background === true ) { 4341 this.addElementBackgroundImages( elem ); 4342 } 4343 4344 // find children 4345 // no non-element nodes, #143 4346 var nodeType = elem.nodeType; 4347 if ( !nodeType || !elementNodeTypes[ nodeType ] ) { 4348 return; 4349 } 4350 var childImgs = elem.querySelectorAll('img'); 4351 // concat childElems to filterFound array 4352 for ( var i=0; i < childImgs.length; i++ ) { 4353 var img = childImgs[i]; 4354 this.addImage( img ); 4355 } 4356 4357 // get child background images 4358 if ( typeof this.options.background == 'string' ) { 4359 var children = elem.querySelectorAll( this.options.background ); 4360 for ( i=0; i < children.length; i++ ) { 4361 var child = children[i]; 4362 this.addElementBackgroundImages( child ); 4363 } 4364 } 4365}; 4366 4367var elementNodeTypes = { 4368 1: true, 4369 9: true, 4370 11: true 4371}; 4372 4373ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) { 4374 var style = getComputedStyle( elem ); 4375 if ( !style ) { 4376 // Firefox returns null if in a hidden iframe https://bugzil.la/548397 4377 return; 4378 } 4379 // get url inside url("...") 4380 var reURL = /url\((['"])?(.*?)\1\)/gi; 4381 var matches = reURL.exec( style.backgroundImage ); 4382 while ( matches !== null ) { 4383 var url = matches && matches[2]; 4384 if ( url ) { 4385 this.addBackground( url, elem ); 4386 } 4387 matches = reURL.exec( style.backgroundImage ); 4388 } 4389}; 4390 4391/** 4392 * @param {Image} img 4393 */ 4394ImagesLoaded.prototype.addImage = function( img ) { 4395 var loadingImage = new LoadingImage( img ); 4396 this.images.push( loadingImage ); 4397}; 4398 4399ImagesLoaded.prototype.addBackground = function( url, elem ) { 4400 var background = new Background( url, elem ); 4401 this.images.push( background ); 4402}; 4403 4404ImagesLoaded.prototype.check = function() { 4405 var _this = this; 4406 this.progressedCount = 0; 4407 this.hasAnyBroken = false; 4408 // complete if no images 4409 if ( !this.images.length ) { 4410 this.complete(); 4411 return; 4412 } 4413 4414 function onProgress( image, elem, message ) { 4415 // HACK - Chrome triggers event before object properties have changed. #83 4416 setTimeout( function() { 4417 _this.progress( image, elem, message ); 4418 }); 4419 } 4420 4421 this.images.forEach( function( loadingImage ) { 4422 loadingImage.once( 'progress', onProgress ); 4423 loadingImage.check(); 4424 }); 4425}; 4426 4427ImagesLoaded.prototype.progress = function( image, elem, message ) { 4428 this.progressedCount++; 4429 this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; 4430 // progress event 4431 this.emitEvent( 'progress', [ this, image, elem ] ); 4432 if ( this.jqDeferred && this.jqDeferred.notify ) { 4433 this.jqDeferred.notify( this, image ); 4434 } 4435 // check if completed 4436 if ( this.progressedCount == this.images.length ) { 4437 this.complete(); 4438 } 4439 4440 if ( this.options.debug && console ) { 4441 console.log( 'progress: ' + message, image, elem ); 4442 } 4443}; 4444 4445ImagesLoaded.prototype.complete = function() { 4446 var eventName = this.hasAnyBroken ? 'fail' : 'done'; 4447 this.isComplete = true; 4448 this.emitEvent( eventName, [ this ] ); 4449 this.emitEvent( 'always', [ this ] ); 4450 if ( this.jqDeferred ) { 4451 var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; 4452 this.jqDeferred[ jqMethod ]( this ); 4453 } 4454}; 4455 4456// -------------------------- -------------------------- // 4457 4458function LoadingImage( img ) { 4459 this.img = img; 4460} 4461 4462LoadingImage.prototype = Object.create( EvEmitter.prototype ); 4463 4464LoadingImage.prototype.check = function() { 4465 // If complete is true and browser supports natural sizes, 4466 // try to check for image status manually. 4467 var isComplete = this.getIsImageComplete(); 4468 if ( isComplete ) { 4469 // report based on naturalWidth 4470 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 4471 return; 4472 } 4473 4474 // If none of the checks above matched, simulate loading on detached element. 4475 this.proxyImage = new Image(); 4476 this.proxyImage.addEventListener( 'load', this ); 4477 this.proxyImage.addEventListener( 'error', this ); 4478 // bind to image as well for Firefox. #191 4479 this.img.addEventListener( 'load', this ); 4480 this.img.addEventListener( 'error', this ); 4481 this.proxyImage.src = this.img.src; 4482}; 4483 4484LoadingImage.prototype.getIsImageComplete = function() {
4485 return this.img.complete && this.img.naturalWidth !== undefined; 4486}; 4487 4488LoadingImage.prototype.confirm = function( isLoaded, message ) { 4489 this.isLoaded = isLoaded; 4490 this.emitEvent( 'progress', [ this, this.img, message ] ); 4491}; 4492 4493// ----- events ----- // 4494 4495// trigger specified handler for event type 4496LoadingImage.prototype.handleEvent = function( event ) { 4497 var method = 'on' + event.type; 4498 if ( this[ method ] ) { 4499 this[ method ]( event ); 4500 } 4501}; 4502 4503LoadingImage.prototype.onload = function() { 4504 this.confirm( true, 'onload' ); 4505 this.unbindEvents(); 4506}; 4507 4508LoadingImage.prototype.onerror = function() { 4509 this.confirm( false, 'onerror' ); 4510 this.unbindEvents(); 4511}; 4512 4513LoadingImage.prototype.unbindEvents = function() { 4514 this.proxyImage.removeEventListener( 'load', this ); 4515 this.proxyImage.removeEventListener( 'error', this ); 4516 this.img.removeEventListener( 'load', this ); 4517 this.img.removeEventListener( 'error', this ); 4518}; 4519 4520// -------------------------- Background -------------------------- // 4521 4522function Background( url, element ) { 4523 this.url = url; 4524 this.element = element; 4525 this.img = new Image(); 4526} 4527 4528// inherit LoadingImage prototype 4529Background.prototype = Object.create( LoadingImage.prototype ); 4530 4531Background.prototype.check = function() { 4532 this.img.addEventListener( 'load', this ); 4533 this.img.addEventListener( 'error', this ); 4534 this.img.src = this.url; 4535 // check if image is already complete 4536 var isComplete = this.getIsImageComplete(); 4537 if ( isComplete ) { 4538 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 4539 this.unbindEvents(); 4540 } 4541}; 4542 4543Background.prototype.unbindEvents = function() { 4544 this.img.removeEventListener( 'load', this ); 4545 this.img.removeEventListener( 'error', this ); 4546}; 4547 4548Background.prototype.confirm = function( isLoaded, message ) { 4549 this.isLoaded = isLoaded; 4550 this.emitEvent( 'progress', [ this, this.element, message ] ); 4551}; 4552 4553// -------------------------- jQuery -------------------------- // 4554 4555ImagesLoaded.makeJQueryPlugin = function( jQuery ) { 4556 jQuery = jQuery || window.jQuery; 4557 if ( !jQuery ) { 4558 return; 4559 } 4560 // set local variable 4561 $ = jQuery; 4562 // $().imagesLoaded() 4563 $.fn.imagesLoaded = function( options, callback ) { 4564 var instance = new ImagesLoaded( this, options, callback ); 4565 return instance.jqDeferred.promise( $(this) ); 4566 }; 4567}; 4568// try making plugin 4569ImagesLoaded.makeJQueryPlugin(); 4570 4571// -------------------------- -------------------------- // 4572 4573return ImagesLoaded; 4574 4575}); 4576 4577/*! 4578 * Flickity imagesLoaded v2.0.0 4579 * enables imagesLoaded option for Flickity 4580 */ 4581 4582/*jshint browser: true, strict: true, undef: true, unused: true */ 4583 4584( function( window, factory ) { 4585 // universal module definition 4586 /*jshint strict: false */ /*globals define, module, require */ 4587 if ( typeof define == 'function' && define.amd ) { 4588 // AMD 4589 define( [ 4590 'flickity/js/index', 4591 'imagesloaded/imagesloaded' 4592 ], function( Flickity, imagesLoaded ) { 4593 return factory( window, Flickity, imagesLoaded ); 4594 }); 4595 } else if ( typeof module == 'object' && module.exports ) { 4596 // CommonJS 4597 module.exports = factory( 4598 window, 4599 require('flickity'), 4600 require('imagesloaded') 4601 ); 4602 } else { 4603 // browser global 4604 window.Flickity = factory( 4605 window, 4606 window.Flickity, 4607 window.imagesLoaded 4608 ); 4609 } 4610 4611}( window, function factory( window, Flickity, imagesLoaded ) { 4612'use strict'; 4613 4614Flickity.createMethods.push('_createImagesLoaded'); 4615 4616var proto = Flickity.prototype; 4617 4618proto._createImagesLoaded = function() { 4619 this.on( 'activate', this.imagesLoaded ); 4620}; 4621 4622proto.imagesLoaded = function() { 4623 if ( !this.options.imagesLoaded ) { 4624 return; 4625 } 4626 var _this = this; 4627 function onImagesLoadedProgress( instance, image ) { 4628 var cell = _this.getParentCell( image.img ); 4629 _this.cellSizeChange( cell && cell.element ); 4630 if ( !_this.options.freeScroll ) { 4631 _this.positionSliderAtSelected(); 4632 } 4633 } 4634 imagesLoaded( this.slider ).on( 'progress', onImagesLoadedProgress ); 4635}; 4636 4637return Flickity; 4638 4639})); 4640 4641 4642// ================================================== 4643// fancyBox v3.0.47 4644// 4645// Licensed GPLv3 for open source use 4646// or fancyBox Commercial License for commercial use 4647// 4648// http://fancyapps.com/fancybox/ 4649// Copyright 2017 fancyApps 4650// 4651// ================================================== 4652;(function (window, document, $, undefined) { 4653 'use strict'; 4654 4655 // If there's no jQuery, fancyBox can't work 4656 // ========================================= 4657 4658 if ( !$ ) { 4659 return undefined; 4660 } 4661 4662 // Private default settings 4663 // ======================== 4664 4665 var defaults = { 4666 4667 // Animation duration in ms 4668 speed : 330, 4669 4670 // Enable infinite gallery navigation 4671 loop : true, 4672 4673 // Should zoom animation change opacity, too 4674 // If opacity is 'auto', then fade-out if image and thumbnail have different aspect ratios 4675 opacity : 'auto', 4676 4677 // Space around image, ignored if zoomed-in or viewport smaller than 800px 4678 margin : [44, 0], 4679 4680 // Horizontal space between slides 4681 gutter : 30, 4682 4683 // Should display toolbars 4684 infobar : true, 4685 buttons : true, 4686 4687 // What buttons should appear in the toolbar 4688 slideShow : true, 4689 fullScreen : true, 4690 thumbs : true, 4691 closeBtn : true, 4692 4693 // Should apply small close button at top right corner of the content 4694 // If 'auto' - will be set for content having type 'html', 'inline' or 'ajax' 4695 smallBtn : 'auto', 4696 4697 image : { 4698 4699 // Wait for images to load before displaying 4700 // Requires predefined image dimensions 4701 // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found 4702 preload : "auto", 4703 4704 // Protect an image from downloading by right-click 4705 protect : false 4706 4707 }, 4708 4709 ajax : { 4710 4711 // Object containing settings for ajax request 4712 settings : { 4713 4714 // This helps to indicate that request comes from the modal 4715 // Feel free to change naming 4716 data : { 4717 fancybox : true 4718 } 4719 } 4720 4721 }, 4722 4723 iframe : { 4724 4725 // Iframe template 4726 tpl : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen allowtransparency="true" src=""></iframe>', 4727 4728 // Preload iframe before displaying it 4729 // This allows to calculate iframe content width and height 4730 // (note: Due to "Same Origin Policy", you can't get cross domain data). 4731 preload : true, 4732 4733 // Scrolling attribute for iframe tag 4734 scrolling : 'no', 4735 4736 // Custom CSS styling for iframe wrapping element 4737 css : {} 4738 4739 }, 4740 4741 // Custom CSS class for layout 4742 baseClass : '', 4743 4744 // Custom CSS class for slide element 4745 slideClass : '', 4746 4747 // Base template for layout 4748 baseTpl : '<div class="fancybox-container" role="dialog" tabindex="-1">' + 4749 '<div class="fancybox-bg"></div>' + 4750 '<div class="fancybox-controls">' + 4751 '<div class="fancybox-infobar">' + 4752 '<button data-fancybox-previous class="fancybox-button fancybox-button--left" title="Previous"></button>' + 4753 '<div class="fancybox-infobar__body">' + 4754 '<span class="js-fancybox-index"></span> / <span class="js-fancybox-count"></span>' + 4755 '</div>' + 4756 '<button data-fancybox-next class="fancybox-button fancybox-button--right" title="Next"></button>' + 4757 '</div>' + 4758 '<div class="fancybox-buttons">' + 4759 '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="Close (Esc)"></button>' + 4760 '</div>' + 4761 '</div>' + 4762 '<div class="fancybox-slider-wrap">' + 4763 '<div class="fancybox-slider"></div>' + 4764 '</div>' + 4765 '<div class="fancybox-caption-wrap"><div class="fancybox-caption"></div></div>' + 4766 '</div>', 4767 4768 // Loading indicator template 4769 spinnerTpl : '<div class="fancybox-loading"></div>', 4770 4771 // Error message template 4772 errorTpl : '<div class="fancybox-error"><p>The requested content cannot be loaded. <br /> Please try again later.<p></div>', 4773 4774 // This will be appended to html content, if "smallBtn" option is not set to false 4775 closeTpl : '<button data-fancybox-close class="fancybox-close-small"></button>', 4776 4777 // Container is injected into this element 4778 parentEl : 'body', 4779 4780 // Enable gestures (tap, zoom, pan and pinch) 4781 touch : true, 4782 4783 // Enable keyboard navigation 4784 keyboard : true, 4785 4786 // Try to focus on first focusable element after opening 4787 focus : true, 4788 4789 // Close when clicked outside of the content 4790 closeClickOutside : true, 4791 4792 // Callbacks 4793 beforeLoad : $.noop, 4794 afterLoad : $.noop, 4795 beforeMove : $.noop, 4796 afterMove : $.noop, 4797 onComplete : $.noop, 4798 4799 onInit : $.noop, 4800 beforeClose : $.noop, 4801 afterClose : $.noop, 4802 onActivate : $.noop, 4803 onDeactivate : $.noop 4804 4805 }; 4806 4807 var $W = $(window); 4808 var $D = $(document); 4809 4810 var called = 0; 4811 4812 // Check if an object is a jQuery object and not a native JavaScript object 4813 // ======================================================================== 4814 4815 var isQuery = function (obj) { 4816 return obj && obj.hasOwnProperty && obj instanceof $; 4817 }; 4818 4819 // Handle multiple browsers for requestAnimationFrame() 4820 // ==================================================== 4821 4822 var requestAFrame = (function() { 4823 return window.requestAnimationFrame || 4824 window.webkitRequestAnimationFrame || 4825 window.mozRequestAnimationFrame || 4826 function( callback ) { 4827 window.setTimeout(callback, 1000 / 60); }; 4828 })(); 4829 4830 4831 // Check if element is inside the viewport by at least 1 pixel 4832 // =========================================================== 4833 4834 var isElementInViewport = function( el ) { 4835 var rect; 4836 4837 if ( typeof $ === "function" && el instanceof $ ) { 4838 el = el[0]; 4839 } 4840 4841 rect = el.getBoundingClientRect(); 4842 4843 return rect.bottom > 0 && rect.right > 0 && 4844 rect.left < (window.innerWidth || document.documentElement.clientWidth) && 4845 rect.top < (window.innerHeight || document.documentElement.clientHeight); 4846 }; 4847 4848 4849 // Class definition 4850 // ================ 4851 4852 var FancyBox = function( content, opts, index ) { 4853 var self = this; 4854 4855 self.opts = $.extend( true, { index : index }, defaults, opts || {} ); 4856 self.id = self.opts.id || ++called; 4857 self.group = []; 4858 4859 self.currIndex = parseInt( self.opts.index, 10 ) || 0; 4860 self.prevIndex = null; 4861 4862 self.prevPos = null; 4863 self.currPos = 0; 4864 4865 self.firstRun = null; 4866 4867 // Create group elements from original item collection 4868 self.createGroup( content ); 4869 4870 if ( !self.group.length ) { 4871 return; 4872 } 4873 4874 // Save last active element and current scroll position 4875 self.$lastFocus = $(document.activeElement).blur(); 4876 4877 // Collection of gallery objects 4878 self.slides = {}; 4879 4880 self.init( content ); 4881 4882 }; 4883 4884 $.extend(FancyBox.prototype, { 4885 4886 // Create DOM structure 4887 // ==================== 4888 4889 init : function() { 4890 var self = this; 4891 4892 var galleryHasHtml = false;
4893 4894 var testWidth; 4895 var $container; 4896 4897 self.scrollTop = $D.scrollTop(); 4898 self.scrollLeft = $D.scrollLeft(); 4899 4900 if ( !$.fancybox.getInstance() ) { 4901 testWidth = $( 'body' ).width(); 4902 4903 $( 'html' ).addClass( 'fancybox-enabled' ); 4904 4905 if ( $.fancybox.isTouch ) { 4906 4907 // Ugly workaround for iOS page shifting issue (when inputs get focus) 4908 // Do not apply for images, otherwise top/bottom bars will appear 4909 $.each( self.group, function( key, item ) { 4910 if ( item.type !== 'image' && item.type !== 'iframe' ) { 4911 galleryHasHtml = true; 4912 return false; 4913 } 4914 }); 4915 4916 if ( galleryHasHtml ) { 4917 $('body').css({ 4918 position : 'fixed', 4919 width : testWidth, 4920 top : self.scrollTop * -1 4921 }); 4922 } 4923 4924 } else { 4925 4926 // Compare page width after adding "overflow:hidden" 4927 testWidth = $( 'body' ).width() - testWidth; 4928
4929 // Width has changed - compensate missing scrollbars 4930 if ( testWidth > 1 ) { 4931 $( '<style id="fancybox-noscroll" type="text/css">' ).html( '.compensate-for-scrollbar, .fancybox-enabled body { margin-right: ' + testWidth + 'px; }' ).appendTo( 'head' ); 4932 } 4933 4934 } 4935 } 4936 4937 $container = $( self.opts.baseTpl ) 4938 .attr('id', 'fancybox-container-' + self.id) 4939 .data( 'FancyBox', self ) 4940 .addClass( self.opts.baseClass ) 4941 .hide() 4942 .prependTo( self.opts.parentEl ); 4943 4944 // Create object holding references to jQuery wrapped nodes 4945 self.$refs = { 4946 container : $container, 4947 bg : $container.find('.fancybox-bg'), 4948 controls : $container.find('.fancybox-controls'), 4949 buttons : $container.find('.fancybox-buttons'), 4950 slider_wrap : $container.find('.fancybox-slider-wrap'), 4951 slider : $container.find('.fancybox-slider'), 4952 caption : $container.find('.fancybox-caption') 4953 }; 4954 4955 self.trigger( 'onInit' ); 4956 4957 // Bring to front and enable events 4958 self.activate(); 4959 4960 // Try to avoid running multiple times 4961 if ( self.current ) { 4962 return; 4963 } 4964 4965 self.jumpTo( self.currIndex ); 4966 4967 }, 4968 4969 4970 // Create array of gally item objects 4971 // Check if each object has valid type and content 4972 // =============================================== 4973 4974 createGroup : function ( content ) { 4975 var self = this; 4976 var items = $.makeArray( content ); 4977 4978 $.each(items, function( i, item ) { 4979 var obj = {}, 4980 opts = {}, 4981 data = [], 4982 $item, 4983 type, 4984 src, 4985 srcParts; 4986 4987 // Step 1 - Make sure we have an object 4988 4989 if ( $.isPlainObject( item ) ) { 4990 4991 obj = item; 4992 opts = item.opts || {}; 4993 4994 } else if ( $.type( item ) === 'object' && $( item ).length ) { 4995 4996 $item = $( item ); 4997 data = $item.data(); 4998 4999 opts = 'options' in data ? data.options : {}; 5000 5001 opts = $.type( opts ) === 'object' ? opts : {}; 5002 5003 obj.type = 'type' in data ? data.type : opts.type; 5004 obj.src = 'src' in data ? data.src : ( opts.src || $item.attr( 'href' ) ); 5005 5006 opts.width = 'width' in data ? data.width : opts.width; 5007 opts.height = 'height' in data ? data.height : opts.height; 5008 opts.thumb = 'thumb' in data ? data.thumb : opts.thumb; 5009 5010 opts.selector = 'selector' in data ? data.selector : opts.selector; 5011 5012 if ( 'srcset' in data ) { 5013 opts.image = { srcset : data.srcset }; 5014 } 5015 5016 opts.$orig = $item; 5017 5018 } else { 5019 5020 obj = { 5021 type : 'html', 5022 content : item + '' 5023 }; 5024 5025 } 5026 5027 obj.opts = $.extend( true, {}, self.opts, opts ); 5028 5029 // Step 2 - Make sure we have supported content type 5030 5031 type = obj.type; 5032 src = obj.src || ''; 5033 5034 if ( !type ) { 5035 5036 if ( obj.content ) { 5037 type = 'html'; 5038 5039 } else if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) { 5040 type = 'image'; 5041 5042 } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) { 5043 type = 'pdf'; 5044 5045 } else if ( src.charAt(0) === '#' ) { 5046 type = 'inline'; 5047 5048 } 5049 5050 obj.type = type; 5051 5052 } 5053 5054 // Step 3 - Some adjustments 5055 5056 obj.index = self.group.length; 5057 5058 // Check if $orig and $thumb objects exist 5059 if ( obj.opts.$orig && !obj.opts.$orig.length ) { 5060 delete obj.opts.$orig; 5061 } 5062 5063 if ( !obj.opts.$thumb && obj.opts.$orig ) {
5064 obj.opts.$thumb = obj.opts.$orig.find( 'img:first' ); 5065 } 5066 5067 if ( obj.opts.$thumb && !obj.opts.$thumb.length ) { 5068 delete obj.opts.$thumb; 5069 } 5070 5071 // Caption is a "special" option, it can be passed as a method 5072 if ( $.type( obj.opts.caption ) === 'function' ) { 5073 obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] ); 5074 5075 } else if ( 'caption' in data ) { 5076 obj.opts.caption = data.caption; 5077 5078 } else if ( opts.$orig ) { 5079 obj.opts.caption = $item.attr( 'title' ); 5080 } 5081 5082 // Make sure we have caption as a string 5083 obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + ''; 5084 5085 // Check if url contains selector used to filter the content 5086 // Example: "ajax.html #something" 5087 if ( type === 'ajax' ) { 5088 srcParts = src.split(/\s+/, 2); 5089 5090 if ( srcParts.length > 1 ) { 5091 obj.src = srcParts.shift(); 5092 5093 obj.opts.selector = srcParts.shift(); 5094 } 5095 } 5096 5097 if ( obj.opts.smallBtn == 'auto' ) { 5098 5099 if ( $.inArray( type, ['html', 'inline', 'ajax'] ) > -1 ) { 5100 obj.opts.buttons = false; 5101 obj.opts.smallBtn = true; 5102 5103 } else { 5104 obj.opts.smallBtn = false; 5105 } 5106 5107 } 5108 5109 if ( type === 'pdf' ) { 5110 5111 obj.type = 'iframe'; 5112 5113 obj.opts.closeBtn = true; 5114 obj.opts.smallBtn = false; 5115 5116 obj.opts.iframe.preload = false; 5117 5118 } 5119 5120 if ( obj.opts.modal ) { 5121 5122 $.extend(true, obj.opts, { 5123 infobar : 0, 5124 buttons : 0, 5125 keyboard : 0, 5126 slideShow : 0, 5127 fullScreen : 0, 5128 closeClickOutside : 0 5129 }); 5130 5131 } 5132 5133 self.group.push( obj ); 5134 5135 }); 5136 5137 }, 5138 5139 5140 // Attach an event handler functions for: 5141 // - navigation elements 5142 // - browser scrolling, resizing; 5143 // - focusing 5144 // - keyboard 5145 // ================= 5146 5147 addEvents : function() { 5148 var self = this; 5149 5150 self.removeEvents(); 5151 5152 // Make navigation elements clickable 5153 5154 self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) { 5155 e.stopPropagation(); 5156 e.preventDefault(); 5157 5158 self.close( e ); 5159 5160 }).on('click.fb-previous', '[data-fancybox-previous]', function(e) { 5161 e.stopPropagation(); 5162 e.preventDefault(); 5163 5164 self.previous(); 5165 5166 }).on('click.fb-next', '[data-fancybox-next]', function(e) { 5167 e.stopPropagation(); 5168 e.preventDefault(); 5169 5170 self.next(); 5171 }); 5172 5173 5174 // Handle page scrolling and browser resizing 5175 5176 $( window ).on('orientationchange.fb resize.fb', function(e) { 5177 requestAFrame(function() { 5178 5179 if ( e && e.originalEvent && e.originalEvent.type === "resize" ) { 5180 self.update(); 5181 5182 } else { 5183 self.$refs.slider_wrap.hide(); 5184 5185 requestAFrame(function () { 5186 self.$refs.slider_wrap.show(); 5187 5188 self.update(); 5189 }); 5190 5191 } 5192 5193 }); 5194 }); 5195 5196 5197 // Trap focus 5198 5199 $D.on('focusin.fb', function(e) { 5200 var instance = $.fancybox ? $.fancybox.getInstance() : null; 5201 5202 if ( instance && !$( e.target ).hasClass( 'fancybox-container' ) && !$.contains( instance.$refs.container[0], e.target ) ) { 5203 e.stopPropagation(); 5204 5205 instance.focus(); 5206 5207 // Sometimes page gets scrolled, set it back 5208 $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); 5209 } 5210 5211 }); 5212 5213 // Enable keyboard navigation 5214 5215 $D.on('keydown.fb', function (e) { 5216 var current = self.current, 5217 keycode = e.keyCode || e.which; 5218 5219 if ( !current || !current.opts.keyboard ) { 5220 return; 5221 } 5222 5223 if ( $(e.target).is('input') || $(e.target).is('textarea') ) { 5224 return; 5225 } 5226 5227 // Backspace and Esc keys 5228 if ( keycode === 8 || keycode === 27 ) { 5229 e.preventDefault(); 5230 5231 self.close( e ); 5232 5233 return; 5234 } 5235 5236 switch ( keycode ) { 5237 5238 case 37: // Left arrow 5239 case 38: // Up arrow 5240 5241 e.preventDefault(); 5242 5243 self.previous(); 5244 5245 break; 5246 5247 case 39: // Right arrow 5248 case 40: // Down arrow 5249 5250 e.preventDefault(); 5251 5252 self.next(); 5253 5254 break; 5255 5256 case 80: // "P" 5257 case 32: // Spacebar 5258 5259 e.preventDefault(); 5260 5261 if ( self.SlideShow ) { 5262 e.preventDefault(); 5263 5264 self.SlideShow.toggle(); 5265 } 5266 5267 break; 5268 5269 case 70: // "F" 5270 5271 if ( self.FullScreen ) { 5272 e.preventDefault(); 5273 5274 self.FullScreen.toggle(); 5275 } 5276 5277 break; 5278 5279 case 71: // "G" 5280 5281 if ( self.Thumbs ) { 5282 e.preventDefault(); 5283 5284 self.Thumbs.toggle(); 5285 } 5286 5287 break; 5288 } 5289 }); 5290 5291 5292 }, 5293 5294 5295 // Remove events added by the core 5296 // =============================== 5297 5298 removeEvents : function () { 5299 5300 $W.off( 'scroll.fb resize.fb orientationchange.fb' ); 5301 $D.off( 'keydown.fb focusin.fb click.fb-close' ); 5302 5303 this.$refs.container.off('click.fb-close click.fb-previous click.fb-next'); 5304 }, 5305 5306 5307 // Slide to left 5308 // ================== 5309 5310 previous : function( duration ) { 5311 this.jumpTo( this.currIndex - 1, duration ); 5312 }, 5313 5314 5315 // Slide to right 5316 // =================== 5317 5318 next : function( duration ) { 5319 this.jumpTo( this.currIndex + 1, duration ); 5320 }, 5321 5322 5323 // Display current gallery item, move slider to current position 5324 // ============================================================= 5325 5326 jumpTo : function ( to, duration ) { 5327 var self = this, 5328 firstRun, 5329 index, 5330 pos, 5331 loop; 5332 5333 firstRun = self.firstRun = ( self.firstRun === null ); 5334 5335 index = pos = to = parseInt( to, 10 ); 5336 loop = self.current ? self.current.opts.loop : false;
5337 5338 if ( self.isAnimating || ( index == self.currIndex && !firstRun ) ) { 5339 return; 5340 } 5341 5342 if ( self.group.length > 1 && loop ) { 5343 5344 index = index % self.group.length; 5345 index = index < 0 ? self.group.length + index : index; 5346 5347 // Calculate closest position of upcoming item from the current one 5348 if ( self.group.length == 2 ) { 5349 pos = to - self.currIndex + self.currPos; 5350 5351 } else { 5352 pos = index - self.currIndex + self.currPos; 5353 5354 if ( Math.abs( self.currPos - ( pos + self.group.length ) ) < Math.abs( self.currPos - pos ) ) { 5355 pos = pos + self.group.length; 5356 5357 } else if ( Math.abs( self.currPos - ( pos - self.group.length ) ) < Math.abs( self.currPos - pos ) ) { 5358 pos = pos - self.group.length; 5359 5360 } 5361 } 5362 5363 } else if ( !self.group[ index ] ) { 5364 self.update( false, false, duration ); 5365 5366 return; 5367 } 5368 5369 if ( self.current ) { 5370 self.current.$slide.removeClass('fancybox-slide--current fancybox-slide--complete'); 5371 5372 self.updateSlide( self.current, true ); 5373 } 5374 5375 self.prevIndex = self.currIndex; 5376 self.prevPos = self.currPos; 5377 5378 self.currIndex = index; 5379 self.currPos = pos; 5380 5381 // Create slides 5382 5383 self.current = self.createSlide( pos ); 5384 5385 if ( self.group.length > 1 ) { 5386 5387 if ( self.opts.loop || pos - 1 >= 0 ) { 5388 self.createSlide( pos - 1 ); 5389 } 5390 5391 if ( self.opts.loop || pos + 1 < self.group.length ) { 5392 self.createSlide( pos + 1 ); 5393 } 5394 } 5395 5396 self.current.isMoved = false; 5397 self.current.isComplete = false; 5398 5399 duration = parseInt( duration === undefined ? self.current.opts.speed * 1.5 : duration, 10 ); 5400 5401 // Move slider to the next position 5402 // Note: the content might still be loading 5403 self.trigger( 'beforeMove' ); 5404 5405 self.updateControls(); 5406 5407 if ( firstRun ) { 5408 self.current.$slide.addClass('fancybox-slide--current'); 5409 5410 self.$refs.container.show(); 5411 5412 requestAFrame(function() { 5413 self.$refs.bg.css('transition-duration', self.current.opts.speed + 'ms'); 5414 5415 self.$refs.container.addClass( 'fancybox-container--ready' ); 5416 }); 5417 } 5418 5419 // Set position immediately on first opening 5420 self.update( true, false, firstRun ? 0 : duration, function() { 5421 self.afterMove(); 5422 }); 5423 5424 self.loadSlide( self.current ); 5425 5426 if ( !( firstRun && self.current.$ghost ) ) { 5427 self.preload(); 5428 } 5429 5430 }, 5431 5432 5433 // Create new "slide" element 5434 // These are gallery items that are actually added to DOM 5435 // ======================================================= 5436 5437 createSlide : function( pos ) { 5438 5439 var self = this; 5440 var $slide; 5441 var index; 5442 var found; 5443 5444 index = pos % self.group.length; 5445 index = index < 0 ? self.group.length + index : index; 5446 5447 if ( !self.slides[ pos ] && self.group[ index ] ) { 5448 5449 // If we are looping and slide with that index already exists, then reuse it 5450 if ( self.opts.loop && self.group.length > 2 ) { 5451 for (var key in self.slides) { 5452 if ( self.slides[ key ].index === index ) { 5453 found = self.slides[ key ]; 5454 found.pos = pos; 5455 5456 self.slides[ pos ] = found; 5457 5458 delete self.slides[ key ]; 5459 5460 self.updateSlide( found ); 5461 5462 return found; 5463 } 5464 } 5465 } 5466
5467 $slide = $('<div class="fancybox-slide"></div>').appendTo( self.$refs.slider ); 5468 5469 self.slides[ pos ] = $.extend( true, {}, self.group[ index ], { 5470 pos : pos, 5471 $slide : $slide, 5472 isMoved : false, 5473 isLoaded : false 5474 }); 5475 5476 } 5477 5478 return self.slides[ pos ]; 5479 5480 }, 5481 5482 zoomInOut : function( type, duration, callback ) { 5483 5484 var self = this; 5485 var current = self.current; 5486 var $what = current.$placeholder; 5487 var opacity = current.opts.opacity; 5488 var $thumb = current.opts.$thumb; 5489 var thumbPos = $thumb ? $thumb.offset() : 0; 5490 var slidePos = current.$slide.offset(); 5491 var props; 5492 var start; 5493 var end; 5494 5495 if ( !$what || !current.isMoved || !thumbPos || !isElementInViewport( $thumb ) ) { 5496 return false; 5497 } 5498 5499 if ( type === 'In' && !self.firstRun ) { 5500 return false; 5501 } 5502 5503 $.fancybox.stop( $what ); 5504 5505 self.isAnimating = true; 5506 5507 props = { 5508 top : thumbPos.top - slidePos.top + parseFloat( $thumb.css( "border-top-width" ) || 0 ), 5509 left : thumbPos.left - slidePos.left + parseFloat( $thumb.css( "border-left-width" ) || 0 ), 5510 width : $thumb.width(), 5511 height : $thumb.height(), 5512 scaleX : 1, 5513 scaleY : 1 5514 }; 5515 5516 // Check if we need to animate opacity 5517 if ( opacity == 'auto' ) { 5518 opacity = Math.abs( current.width / current.height - props.width / props.height ) > 0.1; 5519 } 5520 5521 if ( type === 'In' ) { 5522 start = props; 5523 end = self.getFitPos( current ); 5524 5525 end.scaleX = end.width / start.width; 5526 end.scaleY = end.height / start.height; 5527 5528 if ( opacity ) { 5529 start.opacity = 0.1; 5530 end.opacity = 1; 5531 } 5532 5533 } else { 5534 5535 start = $.fancybox.getTranslate( $what ); 5536 end = props; 5537 5538 // Switch to thumbnail image to improve animation performance 5539 if ( current.$ghost ) { 5540 current.$ghost.show(); 5541 5542 if ( current.$image ) { 5543 current.$image.remove(); 5544 } 5545 } 5546 5547 start.scaleX = start.width / end.width; 5548 start.scaleY = start.height / end.height; 5549 5550 start.width = end.width; 5551 start.height = end.height; 5552 5553 if ( opacity ) { 5554 end.opacity = 0; 5555 } 5556 5557 } 5558 5559 self.updateCursor( end.width, end.height ); 5560 5561 // There is no need to animate width/height properties 5562 delete end.width; 5563 delete end.height; 5564 5565 $.fancybox.setTranslate( $what, start ); 5566 5567 $what.show(); 5568 5569 self.trigger( 'beforeZoom' + type ); 5570 5571 $what.css( 'transition', 'all ' + duration + 'ms' ); 5572 5573 $.fancybox.setTranslate( $what, end ); 5574 5575 setTimeout(function() { 5576 var reset; 5577 5578 $what.css( 'transition', 'none' ); 5579 5580 reset = $.fancybox.getTranslate( $what ); 5581 5582 reset.scaleX = 1; 5583 reset.scaleY = 1; 5584 5585 // Reset scalex/scaleY values; this helps for perfomance 5586 $.fancybox.setTranslate( $what, reset ); 5587 5588 self.trigger( 'afterZoom' + type ); 5589 5590 callback.apply( self ); 5591 5592 self.isAnimating = false; 5593 5594 }, duration); 5595 5596 5597 return true; 5598 5599 }, 5600 5601 // Check if image dimensions exceed parent element 5602 // =============================================== 5603 5604 canPan : function() { 5605 5606 var self = this; 5607 5608 var current = self.current; 5609 var $what = current.$placeholder; 5610 5611 var rez = false;
5612 5613 if ( $what ) { 5614 rez = self.getFitPos( current ); 5615 rez = Math.abs( $what.width() - rez.width ) > 1 || Math.abs( $what.height() - rez.height ) > 1; 5616 5617 } 5618 5619 return rez; 5620 5621 }, 5622 5623 5624 // Check if current image dimensions are smaller than actual 5625 // ========================================================= 5626 5627 isScaledDown : function() { 5628 5629 var self = this; 5630 5631 var current = self.current; 5632 var $what = current.$placeholder; 5633 5634 var rez = false; 5635 5636 if ( $what ) { 5637 rez = $.fancybox.getTranslate( $what ); 5638 rez = rez.width < current.width || rez.height < current.height; 5639 } 5640 5641 return rez; 5642 5643 }, 5644 5645 5646 // Scale image to the actual size of the image 5647 // =========================================== 5648 5649 scaleToActual : function( x, y, duration ) { 5650 5651 var self = this; 5652 5653 var current = self.current; 5654 var $what = current.$placeholder; 5655 5656 var imgPos, posX, posY, scaleX, scaleY; 5657 5658 var canvasWidth = parseInt( current.$slide.width(), 10 ); 5659 var canvasHeight = parseInt( current.$slide.height(), 10 ); 5660 5661 var newImgWidth = current.width; 5662 var newImgHeight = current.height; 5663 5664 if ( !$what ) { 5665 return; 5666 } 5667 5668 self.isAnimating = true; 5669 5670 x = x === undefined ? canvasWidth * 0.5 : x; 5671 y = y === undefined ? canvasHeight * 0.5 : y; 5672 5673 imgPos = $.fancybox.getTranslate( $what ); 5674 5675 scaleX = newImgWidth / imgPos.width; 5676 scaleY = newImgHeight / imgPos.height; 5677 5678 // Get center position for original image 5679 posX = ( canvasWidth * 0.5 - newImgWidth * 0.5 ); 5680 posY = ( canvasHeight * 0.5 - newImgHeight * 0.5 ); 5681 5682 // Make sure image does not move away from edges 5683 5684 if ( newImgWidth > canvasWidth ) { 5685 posX = imgPos.left * scaleX - ( ( x * scaleX ) - x ); 5686 5687 if ( posX > 0 ) { 5688 posX = 0; 5689 } 5690 5691 if ( posX < canvasWidth - newImgWidth ) { 5692 posX = canvasWidth - newImgWidth; 5693 } 5694 } 5695 5696 if ( newImgHeight > canvasHeight) { 5697 posY = imgPos.top * scaleY - ( ( y * scaleY ) - y ); 5698 5699 if ( posY > 0 ) { 5700 posY = 0; 5701 } 5702 5703 if ( posY < canvasHeight - newImgHeight ) { 5704 posY = canvasHeight - newImgHeight; 5705 } 5706 } 5707 5708 self.updateCursor( newImgWidth, newImgHeight ); 5709 5710 $.fancybox.animate( $what, null, { 5711 top : posY, 5712 left : posX, 5713 scaleX : scaleX, 5714 scaleY : scaleY 5715 }, duration || current.opts.speed, function() { 5716 self.isAnimating = false; 5717 }); 5718 5719 }, 5720 5721 5722 // Scale image to fit inside parent element 5723 // ======================================== 5724 5725 scaleToFit : function( duration ) { 5726 5727 var self = this; 5728 5729 var current = self.current; 5730 var $what = current.$placeholder; 5731 var end; 5732 5733 if ( !$what ) { 5734 return; 5735 } 5736 5737 self.isAnimating = true; 5738 5739 end = self.getFitPos( current ); 5740 5741 self.updateCursor( end.width, end.height ); 5742 5743 $.fancybox.animate( $what, null, { 5744 top : end.top, 5745 left : end.left, 5746 scaleX : end.width / $what.width(), 5747 scaleY : end.height / $what.height() 5748 }, duration || current.opts.speed, function() { 5749 self.isAnimating = false; 5750 }); 5751 5752 }, 5753 5754 // Calculate image size to fit inside viewport 5755 // =========================================== 5756 5757 getFitPos : function( slide ) { 5758 var $what = slide.$placeholder || slide.$content; 5759 5760 var imgWidth = slide.width; 5761 var imgHeight = slide.height; 5762 5763 var margin = slide.opts.margin; 5764 5765 var canvasWidth, canvasHeight, minRatio, top, left, width, height; 5766 5767 if ( !$what || !$what.length || ( !imgWidth && !imgHeight) ) { 5768 return false;
5769 } 5770 5771 // Convert "margin to CSS style: [ top, right, bottom, left ] 5772 if ( $.type( margin ) === "number" ) { 5773 margin = [ margin, margin ]; 5774 } 5775 5776 if ( margin.length == 2 ) { 5777 margin = [ margin[0], margin[1], margin[0], margin[1] ]; 5778 } 5779 5780 if ( $W.width() < 800 ) { 5781 margin = [0, 0, 0, 0]; 5782 } 5783 5784 canvasWidth = parseInt( slide.$slide.width(), 10 ) - ( margin[ 1 ] + margin[ 3 ] ); 5785 canvasHeight = parseInt( slide.$slide.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] ); 5786 5787 minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight ); 5788 5789 // Use floor rounding to make sure it really fits 5790 5791 width = Math.floor( minRatio * imgWidth ); 5792 height = Math.floor( minRatio * imgHeight ); 5793 5794 top = Math.floor( ( canvasHeight - height ) * 0.5 ) + margin[ 0 ]; 5795 left = Math.floor( ( canvasWidth - width ) * 0.5 ) + margin[ 3 ]; 5796 5797 return { 5798 top : top, 5799 left : left, 5800 width : width, 5801 height : height 5802 }; 5803 5804 }, 5805 5806 // Move slider to current position 5807 // Update all slides (and their content) 5808 // ===================================== 5809 5810 update : function( andSlides, andContent, duration, callback ) { 5811 5812 var self = this; 5813 var leftValue; 5814 5815 if ( self.isAnimating === true || !self.current ) { 5816 return; 5817 } 5818 5819 leftValue = ( self.current.pos * Math.floor( self.current.$slide.width() ) * -1 ) - ( self.current.pos * self.current.opts.gutter ); 5820 duration = parseInt( duration, 10 ) || 0; 5821 5822 $.fancybox.stop( self.$refs.slider ); 5823 5824 if ( andSlides === false ) { 5825 self.updateSlide( self.current, andContent ); 5826 5827 } else { 5828 5829 $.each( self.slides, function( key, slide ) { 5830 self.updateSlide( slide, andContent ); 5831 }); 5832 5833 } 5834 5835 if ( duration ) { 5836 5837 $.fancybox.animate( self.$refs.slider, null, { 5838 top : 0, 5839 left : leftValue 5840 }, duration, function() { 5841 self.current.isMoved = true; 5842 5843 if ( $.type( callback ) === 'function' ) { 5844 callback.apply( self ); 5845 } 5846 5847 }); 5848 5849 } else { 5850 5851 $.fancybox.setTranslate( self.$refs.slider, { top : 0, left : leftValue } ); 5852 5853 self.current.isMoved = true; 5854 5855 if ( $.type( callback ) === 'function' ) { 5856 callback.apply( self ); 5857 } 5858 5859 } 5860 5861 }, 5862 5863 5864 // Update slide position and scale content to fit 5865 // ============================================== 5866 5867 updateSlide : function( slide, andContent ) { 5868 5869 var self = this; 5870 var $what = slide.$placeholder; 5871 var leftPos; 5872 5873 slide = slide || self.current; 5874 5875 if ( !slide || self.isClosing ) { 5876 return; 5877 } 5878 5879 leftPos = ( slide.pos * Math.floor( slide.$slide.width() ) ) + ( slide.pos * slide.opts.gutter); 5880 5881 if ( leftPos !== slide.leftPos ) { 5882 $.fancybox.setTranslate( slide.$slide, { top: 0, left : leftPos } ); 5883 5884 slide.leftPos = leftPos; 5885 } 5886 5887 if ( andContent !== false && $what ) { 5888 $.fancybox.setTranslate( $what, self.getFitPos( slide ) ); 5889 5890 if ( slide.pos === self.currPos ) { 5891 self.updateCursor(); 5892 } 5893 } 5894 5895 slide.$slide.trigger( 'refresh' ); 5896 5897 self.trigger( 'onUpdate', slide ); 5898 }, 5899 5900 // Update cursor style depending if content can be zoomed 5901 // ====================================================== 5902 5903 updateCursor : function( nextWidth, nextHeight ) { 5904 5905 var self = this; 5906 var canScale; 5907 5908 var $container = self.$refs.container.removeClass('fancybox-controls--canzoomIn fancybox-controls--canzoomOut fancybox-controls--canGrab'); 5909 5910 if ( self.isClosing || !self.opts.touch ) { 5911 return; 5912 } 5913 5914 if ( nextWidth !== undefined && nextHeight !== undefined ) { 5915 canScale = nextWidth < self.current.width && nextHeight < self.current.height; 5916 5917 } else { 5918 canScale = self.isScaledDown(); 5919 } 5920 5921 if ( canScale ) { 5922 $container.addClass('fancybox-controls--canzoomIn'); 5923 5924 } else if ( self.group.length < 2 ) { 5925 $container.addClass('fancybox-controls--canzoomOut'); 5926 5927 } else { 5928 $container.addClass('fancybox-controls--canGrab'); 5929 } 5930 5931 }, 5932 5933 // Load content into the slide 5934 // =========================== 5935 5936 loadSlide : function( slide ) { 5937 5938 var self = this, type, $slide; 5939 var ajaxLoad; 5940 5941 if ( !slide || slide.isLoaded || slide.isLoading ) { 5942 return; 5943 } 5944 5945 slide.isLoading = true; 5946 5947 self.trigger( 'beforeLoad', slide ); 5948 5949 type = slide.type; 5950 $slide = slide.$slide; 5951 5952 $slide 5953 .off( 'refresh' ) 5954 .trigger( 'onReset' ) 5955 .addClass( 'fancybox-slide--' + ( type || 'unknown' ) ) 5956 .addClass( slide.opts.slideClass ); 5957 5958 // Create content depending on the type 5959 5960 switch ( type ) { 5961 5962 case 'image': 5963 5964 self.setImage( slide ); 5965 5966 break; 5967 5968 case 'iframe': 5969 5970 self.setIframe( slide ); 5971 5972 break; 5973 5974 case 'html': 5975 5976 self.setContent( slide, slide.content ); 5977 5978 break; 5979 5980 case 'inline': 5981 5982 if ( $( slide.src ).length ) { 5983 self.setContent( slide, $( slide.src ) ); 5984 5985 } else { 5986 self.setError( slide ); 5987 } 5988 5989 break; 5990 5991 case 'ajax': 5992 5993 self.showLoading( slide ); 5994 5995 ajaxLoad = $.ajax( $.extend( {}, slide.opts.ajax.settings, { 5996 5997 url: slide.src, 5998 5999 success: function ( data, textStatus ) { 6000 6001 if ( textStatus === 'success' ) { 6002 self.setContent( slide, data ); 6003 } 6004 6005 }, 6006 6007 error: function ( jqXHR, textStatus ) { 6008 6009 if ( jqXHR && textStatus !== 'abort' ) { 6010 self.setError( slide ); 6011 } 6012 6013 } 6014 6015 })); 6016 6017 $slide.one( 'onReset', function () { 6018 ajaxLoad.abort(); 6019 }); 6020 6021 break; 6022 6023 default: 6024 6025 self.setError( slide ); 6026 6027 break; 6028 6029 } 6030 6031 return true; 6032 6033 }, 6034 6035 6036 // Use thumbnail image, if possible 6037 // ================================ 6038 6039 setImage : function( slide ) { 6040 6041 var self = this; 6042 var srcset = slide.opts.image.srcset; 6043
6044 var found, temp, pxRatio, windowWidth; 6045 6046 if ( slide.isLoaded && !slide.hasError ) { 6047 self.afterLoad( slide ); 6048 6049 return; 6050 } 6051 6052 // If we have "srcset", then we need to find matching "src" value. 6053 // This is necessary, because when you set an src attribute, the browser will preload the image 6054 // before any javascript or even CSS is applied. 6055 if ( srcset ) { 6056 pxRatio = window.devicePixelRatio || 1; 6057 windowWidth = window.innerWidth * pxRatio; 6058 6059 temp = srcset.split(',').map(function (el) { 6060 var ret = {}; 6061 6062 el.trim().split(/\s+/).forEach(function (el, i) { 6063 var value = parseInt(el.substring(0, el.length - 1), 10); 6064 6065 if ( i === 0 ) { 6066 return (ret.url = el); 6067 } 6068 6069 if ( value ) { 6070 ret.value = value; 6071 ret.postfix = el[el.length - 1]; 6072 } 6073 6074 }); 6075 6076 return ret; 6077 }); 6078 6079 // Sort by value 6080 temp.sort(function (a, b) { 6081 return a.value - b.value; 6082 }); 6083 6084 // Ok, now we have an array of all srcset values 6085 for ( var j = 0; j < temp.length; j++ ) { 6086 var el = temp[ j ]; 6087 6088 if ( ( el.postfix === 'w' && el.value >= windowWidth ) || ( el.postfix === 'x' && el.value >= pxRatio ) ) { 6089 found = el; 6090 break; 6091 } 6092 } 6093 6094 // If not found, take the last one 6095 if ( !found && temp.length ) { 6096 found = temp[ temp.length - 1 ]; 6097 } 6098 6099 if ( found ) { 6100 slide.src = found.url; 6101 6102 // If we have default width/height values, we can calculate height for matching source 6103 if ( slide.width && slide.height && found.postfix == 'w' ) { 6104 slide.height = ( slide.width / slide.height ) * found.value; 6105 slide.width = found.value; 6106 } 6107 } 6108 } 6109 6110 slide.$placeholder = $('<div class="fancybox-placeholder"></div>') 6111 .hide() 6112 .appendTo( slide.$slide ); 6113 6114 if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) { 6115 6116 slide.width = slide.opts.width; 6117 slide.height = slide.opts.height; 6118 6119 slide.$ghost = $('<img />') 6120 .one('load error', function() { 6121 6122 if ( self.isClosing ) { 6123 return; 6124 } 6125 6126 // Start preloading full size image 6127 $('<img/>')[0].src = slide.src; 6128 6129 // zoomIn or just show 6130 self.revealImage( slide, function() { 6131 6132 self.setBigImage( slide ); 6133 6134 if ( self.firstRun && slide.index === self.currIndex ) { 6135 self.preload(); 6136 } 6137 }); 6138 6139 }) 6140 .addClass( 'fancybox-image' ) 6141 .appendTo( slide.$placeholder ) 6142 .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) ); 6143 6144 } else { 6145 6146 self.setBigImage( slide ); 6147 6148 } 6149 6150 }, 6151 6152 6153 // Create full-size image 6154 // ====================== 6155 6156 setBigImage : function ( slide ) { 6157 var self = this; 6158 var $img = $('<img />'); 6159 6160 slide.$image = $img 6161 .one('error', function() { 6162 6163 self.setError( slide ); 6164 6165 }) 6166 .one('load', function() { 6167 6168 // Clear timeout that checks if loading icon needs to be displayed 6169 clearTimeout( slide.timouts ); 6170 6171 slide.timouts = null; 6172 6173 if ( self.isClosing ) { 6174 return; 6175 } 6176
6177 slide.width = this.naturalWidth; 6178 slide.height = this.naturalHeight; 6179 6180 if ( slide.opts.image.srcset ) { 6181 $img.attr('sizes', '100vw').attr('srcset', slide.opts.image.srcset); 6182 } 6183 6184 self.afterLoad( slide ); 6185 6186 if ( slide.$ghost ) { 6187 slide.timouts = setTimeout(function() { 6188 slide.$ghost.hide(); 6189 6190 }, 350); 6191 } 6192 6193 }) 6194 .addClass('fancybox-image') 6195 .attr('src', slide.src) 6196 .appendTo( slide.$placeholder ); 6197 6198 if ( $img[0].complete ) { 6199 $img.trigger('load'); 6200 6201 } else if( $img[0].error ) { 6202 $img.trigger('error'); 6203 6204 } else { 6205 6206 slide.timouts = setTimeout(function() { 6207 if ( !$img[0].complete && !slide.hasError ) { 6208 self.showLoading( slide ); 6209 } 6210 6211 }, 150); 6212 6213 } 6214 6215 if ( slide.opts.image.protect ) { 6216 $('<div class="fancybox-spaceball"></div>').appendTo( slide.$placeholder ).on('contextmenu.fb',function(e){ 6217 if ( e.button == 2 ) { 6218 e.preventDefault(); 6219 } 6220 6221 return true; 6222 }); 6223 } 6224 6225 }, 6226 6227 // Simply show image holder without animation 6228 // It has been hidden initially to avoid flickering 6229 // ================================================ 6230 6231 revealImage : function( slide, callback ) { 6232 6233 var self = this; 6234 6235 callback = callback || $.noop; 6236 6237 if ( slide.type !== 'image' || slide.hasError || slide.isRevealed === true ) { 6238 6239 callback.apply( self ); 6240 6241 return; 6242 } 6243 6244 slide.isRevealed = true; 6245 6246 if ( !( slide.pos === self.currPos && self.zoomInOut( 'In', slide.opts.speed, callback ) ) ) { 6247 6248 if ( slide.$ghost && !slide.isLoaded ) { 6249 self.updateSlide( slide, true ); 6250 } 6251 6252 if ( slide.pos === self.currPos ) { 6253 $.fancybox.animate( slide.$placeholder, { opacity: 0 }, { opacity: 1 }, 300, callback ); 6254 6255 } else { 6256 slide.$placeholder.show(); 6257 } 6258 6259 callback.apply( self ); 6260 6261 } 6262 6263 }, 6264 6265 // Create iframe wrapper, iframe and bindings 6266 // ========================================== 6267 6268 setIframe : function( slide ) { 6269 var self = this, 6270 opts = slide.opts.iframe, 6271 $slide = slide.$slide, 6272 $iframe; 6273 6274 slide.$content = $('<div class="fancybox-content"></div>') 6275 .css( opts.css ) 6276 .appendTo( $slide ); 6277 6278 $iframe = $( opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) ) 6279 .attr('scrolling', $.fancybox.isTouch ? 'auto' : opts.scrolling) 6280 .appendTo( slide.$content ); 6281 6282 if ( opts.preload ) { 6283 slide.$content.addClass( 'fancybox-tmp' ); 6284 6285 self.showLoading( slide ); 6286 6287 // Unfortunately, it is not always possible to determine if iframe is successfully loaded 6288 // (due to browser security policy) 6289 6290 $iframe.on('load.fb error.fb', function(e) { 6291 this.isReady = 1; 6292 6293 slide.$slide.trigger( 'refresh' ); 6294 6295 self.afterLoad( slide ); 6296 6297 }); 6298 6299 // Recalculate iframe content size 6300 6301 $slide.on('refresh.fb', function() { 6302 var $wrap = slide.$content, 6303 $contents, 6304 $body, 6305 scrollWidth, 6306 frameWidth, 6307 frameHeight; 6308 6309 if ( $iframe[0].isReady !== 1 ) { 6310 return; 6311 } 6312 6313 // Check if content is accessible, 6314 // it will fail if frame is not with the same origin 6315 6316 try { 6317 $contents = $iframe.contents(); 6318 $body = $contents.find('body'); 6319 6320 } catch (ignore) {} 6321 6322 // Calculate dimensions for the wrapper 6323 6324 if ( $body && $body.length && !( opts.css.width !== undefined && opts.css.height !== undefined ) ) { 6325 6326 scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth; 6327 6328 frameWidth = Math.ceil( $body.outerWidth(true) + ( $wrap.width() - scrollWidth ) );
6329 frameHeight = Math.ceil( $body.outerHeight(true) ); 6330 6331 // Resize wrapper to fit iframe content 6332 6333 $wrap.css({ 6334 'width' : opts.css.width === undefined ? frameWidth + ( $wrap.outerWidth() - $wrap.innerWidth() ) : opts.css.width, 6335 'height' : opts.css.height === undefined ? frameHeight + ( $wrap.outerHeight() - $wrap.innerHeight() ) : opts.css.height 6336 }); 6337 6338 } 6339 6340 $wrap.removeClass( 'fancybox-tmp' ); 6341 6342 }); 6343 6344 } else { 6345 6346 this.afterLoad( slide ); 6347 6348 } 6349 6350 $iframe.attr( 'src', slide.src ); 6351 6352 if ( slide.opts.smallBtn ) { 6353 slide.$content.prepend( slide.opts.closeTpl ); 6354 } 6355 6356 // Remove iframe if closing or changing gallery item 6357 6358 $slide.one('onReset', function () { 6359 6360 // This helps IE not to throw errors when closing 6361 6362 try { 6363 6364 $(this).find('iframe').hide().attr('src', '//about:blank'); 6365 6366 } catch (ignore) {} 6367 6368 $(this).empty(); 6369 6370 slide.isLoaded = false; 6371 6372 }); 6373 6374 }, 6375 6376 6377 // Wrap and append content to the slide 6378 // ====================================== 6379 6380 setContent : function ( slide, content ) { 6381 6382 var self = this; 6383 6384 if ( self.isClosing ) { 6385 return; 6386 } 6387 6388 self.hideLoading( slide ); 6389 6390 slide.$slide.empty(); 6391 6392 if ( isQuery( content ) && content.parent().length ) { 6393 6394 // If it is a jQuery object, then it will be moved to the box. 6395 // The placeholder is created so we will know where to put it back. 6396 // If user is navigating gallery fast, then the content might be already moved to the box 6397 6398 if ( content.data( 'placeholder' ) ) { 6399 content.parents('.fancybox-slide').trigger( 'onReset' ); 6400 } 6401 6402 content.data({'placeholder' : $('<div></div>' ).hide().insertAfter( content ) }).css('display', 'inline-block'); 6403 6404 } else { 6405 6406 if ( $.type( content ) === 'string' ) { 6407 6408 content = $('<div>').append( content ).contents(); 6409 6410 if ( content[0].nodeType === 3 ) { 6411 content = $('<div>').html( content ); 6412 } 6413 6414 } 6415 6416 if ( slide.opts.selector ) { 6417 content = $('<div>').html( content ).find( slide.opts.selector ); 6418 } 6419 6420 } 6421 6422 slide.$slide.one('onReset', function () { 6423 var placeholder = isQuery( content ) ? content.data('placeholder') : 0; 6424 6425 if ( placeholder ) { 6426 content.hide().replaceAll( placeholder ); 6427 6428 content.data( 'placeholder', null ); 6429 } 6430 6431 if ( !slide.hasError ) { 6432 $(this).empty(); 6433 6434 slide.isLoaded = false; 6435 } 6436 6437 }); 6438 6439 slide.$content = $( content ).appendTo( slide.$slide ); 6440 6441 if ( slide.opts.smallBtn === true ) { 6442 slide.$content.find( '.fancybox-close-small' ).remove().end().eq(0).append( slide.opts.closeTpl ); 6443 } 6444 6445 this.afterLoad( slide ); 6446 6447 }, 6448 6449 // Display error message 6450 // ===================== 6451 6452 setError : function ( slide ) { 6453 6454 slide.hasError = true; 6455 6456 this.setContent( slide, slide.opts.errorTpl ); 6457 6458 }, 6459 6460 6461 showLoading : function( slide ) { 6462 var self = this; 6463 6464 slide = slide || self.current; 6465 6466 if ( slide && !slide.$spinner ) { 6467 slide.$spinner = $( self.opts.spinnerTpl ).appendTo( slide.$slide ); 6468 } 6469 6470 }, 6471 6472 hideLoading : function( slide ) { 6473 6474 var self = this; 6475 6476 slide = slide || self.current; 6477 6478 if ( slide && slide.$spinner ) { 6479 slide.$spinner.remove(); 6480 6481 delete slide.$spinner; 6482 } 6483 6484 }, 6485 6486 afterMove : function() { 6487 6488 var self = this; 6489 var current = self.current; 6490 var slides = {}; 6491 6492 if ( !current ) { 6493 return; 6494 } 6495 6496 current.$slide.siblings().trigger( 'onReset' ); 6497 6498 // Remove unnecessary slides 6499 $.each( self.slides, function( key, slide ) { 6500 6501 if ( slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1 ) { 6502 slides[ slide.pos ] = slide; 6503 6504 } else if ( slide ) { 6505 slide.$slide.remove(); 6506 } 6507 6508 }); 6509 6510 self.slides = slides; 6511 6512 self.trigger( 'afterMove' ); 6513 6514 if ( current.isLoaded ) { 6515 self.complete(); 6516 } 6517 6518 }, 6519 6520 // Adjustments after slide has been loaded 6521 // ======================================= 6522 6523 afterLoad : function( slide ) { 6524 6525 var self = this; 6526 6527 if ( self.isClosing ) { 6528 return; 6529 } 6530 6531 slide.isLoading = false;
6532 slide.isLoaded = true; 6533 6534 self.trigger( 'afterLoad', slide ); 6535 6536 self.hideLoading( slide ); 6537 6538 // Resize content to fit inside slide 6539 // Skip if slide has an $ghost element, because then it has been already processed 6540 if ( !slide.$ghost ) { 6541 self.updateSlide( slide, true ); 6542 } 6543 6544 if ( slide.index === self.currIndex && slide.isMoved ) { 6545 self.complete(); 6546 6547 } else if ( !slide.$ghost ) { 6548 self.revealImage( slide ); 6549 } 6550 6551 }, 6552 6553 6554 // Final adjustments after current gallery item is moved to position 6555 // and it`s content is loaded 6556 // ================================================================== 6557 6558 complete : function() { 6559 6560 var self = this; 6561 6562 var current = self.current; 6563 6564 self.revealImage( current, function() { 6565 current.isComplete = true; 6566 6567 current.$slide.addClass('fancybox-slide--complete'); 6568 6569 self.updateCursor(); 6570 6571 self.trigger( 'onComplete' ); 6572 6573 // Try to focus on the first focusable element, skip for images and iframes 6574 if ( current.opts.focus && !( current.type === 'image' || current.type === 'iframe' ) ) { 6575 self.focus(); 6576 } 6577 6578 }); 6579 6580 }, 6581 6582 6583 // Preload next and previous slides 6584 // ================================ 6585 6586 preload : function() { 6587 var self = this; 6588 var next, prev; 6589 6590 if ( self.group.length < 2 ) { 6591 return; 6592 } 6593 6594 next = self.slides[ self.currPos + 1 ]; 6595 prev = self.slides[ self.currPos - 1 ]; 6596 6597 if ( next && next.type === 'image' ) { 6598 self.loadSlide( next ); 6599 } 6600 6601 if ( prev && prev.type === 'image' ) { 6602 self.loadSlide( prev ); 6603 } 6604 6605 }, 6606 6607 6608 // Try to find and focus on the first focusable element 6609 // ==================================================== 6610 6611 focus : function() { 6612 var current = this.current; 6613 var $el; 6614 6615 $el = current && current.isComplete ? current.$slide.find('button,:input,[tabindex],a:not(".disabled")').filter(':visible:first') : null; 6616 6617 if ( !$el || !$el.length ) { 6618 $el = this.$refs.container; 6619 } 6620 6621 $el.focus(); 6622 6623 // Scroll position of wrapper element sometimes changes after focusing (IE) 6624 this.$refs.slider_wrap.scrollLeft(0); 6625 6626 // And the same goes for slide element 6627 if ( current ) { 6628 current.$slide.scrollTop(0); 6629 } 6630 6631 }, 6632 6633 6634 // Activates current instance - brings container to the front and enables keyboard, 6635 // notifies other instances about deactivating 6636 // ================================================================================= 6637 6638 activate : function () { 6639 var self = this; 6640 6641 // Deactivate all instances 6642 6643 $( '.fancybox-container' ).each(function () { 6644 var instance = $(this).data( 'FancyBox' ); 6645 6646 // Skip self and closing instances 6647 6648 if (instance && instance.uid !== self.uid && !instance.isClosing) { 6649 instance.trigger( 'onDeactivate' ); 6650 } 6651 6652 }); 6653 6654 if ( self.current ) { 6655 6656 if ( self.$refs.container.index() > 0 ) { 6657 self.$refs.container.prependTo( document.body ); 6658 } 6659 6660 self.updateControls(); 6661 } 6662 6663 self.trigger( 'onActivate' ); 6664 6665 self.addEvents(); 6666 6667 }, 6668 6669 6670 // Start closing procedure 6671 // This will start "zoom-out" animation if needed and clean everything up afterwards 6672 // ================================================================================= 6673 6674 close : function( e ) { 6675 6676 var self = this; 6677 var current = self.current; 6678 var duration = current.opts.speed; 6679 6680 var done = $.proxy(function() { 6681 6682 self.cleanUp( e ); // Now "this" is again our instance 6683 6684 }, this); 6685 6686 if ( self.isAnimating || self.isClosing ) { 6687 return false;
6688 } 6689 6690 // If beforeClose callback prevents closing, make sure content is centered 6691 if ( self.trigger( 'beforeClose', e ) === false ) { 6692 $.fancybox.stop( self.$refs.slider ); 6693 6694 requestAFrame(function() { 6695 self.update( true, true, 150 ); 6696 }); 6697 6698 return; 6699 } 6700 6701 self.isClosing = true; 6702 6703 if ( current.timouts ) { 6704 clearTimeout( current.timouts ); 6705 } 6706 6707 if ( e !== true) { 6708 $.fancybox.stop( self.$refs.slider ); 6709 } 6710 6711 self.$refs.container 6712 .removeClass('fancybox-container--active') 6713 .addClass('fancybox-container--closing'); 6714 6715 current.$slide 6716 .removeClass('fancybox-slide--complete') 6717 .siblings() 6718 .remove(); 6719 6720 6721 if ( !current.isMoved ) { 6722 current.$slide.css('overflow', 'visible'); 6723 } 6724 6725 // Remove all events 6726 // If there are multiple instances, they will be set again by "activate" method 6727 6728 self.removeEvents(); 6729 6730 // Clean up 6731 6732 self.hideLoading( current ); 6733 6734 self.hideControls(); 6735 6736 self.updateCursor(); 6737 6738 self.$refs.bg.css('transition-duration', duration + 'ms'); 6739 6740 this.$refs.container.removeClass( 'fancybox-container--ready' ); 6741 6742 if ( e === true ) { 6743 setTimeout( done, duration ); 6744 6745 } else if ( !self.zoomInOut( 'Out', duration, done ) ) { 6746 $.fancybox.animate( self.$refs.container, null, { opacity : 0 }, duration, "easeInSine", done ); 6747 } 6748 6749 }, 6750 6751 6752 // Final adjustments after removing the instance 6753 // ============================================= 6754 6755 cleanUp : function( e ) { 6756 var self = this, 6757 instance; 6758 6759 self.$refs.slider.children().trigger( 'onReset' ); 6760 6761 self.$refs.container.empty().remove(); 6762 6763 self.trigger( 'afterClose', e ); 6764 6765 self.current = null; 6766 6767 // Check if there are other instances 6768 instance = $.fancybox.getInstance(); 6769 6770 if ( instance ) { 6771 instance.activate(); 6772 6773 } else { 6774 6775 $( 'html' ).removeClass( 'fancybox-enabled' ); 6776 $( 'body' ).removeAttr( 'style' ); 6777 6778 $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); 6779 6780 $( '#fancybox-noscroll' ).remove(); 6781 6782 } 6783 6784 // Place back focus 6785 if ( self.$lastFocus ) { 6786 self.$lastFocus.focus(); 6787 } 6788 6789 }, 6790 6791 6792 // Call callback and trigger an event 6793 // ================================== 6794 6795 trigger : function( name, slide ) { 6796 var args = Array.prototype.slice.call(arguments, 1), 6797 self = this, 6798 obj = slide && slide.opts ? slide : self.current, 6799 rez; 6800 6801 if ( obj ) { 6802 args.unshift( obj ); 6803 6804 } else { 6805 obj = self; 6806 } 6807 6808 args.unshift( self ); 6809 6810 if ( $.isFunction( obj.opts[ name ] ) ) { 6811 rez = obj.opts[ name ].apply( obj, args ); 6812 } 6813 6814 if ( rez === false ) { 6815 return rez; 6816 } 6817 6818 if ( name === 'afterClose' ) { 6819 $( document ).trigger( name + '.fb', args ); 6820 6821 } else { 6822 self.$refs.container.trigger( name + '.fb', args ); 6823 } 6824 6825 }, 6826 6827 6828 // Toggle toolbar and caption 6829 // ========================== 6830 6831 toggleControls : function( force ) { 6832 6833 if ( this.isHiddenControls ) { 6834 this.updateControls( force ); 6835 6836 } else { 6837 this.hideControls(); 6838 } 6839 6840 6841 }, 6842 6843 6844 // Hide toolbar and caption 6845 // ======================== 6846 6847 hideControls : function () { 6848
6849 this.isHiddenControls = true; 6850 6851 this.$refs.container.removeClass('fancybox-show-controls'); 6852 6853 this.$refs.container.removeClass('fancybox-show-caption'); 6854 6855 }, 6856 6857 6858 // Update infobar values, navigation button states and reveal caption 6859 // ================================================================== 6860 6861 updateControls : function ( force ) { 6862 6863 var self = this; 6864 6865 var $container = self.$refs.container; 6866 var $caption = self.$refs.caption; 6867 6868 // Toggle infobar and buttons 6869 6870 var current = self.current; 6871 var index = current.index; 6872 var opts = current.opts; 6873 var caption = opts.caption; 6874 6875 if ( this.isHiddenControls && force !== true ) { 6876 return; 6877 } 6878 6879 this.isHiddenControls = false; 6880 6881 $container 6882 .addClass('fancybox-show-controls') 6883 .toggleClass('fancybox-show-infobar', !!opts.infobar && self.group.length > 1) 6884 .toggleClass('fancybox-show-buttons', !!opts.buttons ) 6885 .toggleClass('fancybox-is-modal', !!opts.modal ); 6886 6887 $('.fancybox-button--left', $container).toggleClass( 'fancybox-button--disabled', (!opts.loop && index <= 0 ) ); 6888 $('.fancybox-button--right', $container).toggleClass( 'fancybox-button--disabled', (!opts.loop && index >= self.group.length - 1) ); 6889 6890 $('.fancybox-button--play', $container).toggle( !!( opts.slideShow && self.group.length > 1) ); 6891 $('.fancybox-button--close', $container).toggle( !!opts.closeBtn ); 6892 6893 // Update infobar values 6894 6895 $('.js-fancybox-count', $container).html( self.group.length ); 6896 $('.js-fancybox-index', $container).html( index + 1 ); 6897 6898 // Recalculate content dimensions 6899 current.$slide.trigger( 'refresh' ); 6900 6901 // Reveal or create new caption 6902 if ( $caption ) { 6903 $caption.empty(); 6904 } 6905 6906 if ( caption && caption.length ) { 6907 $caption.html( caption ); 6908 6909 this.$refs.container.addClass( 'fancybox-show-caption '); 6910 6911 self.$caption = $caption; 6912 6913 } else { 6914 this.$refs.container.removeClass( 'fancybox-show-caption' ); 6915 } 6916 6917 } 6918 6919 }); 6920 6921 6922 $.fancybox = { 6923 6924 version : "3.0.47", 6925 defaults : defaults, 6926 6927 6928 // Get current instance and execute a command. 6929 // 6930 // Examples of usage: 6931 // 6932 // $instance = $.fancybox.getInstance(); 6933 // $.fancybox.getInstance().jumpTo( 1 ); 6934 // $.fancybox.getInstance( 'jumpTo', 1 ); 6935 // $.fancybox.getInstance( function() { 6936 // console.info( this.currIndex ); 6937 // }); 6938 // ====================================================== 6939 6940 getInstance : function ( command ) { 6941 var instance = $('.fancybox-container:not(".fancybox-container--closing"):first').data( 'FancyBox' ); 6942 var args = Array.prototype.slice.call(arguments, 1); 6943 6944 if ( instance instanceof FancyBox ) { 6945 6946 if ( $.type( command ) === 'string' ) { 6947 instance[ command ].apply( instance, args ); 6948 6949 } else if ( $.type( command ) === 'function' ) { 6950 command.apply( instance, args ); 6951 6952 } 6953 6954 return instance; 6955 } 6956 6957 return false; 6958 6959 }, 6960 6961 6962 // Create new instance 6963 // =================== 6964 6965 open : function ( items, opts, index ) { 6966 return new FancyBox( items, opts, index ); 6967 }, 6968 6969 6970 // Close current or all instances 6971 // ============================== 6972 6973 close : function ( all ) { 6974 var instance = this.getInstance(); 6975 6976 if ( instance ) { 6977 instance.close(); 6978 6979 // Try to find and close next instance 6980 6981 if ( all === true ) { 6982 this.close(); 6983 } 6984 } 6985 6986 }, 6987 6988 6989 // Test for the existence of touch events in the browser 6990 // Limit to mobile devices 6991 // ==================================================== 6992 6993 isTouch : document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent), 6994 6995 6996 // Detect if 'translate3d' support is available 6997 // ============================================ 6998 6999 use3d : (function() { 7000 var div = document.createElement('div'); 7001 7002 return window.getComputedStyle( div ).getPropertyValue('transform') && !(document.documentMode && document.documentMode <= 11); 7003 }()), 7004 7005 7006 // Helper function to get current visual state of an element 7007 // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] 7008 // ===================================================================== 7009 7010 getTranslate : function( $el ) { 7011 var position, matrix; 7012 7013 if ( !$el || !$el.length ) { 7014 return false;
7015 } 7016 7017 position = $el.get( 0 ).getBoundingClientRect(); 7018 matrix = $el.eq( 0 ).css('transform'); 7019 7020 if ( matrix && matrix.indexOf( 'matrix' ) !== -1 ) { 7021 matrix = matrix.split('(')[1]; 7022 matrix = matrix.split(')')[0]; 7023 matrix = matrix.split(','); 7024 } else { 7025 matrix = []; 7026 } 7027 7028 if ( matrix.length ) { 7029 7030 // If IE 7031 if ( matrix.length > 10 ) { 7032 matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ]; 7033 7034 } else { 7035 matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]]; 7036 } 7037 7038 matrix = matrix.map(parseFloat); 7039 7040 } else { 7041 matrix = [ 0, 0, 1, 1 ]; 7042 } 7043 7044 return { 7045 top : matrix[ 0 ], 7046 left : matrix[ 1 ], 7047 scaleX : matrix[ 2 ], 7048 scaleY : matrix[ 3 ], 7049 opacity : parseFloat( $el.css('opacity') ), 7050 width : position.width, 7051 height : position.height 7052 }; 7053 7054 }, 7055 7056 7057 // Shortcut for setting "translate3d" properties for element 7058 // Can set be used to set opacity, too 7059 // ======================================================== 7060 7061 setTranslate : function( $el, props ) { 7062 var str = ''; 7063 var css = {}; 7064 7065 if ( !$el || !props ) { 7066 return; 7067 } 7068 7069 if ( props.left !== undefined || props.top !== undefined ) { 7070 7071 str = ( props.left === undefined ? $el.position().top : props.left ) + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px'; 7072 7073 if ( this.use3d ) { 7074 str = 'translate3d(' + str + ', 0px)'; 7075 7076 } else { 7077 str = 'translate(' + str + ')'; 7078 } 7079 7080 } 7081 7082 if ( props.scaleX !== undefined && props.scaleY !== undefined ) { 7083 str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')'; 7084 } 7085 7086 if ( str.length ) { 7087 css.transform = str; 7088 } 7089 7090 if ( props.opacity !== undefined ) { 7091 css.opacity = props.opacity; 7092 } 7093 7094 if ( props.width !== undefined ) { 7095 css.width = props.width; 7096 } 7097 7098 if ( props.height !== undefined ) { 7099 css.height = props.height; 7100 } 7101 7102 return $el.css( css ); 7103 7104 }, 7105 7106 7107 // Common easings for entrances and exits 7108 // t: current time, b: begInnIng value, c: change In value, d: duration 7109 // ==================================================================== 7110 7111 easing : { 7112 easeOutCubic : function (t, b, c, d) { 7113 return c * ((t=t/d-1)*t*t + 1) + b; 7114 }, 7115 easeInCubic : function (t, b, c, d) { 7116 return c * (t/=d)*t*t + b; 7117 }, 7118 easeOutSine : function (t, b, c, d) { 7119 return c * Math.sin(t/d * (Math.PI/2)) + b; 7120 }, 7121 easeInSine : function (t, b, c, d) { 7122 return -c * Math.cos(t/d * (Math.PI/2)) + c + b; 7123 } 7124 }, 7125 7126 7127 // Stop fancyBox animation 7128 // ======================= 7129 7130 stop : function( $el ) { 7131 7132 $el.removeData( 'animateID' ); 7133 7134 }, 7135 7136 // Animate element using "translate3d" 7137 // Usage: 7138 // animate( element, start properties, end properties, duration, easing, callback ) 7139 // or 7140 // animate( element, start properties, end properties, duration, callback ) 7141 // ================================================================================= 7142 7143 animate : function( $el, from, to, duration, easing, done ) { 7144 7145 var self = this; 7146 7147 var lastTime = null; 7148 var animTime = 0; 7149 7150 var curr; 7151 var diff; 7152 var id; 7153 7154 var finish = function() { 7155 if ( to.scaleX !== undefined && to.scaleY !== undefined && from && from.width !== undefined && from.height !== undefined ) { 7156 to.width = from.width * to.scaleX; 7157 to.height = from.height * to.scaleY; 7158 7159 to.scaleX = 1; 7160 to.scaleY = 1; 7161 } 7162 7163 self.setTranslate( $el, to ); 7164 7165 done(); 7166 }; 7167 7168 var frame = function ( timestamp ) { 7169 curr = []; 7170 diff = 0; 7171 7172 // If "stop" method has been called on this element, then just stop 7173 if ( !$el.length || $el.data( 'animateID' ) !== id ) { 7174 return; 7175 } 7176 7177 timestamp = timestamp || Date.now(); 7178 7179 if ( lastTime ) { 7180 diff = timestamp - lastTime; 7181 } 7182 7183 lastTime = timestamp; 7184 animTime += diff; 7185 7186 // Are we done? 7187 if ( animTime >= duration ) { 7188 7189 finish(); 7190 7191 return; 7192 } 7193 7194 for ( var prop in to ) { 7195 7196 if ( to.hasOwnProperty( prop ) && from[ prop ] !== undefined ) { 7197 7198 if ( from[ prop ] == to[ prop ] ) { 7199 curr[ prop ] = to[ prop ]; 7200 7201 } else { 7202 curr[ prop ] = self.easing[ easing ]( animTime, from[ prop ], to[ prop ] - from[ prop ], duration ); 7203 } 7204 7205 } 7206 } 7207 7208 self.setTranslate( $el, curr ); 7209 7210 requestAFrame( frame ); 7211 }; 7212 7213 self.animateID = id = self.animateID === undefined ? 1 : self.animateID + 1; 7214 7215 $el.data( 'animateID', id ); 7216 7217 if ( done === undefined && $.type(easing) == 'function' ) { 7218 done = easing; 7219 easing = undefined; 7220 } 7221 7222 if ( !easing ) { 7223 easing = "easeOutCubic"; 7224 } 7225 7226 done = done || $.noop; 7227 7228 if ( from ) { 7229 this.setTranslate( $el, from ); 7230 7231 } else { 7232 7233 // We need current values to calculate change in time 7234 from = this.getTranslate( $el ); 7235 } 7236 7237 if ( duration ) { 7238 $el.show(); 7239 7240 requestAFrame( frame ); 7241 7242 } else { 7243 finish(); 7244 } 7245 7246 } 7247 7248 }; 7249 7250 7251 // Event handler for click event to "fancyboxed" links 7252 // =================================================== 7253 7254 function _run( e ) { 7255 var target = e.currentTarget, 7256 opts = e.data ? e.data.options : {}, 7257 items = e.data ? e.data.items : [], 7258 value = '', 7259 index = 0; 7260 7261 e.preventDefault(); 7262 e.stopPropagation(); 7263 7264 // Get all related items and find index for clicked one 7265 7266 if ( $(target).attr( 'data-fancybox' ) ) { 7267 value = $(target).data( 'fancybox' ); 7268 } 7269 7270 if ( value ) { 7271 items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox=' + value + ']' ); 7272 index = items.index( target ); 7273 7274 } else { 7275 items = [ target ]; 7276 } 7277 7278 $.fancybox.open( items, opts, index ); 7279 } 7280 7281 7282 // Create a jQuery plugin 7283 // ====================== 7284 7285 $.fn.fancybox = function (options) { 7286 7287 this.off('click.fb-start').on('click.fb-start', { 7288 items : this, 7289 options : options || {} 7290 }, _run); 7291 7292 return this; 7293 7294 }; 7295 7296 7297 // Self initializing plugin 7298 // ======================== 7299 7300 $(document).on('click.fb-start', '[data-fancybox]', _run); 7301 7302}(window, document, window.jQuery)); 7303 7304// ========================================================================== 7305// 7306// Media 7307// Adds additional media type support 7308// 7309// ========================================================================== 7310;(function ($) { 7311 7312 'use strict'; 7313 7314 // Formats matching url to final form 7315 7316 var format = function (url, rez, params) { 7317 if ( !url ) { 7318 return; 7319 } 7320 7321 params = params || ''; 7322 7323 if ( $.type(params) === "object" ) { 7324 params = $.param(params, true); 7325 } 7326 7327 $.each(rez, function (key, value) { 7328 url = url.replace('$' + key, value || ''); 7329 }); 7330 7331 if (params.length) { 7332 url += (url.indexOf('?') > 0 ? '&' : '?') + params; 7333 } 7334 7335 return url; 7336 }; 7337 7338 // Object containing properties for each media type 7339 7340 var media = { 7341 youtube: { 7342 matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, 7343 params: { 7344 autoplay: 1, 7345 autohide: 1, 7346 fs: 1, 7347 rel: 0, 7348 hd: 1, 7349 wmode: 'transparent', 7350 enablejsapi: 1, 7351 html5: 1 7352 }, 7353 paramPlace : 8, 7354 type: 'iframe', 7355 url: '//www.youtube.com/embed/$4', 7356 thumb: '//img.youtube.com/vi/$4/hqdefault.jpg' 7357 }, 7358 7359 vimeo: { 7360 matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, 7361 params: { 7362 autoplay: 1, 7363 hd: 1, 7364 show_title: 1, 7365 show_byline: 1, 7366 show_portrait: 0, 7367 fullscreen: 1, 7368 api: 1 7369 }, 7370 paramPlace : 3, 7371 type: 'iframe', 7372 url: '//player.vimeo.com/video/$2' 7373 }, 7374 7375 metacafe: { 7376 matcher: /metacafe.com\/watch\/(\d+)\/(.*)?/, 7377 type: 'iframe', 7378 url: '//www.metacafe.com/embed/$1/?ap=1' 7379 }, 7380 7381 dailymotion: { 7382 matcher: /dailymotion.com\/video\/(.*)\/?(.*)/, 7383 params: { 7384 additionalInfos: 0, 7385 autoStart: 1 7386 }, 7387 type: 'iframe', 7388 url: '//www.dailymotion.com/embed/video/$1' 7389 }, 7390 7391 vine: { 7392 matcher: /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/, 7393 type: 'iframe', 7394 url: '//vine.co/v/$1/embed/simple' 7395 }, 7396 7397 instagram: { 7398 matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, 7399 type: 'image', 7400 url: '//$1/p/$2/media/?size=l' 7401 }, 7402 7403 // Examples: 7404 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 7405 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 7406 // https://www.google.lv/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 7407 google_maps: { 7408 matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, 7409 type: 'iframe', 7410 url: function (rez) { 7411 return '//maps.google.' + rez[2] + '/?ll=' + ( rez[9] ? rez[9] + '&z=' + Math.floor( rez[10] ) + ( rez[12] ? rez[12].replace(/^\//, "&") : '' ) : rez[12] ) + '&output=' + ( rez[12] && rez[12].indexOf('layer=c') > 0 ? 'svembed' : 'embed' ); 7412 } 7413 } 7414 }; 7415 7416 $(document).on('onInit.fb', function (e, instance) { 7417 7418 $.each(instance.group, function( i, item ) { 7419 7420 var url = item.src || '', 7421 type = false, 7422 thumb, 7423 rez, 7424 params,
7425 urlParams, 7426 o, 7427 provider; 7428 7429 // Skip items that already have content type 7430 if ( item.type ) { 7431 return; 7432 } 7433 7434 // Look for any matching media type 7435 7436 $.each(media, function ( n, el ) { 7437 rez = url.match(el.matcher); 7438 o = {}; 7439 provider = n; 7440 7441 if (!rez) { 7442 return; 7443 } 7444 7445 type = el.type; 7446 7447 if ( el.paramPlace && rez[ el.paramPlace ] ) { 7448 urlParams = rez[ el.paramPlace ]; 7449 7450 if ( urlParams[ 0 ] == '?' ) { 7451 urlParams = urlParams.substring(1); 7452 } 7453 7454 urlParams = urlParams.split('&'); 7455 7456 for ( var m = 0; m < urlParams.length; ++m ) { 7457 var p = urlParams[ m ].split('=', 2); 7458 7459 if ( p.length == 2 ) { 7460 o[ p[0] ] = decodeURIComponent( p[1].replace(/\+/g, " ") ); 7461 } 7462 } 7463 } 7464 7465 params = $.extend( true, {}, el.params, item.opts[ n ], o ); 7466 7467 url = $.type(el.url) === "function" ? el.url.call(this, rez, params, item) : format(el.url, rez, params); 7468 thumb = $.type(el.thumb) === "function" ? el.thumb.call(this, rez, params, item) : format(el.thumb, rez); 7469 7470 if ( provider === 'vimeo' ) { 7471 url = url.replace('&%23', '#'); 7472 } 7473 7474 return false; 7475 }); 7476 7477 // If it is found, then change content type and update the url 7478 7479 if ( type ) { 7480 item.src = url; 7481 item.type = type; 7482 7483 if ( !item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length ) ) { 7484 item.opts.thumb = thumb; 7485 } 7486 7487 if ( type === 'iframe' ) { 7488 $.extend(true, item.opts, { 7489 iframe : { 7490 preload : false, 7491 scrolling : "no" 7492 }, 7493 smallBtn : false, 7494 closeBtn : true, 7495 fullScreen : false, 7496 slideShow : false 7497 }); 7498 7499 item.opts.slideClass += ' fancybox-slide--video'; 7500 } 7501 7502 } else { 7503 7504 // If no content type is found, then set it to `iframe` as fallback 7505 item.type = 'iframe'; 7506 7507 } 7508 7509 }); 7510 7511 }); 7512 7513}(window.jQuery)); 7514 7515// ========================================================================== 7516// 7517// Guestures 7518// Adds touch guestures, handles click and tap events 7519// 7520// ========================================================================== 7521;(function (window, document, $) { 7522 'use strict'; 7523 7524 var requestAFrame = (function() { 7525 return window.requestAnimationFrame || 7526 window.webkitRequestAnimationFrame || 7527 window.mozRequestAnimationFrame || 7528 function( callback ) { 7529 window.setTimeout(callback, 1000 / 60); }; 7530 })(); 7531 7532 7533 var pointers = function( e ) { 7534 var result = []; 7535 7536 e = e.originalEvent || e || window.e; 7537 e = e.touches && e.touches.length ? e.touches : ( e.changedTouches && e.changedTouches.length ? e.changedTouches : [ e ] ); 7538 7539 for ( var key in e ) { 7540 7541 if ( e[ key ].pageX ) { 7542 result.push( { x : e[ key ].pageX, y : e[ key ].pageY } ); 7543 7544 } else if ( e[ key ].clientX ) { 7545 result.push( { x : e[ key ].clientX, y : e[ key ].clientY } ); 7546 } 7547 } 7548 7549 return result; 7550 }; 7551 7552 var distance = function( point2, point1, what ) { 7553 7554 if ( !point1 || !point2 ) { 7555 return 0; 7556 } 7557 7558 if ( what === 'x' ) { 7559 return point2.x - point1.x; 7560 7561 } else if ( what === 'y' ) { 7562 return point2.y - point1.y; 7563 } 7564 7565 return Math.sqrt( Math.pow( point2.x - point1.x, 2 ) + Math.pow( point2.y - point1.y, 2 ) ); 7566 7567 }; 7568 7569 var isClickable = function( $el ) { 7570 7571 return $el.is('a') || $el.is('button') || $el.is('input') || $el.is('select') || $el.is('textarea') || $.isFunction( $el.get(0).onclick ); 7572 7573 }; 7574 7575 var hasScrollbars = function( el ) { 7576 var overflowY = window.getComputedStyle( el )['overflow-y']; 7577 var overflowX = window.getComputedStyle( el )['overflow-x']; 7578 7579 var vertical = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight; 7580 var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth; 7581 7582 return vertical || horizontal; 7583 }; 7584 7585 var isScrollable = function ( $el ) { 7586 7587 var rez = false; 7588 7589 while ( true ) { 7590 rez = hasScrollbars( $el.get(0) ); 7591 7592 if ( rez ) { 7593 break; 7594 } 7595 7596 $el = $el.parent(); 7597 7598 if ( !$el.length || $el.hasClass('fancybox-slider') || $el.is('body') ) { 7599 break; 7600 } 7601 7602 } 7603 7604 return rez; 7605 7606 }; 7607 7608 7609 var Guestures = function ( instance ) { 7610 7611 var self = this; 7612 7613 self.instance = instance; 7614 7615 self.$wrap = instance.$refs.slider_wrap; 7616 self.$slider = instance.$refs.slider; 7617 self.$container = instance.$refs.container; 7618 7619 self.destroy(); 7620 7621 self.$wrap.on('touchstart.fb mousedown.fb', $.proxy(self, "ontouchstart")); 7622 7623 }; 7624 7625 Guestures.prototype.destroy = function() { 7626 7627 this.$wrap.off('touchstart.fb mousedown.fb touchmove.fb mousemove.fb touchend.fb touchcancel.fb mouseup.fb mouseleave.fb'); 7628 7629 }; 7630 7631 Guestures.prototype.ontouchstart = function( e ) { 7632 7633 var self = this; 7634 7635 var $target = $( e.target ); 7636 var instance = self.instance; 7637 var current = instance.current; 7638 var $content = current.$content || current.$placeholder; 7639 7640 self.startPoints = pointers( e ); 7641 7642 self.$target = $target; 7643 self.$content = $content; 7644 7645 self.canvasWidth = Math.round( current.$slide[0].clientWidth ); 7646 self.canvasHeight = Math.round( current.$slide[0].clientHeight ); 7647 7648 self.startEvent = e; 7649 7650 // Skip if clicked on the scrollbar 7651 if ( e.originalEvent.clientX > self.canvasWidth + current.$slide.offset().left ) { 7652 return true; 7653 } 7654 7655 // Ignore taping on links, buttons and scrollable items 7656 if ( isClickable( $target ) || isClickable( $target.parent() ) || ( isScrollable( $target ) ) ) { 7657 return; 7658 } 7659 7660 // If "touch" is disabled, then handle click event 7661 if ( !current.opts.touch ) { 7662 self.endPoints = self.startPoints; 7663 7664 return self.ontap(); 7665 } 7666 7667 // Ignore right click 7668 if ( e.originalEvent && e.originalEvent.button == 2 ) { 7669 return; 7670 } 7671 7672 e.stopPropagation(); 7673 e.preventDefault(); 7674 7675 if ( !current || self.instance.isAnimating || self.instance.isClosing ) { 7676 return; 7677 } 7678 7679 // Prevent zooming if already swiping 7680 if ( !self.startPoints || ( self.startPoints.length > 1 && !current.isMoved ) ) { 7681 return; 7682 } 7683 7684 self.$wrap.off('touchmove.fb mousemove.fb', $.proxy(self, "ontouchmove")); 7685 self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(self, "ontouchend")); 7686 7687 self.$wrap.on('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(self, "ontouchend")); 7688 self.$wrap.on('touchmove.fb mousemove.fb', $.proxy(self, "ontouchmove")); 7689 7690 self.startTime = new Date().getTime(); 7691 self.distanceX = self.distanceY = self.distance = 0; 7692 7693 self.canTap = false;
7694 self.isPanning = false; 7695 self.isSwiping = false; 7696 self.isZooming = false; 7697 7698 self.sliderStartPos = $.fancybox.getTranslate( self.$slider ); 7699 7700 self.contentStartPos = $.fancybox.getTranslate( self.$content ); 7701 self.contentLastPos = null; 7702 7703 if ( self.startPoints.length === 1 && !self.isZooming ) { 7704 self.canTap = current.isMoved; 7705 7706 if ( current.type === 'image' && ( self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1 ) ) { 7707 7708 $.fancybox.stop( self.$content ); 7709 7710 self.isPanning = true; 7711 7712 } else { 7713 7714 $.fancybox.stop( self.$slider ); 7715 7716 self.isSwiping = true; 7717 } 7718 7719 self.$container.addClass('fancybox-controls--isGrabbing'); 7720 7721 } 7722 7723 if ( self.startPoints.length === 2 && current.isMoved && !current.hasError && current.type === 'image' && ( current.isLoaded || current.$ghost ) ) { 7724 7725 self.isZooming = true; 7726 7727 self.isSwiping = false; 7728 self.isPanning = false; 7729 7730 $.fancybox.stop( self.$content ); 7731 7732 self.centerPointStartX = ( ( self.startPoints[0].x + self.startPoints[1].x ) * 0.5 ) - $(window).scrollLeft(); 7733 self.centerPointStartY = ( ( self.startPoints[0].y + self.startPoints[1].y ) * 0.5 ) - $(window).scrollTop(); 7734 7735 self.percentageOfImageAtPinchPointX = ( self.centerPointStartX - self.contentStartPos.left ) / self.contentStartPos.width; 7736 self.percentageOfImageAtPinchPointY = ( self.centerPointStartY - self.contentStartPos.top ) / self.contentStartPos.height; 7737 7738 self.startDistanceBetweenFingers = distance( self.startPoints[0], self.startPoints[1] ); 7739 } 7740 7741 }; 7742 7743 Guestures.prototype.ontouchmove = function( e ) { 7744 7745 var self = this; 7746 7747 e.preventDefault(); 7748 7749 self.newPoints = pointers( e ); 7750 7751 if ( !self.newPoints || !self.newPoints.length ) { 7752 return; 7753 } 7754 7755 self.distanceX = distance( self.newPoints[0], self.startPoints[0], 'x' ); 7756 self.distanceY = distance( self.newPoints[0], self.startPoints[0], 'y' ); 7757 7758 self.distance = distance( self.newPoints[0], self.startPoints[0] ); 7759 7760 // Skip false ontouchmove events (Chrome) 7761 if ( self.distance > 0 ) { 7762 7763 if ( self.isSwiping ) { 7764 self.onSwipe(); 7765 7766 } else if ( self.isPanning ) { 7767 self.onPan(); 7768 7769 } else if ( self.isZooming ) { 7770 self.onZoom(); 7771 } 7772 7773 } 7774 7775 }; 7776 7777 Guestures.prototype.onSwipe = function() { 7778 7779 var self = this; 7780 7781 var swiping = self.isSwiping; 7782 var left = self.sliderStartPos.left; 7783 var angle; 7784 7785 if ( swiping === true ) { 7786 7787 if ( Math.abs( self.distance ) > 10 ) { 7788 7789 if ( self.instance.group.length < 2 ) { 7790 self.isSwiping = 'y'; 7791 7792 } else if ( !self.instance.current.isMoved || self.instance.opts.touch.vertical === false || ( self.instance.opts.touch.vertical === 'auto' && $( window ).width() > 800 ) ) { 7793 self.isSwiping = 'x'; 7794 7795 } else { 7796 angle = Math.abs( Math.atan2( self.distanceY, self.distanceX ) * 180 / Math.PI ); 7797 7798 self.isSwiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x'; 7799 } 7800 7801 self.canTap = false; 7802 7803 self.instance.current.isMoved = false; 7804 7805 // Reset points to avoid jumping, because we dropped first swipes to calculate the angle 7806 self.startPoints = self.newPoints; 7807 } 7808 7809 } else { 7810 7811 if ( swiping == 'x' ) { 7812 7813 // Sticky edges 7814 if ( !self.instance.current.opts.loop && self.instance.current.index === 0 && self.distanceX > 0 ) { 7815 left = left + Math.pow( self.distanceX, 0.8 ); 7816 7817 } else if ( !self.instance.current.opts.loop &&self.instance.current.index === self.instance.group.length - 1 && self.distanceX < 0 ) { 7818 left = left - Math.pow( -self.distanceX, 0.8 ); 7819 7820 } else { 7821 left = left + self.distanceX; 7822 } 7823 7824 } 7825 7826 self.sliderLastPos = { 7827 top : swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY, 7828 left : left 7829 }; 7830 7831 requestAFrame(function() { 7832 $.fancybox.setTranslate( self.$slider, self.sliderLastPos ); 7833 }); 7834 } 7835 7836 }; 7837 7838 Guestures.prototype.onPan = function() { 7839 7840 var self = this; 7841 7842 var newOffsetX, newOffsetY, newPos; 7843 7844 self.canTap = false; 7845 7846 if ( self.contentStartPos.width > self.canvasWidth ) { 7847 newOffsetX = self.contentStartPos.left + self.distanceX; 7848 7849 } else { 7850 newOffsetX = self.contentStartPos.left; 7851 } 7852 7853 newOffsetY = self.contentStartPos.top + self.distanceY; 7854 7855 newPos = self.limitMovement( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); 7856 7857 newPos.scaleX = self.contentStartPos.scaleX; 7858 newPos.scaleY = self.contentStartPos.scaleY; 7859 7860 self.contentLastPos = newPos; 7861 7862 requestAFrame(function() { 7863 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 7864 }); 7865 }; 7866 7867 // Make panning sticky to the edges 7868 Guestures.prototype.limitMovement = function( newOffsetX, newOffsetY, newWidth, newHeight ) { 7869 7870 var self = this; 7871 7872 var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY; 7873 7874 var canvasWidth = self.canvasWidth; 7875 var canvasHeight = self.canvasHeight; 7876 7877 var currentOffsetX = self.contentStartPos.left; 7878 var currentOffsetY = self.contentStartPos.top; 7879 7880 var distanceX = self.distanceX; 7881 var distanceY = self.distanceY; 7882 7883 // Slow down proportionally to traveled distance 7884 7885 minTranslateX = Math.max(0, canvasWidth * 0.5 - newWidth * 0.5 ); 7886 minTranslateY = Math.max(0, canvasHeight * 0.5 - newHeight * 0.5 ); 7887 7888 maxTranslateX = Math.min( canvasWidth - newWidth, canvasWidth * 0.5 - newWidth * 0.5 ); 7889 maxTranslateY = Math.min( canvasHeight - newHeight, canvasHeight * 0.5 - newHeight * 0.5 ); 7890 7891 if ( newWidth > canvasWidth ) { 7892 7893 // -> 7894 if ( distanceX > 0 && newOffsetX > minTranslateX ) { 7895 newOffsetX = minTranslateX - 1 + Math.pow( -minTranslateX + currentOffsetX + distanceX, 0.8 ) || 0; 7896 } 7897 7898 // <- 7899 if ( distanceX < 0 && newOffsetX < maxTranslateX ) { 7900 newOffsetX = maxTranslateX + 1 - Math.pow( maxTranslateX - currentOffsetX - distanceX, 0.8 ) || 0; 7901 } 7902 7903 } 7904 7905 if ( newHeight > canvasHeight ) { 7906 7907 // \/ 7908 if ( distanceY > 0 && newOffsetY > minTranslateY ) { 7909 newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8 ) || 0; 7910 } 7911 7912 // /\ 7913 if ( distanceY < 0 && newOffsetY < maxTranslateY ) { 7914 newOffsetY = maxTranslateY + 1 - Math.pow ( maxTranslateY - currentOffsetY - distanceY, 0.8 ) || 0; 7915 } 7916 7917 } 7918 7919 return { 7920 top : newOffsetY, 7921 left : newOffsetX 7922 }; 7923 7924 }; 7925 7926 7927 Guestures.prototype.limitPosition = function( newOffsetX, newOffsetY, newWidth, newHeight ) { 7928 7929 var self = this; 7930 7931 var canvasWidth = self.canvasWidth; 7932 var canvasHeight = self.canvasHeight; 7933 7934 if ( newWidth > canvasWidth ) { 7935 newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; 7936 newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX; 7937 7938 } else { 7939 7940 // Center horizontally 7941 newOffsetX = Math.max( 0, canvasWidth / 2 - newWidth / 2 ); 7942 7943 } 7944 7945 if ( newHeight > canvasHeight ) { 7946 newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; 7947 newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY; 7948 7949 } else { 7950 7951 // Center vertically 7952 newOffsetY = Math.max( 0, canvasHeight / 2 - newHeight / 2 ); 7953 7954 } 7955 7956 return { 7957 top : newOffsetY, 7958 left : newOffsetX 7959 }; 7960 7961 }; 7962 7963 Guestures.prototype.onZoom = function() { 7964 7965 var self = this; 7966 7967 // Calculate current distance between points to get pinch ratio and new width and height 7968 7969 var currentWidth = self.contentStartPos.width; 7970 var currentHeight = self.contentStartPos.height; 7971 7972 var currentOffsetX = self.contentStartPos.left; 7973 var currentOffsetY = self.contentStartPos.top; 7974 7975 var endDistanceBetweenFingers = distance( self.newPoints[0], self.newPoints[1] ); 7976 7977 var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers; 7978 7979 var newWidth = Math.floor( currentWidth * pinchRatio ); 7980 var newHeight = Math.floor( currentHeight * pinchRatio ); 7981 7982 // This is the translation due to pinch-zooming 7983 var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; 7984 var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; 7985 7986 //Point between the two touches 7987 7988 var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft(); 7989 var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop(); 7990 7991 // And this is the translation due to translation of the centerpoint 7992 // between the two fingers 7993 7994 var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; 7995 var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; 7996 7997 // The new offset is the old/current one plus the total translation 7998 7999 var newOffsetX = currentOffsetX + ( translateFromZoomingX + translateFromTranslatingX ); 8000 var newOffsetY = currentOffsetY + ( translateFromZoomingY + translateFromTranslatingY ); 8001 8002 var newPos = { 8003 top : newOffsetY, 8004 left : newOffsetX, 8005 scaleX : self.contentStartPos.scaleX * pinchRatio, 8006 scaleY : self.contentStartPos.scaleY * pinchRatio 8007 }; 8008 8009 self.canTap = false;
8010 8011 self.newWidth = newWidth; 8012 self.newHeight = newHeight; 8013 8014 self.contentLastPos = newPos; 8015 8016 requestAFrame(function() { 8017 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 8018 }); 8019 8020 }; 8021 8022 Guestures.prototype.ontouchend = function( e ) { 8023 8024 var self = this; 8025 8026 var current = self.instance.current; 8027 8028 var dMs = Math.max( (new Date().getTime() ) - self.startTime, 1); 8029 8030 var swiping = self.isSwiping; 8031 var panning = self.isPanning; 8032 var zooming = self.isZooming; 8033 8034 self.endPoints = pointers( e ); 8035 8036 self.$container.removeClass('fancybox-controls--isGrabbing'); 8037 8038 self.$wrap.off('touchmove.fb mousemove.fb', $.proxy(this, "ontouchmove")); 8039 self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(this, "ontouchend")); 8040 8041 self.isSwiping = false; 8042 self.isPanning = false; 8043 self.isZooming = false; 8044 8045 if ( self.canTap ) { 8046 return self.ontap(); 8047 } 8048 8049 // Speed in px/ms 8050 self.velocityX = self.distanceX / dMs * 0.5; 8051 self.velocityY = self.distanceY / dMs * 0.5; 8052 8053 self.speed = current.opts.speed || 330; 8054 8055 self.speedX = Math.max( self.speed * 0.75, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityX ) ) * self.speed ) ); 8056 self.speedY = Math.max( self.speed * 0.75, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityY ) ) * self.speed ) ); 8057 8058 if ( panning ) { 8059 self.endPanning(); 8060 8061 } else if ( zooming ) { 8062 self.endZooming(); 8063 8064 } else { 8065 self.endSwiping( swiping ); 8066 } 8067 8068 return; 8069 }; 8070 8071 Guestures.prototype.endSwiping = function( swiping ) { 8072 8073 var self = this; 8074 8075 // Close if swiped vertically / navigate if horizontally 8076 8077 if ( swiping == 'y' && Math.abs( self.distanceY ) > 50 ) { 8078 8079 // Continue vertical movement 8080 8081 $.fancybox.animate( self.$slider, null, { 8082 top : self.sliderStartPos.top + self.distanceY + self.velocityY * 150, 8083 left : self.sliderStartPos.left, 8084 opacity : 0 8085 }, self.speedY ); 8086 8087 self.instance.close( true ); 8088 8089 } else if ( swiping == 'x' && self.distanceX > 50 ) { 8090 self.instance.previous( self.speedX ); 8091 8092 } else if ( swiping == 'x' && self.distanceX < -50 ) { 8093 self.instance.next( self.speedX ); 8094 8095 } else { 8096 8097 // Move back to center 8098 self.instance.update( false, true, 150 ); 8099 8100 } 8101 8102 }; 8103 8104 Guestures.prototype.endPanning = function() { 8105 8106 var self = this; 8107 var newOffsetX, newOffsetY, newPos; 8108 8109 if ( !self.contentLastPos ) { 8110 return; 8111 } 8112 8113 newOffsetX = self.contentLastPos.left + ( self.velocityX * self.speed * 2 ); 8114 newOffsetY = self.contentLastPos.top + ( self.velocityY * self.speed * 2 ); 8115 8116 newPos = self.limitPosition( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); 8117 8118 newPos.width = self.contentStartPos.width; 8119 newPos.height = self.contentStartPos.height; 8120 8121 $.fancybox.animate( self.$content, null, newPos, self.speed, "easeOutSine" ); 8122 8123 }; 8124 8125 8126 Guestures.prototype.endZooming = function() { 8127 8128 var self = this; 8129 8130 var current = self.instance.current; 8131 8132 var newOffsetX, newOffsetY, newPos, reset; 8133 8134 var newWidth = self.newWidth; 8135 var newHeight = self.newHeight; 8136 8137 if ( !self.contentLastPos ) { 8138 return; 8139 } 8140 8141 newOffsetX = self.contentLastPos.left; 8142 newOffsetY = self.contentLastPos.top; 8143 8144 reset = { 8145 top : newOffsetY, 8146 left : newOffsetX, 8147 width : newWidth, 8148 height : newHeight, 8149 scaleX : 1, 8150 scaleY : 1 8151 }; 8152 8153 // Reset scalex/scaleY values; this helps for perfomance and does not break animation 8154 $.fancybox.setTranslate( self.$content, reset ); 8155 8156 if ( newWidth < self.canvasWidth && newHeight < self.canvasHeight ) { 8157 self.instance.scaleToFit( 150 ); 8158 8159 } else if ( newWidth > current.width || newHeight > current.height ) { 8160 self.instance.scaleToActual( self.centerPointStartX, self.centerPointStartY, 150 ); 8161 8162 } else { 8163 8164 newPos = self.limitPosition( newOffsetX, newOffsetY, newWidth, newHeight ); 8165 8166 $.fancybox.animate( self.$content, null, newPos, self.speed, "easeOutSine" ); 8167 8168 } 8169 8170 }; 8171 8172 Guestures.prototype.ontap = function() { 8173 8174 var self = this; 8175 8176 var instance = self.instance; 8177 var current = instance.current; 8178 8179 var x = self.endPoints[0].x; 8180 var y = self.endPoints[0].y; 8181 8182 x = x - self.$wrap.offset().left; 8183 y = y - self.$wrap.offset().top; 8184 8185 // Stop slideshow 8186 if ( instance.SlideShow && instance.SlideShow.isActive ) { 8187 instance.SlideShow.stop(); 8188 } 8189 8190 if ( !$.fancybox.isTouch ) { 8191 8192 if ( current.opts.closeClickOutside && self.$target.is('.fancybox-slide') ) { 8193 instance.close( self.startEvent ); 8194 8195 return; 8196 } 8197 8198 if ( current.type == 'image' && current.isMoved ) { 8199 8200 if ( instance.canPan() ) { 8201 instance.scaleToFit(); 8202 8203 } else if ( instance.isScaledDown() ) { 8204 instance.scaleToActual( x, y ); 8205 8206 } else if ( instance.group.length < 2 ) { 8207 instance.close( self.startEvent ); 8208 } 8209 8210 } 8211 8212 return; 8213 } 8214 8215 8216 // Double tap 8217 if ( self.tapped ) { 8218 8219 clearTimeout( self.tapped ); 8220 8221 self.tapped = null; 8222 8223 if ( Math.abs( x - self.x ) > 50 || Math.abs( y - self.y ) > 50 || !current.isMoved ) { 8224 return this; 8225 } 8226 8227 if ( current.type == 'image' && ( current.isLoaded || current.$ghost ) ) { 8228 8229 if ( instance.canPan() ) { 8230 instance.scaleToFit(); 8231 8232 } else if ( instance.isScaledDown() ) { 8233 instance.scaleToActual( x, y ); 8234 8235 } 8236 8237 } 8238 8239 } else { 8240 8241 // Single tap 8242 8243 self.x = x; 8244 self.y = y; 8245 8246 self.tapped = setTimeout(function() { 8247 self.tapped = null; 8248 8249 instance.toggleControls( true ); 8250 8251 }, 300); 8252 } 8253 8254 return this; 8255 }; 8256 8257 $(document).on('onActivate.fb', function (e, instance) { 8258 8259 if ( instance && !instance.Guestures ) { 8260 instance.Guestures = new Guestures( instance ); 8261 } 8262 8263 }); 8264 8265 $(document).on('beforeClose.fb', function (e, instance) { 8266 8267 if ( instance && instance.Guestures ) { 8268 instance.Guestures.destroy(); 8269 } 8270 8271 }); 8272 8273 8274}(window, document, window.jQuery)); 8275 8276// ========================================================================== 8277// 8278// SlideShow 8279// Enables slideshow functionality 8280// 8281// Example of usage: 8282// $.fancybox.getInstance().slideShow.start() 8283// 8284// ========================================================================== 8285;(function (document, $) { 8286 'use strict'; 8287 8288 var SlideShow = function( instance ) { 8289 8290 this.instance = instance; 8291 8292 this.init(); 8293 8294 }; 8295 8296 $.extend( SlideShow.prototype, { 8297 timer : null, 8298 isActive : false, 8299 $button : null, 8300 speed : 3000, 8301 8302 init : function() { 8303 var self = this; 8304 8305 self.$button = $('<button data-fancybox-play class="fancybox-button fancybox-button--play" title="Slideshow (P)"></button>') 8306 .appendTo( self.instance.$refs.buttons ); 8307 8308 self.instance.$refs.container.on('click', '[data-fancybox-play]', function() {
8309 self.toggle(); 8310 }); 8311 8312 }, 8313 8314 set : function() { 8315 var self = this; 8316 8317 // Check if reached last element 8318 if ( self.instance && self.instance.current && (self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { 8319 8320 self.timer = setTimeout(function() { 8321 self.instance.next(); 8322 8323 }, self.instance.current.opts.slideShow.speed || self.speed); 8324 8325 } else { 8326 self.stop(); 8327 } 8328 }, 8329 8330 clear : function() { 8331 var self = this; 8332 8333 clearTimeout( self.timer ); 8334 8335 self.timer = null; 8336 }, 8337 8338 start : function() { 8339 var self = this; 8340 8341 self.stop(); 8342 8343 if ( self.instance && self.instance.current && ( self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { 8344 8345 self.instance.$refs.container.on({ 8346 'beforeLoad.fb.player' : $.proxy(self, "clear"), 8347 'onComplete.fb.player' : $.proxy(self, "set"), 8348 }); 8349 8350 self.isActive = true; 8351 8352 if ( self.instance.current.isComplete ) { 8353 self.set(); 8354 } 8355 8356 self.instance.$refs.container.trigger('onPlayStart'); 8357 8358 self.$button.addClass('fancybox-button--pause'); 8359 } 8360 8361 }, 8362 8363 stop: function() { 8364 var self = this; 8365 8366 self.clear(); 8367 8368 self.instance.$refs.container 8369 .trigger('onPlayEnd') 8370 .off('.player'); 8371 8372 self.$button.removeClass('fancybox-button--pause'); 8373 8374 self.isActive = false; 8375 }, 8376 8377 toggle : function() { 8378 var self = this; 8379 8380 if ( self.isActive ) { 8381 self.stop(); 8382 8383 } else { 8384 self.start(); 8385 } 8386 } 8387 8388 }); 8389 8390 $(document).on('onInit.fb', function(e, instance) { 8391 8392 if ( instance && instance.group.length > 1 && !!instance.opts.slideShow && !instance.SlideShow ) { 8393 instance.SlideShow = new SlideShow( instance ); 8394 } 8395 8396 }); 8397 8398 $(document).on('beforeClose.fb onDeactivate.fb', function(e, instance) { 8399 8400 if ( instance && instance.SlideShow ) { 8401 instance.SlideShow.stop(); 8402 } 8403 8404 }); 8405 8406}(document, window.jQuery)); 8407 8408// ========================================================================== 8409// 8410// FullScreen 8411// Adds fullscreen functionality 8412// 8413// ========================================================================== 8414;(function (document, $) { 8415 'use strict'; 8416 8417 // Collection of methods supported by user browser 8418 var fn = (function () { 8419 8420 var fnMap = [ 8421 [ 8422 'requestFullscreen', 8423 'exitFullscreen', 8424 'fullscreenElement', 8425 'fullscreenEnabled', 8426 'fullscreenchange', 8427 'fullscreenerror' 8428 ], 8429 // new WebKit 8430 [ 8431 'webkitRequestFullscreen', 8432 'webkitExitFullscreen', 8433 'webkitFullscreenElement', 8434 'webkitFullscreenEnabled', 8435 'webkitfullscreenchange', 8436 'webkitfullscreenerror' 8437 8438 ], 8439 // old WebKit (Safari 5.1) 8440 [ 8441 'webkitRequestFullScreen', 8442 'webkitCancelFullScreen', 8443 'webkitCurrentFullScreenElement', 8444 'webkitCancelFullScreen', 8445 'webkitfullscreenchange', 8446 'webkitfullscreenerror' 8447 8448 ], 8449 [ 8450 'mozRequestFullScreen', 8451 'mozCancelFullScreen', 8452 'mozFullScreenElement', 8453 'mozFullScreenEnabled', 8454 'mozfullscreenchange', 8455 'mozfullscreenerror' 8456 ], 8457 [ 8458 'msRequestFullscreen', 8459 'msExitFullscreen', 8460 'msFullscreenElement', 8461 'msFullscreenEnabled', 8462 'MSFullscreenChange', 8463 'MSFullscreenError' 8464 ] 8465 ]; 8466 8467 var val; 8468 var ret = {}; 8469 var i, j; 8470 8471 for ( i = 0; i < fnMap.length; i++ ) { 8472 val = fnMap[ i ]; 8473 8474 if ( val && val[ 1 ] in document ) { 8475 for ( j = 0; j < val.length; j++ ) { 8476 ret[ fnMap[ 0 ][ j ] ] = val[ j ]; 8477 } 8478 8479 return ret; 8480 } 8481 } 8482 8483 return false; 8484 })(); 8485 8486 if ( !fn ) { 8487 return; 8488 } 8489 8490 var FullScreen = { 8491 request : function ( elem ) { 8492 8493 elem = elem || document.documentElement; 8494 8495 elem[ fn.requestFullscreen ]( elem.ALLOW_KEYBOARD_INPUT ); 8496 8497 }, 8498 exit : function () { 8499 document[ fn.exitFullscreen ](); 8500 }, 8501 toggle : function ( elem ) { 8502 8503 if ( this.isFullscreen() ) { 8504 this.exit(); 8505 } else { 8506 this.request( elem ); 8507 } 8508 8509 }, 8510 isFullscreen : function() { 8511 return Boolean( document[ fn.fullscreenElement ] ); 8512 }, 8513 enabled : function() { 8514 return Boolean( document[ fn.fullscreenEnabled ] ); 8515 } 8516 }; 8517 8518 $(document).on({ 8519 'onInit.fb' : function(e, instance) { 8520 var $container; 8521 8522 if ( instance && !!instance.opts.fullScreen && !instance.FullScreen) { 8523 $container = instance.$refs.container; 8524 8525 instance.$refs.button_fs = $('<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="Full screen (F)"></button>') 8526 .appendTo( instance.$refs.buttons ); 8527 8528 $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) { 8529 8530 e.stopPropagation(); 8531 e.preventDefault(); 8532 8533 FullScreen.toggle( $container[ 0 ] ); 8534 8535 }); 8536 8537 if ( instance.opts.fullScreen.requestOnStart === true ) { 8538 FullScreen.request( $container[ 0 ] ); 8539 } 8540 8541 } 8542 8543 }, 'beforeMove.fb' : function(e, instance) { 8544 8545 if ( instance && instance.$refs.button_fs ) { 8546 instance.$refs.button_fs.toggle( !!instance.current.opts.fullScreen ); 8547 } 8548 8549 }, 'beforeClose.fb': function() { 8550 FullScreen.exit(); 8551 } 8552 }); 8553 8554 $(document).on(fn.fullscreenchange, function() { 8555 var instance = $.fancybox.getInstance(); 8556 var $what = instance ? instance.current.$placeholder : null; 8557 8558 if ( $what ) { 8559 8560 // If image is zooming, then this will force it to stop and reposition properly 8561 $what.css( 'transition', 'none' ); 8562 8563 instance.isAnimating = false;
8564 8565 instance.update( true, true, 0 ); 8566 } 8567 8568 }); 8569 8570}(document, window.jQuery)); 8571 8572// ========================================================================== 8573// 8574// Thumbs 8575// Displays thumbnails in a grid 8576// 8577// ========================================================================== 8578;(function (document, $) { 8579 'use strict'; 8580 8581 var FancyThumbs = function( instance ) { 8582 8583 this.instance = instance; 8584 8585 this.init(); 8586 8587 }; 8588 8589 $.extend( FancyThumbs.prototype, { 8590 8591 $button : null, 8592 $grid : null, 8593 $list : null, 8594 isVisible : false, 8595 8596 init : function() { 8597 var self = this; 8598 8599 self.$button = $('<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="Thumbnails (G)"></button>') 8600 .appendTo( this.instance.$refs.buttons ) 8601 .on('touchend click', function(e) { 8602 e.stopPropagation(); 8603 e.preventDefault(); 8604 8605 self.toggle(); 8606 }); 8607 8608 }, 8609 8610 create : function() { 8611 var instance = this.instance, 8612 list, 8613 src; 8614 8615 this.$grid = $('<div class="fancybox-thumbs"></div>').appendTo( instance.$refs.container ); 8616 8617 list = '<ul>'; 8618 8619 $.each(instance.group, function( i, item ) { 8620 8621 src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr('src') : null ); 8622 8623 if ( !src && item.type === 'image' ) { 8624 src = item.src; 8625 } 8626 8627 if ( src && src.length ) { 8628 list += '<li data-index="' + i + '" tabindex="0" class="fancybox-thumbs-loading"><img data-src="' + src + '" /></li>'; 8629 } 8630 8631 }); 8632 8633 list += '</ul>'; 8634 8635 this.$list = $( list ).appendTo( this.$grid ).on('click touchstart', 'li', function() { 8636 8637 instance.jumpTo( $(this).data('index') ); 8638 8639 }); 8640 8641 this.$list.find('img').hide().one('load', function() { 8642 8643 var $parent = $(this).parent().removeClass('fancybox-thumbs-loading'), 8644 thumbWidth = $parent.outerWidth(), 8645 thumbHeight = $parent.outerHeight(), 8646 width, 8647 height, 8648 widthRatio, 8649 heightRatio; 8650 8651 width = this.naturalWidth || this.width; 8652 height = this.naturalHeight || this.height; 8653 8654 //Calculate thumbnail width/height and center it 8655 8656 widthRatio = width / thumbWidth; 8657 heightRatio = height / thumbHeight; 8658 8659 if (widthRatio >= 1 && heightRatio >= 1) { 8660 if (widthRatio > heightRatio) { 8661 width = width / heightRatio; 8662 height = thumbHeight; 8663 8664 } else { 8665 width = thumbWidth; 8666 height = height / widthRatio; 8667 } 8668 } 8669 8670 $(this).css({ 8671 width : Math.floor(width), 8672 height : Math.floor(height), 8673 'margin-top' : Math.min( 0, Math.floor(thumbHeight * 0.3 - he
8673ight * 0.3 ) ), 8674 'margin-left' : Math.min( 0, Math.floor(thumbWidth * 0.5 - width * 0.5 ) ) 8675 }).show(); 8676 8677 }) 8678 .each(function() { 8679 this.src = $( this ).data( 'src' ); 8680 }); 8681 8682 }, 8683 8684 focus : function() { 8685 8686 if ( this.instance.current ) { 8687 this.$list 8688 .children() 8689 .removeClass('fancybox-thumbs-active') 8690 .filter('[data-index="' + this.instance.current.index + '"]') 8691 .addClass('fancybox-thumbs-active') 8692 .focus(); 8693 } 8694 8695 }, 8696 8697 close : function() { 8698 8699 this.$grid.hide(); 8700 8701 }, 8702 8703 update : function() { 8704 8705 this.instance.$refs.container.toggleClass('fancybox-container--thumbs', this.isVisible); 8706 8707 if ( this.isVisible ) { 8708 8709 if ( !this.$grid ) { 8710 this.create(); 8711 } 8712 8713 this.$grid.show(); 8714 8715 this.focus(); 8716 8717 } else if ( this.$grid ) { 8718 this.$grid.hide(); 8719 } 8720 8721 this.instance.update(); 8722 8723 }, 8724 8725 hide : function() { 8726 8727 this.isVisible = false; 8728 8729 this.update(); 8730 8731 }, 8732 8733 show : function() { 8734 8735 this.isVisible = true; 8736 8737 this.update(); 8738 8739 }, 8740 8741 toggle : function() { 8742 8743 if ( this.isVisible ) { 8744 this.hide(); 8745 8746 } else { 8747 this.show(); 8748 } 8749 } 8750 8751 }); 8752 8753 $(document).on('onInit.fb', function(e, instance) { 8754 var first = instance.group[0], 8755 second = instance.group[1]; 8756 8757 if ( !!instance.opts.thumbs && !instance.Thumbs && instance.group.length > 1 && ( 8758 ( first.type == 'image' || first.opts.thumb || first.opts.$thumb ) && 8759 ( second.type == 'image' || second.opts.thumb || second.opts.$thumb ) 8760 ) 8761 ) { 8762 8763 instance.Thumbs = new FancyThumbs( instance ); 8764 } 8765 8766 }); 8767 8768 $(document).on('beforeMove.fb', function(e, instance, item) { 8769 var self = instance && instance.Thumbs; 8770 8771 if ( !self ) { 8772 return; 8773 } 8774 8775 if ( item.modal ) { 8776 8777 self.$button.hide(); 8778 8779 self.hide(); 8780 8781 } else { 8782 8783 if ( instance.opts.thumbs.showOnStart === true && instance.firstRun ) { 8784 self.show(); 8785 8786 } 8787 8788 self.$button.show(); 8789 8790 if ( self.isVisible ) { 8791 self.focus(); 8792 } 8793 8794 } 8795 8796 }); 8797 8798 $(document).on('beforeClose.fb', function(e, instance) { 8799 8800 if ( instance && instance.Thumbs) { 8801 if ( instance.Thumbs.isVisible && instance.opts.thumbs.hideOnClosing !== false ) { 8802 instance.Thumbs.close(); 8803 } 8804 8805 instance.Thumbs = null; 8806 } 8807 8808 }); 8809 8810}(document, window.jQuery)); 8811 8812// ========================================================================== 8813// 8814// Hash 8815// Enables linking to each modal 8816// 8817// ========================================================================== 8818;(function (document, window, $) { 8819 'use strict'; 8820 8821 // Simple $.escapeSelector polyfill (for jQuery prior v3) 8822 if ( !$.escapeSelector ) { 8823 $.escapeSelector = function( sel ) { 8824 var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; 8825 var fcssescape = function( ch, asCodePoint ) { 8826 if ( asCodePoint ) { 8827 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER 8828 if ( ch === "\0" ) { 8829 return "\uFFFD"; 8830 } 8831 8832 // Control characters and (dependent upon position) numbers get escaped as code points 8833 return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; 8834 } 8835 8836 // Other potentially-special ASCII characters get backslash-escaped 8837 return "\\" + ch; 8838 }; 8839 8840 return ( sel + "" ).replace( rcssescape, fcssescape ); 8841 }; 8842 } 8843 8844 // Variable containing last hash value set by fancyBox 8845 // It will be used to determine if fancyBox needs to close after hash change is detected 8846 var currentHash = null; 8847 8848 // Get info about gallery name and current index from url 8849 function parseUrl() { 8850 var hash = window.location.hash.substr( 1 ); 8851 var rez = hash.split( '-' ); 8852 var index = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseInt( rez.pop( -1 ), 10 ) || 1 : 1; 8853 var gallery = rez.join( '-' ); 8854 8855 // Index is starting from 1 8856 if ( index < 1 ) { 8857 index = 1; 8858 } 8859 8860 return { 8861 hash : hash, 8862 index : index, 8863 gallery : gallery 8864 }; 8865 } 8866 8867 // Trigger click evnt on links to open new fancyBox instance 8868 function triggerFromUrl( url ) { 8869 var $el; 8870 8871 if ( url.gallery !== '' ) { 8872 8873 // If we can find element matching 'data-fancybox' atribute, then trigger click event for that .. 8874 $el = $( "[data-fancybox='" + $.escapeSelector( url.gallery ) + "']" ).eq( url.index - 1 ); 8875 8876 if ( $el.length ) { 8877 $el.trigger( 'click' ); 8878 8879 } else { 8880 8881 // .. if not, try finding element by ID 8882 $( "#" + $.escapeSelector( url.gallery ) + "" ).trigger( 'click' ); 8883 8884 } 8885 8886 } 8887 } 8888 8889 // Get gallery name from current instance 8890 function getGallery( instance ) { 8891 var opts; 8892 8893 if ( !instance ) { 8894 return false;
8895 } 8896 8897 opts = instance.current ? instance.current.opts : instance.opts; 8898 8899 return opts.$orig ? opts.$orig.data( 'fancybox' ) : ( opts.hash || '' ); 8900 } 8901 8902 // Star when DOM becomes ready 8903 $(function() { 8904 8905 // Small delay is used to allow other scripts to process "dom ready" event 8906 setTimeout(function() { 8907 8908 // Check if this module is not disabled 8909 if ( $.fancybox.defaults.hash === false ) { 8910 return; 8911 } 8912 8913 // Check if need to close after url has changed 8914 $(window).on('hashchange.fb', function() { 8915 var url = parseUrl(); 8916 8917 if ( $.fancybox.getInstance() ) { 8918 if ( currentHash && currentHash !== url.gallery + '-' + url.index ) { 8919 currentHash = null; 8920 8921 $.fancybox.close(); 8922 } 8923 8924 } else if ( url.gallery !== '' ) { 8925 triggerFromUrl( url ); 8926 } 8927 8928 }); 8929 8930 // Update hash when opening/closing fancyBox 8931 $(document).on({ 8932 'onInit.fb' : function( e, instance ) { 8933 var url = parseUrl(); 8934 var gallery = getGallery( instance ); 8935 8936 // Make sure gallery start index matches index from hash 8937 if ( gallery && url.gallery && gallery == url.gallery ) { 8938 instance.currIndex = url.index - 1; 8939 } 8940 8941 }, 'beforeMove.fb' : function( e, instance, current ) { 8942 var gallery = getGallery( instance ); 8943 8944 // Update window hash 8945 if ( gallery && gallery !== '' ) { 8946 8947 if ( window.location.hash.indexOf( gallery ) < 0 ) { 8948 instance.opts.origHash = window.location.hash; 8949 } 8950 8951 currentHash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' ); 8952 8953 if ( "pushState" in history ) { 8954 history.pushState( '', document.title, window.location.pathname + window.location.search + '#' + currentHash ); 8955 8956 } else { 8957 window.location.hash = currentHash; 8958 } 8959 8960 } 8961 8962 }, 'beforeClose.fb' : function( e, instance, current ) { 8963 var gallery = getGallery( instance ); 8964 var origHash = instance && instance.opts.origHash ? instance.opts.origHash : ''; 8965 8966 // Remove hash from location bar 8967 if ( gallery && gallery !== '' ) { 8968 if ( "pushState" in history ) { 8969 history.pushState( '', document.title, window.location.pathname + window.location.search + origHash ); 8970 8971 } else { 8972 window.location.hash = origHash; 8973 } 8974 } 8975 8976 currentHash = null; 8977 } 8978 }); 8979 8980 // Check current hash and trigger click event on matching element to start fancyBox, if needed 8981 triggerFromUrl( parseUrl() ); 8982 8983 }, 50); 8984 }); 8985 8986 8987}(document, window, window.jQuery)); 8988 8989jQuery(document).foundation(); 8990/* 8991These functions make sure WordPress 8992and Foundation play nice together. 8993*/ 8994 8995jQuery(document).ready(function() { 8996 initFlickity(); 8997 siteJsEssentials(); 8998 initParallax(); 8999 coolColorOverLay(); 9000 initHpVideo(); 9001 checkVideo(); 9002 initScroller(); 9003 initGalleries(); 9004 //formLabel2Placeholder(); 9005 cf7fix(); 9006 initPreloaderScreen(); 9007 initAccordionScripts(); 9008}); 9009 9010jQuery(window).resize(function() { 9011 navCheck(); 9012}); 9013 9014jQuery(window).scroll(function() { 9015 navCheck(); 9016 9017 jQuery('.fadeInBlock').each( function(i){ 9018 var bottom_of_object = jQuery(this).position().top + jQuery(this).outerHeight(); 9019 var bottom_of_window = jQuery(window).scrollTop() + jQuery(window).height(); 9020 9021 /* Adjust the "200" to either have a delay or that the content starts fading a bit before you reach it */ 9022 bottom_of_window = bottom_of_window + 500; 9023 9024 if( bottom_of_window > bottom_of_object ){ 9025 jQuery(this).animate({'opacity':'1'},800); 9026 } 9027 }); 9028 9029}); 9030 9031jQuery(window).on('changed.zf.mediaquery', function(event, newSize, oldSize) { 9032 // newSize is the name of the now-current breakpoint, oldSize is the previous breakpoint 9033 9034}); 9035 9036 9037 9038function initAccordionScripts() { 9039 9040 jQuery("ul.gdl-accordion li").each(function(){ 9041 9042 9043 jQuery(this).children(".accordion-head").bind("click", function(){ 9044 jQuery(this).toggleClass(function(){ 9045 if(jQuery(this).is("active")) return ""; 9046 return "active"; 9047 }); 9048 9049 jQuery(this).siblings(".accordion-content").slideToggle(); 9050 jQuery(this).parent().siblings("li").children(".accordion-content").slideUp(); 9051 jQuery(this).parent().siblings("li").children(".accordion-head").removeClass("active"); 9052 }); 9053 }); 9054} 9055 9056 9057 9058 9059// dont forget to put the CSS 9060// form.form label { display: none; } 9061// 9062function formLabel2Placeholder() { 9063 jQuery("form :input").each(function(index, elem) { 9064 var eId = jQuery(elem).attr("id"); 9065 var label = null; 9066 if (eId && (label = jQuery(elem).parents("form").find("label[for="+eId+"]")).length == 1) { 9067 jQuery(elem).attr("placeholder", jQuery(label).html()); 9068 jQuery(label).remove(); 9069 } 9070 }); 9071 } 9072 9073function initGalleries(){ 9074 jQuery('.imgGalItem').click(function() { 9075 event.preventDefault(); 9076 var link = jQuery(this).attr('href'); 9077 var rel = jQuery(this).attr('rel'); 9078 var counts = jQuery(this).attr('data-groups'); 9079 var newContent = '<a href="'+link+'" rel="galSlideCell'+rel+'" data-fancybox="group'+counts+'" data-caption="*Individual Results May Vary"><img src="'+link+'"></a> '; 9080 9081 jQuery(".photoGallery"+rel).removeClass("is-active"); 9082 jQuery(this).addClass("is-active"); 9083 9084 jQuery(".galSlideCell"+rel).stop().css('opacity', '0').html(function () { 9085 return newContent; 9086 }).animate({ 9087 opacity: 1 9088 }, 400); 9089 9090 }); 9091 9092 jQuery( "#listView" ).click(function() { 9093 event.preventDefault(); 9094 jQuery(this).addClass("active"); 9095 jQuery( "#gridView" ).removeClass("active"); 9096 jQuery( ".imgWrapSub" ).removeClass( "toggleHide" ); 9097 }); 9098 9099 jQuery( "#gridView" ).click(function() { 9100 event.preventDefault(); 9101 jQuery(this).addClass("active"); 9102 jQuery( "#listView" ).removeClass("active"); 9103 jQuery( ".imgWrapSub" ).addClass( "toggleHide" ); 9104 }); 9105 9106 9107 jQuery('select#mobile-sorting').on('change', function() { 9108 window.location.replace(this.value); 9109 }); 9110 9111} 9112 9113function cf7fix() { 9114 jQuery('input.wpcf7-submit').click(function() { 9115 setTimeout(function(){ 9116 jQuery('input.wpcf7-submit').attr('disabled','disabled'); 9117 }, 100); 9118 }); 9119 9120 jQuery(".wpcf7").on('wpcf7:submit', function(event){ 9121 jQuery(this).find('input.wpcf7-submit').removeAttr('disabled'); 9122 }); 9123 9124} 9125 9126function initSubmenus() { 9127 jQuery('.top-bar #menu-topnav-1').fadeIn('slow').css('display','inline-block'); 9128 jQuery('#head-bar-menu #head-content').addClass('animated fadeInDown'); 9129 9130} 9131 9132function initPreloaderScreen() { 9133 setTimeout(function(){ 9134 jQuery('.home #loader').addClass('animated zoomOut'); 9135 }, 800); 9136 setTimeout(function(){ 9137 9138 jQuery('.home #loader-wrapper').fadeOut('slow'); 9139 jQuery('.home .off-canvas-wrapper').css('opacity', 1); 9140 }, 1200); 9141 9142 var fpBannerLogo = jQuery('.slide-logo'); 9143 var fpButton = jQuery('.fp-banner-link'); 9144 var fpfixedlogo = jQuery('.box .button'); 9145 if(fpBannerLogo){ 9146 fpBannerLogo.css('opacity',0); 9147 setTimeout(function(){ 9148 fpBannerLogo.css('opacity',1); 9149 fpBannerLogo.addClass('animated zoomIn'); 9150 }, 1400); 9151 } 9152 if(fpfixedlogo) { 9153 setTimeout(function(){ 9154 fpfixedlogo.addClass('animated fadeIn'); 9155 }, 1400); 9156 } 9157 9158 if(fpButton) { 9159 fpButton.css('opacity',0); 9160 setTimeout(function(){ 9161 fpButton.css('opacity',1); 9162 fpButton.addClass('animated fadeIn'); 9163 initSubmenus(); 9164 }, 1300); 9165 } 9166 9167} 9168 9169 9170// CHECKS FOR STATIC / FIXED NAVIGATION 9171function navCheck(){ 9172 var isDesktop = jQuery(document).width() > 1024? true : false; 9173 // If document (your website) is wider than 1023px, notVisible = true, else notVisible = false 9174 if(!isDesktop) { 9175 jQuery('#menu-bar-wrapper').removeClass('swipeup'); 9176 jQuery('#menu-bar-wrapper').removeClass('fixed'); 9177 } else { desktopNavToggle(); } 9178} 9179 9180var toggler = false;
9181var previousScroll = 0; 9182function desktopNavToggle() { 9183 var offset = jQuery('#menu-bar-wrapper').offset(); 9184 9185 if(offset) { 9186 var height = jQuery(window).scrollTop(); 9187 var currentScroll = jQuery(window).scrollTop(); 9188 9189 if (currentScroll < previousScroll && toggler === true && height < 95 && offset.top < 145){ 9190 jQuery('.home #menu-bar-wrapper').addClass('swipeup'); 9191 toggler = false; 9192 9193 } 9194 previousScroll = currentScroll; 9195 9196 9197 if(height < 89 && offset.top < 140) { 9198 toggler = false; 9199 } else { 9200 toggler = true; 9201 jQuery('.home #menu-bar-wrapper').addClass('fixed').removeClass('swipeup'); 9202 jQuery('.home .menu-wrapper ').addClass('small-12').removeClass('small-10'); 9203 } 9204 if ( currentScroll < 30 ) { 9205 jQuery('.home #menu-bar-wrapper').removeClass('swipeup').removeClass('fixed'); 9206 jQuery('.home .menu-wrapper ').addClass('small-10').removeClass('small-12'); 9207 } 9208 9209 } 9210} 9211 9212// CUSTOM SLIDER WIDGET SCRIPT 9213function initFlickity() { 9214 jQuery('.sbReviewsSlider').flickity({ 9215 // options 9216 cellAlign: 'left', 9217 contain: true, 9218 autoPlay: 3500, 9219 wrapAround: true, 9220 pageDots: false, 9221 prevNextButtons: false, 9222 imagesLoaded: true, 9223 cellSelector: ".wSlide", 9224 arrowShape: { 9225 x0: 10, 9226 x1: 70, y1: 40, 9227 x2: 70, y2: 40, 9228 x3: 70 9229 } 9230 }); 9231 9232 9233 var servDiv = jQuery('#services-gallery'); 9234 if(servDiv){ 9235 var $servCarousel; 9236 9237 $servCarousel = servDiv.flickity({ 9238 // options 9239 cellAlign: 'center', 9240 contain: true, 9241 autoPlay: 15000, 9242 wrapAround:true, 9243 pageDots: true, 9244 selectedAttraction: 0.008, 9245 friction: 0.12, 9246 imagesLoaded: true, 9247 prevNextButtons:true, 9248 arrowShape: 'M76.854.8,99.207,23.152a2.709,2.709,0,0,1,0,3.831L76.854,49.335A2.709,2.709,0,0,1,72.23,47.42V31.387H2.709A2.709,2.709,0,0,1,0,28.678V21.455a2.709,2.709,0,0,1,2.709-2.709H72.23V2.714A2.709,2.709,0,0,1,76.854.8Z', 9249 //watchCSS: 'fallbackOn' 9250 }); 9251 } 9252 9253 var revDiv = jQuery('#review-slider'); 9254 if(revDiv){ 9255 var $revCarousel; 9256 9257 $revCarousel = revDiv.flickity({ 9258 // options 9259 cellAlign: 'center', 9260 contain: true, 9261 autoPlay: 15000, 9262 wrapAround:true, 9263 pageDots: true, 9264 selectedAttraction: 0.008, 9265 friction: 0.12, 9266 imagesLoaded: true, 9267 prevNextButtons:true, 9268 arrowShape: 'M76.854.8,99.207,23.152a2.709,2.709,0,0,1,0,3.831L76.854,49.335A2.709,2.709,0,0,1,72.23,47.42V31.387H2.709A2.709,2.709,0,0,1,0,28.678V21.455a2.709,2.709,0,0,1,2.709-2.709H72.23V2.714A2.709,2.709,0,0,1,76.854.8Z', 9269 9270 //watchCSS: 'fallbackOn' 9271 }); 9272 } 9273} 9274 9275function initParallax() { 9276 9277 // use data-fptop to set the default starting point of the bg image 9278 // eg. <div data-fptop="100" style="background-image:url('somebg.jpg')></div> 9279 9280 var parallax = document.querySelectorAll(".fpparallax"), 9281 speed = 0.4; 9282 9283 window.onscroll = function(){ 9284 [].slice.call(parallax).forEach(function(el,i){ 9285 9286 var w = window.innerWidth; 9287 var mediaQueryVal = ""; 9288 var ParaOffset = el.offsetTop; 9289 9290 if(el.getAttribute('data-speed')) { 9291 speed = el.getAttribute('data-speed'); 9292 } 9293 9294 if(w > 1024){ mediaQueryVal = el.getAttribute('data-fp-large'); } 9295 else if (w <= 1024 && w > 640) { mediaQueryVal = el.getAttribute('data-fp-medium'); } 9296 else if (w <= 640 && w > 420) { mediaQueryVal = el.getAttribute('data-fp-small'); } 9297 else if (w <= 420) { mediaQueryVal = el.getAttribute('data-fp-xsmall'); } 9298 9299 var windowYOffset = window.pageYOffset, 9300 minusTop = mediaQueryVal , 9301 // elBackgrounPosVanilla = "50% " + (windowYOffset * speed) + "px"; 9302 elBackgrounPos = "50% " + (((windowYOffset - ParaOffset) * speed) - minusTop) + "px"; 9303 el.style.backgroundPosition = elBackgrounPos; 9304 }); 9305 }; 9306} 9307 9308function siteJsEssentials() { 9309 // Remove empty P tags created by WP inside of Accordion and Orbit 9310 jQuery('.accordion p:empty, .orbit p:empty').remove(); 9311 9312 // Makes sure last grid item floats left 9313 jQuery('.archive-grid .columns').last().addClass( 'end' ); 9314 9315 // Emma Form Tracking 9316 jQuery('#emma-form-submit').addClass('emmaTrackingLink'); 9317 9318 // Adds Flex Video to YouTube and Vimeo Embeds 9319 jQuery('iframe[src*="youtube.com"], iframe[src*="vimeo.com"]').wrap("<div class='flex-vi
9319deo responsive-embed widescreen'/>"); 9320 //console.log('hello dev team!'); 9321 9322 jQuery( ".ginput_complex.ginput_container span" ).wrap( "<div class='theGcontent'></div>" ); 9323 9324} 9325 9326// VIDEO FOR FRONT PAGE 9327function initHpVideo() { 9328 var isTouchDevice = /Windows Phone/.test(navigator.userAgent) || ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch; 9329 jQuery('.promo .video').each(function() { 9330 var holder = jQuery(this); 9331 var video = holder.find('video'); 9332 var isDesktop = jQuery(document).width() < 1025 ? true : false; 9333 9334 if (isTouchDevice && isDesktop) { 9335 video.remove(); 9336 jQuery(".promo").addClass("mobile"); 9337 jQuery(".promo").removeClass("desktop"); 9338 } else { 9339 jQuery(".promo").addClass("desktop"); 9340 } 9341 }); 9342} 9343 9344function checkVideo(){ 9345var vid = document.getElementById("fpVideo"); 9346 9347 if (vid) { 9348 vid.onloadedmetadata = function() { 9349 jQuery('.loader').fadeOut('slow'); 9350 }; 9351 } 9352} 9353 9354function initScroller(){ 9355 9356 jQuery('.scrollink').click(function() { 9357 if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) { 9358 var target = jQuery(this.hash); 9359 target = target.length ? target : jQuery('[name=' + this.hash.slice(1) +']'); 9360 if (target.length) { 9361 jQuery('html,body').animate({ 9362 scrollTop: target.offset().top-40 9363 }, 1000); 9364 return false; 9365 } 9366 } 9367 }); 9368 9369} 9370 9371// coolColorOverlay Hover box 9372// this is dependent on /scss/sections/plugins/cool-color.scss (include it via style.scss) 9373/* 9374 - to add coolColorOverlay to and element in your templates add the class .coolColor 9375 the js will look for a link with a class of .button to act as the trigger that will fade the background color when hovered. 9376 9377 - if you would like to fade out any child elements with the color add a class of .unThis to them. 9378 9379 - Available data attributes for coolColorOverlay 9380 9381 data-cool-this="1" 9382 this allows the color to fade away when we hover on the target button 9383 set value to 1 or true to enable and 0 or false to disable 9384 (by default this is disabled) 9385 9386 data-image-src="url" 9387 (add this if you would like a background image) 9388 9389 data-cool-color="primary" 9390 this chooses the overlay color. It pull directly from the foundation color pallet 9391 the default / fallback is primary 9392 you may also set it to: secondary, success, warning, alert, black or white 9393 9394 data-cool-btn="primary" 9395 this sets the hover color of the target button (if not set it will fallback to the data-cool-color value) 9396 you may also set it to: secondary, success, warning, alert, black or white 9397*/ 9398 9399function coolColorOverLay() { 9400 jQuery(".coolColor").each(function(){ 9401 var ccattr = jQuery(this).attr('data-cool-color'); 9402 var bcattr = jQuery(this).attr('data-cool-btn'); 9403 if (typeof ccattr !== typeof undefined && ccattr !== false && ccattr !== '') { 9404 ccattr = jQuery(this).attr('data-cool-color'); 9405 } else { 9406 ccattr = 'primary'; 9407 } 9408 if (typeof bcattr !== typeof undefined && bcattr !== false && bcattr !== '') { 9409 bcattr = jQuery(this).attr('data-cool-btn'); 9410 } else { 9411 bcattr = ccattr; 9412 } 9413 jQuery(this).children().wrapAll("<div class='coolContent'></div>"); 9414 jQuery(this).children().find('.button').addClass(bcattr); 9415 var bgattr = jQuery(this).attr('data-image-src'); 9416 if (typeof bgattr !== typeof undefined && bgattr !== false) { 9417 jQuery(this).addClass(ccattr).css({ 9418 'background' : 'url('+bgattr+')', 9419 'background-repeat': 'no-repeat', 9420 'background-size': 'cover', 9421 'background-position': 'center top', 9422 'padding': '3rem 0 5rem', 9423 }); 9424 } else { 9425 jQuery(this).css({ 9426 'position': 'relative', 9427 'padding': '3rem 0 5rem', 9428 }); 9429 } 9430 var coolthis = jQuery(this).attr('data-cool-this'); 9431 if (typeof coolthis !== typeof undefined && coolthis !== false && (coolthis === 'true'|| coolthis === '1') && (coolthis !== '' || coolthis !== 0 || coolthis !== 'false')) { 9432 jQuery(this).find('.button').hover( function(){ 9433 jQuery(this).parents('.coolColor').toggleClass('unCoolThis'); 9434 }); 9435 } else { 9436 9437 } 9438 }); 9439} 9440
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.