PageSourceSearch

https://drinkmebeverages.ca/wp-content/plugins/advanced-woocommerc…/assets/js/jquery.fancybox.js?ver=1.0

js drinkmebeverages.ca collected 2026-10-07 04:41:10 UTC 150,959 bytes, 5,142 lines download raw bytes

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