PageSourceSearch

https://bdmk.hu/templ/js/jQuery-plugins/fancybox/core.js?ver=1606311698

js bdmk.hu collected 2026-10-02 12:37:04 UTC 92,148 bytes, 2,964 lines download raw bytes

1;(function (window, document, $, undefined) {
2    'use strict';
3
4    // If there's no jQuery, fancyBox can't work
5    // =========================================
6
7    if ( !$ ) {
8        return;
9    }
10
11    // Check if fancyBox is already initialized
12    // ========================================
13
14    if ( $.fn.fancybox ) {
15
16        $.error('fancyBox already initialized');
17
18        return;
19    }
20
21    // Private default settings
22    // ========================
23
24    var defaults = {
25
26        // Enable infinite gallery navigation
27        loop : false,
28
29        // Space around image, ignored if zoomed-in or viewport smaller than 800px
30        margin : [44, 0],
31
32        // Horizontal space between slides
33        gutter : 50,
34
35        // Enable keyboard navigation
36        keyboard : true,
37
38        // Should display navigation arrows at the screen edges
39        arrows : true,
40
41        // Should display infobar (counter and arrows at the top)
42        infobar : false,
43
44        // Should display toolbar (buttons at the top)
45        toolbar : true,
46
47        // What buttons should appear in the top right corner.
48        // Buttons will be created using templates from `btnTpl` option
49        // and they will be placed into toolbar (class="fancybox-toolbar"` element)
50        buttons : [
51            'slideShow',
52            'fullScreen',
53            'thumbs',
54            'close'
55        ],
56
57        // Detect "idle" time in seconds
58        idleTime : 4,
59
60        // Should display buttons at top right corner of the content
61        // If 'auto' - they will be created for content having type 'html', 'inline' or 'ajax'
62        // Use template from `btnTpl.smallBtn` for customization
63        smallBtn : 'auto',
64
65        // Disable right-click and use simple image protection for images
66        protect : false,
67
68        // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc
69        modal : false,
70
71        image : {
72
73            // Wait for images to load before displaying
74            // Requires predefined image dimensions
75            // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found
76            preload : "auto",
77
78        },
79
80        ajax : {
81
82            // Object containing settings for ajax request
83            settings : {
84
85                // This helps to indicate that request comes from the modal
86                // Feel free to change naming
87                data : {
88                    fancybox : true
89                }
90            }
91
92        },
93
94        iframe : {
95
96            // Iframe template
97            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>',
98
99            // Preload iframe before displaying it
100            // This allows to calculate iframe content width and height
101            // (note: Due to "Same Origin Policy", you can't get cross domain data).
102            preload : true,
103
104            // Custom CSS styling for iframe wrapping element
105            // You can use this to set custom iframe dimensions
106            css : {},
107
108            // Iframe tag attributes
109            attr : {
110                scrolling : 'auto'
111            }
112
113        },
114
115        // Open/close animation type
116        // Possible values:
117        //   false            - disable
118        //   "zoom"           - zoom images from/to thumbnail
119        //   "fade"
120        //   "zoom-in-out"
121        //
122        animationEffect : "zoom",
123
124        // Duration in ms for open/close animation
125        animationDuration : 366,
126
127        // Should image change opacity while zooming
128        // If opacity is 'auto', then opacity will be changed if image and thumbnail have different aspect ratios
129        zoomOpacity : 'auto',
130
131        // Transition effect between slides
132        //
133        // Possible values:
134        //   false            - disable
135        //   "fade'
136        //   "slide'
137        //   "circular'
138        //   "tube'
139        //   "zoom-in-out'
140        //   "rotate'
141        //
142        transitionEffect : "fade",
143
144        // Duration in ms for transition animation
145        transitionDuration : 366,
146
147        // Custom CSS class for slide element
148        slideClass : '',
149
150        // Custom CSS class for layout
151        baseClass : '',
152
153        // Base template for layout
154        baseTpl	:
155            '<div class="fancybox-container" role="dialog" tabindex="-1">' +
156                '<div class="fancybox-bg"></div>' +
157                '<div class="fancybox-inner">' +
158                    '<div class="fancybox-infobar">' +
159                        '<button data-fancybox-prev title="{{PREV}}" class="fancybox-button fancybox-button--left"></button>' +
160                        '<div class="fancybox-infobar__body">' +
161                            '<span data-fancybox-index></span>&nbsp;/&nbsp;<span data-fancybox-count></span>' +
162                        '</div>' +
163                        '<button data-fancybox-next title="{{NEXT}}" class="fancybox-button fancybox-button--right"></button>' +
164                    '</div>' +
165                    '<div class="fancybox-toolbar">' +
166                        '{{BUTTONS}}' +
167                    '</div>' +
168                    '<div class="fancybox-navigation">' +
169                        '<button data-fancybox-prev title="{{PREV}}" class="fancybox-arrow fancybox-arrow--left" />' +
170                        '<button data-fancybox-next title="{{NEXT}}" class="fancybox-arrow fancybox-arrow--right" />' +
171                    '</div>' +
172                    '<div class="fancybox-stage"></div>' +
173                    '<div class="fancybox-caption-wrap">' +
174                        '<div class="fancybox-caption"></div>' +
175                    '</div>' +
176                '</div>' +
177            '</div>',
178
179        // Loading indicator template
180        spinnerTpl : '<div class="fancybox-loading"></div>',
181
182        // Error message template
183        errorTpl : '<div class="fancybox-error"><p>{{ERROR}}<p></div>',
184
185        btnTpl : {
186            slideShow  : '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}"></button>',
187            fullScreen : '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="{{FULL_SCREEN}}"></button>',
188            thumbs     : '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}"></button>',
189            close      : '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}"></button>',
190
191            // This small close button will be appended to your html/inline/ajax content by default,
192            // if "smallBtn" option is not set to false
193            smallBtn   : '<button data-fancybox-close class="fancybox-close-small" title="{{CLOSE}}"></button>'
194        },
195
196        // Container is injected into this element
197        parentEl : 'body',
198
199
200        // Focus handling
201        // ==============
202
203        // Try to focus on the first focusable element after opening
204        autoFocus : true,
205
206        // Put focus back to active element after closing
207        backFocus : true,
208
209        // Do not let user to focus on element outside modal content
210        trapFocus : true,
211
212
213        // Module specific options
214        // =======================
215
216        fullScreen : {
217            autoStart : false,
218        },
219
220        touch : {
221            vertical : true,  // Allow to drag content vertically
222            momentum : true   // Continue movement after releasing mouse/touch when panning
223        },
224
225        // Hash value when initializing manually,
226        // set `false` to disable hash change
227        hash : null,
228
229        // Customize or add new media types
230        // Example:
231        /*
232        media : {
233            youtube : {
234                params : {
235                    autoplay : 0
236                }
237            }
238        }
239        */
240        media : {},
241
242        slideShow : {
243            autoStart : false,
244            speed     : 4000
245        },
246
247        thumbs : {
248            autoStart   : false,   // Display thumbnails on opening
249            hideOnClose : true     // Hide thumbnail grid when closing animation starts
250        },
251
252        // Callbacks
253        //==========
254
255        // See Documentation/API/Events for more information
256        // Example:
257        /*
258            afterShow: function( instance, current ) {
259                 console.info( 'Clicked element:' );
260                 console.info( current.opts.$orig );
261            }
262        */
263
264        onInit       : $.noop,  // When instance has been initialized
265
266        beforeLoad   : $.noop,  // Before the content of a slide is being loaded
267        afterLoad    : $.noop,  // When the content of a slide is done loading
268
269        beforeShow   : $.noop,  // Before open animation starts
270        afterShow    : $.noop,  // When content is done loading and animating
271
272        beforeClose  : $.noop,  // Before the instance attempts to close. Return false to cancel the close.
273        afterClose   : $.noop,  // After instance has been closed
274
275        onActivate   : $.noop,  // When instance is brought to front
276        onDeactivate : $.noop,  // When other instance has been activated
277
278
279        // Interaction
280        // ===========
281
282        // Use options below to customize taken action when user clicks or double clicks on the fancyBox area,
283        // each option can be string or method that returns value.
284        //
285        // Possible values:
286        //   "close"           - close instance
287        //   "next"            - move to next gallery item
288        //   "nextOrClose"     - move to next gallery item or close if gallery has only one item
289        //   "toggleControls"  - show/hide controls
290        //   "zoom"            - zoom image (if loaded)
291        //   false             - do nothing
292
293        // Clicked on the content
294        clickContent : function( current, event ) {
295            return current.type === 'image' ? 'zoom' : false;
296        },
297
298        // Clicked on the slide
299        clickSlide : 'close',
300
301        // Clicked on the background (backdrop) element
302        clickOutside : 'close',
303
304        // Same as previous two, but for double click
305        dblclickContent : false,
306        dblclickSlide   : false,
307        dblclickOutside : false,
308
309
310        // Custom options when mobile device is detected
311        // =============================================
312
313        mobile : {
314            clickContent : function( current, event ) {
315                return current.type === 'image' ? 'toggleControls' : false;
316            },
317            clickSlide : function( current, event ) {
318                return current.type === 'image' ? 'toggleControls' : "close";
319            },
320            dblclickContent : function( current, event ) {
321                return current.type === 'image' ? 'zoom' : false;
322            },
323            dblclickSlide : function( current, event ) {
324                return current.type === 'image' ? 'zoom' : false;
325            }
326        },
327
328
329        // Internationalization
330        // ============
331
332        lang : 'en',
333        i18n : {
334            'en' : {
335                CLOSE       : 'Close',
336                NEXT        : 'Next',
337                PREV        : 'Previous',
338                ERROR       : 'The requested content cannot be loaded. <br/> Please try again later.',
339                PLAY_START  : 'Start slideshow',
340                PLAY_STOP   : 'Pause slideshow',
341                FULL_SCREEN : 'Full screen',
342                THUMBS      : 'Thumbnails'
343            },
344            'de' : {
345                CLOSE       : 'Schliessen',
346                NEXT        : 'Weiter',
347                PREV        : 'Zurück',
348                ERROR       : 'Die angeforderten Daten konnten nicht geladen werden. <br/>
348 Bitte versuchen Sie es später nochmal.',
349                PLAY_START  : 'Diaschau starten',
350                PLAY_STOP   : 'Diaschau beenden',
351                FULL_SCREEN : 'Vollbild',
352                THUMBS      : 'Vorschaubilder'
353            }
354        }
355
356    };
357
358    // Few useful variables and methods
359    // ================================
360
361    var $W = $(window);
362    var $D = $(document);
363
364    var called = 0;
365
366
367    // Check if an object is a jQuery object and not a native JavaScript object
368    // ========================================================================
369
370    var isQuery = function ( obj ) {
371        return obj && obj.hasOwnProperty && obj instanceof $;
372    };
373
374
375    // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame"
376    // ===============================================================================
377
378    var requestAFrame = (function () {
379        return window.requestAnimationFrame ||
380                window.webkitRequestAnimationFrame ||
381                window.mozRequestAnimationFrame ||
382                window.oRequestAnimationFrame ||
383                // if all else fails, use setTimeout
384                function (callback) {
385                    return window.setTimeout(callback, 1000 / 60);
386                };
387    })();
388
389
390    // Detect the supported transition-end event property name
391    // =======================================================
392
393    var transitionEnd = (function () {
394        var t, el = document.createElement("fakeelement");
395
396        var transitions = {
397            "transition"      : "transitionend",
398            "OTransition"     : "oTransitionEnd",
399            "MozTransition"   : "transitionend",
400            "WebkitTransition": "webkitTransitionEnd"
401        };
402
403        for (t in transitions) {
404            if (el.style[t] !== undefined){
405                return transitions[t];
406            }
407        }
408    })();
409
410
411    // Force redraw on an element.
412    // This helps in cases where the browser doesn't redraw an updated element properly.
413    // =================================================================================
414
415    var forceRedraw = function( $el ) {
416        return ( $el && $el.length && $el[0].offsetHeight );
417    };
418
419
420    // Class definition
421    // ================
422
423    var FancyBox = function( content, opts, index ) {
424        var self = this;
425
426        self.opts  = $.extend( true, { index : index }, defaults, opts || {} );
427
428        // Exclude buttons option from deep merging
429        if ( opts && $.isArray( opts.buttons ) ) {
430            self.opts.buttons = opts.buttons;
431        }
432
433        self.id    = self.opts.id || ++called;
434        self.group = [];
435
436        self.currIndex = parseInt( self.opts.index, 10 ) || 0;
437        self.prevIndex = null;
438
439        self.prevPos = null;
440        self.currPos = 0;
441
442        self.firstRun = null;
443
444        // Create group elements from original item collection
445        self.createGroup( content );
446
447        if ( !self.group.length ) {
448            return;
449        }
450
451        // Save last active element and current scroll position
452        self.$lastFocus = $(document.activeElement).blur();
453
454        // Collection of gallery objects
455        self.slides = {};
456
457        self.init( content );
458
459    };
460
461    $.extend(FancyBox.prototype, {
462
463        // Create DOM structure
464        // ====================
465
466        init : function() {
467            var self = this;
468
469            var testWidth, $container, buttonStr;
470
471            var firstItemOpts = self.group[ self.currIndex ].opts;
472
473            self.scrollTop  = $D.scrollTop();
474            self.scrollLeft = $D.scrollLeft();
475
476
477            // Hide scrollbars
478            // ===============
479
480            if ( !$.fancybox.getInstance() && !$.fancybox.isMobile && $( 'body' ).css('overflow') !== 'hidden' ) {
481                testWidth = $( 'body' ).width();
482
483                $( 'html' ).addClass( 'fancybox-enabled' );
484
485                // Compare body width after applying "overflow: hidden"
486                testWidth = $( 'body' ).width() - testWidth;
487
488                // If width has changed - compensate missing scrollbars by adding right margin
489                if ( testWidth > 1 ) {
490                    $( 'head' ).append( '<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar, .fancybox-enabled body { margin-right: ' + testWidth + 'px; }</style>' );
491                }
492            }
493
494
495            // Build html markup and set references
496            // ====================================
497
498            // Build html code for buttons and insert into main template
499            buttonStr = '';
500
501            $.each( firstItemOpts.buttons, function( index, value ) {
502                buttonStr += ( firstItemOpts.btnTpl[ value ] || '' );
503            });
504
505            // Create markup from base template, it will be initially hidden to
506            // avoid unnecessary work like painting while initializing is not complete
507            $container = $( self.translate( self, firstItemOpts.baseTpl.replace( '\{\{BUTTONS\}\}', buttonStr ) ) )
508                .addClass( 'fancybox-is-hidden' )
509                .attr('id', 'fancybox-container-' + self.id)
510                .addClass( firstItemOpts.baseClass )
511                .data( 'FancyBox', self )
512                .prependTo( firstItemOpts.parentEl );
513
514            // Create object holding references to jQuery wrapped nodes
515            self.$refs = {
516                container : $container
517            };
518
519            [ 'bg', 'inner', 'infobar', 'toolbar', 'stage', 'caption' ].forEach(function(item) {
520                self.$refs[ item ] = $container.find( '.fancybox-' + item );
521            });
522
523            // Check for redundant elements
524            if ( !firstItemOpts.arrows || self.group.length < 2 ) {
525                $container.find('.fancybox-navigation').remove();
526            }
527
528            if ( !firstItemOpts.infobar ) {
529                self.$refs.infobar.remove();
530            }
531
532            if ( !firstItemOpts.toolbar ) {
533                self.$refs.toolbar.remove();
534            }
535
536            self.trigger( 'onInit' );
537
538            // Bring to front and enable events
539            self.activate();
540
541            // Build slides, load and reveal content
542            self.jumpTo( self.currIndex );
543        },
544
545
546        // Simple i18n support - replaces object keys found in template
547        // with corresponding values
548        // ============================================================
549
550        translate : function( obj, str ) {
551            var arr = obj.opts.i18n[ obj.opts.lang ];
552
553            return str.replace(/\{\{(\w+)\}\}/g, function(match, n) {
554                var value = arr[n];
555
556                if ( value === undefined ) {
557                    return match;
558                }
559
560                return value;
561            });
562        },
563
564        // Create array of gally item objects
565        // Check if each object has valid type and content
566        // ===============================================
567
568        createGroup : function ( content ) {
569            var self  = this;
570            var items = $.makeArray( content );
571
572            $.each(items, function( i, item ) {
573                var obj  = {},
574                    opts = {},
575                    data = [],
576                    $item,
577                    type,
578                    src,
579                    srcParts;
580
581                // Step 1 - Make sure we have an object
582                // ====================================
583
584                if ( $.isPlainObject( item ) ) {
585
586                    // We probably have manual usage here, something like
587                    // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] )
588
589                    obj  = item;
590                    opts = item.opts || item;
591
592                } else if ( $.type( item ) === 'object' && $( item ).length ) {
593
594                    // Here we propbably have jQuery collection returned by some selector
595
596                    $item = $( item );
597                    data  = $item.data();
598
599                    opts = 'options' in data ? data.options : {};
600                    opts = $.type( opts ) === 'object' ? opts : {};
601
602                    obj.src  = 'src' in data ? data.src : ( opts.src || $item.attr( 'href' ) );
603
604                    [ 'width', 'height', 'thumb', 'type', 'filter' ].forEach(function(item) {
605                        if ( item in data ) {
606                            opts[ item ] = data[ item ];
607                        }
608                    });
609
610                    if ( 'srcset' in data ) {
611                        opts.image = { srcset : data.srcset };
612                    }
613
614                    opts.$orig = $item;
615
616                    if ( !obj.type && !obj.src ) {
617                        obj.type = 'inline';
618                        obj.src  = item;
619                    }
620
621                } else {
622
623                    // Assume we have a simple html code, for example:
624                    // $.fancybox.open( '<div><h1>Hi!</h1></div>' );
625
626                    obj = {
627                        type : 'html',
628                        src  : item + ''
629                    };
630
631                }
632
633                // Each gallery object has full collection of options
634                obj.opts = $.extend( true, {}, self.opts, opts );
635
636                if ( $.fancybox.isMobile ) {
637                    obj.opts = $.extend( true, {}, obj.opts, obj.opts.mobile );
638                }
639
640
641                // Step 2 - Make sure we have content type, if not - try to guess
642                // ==============================================================
643
644                type = obj.type || obj.opts.type;
645                src  = obj.src || '';
646
647                if ( !type && src ) {
648                    if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) {
649                        type = 'image';
650
651                    } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) {
652                        type = 'pdf';
653
654                    } else if ( src.charAt(0) === '#' ) {
655                        type = 'inline';
656                    }
657                }
658
659                obj.type = type;
660
661
662                // Step 3 - Some adjustments
663                // =========================
664
665                obj.index = self.group.length;
666
667                // Check if $orig and $thumb objects exist
668                if ( obj.opts.$orig && !obj.opts.$orig.length ) {
669                    delete obj.opts.$orig;
670                }
671
672                if ( !obj.opts.$thumb && obj.opts.$orig ) {
673                    obj.opts.$thumb = obj.opts.$orig.find( 'img:first' );
674                }
675
676                if ( obj.opts.$thumb && !obj.opts.$thumb.length ) {
677                    delete obj.opts.$thumb;
678                }
679
680                // Caption is a "special" option, it can be passed as a method
681                if ( $.type( obj.opts.caption ) === 'function' ) {
682                    obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] );
683
684                } else if ( 'caption' in data ) {
685                    obj.opts.caption = data.caption;
686                }
687
688                // Make sure we have caption as a string
689                obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + '';
690
691                // Check if url contains "filter" used to filter the content
692                // Example: "ajax.html #something"
693                if ( type === 'ajax' ) {
694                    srcParts = src.split(/\s+/, 2);
695
696                    if ( srcParts.length > 1 ) {
697                        obj.src = srcParts.shift();
698
699                        obj.opts.filter = srcParts.shift();
700                    }
701                }
702
703                if ( obj.opts.smallBtn == 'auto' ) {
704
705                    if ( $.inArray( type, ['html', 'inline', 'ajax'] ) > -1 ) {
706                        obj.opts.toolbar  = false;
707                        obj.opts.smallBtn = true;
708
709                    } else {
710                        obj.opts.smallBtn = false;
711                    }
712
713                }
714
715                // If the type is "pdf", then simply load file into iframe
716                if ( type === 'pdf' ) {
717                    obj.type = 'iframe';
718
719                    obj.opts.iframe.preload = false;
720                }
721
722                // Hide all buttons and disable interactivity for modal items
723                if ( obj.opts.modal ) {
724
725                    obj.opts = $.extend(true, obj.opts, {
726                        // Remove buttons
727                        infobar : 0,
728                        toolbar : 0,
729
730                        smallBtn : 0,
731
732                        // Disable keyboard navigation
733                        keyboard : 0,
734
735                        // Disable some modules
736                        slideShow  : 0,
737                        fullScreen : 0,
738                        thumbs     : 0,
739                        touch      : 0,
740
741                        // Disable click event handlers
742                        clickContent    : false,
743                        clickSlide      : false,
744                        clickOutside    : false,
745                        dblclickContent : false,
746                        dblclickSlide   : false,
747                        dblclickOutside : false
748                    });
749
750                }
751
752                // Step 4 - Add processed object to group
753                // ======================================
754
755                self.group.push( obj );
756
757            });
758
759        },
760
761
762        // Attach an event handler functions for:
763        //   - navigation buttons
764        //   - browser scrolling, resizing;
765        //   - focusing
766        //   - keyboard
767        //   - detect idle
768        // ======================================
769
770        addEvents : function() {
771            var self = this;
772
773            self.removeEvents();
774
775            // Make navigation elements clickable
776            self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) {
777                e.stopPropagation();
778                e.preventDefault();
779
780                self.close( e );
781
782            }).on( 'click.fb-prev touchend.fb-prev', '[data-fancybox-prev]', function(e) {
783                e.stopPropagation();
784                e.preventDefault();
785
786                self.previous();
787
788            }).on( 'click.fb-next touchend.fb-next', '[data-fancybox-next]', function(e) {
789                e.stopPropagation();
790                e.preventDefault();
791
792                self.next();
793
794            });
795
796
797            // Handle page scrolling and browser resizing
798            $W.on('orientationchange.fb resize.fb', function(e) {
799
800                if ( e && e.originalEvent && e.originalEvent.type === "resize" ) {
801
802                    requestAFrame(function() {
803                        self.update();
804                    });
805
806                } else {
807
808                    self.$refs.stage.hide();
809
810                    setTimeout(function() {
811                        self.$refs.stage.show();
812
813                        self.update();
814                    }, 500);
815
816                }
817
818            });
819
820            // Trap keyboard focus inside of the modal, so the user does not accidentally tab outside of the modal
821            // (a.k.a. "escaping the modal")
822            $D.on('focusin.fb', function(e) {
823                var instance = $.fancybox ? $.fancybox.getInstance() : null;
824
825                if ( instance.isClosing || !instance.current || !instance.current.opts.trapFocus || $( e.target ).hasClass( 'fancybox-container' ) || $( e.target ).is( document ) ) {
826                    return;
827                }
828
829                if ( instance && $( e.target ).css( 'position' ) !== 'fixed' && !instance.$refs.container.has( e.target ).length ) {
830                    e.stopPropagation();
831
832                    instance.focus();
833
834                    // Sometimes page gets scrolled, set it back
835                    $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft );
836                }
837            });
838
839
840            // Enable keyboard navigation
841            $D.on('keydown.fb', function (e) {
842                var current = self.current,
843                    keycode = e.keyCode || e.which;
844
845                if ( !current || !current.opts.keyboard ) {
846                    return;
847                }
848
849                if ( $(e.target).is('input') || $(e.target).is('textarea') ) {
850                    return;
851                }
852
853                // Backspace and Esc keys
854                if ( keycode === 8 || keycode === 27 ) {
855                    e.preventDefault();
856
857                    self.close( e );
858
859                    return;
860                }
861
862                // Left arrow and Up arrow
863                if ( keycode === 37 || keycode === 38 ) {
864                    e.preventDefault();
865
866                    self.previous();
867
868                    return;
869                }
870
871                // Righ arrow and Down arrow
872                if ( keycode === 39 || keycode === 40 ) {
873                    e.preventDefault();
874
875                    self.next();
876
877                    return;
878                }
879
880                self.trigger('afterKeydown', e, keycode);
881            });
882
883
884            // Hide controls after some inactivity period
885            if ( self.group[ self.currIndex ].opts.idleTime ) {
886                self.idleSecondsCounter = 0;
887
888                $D.on('mousemove.fb-idle mouseenter.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle', function() {
889                    self.idleSecondsCounter = 0;
890
891                    if ( self.isIdle ) {
892                        self.showControls();
893                    }
894
895                    self.isIdle = false;
896                });
897
898                self.idleInterval = window.setInterval(function() {
899
900                    self.idleSecondsCounter++;
901
902                    if ( self.idleSecondsCounter >= self.group[ self.currIndex ].opts.idleTime ) {
903                        self.isIdle = true;
904                        self.idleSecondsCounter = 0;
905
906                        self.hideControls();
907                    }
908
909                }, 1000);
910            }
911
912        },
913
914
915        // Remove events added by the core
916        // ===============================
917
918        removeEvents : function () {
919            var self = this;
920
921            $W.off( 'orientationchange.fb resize.fb' );
922            $D.off( 'focusin.fb keydown.fb .fb-idle' );
923
924            this.$refs.container.off( '.fb-close .fb-prev .fb-next' );
925
926            if ( self.idleInterval ) {
927                window.clearInterval( self.idleInterval );
928
929                self.idleInterval = null;
930            }
931        },
932
933
934        // Change to previous gallery item
935        // ===============================
936
937        previous : function( duration ) {
938            return this.jumpTo( this.currPos - 1, duration );
939        },
940
941
942        // Change to next gallery item
943        // ===========================
944
945        next : function( duration ) {
946            return this.jumpTo( this.currPos + 1, duration );
947        },
948
949
950        // Switch to selected gallery item
951        // ===============================
952
953        jumpTo : function ( pos, duration, slide ) {
954            var self = this,
955                firstRun,
956                loop,
957                current,
958                previous,
959                canvasWidth,
960                currentPos,
961                transitionProps;
962
963            var groupLen = self.group.length;
964
965            if ( self.isSliding || self.isClosing || ( self.isAnimating && self.firstRun ) ) {
966                return;
967            }
968
969            pos  = parseInt( pos, 10 );
970            loop = self.current ? self.current.opts.loop : self.opts.loop;
971
972            if ( !loop && ( pos < 0 || pos >= groupLen ) ) {
973                return false;
974            }
975
976            firstRun = self.firstRun = ( self.firstRun === null );
977
978            if ( groupLen < 2 && !firstRun && !!self.isSliding ) {
979                return;
980            }
981
982            previous = self.current;
983
984            self.prevIndex = self.currIndex;
985            self.prevPos   = self.currPos;
986
987            // Create slides
988            current = self.createSlide( pos );
989
990            if ( groupLen > 1 ) {
991                if ( loop || current.index > 0 ) {
992                    self.createSlide( pos - 1 );
993                }
994
995                if ( loop || current.index < groupLen - 1 ) {
996                    self.createSlide( pos + 1 );
997                }
998            }
999
1000            self.current   = current;
1001            self.currIndex = current.index;
1002            self.currPos   = current.pos;
1003
1004            self.trigger( 'beforeShow', firstRun );
1005
1006            self.updateControls();
1007
1008            currentPos = $.fancybox.getTranslate( current.$slide );
1009
1010            current.isMoved        = ( currentPos.left !== 0 || currentPos.top !== 0 ) && !current.$slide.hasClass( 'fancybox-animated' );
1011            current.forcedDuration = undefined;
1012
1013            if ( $.isNumeric( duration ) ) {
1014                current.forcedDuration = duration;
1015            } else {
1016                duration = current.opts[ firstRun ? 'animationDuration' : 'transitionDuration' ];
1017            }
1018
1019            duration = parseInt( duration, 10 );
1020
1021            // Fresh start - reveal container, current slide and start loading content
1022            if ( firstRun ) {
1023
1024                if ( current.opts.animationEffect && duration ) {
1025                    self.$refs.container.css( 'transition-duration', duration + 'ms' );
1026                }
1027
1028                self.$refs.container.removeClass( 'fancybox-is-hidden' );
1029
1030                forceRedraw( self.$refs.container );
1031
1032                self.$refs.container.addClass( 'fancybox-is-open' );
1033
1034                // Make first slide visible (to display loading icon, if needed)
1035                current.$slide.addClass( 'fancybox-slide--current' );
1036
1037                self.loadSlide( current );
1038
1039                self.preload();
1040
1041                return;
1042            }
1043
1044            // Clean up
1045            $.each(self.slides, function( index, slide ) {
1046                $.fancybox.stop( slide.$slide );
1047            });
1048
1049            // Make current that slide is visible even if content is still loading
1050            current.$slide.removeClass( 'fancybox-slide--next fancybox-slide--previous' ).addClass( 'fancybox-slide--current' );
1051
1052            // If slides have been dragged, animate them to correct position
1053            if ( current.isMoved ) {
1054                canvasWidth = Math.round( current.$slide.width() );
1055
1056                $.each(self.slides, function( index, slide ) {
1057                    var pos = slide.pos - current.pos;
1058
1059                    $.fancybox.animate( slide.$slide, {
1060                        top  : 0,
1061                        left : ( pos * canvasWidth ) + ( pos * slide.opts.gutter )
1062                    }, duration, function() {
1063
1064                        slide.$slide.removeAttr('style').removeClass( 'fancybox-slide--next fancybox-slide--previous' );
1065
1066                        if ( slide.pos === self.currPos ) {
1067                            current.isMoved = false;
1068
1069                            self.complete();
1070                        }
1071                    });
1072                });
1073
1074            } else {
1075                self.$refs.stage.children().removeAttr( 'style' );
1076            }
1077
1078            // Start transition that reveals current content
1079            // or wait when it will be loaded
1080
1081            if ( current.isLoaded ) {
1082                self.revealContent( current );
1083
1084            } else {
1085                self.loadSlide( current );
1086            }
1087
1088            self.preload();
1089
1090            if ( previous.pos === current.pos ) {
1091                return;
1092            }
1093
1094            // Handle previous slide
1095            // =====================
1096
1097            transitionProps = 'fancybox-slide--' + ( previous.pos > current.pos ? 'next' : 'previous' );
1098
1099            previous.$slide.removeClass( 'fancybox-slide--complete fancybox-slide--current fancybox-slide--next fancybox-slide--previous' );
1100
1101            previous.isComplete = false;
1102
1103            if ( !duration || ( !current.isMoved && !current.opts.transitionEffect ) ) {
1104                return;
1105            }
1106
1107            if ( current.isMoved ) {
1108                previous.$slide.addClass( transitionProps );
1109
1110            } else {
1111
1112                transitionProps = 'fancybox-animated ' + transitionProps + ' fancybox-fx-' + current.opts.transitionEffect;
1113
1114                $.fancybox.animate( previous.$slide, transitionProps, duration, function() {
1115                    previous.$slide.removeClass( transitionProps ).removeAttr( 'style' );
1116                });
1117
1118            }
1119
1120        },
1121
1122
1123        // Create new "slide" element
1124        // These are gallery items  that are actually added to DOM
1125        // =======================================================
1126
1127        createSlide : function( pos ) {
1128
1129            var self = this;
1130            var $slide;
1131            var index;
1132
1133            index = pos % self.group.length;
1134            index = index < 0 ? self.group.length + index : index;
1135
1136            if ( !self.slides[ pos ] && self.group[ index ] ) {
1137                $slide = $('<div class="fancybox-slide"></div>').appendTo( self.$refs.stage );
1138
1139                self.slides[ pos ] = $.extend( true, {}, self.group[ index ], {
1140                    pos      : pos,
1141                    $slide   : $slide,
1142                    isLoaded : false,
1143                });
1144
1145                self.updateSlide( self.slides[ pos ] );
1146            }
1147
1148            return self.slides[ pos ];
1149        },
1150
1151
1152        // Scale image to the actual size of the image
1153        // ===========================================
1154
1155        scaleToActual : function( x, y, duration ) {
1156
1157            var self = this;
1158
1159            var current = self.current;
1160            var $what   = current.$content;
1161
1162            var imgPos, posX, posY, scaleX, scaleY;
1163
1164            var canvasWidth  = parseInt( current.$slide.width(), 10 );
1165            var canvasHeight = parseInt( current.$slide.height(), 10 );
1166
1167            var newImgWidth  = current.width;
1168            var newImgHeight = current.height;
1169
1170            if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating) {
1171                return;
1172            }
1173
1174            $.fancybox.stop( $what );
1175
1176            self.isAnimating = true;
1177
1178            x = x === undefined ? canvasWidth  * 0.5  : x;
1179            y = y === undefined ? canvasHeight * 0.5  : y;
1180
1181            imgPos = $.fancybox.getTranslate( $what );
1182
1183            scaleX  = newImgWidth  / imgPos.width;
1184            scaleY  = newImgHeight / imgPos.height;
1185
1186            // Get center position for original image
1187            posX = ( canvasWidth * 0.5  - newImgWidth * 0.5 );
1188            posY = ( canvasHeight * 0.5 - newImgHeight * 0.5 );
1189
1190            // Make sure image does not move away from edges
1191            if ( newImgWidth > canvasWidth ) {
1192                posX = imgPos.left * scaleX - ( ( x * scaleX ) - x );
1193
1194                if ( posX > 0 ) {
1195                    posX = 0;
1196                }
1197
1198                if ( posX <  canvasWidth - newImgWidth ) {
1199                    posX = canvasWidth - newImgWidth;
1200                }
1201            }
1202
1203            if ( newImgHeight > canvasHeight) {
1204                posY = imgPos.top  * scaleY - ( ( y * scaleY ) - y );
1205
1206                if ( posY > 0 ) {
1207                    posY = 0;
1208                }
1209
1210                if ( posY <  canvasHeight - newImgHeight ) {
1211                    posY = canvasHeight - newImgHeight;
1212                }
1213            }
1214
1215            self.updateCursor( newImgWidth, newImgHeight );
1216
1217            $.fancybox.animate( $what, {
1218                top    : posY,
1219                left   : posX,
1220                scaleX : scaleX,
1221                scaleY : scaleY
1222            }, duration || 330, function() {
1223                self.isAnimating = false;
1224            });
1225
1226            // Stop slideshow
1227            if ( self.SlideShow && self.SlideShow.isActive ) {
1228                self.SlideShow.stop();
1229            }
1230        },
1231
1232
1233        // Scale image to fit inside parent element
1234        // ========================================
1235
1236        scaleToFit : function( duration ) {
1237
1238            var self = this;
1239
1240            var current = self.current;
1241            var $what   = current.$content;
1242            var end;
1243
1244            if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating ) {
1245                return;
1246            }
1247
1248            $.fancybox.stop( $what );
1249
1250            self.isAnimating = true;
1251
1252            end = self.getFitPos( current );
1253
1254            self.updateCursor( end.width, end.height );
1255
1256            $.fancybox.animate( $what, {
1257                top    : end.top,
1258                left   : end.left,
1259                scaleX : end.width  / $what.width(),
1260                scaleY : end.height / $what.height()
1261            }, duration || 330, function() {
1262                self.isAnimating = false;
1263            });
1264
1265        },
1266
1267        // Calculate image size to fit inside viewport
1268        // ===========================================
1269
1270        getFitPos : function( slide ) {
1271            var self  = this;
1272            var $what = slide.$content;
1273
1274            var imgWidth  = slide.width;
1275            var imgHeight = slide.height;
1276
1277            var margin = slide.opts.margin;
1278
1279            var canvasWidth, canvasHeight, minRatio, width, height;
1280
1281            if ( !$what || !$what.length || ( !imgWidth && !imgHeight) ) {
1282                return false;
1283            }
1284
1285            // Convert "margin to CSS style: [ top, right, bottom, left ]
1286            if ( $.type( margin ) === "number" ) {
1287                margin = [ margin, margin ];
1288            }
1289
1290            if ( margin.length == 2 ) {
1291                margin = [ margin[0], margin[1], margin[0], margin[1] ];
1292            }
1293
1294            if ( $W.width() < 800 ) {
1295                margin = [ 0, 0, 0, 0 ];
1296            }
1297
1298            // We can not use $slide width here, because it can have different diemensions while in transiton
1299            canvasWidth  = parseInt( self.$refs.stage.width(), 10 )  - ( margin[ 1 ] + margin[ 3 ] );
1300            canvasHeight = parseInt( self.$refs.stage.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] );
1301
1302            minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight );
1303
1304            width  = Math.floor( minRatio * imgWidth );
1305            height = Math.floor( minRatio * imgHeight );
1306
1307            // Use floor rounding to make sure it really fits
1308            return {
1309                top    : Math.floor( ( canvasHeight - height ) * 0.5 ) + margin[ 0 ],
1310                left   : Math.floor( ( canvasWidth  - width )  * 0.5 ) + margin[ 3 ],
1311                width  : width,
1312                height : height
1313            };
1314
1315        },
1316
1317
1318        // Update position and content of all slides
1319        // =========================================
1320
1321        update : function() {
1322
1323            var self = this;
1324
1325            $.each( self.slides, function( key, slide ) {
1326                self.updateSlide( slide );
1327            });
1328
1329        },
1330
1331
1332        // Update slide position and scale content to fit
1333        // ==============================================
1334
1335        updateSlide : function( slide ) {
1336
1337            var self  = this;
1338            var $what = slide.$content;
1339
1340            if ( $what && ( slide.width || slide.height ) ) {
1341                $.fancybox.stop( $what );
1342
1343                $.fancybox.setTranslate( $what, self.getFitPos( slide ) );
1344
1345                if ( slide.pos === self.currPos ) {
1346                    self.updateCursor();
1347                }
1348            }
1349
1350            slide.$slide.trigger( 'refresh' );
1351
1352            self.trigger( 'onUpdate', slide );
1353
1354        },
1355
1356        // Update cursor style depending if content can be zoomed
1357        // ======================================================
1358
1359        updateCursor : function( nextWidth, nextHeight ) {
1360
1361            var self = this;
1362            var isScaledDown;
1363
1364            var $container = self.$refs.container.removeClass('fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-drag fancybox-can-zoomOut');
1365
1366            if ( !self.current || self.isClosing ) {
1367                return;
1368            }
1369
1370            if ( self.isZoomable() ) {
1371
1372                $container.addClass( 'fancybox-is-zoomable' );
1373
1374                if ( nextWidth !== undefined && nextHeight !== undefined ) {
1375                    isScaledDown = nextWidth < self.current.width && nextHeight < self.current.height;
1376
1377                } else {
1378                    isScaledDown = self.isScaledDown();
1379                }
1380
1381                if ( isScaledDown ) {
1382
1383                    // If image is scaled down, then, obviously, it can be zoomed to full size
1384                    $container.addClass('fancybox-can-zoomIn');
1385
1386                } else {
1387
1388                    if ( self.current.opts.touch ) {
1389
1390                        // If image size ir largen than available available and touch module is not disable,
1391                        // then user can do panning
1392                        $container.addClass('fancybox-can-drag');
1393
1394                    } else {
1395                        $container.addClass('fancybox-can-zoomOut');
1396                    }
1397
1398                }
1399
1400            } else if ( self.current.opts.touch ) {
1401                $container.addClass('fancybox-can-drag');
1402            }
1403
1404        },
1405
1406
1407        // Check if current slide is zoomable
1408        // ==================================
1409
1410        isZoomable : function() {
1411
1412            var self = this;
1413
1414            var current = self.current;
1415            var fitPos;
1416
1417            if ( !current || self.isClosing ) {
1418                return;
1419            }
1420
1421            // Assume that slide is zoomable if
1422            //   - image is loaded successfuly
1423            //   - click action is "zoom"
1424            //   - actual size of the image is smaller than available area
1425            if ( current.type === 'image' && current.isLoaded && !current.hasError &&
1426                ( current.opts.clickContent === 'zoom' || ( $.isFunction( current.opts.clickContent ) && current.opts.clickContent( current ) ===  "zoom" ) )
1427            ) {
1428
1429                fitPos = self.getFitPos( current );
1430
1431                if ( current.width > fitPos.width || current.height > fitPos.height ) {
1432                    return true;
1433                }
1434
1435            }
1436
1437            return false;
1438
1439        },
1440
1441
1442        // Check if current image dimensions are smaller than actual
1443        // =========================================================
1444
1445        isScaledDown : function() {
1446
1447            var self = this;
1448
1449            var current = self.current;
1450            var $what   = current.$content;
1451
1452            var rez = false;
1453
1454            if ( $what ) {
1455                rez = $.fancybox.getTranslate( $what );
1456                rez = rez.width < current.width || rez.height < current.height;
1457            }
1458
1459            return rez;
1460
1461        },
1462
1463
1464        // Check if image dimensions exceed parent element
1465        // ===============================================
1466
1467        canPan : function() {
1468
1469            var self = this;
1470
1471            var current = self.current;
1472            var $what   = current.$content;
1473
1474            var rez = false;
1475
1476            if ( $what ) {
1477                rez = self.getFitPos( current );
1478                rez = Math.abs( $what.width() - rez.width ) > 1  || Math.abs( $what.height() - rez.height ) > 1;
1479
1480            }
1481
1482            return rez;
1483
1484        },
1485
1486
1487        // Load content into the slide
1488        // ===========================
1489
1490        loadSlide : function( slide ) {
1491
1492            var self = this, type, $slide;
1493            var ajaxLoad;
1494
1495            if ( slide.isLoading ) {
1496                return;
1497            }
1498
1499            if ( slide.isLoaded ) {
1500                return;
1501            }
1502
1503            slide.isLoading = true;
1504
1505            self.trigger( 'beforeLoad', slide );
1506
1507            type   = slide.type;
1508            $slide = slide.$slide;
1509
1510            $slide
1511                .off( 'refresh' )
1512                .trigger( 'onReset' )
1513                .addClass( 'fancybox-slide--' + ( type || 'unknown' ) )
1514                .addClass( slide.opts.slideClass );
1515
1516            // Create content depending on the type
1517
1518            switch ( type ) {
1519
1520                case 'image':
1521
1522                    self.setImage( slide );
1523
1524                break;
1525
1526                case 'iframe':
1527
1528                    self.setIframe( slide );
1529
1530                break;
1531
1532                case 'html':
1533
1534                    self.setContent( slide, slide.src || slide.content );
1535
1536                break;
1537
1538                case 'inline':
1539
1540                    if ( $( slide.src ).length ) {
1541                        self.setContent( slide, $( slide.src ) );
1542
1543                    } else {
1544                        self.setError( slide );
1545                    }
1546
1547                break;
1548
1549                case 'ajax':
1550
1551                    self.showLoading( slide );
1552
1553                    ajaxLoad = $.ajax( $.extend( {}, slide.opts.ajax.settings, {
1554                        url : slide.src,
1555                        success : function ( data, textStatus ) {
1556
1557                            if ( textStatus === 'success' ) {
1558                                self.setContent( slide, data );
1559                            }
1560
1561                        },
1562                        error : function ( jqXHR, textStatus ) {
1563
1564                            if ( jqXHR && textStatus !== 'abort' ) {
1565                                self.setError( slide );
1566                            }
1567
1568                        }
1569                    }));
1570
1571                    $slide.one( 'onReset', function () {
1572                        ajaxLoad.abort();
1573                    });
1574
1575                break;
1576
1577                default:
1578
1579                    self.setError( slide );
1580
1581                break;
1582
1583            }
1584
1585            return true;
1586
1587        },
1588
1589
1590        // Use thumbnail image, if possible
1591        // ================================
1592
1593        setImage : function( slide ) {
1594
1595            var self   = this;
1596            var srcset = slide.opts.image.srcset;
1597
1598            var found, temp, pxRatio, windowWidth;
1599
1600            // If we have "srcset", then we need to find matching "src" value.
1601            // This is necessary, because when you set an src attribute, the browser will preload the image
1602            // before any javascript or even CSS is applied.
1603            if ( srcset ) {
1604                pxRatio     = window.devicePixelRatio || 1;
1605                windowWidth = window.innerWidth  * pxRatio;
1606
1607                temp = srcset.split(',').map(function ( el ) {
1608            		var ret = {};
1609
1610            		el.trim().split(/\s+/).forEach(function ( el, i ) {
1611                        var value = parseInt( el.substring(0, el.length - 1), 10 );
1612
1613            			if ( i === 0 ) {
1614            				return ( ret.url = el );
1615            			}
1616
1617                        if ( value ) {
1618                            ret.value   = value;
1619                            ret.postfix = el[ el.length - 1 ];
1620                        }
1621
1622            		});
1623
1624            		return ret;
1625            	});
1626
1627                // Sort by value
1628                temp.sort(function (a, b) {
1629                  return a.value - b.value;
1630                });
1631
1632                // Ok, now we have an array of all srcset values
1633                for ( var j = 0; j < temp.length; j++ ) {
1634                    var el = temp[ j ];
1635
1636                    if ( ( el.postfix === 'w' && el.value >= windowWidth ) || ( el.postfix === 'x' && el.value >= pxRatio ) ) {
1637                        found = el;
1638                        break;
1639                    }
1640                }
1641
1642                // If not found, take the last one
1643                if ( !found && temp.length ) {
1644                    found = temp[ temp.length - 1 ];
1645                }
1646
1647                if ( found ) {
1648                    slide.src = found.url;
1649
1650                    // If we have default width/height values, we can calculate height for matching source
1651                    if ( slide.width && slide.height && found.postfix == 'w' ) {
1652                        slide.height = ( slide.width / slide.height ) * found.value;
1653                        slide.width  = found.value;
1654                    }
1655                }
1656            }
1657
1658            // This will be wrapper containing both ghost and actual image
1659            slide.$content = $('<div class="fancybox-image-wrap"></div>')
1660                .addClass( 'fancybox-is-hidden' )
1661                .appendTo( slide.$slide );
1662
1663
1664            // If we have a thumbnail, we can display it while actual image is loading
1665            // Users will not stare at black screen and actual image will appear gradually
1666            if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) {
1667
1668                slide.width  = slide.opts.width;
1669                slide.height = slide.opts.height;
1670
1671                slide.$ghost = $('<img />')
1672                    .one('error', function() {
1673
1674                        $(this).remove();
1675
1676                        slide.$ghost = null;
1677
1678                        self.setBigImage( slide );
1679
1680                    })
1681                    .one('load', function() {
1682
1683                        self.afterLoad( slide );
1684
1685                        self.setBigImage( slide );
1686
1687                    })
1688                    .addClass( 'fancybox-image' )
1689                    .appendTo( slide.$content )
1690                    .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) );
1691
1692            } else {
1693
1694                self.setBigImage( slide );
1695
1696            }
1697
1698        },
1699
1700
1701        // Create full-size image
1702        // ======================
1703
1704        setBigImage : function ( slide ) {
1705            var self = this;
1706            var $img = $('<img />');
1707
1708            slide.$image = $img
1709                .one('error', function() {
1710
1711                    self.setError( slide );
1712
1713                })
1714                .one('load', function() {
1715
1716                    // Clear timeout that checks if loading icon needs to be displayed
1717                    clearTimeout( slide.timouts );
1718
1719                    slide.timouts = null;
1720
1721                    if ( self.isClosing ) {
1722                        return;
1723                    }
1724
1725                    slide.width  = this.naturalWidth;
1726                    slide.height = this.naturalHeight;
1727
1728                    if ( slide.opts.image.srcset ) {
1729                        $img.attr( 'sizes', '100vw' ).attr( 'srcset', slide.opts.image.srcset );
1730                    }
1731
1732                    self.hideLoading( slide );
1733
1734                    if ( slide.$ghost ) {
1735
1736                        slide.timouts = setTimeout(function() {
1737                            slide.timouts = null;
1738
1739                            slide.$ghost.hide();
1740
1741                        }, Math.min( 300, Math.max( 1000, slide.height / 1600 ) ) );
1742
1743                    } else {
1744                        self.afterLoad( slide );
1745                    }
1746
1747                })
1748                .addClass( 'fancybox-image' )
1749                .attr('src', slide.src)
1750                .appendTo( slide.$content );
1751
1752            if ( $img[0].complete ) {
1753                  $img.trigger( 'load' );
1754
1755            } else if( $img[0].error ) {
1756                 $img.trigger( 'error' );
1757
1758            } else {
1759
1760                slide.timouts = setTimeout(function() {
1761                    if ( !$img[0].complete && !slide.hasError ) {
1762                        self.showLoading( slide );
1763                    }
1764
1765                }, 100);
1766
1767            }
1768
1769        },
1770
1771
1772        // Create iframe wrapper, iframe and bindings
1773        // ==========================================
1774
1775        setIframe : function( slide ) {
1776            var self	= this,
1777                opts    = slide.opts.iframe,
1778                $slide	= slide.$slide,
1779                $iframe;
1780
1781            slide.$content = $('<div class="fancybox-content' + ( opts.preload ? ' fancybox-is-hidden' : '' ) + '"></div>')
1782                .css( opts.css )
1783                .appendTo( $slide );
1784
1785            $iframe = $( opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) )
1786                .attr( opts.attr )
1787                .appendTo( slide.$content );
1788
1789            if ( opts.preload ) {
1790
1791                self.showLoading( slide );
1792
1793                // Unfortunately, it is not always possible to determine if iframe is successfully loaded
1794                // (due to browser security policy)
1795
1796                $iframe.on('load.fb error.fb', function(e) {
1797                    this.isReady = 1;
1798
1799                    slide.$slide.trigger( 'refresh' );
1800
1801                    self.afterLoad( slide );
1802                });
1803
1804                // Recalculate iframe content size
1805                // ===============================
1806
1807                $slide.on('refresh.fb', function() {
1808                    var $wrap = slide.$content,
1809                        $contents,
1810                        $body,
1811                        scrollWidth,
1812                        frameWidth,
1813                        frameHeight;
1814
1815                    if ( $iframe[0].isReady !== 1 ) {
1816                        return;
1817                    }
1818
1819                    // Check if content is accessible,
1820                    // it will fail if frame is not with the same origin
1821
1822                    try {
1823                        $contents = $iframe.contents();
1824                        $body     = $contents.find('body');
1825
1826                    } catch (ignore) {}
1827
1828                    // Calculate dimensions for the wrapper
1829                    if ( $body && $body.length && !( opts.css.width !== undefined && opts.css.height !== undefined ) ) {
1830
1831                        scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth;
1832
1833                        frameWidth	= Math.ceil( $body.outerWidth(true) + ( $wrap.width() - scrollWidth ) );
1834                        frameHeight	= Math.ceil( $body.outerHeight(true) );
1835
1836                        // Resize wrapper to fit iframe content
1837                        $wrap.css({
1838                            'width'  : opts.css.width  === undefined ? frameWidth  + ( $wrap.outerWidth()  - $wrap.innerWidth() )  : opts.css.width,
1839                            'height' : opts.css.height === undefined ? frameHeight + ( $wrap.outerHeight() - $wrap.innerHeight() ) : opts.css.height
1840                        });
1841
1842                    }
1843
1844                    $wrap.removeClass( 'fancybox-is-hidden' );
1845
1846                });
1847
1848            } else {
1849
1850                this.afterLoad( slide );
1851
1852            }
1853
1854            $iframe.attr( 'src', slide.src );
1855
1856            if ( slide.opts.smallBtn === true ) {
1857                slide.$content.prepend( self.translate( slide, slide.opts.btnTpl.smallBtn ) );
1858            }
1859
1860            // Remove iframe if closing or changing gallery item
1861            $slide.one( 'onReset', function () {
1862
1863                // This helps IE not to throw errors when closing
1864                try {
1865
1866                    $( this ).find( 'iframe' ).hide().attr( 'src', '//about:blank' );
1867
1868                } catch ( ignore ) {}
1869
1870                $( this ).empty();
1871
1872                slide.isLoaded = false;
1873
1874            });
1875
1876        },
1877
1878
1879        // Wrap and append content to the slide
1880        // ======================================
1881
1882        setContent : function ( slide, content ) {
1883
1884            var self = this;
1885
1886            if ( self.isClosing ) {
1887                return;
1888            }
1889
1890            self.hideLoading( slide );
1891
1892            slide.$slide.empty();
1893
1894            if ( isQuery( content ) && content.parent().length ) {
1895
1896                // If content is a jQuery object, then it will be moved to the slide.
1897                // The placeholder is created so we will know where to put it back.
1898                // If user is navigating gallery fast, then the content might be already inside fancyBox
1899                // =====================================================================================
1900
1901                // Make sure content is not already moved to fancyBox
1902                content.parent( '.fancybox-slide--inline' ).trigger( 'onReset' );
1903
1904                // Create temporary element marking original place of the content
1905                slide.$placeholder = $( '<div></div>' ).hide().insertAfter( content );
1906
1907                // Make sure content is visible
1908                content.css('display', 'inline-block');
1909
1910            } else if ( !slide.hasError ) {
1911
1912                // If content is just a plain text, try to convert it to html
1913                if ( $.type( content ) === 'string' ) {
1914                    content = $('<div>').append( $.trim( content ) ).contents();
1915
1916                    // If we have text node, then add wrapping element to make vertical alignment work
1917                    if ( content[0].nodeType === 3 ) {
1918                        content = $('<div>').html( content );
1919                    }
1920                }
1921
1922                // If "filter" option is provided, then filter content
1923                if ( slide.opts.filter ) {
1924                    content = $('<div>').html( content ).find( slide.opts.filter );
1925                }
1926
1927            }
1928
1929            slide.$slide.one('onReset', function () {
1930
1931                // Put content back
1932                if ( slide.$placeholder ) {
1933                    slide.$placeholder.after( content.hide() ).remove();
1934
1935                    slide.$placeholder = null;
1936                }
1937
1938                // Remove custom close button
1939                if ( slide.$smallBtn ) {
1940                    slide.$smallBtn.remove();
1941
1942                    slide.$smallBtn = null;
1943                }
1944
1945                // Remove content and mark slide as not loaded
1946                if ( !slide.hasError ) {
1947                    $(this).empty();
1948
1949                    slide.isLoaded = false;
1950                }
1951
1952            });
1953
1954            slide.$content = $( content ).appendTo( slide.$slide );
1955
1956            if ( slide.opts.smallBtn && !slide.$smallBtn ) {
1957                slide.$smallBtn = $( self.translate( slide, slide.opts.btnTpl.smallBtn ) ).appendTo( slide.$content );
1958            }
1959
1960            this.afterLoad( slide );
1961        },
1962
1963        // Display error message
1964        // =====================
1965
1966        setError : function ( slide ) {
1967
1968            slide.hasError = true;
1969
1970            slide.$slide.removeClass( 'fancybox-slide--' + slide.type );
1971
1972            this.setContent( slide, this.translate( slide, slide.opts.errorTpl ) );
1973
1974        },
1975
1976
1977        // Show loading icon inside the slide
1978        // ==================================
1979
1980        showLoading : function( slide ) {
1981
1982            var self = this;
1983
1984            slide = slide || self.current;
1985
1986            if ( slide && !slide.$spinner ) {
1987                slide.$spinner = $( self.opts.spinnerTpl ).appendTo( slide.$slide );
1988            }
1989
1990        },
1991
1992        // Remove loading icon from the slide
1993        // ==================================
1994
1995        hideLoading : function( slide ) {
1996
1997            var self = this;
1998
1999            slide = slide || self.current;
2000
2001            if ( slide && slide.$spinner ) {
2002                slide.$spinner.remove();
2003
2004                delete slide.$spinner;
2005            }
2006
2007        },
2008
2009
2010        // Adjustments after slide content has been loaded
2011        // ===============================================
2012
2013        afterLoad : function( slide ) {
2014
2015            var self = this;
2016
2017            if ( self.isClosing ) {
2018                return;
2019            }
2020
2021            slide.isLoading = false;
2022            slide.isLoaded  = true;
2023
2024            self.trigger( 'afterLoad', slide );
2025
2026            self.hideLoading( slide );
2027
2028            if ( slide.opts.protect && slide.$content && !slide.hasError ) {
2029
2030                // Disable right click
2031                slide.$content.on( 'contextmenu.fb', function( e ) {
2032                     if ( e.button == 2 ) {
2033                         e.preventDefault();
2034                     }
2035
2036                    return true;
2037                });
2038
2039                // Add fake element on top of the image
2040                // This makes a bit harder for user to select image
2041                if ( slide.type === 'image' ) {
2042                    $( '<div class="fancybox-spaceball"></div>' ).appendTo( slide.$content );
2043                }
2044
2045            }
2046
2047            self.revealContent( slide );
2048
2049        },
2050
2051
2052        // Make content visible
2053        // This method is called right after content has been loaded or
2054        // user navigates gallery and transition should start
2055        // ============================================================
2056
2057        revealContent : function( slide ) {
2058
2059            var self   = this;
2060            var $slide = slide.$slide;
2061
2062            var effect, effectClassName, duration, opacity, end, start = false;
2063
2064            effect   = slide.opts[ self.firstRun ? 'animationEffect'   : 'transitionEffect' ];
2065            duration = slide.opts[ self.firstRun ? 'animationDuration' : 'transitionDuration' ];
2066
2067            duration = parseInt( slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10 );
2068
2069            if ( slide.isMoved || slide.pos !== self.currPos || !duration ) {
2070                effect = false;
2071            }
2072
2073            // Check if can zoom
2074            if ( effect === 'zoom' && !( slide.pos === self.currPos && duration && slide.type === 'image' && !slide.hasError && ( start = self.getThumbPos( slide ) ) ) ) {
2075                effect = 'fade';
2076            }
2077
2078
2079            // Zoom animation
2080            // ==============
2081
2082            if ( effect === 'zoom' ) {
2083                end = self.getFitPos( slide );
2084
2085                end.scaleX = Math.round( (end.width  / start.width)  * 100 ) / 100;
2086                end.scaleY = Math.round( (end.height / start.height) * 100 ) / 100;
2087
2088                delete end.width;
2089                delete end.height;
2090
2091                // Check if we need to animate opacity
2092                opacity = slide.opts.zoomOpacity;
2093
2094                if ( opacity == 'auto' ) {
2095                    opacity = Math.abs( slide.width / slide.height - start.width / start.height ) > 0.1;
2096                }
2097
2098                if ( opacity ) {
2099                    start.opacity = 0.1;
2100                    end.opacity   = 1;
2101                }
2102
2103                // Draw image at start position
2104                $.fancybox.setTranslate( slide.$content.removeClass( 'fancybox-is-hidden' ), start );
2105
2106                forceRedraw( slide.$content );
2107
2108                // Start animation
2109                $.fancybox.animate( slide.$content, end, duration, function() {
2110                    self.complete();
2111                });
2112
2113                return;
2114            }
2115
2116
2117            self.updateSlide( slide );
2118
2119
2120            // Simply show content
2121            // ===================
2122
2123            if ( !effect ) {
2124                forceRedraw( $slide );
2125
2126                slide.$content.removeClass( 'fancybox-is-hidden' );
2127
2128                if ( slide.pos === self.currPos ) {
2129                    self.complete();
2130                }
2131
2132                return;
2133            }
2134
2135            $.fancybox.stop( $slide );
2136
2137            effectClassName = 'fancybox-animated fancybox-slide--' + ( slide.pos > self.prevPos ? 'next' : 'previous' ) + ' fancybox-fx-' + effect;
2138
2139            $slide.removeAttr( 'style' ).removeClass( 'fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ).addClass( effectClassName );
2140
2141            slide.$content.removeClass( 'fancybox-is-hidden' );
2142
2143            //Force reflow for CSS3 transitions
2144            forceRedraw( $slide );
2145
2146            $.fancybox.animate( $slide, 'fancybox-slide--current', duration, function(e) {
2147                $slide.removeClass( effectClassName ).removeAttr( 'style' );
2148
2149                if ( slide.pos === self.currPos ) {
2150                    self.complete();
2151                }
2152
2153            }, true);
2154
2155        },
2156
2157
2158        // Check if we can and have to zoom from thumbnail
2159        //================================================
2160
2161        getThumbPos : function( slide ) {
2162
2163            var self = this;
2164            var rez  = false;
2165
2166            // Check if element is inside the viewport by at least 1 pixel
2167            var isElementVisible = function( $el ) {
2168                var element = $el[0];
2169
2170                var elementRect = element.getBoundingClientRect();
2171                var parentRects = [];
2172
2173                var visibleInAllParents;
2174
2175                while ( element.parentElement !== null ) {
2176                    if ( $(element.parentElement).css('overflow') === 'hidden'  || $(element.parentElement).css('overflow') === 'auto' ) {
2177                        parentRects.push(element.parentElement.getBoundingClientRect());
2178                    }
2179
2180                    element = element.parentElement;
2181                }
2182
2183                visibleInAllParents = parentRects.every(function(parentRect){
2184                    var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left);
2185                    var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top);
2186
2187                    return visiblePixelX > 0 && visiblePixelY > 0;
2188                });
2189
2190                return visibleInAllParents &&
2191                    elementRect.bottom > 0 && elementRect.right > 0 &&
2192                    elementRect.left < $(window).width() && elementRect.top < $(window).height();
2193            };
2194
2195            var $thumb   = slide.opts.$thumb;
2196            var thumbPos = $thumb ? $thumb.offset() : 0;
2197            var slidePos;
2198
2199            if ( thumbPos && $thumb[0].ownerDocument === document && isElementVisible( $thumb ) ) {
2200                slidePos = self.$refs.stage.offset();
2201
2202                rez = {
2203                    top    : thumbPos.top  - slidePos.top  + parseFloat( $thumb.css( "border-top-width" ) || 0 ),
2204                    left   : thumbPos.left - slidePos.left + parseFloat( $thumb.css( "border-left-width" ) || 0 ),
2205                    width  : $thumb.width(),
2206                    height : $thumb.height(),
2207                    scaleX : 1,
2208                    scaleY : 1
2209                };
2210            }
2211
2212            return rez;
2213        },
2214
2215
2216        // Final adjustments after current gallery item is moved to position
2217        // and it`s content is loaded
2218        // ==================================================================
2219
2220        complete : function() {
2221
2222            var self = this;
2223
2224            var current = self.current;
2225            var slides  = {};
2226
2227            if ( current.isMoved || !current.isLoaded || current.isComplete ) {
2228                return;
2229            }
2230
2231            current.isComplete = true;
2232
2233            current.$slide.siblings().trigger( 'onReset' );
2234
2235            // Trigger any CSS3 transiton inside the slide
2236            forceRedraw( current.$slide );
2237
2238            current.$slide.addClass( 'fancybox-slide--complete' );
2239
2240            // Remove unnecessary slides
2241            $.each( self.slides, function( key, slide ) {
2242                if ( slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1 ) {
2243                    slides[ slide.pos ] = slide;
2244
2245                } else if ( slide ) {
2246
2247                    $.fancybox.stop( slide.$slide );
2248
2249                    slide.$slide.unbind().remove();
2250                }
2251            });
2252
2253            self.slides = slides;
2254
2255            self.updateCursor();
2256
2257            self.trigger( 'afterShow' );
2258
2259            // Try to focus on the first focusable element
2260            if ( $( document.activeElement ).is( '[disabled]' ) || ( current.opts.autoFocus && !( current.type == 'image' || current.type === 'iframe' ) ) ) {
2261                self.focus();
2262            }
2263
2264        },
2265
2266
2267        // Preload next and previous slides
2268        // ================================
2269
2270        preload : function() {
2271            var self = this;
2272            var next, prev;
2273
2274            if ( self.group.length < 2 ) {
2275                return;
2276            }
2277
2278            next  = self.slides[ self.currPos + 1 ];
2279            prev  = self.slides[ self.currPos - 1 ];
2280
2281            if ( next && next.type === 'image' ) {
2282                self.loadSlide( next );
2283            }
2284
2285            if ( prev && prev.type === 'image' ) {
2286                self.loadSlide( prev );
2287            }
2288
2289        },
2290
2291
2292        // Try to find and focus on the first focusable element
2293        // ====================================================
2294
2295        focus : function() {
2296            var current = this.current;
2297            var $el;
2298
2299            if ( this.isClosing ) {
2300                return;
2301            }
2302
2303            // Skip for images and iframes
2304            $el = current && current.isComplete ? current.$slide.find('button,:input,[tabindex],a').filter(':not([disabled]):visible:first') : null;
2305            $el = $el && $el.length ? $el : this.$refs.container;
2306
2307            $el.focus();
2308        },
2309
2310
2311        // Activates current instance - brings container to the front and enables keyboard,
2312        // notifies other instances about deactivating
2313        // =================================================================================
2314
2315        activate : function () {
2316            var self = this;
2317
2318            // Deactivate all instances
2319            $( '.fancybox-container' ).each(function () {
2320                var instance = $(this).data( 'FancyBox' );
2321
2322                // Skip self and closing instances
2323                if (instance && instance.uid !== self.uid && !instance.isClosing) {
2324                    instance.trigger( 'onDeactivate' );
2325                }
2326
2327            });
2328
2329            if ( self.current ) {
2330                if ( self.$refs.container.index() > 0 ) {
2331                    self.$refs.container.prependTo( document.body );
2332                }
2333
2334                self.updateControls();
2335            }
2336
2337            self.trigger( 'onActivate' );
2338
2339            self.addEvents();
2340
2341        },
2342
2343
2344        // Start closing procedure
2345        // This will start "zoom-out" animation if needed and clean everything up afterwards
2346        // =================================================================================
2347
2348        close : function( e, d ) {
2349
2350            var self    = this;
2351            var current = self.current;
2352
2353            var effect, duration;
2354            var $what, opacity, start, end;
2355
2356            var done = function() {
2357                self.cleanUp( e );
2358            };
2359
2360            if ( self.isClosing ) {
2361                return false;
2362            }
2363
2364            self.isClosing = true;
2365
2366            // If beforeClose callback prevents closing, make sure content is centered
2367            if ( self.trigger( 'beforeClose', e ) === false ) {
2368                self.isClosing = false;
2369
2370                requestAFrame(function() {
2371                    self.update();
2372                });
2373
2374                return false;
2375            }
2376
2377            // Remove all events
2378            // If there are multiple instances, they will be set again by "activate" method
2379            self.removeEvents();
2380
2381            if ( current.timouts ) {
2382                clearTimeout( current.timouts );
2383            }
2384
2385            $what    = current.$content;
2386            effect   = current.opts.animationEffect;
2387            duration = $.isNumeric( d ) ? d : ( effect ? current.opts.animationDuration : 0 );
2388
2389            // Remove other slides
2390            current.$slide.off( transitionEnd ).removeClass( 'fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated' );
2391
2392            current.$slide.siblings().trigger( 'onReset' ).remove();
2393
2394            // Trigger animations
2395            if ( duration ) {
2396                self.$refs.container.removeClass( 'fancybox-is-open' ).addClass( 'fancybox-is-closing' );
2397            }
2398
2399            // Clean up
2400            self.hideLoading( current );
2401
2402            self.hideControls();
2403
2404            self.updateCursor();
2405
2406            // Check if possible to zoom-out
2407            if ( effect === 'zoom' && !( e !== true && $what && duration && current.type === 'image' && !current.hasError && ( end = self.getThumbPos( current ) ) ) ) {
2408                effect = 'fade';
2409            }
2410
2411            if ( effect === 'zoom' ) {
2412                $.fancybox.stop( $what );
2413
2414                start = $.fancybox.getTranslate( $what );
2415
2416                start.width  = start.width  * start.scaleX;
2417                start.height = start.height * start.scaleY;
2418
2419                // Check if we need to animate opacity
2420                opacity = current.opts.zoomOpacity;
2421
2422                if ( opacity == 'auto' ) {
2423                    opacity = Math.abs( current.width / current.height - end.width / end.height ) > 0.1;
2424                }
2425
2426                if ( opacity ) {
2427                    end.opacity = 0;
2428                }
2429
2430                start.scaleX = start.width  / end.width;
2431                start.scaleY = start.height / end.height;
2432
2433                start.width  = end.width;
2434                start.height = end.height;
2435
2436                $.fancybox.setTranslate( current.$content, start );
2437
2438                $.fancybox.animate( current.$content, end, duration, done );
2439
2440                return true;
2441            }
2442
2443            if ( effect && duration ) {
2444
2445                // If skip animation
2446                if ( e === true ) {
2447                    setTimeout( done, duration );
2448
2449                } else {
2450                    $.fancybox.animate( current.$slide.removeClass( 'fancybox-slide--current' ), 'fancybox-animated fancybox-slide--previous fancybox-fx-' + effect, duration, done );
2451                }
2452
2453            } else {
2454                done();
2455            }
2456
2457            return true;
2458        },
2459
2460
2461        // Final adjustments after removing the instance
2462        // =============================================
2463
2464        cleanUp : function( e ) {
2465            var self = this,
2466                instance;
2467
2468            self.current.$slide.trigger( 'onReset' );
2469
2470            self.$refs.container.empty().remove();
2471
2472            self.trigger( 'afterClose', e );
2473
2474            // Place back focus
2475            if ( self.$lastFocus && !!self.current.opts.backFocus ) {
2476                self.$lastFocus.focus();
2477            }
2478
2479            self.current = null;
2480
2481            // Check if there are other instances
2482            instance = $.fancybox.getInstance();
2483
2484            if ( instance ) {
2485                instance.activate();
2486
2487            } else {
2488
2489                $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft );
2490
2491                $( 'html' ).removeClass( 'fancybox-enabled' );
2492
2493                $( '#fancybox-style-noscroll' ).remove();
2494            }
2495
2496        },
2497
2498
2499        // Call callback and trigger an event
2500        // ==================================
2501
2502        trigger : function( name, slide ) {
2503            var args  = Array.prototype.slice.call(arguments, 1),
2504                self  = this,
2505                obj   = slide && slide.opts ? slide : self.current,
2506                rez;
2507
2508            if ( obj ) {
2509                args.unshift( obj );
2510
2511            } else {
2512                obj = self;
2513            }
2514
2515            args.unshift( self );
2516
2517            if ( $.isFunction( obj.opts[ name ] ) ) {
2518                rez = obj.opts[ name ].apply( obj, args );
2519            }
2520
2521            if ( rez === false ) {
2522                return rez;
2523            }
2524
2525            if ( name === 'afterClose' ) {
2526                $D.trigger( name + '.fb', args );
2527
2528            } else {
2529                self.$refs.container.trigger( name + '.fb', args );
2530            }
2531
2532        },
2533
2534
2535        // Update infobar values, navigation button states and reveal caption
2536        // ==================================================================
2537
2538        updateControls : function ( force ) {
2539
2540            var self = this;
2541
2542            var current  = self.current;
2543            var index    = current.index;
2544            var opts     = current.opts;
2545            var caption  = opts.caption;
2546            var $caption = self.$refs.caption;
2547
2548            // Recalculate content dimensions
2549            current.$slide.trigger( 'refresh' );
2550
2551            self.$caption = caption && caption.length ? $caption.html( caption ) : null;
2552
2553            if ( !self.isHiddenControls ) {
2554                self.showControls();
2555            }
2556
2557            // Update info and navigation elements
2558            $('[data-fancybox-count]').html( self.group.length );
2559            $('[data-fancybox-index]').html( index + 1 );
2560
2561            $('[data-fancybox-prev]').prop('disabled', ( !opts.loop && index <= 0 ) );
2562            $('[data-fancybox-next]').prop('disabled', ( !opts.loop && index >= self.group.length - 1 ) );
2563
2564        },
2565
2566        // Hide toolbar and caption
2567        // ========================
2568
2569        hideControls : function () {
2570
2571            this.isHiddenControls = true;
2572
2573            this.$refs.container.removeClass('fancybox-show-infobar fancybox-show-toolbar fancybox-show-caption fancybox-show-nav');
2574
2575        },
2576
2577        showControls : function() {
2578
2579            var self = this;
2580            var opts = self.current ? self.current.opts : self.opts;
2581            var $container = self.$refs.container;
2582
2583            self.isHiddenControls   = false;
2584            self.idleSecondsCounter = 0;
2585
2586            $container
2587                .toggleClass('fancybox-show-toolbar', !!( opts.toolbar && opts.buttons ) )
2588                .toggleClass('fancybox-show-infobar', !!( opts.infobar && self.group.length > 1 ) )
2589                .toggleClass('fancybox-show-nav',     !!( opts.arrows && self.group.length > 1 ) )
2590                .toggleClass('fancybox-is-modal',     !!opts.modal );
2591
2592            if ( self.$caption ) {
2593                $container.addClass( 'fancybox-show-caption ');
2594
2595            } else {
2596               $container.removeClass( 'fancybox-show-caption' );
2597           }
2598
2599       },
2600
2601
2602       // Toggle toolbar and caption
2603       // ==========================
2604
2605       toggleControls : function() {
2606
2607           if ( this.isHiddenControls ) {
2608               this.showControls();
2609
2610           } else {
2611               this.hideControls();
2612           }
2613
2614       },
2615
2616
2617    });
2618
2619
2620    $.fancybox = {
2621
2622        version  : "{fancybox-version}",
2623        defaults : defaults,
2624
2625
2626        // Get current instance and execute a command.
2627        //
2628        // Examples of usage:
2629        //
2630        //   $instance = $.fancybox.getInstance();
2631        //   $.fancybox.getInstance().jumpTo( 1 );
2632        //   $.fancybox.getInstance( 'jumpTo', 1 );
2633        //   $.fancybox.getInstance( function() {
2634        //       console.info( this.currIndex );
2635        //   });
2636        // ======================================================
2637
2638        getInstance : function ( command ) {
2639            var instance = $('.fancybox-container:not(".fancybox-is-closing"):first').data( 'FancyBox' );
2640            var args     = Array.prototype.slice.call(arguments, 1);
2641
2642            if ( instance instanceof FancyBox ) {
2643
2644                if ( $.type( command ) === 'string' ) {
2645                    instance[ command ].apply( instance, args );
2646
2647                } else if ( $.type( command ) === 'function' ) {
2648                    command.apply( instance, args );
2649
2650                }
2651
2652                return instance;
2653            }
2654
2655            return false;
2656
2657        },
2658
2659
2660        // Create new instance
2661        // ===================
2662
2663        open : function ( items, opts, index ) {
2664            return new FancyBox( items, opts, index );
2665        },
2666
2667
2668        // Close current or all instances
2669        // ==============================
2670
2671        close : function ( all ) {
2672            var instance = this.getInstance();
2673
2674            if ( instance ) {
2675                instance.close();
2676
2677                // Try to find and close next instance
2678
2679                if ( all === true ) {
2680                    this.close();
2681                }
2682            }
2683
2684        },
2685
2686        // Close instances and unbind all events
2687        // ==============================
2688
2689        destroy : function() {
2690
2691            this.close( true );
2692
2693            $D.off( 'click.fb-start' );
2694
2695        },
2696
2697
2698        // Try to detect mobile devices
2699        // ============================
2700
2701        isMobile : document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent),
2702
2703
2704        // Detect if 'translate3d' support is available
2705        // ============================================
2706
2707        use3d : (function() {
2708            var div = document.createElement('div');
2709
2710            return window.getComputedStyle && window.getComputedStyle( div ).getPropertyValue('transform') && !(document.documentMode && document.documentMode < 11);
2711        }()),
2712
2713
2714        // Helper function to get current visual state of an element
2715        // returns array[ top, left, horizontal-scale, vertical-scale, opacity ]
2716        // =====================================================================
2717
2718        getTranslate : function( $el ) {
2719            var matrix;
2720
2721            if ( !$el || !$el.length ) {
2722                return false;
2723            }
2724
2725            matrix  = $el.eq( 0 ).css('transform');
2726
2727            if ( matrix && matrix.indexOf( 'matrix' ) !== -1 ) {
2728                matrix = matrix.split('(')[1];
2729                matrix = matrix.split(')')[0];
2730                matrix = matrix.split(',');
2731            } else {
2732                matrix = [];
2733            }
2734
2735            if ( matrix.length ) {
2736
2737                // If IE
2738                if ( matrix.length > 10 ) {
2739                    matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ];
2740
2741                } else {
2742                    matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]];
2743                }
2744
2745                matrix = matrix.map(parseFloat);
2746
2747            } else {
2748                matrix = [ 0, 0, 1, 1 ];
2749
2750                var transRegex = /\.*translate\((.*)px,(.*)px\)/i;
2751                var transRez = transRegex.exec( $el.eq( 0 ).attr('style') );
2752
2753                if ( transRez ) {
2754                    matrix[ 0 ] = parseFloat( transRez[2] );
2755                    matrix[ 1 ] = parseFloat( transRez[1] );
2756                }
2757            }
2758
2759            return {
2760                top     : matrix[ 0 ],
2761                left    : matrix[ 1 ],
2762                scaleX  : matrix[ 2 ],
2763                scaleY  : matrix[ 3 ],
2764                opacity : parseFloat( $el.css('opacity') ),
2765                width   : $el.width(),
2766                height  : $el.height()
2767            };
2768
2769        },
2770
2771
2772        // Shortcut for setting "translate3d" properties for element
2773        // Can set be used to set opacity, too
2774        // ========================================================
2775
2776        setTranslate : function( $el, props ) {
2777            var str  = '';
2778            var css  = {};
2779
2780            if ( !$el || !props ) {
2781                return;
2782            }
2783
2784            if ( props.left !== undefined || props.top !== undefined ) {
2785                str = ( props.left === undefined ? $el.position().left : props.left )  + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px';
2786
2787                if ( this.use3d ) {
2788                    str = 'translate3d(' + str + ', 0px)';
2789
2790                } else {
2791                    str = 'translate(' + str + ')';
2792                }
2793            }
2794
2795            if ( props.scaleX !== undefined && props.scaleY !== undefined ) {
2796                str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')';
2797            }
2798
2799            if ( str.length ) {
2800                css.transform = str;
2801            }
2802
2803            if ( props.opacity !== undefined ) {
2804                css.opacity = props.opacity;
2805            }
2806
2807            if ( props.width !== undefined ) {
2808                css.width = props.width;
2809            }
2810
2811            if ( props.height !== undefined ) {
2812                css.height = props.height;
2813            }
2814
2815            return $el.css( css );
2816        },
2817
2818
2819        // Simple CSS transition handler
2820        // =============================
2821
2822        animate : function ( $el, to, duration, callback, leaveAnimationName ) {
2823            var event = transitionEnd || 'transitionend';
2824
2825            if ( $.isFunction( duration ) ) {
2826                callback = duration;
2827                duration = null;
2828            }
2829
2830            if ( !$.isPlainObject( to ) ) {
2831                $el.removeAttr('style');
2832            }
2833
2834            $el.on( event, function(e) {
2835
2836                // Skip events from child elements and z-index change
2837                if ( e && e.originalEvent && ( !$el.is( e.originalEvent.target ) || e.originalEvent.propertyName == 'z-index' ) ) {
2838                    return;
2839                }
2840
2841                $el.off( event );
2842
2843                if ( $.isPlainObject( to ) ) {
2844
2845                    if ( to.scaleX !== undefined && to.scaleY !== undefined ) {
2846                        $el.css( 'transition-duration', '0ms' );
2847
2848                        to.width  = $el.width()  * to.scaleX;
2849                        to.height = $el.height() * to.scaleY;
2850
2851                        to.scaleX = 1;
2852                        to.scaleY = 1;
2853
2854                        $.fancybox.setTranslate( $el, to );
2855                    }
2856
2857                } else if ( leaveAnimationName !== true ) {
2858                    $el.removeClass( to );
2859                }
2860
2861                if ( $.isFunction( callback ) ) {
2862                    callback( e );
2863                }
2864
2865            });
2866
2867            if ( $.isNumeric( duration ) ) {
2868                $el.css( 'transition-duration', duration + 'ms' );
2869            }
2870
2871            if ( $.isPlainObject( to ) ) {
2872                $.fancybox.setTranslate( $el, to );
2873
2874            } else {
2875                $el.addClass( to );
2876            }
2877
2878            $el.data("timer", setTimeout(function() {
2879                $el.trigger( 'transitionend' );
2880            }, duration + 16));
2881
2882        },
2883
2884        stop : function( $el ) {
2885            clearTimeout( $el.data("timer") );
2886
2887            $el.off( transitionEnd );
2888        }
2889
2890    };
2891
2892
2893    // Default click handler for "fancyboxed" links
2894    // ============================================
2895
2896    function _run( e ) {
2897        var target	= e.currentTarget,
2898            opts	= e.data ? e.data.options : {},
2899            items	= opts.selector ? $( opts.selector ) : ( e.data ? e.data.items : [] ),
2900            value	= $(target).attr( 'data-fancybox' ) || '',
2901            index	= 0,
2902            active  = $.fancybox.getInstance();
2903
2904        e.preventDefault();
2905        e.stopPropagation();
2906
2907        // Avoid opening multiple times
2908        if ( active && active.current.opts.$orig.is( target ) ) {
2909            return;
2910        }
2911
2912        // Get all related items and find index for clicked one
2913        if ( value ) {
2914            items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox="' + value + '"]' );
2915            index = items.index( target );
2916
2917            // Sometimes current item can not be found
2918            // (for example, when slider clones items)
2919            if ( index < 0 ) {
2920                index = 0;
2921            }
2922
2923        } else {
2924            items = [ target ];
2925        }
2926
2927        $.fancybox.open( items, opts, index );
2928    }
2929
2930
2931    // Create a jQuery plugin
2932    // ======================
2933
2934    $.fn.fancybox = function (options) {
2935        var selector;
2936
2937        options  = options || {};
2938        selector = options.selector || false;
2939
2940        if ( selector ) {
2941
2942            $( 'body' ).off( 'click.fb-start', selector ).on( 'click.fb-start', selector, {
2943                options : options
2944            }, _run );
2945
2946        } else {
2947
2948            this.off( 'click.fb-start' ).on( 'click.fb-start', {
2949                items   : this,
2950                options : options
2951            }, _run);
2952
2953        }
2954
2955        return this;
2956    };
2957
2958
2959    // Self initializing plugin
2960    // ========================
2961
2962    $D.on( 'click.fb-start', '[data-fancybox]', _run );
2963
2964}( window, document, window.jQuery ));

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