PageSourceSearch

https://memoriaparatodos.pt/scripts/fancybox/source/jquery.fancybox.js?t=639260251382511571

js memoriaparatodos.pt collected 2026-09-26 12:12:54 UTC 129,247 bytes, 4,349 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: '<span data-fancybox-close class="fancybox-close-small"></span>',
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
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 input
267s 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.spee
3416dX = 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            if (document.fullscreen) { documentfn.exitFullscreen; }
3862        },
3863        toggle: function (elem) {
3864
3865            if (this.isFullscreen()) {
3866                this.exit();
3867            } else {
3868                this.request(elem);
3869            }
3870
3871        },
3872        isFullscreen: function () {
3873            return Boolean(document[fn.fullscreenElement]);
3874        },
3875        enabled: function () {
3876            return Boolean(document[fn.fullscreenEnabled]);
3877        }
3878    };
3879
3880    $(document).on({
3881        'onInit.fb': function (e, instance) {
3882            var $container;
3883
3884            if (instance && !!instance.opts.fullScreen && !instance.FullScreen) {
3885                $container = instance.$refs.container;
3886
3887                instance.$refs.button_fs = $('<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="Full screen (F)"></button>')
3888                    .appendTo(instance.$refs.buttons);
3889
3890                $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function (e) {
3891
3892                    e.stopPropagation();
3893                    e.preventDefault();
3894
3895                    FullScreen.toggle($container[0]);
3896
3897                });
3898
3899                if (instance.opts.fullScreen.requestOnStart === true) {
3900                    FullScreen.request($container[0]);
3901                }
3902
3903            }
3904
3905        }, 'beforeMove.fb': function (e, instance) {
3906
3907            if (instance && instance.$refs.button_fs) {
3908                instance.$refs.button_fs.toggle(!!instance.current.opts.fullScreen);
3909            }
3910
3911        }, 'beforeClose.fb': function () {
3912            FullScreen.exit();
3913        }
3914    });
3915
3916    $(document).on(fn.fullscreenchange, function () {
3917        var instance = $.fancybox.getInstance();
3918        var $what = instance ? instance.current.$placeholder : null;
3919
3920        if ($what) {
3921
3922            // If image is zooming, then this will force it to stop and reposition properly
3923            $what.css('transition', 'none');
3924
3925            instance.isAnimating = false;
3926
3927            instance.update(true, true, 0);
3928        }
3929
3930    });
3931
3932}(document, window.jQuery));
3933
3934// ==========================================================================
3935//
3936// Thumbs
3937// Displays thumbnails in a grid
3938//
3939// ==========================================================================
3940; (function (document, $) {
3941    'use strict';
3942
3943    var FancyThumbs = function (instance) {
3944
3945        this.instance = instance;
3946
3947        this.init();
3948
3949    };
3950
3951    $.extend(FancyThumbs.prototype, {
3952
3953        $button: null,
3954        $grid: null,
3955        $list: null,
3956        isVisible: false,
3957
3958        init: function () {
3959            var self = this;
3960
3961            self.$button = $('<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="Thumbnails (G)"></button>')
3962                .appendTo(this.instance.$refs.buttons)
3963                .on('touchend click', function (e) {
3964                    e.stopPropagation();
3965                    e.preventDefault();
3966
3967                    self.toggle();
3968                });
3969
3970        },
3971
3972        create: function () {
3973            var instance = this.instance,
3974                list,
3975                src;
3976
3977            this.$grid = $('<div class="fancybox-thumbs"></div>').appendTo(instance.$refs.container);
3978
3979            list = '<ul>';
3980
3981            $.each(instance.group, function (i, item) {
3982
3983                src = item.opts.thumb || (item.opts.$thumb ? item.opts.$thumb.attr('src') : null);
3984
3985                if (!src && item.type === 'image') {
3986                    src = item.src;
3987                }
3988
3989                if (src && src.length) {
3990                    list += '<li data-index="' + i + '"  tabindex="0" class="fancybox-thumbs-loading"><img data-src="' + src + '" /></li>';
3991                }
3992
3993            });
3994
3995            list += '</ul>';
3996
3997            this.$list = $(list).appendTo(this.$grid).on('click touchstart', 'li', function () {
3998
3999                instance.jumpTo($(this).data('index'));
4000
4001            });
4002
4003            this.$list.find('img').hide().one('load', function () {
4004
4005                var $parent = $(this).parent().removeClass('fancybox-thumbs-loading'),
4006                    thumbWidth = $parent.outerWidth(),
4007                    thumbHeight = $parent.outerHeight(),
4008                    width,
4009                    height,
4010                    widthRatio,
4011                    heightRatio;
4012
4013                width = this.naturalWidth || this.width;
4014                height = this.naturalHeight || this.height;
4015
4016                //Calculate thumbnail width/height and center it
4017
4018                widthRatio = width / thumbWidth;
4019                heightRatio = height / thumbHeight;
4020
4021                if (widthRatio >= 1 && heightRatio >= 1) {
4022                    if (widthRatio > heightRatio) {
4023                        width = width / heightRatio;
4024                        height = thumbHeight;
4025
4026                    } else {
4027                        width = thumbWidth;
4028                        height = height / widthRatio;
4029                    }
4030                }
4031
4032                $(this).css({
4033                    width: Math.floor(width),
4034                    height: Math.floor(height),
4035                    'margin-top': Math.min(0, Math.floor(thumbHeight * 0.3 - he
4035ight * 0.3)),
4036                    'margin-left': Math.min(0, Math.floor(thumbWidth * 0.5 - width * 0.5))
4037                }).show();
4038
4039            })
4040                .each(function () {
4041                    this.src = $(this).data('src');
4042                });
4043
4044        },
4045
4046        focus: function () {
4047
4048            if (this.instance.current) {
4049                this.$list
4050                    .children()
4051                    .removeClass('fancybox-thumbs-active')
4052                    .filter('[data-index="' + this.instance.current.index + '"]')
4053                    .addClass('fancybox-thumbs-active')
4054                    .focus();
4055            }
4056
4057        },
4058
4059        close: function () {
4060
4061            this.$grid.hide();
4062
4063        },
4064
4065        update: function () {
4066
4067            this.instance.$refs.container.toggleClass('fancybox-container--thumbs', this.isVisible);
4068
4069            if (this.isVisible) {
4070
4071                if (!this.$grid) {
4072                    this.create();
4073                }
4074
4075                this.$grid.show();
4076
4077                this.focus();
4078
4079            } else if (this.$grid) {
4080                this.$grid.hide();
4081            }
4082
4083            this.instance.update();
4084
4085        },
4086
4087        hide: function () {
4088
4089            this.isVisible = false;
4090
4091            this.update();
4092
4093        },
4094
4095        show: function () {
4096
4097            this.isVisible = true;
4098
4099            this.update();
4100
4101        },
4102
4103        toggle: function () {
4104
4105            if (this.isVisible) {
4106                this.hide();
4107
4108            } else {
4109                this.show();
4110            }
4111        }
4112
4113    });
4114
4115    $(document).on('onInit.fb', function (e, instance) {
4116        var first = instance.group[0],
4117            second = instance.group[1];
4118
4119        if (!!instance.opts.thumbs && !instance.Thumbs && instance.group.length > 1 && (
4120            (first.type == 'image' || first.opts.thumb || first.opts.$thumb) &&
4121            (second.type == 'image' || second.opts.thumb || second.opts.$thumb)
4122        )
4123        ) {
4124
4125            instance.Thumbs = new FancyThumbs(instance);
4126        }
4127
4128    });
4129
4130    $(document).on('beforeMove.fb', function (e, instance, item) {
4131        var self = instance && instance.Thumbs;
4132
4133        if (!self) {
4134            return;
4135        }
4136
4137        if (item.modal) {
4138
4139            self.$button.hide();
4140
4141            self.hide();
4142
4143        } else {
4144
4145            if (instance.opts.thumbs.showOnStart === true && instance.firstRun) {
4146                self.show();
4147
4148            }
4149
4150            self.$button.show();
4151
4152            if (self.isVisible) {
4153                self.focus();
4154            }
4155
4156        }
4157
4158    });
4159
4160    $(document).on('beforeClose.fb', function (e, instance) {
4161
4162        if (instance && instance.Thumbs) {
4163            if (instance.Thumbs.isVisible && instance.opts.thumbs.hideOnClosing !== false) {
4164                instance.Thumbs.close();
4165            }
4166
4167            instance.Thumbs = null;
4168        }
4169
4170    });
4171
4172}(document, window.jQuery));
4173
4174// ==========================================================================
4175//
4176// Hash
4177// Enables linking to each modal
4178//
4179// ==========================================================================
4180; (function (document, window, $) {
4181    'use strict';
4182
4183    // Simple $.escapeSelector polyfill (for jQuery prior v3)
4184    if (!$.escapeSelector) {
4185        $.escapeSelector = function (sel) {
4186            var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
4187            var fcssescape = function (ch, asCodePoint) {
4188                if (asCodePoint) {
4189                    // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
4190                    if (ch === "\0") {
4191                        return "\uFFFD";
4192                    }
4193
4194                    // Control characters and (dependent upon position) numbers get escaped as code points
4195                    return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " ";
4196                }
4197
4198                // Other potentially-special ASCII characters get backslash-escaped
4199                return "\\" + ch;
4200            };
4201
4202            return (sel + "").replace(rcssescape, fcssescape);
4203        };
4204    }
4205
4206    // Variable containing last hash value set by fancyBox
4207    // It will be used to determine if fancyBox needs to close after hash change is detected
4208    var currentHash = null;
4209
4210    // Get info about gallery name and current index from url
4211    function parseUrl() {
4212        var hash = window.location.hash.substr(1);
4213        var rez = hash.split('-');
4214        var index = rez.length > 1 && /^\+?\d+$/.test(rez[rez.length - 1]) ? parseInt(rez.pop(-1), 10) || 1 : 1;
4215        var gallery = rez.join('-');
4216
4217        // Index is starting from 1
4218        if (index < 1) {
4219            index = 1;
4220        }
4221
4222        return {
4223            hash: hash,
4224            index: index,
4225            gallery: gallery
4226        };
4227    }
4228
4229    // Trigger click evnt on links to open new fancyBox instance
4230    function triggerFromUrl(url) {
4231        var $el;
4232
4233        if (url.gallery !== '') {
4234
4235            // If we can find element matching 'data-fancybox' atribute, then trigger click event for that ..
4236            $el = $("[data-fancybox='" + $.escapeSelector(url.gallery) + "']").eq(url.index - 1);
4237
4238            if ($el.length) {
4239                $el.trigger('click');
4240
4241            } else {
4242
4243                // .. if not, try finding element by ID
4244                $("#" + $.escapeSelector(url.gallery) + "").trigger('click');
4245
4246            }
4247
4248        }
4249    }
4250
4251    // Get gallery name from current instance
4252    function getGallery(instance) {
4253        var opts;
4254
4255        if (!instance) {
4256            return false;
4257        }
4258
4259        opts = instance.current ? instance.current.opts : instance.opts;
4260
4261        return opts.$orig ? opts.$orig.data('fancybox') : (opts.hash || '');
4262    }
4263
4264    // Star when DOM becomes ready
4265    $(function () {
4266
4267        // Small delay is used to allow other scripts to process "dom ready" event
4268        setTimeout(function () {
4269
4270            // Check if this module is not disabled
4271            if ($.fancybox.defaults.hash === false) {
4272                return;
4273            }
4274
4275            // Check if need to close after url has changed
4276            $(window).on('hashchange.fb', function () {
4277                var url = parseUrl();
4278
4279                if ($.fancybox.getInstance()) {
4280                    if (currentHash && currentHash !== url.gallery + '-' + url.index) {
4281                        currentHash = null;
4282
4283                        $.fancybox.close();
4284                    }
4285
4286                } else if (url.gallery !== '') {
4287                    triggerFromUrl(url);
4288                }
4289
4290            });
4291
4292            // Update hash when opening/closing fancyBox
4293            $(document).on({
4294                'onInit.fb': function (e, instance) {
4295                    var url = parseUrl();
4296                    var gallery = getGallery(instance);
4297
4298                    // Make sure gallery start index matches index from hash
4299                    if (gallery && url.gallery && gallery == url.gallery) {
4300                        instance.currIndex = url.index - 1;
4301                    }
4302
4303                }, 'beforeMove.fb': function (e, instance, current) {
4304                    var gallery = getGallery(instance);
4305
4306                    // Update window hash
4307                    if (gallery && gallery !== '') {
4308
4309                        if (window.location.hash.indexOf(gallery) < 0) {
4310                            instance.opts.origHash = window.location.hash;
4311                        }
4312
4313                        currentHash = gallery + (instance.group.length > 1 ? '-' + (current.index + 1) : '');
4314
4315                        if ("pushState" in history) {
4316                            history.pushState('', document.title, window.location.pathname + window.location.search + '#' + currentHash);
4317
4318                        } else {
4319                            window.location.hash = currentHash;
4320                        }
4321
4322                    }
4323
4324                }, 'beforeClose.fb': function (e, instance, current) {
4325                    var gallery = getGallery(instance);
4326                    var origHash = instance && instance.opts.origHash ? instance.opts.origHash : '';
4327
4328                    // Remove hash from location bar
4329                    if (gallery && gallery !== '') {
4330                        if ("pushState" in history) {
4331                            history.pushState('', document.title, window.location.pathname + window.location.search + origHash);
4332
4333                        } else {
4334                            window.location.hash = origHash;
4335                        }
4336                    }
4337
4338                    currentHash = null;
4339                }
4340            });
4341
4342            // Check current hash and trigger click event on matching element to start fancyBox, if needed
4343            triggerFromUrl(parseUrl());
4344
4345        }, 50);
4346    });
4347
4348
4349}(document, window, window.jQuery));

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