PageSourceSearch

https://aeronext.aero/cssjs/fancybox.js

js aeronext.aero collected 2026-09-29 07:36:30 UTC 107,707 bytes, 4,417 lines download raw bytes

1// ==================================================
2// fancyBox v3.0.47
3//
4// Licensed GPLv3 for open source use
5// or fancyBox Commercial License for commercial use
6//
7// http://fancyapps.com/fancybox/
8// Copyright 2017 fancyApps
9//
10// ==================================================
11; (function (window, document, $, undefined) {
12  'use strict';
13
14  // If there's no jQuery, fancyBox can't work
15  // =========================================
16
17  if (!$) {
18    return undefined;
19  }
20
21  // Private default settings
22  // ========================
23
24  var defaults = {
25
26    // Animation duration in ms
27    speed: 330,
28
29    // Enable infinite gallery navigation
30    loop: true,
31
32    // Should zoom animation change opacity, too
33    // If opacity is 'auto', then fade-out if image and thumbnail have different aspect ratios
34    opacity: 'auto',
35
36    // Space around image, ignored if zoomed-in or viewport smaller than 800px
37    margin: [44, 0],
38
39    // Horizontal space between slides
40    gutter: 30,
41
42    // Should display toolbars
43    infobar: true,
44    buttons: true,
45
46    // What buttons should appear in the toolbar
47    slideShow: true,
48    fullScreen: true,
49    thumbs: true,
50    closeBtn: true,
51
52    // Should apply small close button at top right corner of the content
53    // If 'auto' - will be set for content having type 'html', 'inline' or 'ajax'
54    smallBtn: 'auto',
55
56    image: {
57
58      // Wait for images to load before displaying
59      // Requires predefined image dimensions
60      // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found
61      preload: "auto",
62
63      // Protect an image from downloading by right-click
64      protect: false
65
66    },
67
68    ajax: {
69
70      // Object containing settings for ajax request
71      settings: {
72
73        // This helps to indicate that request comes from the modal
74        // Feel free to change naming
75        data: {
76          fancybox: true
77        }
78      }
79
80    },
81
82    iframe: {
83
84      // Iframe template
85      tpl: '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen allowtransparency="true" src=""></iframe>',
86
87      // Preload iframe before displaying it
88      // This allows to calculate iframe content width and height
89      // (note: Due to "Same Origin Policy", you can't get cross domain data).
90      preload: true,
91
92      // Scrolling attribute for iframe tag
93      scrolling: 'no',
94
95      // Custom CSS styling for iframe wrapping element
96      css: {}
97
98    },
99
100    // Custom CSS class for layout
101    baseClass: '',
102
103    // Custom CSS class for slide element
104    slideClass: '',
105
106    // Base template for layout
107    baseTpl: '<div class="fancybox-container" role="dialog" tabindex="-1">' +
108            '<div class="fancybox-bg"></div>' +
109            '<div class="fancybox-controls">' +
110                '<div class="fancybox-infobar">' +
111                    '<button data-fancybox-previous class="fancybox-button fancybox-button--left" title="Previous"></button>' +
112                    '<div class="fancybox-infobar__body">' +
113                        '<span class="js-fancybox-index"></span>&nbsp;/&nbsp;<span class="js-fancybox-count"></span>' +
114                    '</div>' +
115                    '<button data-fancybox-next class="fancybox-button fancybox-button--right" title="Next"></button>' +
116                '</div>' +
117                '<div class="fancybox-buttons">' +
118                    '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="Close (Esc)"></button>' +
119                '</div>' +
120            '</div>' +
121            '<div class="fancybox-slider-wrap">' +
122                '<div class="fancybox-slider"></div>' +
123            '</div>' +
124            '<div class="fancybox-caption-wrap"><div class="fancybox-caption"></div></div>' +
125        '</div>',
126
127    // Loading indicator template
128    spinnerTpl: '<div class="fancybox-loading"></div>',
129
130    // Error message template
131    errorTpl: '<div class="fancybox-error"><p>The requested content cannot be loaded. <br /> Please try again later.<p></div>',
132
133    // This will be appended to html content, if "smallBtn" option is not set to false
134    closeTpl: '<button data-fancybox-close class="fancybox-close-small"></button>',
135
136    // Container is injected into this element
137    parentEl: 'body',
138
139    // Enable gestures (tap, zoom, pan and pinch)
140    touch: true,
141
142    // Enable keyboard navigation
143    keyboard: true,
144
145    // Try to focus on first focusable element after opening
146    focus: true,
147
148    // Close when clicked outside of the content
149    closeClickOutside: true,
150
151    // Callbacks
152    beforeLoad: $.noop,
153    afterLoad: $.noop,
154    beforeMove: $.noop,
155    afterMove: $.noop,
156    onComplete: $.noop,
157
158    onInit: $.noop,
159    beforeClose: $.noop,
160    afterClose: $.noop,
161    onActivate: $.noop,
162    onDeactivate: $.noop
163
164  };
165
166  var $W = $(window);
167  var $D = $(document);
168
169  var called = 0;
170
171  // Check if an object is a jQuery object and not a native JavaScript object
172  // ========================================================================
173
174  var isQuery = function (obj) {
175    return obj && obj.hasOwnProperty && obj instanceof $;
176  };
177
178  // Handle multiple browsers for requestAnimationFrame()
179  // ====================================================
180
181  var requestAFrame = (function () {
182    return window.requestAnimationFrame ||
183            window.webkitRequestAnimationFrame ||
184            window.mozRequestAnimationFrame ||
185            function (callback) {
186              window.setTimeout(callback, 1000 / 60);
187            };
188  })();
189
190
191  // Check if element is inside the viewport by at least 1 pixel
192  // ===========================================================
193
194  var isElementInViewport = function (el) {
195    var rect;
196
197    if (typeof $ === "function" && el instanceof $) {
198      el = el[0];
199    }
200
201    rect = el.getBoundingClientRect();
202
203    return rect.bottom > 0 && rect.right > 0 &&
204            rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
205            rect.top < (window.innerHeight || document.documentElement.clientHeight);
206  };
207
208
209  // Class definition
210  // ================
211
212  var FancyBox = function (content, opts, index) {
213    var self = this;
214
215    self.opts = $.extend(true, { index: index }, defaults, opts || {});
216    self.id = self.opts.id || ++called;
217    self.group = [];
218
219    self.currIndex = parseInt(self.opts.index, 10) || 0;
220    self.prevIndex = null;
221
222    self.prevPos = null;
223    self.currPos = 0;
224
225    self.firstRun = null;
226
227    // Create group elements from original item collection
228    self.createGroup(content);
229
230    if (!self.group.length) {
231      return;
232    }
233
234    // Save last active element and current scroll position
235    self.$lastFocus = $(document.activeElement).blur();
236
237    // Collection of gallery objects
238    self.slides = {};
239
240    self.init(content);
241    self.Thumbs.toggle();//BORIS
242  };
243
244  $.extend(FancyBox.prototype, {
245
246    // Create DOM structure
247    // ====================
248
249    init: function () {
250      var self = this;
251
252      var galleryHasHtml = false;
253
254      var testWidth;
255      var $container;
256
257      self.scrollTop = $D.scrollTop();
258      self.scrollLeft = $D.scrollLeft();
259
260      if (!$.fancybox.getInstance()) {
261        testWidth = $('body').width();
262
263        $('html').addClass('fancybox-enabled');
264
265        if ($.fancybox.isTouch) {
266
267          // Ugly workaround for iOS page shifting issue (when inputs get focus)
268          // Do not apply for images, otherwise top/bottom bars will appear
269          $.each(self.group, function (key, item) {
270            if (item.type !== 'image' && item.type !== 'iframe') {
271              galleryHasHtml = true;
272              return false;
273            }
274          });
275
276          if (galleryHasHtml) {
277            $('body').css({
278              position: 'fixed',
279              width: testWidth,
280              top: self.scrollTop * -1
281            });
282          }
283
284        } else {
285
286          // Compare page width after adding "overflow:hidden"
287          testWidth = $('body').width() - testWidth;
288
289          // Width has changed - compensate missing scrollbars
290          if (testWidth > 1) {
291            $('<style id="fancybox-noscroll" type="text/css">').html('.compensate-for-scrollbar, .fancybox-enabled body { margin-right: ' + testWidth + 'px; }').appendTo('head');
292          }
293
294        }
295      }
296
297      $container = $(self.opts.baseTpl)
298          .attr('id', 'fancybox-container-' + self.id)
299          .data('FancyBox', self)
300          .addClass(self.opts.baseClass)
301          .hide()
302          .prependTo(self.opts.parentEl);
303
304      // Create object holding references to jQuery wrapped nodes
305      self.$refs = {
306        container: $container,
307        bg: $container.find('.fancybox-bg'),
308        controls: $container.find('.fancybox-controls'),
309        buttons: $container.find('.fancybox-buttons'),
310        slider_wrap: $container.find('.fancybox-slider-wrap'),
311        slider: $container.find('.fancybox-slider'),
312        caption: $container.find('.fancybox-caption')
313      };
314
315      self.trigger('onInit');
316
317      // Bring to front and enable events
318      self.activate();
319
320      // Try to avoid running multiple times
321      if (self.current) {
322        return;
323      }
324
325      self.jumpTo(self.currIndex);
326
327    },
328
329
330    // Create array of gally item objects
331    // Check if each object has valid type and content
332    // ===============================================
333
334    createGroup: function (content) {
335      var self = this;
336      var items = $.makeArray(content);
337
338      $.each(items, function (i, item) {
339        var obj = {},
340            opts = {},
341            data = [],
342            $item,
343            type,
344            src,
345            srcParts;
346
347        // Step 1 - Make sure we have an object
348
349        if ($.isPlainObject(item)) {
350
351          obj = item;
352          opts = item.opts || {};
353
354        } else if ($.type(item) === 'object' && $(item).length) {
355
356          $item = $(item);
357          data = $item.data();
358
359          opts = 'options' in data ? data.options : {};
360
361          opts = $.type(opts) === 'object' ? opts : {};
362
363          obj.type = 'type' in data ? data.type : opts.type;
364          obj.src = 'src' in data ? data.src : (opts.src || $item.attr('href'));
365
366          opts.width = 'width' in data ? data.width : opts.width;
367          opts.height = 'height' in data ? data.height : opts.height;
368          opts.thumb = 'thumb' in data ? data.thumb : opts.thumb;
369
370          opts.selector = 'selector' in data ? data.selector : opts.selector;
371
372          if ('srcset' in data) {
373            opts.image = { srcset: data.srcset };
374          }
375
376          opts.$orig = $item;
377
378        } else {
379
380          obj = {
381            type: 'html',
382            content: item + ''
383          };
384
385        }
386
387        obj.opts = $.extend(true, {}, self.opts, opts);
388
389        // Step 2 - Make sure we have supported content type
390
391        type = obj.type;
392        src = obj.src || '';
393
394        if (!type) {
395
396          if (obj.content) {
397            type = 'html';
398
399          } else if (src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i)) {
400            type = 'image';
401
402          } else if (src.match(/\.(pdf)((\?|#).*)?$/i)) {
403            type = 'pdf';
404
405          } else if (src.charAt(0) === '#') {
406            type = 'inline';
407
408          }
409
410          obj.type = type;
411
412        }
413
414        // Step 3 - Some adjustments
415
416        obj.index = self.group.length;
417
418        // Check if $orig and $thumb objects exist
419        if (obj.opts.$orig && !obj.opts.$orig.length) {
420          delete obj.opts.$orig;
421        }
422
423        if (!obj.opts.$thumb && obj.opts.$orig) {
424          obj.opts.$thumb = obj.opts.$orig.find('img:first');
425        }
426
427        if (obj.opts.$thumb && !obj.opts.$thumb.length) {
428          delete obj.opts.$thumb;
429        }
430
431        // Caption is a "special" option, it can be passed as a method
432        if ($.type(obj.opts.caption) === 'function') {
433          obj.opts.caption = obj.opts.caption.apply(item, [self, obj]);
434
435        } else if ('caption' in data) {
436          obj.opts.caption = data.caption;
437
438        } else if (opts.$orig) {
439          obj.opts.caption = $item.attr('title');
440        }
441
442        // Make sure we have caption as a string
443        obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + '';
444
445        // Check if url contains selector used to filter the content
446        // Example: "ajax.html #something"
447        if (type === 'ajax') {
448          srcParts = src.split(/\s+/, 2);
449
450          if (srcParts.length > 1) {
451            obj.src = srcParts.shift();
452
453            obj.opts.selector = srcParts.shift();
454          }
455        }
456
457        if (obj.opts.smallBtn == 'auto') {
458
459          if ($.inArray(type, ['html', 'inline', 'ajax']) > -1) {
460            obj.opts.buttons = false;
461            obj.opts.smallBtn = true;
462
463          } else {
464            obj.opts.smallBtn = false;
465          }
466
467        }
468
469        if (type === 'pdf') {
470
471          obj.type = 'iframe';
472
473          obj.opts.closeBtn = true;
474          obj.opts.smallBtn = false;
475
476          obj.opts.iframe.preload = false;
477
478        }
479
480        if (obj.opts.modal) {
481
482          $.extend(true, obj.opts, {
483            infobar: 0,
484            buttons: 0,
485            keyboard: 0,
486            slideShow: 0,
487            fullScreen: 0,
488            closeClickOutside: 0
489          });
490
491        }
492
493        self.group.push(obj);
494
495      });
496
497    },
498
499
500    // Attach an event handler functions for:
501    //   - navigation elements
502    //   - browser scrolling, resizing;
503    //   - focusing
504    //   - keyboard
505    // =================
506
507    addEvents: function () {
508      var self = this;
509
510      self.removeEvents();
511
512      // Make navigation elements clickable
513
514      self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function (e) {
515        e.stopPropagation();
516        e.preventDefault();
517
518        self.close(e);
519
520      }).on('click.fb-previous', '[data-fancybox-previous]', function (e) {
521        e.stopPropagation();
522        e.preventDefault();
523
524        self.previous();
525
526      }).on('click.fb-next', '[data-fancybox-next]', function (e) {
527        e.stopPropagation();
528        e.preventDefault();
529
530        self.next();
531      });
532
533
534      // Handle page scrolling and browser resizing
535
536      $(window).on('orientationchange.fb resize.fb', function (e) {
537        requestAFrame(function () {
538
539          if (e && e.originalEvent && e.originalEvent.type === "resize") {
540            self.update();
541
542          } else {
543            self.$refs.slider_wrap.hide();
544
545            requestAFrame(function () {
546              self.$refs.slider_wrap.show();
547
548              self.update();
549            });
550
551          }
552
553        });
554      });
555
556
557      // Trap focus
558
559      $D.on('focusin.fb', function (e) {
560        var instance = $.fancybox ? $.fancybox.getInstance() : null;
561
562        if (instance && !$(e.target).hasClass('fancybox-container') && !$.contains(instance.$refs.container[0], e.target)) {
563          e.stopPropagation();
564
565          instance.focus();
566
567          // Sometimes page gets scrolled, set it back
568          $W.scrollTop(self.scrollTop).scrollLeft(self.scrollLeft);
569        }
570
571      });
572
573      // Enable keyboard navigation
574
575      $D.on('keydown.fb', function (e) {
576        var current = self.current,
577            keycode = e.keyCode || e.which;
578
579        if (!current || !current.opts.keyboard) {
580          return;
581        }
582
583        if ($(e.target).is('input') || $(e.target).is('textarea')) {
584          return;
585        }
586
587        // Backspace and Esc keys
588        if (keycode === 8 || keycode === 27) {
589          e.preventDefault();
590
591          self.close(e);
592
593          return;
594        }
595
596        switch (keycode) {
597
598          case 37: // Left arrow
599          case 38: // Up arrow
600
601            e.preventDefault();
602
603            self.previous();
604
605            break;
606
607          case 39: // Right arrow
608          case 40: // Down arrow
609
610            e.preventDefault();
611
612            self.next();
613
614            break;
615
616          case 80: // "P"
617          case 32: // Spacebar
618
619            e.preventDefault();
620
621            if (self.SlideShow) {
622              e.preventDefault();
623
624              self.SlideShow.toggle();
625            }
626
627            break;
628
629          case 70: // "F"
630
631            if (self.FullScreen) {
632              e.preventDefault();
633
634              self.FullScreen.toggle();
635            }
636
637            break;
638
639          case 71: // "G"
640
641            if (self.Thumbs) {
642              e.preventDefault();
643
644              self.Thumbs.toggle();
645            }
646
647            break;
648        }
649      });
650
651
652    },
653
654
655    // Remove events added by the core
656    // ===============================
657
658    removeEvents: function () {
659
660      $W.off('scroll.fb resize.fb orientationchange.fb');
661      $D.off('keydown.fb focusin.fb click.fb-close');
662
663      this.$refs.container.off('click.fb-close click.fb-previous click.fb-next');
664    },
665
666
667    // Slide to left
668    // ==================
669
670    previous: function (duration) {
671      this.jumpTo(this.currIndex - 1, duration);
672    },
673
674
675    // Slide to right
676    // ===================
677
678    next: function (duration) {
679      this.jumpTo(this.currIndex + 1, duration);
680    },
681
682
683    // Display current gallery item, move slider to current position
684    // =============================================================
685
686    jumpTo: function (to, duration) {
687      var self = this,
688          firstRun,
689          index,
690          pos,
691          loop;
692
693      firstRun = self.firstRun = (self.firstRun === null);
694
695      index = pos = to = parseInt(to, 10);
696      loop = self.current ? self.current.opts.loop : false;
697
698      if (self.isAnimating || (index == self.currIndex && !firstRun)) {
699        return;
700      }
701
702      if (self.group.length > 1 && loop) {
703
704        index = index % self.group.length;
705        index = index < 0 ? self.group.length + index : index;
706
707        // Calculate closest position of upcoming item from the current one
708        if (self.group.length == 2) {
709          pos = to - self.currIndex + self.currPos;
710
711        } else {
712          pos = index - self.currIndex + self.currPos;
713
714          if (Math.abs(self.currPos - (pos + self.group.length)) < Math.abs(self.currPos - pos)) {
715            pos = pos + self.group.length;
716
717          } else if (Math.abs(self.currPos - (pos - self.group.length)) < Math.abs(self.currPos - pos)) {
718            pos = pos - self.group.length;
719
720          }
721        }
722
723      } else if (!self.group[index]) {
724        self.update(false, false, duration);
725
726        return;
727      }
728
729      if (self.current) {
730        self.current.$slide.removeClass('fancybox-slide--current fancybox-slide--complete');
731
732        self.updateSlide(self.current, true);
733      }
734
735      self.prevIndex = self.currIndex;
736      self.prevPos = self.currPos;
737
738      self.currIndex = index;
739      self.currPos = pos;
740
741      // Create slides
742
743      self.current = self.createSlide(pos);
744
745      if (self.group.length > 1) {
746
747        if (self.opts.loop || pos - 1 >= 0) {
748          self.createSlide(pos - 1);
749        }
750
751        if (self.opts.loop || pos + 1 < self.group.length) {
752          self.createSlide(pos + 1);
753        }
754      }
755
756      self.current.isMoved = false;
757      self.current.isComplete = false;
758
759      duration = parseInt(duration === undefined ? self.current.opts.speed * 1.5 : duration, 10);
760
761      // Move slider to the next position
762      // Note: the content might still be loading
763      self.trigger('beforeMove');
764
765      self.updateControls();
766
767      if (firstRun) {
768        self.current.$slide.addClass('fancybox-slide--current');
769
770        self.$refs.container.show();
771
772        requestAFrame(function () {
773          self.$refs.bg.css('transition-duration', self.current.opts.speed + 'ms');
774
775          self.$refs.container.addClass('fancybox-container--ready');
776        });
777      }
778
779      // Set position immediately on first opening
780      self.update(true, false, firstRun ? 0 : duration, function () {
781        self.afterMove();
782      });
783
784      self.loadSlide(self.current);
785
786      if (!(firstRun && self.current.$ghost)) {
787        self.preload();
788      }
789
790    },
791
792
793    // Create new "slide" element
794    // These are gallery items  that are actually added to DOM
795    // =======================================================
796
797    createSlide: function (pos) {
798
799      var self = this;
800      var $slide;
801      var index;
802      var found;
803
804      index = pos % self.group.length;
805      index = index < 0 ? self.group.length + index : index;
806
807      if (!self.slides[pos] && self.group[index]) {
808
809        // If we are looping and slide with that index already exists, then reuse it
810        if (self.opts.loop && self.group.length > 2) {
811          for (var key in self.slides) {
812            if (self.slides[key].index === index) {
813              found = self.slides[key];
814              found.pos = pos;
815
816              self.slides[pos] = found;
817
818              delete self.slides[key];
819
820              self.updateSlide(found);
821
822              return found;
823            }
824          }
825        }
826
827        $slide = $('<div class="fancybox-slide"></div>').appendTo(self.$refs.slider);
828
829        self.slides[pos] = $.extend(true, {}, self.group[index], {
830          pos: pos,
831          $slide: $slide,
832          isMoved: false,
833          isLoaded: false
834        });
835
836      }
837
838      return self.slides[pos];
839
840    },
841
842    zoomInOut: function (type, duration, callback) {
843
844      var self = this;
845      var current = self.current;
846      var $what = current.$placeholder;
847      var opacity = current.opts.opacity;
848      var $thumb = current.opts.$thumb;
849      var thumbPos = $thumb ? $thumb.offset() : 0;
850      var slidePos = current.$slide.offset();
851      var props;
852      var start;
853      var end;
854
855      if (!$what || !current.isMoved || !thumbPos || !isElementInViewport($thumb)) {
856        return false;
857      }
858
859      if (type === 'In' && !self.firstRun) {
860        return false;
861      }
862
863      $.fancybox.stop($what);
864
865      self.isAnimating = true;
866
867      props = {
868        top: thumbPos.top - slidePos.top + parseFloat($thumb.css("border-top-width") || 0),
869        left: thumbPos.left - slidePos.left + parseFloat($thumb.css("border-left-width") || 0),
870        width: $thumb.width(),
871        height: $thumb.height(),
872        scaleX: 1,
873        scaleY: 1
874      };
875
876      // Check if we need to animate opacity
877      if (opacity == 'auto') {
878        opacity = Math.abs(current.width / current.height - props.width / props.height) > 0.1;
879      }
880
881      if (type === 'In') {
882        start = props;
883        end = self.getFitPos(current);
884
885        end.scaleX = end.width / start.width;
886        end.scaleY = end.height / start.height;
887
888        if (opacity) {
889          start.opacity = 0.1;
890          end.opacity = 1;
891        }
892
893      } else {
894
895        start = $.fancybox.getTranslate($what);
896        end = props;
897
898        // Switch to thumbnail image to improve animation performance
899        if (current.$ghost) {
900          current.$ghost.show();
901
902          if (current.$image) {
903            current.$image.remove();
904          }
905        }
906
907        start.scaleX = start.width / end.width;
908        start.scaleY = start.height / end.height;
909
910        start.width = end.width;
911        start.height = end.height;
912
913        if (opacity) {
914          end.opacity = 0;
915        }
916
917      }
918
919      self.updateCursor(end.width, end.height);
920
921      // There is no need to animate width/height properties
922      delete end.width;
923      delete end.height;
924
925      $.fancybox.setTranslate($what, start);
926
927      $what.show();
928
929      self.trigger('beforeZoom' + type);
930
931      $what.css('transition', 'all ' + duration + 'ms');
932
933      $.fancybox.setTranslate($what, end);
934
935      setTimeout(function () {
936        var reset;
937
938        $what.css('transition', 'none');
939
940        reset = $.fancybox.getTranslate($what);
941
942        reset.scaleX = 1;
943        reset.scaleY = 1;
944
945        // Reset scalex/scaleY values; this helps for perfomance
946        $.fancybox.setTranslate($what, reset);
947
948        self.trigger('afterZoom' + type);
949
950        callback.apply(self);
951
952        self.isAnimating = false;
953
954      }, duration);
955
956
957      return true;
958
959    },
960
961    // Check if image dimensions exceed parent element
962    // ===============================================
963
964    canPan: function () {
965
966      var self = this;
967
968      var current = self.current;
969      var $what = current.$placeholder;
970
971      var rez = false;
972
973      if ($what) {
974        rez = self.getFitPos(current);
975        rez = Math.abs($what.width() - rez.width) > 1 || Math.abs($what.height() - rez.height) > 1;
976
977      }
978
979      return rez;
980
981    },
982
983
984    // Check if current image dimensions are smaller than actual
985    // =========================================================
986
987    isScaledDown: function () {
988
989      var self = this;
990
991      var current = self.current;
992      var $what = current.$placeholder;
993
994      var rez = false;
995
996      if ($what) {
997        rez = $.fancybox.getTranslate($what);
998        rez = rez.width < current.width || rez.height < current.height;
999      }
1000
1001      return rez;
1002
1003    },
1004
1005
1006    // Scale image to the actual size of the image
1007    // ===========================================
1008
1009    scaleToActual: function (x, y, duration) {
1010
1011      var self = this;
1012
1013      var current = self.current;
1014      var $what = current.$placeholder;
1015
1016      var imgPos, posX, posY, scaleX, scaleY;
1017
1018      var canvasWidth = parseInt(current.$slide.width(), 10);
1019      var canvasHeight = parseInt(current.$slide.height(), 10);
1020
1021      var newImgWidth = current.width;
1022      var newImgHeight = current.height;
1023
1024      if (!$what) {
1025        return;
1026      }
1027
1028      self.isAnimating = true;
1029
1030      x = x === undefined ? canvasWidth * 0.5 : x;
1031      y = y === undefined ? canvasHeight * 0.5 : y;
1032
1033      imgPos = $.fancybox.getTranslate($what);
1034
1035      scaleX = newImgWidth / imgPos.width;
1036      scaleY = newImgHeight / imgPos.height;
1037
1038      // Get center position for original image
1039      posX = (canvasWidth * 0.5 - newImgWidth * 0.5);
1040      posY = (canvasHeight * 0.5 - newImgHeight * 0.5);
1041
1042      // Make sure image does not move away from edges
1043
1044      if (newImgWidth > canvasWidth) {
1045        posX = imgPos.left * scaleX - ((x * scaleX) - x);
1046
1047        if (posX > 0) {
1048          posX = 0;
1049        }
1050
1051        if (posX < canvasWidth - newImgWidth) {
1052          posX = canvasWidth - newImgWidth;
1053        }
1054      }
1055
1056      if (newImgHeight > canvasHeight) {
1057        posY = imgPos.top * scaleY - ((y * scaleY) - y);
1058
1059        if (posY > 0) {
1060          posY = 0;
1061        }
1062
1063        if (posY < canvasHeight - newImgHeight) {
1064          posY = canvasHeight - newImgHeight;
1065        }
1066      }
1067
1068      self.updateCursor(newImgWidth, newImgHeight);
1069
1070      $.fancybox.animate($what, null, {
1071        top: posY,
1072        left: posX,
1073        scaleX: scaleX,
1074        scaleY: scaleY
1075      }, duration || current.opts.speed, function () {
1076        self.isAnimating = false;
1077      });
1078
1079    },
1080
1081
1082    // Scale image to fit inside parent element
1083    // ========================================
1084
1085    scaleToFit: function (duration) {
1086
1087      var self = this;
1088
1089      var current = self.current;
1090      var $what = current.$placeholder;
1091      var end;
1092
1093      if (!$what) {
1094        return;
1095      }
1096
1097      self.isAnimating = true;
1098
1099      end = self.getFitPos(current);
1100
1101      self.updateCursor(end.width, end.height);
1102
1103      $.fancybox.animate($what, null, {
1104        top: end.top,
1105        left: end.left,
1106        scaleX: end.width / $what.width(),
1107        scaleY: end.height / $what.height()
1108      }, duration || current.opts.speed, function () {
1109        self.isAnimating = false;
1110      });
1111
1112    },
1113
1114    // Calculate image size to fit inside viewport
1115    // ===========================================
1116
1117    getFitPos: function (slide) {
1118      var $what = slide.$placeholder || slide.$content;
1119
1120      var imgWidth = slide.width;
1121      var imgHeight = slide.height;
1122
1123      var margin = slide.opts.margin;
1124
1125      var canvasWidth, canvasHeight, minRatio, top, left, width, height;
1126
1127      if (!$what || !$what.length || (!imgWidth && !imgHeight)) {
1128        return false;
1129      }
1130
1131      // Convert "margin to CSS style: [ top, right, bottom, left ]
1132      if ($.type(margin) === "number") {
1133        margin = [margin, margin];
1134      }
1135
1136      if (margin.length == 2) {
1137        margin = [margin[0], margin[1], margin[0], margin[1]];
1138      }
1139
1140      if ($W.width() < 800) {
1141        margin = [0, 0, 0, 0];
1142      }
1143
1144      canvasWidth = parseInt(slide.$slide.width(), 10) - (margin[1] + margin[3]);
1145      canvasHeight = parseInt(slide.$slide.height(), 10) - (margin[0] + margin[2]);
1146
1147      minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight);
1148
1149      // Use floor rounding to make sure it really fits
1150
1151      width = Math.floor(minRatio * imgWidth);
1152      height = Math.floor(minRatio * imgHeight);
1153
1154      top = Math.floor((canvasHeight - height) * 0.5) + margin[0];
1155      left = Math.floor((canvasWidth - width) * 0.5) + margin[3];
1156
1157      return {
1158        top: top,
1159        left: left,
1160        width: width,
1161        height: height
1162      };
1163
1164    },
1165
1166    // Move slider to current position
1167    // Update all slides (and their content)
1168    // =====================================
1169
1170    update: function (andSlides, andContent, duration, callback) {
1171
1172      var self = this;
1173      var leftValue;
1174
1175      if (self.isAnimating === true || !self.current) {
1176        return;
1177      }
1178
1179      leftValue = (self.current.pos * Math.floor(self.current.$slide.width()) * -1) - (self.current.pos * self.current.opts.gutter);
1180      duration = parseInt(duration, 10) || 0;
1181
1182      $.fancybox.stop(self.$refs.slider);
1183
1184      if (andSlides === false) {
1185        self.updateSlide(self.current, andContent);
1186
1187      } else {
1188
1189        $.each(self.slides, function (key, slide) {
1190          self.updateSlide(slide, andContent);
1191        });
1192
1193      }
1194
1195      if (duration) {
1196
1197        $.fancybox.animate(self.$refs.slider, null, {
1198          top: 0,
1199          left: leftValue
1200        }, duration, function () {
1201          self.current.isMoved = true;
1202
1203          if ($.type(callback) === 'function') {
1204            callback.apply(self);
1205          }
1206
1207        });
1208
1209      } else {
1210
1211        $.fancybox.setTranslate(self.$refs.slider, { top: 0, left: leftValue });
1212
1213        self.current.isMoved = true;
1214
1215        if ($.type(callback) === 'function') {
1216          callback.apply(self);
1217        }
1218
1219      }
1220
1221    },
1222
1223
1224    // Update slide position and scale content to fit
1225    // ==============================================
1226
1227    updateSlide: function (slide, andContent) {
1228
1229      var self = this;
1230      var $what = slide.$placeholder;
1231      var leftPos;
1232
1233      slide = slide || self.current;
1234
1235      if (!slide || self.isClosing) {
1236        return;
1237      }
1238
1239      leftPos = (slide.pos * Math.floor(slide.$slide.width())) + (slide.pos * slide.opts.gutter);
1240
1241      if (leftPos !== slide.leftPos) {
1242        $.fancybox.setTranslate(slide.$slide, { top: 0, left: leftPos });
1243
1244        slide.leftPos = leftPos;
1245      }
1246
1247      if (andContent !== false && $what) {
1248        $.fancybox.setTranslate($what, self.getFitPos(slide));
1249
1250        if (slide.pos === self.currPos) {
1251          self.updateCursor();
1252        }
1253      }
1254
1255      slide.$slide.trigger('refresh');
1256
1257      self.trigger('onUpdate', slide);
1258    },
1259
1260    // Update cursor style depending if content can be zoomed
1261    // ======================================================
1262
1263    updateCursor: function (nextWidth, nextHeight) {
1264
1265      var self = this;
1266      var canScale;
1267
1268      var $container = self.$refs.container.removeClass('fancybox-controls--canzoomIn fancybox-controls--canzoomOut fancybox-controls--canGrab');
1269
1270      if (self.isClosing || !self.opts.touch) {
1271        return;
1272      }
1273
1274      if (nextWidth !== undefined && nextHeight !== undefined) {
1275        canScale = nextWidth < self.current.width && nextHeight < self.current.height;
1276
1277      } else {
1278        canScale = self.isScaledDown();
1279      }
1280
1281      if (canScale) {
1282        $container.addClass('fancybox-controls--canzoomIn');
1283
1284      } else if (self.group.length < 2) {
1285        $container.addClass('fancybox-controls--canzoomOut');
1286
1287      } else {
1288        $container.addClass('fancybox-controls--canGrab');
1289      }
1290
1291    },
1292
1293    // Load content into the slide
1294    // ===========================
1295
1296    loadSlide: function (slide) {
1297
1298      var self = this, type, $slide;
1299      var ajaxLoad;
1300
1301      if (!slide || slide.isLoaded || slide.isLoading) {
1302        return;
1303      }
1304
1305      slide.isLoading = true;
1306
1307      self.trigger('beforeLoad', slide);
1308
1309      type = slide.type;
1310      $slide = slide.$slide;
1311
1312      $slide
1313          .off('refresh')
1314          .trigger('onReset')
1315          .addClass('fancybox-slide--' + (type || 'unknown'))
1316          .addClass(slide.opts.slideClass);
1317
1318      // Create content depending on the type
1319
1320      switch (type) {
1321
1322        case 'image':
1323
1324          self.setImage(slide);
1325
1326          break;
1327
1328        case 'iframe':
1329
1330          self.setIframe(slide);
1331
1332          break;
1333
1334        case 'html':
1335
1336          self.setContent(slide, slide.content);
1337
1338          break;
1339
1340        case 'inline':
1341
1342          if ($(slide.src).length) {
1343            self.setContent(slide, $(slide.src));
1344
1345          } else {
1346            self.setError(slide);
1347          }
1348
1349          break;
1350
1351        case 'ajax':
1352
1353          self.showLoading(slide);
1354
1355          ajaxLoad = $.ajax($.extend({}, slide.opts.ajax.settings, {
1356
1357            url: slide.src,
1358
1359            success: function (data, textStatus) {
1360
1361              if (textStatus === 'success') {
1362                self.setContent(slide, data);
1363              }
1364
1365            },
1366
1367            error: function (jqXHR, textStatus) {
1368
1369              if (jqXHR && textStatus !== 'abort') {
1370                self.setError(slide);
1371              }
1372
1373            }
1374
1375          }));
1376
1377          $slide.one('onReset', function () {
1378            ajaxLoad.abort();
1379          });
1380
1381          break;
1382
1383        default:
1384
1385          self.setError(slide);
1386
1387          break;
1388
1389      }
1390
1391      return true;
1392
1393    },
1394
1395
1396    // Use thumbnail image, if possible
1397    // ================================
1398
1399    setImage: function (slide) {
1400
1401      var self = this;
1402      var srcset = slide.opts.image.srcset;
1403
1404      var found, temp, pxRatio, windowWidth;
1405
1406      if (slide.isLoaded && !slide.hasError) {
1407        self.afterLoad(slide);
1408
1409        return;
1410      }
1411
1412      // If we have "srcset", then we need to find matching "src" value.
1413      // This is necessary, because when you set an src attribute, the browser will preload the image
1414      // before any javascript or even CSS is applied.
1415      if (srcset) {
1416        pxRatio = window.devicePixelRatio || 1;
1417        windowWidth = window.innerWidth * pxRatio;
1418
1419        temp = srcset.split(',').map(function (el) {
1420          var ret = {};
1421
1422          el.trim().split(/\s+/).forEach(function (el, i) {
1423            var value = parseInt(el.substring(0, el.length - 1), 10);
1424
1425            if (i === 0) {
1426              return (ret.url = el);
1427            }
1428
1429            if (value) {
1430              ret.value = value;
1431              ret.postfix = el[el.length - 1];
1432            }
1433
1434          });
1435
1436          return ret;
1437        });
1438
1439        // Sort by value
1440        temp.sort(function (a, b) {
1441          return a.value - b.value;
1442        });
1443
1444        // Ok, now we have an array of all srcset values
1445        for (var j = 0; j < temp.length; j++) {
1446          var el = temp[j];
1447
1448          if ((el.postfix === 'w' && el.value >= windowWidth) || (el.postfix === 'x' && el.value >= pxRatio)) {
1449            found = el;
1450            break;
1451          }
1452        }
1453
1454        // If not found, take the last one
1455        if (!found && temp.length) {
1456          found = temp[temp.length - 1];
1457        }
1458
1459        if (found) {
1460          slide.src = found.url;
1461
1462          // If we have default width/height values, we can calculate height for matching source
1463          if (slide.width && slide.height && found.postfix == 'w') {
1464            slide.height = (slide.width / slide.height) * found.value;
1465            slide.width = found.value;
1466          }
1467        }
1468      }
1469
1470      slide.$placeholder = $('<div class="fancybox-placeholder"></div>')
1471          .hide()
1472          .appendTo(slide.$slide);
1473
1474      if (slide.opts.preload !== false && slide.opts.width && slide.opts.height && (slide.opts.thumb || slide.opts.$thumb)) {
1475
1476        slide.width = slide.opts.width;
1477        slide.height = slide.opts.height;
1478
1479        slide.$ghost = $('<img />')
1480            .one('load error', function () {
1481
1482              if (self.isClosing) {
1483                return;
1484              }
1485
1486              // Start preloading full size image
1487              $('<img/>')[0].src = slide.src;
1488
1489              // zoomIn or just show
1490              self.revealImage(slide, function () {
1491
1492                self.setBigImage(slide);
1493
1494                if (self.firstRun && slide.index === self.currIndex) {
1495                  self.preload();
1496                }
1497              });
1498
1499            })
1500            .addClass('fancybox-image')
1501            .appendTo(slide.$placeholder)
1502            .attr('src', slide.opts.thumb || slide.opts.$thumb.attr('src'));
1503
1504      } else {
1505
1506        self.setBigImage(slide);
1507
1508      }
1509
1510    },
1511
1512
1513    // Create full-size image
1514    // ======================
1515
1516    setBigImage: function (slide) {
1517      var self = this;
1518      var $img = $('<img />');
1519
1520      slide.$image = $img
1521          .one('error', function () {
1522
1523            self.setError(slide);
1524
1525          })
1526          .one('load', function () {
1527
1528            // Clear timeout that checks if loading icon needs to be displayed
1529            clearTimeout(slide.timouts);
1530
1531            slide.timouts = null;
1532
1533            if (self.isClosing) {
1534              return;
1535            }
1536
1537            slide.width = this.naturalWidth;
1538            slide.height = this.naturalHeight;
1539
1540            if (slide.opts.image.srcset) {
1541              $img.attr('sizes', '100vw').attr('srcset', slide.opts.image.srcset);
1542            }
1543
1544            self.afterLoad(slide);
1545
1546            if (slide.$ghost) {
1547              slide.timouts = setTimeout(function () {
1548                slide.$ghost.hide();
1549
1550              }, 350);
1551            }
1552
1553          })
1554          .addClass('fancybox-image')
1555          .attr('src', slide.src)
1556          .appendTo(slide.$placeholder);
1557
1558      if ($img[0].complete) {
1559        $img.trigger('load');
1560
1561      } else if ($img[0].error) {
1562        $img.trigger('error');
1563
1564      } else {
1565
1566        slide.timouts = setTimeout(function () {
1567          if (!$img[0].complete && !slide.hasError) {
1568            self.showLoading(slide);
1569          }
1570
1571        }, 150);
1572
1573      }
1574
1575      if (slide.opts.image.protect) {
1576        $('<div class="fancybox-spaceball"></div>').appendTo(slide.$placeholder).on('contextmenu.fb', function (e) {
1577          if (e.button == 2) {
1578            e.preventDefault();
1579          }
1580
1581          return true;
1582        });
1583      }
1584
1585    },
1586
1587    // Simply show image holder without animation
1588    // It has been hidden initially to avoid flickering
1589    // ================================================
1590
1591    revealImage: function (slide, callback) {
1592
1593      var self = this;
1594
1595      callback = callback || $.noop;
1596
1597      if (slide.type !== 'image' || slide.hasError || slide.isRevealed === true) {
1598
1599        callback.apply(self);
1600
1601        return;
1602      }
1603
1604      slide.isRevealed = true;
1605
1606      if (!(slide.pos === self.currPos && self.zoomInOut('In', slide.opts.speed, callback))) {
1607
1608        if (slide.$ghost && !slide.isLoaded) {
1609          self.updateSlide(slide, true);
1610        }
1611
1612        if (slide.pos === self.currPos) {
1613          $.fancybox.animate(slide.$placeholder, { opacity: 0 }, { opacity: 1 }, 300, callback);
1614
1615        } else {
1616          slide.$placeholder.show();
1617        }
1618
1619        callback.apply(self);
1620
1621      }
1622
1623    },
1624
1625    // Create iframe wrapper, iframe and bindings
1626    // ==========================================
1627
1628    setIframe: function (slide) {
1629      var self = this,
1630          opts = slide.opts.iframe,
1631          $slide = slide.$slide,
1632          $iframe;
1633
1634      slide.$content = $('<div class="fancybox-content"></div>')
1635          .css(opts.css)
1636          .appendTo($slide);
1637
1638      $iframe = $(opts.tpl.replace(/\{rnd\}/g, new Date().getTime()))
1639          .attr('scrolling', $.fancybox.isTouch ? 'auto' : opts.scrolling)
1640          .appendTo(slide.$content);
1641
1642      if (opts.preload) {
1643        slide.$content.addClass('fancybox-tmp');
1644
1645        self.showLoading(slide);
1646
1647        // Unfortunately, it is not always possible to determine if iframe is successfully loaded
1648        // (due to browser security policy)
1649
1650        $iframe.on('load.fb error.fb', function (e) {
1651          this.isReady = 1;
1652
1653          slide.$slide.trigger('refresh');
1654
1655          self.afterLoad(slide);
1656
1657        });
1658
1659        // Recalculate iframe content size
1660
1661        $slide.on('refresh.fb', function () {
1662          var $wrap = slide.$content,
1663              $contents,
1664              $body,
1665              scrollWidth,
1666              frameWidth,
1667              frameHeight;
1668
1669          if ($iframe[0].isReady !== 1) {
1670            return;
1671          }
1672
1673          // Check if content is accessible,
1674          // it will fail if frame is not with the same origin
1675
1676          try {
1677            $contents = $iframe.contents();
1678            $body = $contents.find('body');
1679
1680          } catch (ignore) { }
1681
1682          // Calculate dimensions for the wrapper
1683
1684          if ($body && $body.length && !(opts.css.width !== undefined && opts.css.height !== undefined)) {
1685
1686            scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth;
1687
1688            frameWidth = Math.ceil($body.outerWidth(true) + ($wrap.width() - scrollWidth));
1689            frameHeight = Math.ceil($body.outerHeight(true));
1690
1691            // Resize wrapper to fit iframe content
1692
1693            $wrap.css({
1694              'width': opts.css.width === undefined ? frameWidth + ($wrap.outerWidth() - $wrap.innerWidth()) : opts.css.width,
1695              'height': opts.css.height === undefined ? frameHeight + ($wrap.outerHeight() - $wrap.innerHeight()) : opts.css.height
1696            });
1697
1698          }
1699
1700          $wrap.removeClass('fancybox-tmp');
1701
1702        });
1703
1704      } else {
1705
1706        this.afterLoad(slide);
1707
1708      }
1709
1710      $iframe.attr('src', slide.src);
1711
1712      if (slide.opts.smallBtn) {
1713        slide.$content.prepend(slide.opts.closeTpl);
1714      }
1715
1716      // Remove iframe if closing or changing gallery item
1717
1718      $slide.one('onReset', function () {
1719
1720        // This helps IE not to throw errors when closing
1721
1722        try {
1723
1724          $(this).find('iframe').hide().attr('src', '//about:blank');
1725
1726        } catch (ignore) { }
1727
1728        $(this).empty();
1729
1730        slide.isLoaded = false;
1731
1732      });
1733
1734    },
1735
1736
1737    // Wrap and append content to the slide
1738    // ======================================
1739
1740    setContent: function (slide, content) {
1741
1742      var self = this;
1743
1744      if (self.isClosing) {
1745        return;
1746      }
1747
1748      self.hideLoading(slide);
1749
1750      slide.$slide.empty();
1751
1752      if (isQuery(content) && content.parent().length) {
1753
1754        // If it is a jQuery object, then it will be moved to the box.
1755        // The placeholder is created so we will know where to put it back.
1756        // If user is navigating gallery fast, then the content might be already moved to the box
1757
1758        if (content.data('placeholder')) {
1759          content.parents('.fancybox-slide').trigger('onReset');
1760        }
1761
1762        content.data({ 'placeholder': $('<div></div>').hide().insertAfter(content) }).css('display', 'inline-block');
1763
1764      } else {
1765
1766        if ($.type(content) === 'string') {
1767
1768          content = $('<div>').append(content).contents();
1769
1770          if (content[0].nodeType === 3) {
1771            content = $('<div>').html(content);
1772          }
1773
1774        }
1775
1776        if (slide.opts.selector) {
1777          content = $('<div>').html(content).find(slide.opts.selector);
1778        }
1779
1780      }
1781
1782      slide.$slide.one('onReset', function () {
1783        var placeholder = isQuery(content) ? content.data('placeholder') : 0;
1784
1785        if (placeholder) {
1786          content.hide().replaceAll(placeholder);
1787
1788          content.data('placeholder', null);
1789        }
1790
1791        if (!slide.hasError) {
1792          $(this).empty();
1793
1794          slide.isLoaded = false;
1795        }
1796
1797      });
1798
1799      slide.$content = $(content).appendTo(slide.$slide);
1800
1801      if (slide.opts.smallBtn === true) {
1802        slide.$content.find('.fancybox-close-small').remove().end().eq(0).append(slide.opts.closeTpl);
1803      }
1804
1805      this.afterLoad(slide);
1806
1807    },
1808
1809    // Display error message
1810    // =====================
1811
1812    setError: function (slide) {
1813
1814      slide.hasError = true;
1815
1816      this.setContent(slide, slide.opts.errorTpl);
1817
1818    },
1819
1820
1821    showLoading: function (slide) {
1822      var self = this;
1823
1824      slide = slide || self.current;
1825
1826      if (slide && !slide.$spinner) {
1827        slide.$spinner = $(self.opts.spinnerTpl).appendTo(slide.$slide);
1828      }
1829
1830    },
1831
1832    hideLoading: function (slide) {
1833
1834      var self = this;
1835
1836      slide = slide || self.current;
1837
1838      if (slide && slide.$spinner) {
1839        slide.$spinner.remove();
1840
1841        delete slide.$spinner;
1842      }
1843
1844    },
1845
1846    afterMove: function () {
1847
1848      var self = this;
1849      var current = self.current;
1850      var slides = {};
1851
1852      if (!current) {
1853        return;
1854      }
1855
1856      current.$slide.siblings().trigger('onReset');
1857
1858      // Remove unnecessary slides
1859      $.each(self.slides, function (key, slide) {
1860
1861        if (slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1) {
1862          slides[slide.pos] = slide;
1863
1864        } else if (slide) {
1865          slide.$slide.remove();
1866        }
1867
1868      });
1869
1870      self.slides = slides;
1871
1872      self.trigger('afterMove');
1873
1874      if (current.isLoaded) {
1875        self.complete();
1876      }
1877
1878    },
1879
1880    // Adjustments after slide has been loaded
1881    // =======================================
1882
1883    afterLoad: function (slide) {
1884
1885      var self = this;
1886
1887      if (self.isClosing) {
1888        return;
1889      }
1890
1891      slide.isLoading = false;
1892      slide.isLoaded = true;
1893
1894      self.trigger('afterLoad', slide);
1895
1896      self.hideLoading(slide);
1897
1898      // Resize content to fit inside slide
1899      // Skip if slide has an $ghost element, because then it has been already processed
1900      if (!slide.$ghost) {
1901        self.updateSlide(slide, true);
1902      }
1903
1904      if (slide.index === self.currIndex && slide.isMoved) {
1905        self.complete();
1906
1907      } else if (!slide.$ghost) {
1908        self.revealImage(slide);
1909      }
1910
1911    },
1912
1913
1914    // Final adjustments after current gallery item is moved to position
1915    // and it`s content is loaded
1916    // ==================================================================
1917
1918    complete: function () {
1919
1920      var self = this;
1921
1922      var current = self.current;
1923
1924      self.revealImage(current, function () {
1925        current.isComplete = true;
1926
1927        current.$slide.addClass('fancybox-slide--complete');
1928
1929        self.updateCursor();
1930
1931        self.trigger('onComplete');
1932
1933        // Try to focus on the first focusable element, skip for images and iframes
1934        if (current.opts.focus && !(current.type === 'image' || current.type === 'iframe')) {
1935          self.focus();
1936        }
1937
1938      });
1939
1940    },
1941
1942
1943    // Preload next and previous slides
1944    // ================================
1945
1946    preload: function () {
1947      var self = this;
1948      var next, prev;
1949
1950      if (self.group.length < 2) {
1951        return;
1952      }
1953
1954      next = self.slides[self.currPos + 1];
1955      prev = self.slides[self.currPos - 1];
1956
1957      if (next && next.type === 'image') {
1958        self.loadSlide(next);
1959      }
1960
1961      if (prev && prev.type === 'image') {
1962        self.loadSlide(prev);
1963      }
1964
1965    },
1966
1967
1968    // Try to find and focus on the first focusable element
1969    // ====================================================
1970
1971    focus: function () {
1972      var current = this.current;
1973      var $el;
1974
1975      $el = current && current.isComplete ? current.$slide.find('button,:input,[tabindex],a:not(".disabled")').filter(':visible:first') : null;
1976
1977      if (!$el || !$el.length) {
1978        $el = this.$refs.container;
1979      }
1980
1981      $el.focus();
1982
1983      // Scroll position of wrapper element sometimes changes after focusing (IE)
1984      this.$refs.slider_wrap.scrollLeft(0);
1985
1986      // And the same goes for slide element
1987      if (current) {
1988        current.$slide.scrollTop(0);
1989      }
1990
1991    },
1992
1993
1994    // Activates current instance - brings container to the front and enables keyboard,
1995    // notifies other instances about deactivating
1996    // =================================================================================
1997
1998    activate: function () {
1999      var self = this;
2000
2001      // Deactivate all instances
2002
2003      $('.fancybox-container').each(function () {
2004        var instance = $(this).data('FancyBox');
2005
2006        // Skip self and closing instances
2007
2008        if (instance && instance.uid !== self.uid && !instance.isClosing) {
2009          instance.trigger('onDeactivate');
2010        }
2011
2012      });
2013
2014      if (self.current) {
2015
2016        if (self.$refs.container.index() > 0) {
2017          self.$refs.container.prependTo(document.body);
2018        }
2019
2020        self.updateControls();
2021      }
2022
2023      self.trigger('onActivate');
2024
2025      self.addEvents();
2026
2027    },
2028
2029
2030    // Start closing procedure
2031    // This will start "zoom-out" animation if needed and clean everything up afterwards
2032    // =================================================================================
2033
2034    close: function (e) {
2035
2036      var self = this;
2037      var current = self.current;
2038      var duration = current.opts.speed;
2039
2040      var done = $.proxy(function () {
2041
2042        self.cleanUp(e);  // Now "this" is again our instance
2043
2044      }, this);
2045
2046      if (self.isAnimating || self.isClosing) {
2047        return false;
2048      }
2049
2050      // If beforeClose callback prevents closing, make sure content is centered
2051      if (self.trigger('beforeClose', e) === false) {
2052        $.fancybox.stop(self.$refs.slider);
2053
2054        requestAFrame(function () {
2055          self.update(true, true, 150);
2056        });
2057
2058        return;
2059      }
2060
2061      self.isClosing = true;
2062
2063      if (current.timouts) {
2064        clearTimeout(current.timouts);
2065      }
2066
2067      if (e !== true) {
2068        $.fancybox.stop(self.$refs.slider);
2069      }
2070
2071      self.$refs.container
2072          .removeClass('fancybox-container--active')
2073          .addClass('fancybox-container--closing');
2074
2075      current.$slide
2076          .removeClass('fancybox-slide--complete')
2077          .siblings()
2078          .remove();
2079
2080
2081      if (!current.isMoved) {
2082        current.$slide.css('overflow', 'visible');
2083      }
2084
2085      // Remove all events
2086      // If there are multiple instances, they will be set again by "activate" method
2087
2088      self.removeEvents();
2089
2090      // Clean up
2091
2092      self.hideLoading(current);
2093
2094      self.hideControls();
2095
2096      self.updateCursor();
2097
2098      self.$refs.bg.css('transition-duration', duration + 'ms');
2099
2100      this.$refs.container.removeClass('fancybox-container--ready');
2101
2102      if (e === true) {
2103        setTimeout(done, duration);
2104
2105      } else if (!self.zoomInOut('Out', duration, done)) {
2106        $.fancybox.animate(self.$refs.container, null, { opacity: 0 }, duration, "easeInSine", done);
2107      }
2108
2109    },
2110
2111
2112    // Final adjustments after removing the instance
2113    // =============================================
2114
2115    cleanUp: function (e) {
2116      var self = this,
2117          instance;
2118
2119      self.$refs.slider.children().trigger('onReset');
2120
2121      self.$refs.container.empty().remove();
2122
2123      self.trigger('afterClose', e);
2124
2125      self.current = null;
2126
2127      // Check if there are other instances
2128      instance = $.fancybox.getInstance();
2129
2130      if (instance) {
2131        instance.activate();
2132
2133      } else {
2134
2135        $('html').removeClass('fancybox-enabled');
2136        $('body').removeAttr('style');
2137
2138        $W.scrollTop(self.scrollTop).scrollLeft(self.scrollLeft);
2139
2140        $('#fancybox-noscroll').remove();
2141
2142      }
2143
2144      // Place back focus
2145      if (self.$lastFocus) {
2146        self.$lastFocus.focus();
2147      }
2148
2149    },
2150
2151
2152    // Call callback and trigger an event
2153    // ==================================
2154
2155    trigger: function (name, slide) {
2156      var args = Array.prototype.slice.call(arguments, 1),
2157          self = this,
2158          obj = slide && slide.opts ? slide : self.current,
2159          rez;
2160
2161      if (obj) {
2162        args.unshift(obj);
2163
2164      } else {
2165        obj = self;
2166      }
2167
2168      args.unshift(self);
2169
2170      if ($.isFunction(obj.opts[name])) {
2171        rez = obj.opts[name].apply(obj, args);
2172      }
2173
2174      if (rez === false) {
2175        return rez;
2176      }
2177
2178      if (name === 'afterClose') {
2179        $(document).trigger(name + '.fb', args);
2180
2181      } else {
2182        self.$refs.container.trigger(name + '.fb', args);
2183      }
2184
2185    },
2186
2187
2188    // Toggle toolbar and caption
2189    // ==========================
2190
2191    toggleControls: function (force) {
2192
2193      if (this.isHiddenControls) {
2194        this.updateControls(force);
2195
2196      } else {
2197        this.hideControls();
2198      }
2199
2200
2201    },
2202
2203
2204    // Hide toolbar and caption
2205    // ========================
2206
2207    hideControls: function () {
2208
2209      this.isHiddenControls = true;
2210
2211      this.$refs.container.removeClass('fancybox-show-controls');
2212
2213      this.$refs.container.removeClass('fancybox-show-caption');
2214
2215    },
2216
2217
2218    // Update infobar values, navigation button states and reveal caption
2219    // ==================================================================
2220
2221    updateControls: function (force) {
2222
2223      var self = this;
2224
2225      var $container = self.$refs.container;
2226      var $caption = self.$refs.caption;
2227
2228      // Toggle infobar and buttons
2229
2230      var current = self.current;
2231      var index = current.index;
2232      var opts = current.opts;
2233      var caption = opts.caption;
2234
2235      if (this.isHiddenControls && force !== true) {
2236        return;
2237      }
2238
2239      this.isHiddenControls = false;
2240
2241      $container
2242          .addClass('fancybox-show-controls')
2243          .toggleClass('fancybox-show-infobar', !!opts.infobar && self.group.length > 1)
2244          .toggleClass('fancybox-show-buttons', !!opts.buttons)
2245          .toggleClass('fancybox-is-modal', !!opts.modal);
2246
2247      $('.fancybox-button--left', $container).toggleClass('fancybox-button--disabled', (!opts.loop && index <= 0));
2248      $('.fancybox-button--right', $container).toggleClass('fancybox-button--disabled', (!opts.loop && index >= self.group.length - 1));
2249
2250      $('.fancybox-button--play', $container).toggle(!!(opts.slideShow && self.group.length > 1));
2251      $('.fancybox-button--close', $container).toggle(!!opts.closeBtn);
2252
2253      // Update infobar values
2254
2255      $('.js-fancybox-count', $container).html(self.group.length);
2256      $('.js-fancybox-index', $container).html(index + 1);
2257
2258      // Recalculate content dimensions
2259      current.$slide.trigger('refresh');
2260
2261      // Reveal or create new caption
2262      if ($caption) {
2263        $caption.empty();
2264      }
2265
2266      if (caption && caption.length) {
2267        $caption.html(caption);
2268
2269        this.$refs.container.addClass('fancybox-show-caption ');
2270
2271        self.$caption = $caption;
2272
2273      } else {
2274        this.$refs.container.removeClass('fancybox-show-caption');
2275      }
2276
2277    }
2278
2279  });
2280
2281
2282  $.fancybox = {
2283
2284    version: "3.0.47",
2285    defaults: defaults,
2286
2287
2288    // Get current instance and execute a command.
2289    //
2290    // Examples of usage:
2291    //
2292    //   $instance = $.fancybox.getInstance();
2293    //   $.fancybox.getInstance().jumpTo( 1 );
2294    //   $.fancybox.getInstance( 'jumpTo', 1 );
2295    //   $.fancybox.getInstance( function() {
2296    //       console.info( this.currIndex );
2297    //   });
2298    // ======================================================
2299
2300    getInstance: function (command) {
2301      var instance = $('.fancybox-container:not(".fancybox-container--closing"):first').data('FancyBox');
2302      var args = Array.prototype.slice.call(arguments, 1);
2303
2304      if (instance instanceof FancyBox) {
2305
2306        if ($.type(command) === 'string') {
2307          instance[command].apply(instance, args);
2308
2309        } else if ($.type(command) === 'function') {
2310          command.apply(instance, args);
2311
2312        }
2313
2314        return instance;
2315      }
2316
2317      return false;
2318
2319    },
2320
2321
2322    // Create new instance
2323    // ===================
2324
2325    open: function (items, opts, index) {
2326      return new FancyBox(items, opts, index);
2327    },
2328
2329
2330    // Close current or all instances
2331    // ==============================
2332
2333    close: function (all) {
2334      var instance = this.getInstance();
2335
2336      if (instance) {
2337        instance.close();
2338
2339        // Try to find and close next instance
2340
2341        if (all === true) {
2342          this.close();
2343        }
2344      }
2345
2346    },
2347
2348
2349    // Test for the existence of touch events in the browser
2350    // Limit to mobile devices
2351    // ====================================================
2352
2353    isTouch: document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent),
2354
2355
2356    // Detect if 'translate3d' support is available
2357    // ============================================
2358
2359    use3d: (function () {
2360      var div = document.createElement('div');
2361
2362      return window.getComputedStyle(div).getPropertyValue('transform') && !(document.documentMode && document.documentMode <= 11);
2363    }()),
2364
2365
2366    // Helper function to get current visual state of an element
2367    // returns array[ top, left, horizontal-scale, vertical-scale, opacity ]
2368    // =====================================================================
2369
2370    getTranslate: function ($el) {
2371      var position, matrix;
2372
2373      if (!$el || !$el.length) {
2374        return false;
2375      }
2376
2377      position = $el.get(0).getBoundingClientRect();
2378      matrix = $el.eq(0).css('transform');
2379
2380      if (matrix && matrix.indexOf('matrix') !== -1) {
2381        matrix = matrix.split('(')[1];
2382        matrix = matrix.split(')')[0];
2383        matrix = matrix.split(',');
2384      } else {
2385        matrix = [];
2386      }
2387
2388      if (matrix.length) {
2389
2390        // If IE
2391        if (matrix.length > 10) {
2392          matrix = [matrix[13], matrix[12], matrix[0], matrix[5]];
2393
2394        } else {
2395          matrix = [matrix[5], matrix[4], matrix[0], matrix[3]];
2396        }
2397
2398        matrix = matrix.map(parseFloat);
2399
2400      } else {
2401        matrix = [0, 0, 1, 1];
2402      }
2403
2404      return {
2405        top: matrix[0],
2406        left: matrix[1],
2407        scaleX: matrix[2],
2408        scaleY: matrix[3],
2409        opacity: parseFloat($el.css('opacity')),
2410        width: position.width,
2411        height: position.height
2412      };
2413
2414    },
2415
2416
2417    // Shortcut for setting "translate3d" properties for element
2418    // Can set be used to set opacity, too
2419    // ========================================================
2420
2421    setTranslate: function ($el, props) {
2422      var str = '';
2423      var css = {};
2424
2425      if (!$el || !props) {
2426        return;
2427      }
2428
2429      if (props.left !== undefined || props.top !== undefined) {
2430
2431        str = (props.left === undefined ? $el.position().top : props.left) + 'px, ' + (props.top === undefined ? $el.position().top : props.top) + 'px';
2432
2433        if (this.use3d) {
2434          str = 'translate3d(' + str + ', 0px)';
2435
2436        } else {
2437          str = 'translate(' + str + ')';
2438        }
2439
2440      }
2441
2442      if (props.scaleX !== undefined && props.scaleY !== undefined) {
2443        str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')';
2444      }
2445
2446      if (str.length) {
2447        css.transform = str;
2448      }
2449
2450      if (props.opacity !== undefined) {
2451        css.opacity = props.opacity;
2452      }
2453
2454      if (props.width !== undefined) {
2455        css.width = props.width;
2456      }
2457
2458      if (props.height !== undefined) {
2459        css.height = props.height;
2460      }
2461
2462      return $el.css(css);
2463
2464    },
2465
2466
2467    // Common easings for entrances and exits
2468    // t: current time, b: begInnIng value, c: change In value, d: duration
2469    // ====================================================================
2470
2471    easing: {
2472      easeOutCubic: function (t, b, c, d) {
2473        return c * ((t = t / d - 1) * t * t + 1) + b;
2474      },
2475      easeInCubic: function (t, b, c, d) {
2476        return c * (t /= d) * t * t + b;
2477      },
2478      easeOutSine: function (t, b, c, d) {
2479        return c * Math.sin(t / d * (Math.PI / 2)) + b;
2480      },
2481      easeInSine: function (t, b, c, d) {
2482        return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
2483      }
2484    },
2485
2486
2487    // Stop fancyBox animation
2488    // =======================
2489
2490    stop: function ($el) {
2491
2492      $el.removeData('animateID');
2493
2494    },
2495
2496    // Animate element using "translate3d"
2497    // Usage:
2498    // animate( element, start properties, end properties, duration, easing, callback )
2499    // or
2500    // animate( element, start properties, end properties, duration, callback )
2501    // =================================================================================
2502
2503    animate: function ($el, from, to, duration, easing, done) {
2504
2505      var self = this;
2506
2507      var lastTime = null;
2508      var animTime = 0;
2509
2510      var curr;
2511      var diff;
2512      var id;
2513
2514      var finish = function () {
2515        if (to.scaleX !== undefined && to.scaleY !== undefined && from && from.width !== undefined && from.height !== undefined) {
2516          to.width = from.width * to.scaleX;
2517          to.height = from.height * to.scaleY;
2518
2519          to.scaleX = 1;
2520          to.scaleY = 1;
2521        }
2522
2523        self.setTranslate($el, to);
2524
2525        done();
2526      };
2527
2528      var frame = function (timestamp) {
2529        curr = [];
2530        diff = 0;
2531
2532        // If "stop" method has been called on this element, then just stop
2533        if (!$el.length || $el.data('animateID') !== id) {
2534          return;
2535        }
2536
2537        timestamp = timestamp || Date.now();
2538
2539        if (lastTime) {
2540          diff = timestamp - lastTime;
2541        }
2542
2543        lastTime = timestamp;
2544        animTime += diff;
2545
2546        // Are we done?
2547        if (animTime >= duration) {
2548
2549          finish();
2550
2551          return;
2552        }
2553
2554        for (var prop in to) {
2555
2556          if (to.hasOwnProperty(prop) && from[prop] !== undefined) {
2557
2558            if (from[prop] == to[prop]) {
2559              curr[prop] = to[prop];
2560
2561            } else {
2562              curr[prop] = self.easing[easing](animTime, from[prop], to[prop] - from[prop], duration);
2563            }
2564
2565          }
2566        }
2567
2568        self.setTranslate($el, curr);
2569
2570        requestAFrame(frame);
2571      };
2572
2573      self.animateID = id = self.animateID === undefined ? 1 : self.animateID + 1;
2574
2575      $el.data('animateID', id);
2576
2577      if (done === undefined && $.type(easing) == 'function') {
2578        done = easing;
2579        easing = undefined;
2580      }
2581
2582      if (!easing) {
2583        easing = "easeOutCubic";
2584      }
2585
2586      done = done || $.noop;
2587
2588      if (from) {
2589        this.setTranslate($el, from);
2590
2591      } else {
2592
2593        // We need current values to calculate change in time
2594        from = this.getTranslate($el);
2595      }
2596
2597      if (duration) {
2598        $el.show();
2599
2600        requestAFrame(frame);
2601
2602      } else {
2603        finish();
2604      }
2605
2606    }
2607
2608  };
2609
2610
2611  // Event handler for click event to "fancyboxed" links
2612  // ===================================================
2613
2614  function _run(e) {
2615    var target = e.currentTarget,
2616        opts = e.data ? e.data.options : {},
2617        items = e.data ? e.data.items : [],
2618        value = '',
2619        index = 0;
2620
2621    e.preventDefault();
2622    e.stopPropagation();
2623
2624    // Get all related items and find index for clicked one
2625
2626    if ($(target).attr('data-fancybox')) {
2627      value = $(target).data('fancybox');
2628    }
2629
2630    if (value) {
2631      items = items.length ? items.filter('[data-fancybox="' + value + '"]') : $('[data-fancybox=' + value + ']');
2632      index = items.index(target);
2633
2634    } else {
2635      items = [target];
2636    }
2637
2638    $.fancybox.open(items, opts, index);
2639  }
2640
2641
2642  // Create a jQuery plugin
2643  // ======================
2644
2645  $.fn.fancybox = function (options) {
2646
2647    this.off('click.fb-start').on('click.fb-start', {
2648      items: this,
2649      options: options || {}
2650    }, _run);
2651
2652    return this;
2653
2654  };
2655
2656
2657  // Self initializing plugin
2658  // ========================
2659
2660  $(document).on('click.fb-start', '[data-fancybox]', _run);
2661
2662}(window, document, window.jQuery));
2663
2664// ==========================================================================
2665//
2666// Media
2667// Adds additional media type support
2668//
2669// ==========================================================================
2670; (function ($) {
2671
2672  'use strict';
2673
2674  // Formats matching url to final form
2675
2676  var format = function (url, rez, params) {
2677    if (!url) {
2678      return;
2679    }
2680
2681    params = params || '';
2682
2683    if ($.type(params) === "object") {
2684      params = $.param(params, true);
2685    }
2686
2687    $.each(rez, function (key, value) {
2688      url = url.replace('$' + key, value || '');
2689    });
2690
2691    if (params.length) {
2692      url += (url.indexOf('?') > 0 ? '&' : '?') + params;
2693    }
2694
2695    return url;
2696  };
2697
2698  // Object containing properties for each media type
2699
2700  var media = {
2701    youtube: {
2702      matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i,
2703      params: {
2704        autoplay: 1,
2705        autohide: 1,
2706        fs: 1,
2707        rel: 0,
2708        hd: 1,
2709        wmode: 'transparent',
2710        enablejsapi: 1,
2711        html5: 1
2712      },
2713      paramPlace: 8,
2714      type: 'iframe',
2715      url: '//www.youtube.com/embed/$4',
2716      thumb: '//img.youtube.com/vi/$4/hqdefault.jpg'
2717    },
2718
2719    vimeo: {
2720      matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/,
2721      params: {
2722        autoplay: 1,
2723        hd: 1,
2724        show_title: 1,
2725        show_byline: 1,
2726        show_portrait: 0,
2727        fullscreen: 1,
2728        api: 1
2729      },
2730      paramPlace: 3,
2731      type: 'iframe',
2732      url: '//player.vimeo.com/video/$2'
2733    },
2734
2735    metacafe: {
2736      matcher: /metacafe.com\/watch\/(\d+)\/(.*)?/,
2737      type: 'iframe',
2738      url: '//www.metacafe.com/embed/$1/?ap=1'
2739    },
2740
2741    dailymotion: {
2742      matcher: /dailymotion.com\/video\/(.*)\/?(.*)/,
2743      params: {
2744        additionalInfos: 0,
2745        autoStart: 1
2746      },
2747      type: 'iframe',
2748      url: '//www.dailymotion.com/embed/video/$1'
2749    },
2750
2751    vine: {
2752      matcher: /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/,
2753      type: 'iframe',
2754      url: '//vine.co/v/$1/embed/simple'
2755    },
2756
2757    instagram: {
2758      matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i,
2759      type: 'image',
2760      url: '//$1/p/$2/media/?size=l'
2761    },
2762
2763    // Examples:
2764    // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
2765    // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
2766    // https://www.google.lv/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572
2767    google_maps: {
2768      matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i,
2769      type: 'iframe',
2770      url: function (rez) {
2771        return '//maps.google.' + rez[2] + '/?ll=' + (rez[9] ? rez[9] + '&z=' + Math.floor(rez[10]) + (rez[12] ? rez[12].replace(/^\//, "&") : '') : rez[12]) + '&output=' + (rez[12] && rez[12].indexOf('layer=c') > 0 ? 'svembed' : 'embed');
2772      }
2773    }
2774  };
2775
2776  $(document).on('onInit.fb', function (e, instance) {
2777
2778    $.each(instance.group, function (i, item) {
2779
2780      var url = item.src || '',
2781				type = false,
2782				thumb,
2783				rez,
2784				params,
2785				urlParams,
2786				o,
2787				provider;
2788
2789      // Skip items that already have content type
2790      if (item.type) {
2791        return;
2792      }
2793
2794      // Look for any matching media type
2795
2796      $.each(media, function (n, el) {
2797        rez = url.match(el.matcher);
2798        o = {};
2799        provider = n;
2800
2801        if (!rez) {
2802          return;
2803        }
2804
2805        type = el.type;
2806
2807        if (el.paramPlace && rez[el.paramPlace]) {
2808          urlParams = rez[el.paramPlace];
2809
2810          if (urlParams[0] == '?') {
2811            urlParams = urlParams.substring(1);
2812          }
2813
2814          urlParams = urlParams.split('&');
2815
2816          for (var m = 0; m < urlParams.length; ++m) {
2817            var p = urlParams[m].split('=', 2);
2818
2819            if (p.length == 2) {
2820              o[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
2821            }
2822          }
2823        }
2824
2825        params = $.extend(true, {}, el.params, item.opts[n], o);
2826
2827        url = $.type(el.url) === "function" ? el.url.call(this, rez, params, item) : format(el.url, rez, params);
2828        thumb = $.type(el.thumb) === "function" ? el.thumb.call(this, rez, params, item) : format(el.thumb, rez);
2829
2830        if (provider === 'vimeo') {
2831          url = url.replace('&%23', '#');
2832        }
2833
2834        return false;
2835      });
2836
2837      // If it is found, then change content type and update the url
2838
2839      if (type) {
2840        item.src = url;
2841        item.type = type;
2842
2843        if (!item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length)) {
2844          item.opts.thumb = thumb;
2845        }
2846
2847        if (type === 'iframe') {
2848          $.extend(true, item.opts, {
2849            iframe: {
2850              preload: false,
2851              scrolling: "no"
2852            },
2853            smallBtn: false,
2854            closeBtn: true,
2855            fullScreen: false,
2856            slideShow: false
2857          });
2858
2859          item.opts.slideClass += ' fancybox-slide--video';
2860        }
2861
2862      } else {
2863
2864        // If no content type is found, then set it to `iframe` as fallback
2865        item.type = 'iframe';
2866
2867      }
2868
2869    });
2870
2871  });
2872
2873}(window.jQuery));
2874
2875// ==========================================================================
2876//
2877// Guestures
2878// Adds touch guestures, handles click and tap events
2879//
2880// ==========================================================================
2881; (function (window, document, $) {
2882  'use strict';
2883
2884  var requestAFrame = (function () {
2885    return window.requestAnimationFrame ||
2886				window.webkitRequestAnimationFrame ||
2887				window.mozRequestAnimationFrame ||
2888				function (callback) {
2889				  window.setTimeout(callback, 1000 / 60);
2890				};
2891  })();
2892
2893
2894  var pointers = function (e) {
2895    var result = [];
2896
2897    e = e.originalEvent || e || window.e;
2898    e = e.touches && e.touches.length ? e.touches : (e.changedTouches && e.changedTouches.length ? e.changedTouches : [e]);
2899
2900    for (var key in e) {
2901
2902      if (e[key].pageX) {
2903        result.push({ x: e[key].pageX, y: e[key].pageY });
2904
2905      } else if (e[key].clientX) {
2906        result.push({ x: e[key].clientX, y: e[key].clientY });
2907      }
2908    }
2909
2910    return result;
2911  };
2912
2913  var distance = function (point2, point1, what) {
2914
2915    if (!point1 || !point2) {
2916      return 0;
2917    }
2918
2919    if (what === 'x') {
2920      return point2.x - point1.x;
2921
2922    } else if (what === 'y') {
2923      return point2.y - point1.y;
2924    }
2925
2926    return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
2927
2928  };
2929
2930  var isClickable = function ($el) {
2931
2932    return $el.is('a') || $el.is('button') || $el.is('input') || $el.is('select') || $el.is('textarea') || $.isFunction($el.get(0).onclick);
2933
2934  };
2935
2936  var hasScrollbars = function (el) {
2937    var overflowY = window.getComputedStyle(el)['overflow-y'];
2938    var overflowX = window.getComputedStyle(el)['overflow-x'];
2939
2940    var vertical = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight;
2941    var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth;
2942
2943    return vertical || horizontal;
2944  };
2945
2946  var isScrollable = function ($el) {
2947
2948    var rez = false;
2949
2950    while (true) {
2951      rez = hasScrollbars($el.get(0));
2952
2953      if (rez) {
2954        break;
2955      }
2956
2957      $el = $el.parent();
2958
2959      if (!$el.length || $el.hasClass('fancybox-slider') || $el.is('body')) {
2960        break;
2961      }
2962
2963    }
2964
2965    return rez;
2966
2967  };
2968
2969
2970  var Guestures = function (instance) {
2971
2972    var self = this;
2973
2974    self.instance = instance;
2975
2976    self.$wrap = instance.$refs.slider_wrap;
2977    self.$slider = instance.$refs.slider;
2978    self.$container = instance.$refs.container;
2979
2980    self.destroy();
2981
2982    self.$wrap.on('touchstart.fb mousedown.fb', $.proxy(self, "ontouchstart"));
2983
2984  };
2985
2986  Guestures.prototype.destroy = function () {
2987
2988    this.$wrap.off('touchstart.fb mousedown.fb touchmove.fb mousemove.fb touchend.fb touchcancel.fb mouseup.fb mouseleave.fb');
2989
2990  };
2991
2992  Guestures.prototype.ontouchstart = function (e) {
2993
2994    var self = this;
2995
2996    var $target = $(e.target);
2997    var instance = self.instance;
2998    var current = instance.current;
2999    var $content = current.$content || current.$placeholder;
3000
3001    self.startPoints = pointers(e);
3002
3003    self.$target = $target;
3004    self.$content = $content;
3005
3006    self.canvasWidth = Math.round(current.$slide[0].clientWidth);
3007    self.canvasHeight = Math.round(current.$slide[0].clientHeight);
3008
3009    self.startEvent = e;
3010
3011    // Skip if clicked on the scrollbar
3012    if (e.originalEvent.clientX > self.canvasWidth + current.$slide.offset().left) {
3013      return true;
3014    }
3015
3016    // Ignore taping on links, buttons and scrollable items
3017    if (isClickable($target) || isClickable($target.parent()) || (isScrollable($target))) {
3018      return;
3019    }
3020
3021    // If "touch" is disabled, then handle click event
3022    if (!current.opts.touch) {
3023      self.endPoints = self.startPoints;
3024
3025      return self.ontap();
3026    }
3027
3028    // Ignore right click
3029    if (e.originalEvent && e.originalEvent.button == 2) {
3030      return;
3031    }
3032
3033    e.stopPropagation();
3034    e.preventDefault();
3035
3036    if (!current || self.instance.isAnimating || self.instance.isClosing) {
3037      return;
3038    }
3039
3040    // Prevent zooming if already swiping
3041    if (!self.startPoints || (self.startPoints.length > 1 && !current.isMoved)) {
3042      return;
3043    }
3044
3045    self.$wrap.off('touchmove.fb mousemove.fb', $.proxy(self, "ontouchmove"));
3046    self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(self, "ontouchend"));
3047
3048    self.$wrap.on('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(self, "ontouchend"));
3049    self.$wrap.on('touchmove.fb mousemove.fb', $.proxy(self, "ontouchmove"));
3050
3051    self.startTime = new Date().getTime();
3052    self.distanceX = self.distanceY = self.distance = 0;
3053
3054    self.canTap = false;
3055    self.isPanning = false;
3056    self.isSwiping = false;
3057    self.isZooming = false;
3058
3059    self.sliderStartPos = $.fancybox.getTranslate(self.$slider);
3060
3061    self.contentStartPos = $.fancybox.getTranslate(self.$content);
3062    self.contentLastPos = null;
3063
3064    if (self.startPoints.length === 1 && !self.isZooming) {
3065      self.canTap = current.isMoved;
3066
3067      if (current.type === 'image' && (self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1)) {
3068
3069        $.fancybox.stop(self.$content);
3070
3071        self.isPanning = true;
3072
3073      } else {
3074
3075        $.fancybox.stop(self.$slider);
3076
3077        self.isSwiping = true;
3078      }
3079
3080      self.$container.addClass('fancybox-controls--isGrabbing');
3081
3082    }
3083
3084    if (self.startPoints.length === 2 && current.isMoved && !current.hasError && current.type === 'image' && (current.isLoaded || current.$ghost)) {
3085
3086      self.isZooming = true;
3087
3088      self.isSwiping = false;
3089      self.isPanning = false;
3090
3091      $.fancybox.stop(self.$content);
3092
3093      self.centerPointStartX = ((self.startPoints[0].x + self.startPoints[1].x) * 0.5) - $(window).scrollLeft();
3094      self.centerPointStartY = ((self.startPoints[0].y + self.startPoints[1].y) * 0.5) - $(window).scrollTop();
3095
3096      self.percentageOfImageAtPinchPointX = (self.centerPointStartX - self.contentStartPos.left) / self.contentStartPos.width;
3097      self.percentageOfImageAtPinchPointY = (self.centerPointStartY - self.contentStartPos.top) / self.contentStartPos.height;
3098
3099      self.startDistanceBetweenFingers = distance(self.startPoints[0], self.startPoints[1]);
3100    }
3101
3102  };
3103
3104  Guestures.prototype.ontouchmove = function (e) {
3105
3106    var self = this;
3107
3108    e.preventDefault();
3109
3110    self.newPoints = pointers(e);
3111
3112    if (!self.newPoints || !self.newPoints.length) {
3113      return;
3114    }
3115
3116    self.distanceX = distance(self.newPoints[0], self.startPoints[0], 'x');
3117    self.distanceY = distance(self.newPoints[0], self.startPoints[0], 'y');
3118
3119    self.distance = distance(self.newPoints[0], self.startPoints[0]);
3120
3121    // Skip false ontouchmove events (Chrome)
3122    if (self.distance > 0) {
3123
3124      if (self.isSwiping) {
3125        self.onSwipe();
3126
3127      } else if (self.isPanning) {
3128        self.onPan();
3129
3130      } else if (self.isZooming) {
3131        self.onZoom();
3132      }
3133
3134    }
3135
3136  };
3137
3138  Guestures.prototype.onSwipe = function () {
3139
3140    var self = this;
3141
3142    var swiping = self.isSwiping;
3143    var left = self.sliderStartPos.left;
3144    var angle;
3145
3146    if (swiping === true) {
3147
3148      if (Math.abs(self.distance) > 10) {
3149
3150        if (self.instance.group.length < 2) {
3151          self.isSwiping = 'y';
3152
3153        } else if (!self.instance.current.isMoved || self.instance.opts.touch.vertical === false || (self.instance.opts.touch.vertical === 'auto' && $(window).width() > 800)) {
3154          self.isSwiping = 'x';
3155
3156        } else {
3157          angle = Math.abs(Math.atan2(self.distanceY, self.distanceX) * 180 / Math.PI);
3158
3159          self.isSwiping = (angle > 45 && angle < 135) ? 'y' : 'x';
3160        }
3161
3162        self.canTap = false;
3163
3164        self.instance.current.isMoved = false;
3165
3166        // Reset points to avoid jumping, because we dropped first swipes to calculate the angle
3167        self.startPoints = self.newPoints;
3168      }
3169
3170    } else {
3171
3172      if (swiping == 'x') {
3173
3174        // Sticky edges
3175        if (!self.instance.current.opts.loop && self.instance.current.index === 0 && self.distanceX > 0) {
3176          left = left + Math.pow(self.distanceX, 0.8);
3177
3178        } else if (!self.instance.current.opts.loop && self.instance.current.index === self.instance.group.length - 1 && self.distanceX < 0) {
3179          left = left - Math.pow(-self.distanceX, 0.8);
3180
3181        } else {
3182          left = left + self.distanceX;
3183        }
3184
3185      }
3186
3187      self.sliderLastPos = {
3188        top: swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY,
3189        left: left
3190      };
3191
3192      requestAFrame(function () {
3193        $.fancybox.setTranslate(self.$slider, self.sliderLastPos);
3194      });
3195    }
3196
3197  };
3198
3199  Guestures.prototype.onPan = function () {
3200
3201    var self = this;
3202
3203    var newOffsetX, newOffsetY, newPos;
3204
3205    self.canTap = false;
3206
3207    if (self.contentStartPos.width > self.canvasWidth) {
3208      newOffsetX = self.contentStartPos.left + self.distanceX;
3209
3210    } else {
3211      newOffsetX = self.contentStartPos.left;
3212    }
3213
3214    newOffsetY = self.contentStartPos.top + self.distanceY;
3215
3216    newPos = self.limitMovement(newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height);
3217
3218    newPos.scaleX = self.contentStartPos.scaleX;
3219    newPos.scaleY = self.contentStartPos.scaleY;
3220
3221    self.contentLastPos = newPos;
3222
3223    requestAFrame(function () {
3224      $.fancybox.setTranslate(self.$content, self.contentLastPos);
3225    });
3226  };
3227
3228  // Make panning sticky to the edges
3229  Guestures.prototype.limitMovement = function (newOffsetX, newOffsetY, newWidth, newHeight) {
3230
3231    var self = this;
3232
3233    var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY;
3234
3235    var canvasWidth = self.canvasWidth;
3236    var canvasHeight = self.canvasHeight;
3237
3238    var currentOffsetX = self.contentStartPos.left;
3239    var currentOffsetY = self.contentStartPos.top;
3240
3241    var distanceX = self.distanceX;
3242    var distanceY = self.distanceY;
3243
3244    // Slow down proportionally to traveled distance
3245
3246    minTranslateX = Math.max(0, canvasWidth * 0.5 - newWidth * 0.5);
3247    minTranslateY = Math.max(0, canvasHeight * 0.5 - newHeight * 0.5);
3248
3249    maxTranslateX = Math.min(canvasWidth - newWidth, canvasWidth * 0.5 - newWidth * 0.5);
3250    maxTranslateY = Math.min(canvasHeight - newHeight, canvasHeight * 0.5 - newHeight * 0.5);
3251
3252    if (newWidth > canvasWidth) {
3253
3254      //   ->
3255      if (distanceX > 0 && newOffsetX > minTranslateX) {
3256        newOffsetX = minTranslateX - 1 + Math.pow(-minTranslateX + currentOffsetX + distanceX, 0.8) || 0;
3257      }
3258
3259      //    <-
3260      if (distanceX < 0 && newOffsetX < maxTranslateX) {
3261        newOffsetX = maxTranslateX + 1 - Math.pow(maxTranslateX - currentOffsetX - distanceX, 0.8) || 0;
3262      }
3263
3264    }
3265
3266    if (newHeight > canvasHeight) {
3267
3268      //   \/
3269      if (distanceY > 0 && newOffsetY > minTranslateY) {
3270        newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8) || 0;
3271      }
3272
3273      //   /\
3274      if (distanceY < 0 && newOffsetY < maxTranslateY) {
3275        newOffsetY = maxTranslateY + 1 - Math.pow(maxTranslateY - currentOffsetY - distanceY, 0.8) || 0;
3276      }
3277
3278    }
3279
3280    return {
3281      top: newOffsetY,
3282      left: newOffsetX
3283    };
3284
3285  };
3286
3287
3288  Guestures.prototype.limitPosition = function (newOffsetX, newOffsetY, newWidth, newHeight) {
3289
3290    var self = this;
3291
3292    var canvasWidth = self.canvasWidth;
3293    var canvasHeight = self.canvasHeight;
3294
3295    if (newWidth > canvasWidth) {
3296      newOffsetX = newOffsetX > 0 ? 0 : newOffsetX;
3297      newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX;
3298
3299    } else {
3300
3301      // Center horizontally
3302      newOffsetX = Math.max(0, canvasWidth / 2 - newWidth / 2);
3303
3304    }
3305
3306    if (newHeight > canvasHeight) {
3307      newOffsetY = newOffsetY > 0 ? 0 : newOffsetY;
3308      newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY;
3309
3310    } else {
3311
3312      // Center vertically
3313      newOffsetY = Math.max(0, canvasHeight / 2 - newHeight / 2);
3314
3315    }
3316
3317    return {
3318      top: newOffsetY,
3319      left: newOffsetX
3320    };
3321
3322  };
3323
3324  Guestures.prototype.onZoom = function () {
3325
3326    var self = this;
3327
3328    // Calculate current distance between points to get pinch ratio and new width and height
3329
3330    var currentWidth = self.contentStartPos.width;
3331    var currentHeight = self.contentStartPos.height;
3332
3333    var currentOffsetX = self.contentStartPos.left;
3334    var currentOffsetY = self.contentStartPos.top;
3335
3336    var endDistanceBetweenFingers = distance(self.newPoints[0], self.newPoints[1]);
3337
3338    var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers;
3339
3340    var newWidth = Math.floor(currentWidth * pinchRatio);
3341    var newHeight = Math.floor(currentHeight * pinchRatio);
3342
3343    // This is the translation due to pinch-zooming
3344    var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX;
3345    var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY;
3346
3347    //Point between the two touches
3348
3349    var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft();
3350    var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop();
3351
3352    // And this is the translation due to translation of the centerpoint
3353    // between the two fingers
3354
3355    var translateFromTranslatingX = centerPointEndX - self.centerPointStartX;
3356    var translateFromTranslatingY = centerPointEndY - self.centerPointStartY;
3357
3358    // The new offset is the old/current one plus the total translation
3359
3360    var newOffsetX = currentOffsetX + (translateFromZoomingX + translateFromTranslatingX);
3361    var newOffsetY = currentOffsetY + (translateFromZoomingY + translateFromTranslatingY);
3362
3363    var newPos = {
3364      top: newOffsetY,
3365      left: newOffsetX,
3366      scaleX: self.contentStartPos.scaleX * pinchRatio,
3367      scaleY: self.contentStartPos.scaleY * pinchRatio
3368    };
3369
3370    self.canTap = false;
3371
3372    self.newWidth = newWidth;
3373    self.newHeight = newHeight;
3374
3375    self.contentLastPos = newPos;
3376
3377    requestAFrame(function () {
3378      $.fancybox.setTranslate(self.$content, self.contentLastPos);
3379    });
3380
3381  };
3382
3383  Guestures.prototype.ontouchend = function (e) {
3384
3385    var self = this;
3386
3387    var current = self.instance.current;
3388
3389    var dMs = Math.max((new Date().getTime()) - self.startTime, 1);
3390
3391    var swiping = self.isSwiping;
3392    var panning = self.isPanning;
3393    var zooming = self.isZooming;
3394
3395    self.endPoints = pointers(e);
3396
3397    self.$container.removeClass('fancybox-controls--isGrabbing');
3398
3399    self.$wrap.off('touchmove.fb mousemove.fb', $.proxy(this, "ontouchmove"));
3400    self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb', $.proxy(this, "ontouchend"));
3401
3402    self.isSwiping = false;
3403    self.isPanning = false;
3404    self.isZooming = false;
3405
3406    if (self.canTap) {
3407      return self.ontap();
3408    }
3409
3410    // Speed in px/ms
3411    self.velocityX = self.distanceX / dMs * 0.5;
3412    self.velocityY = self.distanceY / dMs * 0.5;
3413
3414    self.speed = current.opts.speed || 330;
3415
3416    self.speedX = Math.max(self.speed * 0.75, Math.min(self.speed * 1.5, (1 / Math.abs(self.velocityX)) * self.speed));
3417    self.speedY = Math.max(self.speed * 0.75, Math.min(self.speed * 1.5, (1 / Math.abs(self.velocityY)) * self.speed));
3418
3419    if (panning) {
3420      self.endPanning();
3421
3422    } else if (zooming) {
3423      self.endZooming();
3424
3425    } else {
3426      self.endSwiping(swiping);
3427    }
3428
3429    return;
3430  };
3431
3432  Guestures.prototype.endSwiping = function (swiping) {
3433
3434    var self = this;
3435
3436    // Close if swiped vertically / navigate if horizontally
3437
3438    if (swiping == 'y' && Math.abs(self.distanceY) > 50) {
3439
3440      // Continue vertical movement
3441
3442      $.fancybox.animate(self.$slider, null, {
3443        top: self.sliderStartPos.top + self.distanceY + self.velocityY * 150,
3444        left: self.sliderStartPos.left,
3445        opacity: 0
3446      }, self.speedY);
3447
3448      self.instance.close(true);
3449
3450    } else if (swiping == 'x' && self.distanceX > 50) {
3451      self.instance.previous(self.speedX);
3452
3453    } else if (swiping == 'x' && self.distanceX < -50) {
3454      self.instance.next(self.speedX);
3455
3456    } else {
3457
3458      // Move back to center
3459      self.instance.update(false, true, 150);
3460
3461    }
3462
3463  };
3464
3465  Guestures.prototype.endPanning = function () {
3466
3467    var self = this;
3468    var newOffsetX, newOffsetY, newPos;
3469
3470    if (!self.contentLastPos) {
3471      return;
3472    }
3473
3474    newOffsetX = self.contentLastPos.left + (self.velocityX * self.speed * 2);
3475    newOffsetY = self.contentLastPos.top + (self.velocityY * self.speed * 2);
3476
3477    newPos = self.limitPosition(newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height);
3478
3479    newPos.width = self.contentStartPos.width;
3480    newPos.height = self.contentStartPos.height;
3481
3482    $.fancybox.animate(self.$content, null, newPos, self.speed, "easeOutSine");
3483
3484  };
3485
3486
3487  Guestures.prototype.endZooming = function () {
3488
3489    var self = this;
3490
3491    var current = self.instance.current;
3492
3493    var newOffsetX, newOffsetY, newPos, reset;
3494
3495    var newWidth = self.newWidth;
3496    var newHeight = self.newHeight;
3497
3498    if (!self.contentLastPos) {
3499      return;
3500    }
3501
3502    newOffsetX = self.contentLastPos.left;
3503    newOffsetY = self.contentLastPos.top;
3504
3505    reset = {
3506      top: newOffsetY,
3507      left: newOffsetX,
3508      width: newWidth,
3509      height: newHeight,
3510      scaleX: 1,
3511      scaleY: 1
3512    };
3513
3514    // Reset scalex/scaleY values; this helps for perfomance and does not break animation
3515    $.fancybox.setTranslate(self.$content, reset);
3516
3517    if (newWidth < self.canvasWidth && newHeight < self.canvasHeight) {
3518      self.instance.scaleToFit(150);
3519
3520    } else if (newWidth > current.width || newHeight > current.height) {
3521      self.instance.scaleToActual(self.centerPointStartX, self.centerPointStartY, 150);
3522
3523    } else {
3524
3525      newPos = self.limitPosition(newOffsetX, newOffsetY, newWidth, newHeight);
3526
3527      $.fancybox.animate(self.$content, null, newPos, self.speed, "easeOutSine");
3528
3529    }
3530
3531  };
3532
3533  Guestures.prototype.ontap = function () {
3534
3535    var self = this;
3536
3537    var instance = self.instance;
3538    var current = instance.current;
3539
3540    var x = self.endPoints[0].x;
3541    var y = self.endPoints[0].y;
3542
3543    x = x - self.$wrap.offset().left;
3544    y = y - self.$wrap.offset().top;
3545
3546    // Stop slideshow
3547    if (instance.SlideShow && instance.SlideShow.isActive) {
3548      instance.SlideShow.stop();
3549    }
3550
3551    if (!$.fancybox.isTouch) {
3552
3553      if (current.opts.closeClickOutside && self.$target.is('.fancybox-slide')) {
3554        instance.close(self.startEvent);
3555
3556        return;
3557      }
3558
3559      if (current.type == 'image' && current.isMoved) {
3560
3561        if (instance.canPan()) {
3562          instance.scaleToFit();
3563
3564        } else if (instance.isScaledDown()) {
3565          instance.scaleToActual(x, y);
3566
3567        } else if (instance.group.length < 2) {
3568          instance.close(self.startEvent);
3569        }
3570
3571      }
3572
3573      return;
3574    }
3575
3576
3577    // Double tap
3578    if (self.tapped) {
3579
3580      clearTimeout(self.tapped);
3581
3582      self.tapped = null;
3583
3584      if (Math.abs(x - self.x) > 50 || Math.abs(y - self.y) > 50 || !current.isMoved) {
3585        return this;
3586      }
3587
3588      if (current.type == 'image' && (current.isLoaded || current.$ghost)) {
3589
3590        if (instance.canPan()) {
3591          instance.scaleToFit();
3592
3593        } else if (instance.isScaledDown()) {
3594          instance.scaleToActual(x, y);
3595
3596        }
3597
3598      }
3599
3600    } else {
3601
3602      // Single tap
3603
3604      self.x = x;
3605      self.y = y;
3606
3607      self.tapped = setTimeout(function () {
3608        self.tapped = null;
3609
3610        instance.toggleControls(true);
3611
3612      }, 300);
3613    }
3614
3615    return this;
3616  };
3617
3618  $(document).on('onActivate.fb', function (e, instance) {
3619
3620    if (instance && !instance.Guestures) {
3621      instance.Guestures = new Guestures(instance);
3622    }
3623
3624  });
3625
3626  $(document).on('beforeClose.fb', function (e, instance) {
3627
3628    if (instance && instance.Guestures) {
3629      instance.Guestures.destroy();
3630    }
3631
3632  });
3633
3634
3635}(window, document, window.jQuery));
3636
3637// ==========================================================================
3638//
3639// SlideShow
3640// Enables slideshow functionality
3641//
3642// Example of usage:
3643// $.fancybox.getInstance().slideShow.start()
3644//
3645// ==========================================================================
3646; (function (document, $) {
3647  'use strict';
3648
3649  var SlideShow = function (instance) {
3650
3651    this.instance = instance;
3652
3653    this.init();
3654
3655  };
3656
3657  $.extend(SlideShow.prototype, {
3658    timer: null,
3659    isActive: false,
3660    $button: null,
3661    speed: 3000,
3662
3663    init: function () {
3664      var self = this;
3665
3666      self.$button = $('<button data-fancybox-play class="fancybox-button fancybox-button--play" title="Slideshow (P)"></button>')
3667				.appendTo(self.instance.$refs.buttons);
3668
3669      self.instance.$refs.container.on('click', '[data-fancybox-play]', function () {
3670        self.toggle();
3671      });
3672
3673    },
3674
3675    set: function () {
3676      var self = this;
3677
3678      // Check if reached last element
3679      if (self.instance && self.instance.current && (self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1)) {
3680
3681        self.timer = setTimeout(function () {
3682          self.instance.next();
3683
3684        }, self.instance.current.opts.slideShow.speed || self.speed);
3685
3686      } else {
3687        self.stop();
3688      }
3689    },
3690
3691    clear: function () {
3692      var self = this;
3693
3694      clearTimeout(self.timer);
3695
3696      self.timer = null;
3697    },
3698
3699    start: function () {
3700      var self = this;
3701
3702      self.stop();
3703
3704      if (self.instance && self.instance.current && (self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1)) {
3705
3706        self.instance.$refs.container.on({
3707          'beforeLoad.fb.player': $.proxy(self, "clear"),
3708          'onComplete.fb.player': $.proxy(self, "set"),
3709        });
3710
3711        self.isActive = true;
3712
3713        if (self.instance.current.isComplete) {
3714          self.set();
3715        }
3716
3717        self.instance.$refs.container.trigger('onPlayStart');
3718
3719        self.$button.addClass('fancybox-button--pause');
3720      }
3721
3722    },
3723
3724    stop: function () {
3725      var self = this;
3726
3727      self.clear();
3728
3729      self.instance.$refs.container
3730				.trigger('onPlayEnd')
3731				.off('.player');
3732
3733      self.$button.removeClass('fancybox-button--pause');
3734
3735      self.isActive = false;
3736    },
3737
3738    toggle: function () {
3739      var self = this;
3740
3741      if (self.isActive) {
3742        self.stop();
3743
3744      } else {
3745        self.start();
3746      }
3747    }
3748
3749  });
3750
3751  $(document).on('onInit.fb', function (e, instance) {
3752
3753    if (instance && instance.group.length > 1 && !!instance.opts.slideShow && !instance.SlideShow) {
3754      instance.SlideShow = new SlideShow(instance);
3755    }
3756
3757  });
3758
3759  $(document).on('beforeClose.fb onDeactivate.fb', function (e, instance) {
3760
3761    if (instance && instance.SlideShow) {
3762      instance.SlideShow.stop();
3763    }
3764
3765  });
3766
3767}(document, window.jQuery));
3768
3769// ==========================================================================
3770//
3771// FullScreen
3772// Adds fullscreen functionality
3773//
3774// ==========================================================================
3775; (function (document, $) {
3776  'use strict';
3777
3778  // Collection of methods supported by user browser
3779  var fn = (function () {
3780
3781    var fnMap = [
3782			[
3783				'requestFullscreen',
3784				'exitFullscreen',
3785				'fullscreenElement',
3786				'fullscreenEnabled',
3787				'fullscreenchange',
3788				'fullscreenerror'
3789			],
3790			// new WebKit
3791			[
3792				'webkitRequestFullscreen',
3793				'webkitExitFullscreen',
3794				'webkitFullscreenElement',
3795				'webkitFullscreenEnabled',
3796				'webkitfullscreenchange',
3797				'webkitfullscreenerror'
3798
3799			],
3800			// old WebKit (Safari 5.1)
3801			[
3802				'webkitRequestFullScreen',
3803				'webkitCancelFullScreen',
3804				'webkitCurrentFullScreenElement',
3805				'webkitCancelFullScreen',
3806				'webkitfullscreenchange',
3807				'webkitfullscreenerror'
3808
3809			],
3810			[
3811				'mozRequestFullScreen',
3812				'mozCancelFullScreen',
3813				'mozFullScreenElement',
3814				'mozFullScreenEnabled',
3815				'mozfullscreenchange',
3816				'mozfullscreenerror'
3817			],
3818			[
3819				'msRequestFullscreen',
3820				'msExitFullscreen',
3821				'msFullscreenElement',
3822				'msFullscreenEnabled',
3823				'MSFullscreenChange',
3824				'MSFullscreenError'
3825			]
3826    ];
3827
3828    var val;
3829    var ret = {};
3830    var i, j;
3831
3832    for (i = 0; i < fnMap.length; i++) {
3833      val = fnMap[i];
3834
3835      if (val && val[1] in document) {
3836        for (j = 0; j < val.length; j++) {
3837          ret[fnMap[0][j]] = val[j];
3838        }
3839
3840        return ret;
3841      }
3842    }
3843
3844    return false;
3845  })();
3846
3847  if (!fn) {
3848    return;
3849  }
3850
3851  var FullScreen = {
3852    request: function (elem) {
3853
3854      elem = elem || document.documentElement;
3855
3856      elem[fn.requestFullscreen](elem.ALLOW_KEYBOARD_INPUT);
3857
3858    },
3859    exit: function () {
3860      document[fn.exitFullscreen]();
3861    },
3862    toggle: function (elem) {
3863
3864      if (this.isFullscreen()) {
3865        this.exit();
3866      } else {
3867        this.request(elem);
3868      }
3869
3870    },
3871    isFullscreen: function () {
3872      return Boolean(document[fn.fullscreenElement]);
3873    },
3874    enabled: function () {
3875      return Boolean(document[fn.fullscreenEnabled]);
3876    }
3877  };
3878
3879  $(document).on({
3880    'onInit.fb': function (e, instance) {
3881      var $container;
3882
3883      if (instance && !!instance.opts.fullScreen && !instance.FullScreen) {
3884        $container = instance.$refs.container;
3885
3886        instance.$refs.button_fs = $('<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="Full screen (F)"></button>')
3887					.appendTo(instance.$refs.buttons);
3888
3889        $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function (e) {
3890
3891          e.stopPropagation();
3892          e.preventDefault();
3893
3894          FullScreen.toggle($container[0]);
3895
3896        });
3897
3898        if (instance.opts.fullScreen.requestOnStart === true) {
3899          FullScreen.request($container[0]);
3900        }
3901
3902      }
3903
3904    }, 'beforeMove.fb': function (e, instance) {
3905
3906      if (instance && instance.$refs.button_fs) {
3907        instance.$refs.button_fs.toggle(!!instance.current.opts.fullScreen);
3908      }
3909
3910    }, 'beforeClose.fb': function () {
3911      FullScreen.exit();
3912    }
3913  });
3914
3915  $(document).on(fn.fullscreenchange, function () {
3916    var instance = $.fancybox.getInstance();
3917    var $what = instance ? instance.current.$placeholder : null;
3918
3919    if ($what) {
3920
3921      // If image is zooming, then this will force it to stop and reposition properly
3922      $what.css('transition', 'none');
3923
3924      instance.isAnimating = false;
3925
3926      instance.update(true, true, 0);
3927    }
3928
3929  });
3930
3931}(document, window.jQuery));
3932
3933// ==========================================================================
3934//
3935// Thumbs
3936// Displays thumbnails in a grid
3937//
3938// ==========================================================================
3939; (function (document, $) {
3940  'use strict';
3941
3942  var FancyThumbs = function (instance) {
3943
3944    this.instance = instance;
3945
3946    this.init();
3947
3948  };
3949
3950  $.extend(FancyThumbs.prototype, {
3951
3952    $button: null,
3953    $grid: null,
3954    $list: null,
3955    isVisible: false,
3956
3957    init: function () {
3958      var self = this;
3959
3960      self.$button = $('<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="Thumbnails (G)"></button>')
3961				.appendTo(this.instance.$refs.buttons)
3962				.on('touchend click', function (e) {
3963				  e.stopPropagation();
3964				  e.preventDefault();
3965
3966				  self.toggle();
3967				});
3968
3969    },
3970
3971    create: function () {
3972      var instance = this.instance,
3973				list,
3974				src;
3975
3976      this.$grid = $('<div class="fancybox-thumbs"></div>').appendTo(instance.$refs.container);
3977
3978      list = '<ul>';
3979
3980      $.each(instance.group, function (i, item) {
3981
3982        src = item.opts.thumb || (item.opts.$thumb ? item.opts.$thumb.attr('src') : null);
3983
3984        if (!src && item.type === 'image') {
3985          src = item.src;
3986        }
3987
3988        if (src && src.length) {
3989          list += '<li data-index="' + i + '"  tabindex="0" class="fancybox-thumbs-loading"><img data-src="' + src + '" /></li>';
3990        }
3991
3992      });
3993
3994      list += '</ul>';
3995
3996      this.$list = $(list).appendTo(this.$grid).on('click touchstart', 'li', function () {
3997
3998        instance.jumpTo($(this).data('index'));
3999
4000      });
4001
4002      this.$list.find('img').hide().one('load', function () {
4003
4004        var $parent = $(this).parent().removeClass('fancybox-thumbs-loading'),
4005					thumbWidth = $parent.outerWidth(),
4006					thumbHeight = $parent.outerHeight(),
4007					width,
4008					height,
4009					widthRatio,
4010					heightRatio;
4011
4012        width = this.naturalWidth || this.width;
4013        height = this.naturalHeight || this.height;
4014
4015        //Calculate thumbnail width/height and center it
4016
4017        widthRatio = width / thumbWidth;
4018        heightRatio = height / thumbHeight;
4019
4020        if (widthRatio >= 1 && heightRatio >= 1) {
4021          if (widthRatio > heightRatio) {
4022            width = width / heightRatio;
4023            height = thumbHeight;
4024
4025          } else {
4026            width = thumbWidth;
4027            height = height / widthRatio;
4028          }
4029        }
4030
4031        $(this).css({
4032          width: Math.floor(width),
4033          height: Math.floor(height),
4034          'margin-top': Math.min(0, Math.floor(thumbHeight * 0.3 - he
4034ight * 0.3)),
4035          'margin-left': Math.min(0, Math.floor(thumbWidth * 0.5 - width * 0.5))
4036        }).show();
4037
4038      })
4039			.each(function () {
4040			  this.src = $(this).data('src');
4041			});
4042
4043    },
4044
4045    focus: function () {
4046
4047      if (this.instance.current) {
4048        this.$list
4049					.children()
4050					.removeClass('fancybox-thumbs-active')
4051					.filter('[data-index="' + this.instance.current.index + '"]')
4052					.addClass('fancybox-thumbs-active')
4053					.focus();
4054      }
4055
4056    },
4057
4058    close: function () {
4059
4060      this.$grid.hide();
4061
4062    },
4063
4064    update: function () {
4065
4066      this.instance.$refs.container.toggleClass('fancybox-container--thumbs', this.isVisible);
4067
4068      if (this.isVisible) {
4069
4070        if (!this.$grid) {
4071          this.create();
4072        }
4073
4074        this.$grid.show();
4075
4076        this.focus();
4077
4078      } else if (this.$grid) {
4079        this.$grid.hide();
4080      }
4081
4082      this.instance.update();
4083
4084    },
4085
4086    hide: function () {
4087
4088      this.isVisible = false;
4089
4090      this.update();
4091
4092    },
4093
4094    show: function () {
4095
4096      this.isVisible = true;
4097
4098      this.update();
4099
4100    },
4101
4102    toggle: function () {
4103
4104      if (this.isVisible) {
4105        this.hide();
4106
4107      } else {
4108        this.show();
4109      }
4110    }
4111
4112  });
4113
4114  $(document).on('onInit.fb', function (e, instance) {
4115    var first = instance.group[0],
4116			second = instance.group[1];
4117
4118    if (!!instance.opts.thumbs && !instance.Thumbs && instance.group.length > 1 && (
4119		    		(first.type == 'image' || first.opts.thumb || first.opts.$thumb) &&
4120		    		(second.type == 'image' || second.opts.thumb || second.opts.$thumb)
4121			 	)
4122		   ) {
4123
4124      instance.Thumbs = new FancyThumbs(instance);
4125    }
4126
4127  });
4128
4129  $(document).on('beforeMove.fb', function (e, instance, item) {
4130    var self = instance && instance.Thumbs;
4131
4132    if (!self) {
4133      return;
4134    }
4135
4136    if (item.modal) {
4137
4138      self.$button.hide();
4139
4140      self.hide();
4141
4142    } else {
4143
4144      if (instance.opts.thumbs.showOnStart === true && instance.firstRun) {
4145        self.show();
4146
4147      }
4148
4149      self.$button.show();
4150
4151      if (self.isVisible) {
4152        self.focus();
4153      }
4154
4155    }
4156
4157  });
4158
4159  $(document).on('beforeClose.fb', function (e, instance) {
4160
4161    if (instance && instance.Thumbs) {
4162      if (instance.Thumbs.isVisible && instance.opts.thumbs.hideOnClosing !== false) {
4163        instance.Thumbs.close();
4164      }
4165
4166      instance.Thumbs = null;
4167    }
4168
4169  });
4170
4171}(document, window.jQuery));
4172
4173// ==========================================================================
4174//
4175// Hash
4176// Enables linking to each modal
4177//
4178// ==========================================================================
4179; (function (document, window, $) {
4180  'use strict';
4181
4182  // Simple $.escapeSelector polyfill (for jQuery prior v3)
4183  if (!$.escapeSelector) {
4184    $.escapeSelector = function (sel) {
4185      var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
4186      var fcssescape = function (ch, asCodePoint) {
4187        if (asCodePoint) {
4188          // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
4189          if (ch === "\0") {
4190            return "\uFFFD";
4191          }
4192
4193          // Control characters and (dependent upon position) numbers get escaped as code points
4194          return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " ";
4195        }
4196
4197        // Other potentially-special ASCII characters get backslash-escaped
4198        return "\\" + ch;
4199      };
4200
4201      return (sel + "").replace(rcssescape, fcssescape);
4202    };
4203  }
4204
4205  // Variable containing last hash value set by fancyBox
4206  // It will be used to determine if fancyBox needs to close after hash change is detected
4207  var currentHash = null;
4208
4209  // Get info about gallery name and current index from url
4210  function parseUrl() {
4211    var hash = window.location.hash.substr(1);
4212    var rez = hash.split('-');
4213    var index = rez.length > 1 && /^\+?\d+$/.test(rez[rez.length - 1]) ? parseInt(rez.pop(-1), 10) || 1 : 1;
4214    var gallery = rez.join('-');
4215
4216    // Index is starting from 1
4217    if (index < 1) {
4218      index = 1;
4219    }
4220
4221    return {
4222      hash: hash,
4223      index: index,
4224      gallery: gallery
4225    };
4226  }
4227
4228  // Trigger click evnt on links to open new fancyBox instance
4229  function triggerFromUrl(url) {
4230    var $el;
4231
4232    if (url.gallery !== '') {
4233
4234      // If we can find element matching 'data-fancybox' atribute, then trigger click event for that ..
4235      $el = $("[data-fancybox='" + $.escapeSelector(url.gallery) + "']").eq(url.index - 1);
4236
4237      if ($el.length) {
4238        $el.trigger('click');
4239
4240      } else {
4241
4242        // .. if not, try finding element by ID
4243        $("#" + $.escapeSelector(url.gallery) + "").trigger('click');
4244
4245      }
4246
4247    }
4248  }
4249
4250  // Get gallery name from current instance
4251  function getGallery(instance) {
4252    var opts;
4253
4254    if (!instance) {
4255      return false;
4256    }
4257
4258    opts = instance.current ? instance.current.opts : instance.opts;
4259
4260    return opts.$orig ? opts.$orig.data('fancybox') : (opts.hash || '');
4261  }
4262
4263  // Star when DOM becomes ready
4264  $(function () {
4265
4266    // Small delay is used to allow other scripts to process "dom ready" event
4267    setTimeout(function () {
4268
4269      // Check if this module is not disabled
4270      if ($.fancybox.defaults.hash === false) {
4271        return;
4272      }
4273
4274      // Check if need to close after url has changed
4275      $(window).on('hashchange.fb', function () {
4276        var url = parseUrl();
4277
4278        if ($.fancybox.getInstance()) {
4279          if (currentHash && currentHash !== url.gallery + '-' + url.index) {
4280            currentHash = null;
4281
4282            $.fancybox.close();
4283          }
4284
4285        } else if (url.gallery !== '') {
4286          triggerFromUrl(url);
4287        }
4288
4289      });
4290
4291      // Update hash when opening/closing fancyBox
4292      $(document).on({
4293        'onInit.fb': function (e, instance) {
4294          var url = parseUrl();
4295          var gallery = getGallery(instance);
4296
4297          // Make sure gallery start index matches index from hash
4298          if (gallery && url.gallery && gallery == url.gallery) {
4299            instance.currIndex = url.index - 1;
4300          }
4301
4302        }, 'beforeMove.fb': function (e, instance, current) {
4303          var gallery = getGallery(instance);
4304
4305          // Update window hash
4306          if (gallery && gallery !== '') {
4307
4308            if (window.location.hash.indexOf(gallery) < 0) {
4309              instance.opts.origHash = window.location.hash;
4310            }
4311
4312            currentHash = gallery + (instance.group.length > 1 ? '-' + (current.index + 1) : '');
4313
4314            if ("pushState" in history) {
4315              history.pushState('', document.title, window.location.pathname + window.location.search + '#' + currentHash);
4316
4317            } else {
4318              window.location.hash = currentHash;
4319            }
4320
4321          }
4322
4323        }, 'beforeClose.fb': function (e, instance, current) {
4324          var gallery = getGallery(instance);
4325          var origHash = instance && instance.opts.origHash ? instance.opts.origHash : '';
4326
4327          // Remove hash from location bar
4328          if (gallery && gallery !== '') {
4329            if ("pushState" in history) {
4330              history.pushState('', document.title, window.location.pathname + window.location.search + origHash);
4331
4332            } else {
4333              window.location.hash = origHash;
4334            }
4335          }
4336
4337          currentHash = null;
4338        }
4339      });
4340
4341      // Check current hash and trigger click event on matching element to start fancyBox, if needed
4342      triggerFromUrl(parseUrl());
4343
4344    }, 50);
4345  });
4346
4347
4348}(document, window, window.jQuery));
4349
4350
4351function fn_SetGallery() {
4352  try {
4353    $('.sitegaljust').justifiedGallery({
4354      lastRow: 'nojustify',
4355      rowHeight: 120,
4356      rel: 'gallery2',
4357      margins: 1
4358    }).on('jg.complete', function () {
4359      fn_InitViewGallery();
4360    });
4361  }
4362  catch (e) { }
4363}
4364
4365
4366
4367
4368
4369function fn_InitViewGallery() {
4370  try {
4371    //.fancybox-thumb
4372    console.log('fancybox');
4373    $("[data-fancybox]").fancybox({
4374      hash: false, // Hash value
4375    });
4376
4377  } catch (e) { }
4378}
4379
4380function fn_LoadGal(ObjID) {
4381  var ID = $(ObjID).attr('id').split('-')[1];
4382  var RefTable = $(ObjID).attr('id').split('-')[2];
4383  var RefID = $(ObjID).attr('id').split('-')[3];
4384  var MainID = $(ObjID).attr('id');
4385
4386  $(".sitegallery li").removeClass('active');
4387  $.get("/Handlers/Misc.ashx", { "A": 20, "ID": ID }, function (data) {
4388    $("#sitegal-" + RefTable + "-" + RefID).html(data);
4389    fn_SetGallery();
4390    $("#" + MainID).parent().addClass('active');
4391  });
4392}
4393
4394
4395$(document).ready(function () {
4396  fn_SetGallery();
4397});
4398
4399
4400function fn_LoadDefaultPageGal(ID, ObjID){
4401var IfExists = $("#df-fb-loader").attr('id') || '';
4402if (IfExists.length > 0){
4403console.log('not allowed');
4404return false;
4405}
4406$(ObjID).append('<div id="df-fb-loader" class="fancybox-loading"></div>');
4407$.get("/Handlers/Misc.ashx", { "A": 22, "ID": ID }, function (data) {
4408    $("#DivGalleryHolder").html(data);
4409    fn_SetGallery();
4410    $("#df-fb-loader").remove();
4411    $("#DivGalleryHolder a:first-child").click();
4412    //$("#DivGalleryHolder").css('display', 'none');
4413    $("#DivGalleryHolder").css('visibility', 'hidden');
4414  });
4415
4416}
4417

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