Subversion-Projekte lars-tiefland.webanos.zeldi.de

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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