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