PageSourceSearch

https://www.phew.org.uk/wp-content/themes/phew/assets/js/lib/jquery.fancybox.js?ver=3.5.7

js phew.org.uk collected 2026-10-09 16:54:26 UTC 145,815 bytes, 5,138 lines download raw bytes

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

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.