PageSourceSearch

https://livetitarmen.se/content/modules/fancybox/js/fancybox.min.js

js livetitarmen.se collected 2026-10-08 05:20:15 UTC 148,834 bytes, 5,095 lines download raw bytes

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

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