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