1// ================================================== 2// fancyBox v3.2.10 3// 4// Licensed GPLv3 for open source use 5// or fancyBox Commercial License for commercial use 6// 7// http://fancyapps.com/fancybox/ 8// Copyright 2017 fancyApps 9// 10// ================================================== 11;(function (window, document, $, undefined) { 12 'use strict'; 13 14 // If there's no jQuery, fancyBox can't work 15 // ========================================= 16 17 if ( !$ ) { 18 return; 19 } 20 21 // Check if fancyBox is already initialized 22 // ======================================== 23 24 if ( $.fn.fancybox ) { 25 26 if ( 'console' in window ) { 27 console.log( 'fancyBox already initialized' ); 28 } 29 30 return; 31 } 32 33 // Private default settings 34 // ======================== 35 36 var defaults = { 37 38 // Enable infinite gallery navigation 39 loop : false, 40 41 // Space around image, ignored if zoomed-in or viewport width is smaller than 800px 42 margin : [44, 0], 43 44 // Horizontal space between slides 45 gutter : 50, 46 47 // Enable keyboard navigation 48 keyboard : true, 49 50 // Should display navigation arrows at the screen edges 51 arrows : true, 52 53 // Should display infobar (counter and arrows at the top) 54 infobar : true, 55 56 // Should display toolbar (buttons at the top) 57 toolbar : true, 58 59 // What buttons should appear in the top right corner. 60 // Buttons will be created using templates from `btnTpl` option 61 // and they will be placed into toolbar (class="fancybox-toolbar"` element) 62 buttons : [ 63 'slideShow', 64 'fullScreen', 65 'thumbs', 66 'share', 67 //'download', 68 //'zoom', 69 'close' 70 ], 71 72 // Detect "idle" time in seconds 73 idleTime : 3, 74 75 // Should display buttons at top right corner of the content 76 // If 'auto' - they will be created for content having type 'html', 'inline' or 'ajax' 77 // Use template from `btnTpl.smallBtn` for customization 78 smallBtn : 'auto', 79 80 // Disable right-click and use simple image protection for images 81 protect : false, 82 83 // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc 84 modal : false, 85 86 image : { 87 88 // Wait for images to load before displaying 89 // Requires predefined image dimensions 90 // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found 91 preload : "auto" 92 93 }, 94 95 ajax : { 96 97 // Object containing settings for ajax request 98 settings : { 99 100 // This helps to indicate that request comes from the modal 101 // Feel free to change naming 102 data : { 103 fancybox : true 104 } 105 } 106 107 }, 108 109 iframe : { 110 111 // Iframe template 112 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>', 113 114 // Preload iframe before displaying it 115 // This allows to calculate iframe content width and height 116 // (note: Due to "Same Origin Policy", you can't get cross domain data). 117 preload : true, 118 119 // Custom CSS styling for iframe wrapping element 120 // You can use this to set custom iframe dimensions 121 css : {}, 122 123 // Iframe tag attributes 124 attr : { 125 scrolling : 'auto' 126 } 127 128 }, 129 130 // Default content type if cannot be detected automatically 131 defaultType : 'image', 132 133 // Open/close animation type 134 // Possible values: 135 // false - disable 136 // "zoom" - zoom images from/to thumbnail 137 // "fade" 138 // "zoom-in-out" 139 // 140 animationEffect : "zoom", 141 142 // Duration in ms for open/close animation 143 animationDuration : 500, 144 145 // Should image change opacity while zooming 146 // If opacity is "auto", then opacity will be changed if image and thumbnail have different aspect ratios 147 zoomOpacity : "auto", 148 149 // Transition effect between slides 150 // 151 // 152 transitionEffect : "fade", 153 154 // Duration in ms for transition animation 155 transitionDuration : 366, 156 157 // Custom CSS class for slide element 158 slideClass : '', 159 160 // Custom CSS class for layout 161 baseClass : '', 162 163 // Base template for layout 164 baseTpl : 165 '<div class="fancybox-container" role="dialog" tabindex="-1">' + 166 '<div class="fancybox-bg"></div>' + 167 '<div class="fancybox-inner">' + 168 '<div class="fancybox-infobar">' + 169 '<span data-fancybox-index></span> / <span data-fancybox-count></span>' + 170 '</div>' + 171 '<div class="fancybox-toolbar">{{buttons}}</div>' + 172 '<div class="fancybox-navigation">{{arrows}}</div>' + 173 '<div class="fancybox-stage"></div>' + 174 '<div class="fancybox-caption-wrap"><div class="fancybox-caption"></div></div>' + 175 '</div>' + 176 '</div>', 177 178 // Loading indicator template 179 spinnerTpl : '<div class="fancybox-loading"></div>', 180 181 // Error message template 182 errorTpl : '<div class="fancybox-error"><p>{{ERROR}}<p></div>', 183 184 btnTpl : { 185 186 download : '<a download data-fancybox-download class="fancybox-button fancybox-button--download" title="{{DOWNLOAD}}">' + 187 '<svg viewBox="0 0 40 40">' + 188 '<path d="M20,23 L20,8 L20,23 L13,16 L20,23 L27,16 L20,23 M26,28 L13,28 L27,28 L14,28" />' + 189 '</svg>' + 190 '</a>', 191 192 zoom : '<button data-fancybox-zoom class="fancybox-button fancybox-button--zoom" title="{{ZOOM}}">' + 193 '<svg viewBox="0 0 40 40">' + 194 '<path d="M 18,17 m-8,0 a 8,8 0 1,0 16,0 a 8,8 0 1,0 -16,0 M25,23 L31,29 L25,23" />' + 195 '</svg>' + 196 '</button>', 197 198 close : '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}">' + 199 '<svg viewBox="0 0 40 40">' + 200 '<path d="M10,10 L30,30 M30,10 L10,30" />' + 201 '</svg>' + 202 '</button>', 203 204 // This small close button will be appended to your html/inline/ajax content by default, 205 // if "smallBtn" option is not set to false 206 smallBtn : '<button data-fancybox-close class="fancybox-close-small" title="{{CLOSE}}"></button>', 207 208 // Arrows 209 arrowLeft : '<button data-fancybox-prev class="fancybox-button fancybox-button--arrow_left" title="{{PREV}}">' + 210 '<svg viewBox="0 0 40 40">' + 211 '<path d="M10,20 L30,20 L10,20 L18,28 L10,20 L18,12 L10,20"></path>' + 212 '</svg>' + 213 '</button>', 214 215 arrowRight : '<button data-fancybox-next class="fancybox-button fancybox-button--arrow_right" title="{{NEXT}}">' + 216 '<svg viewBox="0 0 40 40">' + 217 '<path d="M30,20 L10,20 L30,20 L22,28 L30,20 L22,12 L30,20"></path>' + 218 '</svg>' + 219 '</button>' 220 }, 221 222 // Container is injected into this element 223 parentEl : 'body', 224 225 226 // Focus handling 227 // ============== 228 229 // Try to focus on the first focusable element after opening 230 autoFocus : false, 231 232 // Put focus back to active element after closing 233 backFocus : true, 234
vendor: 5,349 bytes, lines 235-387
235 // Do not let user to focus on element outside modal content 236 trapFocus : true, 237 238 239 // Module specific options 240 // ======================= 241 242 fullScreen : { 243 autoStart : false, 244 }, 245 246 // Set `touch: false` to disable dragging/swiping 247 touch : { 248 vertical : true, // Allow to drag content vertically 249 momentum : true // Continue movement after releasing mouse/touch when panning 250 }, 251 252 // Hash value when initializing manually, 253 // set `false` to disable hash change 254 hash : null, 255 256 // Customize or add new media types 257 // Example: 258 /* 259 media : { 260 youtube : { 261 params : { 262 autoplay : 0 263 } 264 } 265 } 266 */ 267 media : {}, 268 269 slideShow : { 270 autoStart : false, 271 speed : 4000 272 }, 273 274 thumbs : { 275 autoStart : false, // Display thumbnails on opening 276 hideOnClose : true, // Hide thumbnail grid when closing animation starts 277 parentEl : '.fancybox-container', // Container is injected into this element 278 axis : 'y' // Vertical (y) or horizontal (x) scrolling 279 }, 280 281 // Use mousewheel to navigate gallery 282 // If 'auto' - enabled for images only 283 wheel : 'auto', 284 285 // Callbacks 286 //========== 287 288 // See Documentation/API/Events for more information 289 // Example: 290 /* 291 afterShow: function( instance, current ) { 292 console.info( 'Clicked element:' ); 293 console.info( current.opts.$orig ); 294 } 295 */ 296 297 onInit : $.noop, // When instance has been initialized 298 299 beforeLoad : $.noop, // Before the content of a slide is being loaded 300 afterLoad : $.noop, // When the content of a slide is done loading 301 302 beforeShow : $.noop, // Before open animation starts 303 afterShow : $.noop, // When content is done loading and animating 304 305 beforeClose : $.noop, // Before the instance attempts to close. Return false to cancel the close. 306 afterClose : $.noop, // After instance has been closed 307 308 onActivate : $.noop, // When instance is brought to front 309 onDeactivate : $.noop, // When other instance has been activated 310 311 312 // Interaction 313 // =========== 314 315 // Use options below to customize taken action when user clicks or double clicks on the fancyBox area, 316 // each option can be string or method that returns value. 317 // 318 // Possible values: 319 // "close" - close instance 320 // "next" - move to next gallery item 321 // "nextOrClose" - move to next gallery item or close if gallery has only one item 322 // "toggleControls" - show/hide controls 323 // "zoom" - zoom image (if loaded) 324 // false - do nothing 325 326 // Clicked on the content 327 clickContent : function( current, event ) { 328 return current.type === 'image' ? 'zoom' : false; 329 }, 330 331 // Clicked on the slide 332 clickSlide : 'close', 333 334 // Clicked on the background (backdrop) element 335 clickOutside : 'close', 336 337 // Same as previous two, but for double click 338 dblclickContent : false, 339 dblclickSlide : false, 340 dblclickOutside : false, 341 342 343 // Custom options when mobile device is detected 344 // ============================================= 345 346 mobile : { 347 idleTime : false, 348 margin : 0, 349 350 clickContent : function( current, event ) { 351 return current.type === 'image' ? 'toggleControls' : false; 352 }, 353 clickSlide : function( current, event ) { 354 return current.type === 'image' ? 'toggleControls' : 'close'; 355 }, 356 dblclickContent : function( current, event ) { 357 return current.type === 'image' ? 'zoom' : false; 358 }, 359 dblclickSlide : function( current, event ) { 360 return current.type === 'image' ? 'zoom' : false; 361 } 362 }, 363 364 365 // Internationalization 366 // ============ 367 368 lang : 'en', 369 i18n : { 370 'en' : { 371 CLOSE : 'Close', 372 NEXT : 'Next', 373 PREV : 'Previous', 374 ERROR : 'The requested content cannot be loaded. <br/> Please try again later.', 375 PLAY_START : 'Start slideshow', 376 PLAY_STOP : 'Pause slideshow', 377 FULL_SCREEN : 'Full screen', 378 THUMBS : 'Thumbnails', 379 DOWNLOAD : 'Download', 380 SHARE : 'Share', 381 ZOOM : 'Zoom' 382 }, 383 'de' : { 384 CLOSE : 'Schliessen', 385 NEXT : 'Weiter', 386 PREV : 'Zurück', 387 ERROR : 'Die angeforderten Daten konnten nicht geladen werden. <br/>
387 Bitte versuchen Sie es später nochmal.', 388 PLAY_START : 'Diaschau starten', 389 PLAY_STOP : 'Diaschau beenden', 390 FULL_SCREEN : 'Vollbild', 391 THUMBS : 'Vorschaubilder', 392 DOWNLOAD : 'Herunterladen', 393 SHARE : 'Teilen', 394 ZOOM : 'MaÃstab' 395 } 396 } 397 398 }; 399 400 // Few useful variables and methods 401 // ================================ 402 403 var $W = $(window); 404 var $D = $(document); 405 406 var called = 0; 407 408 409 // Check if an object is a jQuery object and not a native JavaScript object 410 // ======================================================================== 411 412 var isQuery = function ( obj ) { 413 return obj && obj.hasOwnProperty && obj instanceof $; 414 }; 415 416 417 // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame" 418 // =============================================================================== 419 420 var requestAFrame = (function () { 421 return window.requestAnimationFrame || 422 window.webkitRequestAnimationFrame || 423 window.mozRequestAnimationFrame || 424 window.oRequestAnimationFrame || 425 // if all else fails, use setTimeout 426 function (callback) { 427 return window.setTimeout(callback, 1000 / 60); 428 }; 429 })(); 430 431 432 // Detect the supported transition-end event property name 433 // ======================================================= 434 435 var transitionEnd = (function () { 436 var t, el = document.createElement("fakeelement"); 437 438 var transitions = { 439 "transition" : "transitionend", 440 "OTransition" : "oTransitionEnd", 441 "MozTransition" : "transitionend", 442 "WebkitTransition": "webkitTransitionEnd" 443 }; 444 445 for (t in transitions) { 446 if (el.style[t] !== undefined){ 447 return transitions[t]; 448 } 449 } 450 451 return 'transitionend'; 452 })(); 453 454 455 // Force redraw on an element. 456 // This helps in cases where the browser doesn't redraw an updated element properly. 457 // ================================================================================= 458 459 var forceRedraw = function( $el ) { 460 return ( $el && $el.length && $el[0].offsetHeight ); 461 }; 462 463 464 // Class definition 465 // ================ 466 467 var FancyBox = function( content, opts, index ) { 468 var self = this; 469 470 self.opts = $.extend( true, { index : index }, $.fancybox.defaults, opts || {} ); 471 472 if ( $.fancybox.isMobile ) { 473 self.opts = $.extend( true, {}, self.opts, self.opts.mobile ); 474 } 475 476 // Exclude buttons option from deep merging 477 if ( opts && $.isArray( opts.buttons ) ) { 478 self.opts.buttons = opts.buttons; 479 } 480 481 self.id = self.opts.id || ++called; 482 self.group = []; 483 484 self.currIndex = parseInt( self.opts.index, 10 ) || 0; 485 self.prevIndex = null; 486 487 self.prevPos = null; 488 self.currPos = 0; 489 490 self.firstRun = null; 491 492 // Create group elements from original item collection 493 self.createGroup( content ); 494 495 if ( !self.group.length ) { 496 return; 497 } 498 499 // Save last active element and current scroll position 500 self.$lastFocus = $(document.activeElement).blur(); 501 502 // Collection of gallery objects 503 self.slides = {}; 504 505 self.init(); 506 }; 507 508 $.extend(FancyBox.prototype, { 509 510 // Create DOM structure 511 // ==================== 512 513 init : function() { 514 var self = this, 515 firstItem = self.group[ self.currIndex ], 516 firstItemOpts = firstItem.opts, 517 scrollbarWidth = $.fancybox.scrollbarWidth, 518 $scrollDiv, 519 $container, 520 buttonStr; 521 522 self.scrollTop = $D.scrollTop(); 523 self.scrollLeft = $D.scrollLeft(); 524 525 526 // Hide scrollbars 527 // =============== 528 529 if ( !$.fancybox.getInstance() ) { 530 531 $( 'body' ).addClass( 'fancybox-active' ); 532 533 // iOS hack 534 if ( /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream ) { 535 536 // iOS has problems for input elements inside fixed containers, 537 // the workaround is to apply `position: fixed` to `<body>` element, 538 // unfortunately, this makes it lose the scrollbars and forces address bar to appear. 539
540 if ( firstItem.type !== 'image' ) { 541 $( 'body' ).css( 'top', $( 'body' ).scrollTop() * -1 ).addClass( 'fancybox-iosfix' ); 542 } 543 544 } else if ( !$.fancybox.isMobile && document.body.scrollHeight > window.innerHeight ) { 545 546 if ( scrollbarWidth === undefined ) { 547 $scrollDiv = $('<div style="width:50px;height:50px;overflow:scroll;" />').appendTo( 'body' ); 548 549 scrollbarWidth = $.fancybox.scrollbarWidth = $scrollDiv[0].offsetWidth - $scrollDiv[0].clientWidth; 550 551 $scrollDiv.remove(); 552 } 553 554 $( 'head' ).append( '<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar { margin-right: ' + scrollbarWidth + 'px; }</style>' ); 555 $( 'body' ).addClass( 'compensate-for-scrollbar' ); 556 } 557 } 558 559 560 // Build html markup and set references 561 // ==================================== 562 563 // Build html code for buttons and insert into main template 564 buttonStr = ''; 565 566 $.each( firstItemOpts.buttons, function( index, value ) { 567 buttonStr += ( firstItemOpts.btnTpl[ value ] || '' ); 568 }); 569 570 // Create markup from base template, it will be initially hidden to 571 // avoid unnecessary work like painting while initializing is not complete 572 $container = $( 573 self.translate( self, 574 firstItemOpts.baseTpl 575 .replace( '\{\{buttons\}\}', buttonStr ) 576 .replace( '\{\{arrows\}\}', firstItemOpts.btnTpl.arrowLeft + firstItemOpts.btnTpl.arrowRight ) 577 ) 578 ) 579 .attr( 'id', 'fancybox-container-' + self.id ) 580 .addClass( 'fancybox-is-hidden' ) 581 .addClass( firstItemOpts.baseClass ) 582 .data( 'FancyBox', self ) 583 .appendTo( firstItemOpts.parentEl ); 584 585 // Create object holding references to jQuery wrapped nodes 586 self.$refs = { 587 container : $container 588 }; 589 590 [ 'bg', 'inner', 'infobar', 'toolbar', 'stage', 'caption', 'navigation' ].forEach(function(item) { 591 self.$refs[ item ] = $container.find( '.fancybox-' + item ); 592 }); 593 594 self.trigger( 'onInit' ); 595 596 // Enable events, deactive previous instances 597 self.activate(); 598 599 // Build slides, load and reveal content 600 self.jumpTo( self.currIndex ); 601 }, 602 603 604 // Simple i18n support - replaces object keys found in template 605 // with corresponding values 606 // ============================================================ 607 608 translate : function( obj, str ) { 609 var arr = obj.opts.i18n[ obj.opts.lang ]; 610 611 return str.replace(/\{\{(\w+)\}\}/g, function(match, n) { 612 var value = arr[n]; 613 614 if ( value === undefined ) { 615 return match; 616 } 617 618 return value; 619 }); 620 }, 621 622 // Create array of gally item objects 623 // Check if each object has valid type and content 624 // =============================================== 625 626 createGroup : function ( content ) { 627 var self = this; 628 var items = $.makeArray( content ); 629 630 $.each(items, function( i, item ) { 631 var obj = {}, 632 opts = {}, 633 $item, 634 type, 635 found, 636 src, 637 srcParts; 638 639 // Step 1 - Make sure we have an object 640 // ==================================== 641 642 if ( $.isPlainObject( item ) ) { 643 644 // We probably have manual usage here, something like 645 // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] ) 646 647 obj = item; 648 opts = item.opts || item; 649 650 } else if ( $.type( item ) === 'object' && $( item ).length ) { 651 652 // Here we probably have jQuery collection returned by some selector 653 $item = $( item ); 654 655 opts = $item.data(); 656 opts = $.extend( {}, opts, opts.options || {} ); 657 658 // Here we store clicked element 659 opts.$orig = $item; 660 661 obj.src = opts.src || $item.attr( 'href' ); 662 663 // Assume that simple syntax is used, for example: 664 // `$.fancybox.open( $("#test"), {} );` 665 if ( !obj.type && !obj.src ) { 666 obj.type = 'inline'; 667 obj.src = item; 668 } 669 670 } else { 671 672 // Assume we have a simple html code, for example: 673 // $.fancybox.open( '<div><h1>Hi!</h1></div>' ); 674 675 obj = { 676 type : 'html', 677 src : item + '' 678 }; 679 680 } 681 682 // Each gallery object has full collection of options 683 obj.opts = $.extend( true, {}, self.opts, opts ); 684
685 // Do not merge buttons array 686 if ( $.isArray( opts.buttons ) ) { 687 obj.opts.buttons = opts.buttons; 688 } 689 690 691 // Step 2 - Make sure we have content type, if not - try to guess 692 // ============================================================== 693 694 type = obj.type || obj.opts.type; 695 src = obj.src || ''; 696 697 if ( !type && src ) { 698 if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) { 699 type = 'image'; 700 701 } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) { 702 type = 'pdf'; 703 704 } else if ( found = src.match(/\.(mp4|mov|ogv)((\?|#).*)?$/i) ) { 705 type = 'video'; 706 707 if ( !obj.opts.videoFormat ) { 708 obj.opts.videoFormat = 'video/' + ( found[1] === 'ogv' ? 'ogg' : found[1] ); 709 } 710 711 } else if ( src.charAt(0) === '#' ) { 712 type = 'inline'; 713 } 714 } 715 716 if ( type ) { 717 obj.type = type; 718 719 } else { 720 self.trigger( 'objectNeedsType', obj ); 721 } 722 723 724 // Step 3 - Some adjustments 725 // ========================= 726 727 obj.index = self.group.length; 728 729 // Check if $orig and $thumb objects exist 730 if ( obj.opts.$orig && !obj.opts.$orig.length ) { 731 delete obj.opts.$orig; 732 } 733 734 if ( !obj.opts.$thumb && obj.opts.$orig ) { 735 obj.opts.$thumb = obj.opts.$orig.find( 'img:first' ); 736 } 737 738 if ( obj.opts.$thumb && !obj.opts.$thumb.length ) { 739 delete obj.opts.$thumb; 740 } 741 742 // "caption" is a "special" option, it can be used to customize caption per gallery item .. 743 if ( $.type( obj.opts.caption ) === 'function' ) { 744 obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] ); 745 } 746 747 if ( $.type( self.opts.caption ) === 'function' ) { 748 obj.opts.caption = self.opts.caption.apply( item, [ self, obj ] ); 749 } 750 751 // Make sure we have caption as a string or jQuery object 752 if ( !( obj.opts.caption instanceof $ ) ) { 753 obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + ''; 754 } 755 756 // Check if url contains "filter" used to filter the content 757 // Example: "ajax.html #something" 758 if ( type === 'ajax' ) { 759 srcParts = src.split(/\s+/, 2); 760 761 if ( srcParts.length > 1 ) { 762 obj.src = srcParts.shift(); 763 764 obj.opts.filter = srcParts.shift(); 765 } 766 } 767 768 if ( obj.opts.smallBtn == 'auto' ) { 769 770 if ( $.inArray( type, ['html', 'inline', 'ajax'] ) > -1 ) { 771 obj.opts.toolbar = false; 772 obj.opts.smallBtn = true; 773 774 } else { 775 obj.opts.smallBtn = false; 776 } 777 778 } 779 780 // If the type is "pdf", then simply load file into iframe 781 if ( type === 'pdf' ) { 782 obj.type = 'iframe'; 783 784 obj.opts.iframe.preload = false; 785 } 786 787 // Hide all buttons and disable interactivity for modal items 788 if ( obj.opts.modal ) { 789 790 obj.opts = $.extend(true, obj.opts, { 791 // Remove buttons 792 infobar : 0, 793 toolbar : 0, 794 795 smallBtn : 0, 796 797 // Disable keyboard navigation 798 keyboard : 0, 799 800 // Disable some modules 801 slideShow : 0, 802 fullScreen : 0, 803 thumbs : 0, 804 touch : 0, 805 806 // Disable click event handlers 807 clickContent : false, 808 clickSlide : false, 809 clickOutside : false, 810 dblclickContent : false, 811 dblclickSlide : false, 812 dblclickOutside : false 813 }); 814 815 } 816 817 // Step 4 - Add processed object to group 818 // ====================================== 819 820 self.group.push( obj ); 821 822 }); 823 824 }, 825 826 827 // Attach an event handler functions for: 828 // - navigation buttons 829 // - browser scrolling, resizing; 830 // - focusing 831 // - keyboard 832 // - detect idle 833 // ====================================== 834 835 addEvents : function() { 836 var self = this; 837 838 self.removeEvents(); 839 840 // Make navigation elements clickable 841 self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) { 842 e.stopPropagation(); 843 e.preventDefault(); 844 845 self.close( e ); 846 847 }).on( 'click.fb-prev touchend.fb-prev', '[data-fancybox-prev]', function(e) { 848 e.stopPropagation(); 849 e.preventDefault(); 850 851 self.previous(); 852 853 }).on( 'click.fb-next touchend.fb-next', '[data-fancybox-next]', function(e) { 854 e.stopPropagation(); 855 e.preventDefault(); 856 857 self.next(); 858 859 }).on( 'click.fb', '[data-fancybox-zoom]', function(e) { 860 // Click handler for zoom button 861 self[ self.isScaledDown() ? 'scaleToActual' : 'scaleToFit' ](); 862 }); 863 864 865 // Handle page scrolling and browser resizing 866 $W.on('orientationchange.fb resize.fb', function(e) { 867 868 if ( e && e.originalEvent && e.originalEvent.type === "resize" ) { 869 870 requestAFrame(function() {
871 self.update(); 872 }); 873 874 } else { 875 876 self.$refs.stage.hide(); 877 878 setTimeout(function() { 879 self.$refs.stage.show(); 880 881 self.update(); 882 }, 600); 883 884 } 885 886 }); 887 888 // Trap keyboard focus inside of the modal, so the user does not accidentally tab outside of the modal 889 // (a.k.a. "escaping the modal") 890 $D.on('focusin.fb', function(e) { 891 var instance = $.fancybox ? $.fancybox.getInstance() : null; 892 893 if ( instance.isClosing || !instance.current || !instance.current.opts.trapFocus || $( e.target ).hasClass( 'fancybox-container' ) || $( e.target ).is( document ) ) { 894 return; 895 } 896 897 if ( instance && $( e.target ).css( 'position' ) !== 'fixed' && !instance.$refs.container.has( e.target ).length ) { 898 e.stopPropagation(); 899 900 instance.focus(); 901 902 // Sometimes page gets scrolled, set it back 903 $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); 904 } 905 }); 906 907 908 // Enable keyboard navigation 909 $D.on('keydown.fb', function (e) { 910 var current = self.current, 911 keycode = e.keyCode || e.which; 912 913 if ( !current || !current.opts.keyboard ) { 914 return; 915 } 916 917 if ( $(e.target).is('input') || $(e.target).is('textarea') ) { 918 return; 919 } 920 921 // Backspace and Esc keys 922 if ( keycode === 8 || keycode === 27 ) { 923 e.preventDefault(); 924 925 self.close( e ); 926 927 return; 928 } 929 930 // Left arrow and Up arrow 931 if ( keycode === 37 || keycode === 38 ) { 932 e.preventDefault(); 933 934 self.previous(); 935 936 return; 937 } 938 939 // Righ arrow and Down arrow 940 if ( keycode === 39 || keycode === 40 ) { 941 e.preventDefault(); 942 943 self.next(); 944 945 return; 946 } 947 948 self.trigger('afterKeydown', e, keycode); 949 }); 950 951 952 // Hide controls after some inactivity period 953 if ( self.group[ self.currIndex ].opts.idleTime ) { 954 self.idleSecondsCounter = 0; 955 956 $D.on('mousemove.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle', function(e) { 957 self.idleSecondsCounter = 0; 958 959 if ( self.isIdle ) { 960 self.showControls(); 961 } 962 963 self.isIdle = false; 964 }); 965 966 self.idleInterval = window.setInterval(function() { 967 self.idleSecondsCounter++; 968 969 if ( self.idleSecondsCounter >= self.group[ self.currIndex ].opts.idleTime && !self.isDragging ) { 970 self.isIdle = true; 971 self.idleSecondsCounter = 0; 972 973 self.hideControls(); 974 } 975 976 }, 1000); 977 } 978 979 }, 980 981 982 // Remove events added by the core 983 // =============================== 984 985 removeEvents : function() { 986 var self = this; 987 988 $W.off( 'orientationchange.fb resize.fb' ); 989 $D.off( 'focusin.fb keydown.fb .fb-idle' ); 990 991 this.$refs.container.off( '.fb-close .fb-prev .fb-next' ); 992 993 if ( self.idleInterval ) { 994 window.clearInterval( self.idleInterval ); 995 996 self.idleInterval = null; 997 } 998 }, 999 1000 1001 // Change to previous gallery item 1002 // =============================== 1003 1004 previous : function( duration ) { 1005 return this.jumpTo( this.currPos - 1, duration ); 1006 }, 1007 1008 1009 // Change to next gallery item 1010 // =========================== 1011 1012 next : function( duration ) { 1013 return this.jumpTo( this.currPos + 1, duration ); 1014 }, 1015 1016 1017 // Switch to selected gallery item 1018 // =============================== 1019 1020 jumpTo : function ( pos, duration, slide ) { 1021 var self = this, 1022 firstRun, 1023 loop, 1024 current, 1025 previous, 1026 canvasWidth, 1027 currentPos, 1028 transitionProps; 1029 1030 var groupLen = self.group.length; 1031 1032 if ( self.isDragging || self.isClosing || ( self.isAnimating && self.firstRun ) ) { 1033 return; 1034 } 1035 1036 pos = parseInt( pos, 10 ); 1037 loop = self.current ? self.current.opts.loop : self.opts.loop; 1038 1039 if ( !loop && ( pos < 0 || pos >= groupLen ) ) { 1040 return false; 1041 } 1042 1043 firstRun = self.firstRun = ( self.firstRun === null ); 1044 1045 if ( groupLen < 2 && !firstRun && !!self.isDragging ) { 1046 return; 1047 } 1048 1049 previous = self.current; 1050 1051 self.prevIndex = self.currIndex; 1052 self.prevPos = self.currPos; 1053 1054 // Create slides 1055 current = self.createSlide( pos ); 1056 1057 if ( groupLen > 1 ) { 1058 if ( loop || current.index > 0 ) { 1059 self.createSlide( pos - 1 ); 1060 } 1061 1062 if ( loop || current.index < groupLen - 1 ) { 1063 self.createSlide( pos + 1 ); 1064 } 1065 } 1066 1067 self.current = current; 1068 self.currIndex = current.index; 1069 self.currPos = current.pos; 1070 1071 self.trigger( 'beforeShow', firstRun ); 1072 1073 self.updateControls(); 1074 1075 currentPos = $.fancybox.getTranslate( current.$slide ); 1076 1077 current.isMoved = ( currentPos.left !== 0 || currentPos.top !== 0 ) && !current.$slide.hasClass( 'fancybox-animated' ); 1078 current.forcedDuration = undefined; 1079 1080 if ( $.isNumeric( duration ) ) { 1081 current.forcedDuration = duration; 1082 } else { 1083 duration = current.opts[ firstRun ? 'animationDuration' : 'transitionDuration' ]; 1084 } 1085 1086 duration = parseInt( duration, 10 ); 1087 1088 // Fresh start - reveal container, current slide and start loading content 1089 if ( firstRun ) { 1090 1091 if ( current.opts.animationEffect && duration ) { 1092 self.$refs.container.css( 'transition-duration', duration + 'ms' ); 1093 } 1094 1095 self.$refs.container.removeClass( 'fancybox-is-hidden' ); 1096 1097 forceRedraw( self.$refs.container ); 1098 1099 self.$refs.container.addClass( 'fancybox-is-open' ); 1100 1101 // Make first slide visible (to display loading icon, if needed) 1102 current.$slide.addClass( 'fancybox-slide--current' ); 1103 1104 self.loadSlide( current ); 1105 1106 self.preload( 'image' ); 1107 1108 return; 1109 } 1110 1111 // Clean up 1112 $.each(self.slides, function( index, slide ) { 1113 $.fancybox.stop( slide.$slide ); 1114 }); 1115 1116 // Make current that slide is visible even if content is still loading 1117 current.$slide.removeClass( 'fancybox-slide--next fancybox-slide--previous' ).addClass( 'fancybox-slide--current' ); 1118 1119 // If slides have been dragged, animate them to correct position 1120 if ( current.isMoved ) { 1121 canvasWidth = Math.round( current.$slide.width() ); 1122 1123 $.each(self.slides, function( index, slide ) { 1124 var pos = slide.pos - current.pos; 1125 1126 $.fancybox.animate( slide.$slide, { 1127 top : 0, 1128 left : ( pos * canvasWidth ) + ( pos * slide.opts.gutter ) 1129 }, duration, function() { 1130 1131 slide.$slide.removeAttr('style').removeClass( 'fancybox-slide--next fancybox-slide--previous' ); 1132 1133 if ( slide.pos === self.currPos ) { 1134 current.isMoved = false; 1135 1136 self.complete(); 1137 } 1138 }); 1139 }); 1140 1141 } else { 1142 self.$refs.stage.children().removeAttr( 'style' ); 1143 } 1144 1145 // Start transition that reveals current content 1146 // or wait when it will be loaded 1147 1148 if ( current.isLoaded ) { 1149 self.revealContent( current ); 1150 1151 } else { 1152 self.loadSlide( current ); 1153 } 1154 1155 self.preload( 'image' ); 1156 1157 if ( previous.pos === current.pos ) { 1158 return; 1159 } 1160 1161 // Handle previous slide 1162 // ===================== 1163 1164 transitionProps = 'fancybox-slide--' + ( previous.pos > current.pos ? 'next' : 'previous' ); 1165 1166 previous.$slide.removeClass( 'fancybox-slide--complete fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ); 1167 1168 previous.isComplete = false; 1169 1170 if ( !duration || ( !current.isMoved && !current.opts.transitionEffect ) ) { 1171 return; 1172 } 1173 1174 if ( current.isMoved ) { 1175 previous.$slide.addClass( transitionProps ); 1176 1177 } else { 1178 1179 transitionProps = 'fancybox-animated ' + transitionProps + ' fancybox-fx-' + current.opts.transitionEffect; 1180 1181 $.fancybox.animate( previous.$slide, transitionProps, duration, function() { 1182 previous.$slide.removeClass( transitionProps ).removeAttr( 'style' ); 1183 }); 1184 1185 } 1186 1187 }, 1188 1189 1190 // Create new "slide" element 1191 // These are gallery items that are actually added to DOM 1192 // ======================================================= 1193 1194 createSlide : function( pos ) { 1195 1196 var self = this; 1197 var $slide; 1198 var index; 1199 1200 index = pos % self.group.length; 1201 index = index < 0 ? self.group.length + index : index; 1202 1203 if ( !self.slides[ pos ] && self.group[ index ] ) {
1204 $slide = $('<div class="fancybox-slide"></div>').appendTo( self.$refs.stage ); 1205 1206 self.slides[ pos ] = $.extend( true, {}, self.group[ index ], { 1207 pos : pos, 1208 $slide : $slide, 1209 isLoaded : false, 1210 }); 1211 1212 self.updateSlide( self.slides[ pos ] ); 1213 } 1214 1215 return self.slides[ pos ]; 1216 }, 1217 1218 1219 // Scale image to the actual size of the image 1220 // =========================================== 1221 1222 scaleToActual : function( x, y, duration ) { 1223 1224 var self = this; 1225 1226 var current = self.current; 1227 var $what = current.$content; 1228 1229 var imgPos, posX, posY, scaleX, scaleY; 1230 1231 var canvasWidth = parseInt( current.$slide.width(), 10 ); 1232 var canvasHeight = parseInt( current.$slide.height(), 10 ); 1233 1234 var newImgWidth = current.width; 1235 var newImgHeight = current.height; 1236 1237 if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating ) { 1238 return; 1239 } 1240 1241 $.fancybox.stop( $what ); 1242 1243 self.isAnimating = true; 1244 1245 x = x === undefined ? canvasWidth * 0.5 : x; 1246 y = y === undefined ? canvasHeight * 0.5 : y; 1247 1248 imgPos = $.fancybox.getTranslate( $what ); 1249 1250 scaleX = newImgWidth / imgPos.width; 1251 scaleY = newImgHeight / imgPos.height; 1252 1253 // Get center position for original image 1254 posX = ( canvasWidth * 0.5 - newImgWidth * 0.5 ); 1255 posY = ( canvasHeight * 0.5 - newImgHeight * 0.5 ); 1256 1257 // Make sure image does not move away from edges 1258 if ( newImgWidth > canvasWidth ) { 1259 posX = imgPos.left * scaleX - ( ( x * scaleX ) - x ); 1260 1261 if ( posX > 0 ) { 1262 posX = 0; 1263 } 1264 1265 if ( posX < canvasWidth - newImgWidth ) { 1266 posX = canvasWidth - newImgWidth; 1267 } 1268 } 1269 1270 if ( newImgHeight > canvasHeight) { 1271 posY = imgPos.top * scaleY - ( ( y * scaleY ) - y ); 1272 1273 if ( posY > 0 ) { 1274 posY = 0; 1275 } 1276 1277 if ( posY < canvasHeight - newImgHeight ) { 1278 posY = canvasHeight - newImgHeight; 1279 } 1280 } 1281 1282 self.updateCursor( newImgWidth, newImgHeight ); 1283 1284 $.fancybox.animate( $what, { 1285 top : posY, 1286 left : posX, 1287 scaleX : scaleX, 1288 scaleY : scaleY 1289 }, duration || 330, function() { 1290 self.isAnimating = false; 1291 }); 1292 1293 // Stop slideshow 1294 if ( self.SlideShow && self.SlideShow.isActive ) { 1295 self.SlideShow.stop(); 1296 } 1297 }, 1298 1299 1300 // Scale image to fit inside parent element 1301 // ======================================== 1302 1303 scaleToFit : function( duration ) { 1304 1305 var self = this; 1306 1307 var current = self.current; 1308 var $what = current.$content; 1309 var end; 1310 1311 if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating ) { 1312 return; 1313 } 1314 1315 $.fancybox.stop( $what ); 1316 1317 self.isAnimating = true; 1318 1319 end = self.getFitPos( current ); 1320 1321 self.updateCursor( end.width, end.height ); 1322 1323 $.fancybox.animate( $what, { 1324 top : end.top, 1325 left : end.left, 1326 scaleX : end.width / $what.width(), 1327 scaleY : end.height / $what.height() 1328 }, duration || 330, function() { 1329 self.isAnimating = false; 1330 }); 1331 1332 }, 1333 1334 // Calculate image size to fit inside viewport 1335 // =========================================== 1336 1337 getFitPos : function( slide ) { 1338 var self = this; 1339 var $what = slide.$content; 1340 1341 var imgWidth = slide.width; 1342 var imgHeight = slide.height; 1343 1344 var margin = slide.opts.margin; 1345 1346 var canvasWidth, canvasHeight, minRatio, width, height; 1347 1348 if ( !$what || !$what.length || ( !imgWidth && !imgHeight) ) { 1349 return false; 1350 } 1351 1352 // Convert "margin to CSS style: [ top, right, bottom, left ] 1353 if ( $.type( margin ) === "number" ) { 1354 margin = [ margin, margin ]; 1355 } 1356 1357 if ( margin.length == 2 ) { 1358 margin = [ margin[0], margin[1], margin[0], margin[1] ]; 1359 } 1360 1361 // We can not use $slide width here, because it can have different diemensions while in transiton 1362 canvasWidth = parseInt( self.$refs.stage.width(), 10 ) - ( margin[ 1 ] + margin[ 3 ] ); 1363 canvasHeight = parseInt( self.$refs.stage.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] ); 1364 1365 minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight ); 1366 1367 width = Math.floor( minRatio * imgWidth ); 1368 height = Math.floor( minRatio * imgHeight ); 1369 1370 // Use floor rounding to make sure it really fits 1371 return { 1372 top : Math.floor( ( canvasHeight - height ) * 0.5 ) + margin[ 0 ], 1373 left : Math.floor( ( canvasWidth - width ) * 0.5 ) + margin[ 3 ], 1374 width : width, 1375 height : height 1376 }; 1377 1378 }, 1379 1380 1381 // Update content size and position for all slides 1382 // ============================================== 1383 1384 update : function() { 1385 var self = this; 1386 1387 $.each( self.slides, function( key, slide ) { 1388 self.updateSlide( slide ); 1389 }); 1390 }, 1391 1392 1393 // Update slide content position and size 1394 // ====================================== 1395 1396 updateSlide : function( slide, duration ) { 1397 var self = this, 1398 $what = slide && slide.$content; 1399 1400 if ( $what && ( slide.width || slide.height ) ) { 1401 self.isAnimating = false; 1402 1403 $.fancybox.stop( $what ); 1404 1405 $.fancybox.setTranslate( $what, self.getFitPos( slide ) ); 1406 1407 if ( slide.pos === self.currPos ) {
1408 self.updateCursor(); 1409 } 1410 } 1411 1412 slide.$slide.trigger( 'refresh' ); 1413 1414 self.trigger( 'onUpdate', slide ); 1415 1416 }, 1417 1418 1419 // Horizontally center slide 1420 // ========================= 1421 1422 centerSlide : function( slide, duration ) { 1423 var self = this, canvasWidth, pos; 1424 1425 if ( self.current ) { 1426 canvasWidth = Math.round( slide.$slide.width() ); 1427 pos = slide.pos - self.current.pos; 1428 1429 $.fancybox.animate( slide.$slide, { 1430 top : 0, 1431 left : ( pos * canvasWidth ) + ( pos * slide.opts.gutter ), 1432 opacity : 1 1433 }, duration === undefined ? 0 : duration, null, false); 1434 } 1435 }, 1436 1437 1438 // Update cursor style depending if content can be zoomed 1439 // ====================================================== 1440 1441 updateCursor : function( nextWidth, nextHeight ) { 1442 1443 var self = this; 1444 var isScaledDown; 1445 1446 var $container = self.$refs.container.removeClass( 'fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-drag fancybox-can-zoomOut' ); 1447 1448 if ( !self.current || self.isClosing ) { 1449 return; 1450 } 1451 1452 if ( self.isZoomable() ) { 1453 1454 $container.addClass( 'fancybox-is-zoomable' ); 1455 1456 if ( nextWidth !== undefined && nextHeight !== undefined ) { 1457 isScaledDown = nextWidth < self.current.width && nextHeight < self.current.height; 1458 1459 } else { 1460 isScaledDown = self.isScaledDown(); 1461 } 1462 1463 if ( isScaledDown ) { 1464 1465 // If image is scaled down, then, obviously, it can be zoomed to full size 1466 $container.addClass( 'fancybox-can-zoomIn' ); 1467 1468 } else { 1469 1470 if ( self.current.opts.touch ) { 1471 1472 // If image size ir largen than available available and touch module is not disable, 1473 // then user can do panning 1474 $container.addClass( 'fancybox-can-drag' ); 1475 1476 } else { 1477 $container.addClass( 'fancybox-can-zoomOut' ); 1478 } 1479 1480 } 1481 1482 } else if ( self.current.opts.touch ) { 1483 $container.addClass( 'fancybox-can-drag' ); 1484 } 1485 1486 }, 1487 1488 1489 // Check if current slide is zoomable 1490 // ================================== 1491 1492 isZoomable : function() { 1493 1494 var self = this; 1495 1496 var current = self.current; 1497 var fitPos; 1498 1499 if ( !current || self.isClosing ) { 1500 return; 1501 } 1502 1503 // Assume that slide is zoomable if 1504 // - image is loaded successfuly 1505 // - click action is "zoom" 1506 // - actual size of the image is smaller than available area 1507 if ( current.type === 'image' && current.isLoaded && !current.hasError && 1508 ( current.opts.clickContent === 'zoom' || ( $.isFunction( current.opts.clickContent ) && current.opts.clickContent( current ) === "zoom" ) ) 1509 ) { 1510 1511 fitPos = self.getFitPos( current ); 1512 1513 if ( current.width > fitPos.width || current.height > fitPos.height ) { 1514 return true; 1515 } 1516 1517 } 1518 1519 return false; 1520 1521 }, 1522 1523 1524 // Check if current image dimensions are smaller than actual 1525 // ========================================================= 1526 1527 isScaledDown : function() { 1528 1529 var self = this; 1530 1531 var current = self.current; 1532 var $what = current.$content; 1533 1534 var rez = false; 1535 1536 if ( $what ) { 1537 rez = $.fancybox.getTranslate( $what ); 1538 rez = rez.width < current.width || rez.height < current.height; 1539 } 1540 1541 return rez; 1542 1543 }, 1544 1545 1546 // Check if image dimensions exceed parent element 1547 // =============================================== 1548 1549 canPan : function() { 1550 1551 var self = this; 1552 1553 var current = self.current; 1554 var $what = current.$content; 1555 1556 var rez = false; 1557 1558 if ( $what ) { 1559 rez = self.getFitPos( current ); 1560 rez = Math.abs( $what.width() - rez.width ) > 1 || Math.abs( $what.height() - rez.height ) > 1; 1561 } 1562 1563 return rez; 1564 1565 }, 1566 1567 1568 // Load content into the slide 1569 // =========================== 1570 1571 loadSlide : function( slide ) { 1572 1573 var self = this, type, $slide; 1574 var ajaxLoad; 1575 1576 if ( slide.isLoading ) { 1577 return; 1578 } 1579 1580 if ( slide.isLoaded ) { 1581 return; 1582 } 1583 1584 slide.isLoading = true; 1585 1586 self.trigger( 'beforeLoad', slide ); 1587 1588 type = slide.type; 1589 $slide = slide.$slide; 1590 1591 $slide 1592 .off( 'refresh' ) 1593 .trigger( 'onReset' ) 1594 .addClass( 'fancybox-slide--' + ( type || 'unknown' ) ) 1595 .addClass( slide.opts.slideClass ); 1596 1597 // Create content depending on the type 1598 1599 switch ( type ) { 1600 1601 case 'image': 1602 1603 self.setImage( slide ); 1604 1605 break; 1606 1607 case 'iframe': 1608 1609 self.setIframe( slide ); 1610 1611 break; 1612 1613 case 'html': 1614 1615 self.setContent( slide, slide.src || slide.content ); 1616 1617 break; 1618 1619 case 'inline': 1620 1621 if ( $( slide.src ).length ) { 1622 self.setContent( slide, $( slide.src ) ); 1623 1624 } else { 1625 self.setError( slide ); 1626 } 1627 1628 break; 1629 1630 case 'ajax': 1631 1632 self.showLoading( slide ); 1633 1634 ajaxLoad = $.ajax( $.extend( {}, slide.opts.ajax.settings, { 1635 url : slide.src, 1636 success : function ( data, textStatus ) { 1637 1638 if ( textStatus === 'success' ) { 1639 self.setContent( slide, data ); 1640 } 1641 1642 }, 1643 error : function ( jqXHR, textStatus ) { 1644 1645 if ( jqXHR && textStatus !== 'abort' ) { 1646 self.setError( slide ); 1647 } 1648 1649 } 1650 })); 1651 1652 $slide.one( 'onReset', function () { 1653 ajaxLoad.abort(); 1654 }); 1655 1656 break; 1657 1658 case 'video' : 1659 1660 self.setContent( slide, 1661 '<video controls>' + 1662 '<source src="' + slide.src + '" type="' + slide.opts.videoFormat + '">' + 1663 'Your browser doesn\'t support HTML5 video' + 1664 '</video>' 1665 ); 1666 1667 break; 1668 1669 default: 1670 1671 self.setError( slide ); 1672 1673 break; 1674 1675 } 1676 1677 return true; 1678 1679 }, 1680 1681 1682 // Use thumbnail image, if possible 1683 // ================================ 1684 1685 setImage : function( slide ) { 1686 1687 var self = this; 1688 var srcset = slide.opts.srcset || slide.opts.image.srcset; 1689
vendor: 4,400 bytes, lines 1690-1817
1690 var found, temp, pxRatio, windowWidth; 1691 1692 // If we have "srcset", then we need to find matching "src" value. 1693 // This is necessary, because when you set an src attribute, the browser will preload the image 1694 // before any javascript or even CSS is applied. 1695 if ( srcset ) { 1696 pxRatio = window.devicePixelRatio || 1; 1697 windowWidth = window.innerWidth * pxRatio; 1698 1699 temp = srcset.split(',').map(function ( el ) { 1700 var ret = {}; 1701 1702 el.trim().split(/\s+/).forEach(function ( el, i ) { 1703 var value = parseInt( el.substring(0, el.length - 1), 10 ); 1704 1705 if ( i === 0 ) { 1706 return ( ret.url = el ); 1707 } 1708 1709 if ( value ) { 1710 ret.value = value; 1711 ret.postfix = el[ el.length - 1 ]; 1712 } 1713 1714 }); 1715 1716 return ret; 1717 }); 1718 1719 // Sort by value 1720 temp.sort(function (a, b) { 1721 return a.value - b.value; 1722 }); 1723 1724 // Ok, now we have an array of all srcset values 1725 for ( var j = 0; j < temp.length; j++ ) { 1726 var el = temp[ j ]; 1727 1728 if ( ( el.postfix === 'w' && el.value >= windowWidth ) || ( el.postfix === 'x' && el.value >= pxRatio ) ) { 1729 found = el; 1730 break; 1731 } 1732 } 1733 1734 // If not found, take the last one 1735 if ( !found && temp.length ) { 1736 found = temp[ temp.length - 1 ]; 1737 } 1738 1739 if ( found ) { 1740 slide.src = found.url; 1741 1742 // If we have default width/height values, we can calculate height for matching source 1743 if ( slide.width && slide.height && found.postfix == 'w' ) { 1744 slide.height = ( slide.width / slide.height ) * found.value; 1745 slide.width = found.value; 1746 } 1747 } 1748 } 1749 1750 // This will be wrapper containing both ghost and actual image 1751 slide.$content = $('<div class="fancybox-image-wrap"></div>') 1752 .addClass( 'fancybox-is-hidden' ) 1753 .appendTo( slide.$slide ); 1754 1755 1756 // If we have a thumbnail, we can display it while actual image is loading 1757 // Users will not stare at black screen and actual image will appear gradually 1758 if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) { 1759 1760 slide.width = slide.opts.width; 1761 slide.height = slide.opts.height; 1762 1763 slide.$ghost = $('<img />') 1764 .one('error', function() { 1765 1766 $(this).remove(); 1767 1768 slide.$ghost = null; 1769 1770 self.setBigImage( slide ); 1771 1772 }) 1773 .one('load', function() { 1774 1775 self.afterLoad( slide ); 1776 1777 self.setBigImage( slide ); 1778 1779 }) 1780 .addClass( 'fancybox-image' ) 1781 .appendTo( slide.$content ) 1782 .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) ); 1783 1784 } else { 1785 1786 self.setBigImage( slide ); 1787 1788 } 1789 1790 }, 1791 1792 1793 // Create full-size image 1794 // ====================== 1795 1796 setBigImage : function ( slide ) { 1797 var self = this; 1798 var $img = $('<img />'); 1799 1800 slide.$image = $img 1801 .one('error', function() { 1802 1803 self.setError( slide ); 1804 1805 }) 1806 .one('load', function() { 1807 1808 // Clear timeout that checks if loading icon needs to be displayed 1809 clearTimeout( slide.timouts ); 1810 1811 slide.timouts = null; 1812 1813 if ( self.isClosing ) { 1814 return; 1815 } 1816 1817 slide.width = slide.opts.width || this.naturalWi
1817dth; 1818 slide.height = slide.opts.height || this.naturalHeight; 1819 1820 if ( slide.opts.image.srcset ) { 1821 $img.attr( 'sizes', '100vw' ).attr( 'srcset', slide.opts.image.srcset ); 1822 } 1823 1824 self.hideLoading( slide ); 1825 1826 if ( slide.$ghost ) { 1827 1828 slide.timouts = setTimeout(function() { 1829 slide.timouts = null; 1830 1831 slide.$ghost.hide(); 1832 1833 }, Math.min( 300, Math.max( 1000, slide.height / 1600 ) ) ); 1834 1835 } else { 1836 self.afterLoad( slide ); 1837 } 1838 1839 }) 1840 .addClass( 'fancybox-image' ) 1841 .attr('src', slide.src) 1842 .appendTo( slide.$content ); 1843 1844 if ( ( $img[0].complete || $img[0].readyState == "complete" ) && $img[0].naturalWidth && $img[0].naturalHeight ) { 1845 $img.trigger( 'load' ); 1846 1847 } else if( $img[0].error ) { 1848 $img.trigger( 'error' ); 1849 1850 } else { 1851 1852 slide.timouts = setTimeout(function() { 1853 if ( !$img[0].complete && !slide.hasError ) { 1854 self.showLoading( slide ); 1855 } 1856 1857 }, 100); 1858 1859 } 1860 1861 }, 1862 1863 1864 // Create iframe wrapper, iframe and bindings 1865 // ========================================== 1866 1867 setIframe : function( slide ) { 1868 var self = this, 1869 opts = slide.opts.iframe, 1870 $slide = slide.$slide, 1871 $iframe; 1872 1873 slide.$content = $('<div class="fancybox-content' + ( opts.preload ? ' fancybox-is-hidden' : '' ) + '"></div>') 1874 .css( opts.css ) 1875 .appendTo( $slide ); 1876 1877 $iframe = $( opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) ) 1878 .attr( opts.attr ) 1879 .appendTo( slide.$content ); 1880 1881 if ( opts.preload ) { 1882 1883 self.showLoading( slide ); 1884 1885 // Unfortunately, it is not always possible to determine if iframe is successfully loaded 1886 // (due to browser security policy) 1887 1888 $iframe.on('load.fb error.fb', function(e) { 1889 this.isReady = 1; 1890 1891 slide.$slide.trigger( 'refresh' ); 1892 1893 self.afterLoad( slide ); 1894 }); 1895 1896 // Recalculate iframe content size 1897 // =============================== 1898 1899 $slide.on('refresh.fb', function() { 1900 var $wrap = slide.$content, 1901 frameWidth = opts.css.width, 1902 frameHeight = opts.css.height, 1903 scrollWidth, 1904 $contents, 1905 $body; 1906 1907 if ( $iframe[0].isReady !== 1 ) { 1908 return; 1909 } 1910 1911 // Check if content is accessible, 1912 // it will fail if frame is not with the same origin 1913 1914 try { 1915 $contents = $iframe.contents(); 1916 $body = $contents.find('body'); 1917 1918 } catch (ignore) {} 1919 1920 // Calculate dimensions for the wrapper 1921 if ( $body && $body.length ) { 1922 1923 if ( frameWidth === undefined ) { 1924 scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth; 1925 1926 frameWidth = Math.ceil( $body.outerWidth(true) + ( $wrap.width() - scrollWidth ) ); 1927 frameWidth += $wrap.outerWidth() - $wrap.innerWidth(); 1928 } 1929 1930 if ( frameHeight === undefined ) { 1931 frameHeight = Math.ceil( $body.outerHeight(true) ); 1932 frameHeight += $wrap.outerHeight() - $wrap.innerHeight(); 1933 } 1934 1935 // Resize wrapper to fit iframe content 1936 if ( frameWidth ) { 1937 $wrap.width( frameWidth ); 1938 } 1939 1940 if ( frameHeight ) { 1941 $wrap.height( frameHeight ); 1942 } 1943 } 1944 1945 $wrap.removeClass( 'fancybox-is-hidden' ); 1946 1947 }); 1948 1949 } else { 1950 1951 this.afterLoad( slide ); 1952 1953 } 1954 1955 $iframe.attr( 'src', slide.src ); 1956 1957 if ( slide.opts.smallBtn === true ) {
1958 slide.$content.prepend( self.translate( slide, slide.opts.btnTpl.smallBtn ) ); 1959 } 1960 1961 // Remove iframe if closing or changing gallery item 1962 $slide.one( 'onReset', function () { 1963 1964 // This helps IE not to throw errors when closing 1965 try { 1966 1967 $( this ).find( 'iframe' ).hide().attr( 'src', '//about:blank' ); 1968 1969 } catch ( ignore ) {} 1970 1971 $( this ).empty(); 1972 1973 slide.isLoaded = false; 1974 1975 }); 1976 1977 }, 1978 1979 1980 // Wrap and append content to the slide 1981 // ====================================== 1982 1983 setContent : function ( slide, content ) { 1984 1985 var self = this; 1986 1987 if ( self.isClosing ) { 1988 return; 1989 } 1990 1991 self.hideLoading( slide ); 1992 1993 slide.$slide.empty(); 1994 1995 if ( isQuery( content ) && content.parent().length ) { 1996 1997 // If content is a jQuery object, then it will be moved to the slide. 1998 // The placeholder is created so we will know where to put it back. 1999 // If user is navigating gallery fast, then the content might be already inside fancyBox 2000 // ===================================================================================== 2001 2002 // Make sure content is not already moved to fancyBox 2003 content.parent( '.fancybox-slide--inline' ).trigger( 'onReset' ); 2004 2005 // Create temporary element marking original place of the content 2006 slide.$placeholder = $( '<div></div>' ).hide().insertAfter( content ); 2007 2008 // Make sure content is visible 2009 content.css('display', 'inline-block'); 2010 2011 } else if ( !slide.hasError ) { 2012 2013 // If content is just a plain text, try to convert it to html 2014 if ( $.type( content ) === 'string' ) { 2015 content = $('<div>').append( $.trim( content ) ).contents(); 2016 2017 // If we have text node, then add wrapping element to make vertical alignment work 2018 if ( content[0].nodeType === 3 ) { 2019 content = $('<div>').html( content ); 2020 } 2021 } 2022 2023 // If "filter" option is provided, then filter content 2024 if ( slide.opts.filter ) { 2025 content = $('<div>').html( content ).find( slide.opts.filter ); 2026 } 2027 2028 } 2029 2030 slide.$slide.one('onReset', function () { 2031 2032 // Pause all html5 video/audio 2033 $( this ).find( 'video,audio' ).trigger( 'pause' ); 2034 2035 // Put content back 2036 if ( slide.$placeholder ) { 2037 slide.$placeholder.after( content.hide() ).remove(); 2038 2039 slide.$placeholder = null; 2040 } 2041 2042 // Remove custom close button 2043 if ( slide.$smallBtn ) { 2044 slide.$smallBtn.remove(); 2045 2046 slide.$smallBtn = null; 2047 } 2048 2049 // Remove content and mark slide as not loaded 2050 if ( !slide.hasError ) { 2051 $(this).empty(); 2052 2053 slide.isLoaded = false; 2054 } 2055 2056 }); 2057 2058 slide.$content = $( content ).appendTo( slide.$slide ); 2059 2060 this.afterLoad( slide ); 2061 }, 2062 2063 // Display error message 2064 // ===================== 2065 2066 setError : function ( slide ) { 2067 2068 slide.hasError = true; 2069 2070 slide.$slide.removeClass( 'fancybox-slide--' + slide.type ); 2071 2072 this.setContent( slide, this.translate( slide, slide.opts.errorTpl ) ); 2073 2074 }, 2075 2076 2077 // Show loading icon inside the slide 2078 // ================================== 2079 2080 showLoading : function( slide ) { 2081 2082 var self = this; 2083 2084 slide = slide || self.current; 2085 2086 if ( slide && !slide.$spinner ) { 2087 slide.$spinner = $( self.opts.spinnerTpl ).appendTo( slide.$slide ); 2088 } 2089 2090 }, 2091 2092 // Remove loading icon from the slide 2093 // ================================== 2094 2095 hideLoading : function( slide ) { 2096 2097 var self = this; 2098 2099 slide = slide || self.current; 2100 2101 if ( slide && slide.$spinner ) { 2102 slide.$spinner.remove(); 2103 2104 delete slide.$spinner; 2105 } 2106 2107 }, 2108 2109 2110 // Adjustments after slide content has been loaded 2111 // =============================================== 2112 2113 afterLoad : function( slide ) { 2114 2115 var self = this; 2116 2117 if ( self.isClosing ) { 2118 return; 2119 } 2120 2121 slide.isLoading = false; 2122 slide.isLoaded = true; 2123 2124 self.trigger( 'afterLoad', slide ); 2125 2126 self.hideLoading( slide ); 2127 2128 if ( slide.opts.smallBtn && !slide.$smallBtn ) { 2129 slide.$smallBtn = $( self.translate( slide, slide.opts.btnTpl.smallBtn ) ).appendTo( slide.$content.filter('div,form').first() ); 2130 } 2131 2132 if ( slide.opts.protect && slide.$content && !slide.hasError ) { 2133 2134 // Disable right click 2135 slide.$content.on( 'contextmenu.fb', function( e ) { 2136 if ( e.button == 2 ) { 2137 e.preventDefault(); 2138 } 2139 2140 return true; 2141 }); 2142 2143 // Add fake element on top of the image 2144 // This makes a bit harder for user to select image 2145 if ( slide.type === 'image' ) { 2146 $( '<div class="fancybox-spaceball"></div>' ).appendTo( slide.$content ); 2147 } 2148 2149 } 2150 2151 self.revealContent( slide ); 2152 2153 }, 2154 2155 2156 // Make content visible 2157 // This method is called right after content has been loaded or 2158 // user navigates gallery and transition should start 2159 // ============================================================ 2160 2161 revealContent : function( slide ) { 2162 2163 var self = this; 2164 var $slide = slide.$slide; 2165 2166 var effect, effectClassName, duration, opacity, end, start = false; 2167 2168 effect = slide.opts[ self.firstRun ? 'animationEffect' : 'transitionEffect' ]; 2169 duration = slide.opts[ self.firstRun ? 'animationDuration' : 'transitionDuration' ]; 2170 2171 duration = parseInt( slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10 ); 2172 2173 if ( slide.isMoved || slide.pos !== self.currPos || !duration ) { 2174 effect = false; 2175 } 2176 2177 // Check if can zoom 2178 if ( effect === 'zoom' && !( slide.pos === self.currPos && duration && slide.type === 'image' && !slide.hasError && ( start = self.getThumbPos( slide ) ) ) ) { 2179 effect = 'fade'; 2180 } 2181 2182 // Zoom animation 2183 // ============== 2184 2185 if ( effect === 'zoom' ) { 2186 end = self.getFitPos( slide ); 2187 2188 end.scaleX = end.width / start.width; 2189 end.scaleY = end.height / start.height; 2190 2191 delete end.width; 2192 delete end.height; 2193 2194 // Check if we need to animate opacity 2195 opacity = slide.opts.zoomOpacity; 2196 2197 if ( opacity == 'auto' ) { 2198 opacity = Math.abs( slide.width / slide.height - start.width / start.height ) > 0.1; 2199 } 2200 2201 if ( opacity ) { 2202 start.opacity = 0.1; 2203 end.opacity = 1; 2204 } 2205 2206 // Draw image at start position 2207 $.fancybox.setTranslate( slide.$content.removeClass( 'fancybox-is-hidden' ), start ); 2208 2209 forceRedraw( slide.$content ); 2210 2211 // Start animation 2212 $.fancybox.animate( slide.$content, end, duration, function() { 2213 self.complete(); 2214 }); 2215 2216 return; 2217 } 2218 2219 self.updateSlide( slide ); 2220 2221 2222 // Simply show content 2223 // =================== 2224 2225 if ( !effect ) { 2226 forceRedraw( $slide ); 2227 2228 slide.$content.removeClass( 'fancybox-is-hidden' ); 2229 2230 if ( slide.pos === self.currPos ) { 2231 self.complete(); 2232 } 2233 2234 return; 2235 } 2236 2237 $.fancybox.stop( $slide ); 2238 2239 effectClassName = 'fancybox-animated fancybox-slide--' + ( slide.pos >= self.prevPos ? 'next' : 'previous' ) + ' fancybox-fx-' + effect; 2240 2241 $slide.removeAttr( 'style' ).removeClass( 'fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ).addClass( effectClassName ); 2242 2243 slide.$content.removeClass( 'fancybox-is-hidden' ); 2244 2245 //Force reflow for CSS3 transitions 2246 forceRedraw( $slide ); 2247 2248 $.fancybox.animate( $slide, 'fancybox-slide--current', duration, function(e) { 2249 $slide.removeClass( effectClassName ).removeAttr( 'style' ); 2250 2251 if ( slide.pos === self.currPos ) { 2252 self.complete(); 2253 } 2254 2255 }, true); 2256 2257 }, 2258 2259 2260 // Check if we can and have to zoom from thumbnail 2261 //================================================ 2262 2263 getThumbPos : function( slide ) { 2264 2265 var self = this; 2266 var rez = false; 2267 2268 // Check if element is inside the viewport by at least 1 pixel 2269 var isElementVisible = function( $el ) { 2270 var element = $el[0]; 2271 2272 var elementRect = element.getBoundingClientRect(); 2273 var parentRects = []; 2274 2275 var visibleInAllParents; 2276 2277 while ( element.parentElement !== null ) { 2278 if ( $(element.parentElement).css('overflow') === 'hidden' || $(element.parentElement).css('overflow') === 'auto' ) { 2279 parentRects.push(element.parentElement.getBoundingClientRect()); 2280 } 2281 2282 element = element.parentElement; 2283 } 2284 2285 visibleInAllParents = parentRects.every(function(parentRect){ 2286 var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left); 2287 var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top); 2288 2289 return visiblePixelX > 0 && visiblePixelY > 0; 2290 }); 2291 2292 return visibleInAllParents && 2293 elementRect.bottom > 0 && elementRect.right > 0 && 2294 elementRect.left < $(window).width() && elementRect.top < $(window).height(); 2295 }; 2296 2297 var $thumb = slide.opts.$thumb; 2298 var thumbPos = $thumb ? $thumb.offset() : 0; 2299 var slidePos; 2300 2301 if ( thumbPos && $thumb[0].ownerDocument === document && isElementVisible( $thumb ) ) { 2302 slidePos = self.$refs.stage.offset(); 2303 2304 rez = { 2305 top : thumbPos.top - slidePos.top + parseFloat( $thumb.css( "border-top-width" ) || 0 ), 2306 left : thumbPos.left - slidePos.left + parseFloat( $thumb.css( "border-left-width" ) || 0 ), 2307 width : $thumb.width(), 2308 height : $thumb.height(), 2309 scaleX : 1, 2310 scaleY : 1 2311 }; 2312 } 2313 2314 return rez; 2315 }, 2316 2317 2318 // Final adjustments after current gallery item is moved to position 2319 // and it`s content is loaded 2320 // ================================================================== 2321 2322 complete : function() { 2323 var self = this, 2324 current = self.current, 2325 slides = {}, 2326 promise; 2327 2328 if ( current.isMoved || !current.isLoaded || current.isComplete ) { 2329 return; 2330 } 2331 2332 current.isComplete = true; 2333 2334 current.$slide.siblings().trigger( 'onReset' ); 2335 2336 self.preload( 'inline' ); 2337 2338 // Trigger any CSS3 transiton inside the slide 2339 forceRedraw( current.$slide ); 2340 2341 current.$slide.addClass( 'fancybox-slide--complete' ); 2342 2343 // Remove unnecessary slides 2344 $.each( self.slides, function( key, slide ) { 2345 if ( slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1 ) { 2346 slides[ slide.pos ] = slide; 2347 2348 } else if ( slide ) { 2349 $.fancybox.stop( slide.$slide ); 2350 2351 slide.$slide.off().remove(); 2352 } 2353 }); 2354 2355 self.slides = slides; 2356
2357 self.updateCursor(); 2358 2359 self.trigger( 'afterShow' ); 2360 2361 // Play first html5 video/audio 2362 current.$slide.find( 'video,audio' ).first().trigger( 'play' ); 2363 2364 // Try to focus on the first focusable element 2365 if ( $( document.activeElement ).is( '[disabled]' ) || ( current.opts.autoFocus && !( current.type == 'image' || current.type === 'iframe' ) ) ) { 2366 self.focus(); 2367 } 2368 2369 }, 2370 2371 2372 // Preload next and previous slides 2373 // ================================ 2374 2375 preload : function( type ) { 2376 var self = this, 2377 next = self.slides[ self.currPos + 1 ], 2378 prev = self.slides[ self.currPos - 1 ]; 2379 2380 if ( next && next.type === type ) { 2381 self.loadSlide( next ); 2382 } 2383 2384 if ( prev && prev.type === type ) { 2385 self.loadSlide( prev ); 2386 } 2387 }, 2388 2389 2390 // Try to find and focus on the first focusable element 2391 // ==================================================== 2392 2393 focus : function() { 2394 var current = this.current; 2395 var $el; 2396 2397 if ( this.isClosing ) { 2398 return; 2399 } 2400 2401 if ( current && current.isComplete ) { 2402 2403 // Look for first input with autofocus attribute 2404 $el = current.$slide.find('input[autofocus]:enabled:visible:first'); 2405 2406 if ( !$el.length ) { 2407 $el = current.$slide.find('button,:input,[tabindex],a').filter(':enabled:visible:first'); 2408 } 2409 } 2410 2411 $el = $el && $el.length ? $el : this.$refs.container; 2412 2413 $el.focus(); 2414 }, 2415 2416 2417 // Activates current instance - brings container to the front and enables keyboard, 2418 // notifies other instances about deactivating 2419 // ================================================================================= 2420 2421 activate : function () { 2422 var self = this; 2423 2424 // Deactivate all instances 2425 $( '.fancybox-container' ).each(function () { 2426 var instance = $(this).data( 'FancyBox' ); 2427 2428 // Skip self and closing instances 2429 if (instance && instance.id !== self.id && !instance.isClosing) { 2430 instance.trigger( 'onDeactivate' ); 2431 2432 instance.removeEvents(); 2433 2434 instance.isVisible = false; 2435 } 2436 2437 }); 2438 2439 self.isVisible = true; 2440 2441 if ( self.current || self.isIdle ) { 2442 self.update(); 2443 2444 self.updateControls(); 2445 } 2446 2447 self.trigger( 'onActivate' ); 2448 2449 self.addEvents(); 2450 }, 2451 2452 2453 // Start closing procedure 2454 // This will start "zoom-out" animation if needed and clean everything up afterwards 2455 // ================================================================================= 2456 2457 close : function( e, d ) { 2458 2459 var self = this; 2460 var current = self.current; 2461 2462 var effect, duration; 2463 var $what, opacity, start, end; 2464 2465 var done = function() { 2466 self.cleanUp( e ); 2467 }; 2468 2469 if ( self.isClosing ) { 2470 return false; 2471 } 2472 2473 self.isClosing = true; 2474 2475 // If beforeClose callback prevents closing, make sure content is centered 2476 if ( self.trigger( 'beforeClose', e ) === false ) { 2477 self.isClosing = false; 2478 2479 requestAFrame(function() { 2480 self.update(); 2481 }); 2482 2483 return false; 2484 } 2485 2486 // Remove all events 2487 // If there are multiple instances, they will be set again by "activate" method 2488 self.removeEvents(); 2489 2490 if ( current.timouts ) { 2491 clearTimeout( current.timouts ); 2492 } 2493 2494 $what = current.$content; 2495 effect = current.opts.animationEffect; 2496 duration = $.isNumeric( d ) ? d : ( effect ? current.opts.animationDuration : 0 ); 2497 2498 // Remove other slides 2499 current.$slide.off( transitionEnd ).removeClass( 'fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated' ); 2500 2501 current.$slide.siblings().trigger( 'onReset' ).remove(); 2502 2503 // Trigger animations 2504 if ( duration ) { 2505 self.$refs.container.removeClass( 'fancybox-is-open' ).addClass( 'fancybox-is-closing' ); 2506 } 2507 2508 // Clean up 2509 self.hideLoading( current ); 2510 2511 self.hideControls(); 2512
2513 self.updateCursor(); 2514 2515 // Check if possible to zoom-out 2516 if ( effect === 'zoom' && !( e !== true && $what && duration && current.type === 'image' && !current.hasError && ( end = self.getThumbPos( current ) ) ) ) { 2517 effect = 'fade'; 2518 } 2519 2520 if ( effect === 'zoom' ) { 2521 $.fancybox.stop( $what ); 2522 2523 start = $.fancybox.getTranslate( $what ); 2524 2525 start.width = start.width * start.scaleX; 2526 start.height = start.height * start.scaleY; 2527 2528 // Check if we need to animate opacity 2529 opacity = current.opts.zoomOpacity; 2530 2531 if ( opacity == 'auto' ) { 2532 opacity = Math.abs( current.width / current.height - end.width / end.height ) > 0.1; 2533 } 2534 2535 if ( opacity ) { 2536 end.opacity = 0; 2537 } 2538 2539 start.scaleX = start.width / end.width; 2540 start.scaleY = start.height / end.height; 2541 2542 start.width = end.width; 2543 start.height = end.height; 2544 2545 $.fancybox.setTranslate( current.$content, start ); 2546 2547 forceRedraw( current.$content ); 2548 2549 $.fancybox.animate( current.$content, end, duration, done ); 2550 2551 return true; 2552 } 2553 2554 if ( effect && duration ) { 2555 2556 // If skip animation 2557 if ( e === true ) { 2558 setTimeout( done, duration ); 2559 2560 } else { 2561 $.fancybox.animate( current.$slide.removeClass( 'fancybox-slide--current' ), 'fancybox-animated fancybox-slide--previous fancybox-fx-' + effect, duration, done ); 2562 } 2563 2564 } else { 2565 done(); 2566 } 2567 2568 return true; 2569 }, 2570 2571 2572 // Final adjustments after removing the instance 2573 // ============================================= 2574 2575 cleanUp : function( e ) { 2576 var self = this, 2577 $body = $( 'body' ), 2578 instance, 2579 offset; 2580 2581 self.current.$slide.trigger( 'onReset' ); 2582 2583 self.$refs.container.empty().remove(); 2584 2585 self.trigger( 'afterClose', e ); 2586 2587 // Place back focus 2588 if ( self.$lastFocus && !!self.current.opts.backFocus ) { 2589 self.$lastFocus.focus(); 2590 } 2591 2592 self.current = null; 2593 2594 // Check if there are other instances 2595 instance = $.fancybox.getInstance(); 2596 2597 if ( instance ) { 2598 instance.activate(); 2599 2600 } else { 2601 2602 $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft ); 2603 2604 $body.removeClass( 'fancybox-active compensate-for-scrollbar' ); 2605 2606 if ( $body.hasClass( 'fancybox-iosfix' ) ) { 2607 offset = parseInt(document.body.style.top, 10); 2608 2609 $body.removeClass( 'fancybox-iosfix' ).css( 'top', '' ).scrollTop( offset * -1 ); 2610 } 2611 2612 $( '#fancybox-style-noscroll' ).remove(); 2613 2614 } 2615 2616 }, 2617 2618 2619 // Call callback and trigger an event 2620 // ================================== 2621 2622 trigger : function( name, slide ) { 2623 var args = Array.prototype.slice.call(arguments, 1), 2624 self = this, 2625 obj = slide && slide.opts ? slide : self.current, 2626 rez; 2627 2628 if ( obj ) { 2629 args.unshift( obj ); 2630 2631 } else { 2632 obj = self; 2633 } 2634 2635 args.unshift( self ); 2636 2637 if ( $.isFunction( obj.opts[ name ] ) ) { 2638 rez = obj.opts[ name ].apply( obj, args ); 2639 } 2640 2641 if ( rez === false ) { 2642 return rez; 2643 } 2644 2645 if ( name === 'afterClose' || !self.$refs ) { 2646 $D.trigger( name + '.fb', args ); 2647 2648 } else { 2649 self.$refs.container.trigger( name + '.fb', args ); 2650 } 2651 2652 }, 2653 2654 2655 // Update infobar values, navigation button states and reveal caption 2656 // ================================================================== 2657 2658 updateControls : function ( force ) { 2659 2660 var self = this; 2661 2662 var current = self.current, 2663 index = current.index, 2664 caption = current.opts.caption, 2665 $container = self.$refs.container, 2666 $caption = self.$refs.caption; 2667 2668 // Recalculate content dimensions 2669 current.$slide.trigger( 'refresh' ); 2670 2671 self.$caption = caption && caption.length ? $caption.html( caption ) : null; 2672
2673 if ( !self.isHiddenControls && !self.isIdle ) { 2674 self.showControls(); 2675 } 2676 2677 // Update info and navigation elements 2678 $container.find('[data-fancybox-count]').html( self.group.length ); 2679 $container.find('[data-fancybox-index]').html( index + 1 ); 2680 2681 $container.find('[data-fancybox-prev]').prop( 'disabled', ( !current.opts.loop && index <= 0 ) ); 2682 $container.find('[data-fancybox-next]').prop( 'disabled', ( !current.opts.loop && index >= self.group.length - 1 ) ); 2683 2684 if ( current.type === 'image' ) { 2685 2686 // Update download button source 2687 $container.find('[data-fancybox-download]').attr( 'href', current.opts.image.src || current.src ).show(); 2688 2689 } else { 2690 $container.find('[data-fancybox-download],[data-fancybox-zoom]').hide(); 2691 } 2692 }, 2693 2694 // Hide toolbar and caption 2695 // ======================== 2696 2697 hideControls : function () { 2698 2699 this.isHiddenControls = true; 2700 2701 this.$refs.container.removeClass( 'fancybox-show-infobar fancybox-show-toolbar fancybox-show-caption fancybox-show-nav' ); 2702 2703 }, 2704 2705 showControls : function() { 2706 var self = this; 2707 var opts = self.current ? self.current.opts : self.opts; 2708 var $container = self.$refs.container; 2709 2710 self.isHiddenControls = false; 2711 self.idleSecondsCounter = 0; 2712 2713 $container 2714 .toggleClass( 'fancybox-show-toolbar', !!( opts.toolbar && opts.buttons ) ) 2715 .toggleClass( 'fancybox-show-infobar', !!( opts.infobar && self.group.length > 1 ) ) 2716 .toggleClass( 'fancybox-show-nav', !!( opts.arrows && self.group.length > 1 ) ) 2717 .toggleClass( 'fancybox-is-modal', !!opts.modal ); 2718 2719 if ( self.$caption ) { 2720 $container.addClass( 'fancybox-show-caption '); 2721 2722 } else { 2723 $container.removeClass( 'fancybox-show-caption' ); 2724 } 2725 2726 }, 2727 2728 2729 // Toggle toolbar and caption 2730 // ========================== 2731 2732 toggleControls : function() { 2733 if ( this.isHiddenControls ) { 2734 this.showControls(); 2735 2736 } else { 2737 this.hideControls(); 2738 } 2739 2740 }, 2741 2742 2743 }); 2744 2745 2746 $.fancybox = { 2747 2748 version : "3.2.10", 2749 defaults : defaults, 2750 2751 2752 // Get current instance and execute a command. 2753 // 2754 // Examples of usage: 2755 // 2756 // $instance = $.fancybox.getInstance(); 2757 // $.fancybox.getInstance().jumpTo( 1 ); 2758 // $.fancybox.getInstance( 'jumpTo', 1 ); 2759 // $.fancybox.getInstance( function() { 2760 // console.info( this.currIndex ); 2761 // }); 2762 // ====================================================== 2763 2764 getInstance : function ( command ) { 2765 var instance = $('.fancybox-container:not(".fancybox-is-closing"):last').data( 'FancyBox' ); 2766 var args = Array.prototype.slice.call(arguments, 1); 2767 2768 if ( instance instanceof FancyBox ) { 2769 2770 if ( $.type( command ) === 'string' ) { 2771 instance[ command ].apply( instance, args ); 2772 2773 } else if ( $.type( command ) === 'function' ) { 2774 command.apply( instance, args ); 2775 } 2776 2777 return instance; 2778 } 2779 2780 return false; 2781 2782 }, 2783 2784 2785 // Create new instance 2786 // =================== 2787 2788 open : function ( items, opts, index ) { 2789 return new FancyBox( items, opts, index ); 2790 }, 2791 2792 2793 // Close current or all instances 2794 // ============================== 2795 2796 close : function ( all ) { 2797 var instance = this.getInstance(); 2798 2799 if ( instance ) { 2800 instance.close(); 2801 2802 // Try to find and close next instance 2803 2804 if ( all === true ) { 2805 this.close(); 2806 } 2807 } 2808 2809 }, 2810 2811 // Close instances and unbind all events 2812 // ============================== 2813 2814 destroy : function() { 2815 2816 this.close( true ); 2817 2818 $D.off( 'click.fb-start' ); 2819 2820 }, 2821 2822 2823 // Try to detect mobile devices 2824 // ============================ 2825 2826 isMobile : document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent), 2827 2828 2829 // Detect if 'translate3d' support is available 2830 // ============================================ 2831 2832 use3d : (function() { 2833 var div = document.createElement('div'); 2834 2835 return window.getComputedStyle && window.getComputedStyle( div ).getPropertyValue('transform') && !(document.documentMode && document.documentMode < 11);
2836 }()), 2837 2838 // Helper function to get current visual state of an element 2839 // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] 2840 // ===================================================================== 2841 2842 getTranslate : function( $el ) { 2843 var matrix; 2844 2845 if ( !$el || !$el.length ) { 2846 return false; 2847 } 2848 2849 matrix = $el.eq( 0 ).css('transform'); 2850 2851 if ( matrix && matrix.indexOf( 'matrix' ) !== -1 ) { 2852 matrix = matrix.split('(')[1]; 2853 matrix = matrix.split(')')[0]; 2854 matrix = matrix.split(','); 2855 } else { 2856 matrix = []; 2857 } 2858 2859 if ( matrix.length ) { 2860 2861 // If IE 2862 if ( matrix.length > 10 ) { 2863 matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ]; 2864 2865 } else { 2866 matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]]; 2867 } 2868 2869 matrix = matrix.map(parseFloat); 2870 2871 } else { 2872 matrix = [ 0, 0, 1, 1 ]; 2873 2874 var transRegex = /\.*translate\((.*)px,(.*)px\)/i; 2875 var transRez = transRegex.exec( $el.eq( 0 ).attr('style') ); 2876 2877 if ( transRez ) { 2878 matrix[ 0 ] = parseFloat( transRez[2] ); 2879 matrix[ 1 ] = parseFloat( transRez[1] ); 2880 } 2881 } 2882 2883 return { 2884 top : matrix[ 0 ], 2885 left : matrix[ 1 ], 2886 scaleX : matrix[ 2 ], 2887 scaleY : matrix[ 3 ], 2888 opacity : parseFloat( $el.css('opacity') ), 2889 width : $el.width(), 2890 height : $el.height() 2891 }; 2892 2893 }, 2894 2895 2896 // Shortcut for setting "translate3d" properties for element 2897 // Can set be used to set opacity, too 2898 // ======================================================== 2899 2900 setTranslate : function( $el, props ) { 2901 var str = ''; 2902 var css = {}; 2903 2904 if ( !$el || !props ) { 2905 return; 2906 } 2907 2908 if ( props.left !== undefined || props.top !== undefined ) { 2909 str = ( props.left === undefined ? $el.position().left : props.left ) + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px'; 2910 2911 if ( this.use3d ) { 2912 str = 'translate3d(' + str + ', 0px)'; 2913 2914 } else { 2915 str = 'translate(' + str + ')'; 2916 } 2917 } 2918 2919 if ( props.scaleX !== undefined && props.scaleY !== undefined ) { 2920 str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')'; 2921 } 2922 2923 if ( str.length ) { 2924 css.transform = str; 2925 } 2926 2927 if ( props.opacity !== undefined ) { 2928 css.opacity = props.opacity; 2929 } 2930 2931 if ( props.width !== undefined ) { 2932 css.width = props.width; 2933 } 2934 2935 if ( props.height !== undefined ) { 2936 css.height = props.height; 2937 } 2938 2939 return $el.css( css ); 2940 }, 2941 2942 2943 // Simple CSS transition handler 2944 // ============================= 2945 2946 animate : function ( $el, to, duration, callback, leaveAnimationName ) { 2947 if ( $.isFunction( duration ) ) { 2948 callback = duration; 2949 duration = null; 2950 } 2951 2952 if ( !$.isPlainObject( to ) ) { 2953 $el.removeAttr( 'style' ); 2954 } 2955 2956 $el.on( transitionEnd, function(e) { 2957 2958 // Skip events from child elements and z-index change 2959 if ( e && e.originalEvent && ( !$el.is( e.originalEvent.target ) || e.originalEvent.propertyName == 'z-index' ) ) { 2960 return; 2961 } 2962 2963 $.fancybox.stop( $el ); 2964 2965 if ( $.isPlainObject( to ) ) { 2966 2967 if ( to.scaleX !== undefined && to.scaleY !== undefined ) { 2968 $el.css( 'transition-duration', '' ); 2969 2970 to.width = Math.round( $el.width() * to.scaleX ); 2971 to.height = Math.round( $el.height() * to.scaleY ); 2972 2973 to.scaleX = 1; 2974 to.scaleY = 1; 2975 2976 $.fancybox.setTranslate( $el, to ); 2977 } 2978 2979 if ( leaveAnimationName === false ) { 2980 $el.removeAttr( 'style' ); 2981 } 2982 2983 } else if ( leaveAnimationName !== true ) { 2984 $el.removeClass( to ); 2985 } 2986 2987 if ( $.isFunction( callback ) ) { 2988 callback( e ); 2989 } 2990 2991 }); 2992 2993 if ( $.isNumeric( duration ) ) { 2994 $el.css( 'transition-duration', duration + 'ms' ); 2995 } 2996 2997 if ( $.isPlainObject( to ) ) { 2998 $.fancybox.setTranslate( $el, to ); 2999 3000 } else { 3001 $el.addClass( to ); 3002 } 3003 3004 if ( to.scaleX && $el.hasClass( 'fancybox-image-wrap' ) ) { 3005 $el.parent().addClass( 'fancybox-is-scaling' ); 3006 } 3007 3008 // Make sure that `transitionend` callback gets fired 3009 $el.data("timer", setTimeout(function() { 3010 $el.trigger( 'transitionend' ); 3011 }, duration + 16)); 3012 3013 }, 3014 3015 stop : function( $el ) { 3016 clearTimeout( $el.data("timer") ); 3017 3018 $el.off( 'transitionend' ).css( 'transition-duration', '' ); 3019 3020 if ( $el.hasClass( 'fancybox-image-wrap' ) ) { 3021 $el.parent().removeClass( 'fancybox-is-scaling' ); 3022 } 3023 } 3024 3025 }; 3026 3027 3028 // Default click handler for "fancyboxed" links 3029 // ============================================ 3030 3031 function _run( e ) { 3032 var $target = $( e.currentTarget ), 3033 opts = e.data ? e.data.options : {}, 3034 value = $target.attr( 'data-fancybox' ) || '', 3035 index = 0, 3036 items = []; 3037 3038 // Avoid opening multiple times 3039 if ( e.isDefaultPrevented() ) { 3040 return; 3041 } 3042 3043 e.preventDefault(); 3044 3045 // Get all related items and find index for clicked one 3046 if ( value ) { 3047 items = opts.selector ? $( opts.selector ) : ( e.data ? e.data.items : [] ); 3048 items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox="' + value + '"]' ); 3049 3050 index = items.index( $target ); 3051 3052 // Sometimes current item can not be found 3053 // (for example, when slider clones items) 3054 if ( index < 0 ) { 3055 index = 0; 3056 } 3057 3058 } else { 3059 items = [ $target ]; 3060 } 3061 3062 $.fancybox.open( items, opts, index ); 3063 } 3064 3065 3066 // Create a jQuery plugin 3067 // ====================== 3068 3069 $.fn.fancybox = function (options) { 3070 var selector; 3071 3072 options = options || {}; 3073 selector = options.selector || false; 3074 3075 if ( selector ) { 3076 3077 $( 'body' ).off( 'click.fb-start', selector ).on( 'click.fb-start', selector, { 3078 options : options 3079 }, _run ); 3080 3081 } else { 3082 3083 this.off( 'click.fb-start' ).on( 'click.fb-start', { 3084 items : this, 3085 options : options 3086 }, _run); 3087 3088 } 3089 3090 return this; 3091 }; 3092 3093 3094 // Self initializing plugin 3095 // ======================== 3096 3097 $D.on( 'click.fb-start', '[data-fancybox]', _run ); 3098 3099}( window, document, window.jQuery || jQuery ));
vendor: 9,389 bytes, lines 3100-3450
3100 3101// ========================================================================== 3102// 3103// Media 3104// Adds additional media type support 3105// 3106// ========================================================================== 3107;(function ($) { 3108 3109 'use strict'; 3110 3111 // Formats matching url to final form 3112 3113 var format = function (url, rez, params) { 3114 if ( !url ) { 3115 return; 3116 } 3117 3118 params = params || ''; 3119 3120 if ( $.type(params) === "object" ) { 3121 params = $.param(params, true); 3122 } 3123 3124 $.each(rez, function (key, value) { 3125 url = url.replace('$' + key, value || ''); 3126 }); 3127 3128 if (params.length) { 3129 url += (url.indexOf('?') > 0 ? '&' : '?') + params; 3130 } 3131 3132 return url; 3133 }; 3134 3135 // Object containing properties for each media type 3136 3137 var defaults = { 3138 youtube : { 3139 matcher : /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, 3140 params : { 3141 autoplay : 1, 3142 autohide : 1, 3143 fs : 1, 3144 rel : 0, 3145 hd : 1, 3146 wmode : 'transparent', 3147 enablejsapi : 1, 3148 html5 : 1 3149 }, 3150 paramPlace : 8, 3151 type : 'iframe', 3152 url : '//www.youtube.com/embed/$4', 3153 thumb : '//img.youtube.com/vi/$4/hqdefault.jpg' 3154 }, 3155 3156 vimeo : { 3157 matcher : /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, 3158 params : { 3159 autoplay : 1, 3160 hd : 1, 3161 show_title : 1, 3162 show_byline : 1, 3163 show_portrait : 0, 3164 fullscreen : 1, 3165 api : 1 3166 }, 3167 paramPlace : 3, 3168 type : 'iframe', 3169 url : '//player.vimeo.com/video/$2' 3170 }, 3171 3172 metacafe : { 3173 matcher : /metacafe.com\/watch\/(\d+)\/(.*)?/, 3174 type : 'iframe', 3175 url : '//www.metacafe.com/embed/$1/?ap=1' 3176 }, 3177 3178 dailymotion : { 3179 matcher : /dailymotion.com\/video\/(.*)\/?(.*)/, 3180 params : { 3181 additionalInfos : 0, 3182 autoStart : 1 3183 }, 3184 type : 'iframe', 3185 url : '//www.dailymotion.com/embed/video/$1' 3186 }, 3187 3188 vine : { 3189 matcher : /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/, 3190 type : 'iframe', 3191 url : '//vine.co/v/$1/embed/simple' 3192 }, 3193 3194 instagram : { 3195 matcher : /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, 3196 type : 'image', 3197 url : '//$1/p/$2/media/?size=l' 3198 }, 3199 3200 // Examples: 3201 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 3202 // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z 3203 // https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 3204 gmap_place : { 3205 matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, 3206 type : 'iframe', 3207 url : function (rez) { 3208 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' ); 3209 } 3210 }, 3211 3212 // Examples: 3213 // https://www.google.com/maps/search/Empire+State+Building/ 3214 // https://www.google.com/maps/search/?api=1&query=centurylink+field 3215 // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393 3216 gmap_search : { 3217 matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i, 3218 type : 'iframe', 3219 url : function (rez) { 3220 return '//maps.google.' + rez[2] + '/maps?q=' + rez[5].replace('query=', 'q=').replace('api=1', '') + '&output=embed'; 3221 } 3222 } 3223 }; 3224 3225 $(document).on('objectNeedsType.fb', function (e, instance, item) { 3226 3227 var url = item.src || '', 3228 type = false, 3229 media, 3230 thumb, 3231 rez, 3232 params, 3233 urlParams, 3234 paramObj, 3235 provider; 3236 3237 media = $.extend( true, {}, defaults, item.opts.media ); 3238 3239 // Look for any matching media type 3240 $.each(media, function ( providerName, providerOpts ) { 3241 rez = url.match( providerOpts.matcher ); 3242 3243 if ( !rez ) { 3244 return; 3245 } 3246 3247 type = providerOpts.type; 3248 paramObj = {}; 3249 3250 if ( providerOpts.paramPlace && rez[ providerOpts.paramPlace ] ) { 3251 urlParams = rez[ providerOpts.paramPlace ]; 3252 3253 if ( urlParams[ 0 ] == '?' ) { 3254 urlParams = urlParams.substring(1); 3255 } 3256 3257 urlParams = urlParams.split('&'); 3258 3259 for ( var m = 0; m < urlParams.length; ++m ) { 3260 var p = urlParams[ m ].split('=', 2); 3261 3262 if ( p.length == 2 ) { 3263 paramObj[ p[0] ] = decodeURIComponent( p[1].replace(/\+/g, " ") ); 3264 } 3265 } 3266 } 3267 3268 params = $.extend( true, {}, providerOpts.params, item.opts[ providerName ], paramObj ); 3269 3270 url = $.type( providerOpts.url ) === "function" ? providerOpts.url.call( this, rez, params, item ) : format( providerOpts.url, rez, params ); 3271 thumb = $.type( providerOpts.thumb ) === "function" ? providerOpts.thumb.call( this, rez, params, item ) : format( providerOpts.thumb, rez ); 3272 3273 if ( providerName === 'vimeo' ) { 3274 url = url.replace('&%23', '#'); 3275 } 3276 3277 return false; 3278 }); 3279 3280 // If it is found, then change content type and update the url 3281 3282 if ( type ) { 3283 item.src = url; 3284 item.type = type; 3285 3286 if ( !item.opts.thumb && !( item.opts.$thumb && item.opts.$thumb.length ) ) { 3287 item.opts.thumb = thumb; 3288 } 3289 3290 if ( type === 'iframe' ) { 3291 $.extend(true, item.opts, { 3292 iframe : { 3293 preload : false, 3294 attr : { 3295 scrolling : "no" 3296 } 3297 } 3298 }); 3299 3300 item.contentProvider = provider; 3301 3302 item.opts.slideClass += ' fancybox-slide--' + ( provider == 'gmap_place' || provider == 'gmap_search' ? 'map' : 'video' ); 3303 } 3304 3305 } else if ( url ) { 3306 item.type = item.opts.defaultType; 3307 } 3308 3309 }); 3310 3311}( window.jQuery || jQuery )); 3312 3313// ========================================================================== 3314// 3315// Guestures 3316// Adds touch guestures, handles click and tap events 3317// 3318// ========================================================================== 3319;(function (window, document, $) { 3320 'use strict'; 3321 3322 var requestAFrame = (function () { 3323 return window.requestAnimationFrame || 3324 window.webkitRequestAnimationFrame || 3325 window.mozRequestAnimationFrame || 3326 window.oRequestAnimationFrame || 3327 // if all else fails, use setTimeout 3328 function (callback) { 3329 return window.setTimeout(callback, 1000 / 60); 3330 }; 3331 })(); 3332 3333 var cancelAFrame = (function () { 3334 return window.cancelAnimationFrame || 3335 window.webkitCancelAnimationFrame || 3336 window.mozCancelAnimationFrame || 3337 window.oCancelAnimationFrame || 3338 function (id) { 3339 window.clearTimeout(id); 3340 }; 3341 })(); 3342 3343 var pointers = function( e ) { 3344 var result = []; 3345 3346 e = e.originalEvent || e || window.e; 3347 e = e.touches && e.touches.length ? e.touches : ( e.changedTouches && e.changedTouches.length ? e.changedTouches : [ e ] ); 3348 3349 for ( var key in e ) { 3350 3351 if ( e[ key ].pageX ) { 3352 result.push( { x : e[ key ].pageX, y : e[ key ].pageY } ); 3353 3354 } else if ( e[ key ].clientX ) { 3355 result.push( { x : e[ key ].clientX, y : e[ key ].clientY } ); 3356 } 3357 } 3358 3359 return result; 3360 }; 3361 3362 var distance = function( point2, point1, what ) { 3363 if ( !point1 || !point2 ) { 3364 return 0; 3365 } 3366 3367 if ( what === 'x' ) { 3368 return point2.x - point1.x; 3369 3370 } else if ( what === 'y' ) { 3371 return point2.y - point1.y; 3372 } 3373 3374 return Math.sqrt( Math.pow( point2.x - point1.x, 2 ) + Math.pow( point2.y - point1.y, 2 ) ); 3375 }; 3376 3377 var isClickable = function( $el ) { 3378 if ( $el.is('a,area,button,[role="button"],input,label,select,summary,textarea') || $.isFunction( $el.get(0).onclick ) || $el.data('selectable') ) { 3379 return true; 3380 } 3381 3382 // Check for attributes like data-fancybox-next or data-fancybox-close 3383 for ( var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++ ) { 3384 if ( atts[i].nodeName.substr(0, 14) === 'data-fancybox-' ) { 3385 return true; 3386 } 3387 } 3388 3389 return false; 3390 }; 3391 3392 var hasScrollbars = function( el ) { 3393 var overflowY = window.getComputedStyle( el )['overflow-y']; 3394 var overflowX = window.getComputedStyle( el )['overflow-x']; 3395 3396 var vertical = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight; 3397 var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth; 3398 3399 return vertical || horizontal; 3400 }; 3401 3402 var isScrollable = function ( $el ) { 3403 var rez = false; 3404 3405 while ( true ) { 3406 rez = hasScrollbars( $el.get(0) ); 3407 3408 if ( rez ) { 3409 break; 3410 } 3411 3412 $el = $el.parent(); 3413 3414 if ( !$el.length || $el.hasClass( 'fancybox-stage' ) || $el.is( 'body' ) ) { 3415 break; 3416 } 3417 } 3418 3419 return rez; 3420 }; 3421 3422 3423 var Guestures = function ( instance ) { 3424 var self = this; 3425 3426 self.instance = instance; 3427 3428 self.$bg = instance.$refs.bg; 3429 self.$stage = instance.$refs.stage; 3430 self.$container = instance.$refs.container; 3431 3432 self.destroy(); 3433 3434 self.$container.on( 'touchstart.fb.touch mousedown.fb.touch', $.proxy(self, 'ontouchstart') ); 3435 }; 3436 3437 Guestures.prototype.destroy = function() { 3438 this.$container.off( '.fb.touch' ); 3439 }; 3440 3441 Guestures.prototype.ontouchstart = function( e ) { 3442 var self = this; 3443 3444 var $target = $( e.target ); 3445 var instance = self.instance; 3446 var current = instance.current; 3447 var $content = current.$content; 3448 3449 var isTouchDevice = ( e.type == 'touchstart' ); 3450
3451 // Do not respond to both (touch and mouse) events 3452 if ( isTouchDevice ) { 3453 self.$container.off( 'mousedown.fb.touch' ); 3454 } 3455 3456 // Ignore right click 3457 if ( e.originalEvent && e.originalEvent.button == 2 ) { 3458 return; 3459 } 3460 3461 // Ignore taping on links, buttons, input elements 3462 if ( !$target.length || isClickable( $target ) || isClickable( $target.parent() ) ) { 3463 return; 3464 } 3465 3466 // Ignore clicks on the scrollbar 3467 if ( !$target.is('img') && e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left ) { 3468 return; 3469 } 3470 3471 // Ignore clicks while zooming or closing 3472 if ( !current || self.instance.isAnimating || self.instance.isClosing ) { 3473 e.stopPropagation(); 3474 e.preventDefault(); 3475 3476 return; 3477 } 3478 3479 self.realPoints = self.startPoints = pointers( e ); 3480 3481 if ( !self.startPoints ) { 3482 return; 3483 } 3484 3485 e.stopPropagation(); 3486 3487 self.startEvent = e; 3488 3489 self.canTap = true; 3490 self.$target = $target; 3491 self.$content = $content; 3492 self.opts = current.opts.touch; 3493 3494 self.isPanning = false; 3495 self.isSwiping = false; 3496 self.isZooming = false; 3497 self.isScrolling = false; 3498 3499 self.sliderStartPos = self.sliderLastPos || { top: 0, left: 0 }; 3500 self.contentStartPos = $.fancybox.getTranslate( self.$content ); 3501 self.contentLastPos = null; 3502 3503 self.startTime = new Date().getTime(); 3504 self.distanceX = self.distanceY = self.distance = 0; 3505 3506 self.canvasWidth = Math.round( current.$slide[0].clientWidth ); 3507 self.canvasHeight = Math.round( current.$slide[0].clientHeight ); 3508 3509 $(document) 3510 .off( '.fb.touch' ) 3511 .on( isTouchDevice ? 'touchend.fb.touch touchcancel.fb.touch' : 'mouseup.fb.touch mouseleave.fb.touch', $.proxy(self, "ontouchend")) 3512 .on( isTouchDevice ? 'touchmove.fb.touch' : 'mousemove.fb.touch', $.proxy(self, "ontouchmove")); 3513 3514 if ( $.fancybox.isMobile ) { 3515 document.addEventListener('scroll', self.onscroll, true); 3516 } 3517 3518 if ( !(self.opts || instance.canPan() ) || !( $target.is( self.$stage ) || self.$stage.find( $target ).length ) ) { 3519 3520 // Prevent image ghosting while dragging 3521 if ( $target.is('img') ) { 3522 e.preventDefault(); 3523 } 3524 3525 return; 3526 } 3527 3528 if ( !( $.fancybox.isMobile && ( isScrollable( $target ) || isScrollable( $target.parent() ) ) ) ) { 3529 e.preventDefault(); 3530 } 3531 3532 if ( self.startPoints.length === 1 ) { 3533 if ( current.type === 'image' && ( self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1 ) ) { 3534 $.fancybox.stop( self.$content ); 3535 3536 self.$content.css( 'transition-duration', '' ); 3537 3538 self.isPanning = true; 3539 3540 } else { 3541 self.isSwiping = true; 3542 } 3543 3544 self.$container.addClass( 'fancybox-controls--isGrabbing' ); 3545 } 3546 3547 if ( self.startPoints.length === 2 && !instance.isAnimating && !current.hasError && current.type === 'image' && ( current.isLoaded || current.$ghost ) ) { 3548 self.canTap = false; 3549 self.isSwiping = false; 3550 self.isPanning = false; 3551 3552 self.isZooming = true; 3553 3554 $.fancybox.stop( self.$content ); 3555 3556 self.$content.css( 'transition-duration', '' ); 3557 3558 self.centerPointStartX = ( ( self.startPoints[0].x + self.startPoints[1].x ) * 0.5 ) - $(window).scrollLeft(); 3559 self.centerPointStartY = ( ( self.startPoints[0].y + self.startPoints[1].y ) * 0.5 ) - $(window).scrollTop(); 3560 3561 self.percentageOfImageAtPinchPointX = ( self.centerPointStartX - self.contentStartPos.left ) / self.contentStartPos.width; 3562 self.percentageOfImageAtPinchPointY = ( self.centerPointStartY - self.contentStartPos.top ) / self.contentStartPos.height; 3563 3564 self.startDistanceBetweenFingers = distance( self.startPoints[0], self.startPoints[1] ); 3565 } 3566 3567 }; 3568 3569 Guestures.prototype.onscroll = function(e) { 3570 self.isScrolling = true; 3571 }; 3572 3573 Guestures.prototype.ontouchmove = function( e ) { 3574 var self = this, 3575 $target = $(e.target); 3576 3577 if ( self.isScrolling || !( $target.is( self.$stage ) || self.$stage.find( $target ).length ) ) { 3578 self.canTap = false; 3579 3580 return; 3581 } 3582 3583 self.newPoints = pointers( e ); 3584 3585 if ( !( self.opts || self.instance.canPan() ) || !self.newPoints || !self.newPoints.length ) { 3586 return; 3587 } 3588 3589 if ( !(self.isSwiping && self.isSwiping === true) ) { 3590 e.preventDefault(); 3591 } 3592 3593 self.distanceX = distance( self.newPoints[0], self.startPoints[0], 'x' ); 3594 self.distanceY = distance( self.newPoints[0], self.startPoints[0], 'y' ); 3595 3596 self.distance = distance( self.newPoints[0], self.startPoints[0] ) 3597 3598 // Skip false ontouchmove events (Chrome) 3599 if ( self.distance > 0 ) { 3600 if ( self.isSwiping ) { 3601 self.onSwipe(e); 3602 3603 } else if ( self.isPanning ) { 3604 self.onPan(); 3605 3606 } else if ( self.isZooming ) { 3607 self.onZoom(); 3608 } 3609 } 3610 3611 }; 3612 3613 Guestures.prototype.onSwipe = function(e) { 3614 var self = this, 3615 swiping = self.isSwiping, 3616 left = self.sliderStartPos.left || 0, 3617 angle; 3618 3619 // If direction is not yet determined 3620 if ( swiping === true ) { 3621 3622 // We need at least 10px distance to correctly calculate an angle 3623 if ( Math.abs( self.distance ) > 10 ) { 3624 self.canTap = false; 3625 3626 if ( self.instance.group.length < 2 && self.opts.vertical ) { 3627 self.isSwiping = 'y'; 3628 3629 } else if ( self.instance.isDragging || self.opts.vertical === false || ( self.opts.vertical === 'auto' && $( window ).width() > 800 ) ) { 3630 self.isSwiping = 'x'; 3631 3632 } else { 3633 angle = Math.abs( Math.atan2( self.distanceY, self.distanceX ) * 180 / Math.PI ); 3634 3635 self.isSwiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x'; 3636 } 3637 3638 self.canTap = false; 3639 3640 if ( self.isSwiping === 'y' && $.fancybox.isMobile && ( isScrollable( self.$target ) || isScrollable( self.$target.parent() ) ) ) { 3641 self.isScrolling = true; 3642 3643 return; 3644 } 3645 3646 self.instance.isDragging = self.isSwiping; 3647 3648 // Reset points to avoid jumping, because we dropped first swipes to calculate the angle 3649 self.startPoints = self.newPoints; 3650 3651 $.each(self.instance.slides, function( index, slide ) { 3652 $.fancybox.stop( slide.$slide ); 3653 3654 slide.$slide.css( 'transition-duration', '' ); 3655 3656 slide.inTransition = false; 3657 3658 if ( slide.pos === self.instance.current.pos ) { 3659 self.sliderStartPos.left = $.fancybox.getTranslate( slide.$slide ).left; 3660 } 3661 }); 3662 3663 // Stop slideshow 3664 if ( self.instance.SlideShow && self.instance.SlideShow.isActive ) { 3665 self.instance.SlideShow.stop(); 3666 } 3667 } 3668 3669 return; 3670 } 3671 3672 // Sticky edges 3673 if ( swiping == 'x' ) { 3674 if ( self.distanceX > 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === 0 && !self.instance.current.opts.loop ) ) ) { 3675 left = left + Math.pow( self.distanceX, 0.8 ); 3676 3677 } else if ( self.distanceX < 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop ) ) ) { 3678 left = left - Math.pow( -self.distanceX, 0.8 ); 3679 3680 } else { 3681 left = left + self.distanceX; 3682 } 3683 } 3684 3685 self.sliderLastPos = { 3686 top : swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY, 3687 left : left 3688 }; 3689 3690 if ( self.requestId ) { 3691 cancelAFrame( self.requestId ); 3692 3693 self.requestId = null; 3694 } 3695 3696 self.requestId = requestAFrame(function() { 3697 3698 if ( self.sliderLastPos ) { 3699 $.each(self.instance.slides, function( index, slide ) { 3700 var pos = slide.pos - self.instance.currPos; 3701 3702 $.fancybox.setTranslate( slide.$slide, { 3703 top : self.sliderLastPos.top, 3704 left : self.sliderLastPos.left + ( pos * self.canvasWidth ) + ( pos * slide.opts.gutter ) 3705 });
vendor: 12,924 bytes, lines 3706-4197
3706 }); 3707 3708 self.$container.addClass( 'fancybox-is-sliding' ); 3709 } 3710 3711 }); 3712 3713 }; 3714 3715 Guestures.prototype.onPan = function() { 3716 var self = this; 3717 3718 // Sometimes, when tapping causally, image can move a bit and that breaks double tapping 3719 if ( distance( self.newPoints[0], self.realPoints[0] ) < ($.fancybox.isMobile ? 10 : 5) ) { 3720 self.startPoints = self.newPoints; 3721 return; 3722 } 3723 3724 self.canTap = false; 3725 3726 self.contentLastPos = self.limitMovement(); 3727 3728 if ( self.requestId ) { 3729 cancelAFrame( self.requestId ); 3730 3731 self.requestId = null; 3732 } 3733 3734 self.requestId = requestAFrame(function() { 3735 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 3736 }); 3737 }; 3738 3739 // Make panning sticky to the edges 3740 Guestures.prototype.limitMovement = function() { 3741 var self = this; 3742 3743 var canvasWidth = self.canvasWidth; 3744 var canvasHeight = self.canvasHeight; 3745 3746 var distanceX = self.distanceX; 3747 var distanceY = self.distanceY; 3748 3749 var contentStartPos = self.contentStartPos; 3750 3751 var currentOffsetX = contentStartPos.left; 3752 var currentOffsetY = contentStartPos.top; 3753 3754 var currentWidth = contentStartPos.width; 3755 var currentHeight = contentStartPos.height; 3756 3757 var minTranslateX, minTranslateY, 3758 maxTranslateX, maxTranslateY, 3759 newOffsetX, newOffsetY; 3760 3761 if ( currentWidth > canvasWidth ) { 3762 newOffsetX = currentOffsetX + distanceX; 3763 3764 } else { 3765 newOffsetX = currentOffsetX; 3766 } 3767 3768 newOffsetY = currentOffsetY + distanceY; 3769 3770 // Slow down proportionally to traveled distance 3771 minTranslateX = Math.max( 0, canvasWidth * 0.5 - currentWidth * 0.5 ); 3772 minTranslateY = Math.max( 0, canvasHeight * 0.5 - currentHeight * 0.5 ); 3773 3774 maxTranslateX = Math.min( canvasWidth - currentWidth, canvasWidth * 0.5 - currentWidth * 0.5 ); 3775 maxTranslateY = Math.min( canvasHeight - currentHeight, canvasHeight * 0.5 - currentHeight * 0.5 ); 3776 3777 if ( currentWidth > canvasWidth ) { 3778 3779 // -> 3780 if ( distanceX > 0 && newOffsetX > minTranslateX ) { 3781 newOffsetX = minTranslateX - 1 + Math.pow( -minTranslateX + currentOffsetX + distanceX, 0.8 ) || 0; 3782 } 3783 3784 // <- 3785 if ( distanceX < 0 && newOffsetX < maxTranslateX ) { 3786 newOffsetX = maxTranslateX + 1 - Math.pow( maxTranslateX - currentOffsetX - distanceX, 0.8 ) || 0; 3787 } 3788 3789 } 3790 3791 if ( currentHeight > canvasHeight ) { 3792 3793 // \/ 3794 if ( distanceY > 0 && newOffsetY > minTranslateY ) { 3795 newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8 ) || 0; 3796 } 3797 3798 // /\ 3799 if ( distanceY < 0 && newOffsetY < maxTranslateY ) { 3800 newOffsetY = maxTranslateY + 1 - Math.pow ( maxTranslateY - currentOffsetY - distanceY, 0.8 ) || 0; 3801 } 3802 3803 } 3804 3805 return { 3806 top : newOffsetY, 3807 left : newOffsetX, 3808 scaleX : contentStartPos.scaleX, 3809 scaleY : contentStartPos.scaleY 3810 }; 3811 3812 }; 3813 3814 Guestures.prototype.limitPosition = function( newOffsetX, newOffsetY, newWidth, newHeight ) { 3815 var self = this; 3816 3817 var canvasWidth = self.canvasWidth; 3818 var canvasHeight = self.canvasHeight; 3819 3820 if ( newWidth > canvasWidth ) { 3821 newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; 3822 newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX; 3823 3824 } else { 3825 3826 // Center horizontally 3827 newOffsetX = Math.max( 0, canvasWidth / 2 - newWidth / 2 ); 3828 3829 } 3830 3831 if ( newHeight > canvasHeight ) { 3832 newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; 3833 newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY; 3834 3835 } else { 3836 3837 // Center vertically 3838 newOffsetY = Math.max( 0, canvasHeight / 2 - newHeight / 2 ); 3839 3840 } 3841 3842 return { 3843 top : newOffsetY, 3844 left : newOffsetX 3845 }; 3846 3847 }; 3848 3849 Guestures.prototype.onZoom = function() { 3850 var self = this; 3851 3852 // Calculate current distance between points to get pinch ratio and new width and height 3853 3854 var currentWidth = self.contentStartPos.width; 3855 var currentHeight = self.contentStartPos.height; 3856 3857 var currentOffsetX = self.contentStartPos.left; 3858 var currentOffsetY = self.contentStartPos.top; 3859 3860 var endDistanceBetweenFingers = distance( self.newPoints[0], self.newPoints[1] ); 3861 3862 var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers; 3863 3864 var newWidth = Math.floor( currentWidth * pinchRatio ); 3865 var newHeight = Math.floor( currentHeight * pinchRatio ); 3866 3867 // This is the translation due to pinch-zooming 3868 var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; 3869 var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; 3870 3871 //Point between the two touches 3872 3873 var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft(); 3874 var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop(); 3875 3876 // And this is the translation due to translation of the centerpoint 3877 // between the two fingers 3878 3879 var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; 3880 var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; 3881 3882 // The new offset is the old/current one plus the total translation 3883 3884 var newOffsetX = currentOffsetX + ( translateFromZoomingX + translateFromTranslatingX ); 3885 var newOffsetY = currentOffsetY + ( translateFromZoomingY + translateFromTranslatingY ); 3886 3887 var newPos = { 3888 top : newOffsetY, 3889 left : newOffsetX, 3890 scaleX : self.contentStartPos.scaleX * pinchRatio, 3891 scaleY : self.contentStartPos.scaleY * pinchRatio 3892 }; 3893 3894 self.canTap = false; 3895 3896 self.newWidth = newWidth; 3897 self.newHeight = newHeight; 3898 3899 self.contentLastPos = newPos; 3900 3901 if ( self.requestId ) { 3902 cancelAFrame( self.requestId ); 3903 3904 self.requestId = null; 3905 } 3906 3907 self.requestId = requestAFrame(function() { 3908 $.fancybox.setTranslate( self.$content, self.contentLastPos ); 3909 }); 3910 3911 }; 3912 3913 Guestures.prototype.ontouchend = function( e ) { 3914 var self = this; 3915 var dMs = Math.max( (new Date().getTime() ) - self.startTime, 1); 3916 3917 var swiping = self.isSwiping; 3918 var panning = self.isPanning; 3919 var zooming = self.isZooming; 3920 var scrolling = self.isScrolling; 3921 3922 self.endPoints = pointers( e ); 3923 3924 self.$container.removeClass( 'fancybox-controls--isGrabbing' ); 3925 3926 $(document).off( '.fb.touch' ); 3927 3928 document.removeEventListener('scroll', self.onscroll, true); 3929 3930 if ( self.requestId ) { 3931 cancelAFrame( self.requestId ); 3932 3933 self.requestId = null; 3934 } 3935 3936 self.isSwiping = false; 3937 self.isPanning = false; 3938 self.isZooming = false; 3939 self.isScrolling = false; 3940 3941 self.instance.isDragging = false; 3942 3943 if ( self.canTap ) { 3944 return self.onTap( e ); 3945 } 3946 3947 self.speed = 366; 3948 3949 // Speed in px/ms 3950 self.velocityX = self.distanceX / dMs * 0.5; 3951 self.velocityY = self.distanceY / dMs * 0.5; 3952 3953 self.speedX = Math.max( self.speed * 0.5, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityX ) ) * self.speed ) ); 3954 3955 if ( panning ) { 3956 self.endPanning(); 3957 3958 } else if ( zooming ) { 3959 self.endZooming(); 3960 3961 } else { 3962 self.endSwiping( swiping, scrolling ); 3963 } 3964 3965 return; 3966 }; 3967 3968 Guestures.prototype.endSwiping = function( swiping, scrolling ) { 3969 var self = this, 3970 ret = false, 3971 len = self.instance.group.length; 3972 3973 self.sliderLastPos = null; 3974 3975 // Close if swiped vertically / navigate if horizontally 3976 if ( swiping == 'y' && !scrolling && Math.abs( self.distanceY ) > 50 ) { 3977 3978 // Continue vertical movement 3979 $.fancybox.animate( self.instance.current.$slide, { 3980 top : self.sliderStartPos.top + self.distanceY + ( self.velocityY * 150 ), 3981 opacity : 0 3982 }, 150 ); 3983 3984 ret = self.instance.close( true, 300 ); 3985 3986 } else if ( swiping == 'x' && self.distanceX > 50 && len > 1 ) { 3987 ret = self.instance.previous( self.speedX ); 3988 3989 } else if ( swiping == 'x' && self.distanceX < -50 && len > 1 ) { 3990 ret = self.instance.next( self.speedX ); 3991 } 3992 3993 if ( ret === false && ( swiping == 'x' || swiping == 'y' ) ) { 3994 if ( scrolling || len < 2 ) { 3995 self.instance.centerSlide( self.instance.current, 150 ); 3996 } else { 3997 self.instance.jumpTo( self.instance.current.index ); 3998 } 3999 } 4000 4001 self.$container.removeClass( 'fancybox-is-sliding' ); 4002 4003 }; 4004 4005 // Limit panning from edges 4006 // ======================== 4007 4008 Guestures.prototype.endPanning = function() { 4009 4010 var self = this; 4011 var newOffsetX, newOffsetY, newPos; 4012 4013 if ( !self.contentLastPos ) { 4014 return; 4015 } 4016 4017 if ( self.opts.momentum === false ) { 4018 newOffsetX = self.contentLastPos.left; 4019 newOffsetY = self.contentLastPos.top; 4020 4021 } else { 4022 4023 // Continue movement 4024 newOffsetX = self.contentLastPos.left + ( self.velocityX * self.speed ); 4025 newOffsetY = self.contentLastPos.top + ( self.velocityY * self.speed ); 4026 } 4027 4028 newPos = self.limitPosition( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height ); 4029 4030 newPos.width = self.contentStartPos.width; 4031 newPos.height = self.contentStartPos.height; 4032 4033 $.fancybox.animate( self.$content, newPos, 330 ); 4034 }; 4035 4036 4037 Guestures.prototype.endZooming = function() { 4038 var self = this; 4039 4040 var current = self.instance.current; 4041 4042 var newOffsetX, newOffsetY, newPos, reset; 4043 4044 var newWidth = self.newWidth; 4045 var newHeight = self.newHeight; 4046 4047 if ( !self.contentLastPos ) { 4048 return; 4049 } 4050 4051 newOffsetX = self.contentLastPos.left; 4052 newOffsetY = self.contentLastPos.top; 4053 4054 reset = { 4055 top : newOffsetY, 4056 left : newOffsetX, 4057 width : newWidth, 4058 height : newHeight, 4059 scaleX : 1, 4060 scaleY : 1 4061 }; 4062 4063 // Reset scalex/scaleY values; this helps for perfomance and does not break animation 4064 $.fancybox.setTranslate( self.$content, reset ); 4065 4066 if ( newWidth < self.canvasWidth && newHeight < self.canvasHeight ) { 4067 self.instance.scaleToFit( 150 ); 4068 4069 } else if ( newWidth > current.width || newHeight > current.height ) { 4070 self.instance.scaleToActual( self.centerPointStartX, self.centerPointStartY, 150 ); 4071 4072 } else { 4073 4074 newPos = self.limitPosition( newOffsetX, newOffsetY, newWidth, newHeight ); 4075 4076 // Switch from scale() to width/height or animation will not work correctly 4077 $.fancybox.setTranslate( self.content, $.fancybox.getTranslate( self.$content ) ); 4078 4079 $.fancybox.animate( self.$content, newPos, 150 ); 4080 } 4081 4082 }; 4083 4084 Guestures.prototype.onTap = function(e) { 4085 var self = this; 4086 var $target = $( e.target ); 4087 4088 var instance = self.instance; 4089 var current = instance.current; 4090 4091 var endPoints = ( e && pointers( e ) ) || self.startPoints; 4092 4093 var tapX = endPoints[0] ? endPoints[0].x - self.$stage.offset().left : 0; 4094 var tapY = endPoints[0] ? endPoints[0].y - self.$stage.offset().top : 0; 4095 4096 var where; 4097 4098 var process = function ( prefix ) { 4099 4100 var action = current.opts[ prefix ]; 4101 4102 if ( $.isFunction( action ) ) { 4103 action = action.apply( instance, [ current, e ] ); 4104 } 4105 4106 if ( !action) { 4107 return; 4108 } 4109 4110 switch ( action ) { 4111 4112 case "close" : 4113 4114 instance.close( self.startEvent ); 4115 4116 break; 4117 4118 case "toggleControls" : 4119 4120 instance.toggleControls( true ); 4121 4122 break; 4123 4124 case "next" : 4125 4126 instance.next(); 4127 4128 break; 4129 4130 case "nextOrClose" : 4131 4132 if ( instance.group.length > 1 ) { 4133 instance.next(); 4134 4135 } else { 4136 instance.close( self.startEvent ); 4137 } 4138 4139 break; 4140 4141 case "zoom" : 4142 4143 if ( current.type == 'image' && ( current.isLoaded || current.$ghost ) ) { 4144 4145 if ( instance.canPan() ) { 4146 instance.scaleToFit(); 4147 4148 } else if ( instance.isScaledDown() ) { 4149 instance.scaleToActual( tapX, tapY ); 4150 4151 } else if ( instance.group.length < 2 ) { 4152 instance.close( self.startEvent ); 4153 } 4154 } 4155 4156 break; 4157 } 4158 4159 }; 4160 4161 // Ignore right click 4162 if ( e.originalEvent && e.originalEvent.button == 2 ) { 4163 return; 4164 } 4165 4166 // Skip if clicked on the scrollbar 4167 if ( !$target.is('img') && tapX > $target[0].clientWidth + $target.offset().left ) { 4168 return; 4169 } 4170 4171 // Check where is clicked 4172 if ( $target.is( '.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container' ) ) { 4173 where = 'Outside'; 4174 4175 } else if ( $target.is( '.fancybox-slide' ) ) { 4176 where = 'Slide'; 4177 4178 } else if ( instance.current.$content && instance.current.$content.find( $target ).addBack().filter( $target ).length ) { 4179 where = 'Content'; 4180 4181 } else { 4182 return; 4183 } 4184 4185 // Check if this is a double tap 4186 if ( self.tapped ) { 4187 4188 // Stop previously created single tap 4189 clearTimeout( self.tapped ); 4190 self.tapped = null; 4191 4192 // Skip if distance between taps is too big 4193 if ( Math.abs( tapX - self.tapX ) > 50 || Math.abs( tapY - self.tapY ) > 50 ) { 4194 return this; 4195 } 4196 4197 // OK, now we assume that this is a double-tap
4198 process( 'dblclick' + where ); 4199 4200 } else { 4201 4202 // Single tap will be processed if user has not clicked second time within 300ms 4203 // or there is no need to wait for double-tap 4204 self.tapX = tapX; 4205 self.tapY = tapY; 4206 4207 if ( current.opts[ 'dblclick' + where ] && current.opts[ 'dblclick' + where ] !== current.opts[ 'click' + where ] ) { 4208 4209 self.tapped = setTimeout(function() { 4210 self.tapped = null; 4211 4212 process( 'click' + where ); 4213 4214 }, 500); 4215 4216 } else { 4217 process( 'click' + where ); 4218 } 4219 4220 } 4221 4222 return this; 4223 }; 4224 4225 $(document).on('onActivate.fb', function (e, instance) { 4226 if ( instance && !instance.Guestures ) { 4227 instance.Guestures = new Guestures( instance ); 4228 } 4229 }); 4230 4231}( window, document, window.jQuery || jQuery )); 4232 4233// ========================================================================== 4234// 4235// SlideShow 4236// Enables slideshow functionality 4237// 4238// Example of usage: 4239// $.fancybox.getInstance().SlideShow.start() 4240// 4241// ========================================================================== 4242;(function (document, $) { 4243 'use strict'; 4244 4245 $.extend(true, $.fancybox.defaults, { 4246 btnTpl : { 4247 slideShow : 4248 '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}">' + 4249 '<svg viewBox="0 0 40 40">' + 4250 '<path d="M13,12 L27,20 L13,27 Z" />' + 4251 '<path d="M15,10 v19 M23,10 v19" />' + 4252 '</svg>' + 4253 '</button>' 4254 }, 4255 slideShow : { 4256 autoStart : false, 4257 speed : 3000 4258 } 4259 }); 4260 4261 var SlideShow = function( instance ) { 4262 this.instance = instance; 4263 this.init(); 4264 }; 4265 4266 $.extend( SlideShow.prototype, { 4267 timer : null, 4268 isActive : false, 4269 $button : null, 4270 4271 init : function() { 4272 var self = this; 4273 4274 self.$button = self.instance.$refs.toolbar.find('[data-fancybox-play]').on('click', function() { 4275 self.toggle(); 4276 }); 4277 4278 if ( self.instance.group.length < 2 || !self.instance.group[ self.instance.currIndex ].opts.slideShow ) { 4279 self.$button.hide(); 4280 } 4281 }, 4282 4283 set : function( force ) { 4284 var self = this; 4285 4286 // Check if reached last element 4287 if ( self.instance && self.instance.current && (force === true || self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) { 4288 self.timer = setTimeout(function() { 4289 if ( self.isActive ) { 4290 self.instance.jumpTo( (self.instance.currIndex + 1) % self.instance.group.length ); 4291 } 4292 4293 }, self.instance.current.opts.slideShow.speed); 4294 4295 } else { 4296 self.stop(); 4297 self.instance.idleSecondsCounter = 0; 4298 self.instance.showControls(); 4299 } 4300 }, 4301 4302 clear : function() { 4303 var self = this; 4304 4305 clearTimeout( self.timer ); 4306 4307 self.timer = null; 4308 }, 4309 4310 start : function() { 4311 var self = this; 4312 var current = self.instance.current; 4313 4314 if ( current ) { 4315 self.isActive = true; 4316 4317 self.$button 4318 .attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_STOP ) 4319 .removeClass( 'fancybox-button--play' ) 4320 .addClass( 'fancybox-button--pause' ); 4321 4322 self.set( true ); 4323 } 4324 }, 4325 4326 stop : function() { 4327 var self = this; 4328 var current = self.instance.current; 4329 4330 self.clear(); 4331 4332 self.$button 4333 .attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_START ) 4334 .removeClass( 'fancybox-button--pause' ) 4335 .addClass( 'fancybox-button--play' ); 4336 4337 self.isActive = false; 4338 }, 4339 4340 toggle : function() { 4341 var self = this; 4342 4343 if ( self.isActive ) { 4344 self.stop(); 4345 4346 } else { 4347 self.start(); 4348 } 4349 } 4350 4351 }); 4352 4353 $(document).on({ 4354 'onInit.fb' : function(e, instance) { 4355 if ( instance && !instance.SlideShow ) { 4356 instance.SlideShow = new SlideShow( instance ); 4357 } 4358 }, 4359 4360 'beforeShow.fb' : function(e, instance, current, firstRun) { 4361 var SlideShow = instance && instance.SlideShow; 4362 4363 if ( firstRun ) { 4364 4365 if ( SlideShow && current.opts.slideShow.autoStart ) { 4366 SlideShow.start(); 4367 } 4368 4369 } else if ( SlideShow && SlideShow.isActive ) { 4370 SlideShow.clear(); 4371 } 4372 }, 4373 4374 'afterShow.fb' : function(e, instance, current) { 4375 var SlideShow = instance && instance.SlideShow; 4376 4377 if ( SlideShow && SlideShow.isActive ) { 4378 SlideShow.set(); 4379 } 4380 }, 4381 4382 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { 4383 var SlideShow = instance && instance.SlideShow; 4384 4385 // "P" or Spacebar 4386 if ( SlideShow && current.opts.slideShow && ( keycode === 80 || keycode === 32 ) && !$(document.activeElement).is( 'button,a,input' ) ) { 4387 keypress.preventDefault(); 4388 4389 SlideShow.toggle(); 4390 } 4391 }, 4392 4393 'beforeClose.fb onDeactivate.fb' : function(e, instance) { 4394 var SlideShow = instance && instance.SlideShow; 4395 4396 if ( SlideShow ) { 4397 SlideShow.stop(); 4398 } 4399 } 4400 }); 4401 4402 // Page Visibility API to pause slideshow when window is not active 4403 $(document).on("visibilitychange", function() { 4404 var instance = $.fancybox.getInstance(); 4405 var SlideShow = instance && instance.SlideShow; 4406 4407 if ( SlideShow && SlideShow.isActive ) { 4408 if ( document.hidden ) { 4409 SlideShow.clear(); 4410 4411 } else { 4412 SlideShow.set(); 4413 } 4414 } 4415 }); 4416 4417}( document, window.jQuery || jQuery ));
4418 4419// ========================================================================== 4420// 4421// FullScreen 4422// Adds fullscreen functionality 4423// 4424// ========================================================================== 4425;(function (document, $) { 4426 'use strict'; 4427 4428 // Collection of methods supported by user browser 4429 var fn = (function () { 4430 4431 var fnMap = [ 4432 [ 4433 'requestFullscreen', 4434 'exitFullscreen', 4435 'fullscreenElement', 4436 'fullscreenEnabled', 4437 'fullscreenchange', 4438 'fullscreenerror' 4439 ], 4440 // new WebKit 4441 [ 4442 'webkitRequestFullscreen', 4443 'webkitExitFullscreen', 4444 'webkitFullscreenElement', 4445 'webkitFullscreenEnabled', 4446 'webkitfullscreenchange', 4447 'webkitfullscreenerror' 4448 4449 ], 4450 // old WebKit (Safari 5.1) 4451 [ 4452 'webkitRequestFullScreen', 4453 'webkitCancelFullScreen', 4454 'webkitCurrentFullScreenElement', 4455 'webkitCancelFullScreen', 4456 'webkitfullscreenchange', 4457 'webkitfullscreenerror' 4458 4459 ], 4460 [ 4461 'mozRequestFullScreen', 4462 'mozCancelFullScreen', 4463 'mozFullScreenElement', 4464 'mozFullScreenEnabled', 4465 'mozfullscreenchange', 4466 'mozfullscreenerror' 4467 ], 4468 [ 4469 'msRequestFullscreen', 4470 'msExitFullscreen', 4471 'msFullscreenElement', 4472 'msFullscreenEnabled', 4473 'MSFullscreenChange', 4474 'MSFullscreenError' 4475 ] 4476 ]; 4477 4478 var val; 4479 var ret = {}; 4480 var i, j; 4481 4482 for ( i = 0; i < fnMap.length; i++ ) { 4483 val = fnMap[ i ]; 4484 4485 if ( val && val[ 1 ] in document ) { 4486 for ( j = 0; j < val.length; j++ ) { 4487 ret[ fnMap[ 0 ][ j ] ] = val[ j ]; 4488 } 4489 4490 return ret; 4491 } 4492 } 4493 4494 return false; 4495 })(); 4496 4497 // If browser does not have Full Screen API, then simply unset default button template and stop 4498 if ( !fn ) { 4499 4500 if ( $ && $.fancybox ) { 4501 $.fancybox.defaults.btnTpl.fullScreen = false; 4502 } 4503 4504 return; 4505 } 4506 4507 var FullScreen = { 4508 4509 request : function ( elem ) { 4510 4511 elem = elem || document.documentElement; 4512 4513 elem[ fn.requestFullscreen ]( elem.ALLOW_KEYBOARD_INPUT ); 4514 4515 }, 4516 exit : function () { 4517 4518 document[ fn.exitFullscreen ](); 4519 4520 }, 4521 toggle : function ( elem ) { 4522 4523 elem = elem || document.documentElement; 4524 4525 if ( this.isFullscreen() ) { 4526 this.exit(); 4527 4528 } else { 4529 this.request( elem ); 4530 } 4531 4532 }, 4533 isFullscreen : function() { 4534 4535 return Boolean( document[ fn.fullscreenElement ] ); 4536 4537 }, 4538 enabled : function() { 4539 4540 return Boolean( document[ fn.fullscreenEnabled ] ); 4541 4542 } 4543 }; 4544 4545 $.extend(true, $.fancybox.defaults, { 4546 btnTpl : { 4547 fullScreen : 4548 '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="{{FULL_SCREEN}}">' + 4549 '<svg viewBox="0 0 40 40">' + 4550 '<path d="M9,12 h22 v16 h-22 v-16 v16 h22 v-16 Z" />' + 4551 '</svg>' + 4552 '</button>' 4553 }, 4554 fullScreen : { 4555 autoStart : false 4556 } 4557 }); 4558 4559 $(document).on({ 4560 'onInit.fb' : function(e, instance) { 4561 var $container; 4562 4563 if ( instance && instance.group[ instance.currIndex ].opts.fullScreen ) { 4564 $container = instance.$refs.container; 4565 4566 $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) { 4567 4568 e.stopPropagation(); 4569 e.preventDefault(); 4570 4571 FullScreen.toggle( $container[ 0 ] ); 4572 4573 }); 4574 4575 if ( instance.opts.fullScreen && instance.opts.fullScreen.autoStart === true ) { 4576 FullScreen.request( $container[ 0 ] ); 4577 } 4578 4579 // Expose API 4580 instance.FullScreen = FullScreen; 4581 4582 } else if ( instance ) { 4583 instance.$refs.toolbar.find('[data-fancybox-fullscreen]').hide(); 4584 } 4585 4586 }, 4587 4588 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { 4589 4590 // "P" or Spacebar 4591 if ( instance && instance.FullScreen && keycode === 70 ) { 4592 keypress.preventDefault(); 4593 4594 instance.FullScreen.toggle( instance.$refs.container[ 0 ] ); 4595 } 4596 4597 }, 4598 4599 'beforeClose.fb' : function( instance ) { 4600 if ( instance && instance.FullScreen ) { 4601 FullScreen.exit(); 4602 } 4603 } 4604 }); 4605 4606 $(document).on(fn.fullscreenchange, function() { 4607 var isFullscreen = FullScreen.isFullscreen(), 4608 instance = $.fancybox.getInstance(); 4609 4610 if ( instance ) { 4611 4612 // If image is zooming, then force to stop and reposition properly 4613 if ( instance.current && instance.current.type === 'image' && instance.isAnimating ) { 4614 instance.current.$content.css( 'transition', 'none' ); 4615 4616 instance.isAnimating = false; 4617 4618 instance.update( true, true, 0 ); 4619 } 4620 4621 instance.trigger( 'onFullscreenChange', isFullscreen ); 4622 4623 instance.$refs.container.toggleClass( 'fancybox-is-fullscreen', isFullscreen ); 4624 } 4625 4626 }); 4627 4628}( document, window.jQuery || jQuery ));
4629 4630// ========================================================================== 4631// 4632// Thumbs 4633// Displays thumbnails in a grid 4634// 4635// ========================================================================== 4636;(function (document, $) { 4637 'use strict'; 4638 4639 // Make sure there are default values 4640 $.fancybox.defaults = $.extend(true, { 4641 btnTpl : { 4642 thumbs : 4643 '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}">' + 4644 '<svg viewBox="0 0 120 120">' + 4645 '<path d="M30,30 h14 v14 h-14 Z M50,30 h14 v14 h-14 Z M70,30 h14 v14 h-14 Z M30,50 h14 v14 h-14 Z M50,50 h14 v14 h-14 Z M70,50 h14 v14 h-14 Z M30,70 h14 v14 h-14 Z M50,70 h14 v14 h-14 Z M70,70 h14 v14 h-14 Z" />' + 4646 '</svg>' + 4647 '</button>' 4648 }, 4649 thumbs : { 4650 autoStart : false, // Display thumbnails on opening 4651 hideOnClose : true, // Hide thumbnail grid when closing animation starts 4652 parentEl : '.fancybox-container', // Container is injected into this element 4653 axis : 'y' // Vertical (y) or horizontal (x) scrolling 4654 } 4655 }, $.fancybox.defaults); 4656 4657 var FancyThumbs = function( instance ) { 4658 this.init( instance ); 4659 }; 4660 4661 $.extend( FancyThumbs.prototype, { 4662 4663 $button : null, 4664 $grid : null, 4665 $list : null, 4666 isVisible : false, 4667 isActive : false, 4668 4669 init : function( instance ) { 4670 var self = this; 4671 4672 self.instance = instance; 4673 4674 instance.Thumbs = self; 4675 4676 // Enable thumbs if at least two group items have thumbnails 4677 var first = instance.group[0], 4678 second = instance.group[1]; 4679 4680 self.opts = instance.group[ instance.currIndex ].opts.thumbs; 4681 4682 self.$button = instance.$refs.toolbar.find( '[data-fancybox-thumbs]' ); 4683 4684 if ( self.opts && first && second && ( 4685 ( first.type == 'image' || first.opts.thumb || first.opts.$thumb ) && 4686 ( second.type == 'image' || second.opts.thumb || second.opts.$thumb ) 4687 )) { 4688 4689 self.$button.show().on('click', function() { 4690 self.toggle(); 4691 }); 4692 4693 self.isActive = true; 4694 4695 } else { 4696 self.$button.hide(); 4697 } 4698 }, 4699 4700 create : function() { 4701 var self = this, 4702 instance = self.instance, 4703 parentEl = self.opts.parentEl, 4704 list, 4705 src; 4706 4707 self.$grid = $('<div class="fancybox-thumbs fancybox-thumbs-' + self.opts.axis + '"></div>').appendTo( instance.$refs.container.find( parentEl ).addBack().filter( parentEl ) ); 4708 4709 // Build list HTML 4710 list = '<ul>'; 4711 4712 $.each(instance.group, function( i, item ) { 4713 src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr( 'src' ) : null ); 4714 4715 if ( !src && item.type === 'image' ) { 4716 src = item.src; 4717 } 4718 4719 if ( src && src.length ) { 4720 list += '<li data-index="' + i + '" tabindex="0" class="fancybox-thumbs-loading"><img data-src="' + src + '" /></li>'; 4721 } 4722 }); 4723 4724 list += '</ul>'; 4725 4726 self.$list = $( list ).appendTo( self.$grid ).on('click', 'li', function() { 4727 instance.jumpTo( $(this).data('index') ); 4728 }); 4729
4730 self.$list.find( 'img' ).hide().one('load', function() { 4731 var $parent = $(this).parent().removeClass( 'fancybox-thumbs-loading' ), 4732 thumbWidth = $parent.outerWidth(), 4733 thumbHeight = $parent.outerHeight(), 4734 width, 4735 height, 4736 widthRatio, 4737 heightRatio; 4738 4739 width = this.naturalWidth || this.width; 4740 height = this.naturalHeight || this.height; 4741 4742 // Calculate thumbnail dimensions; center vertically and horizontally 4743 widthRatio = width / thumbWidth; 4744 heightRatio = height / thumbHeight; 4745 4746 if (widthRatio >= 1 && heightRatio >= 1) { 4747 if (widthRatio > heightRatio) { 4748 width = width / heightRatio; 4749 height = thumbHeight; 4750 4751 } else { 4752 width = thumbWidth; 4753 height = height / widthRatio; 4754 } 4755 } 4756 4757 $(this).css({ 4758 width : Math.floor(width), 4759 height : Math.floor(height), 4760 'margin-top' : height > thumbHeight ? ( Math.floor(thumbHeight * 0.3 - height * 0.3 ) ) : Math.floor(thumbHeight * 0.5 - height * 0.5 ), 4761 'margin-left' : Math.floor(thumbWidth * 0.5 - width * 0.5 ) 4762 }).show(); 4763 4764 }) 4765 .each(function() { 4766 this.src = $( this ).data( 'src' ); 4767 }); 4768 4769 if ( self.opts.axis === 'x' ) { 4770 self.$list.width( parseInt( self.$grid.css("padding-right") ) + ( instance.group.length * self.$list.children().eq(0).outerWidth(true) ) + 'px' ); 4771 } 4772 }, 4773 4774 focus : function( duration ) { 4775 var self = this, 4776 $list = self.$list, 4777 thumb, 4778 thumbPos; 4779 4780 if ( self.instance.current ) { 4781 thumb = $list.children() 4782 .removeClass( 'fancybox-thumbs-active' ) 4783 .filter('[data-index="' + self.instance.current.index + '"]') 4784 .addClass('fancybox-thumbs-active'); 4785 4786 thumbPos = thumb.position(); 4787 4788 // Check if need to scroll to make current thumb visible 4789 if ( self.opts.axis === 'y' && ( thumbPos.top < 0 || thumbPos.top > ( $list.height() - thumb.outerHeight() ) ) ) { 4790 $list.stop().animate({ 'scrollTop' : $list.scrollTop() + thumbPos.top }, duration); 4791 4792 } else if ( self.opts.axis === 'x' && ( 4793 thumbPos.left < $list.parent().scrollLeft() || 4794 thumbPos.left > ( $list.parent().scrollLeft() + ( $list.parent().width() - thumb.outerWidth() ) ) 4795 ) 4796 ) { 4797 $list.parent().stop().animate({ 'scrollLeft' : thumbPos.left }, duration); 4798 } 4799 } 4800 }, 4801 4802 update : function() { 4803 this.instance.$refs.container.toggleClass( 'fancybox-show-thumbs', this.isVisible ); 4804 4805 if ( this.isVisible ) { 4806 if ( !this.$grid ) { 4807 this.create(); 4808 } 4809 4810 this.instance.trigger( 'onThumbsShow' ); 4811 4812 this.focus( 0 ); 4813 4814 } else if ( this.$grid ) { 4815 this.instance.trigger( 'onThumbsHide' ); 4816 } 4817 4818 // Update content position 4819 this.instance.update(); 4820 }, 4821 4822 hide : function() { 4823 this.isVisible = false; 4824 this.update(); 4825 }, 4826 4827 show : function() { 4828 this.isVisible = true; 4829 this.update(); 4830 }, 4831 4832 toggle : function() { 4833 this.isVisible = !this.isVisible; 4834 this.update(); 4835 } 4836 }); 4837 4838 $(document).on({ 4839 4840 'onInit.fb' : function(e, instance) { 4841 var Thumbs; 4842 4843 if ( instance && !instance.Thumbs ) { 4844 Thumbs = new FancyThumbs( instance ); 4845 4846 if ( Thumbs.isActive && Thumbs.opts.autoStart === true ) { 4847 Thumbs.show(); 4848 } 4849 } 4850 }, 4851 4852 'beforeShow.fb' : function(e, instance, item, firstRun) { 4853 var Thumbs = instance && instance.Thumbs; 4854 4855 if ( Thumbs && Thumbs.isVisible ) { 4856 Thumbs.focus( firstRun ? 0 : 250 ); 4857 } 4858 }, 4859 4860 'afterKeydown.fb' : function(e, instance, current, keypress, keycode) { 4861 var Thumbs = instance && instance.Thumbs; 4862 4863 // "G" 4864 if ( Thumbs && Thumbs.isActive && keycode === 71 ) { 4865 keypress.preventDefault(); 4866 4867 Thumbs.toggle(); 4868 } 4869 }, 4870 4871 'beforeClose.fb' : function( e, instance ) { 4872 var Thumbs = instance && instance.Thumbs; 4873 4874 if ( Thumbs && Thumbs.isVisible && Thumbs.opts.hideOnClose !== false ) { 4875 Thumbs.$grid.hide(); 4876 } 4877 } 4878 4879 }); 4880 4881}(document, window.jQuery)); 4882 4883//// ========================================================================== 4884// 4885// Share 4886// Displays simple form for sharing current url 4887// 4888// ========================================================================== 4889;(function (document, $) { 4890 'use strict'; 4891 4892 $.extend(true, $.fancybox.defaults, { 4893 btnTpl : { 4894 share : 4895 '<button data-fancybox-share class="fancybox-button fancybox-button--share" title="{{SHARE}}">' + 4896 '<svg viewBox="0 0 40 40">' + 4897 '<path d="M6,30 C8,18 19,16 23,16 L23,16 L23,10 L33,20 L23,29 L23,24 C19,24 8,27 6,30 Z">' + 4898 '</svg>' + 4899 '</button>' 4900 }, 4901 share : { 4902 tpl : 4903 '<div class="fancybox-share">' + 4904 '<h1>{{SHARE}}</h1>' + 4905 '<p class="fancybox-share__links">' + 4906 '<a class="fancybox-share__button fancybox-share__button--fb" href="https://www.facebook.com/sharer/sharer.php?u={{url}}">' + 4907 '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m287 456v-299c0-21 6-35 35-35h38v-63c-7-1-29-3-55-3-54 0-91 33-91 94v306m143-254h-205v72h196" /></svg>
4907' + 4908 '<span>Facebook</span>' + 4909 '</a>' + 4910 '<a class="fancybox-share__button fancybox-share__button--pt" href="https://www.pinterest.com/pin/create/button/?url={{url}}&description={{descr}}&media={{media}}">' + 4911 '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m265 56c-109 0-164 78-164 144 0 39 15 74 47 87 5 2 10 0 12-5l4-19c2-6 1-8-3-13-9-11-15-25-15-45 0-58 43-110 113-110 62 0 96 38 96 88 0 67-30 122-73 122-24 0-42-19-36-44 6-29 20-60 20-81 0-19-10-35-31-35-25 0-44 26-44 60 0 21 7 36 7 36l-30 125c-8 37-1 83 0 87 0 3 4 4 5 2 2-3 32-39 42-75l16-64c8 16 31 29 56 29 74 0 124-67 124-157 0-69-58-132-146-132z" fill="#fff"/></svg>' + 4912 '<span>Pinterest</span>' + 4913 '</a>' + 4914 '<a class="fancybox-share__button fancybox-share__button--tw" href="https://twitter.com/intent/tweet?url={{url}}&text={{descr}}">' + 4915 '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m456 133c-14 7-31 11-47 13 17-10 30-27 37-46-15 10-34 16-52 20-61-62-157-7-141 75-68-3-129-35-169-85-22 37-11 86 26 109-13 0-26-4-37-9 0 39 28 72 65 80-12 3-25 4-37 2 10 33 41 57 77 57-42 30-77 38-122 34 170 111 378-32 359-208 16-11 30-25 41-42z" /></svg>' + 4916 '<span>Twitter</span>' + 4917 '</a>' + 4918 '</p>' + 4919 '<p><input class="fancybox-share__input" type="text" value="{{url_raw}}" /></p>' + 4920 '</div>' 4921 } 4922 }); 4923 4924 function escapeHtml(string) { 4925 var entityMap = { 4926 '&': '&', 4927 '<': '<', 4928 '>': '>', 4929 '"': '"', 4930 "'": ''', 4931 '/': '/', 4932 '`': '`', 4933 '=': '=' 4934 }; 4935 4936 return String(string).replace(/[&<>"'`=\/]/g, function (s) { 4937 return entityMap[s]; 4938 }); 4939 } 4940 4941 $(document).on('click', '[data-fancybox-share]', function() { 4942 var f = $.fancybox.getInstance(), 4943 url, 4944 tpl; 4945 4946 if ( f ) { 4947 url = f.current.opts.hash === false ? f.current.src : window.location; 4948 tpl = f.current.opts.share.tpl 4949 .replace( /\{\{media\}\}/g, f.current.type === 'image' ? encodeURIComponent( f.current.src ) : '' ) 4950 .replace( /\{\{url\}\}/g, encodeURIComponent( url ) ) 4951 .replace( /\{\{url_raw\}\}/g, escapeHtml( url ) ) 4952 .replace( /\{\{descr\}\}/g, f.$caption ? encodeURIComponent( f.$caption.text() ) : '' ); 4953 4954 $.fancybox.open({ 4955 src : f.translate( f, tpl ), 4956 type : 'html', 4957 opts : { 4958 animationEffect : "fade", 4959 animationDuration : 250, 4960 afterLoad : function(instance, current) { 4961 // Opening links in a popup window 4962 current.$content.find('.fancybox-share__links a').click(function() { 4963 window.open(this.href, "Share", "width=550, height=450"); 4964 return false; 4965 }); 4966 } 4967 } 4968 }); 4969 } 4970 4971 }); 4972 4973}( document, window.jQuery || jQuery )); 4974 4975// ========================================================================== 4976// 4977// Hash 4978// Enables linking to each modal 4979// 4980// ========================================================================== 4981;(function (document, window, $) { 4982 'use strict'; 4983 4984 // Simple $.escapeSelector polyfill (for jQuery prior v3) 4985 if ( !$.escapeSelector ) { 4986 $.escapeSelector = function( sel ) { 4987 var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; 4988 var fcssescape = function( ch, asCodePoint ) { 4989 if ( asCodePoint ) { 4990 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER 4991 if ( ch === "\0" ) { 4992 return "\uFFFD"; 4993 } 4994 4995 // Control characters and (dependent upon position) numbers get escaped as code points 4996 return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; 4997 } 4998 4999 // Other potentially-special ASCII characters get backslash-escaped 5000 return "\\" + ch; 5001 }; 5002 5003 return ( sel + "" ).replace( rcssescape, fcssescape ); 5004 }; 5005 } 5006 5007 // Create new history entry only once 5008 var shouldCreateHistory = true; 5009 5010 // Variable containing last hash value set by fancyBox 5011 // It will be used to determine if fancyBox needs to close after hash change is detected 5012 var currentHash = null; 5013 5014 // Throttling the history change 5015 var timerID = null; 5016 5017 // Get info about gallery name and current index from url 5018 function parseUrl() { 5019 var hash = window.location.hash.substr( 1 ); 5020 var rez = hash.split( '-' ); 5021 var index = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseInt( rez.pop( -1 ), 10 ) || 1 : 1; 5022 var gallery = rez.join( '-' ); 5023 5024 // Index is starting from 1 5025 if ( index < 1 ) { 5026 index = 1; 5027 } 5028 5029 return { 5030 hash : hash, 5031 index : index, 5032 gallery : gallery 5033 }; 5034 } 5035 5036 // Trigger click evnt on links to open new fancyBox instance 5037 function triggerFromUrl( url ) { 5038 var $el; 5039 5040 if ( url.gallery !== '' ) { 5041 5042 // If we can find element matching 'data-fancybox' atribute, then trigger click event for that .. 5043 $el = $( "[data-fancybox='" + $.escapeSelector( url.gallery ) + "']" ).eq( url.index - 1 ); 5044 5045 if ( !$el.length ) { 5046 // .. if not, try finding element by ID 5047 $el = $( "#" + $.escapeSelector( url.gallery ) + "" ); 5048 } 5049 5050 if ( $el.length ) { 5051 shouldCreateHistory = false; 5052 5053 $el.trigger( 'click' ); 5054 } 5055 5056 } 5057 } 5058 5059 // Get gallery name from current instance 5060 function getGalleryID( instance ) { 5061 var opts; 5062 5063 if ( !instance ) { 5064 return false; 5065 } 5066 5067 opts = instance.current ? instance.current.opts : instance.opts; 5068 5069 return opts.hash || ( opts.$orig ? opts.$orig.data( 'fancybox' ) : '' ); 5070 } 5071 5072 // Start when DOM becomes ready 5073 $(function() { 5074 5075 // Check if user has disabled this module 5076 if ( $.fancybox.defaults.hash === false ) { 5077 return; 5078 } 5079 5080 // Update hash when opening/closing fancyBox 5081 $(document).on({ 5082 'onInit.fb' : function( e, instance ) { 5083 var url, gallery; 5084 5085 if ( instance.group[ instance.currIndex ].opts.hash === false ) { 5086 return; 5087 } 5088 5089 url = parseUrl(); 5090 gallery = getGalleryID( instance ); 5091 5092 // Make sure gallery start index matches index from hash 5093 if ( gallery && url.gallery && gallery == url.gallery ) { 5094 instance.currIndex = url.index - 1; 5095 } 5096 }, 5097 5098 'beforeShow.fb' : function( e, instance, current ) { 5099 var gallery; 5100 5101 if ( !current || current.opts.hash === false ) { 5102 return; 5103 } 5104 5105 gallery = getGalleryID( instance ); 5106 5107 // Update window hash 5108 if ( gallery && gallery !== '' ) { 5109 5110 if ( window.location.hash.indexOf( gallery ) < 0 ) { 5111 instance.opts.origHash = window.location.hash; 5112 } 5113 5114 currentHash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' ); 5115 5116 if ( 'replaceState' in window.history ) { 5117 if ( timerID ) { 5118 clearTimeout( timerID ); 5119 } 5120 5121 timerID = setTimeout(function() { 5122 window.history[ shouldCreateHistory ? 'pushState' : 'replaceState' ]( {} , document.title, window.location.pathname + window.location.search + '#' + currentHash ); 5123 5124 timerID = null; 5125 5126 shouldCreateHistory = false; 5127 5128 }, 300); 5129 5130 } else { 5131 window.location.hash = currentHash; 5132 } 5133 5134 } 5135 5136 }, 5137 5138 'beforeClose.fb' : function( e, instance, current ) { 5139 var gallery, origHash; 5140 5141 if ( timerID ) { 5142 clearTimeout( timerID ); 5143 } 5144 5145 if ( current.opts.hash === false ) { 5146 return; 5147 } 5148 5149 gallery = getGalleryID( instance ); 5150 origHash = instance && instance.opts.origHash ? instance.opts.origHash : ''; 5151 5152 // Remove hash from location bar 5153 if ( gallery && gallery !== '' ) { 5154 5155 if ( 'replaceState' in history ) { 5156 window.history.replaceState( {} , document.title, window.location.pathname + window.location.search + origHash ); 5157 5158 } else { 5159 window.location.hash = origHash; 5160 5161 // Keep original scroll position 5162 $( window ).scrollTop( instance.scrollTop ).scrollLeft( instance.scrollLeft ); 5163 } 5164 } 5165 5166 currentHash = null; 5167 } 5168 }); 5169 5170 // Check if need to close after url has changed 5171 $(window).on('hashchange.fb', function() { 5172 var url = parseUrl(); 5173 5174 if ( $.fancybox.getInstance() ) { 5175 if ( currentHash && currentHash !== url.gallery + '-' + url.index && !( url.index === 1 && currentHash == url.gallery ) ) { 5176 currentHash = null; 5177 5178 $.fancybox.close(); 5179 } 5180 5181 } else if ( url.gallery !== '' ) { 5182 triggerFromUrl( url ); 5183 } 5184 }); 5185 5186 // Check current hash and trigger click event on matching element to start fancyBox, if needed 5187 setTimeout(function() { 5188 triggerFromUrl( parseUrl() ); 5189 }, 50); 5190 });
vendor: 988 bytes, lines 5191-5230
5191 5192}( document, window, window.jQuery || jQuery )); 5193 5194;(function (document, $) { 5195 'use strict'; 5196 5197 var prevTime = new Date().getTime(); 5198 5199 $(document).on({ 5200 'onInit.fb' : function( e, instance, current ) { 5201 instance.$refs.stage.on('mousewheel DOMMouseScroll wheel MozMousePixelScroll', function(e) { 5202 var current = instance.current, 5203 currTime = new Date().getTime(); 5204 5205 if ( instance.group.length < 1 || current.opts.wheel === false || ( current.opts.wheel === 'auto' && current.type !== 'image' ) ) { 5206 return; 5207 } 5208 5209 e.preventDefault(); 5210 e.stopPropagation(); 5211 5212 if ( current.$slide.hasClass( 'fancybox-animated' ) ) { 5213 return; 5214 } 5215 5216 e = e.originalEvent || e; 5217 5218 if ( currTime - prevTime < 250 ) { 5219 return; 5220 } 5221 5222 prevTime = currTime; 5223 5224 instance[ ( -e.deltaY || -e.deltaX || e.wheelDelta || -e.detail ) < 0 ? 'next' : 'previous' ](); 5225 5226 }); 5227 } 5228 }); 5229 5230}( document, window.jQuery || jQuery ));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.