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