PageSourceSearch

https://shinwugi.com/resources/js/fancybox/jquery.fancybox.js

js shinwugi.com collected 2026-10-07 17:42:30 UTC 156,101 bytes, 5,138 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.