PageSourceSearch

https://www.redmonkie.com/gallery2/index.php?controller=Front&action=loadJs&id=12&alb=

js redmonkie.com collected 2026-10-07 06:50:21 UTC 205,052 bytes, 7,472 lines download raw bytes

1// ==================================================
2// fancyBox v3.1.20
3//
4// Licensed GPLv3 for open source use
5// or fancyBox Commercial License for commercial use
6//
7// http://fancyapps.com/fancybox/
8// Copyright 2017 fancyApps
9//
10// ==================================================
11;(function (window, document, $, undefined) {
12    'use strict';
13
14    // If there's no jQuery, fancyBox can't work
15    // =========================================
16
17    if ( !$ ) {
18        return;
19    }
20
21    // Check if fancyBox is already initialized
22    // ========================================
23
24    if ( $.fn.fancybox ) {
25
26        $.error('fancyBox already initialized');
27
28        return;
29    }
30
31    // Private default settings
32    // ========================
33
34    var defaults = {
35
36        // Enable infinite gallery navigation
37        loop : true,
38
39        // Space around image, ignored if zoomed-in or viewport smaller than 800px
40        margin : [44, 0],
41
42        // Horizontal space between slides
43        gutter : 50,
44
45        // Enable keyboard navigation
46        keyboard : true,
47
48        // Should display navigation arrows at the screen edges
49        arrows : true,
50
51        // Should display infobar (counter and arrows at the top)
52        infobar : false,
53
54        // Should display toolbar (buttons at the top)
55        toolbar : true,
56
57        // What buttons should appear in the top right corner.
58        // Buttons will be created using templates from `btnTpl` option
59        // and they will be placed into toolbar (class="fancybox-toolbar"` element)
60        buttons : [
61             'close'
62        ],
63
64        // Detect "idle" time in seconds
65        idleTime : 4,
66
67        // Should display buttons at top right corner of the content
68        // If 'auto' - they will be created for content having type 'html', 'inline' or 'ajax'
69        // Use template from `btnTpl.smallBtn` for customization
70        smallBtn : 'auto',
71
72        // Disable right-click and use simple image protection for images
73        protect : false,
74
75        // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc
76        modal : false,
77
78        image : {
79
80            // Wait for images to load before displaying
81            // Requires predefined image dimensions
82            // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found
83            preload : "auto",
84
85        },
86
87        ajax : {
88
89            // Object containing settings for ajax request
90            settings : {
91
92                // This helps to indicate that request comes from the modal
93                // Feel free to change naming
94                data : {
95                    fancybox : true
96                }
97            }
98
99        },
100
101        iframe : {
102
103            // Iframe template
104            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>',
105
106            // Preload iframe before displaying it
107            // This allows to calculate iframe content width and height
108            // (note: Due to "Same Origin Policy", you can't get cross domain data).
109            preload : true,
110
111            // Custom CSS styling for iframe wrapping element
112            // You can use this to set custom iframe dimensions
113            css : {},
114
115            // Iframe tag attributes
116            attr : {
117                scrolling : 'auto'
118            }
119
120        },
121
122        // Open/close animation type
123        // Possible values:
124        //   false            - disable
125        //   "zoom"           - zoom images from/to thumbnail
126        //   "fade"
127        //   "zoom-in-out"
128        //
129        animationEffect : "disable",
130
131        // Duration in ms for open/close animation
132        animationDuration : 366,
133
134        // Should image change opacity while zooming
135        // If opacity is 'auto', then opacity will be changed if image and thumbnail have different aspect ratios
136        zoomOpacity : 'auto',
137
138        // Transition effect between slides
139        //
140        // Possible values:
141        //   false            - disable
142        //   "fade'
143        //   "slide'
144        //   "circular'
145        //   "tube'
146        //   "zoom-in-out'
147        //   "rotate'
148        //
149        transitionEffect : "fade",
150
151        // Duration in ms for transition animation
152        transitionDuration : 366,
153
154        // Custom CSS class for slide element
155        slideClass : '',
156
157        // Custom CSS class for layout
158        baseClass : '',
159
160        // Base template for layout
161        baseTpl	:
162            '<div class="fancybox-container" role="dialog" tabindex="-1">' +
163                '<div class="fancybox-bg"></div>' +
164                '<div class="fancybox-inner">' +
165                    '<div class="fancybox-infobar">' +
166                        '<button data-fancybox-prev title="{{PREV}}" class="fancybox-button fancybox-button--left"></button>' +
167                        '<div class="fancybox-infobar__body">' +
168                            '<span data-fancybox-index></span>&nbsp;/&nbsp;<span data-fancybox-count></span>' +
169                        '</div>' +
170                        '<button data-fancybox-next title="{{NEXT}}" class="fancybox-button fancybox-button--right"></button>' +
171                    '</div>' +
172                    '<div class="fancybox-toolbar">' +
173                        '{{BUTTONS}}' +
174                    '</div>' +
175                    '<div class="fancybox-navigation">' +
176                        '<button data-fancybox-prev title="{{PREV}}" class="fancybox-arrow fancybox-arrow--left" />' +
177                        '<button data-fancybox-next title="{{NEXT}}" class="fancybox-arrow fancybox-arrow--right" />' +
178                    '</div>' +
179                    '<div class="fancybox-stage"></div>' +
180                    '<div class="fancybox-caption-wrap">' +
181                        '<div class="fancybox-caption"></div>' +
182                    '</div>' +
183                '</div>' +
184            '</div>',
185
186        // Loading indicator template
187        spinnerTpl : '<div class="fancybox-loading"></div>',
188
189        // Error message template
190        errorTpl : '<div class="fancybox-error"><p>{{ERROR}}<p></div>',
191
192        btnTpl : {
193            slideShow  : '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}"></button>',
194            fullScreen : '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="{{FULL_SCREEN}}"></button>',
195            thumbs     : '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}"></button>',
196            close      : '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}"></button>',
197
198            // This small close button will be appended to your html/inline/ajax content by default,
199            // if "smallBtn" option is not set to false
200            smallBtn   : '<button data-fancybox-close class="fancybox-close-small" title="{{CLOSE}}"></button>'
201        },
202
203        // Container is injected into this element
204        parentEl : 'body',
205
206
207        // Focus handling
208        // ==============
209
210        // Try to focus on the first focusable element after opening
211        autoFocus : true,
212
213        // Put focus back to active element after closing
214        backFocus : true,
215
216        // Do not let user to focus on element outside modal content
217        trapFocus : true,
218
219
220        // Module specific options
221        // =======================
222
223        fullScreen : {
224            autoStart : false,
225        },
226
227        touch : {
228            vertical : true,  // Allow to drag content vertically
229            momentum : true   // Continue movement after releasing mouse/touch when panning
230        },
231
232        // Hash value when initializing manually,
233        // set `false` to disable hash change
234        hash : null,
235
236        // Customize or add new media types
237        // Example:
238        /*
239        media : {
240            youtube : {
241                params : {
242                    autoplay : 0
243                }
244            }
245        }
246        */        
247        media : {},
248
249        slideShow : {
250            autoStart : false,
251            speed     : 4000
252        },
253
254        thumbs : {
255            autoStart   : false,   // Display thumbnails on opening
256            hideOnClose : true     // Hide thumbnail grid when closing animation starts
257        },
258
259        // Callbacks
260        //==========
261
262        // See Documentation/API/Events for more information
263        // Example:
264        /*
265            afterShow: function( instance, current ) {
266                 console.info( 'Clicked element:' );
267                 console.info( current.opts.$orig );
268            }
269        */
270
271        onInit       : $.noop,  // When instance has been initialized
272
273        beforeLoad   : $.noop,  // Before the content of a slide is being loaded
274        afterLoad    : $.noop,  // When the content of a slide is done loading
275
276        beforeShow   : $.noop,  // Before open animation starts
277        afterShow    : $.noop,  // When content is done loading and animating
278
279        beforeClose  : $.noop,  // Before the instance attempts to close. Return false to cancel the close.
280        afterClose   : $.noop,  // After instance has been closed
281
282        onActivate   : $.noop,  // When instance is brought to front
283        onDeactivate : $.noop,  // When other instance has been activated
284
285
286        // Interaction
287        // ===========
288
289        // Use options below to customize taken action when user clicks or double clicks on the fancyBox area,
290        // each option can be string or method that returns value.
291        //
292        // Possible values:
293        //   "close"           - close instance
294        //   "next"            - move to next gallery item
295        //   "nextOrClose"     - move to next gallery item or close if gallery has only one item
296        //   "toggleControls"  - show/hide controls
297        //   "zoom"            - zoom image (if loaded)
298        //   false             - do nothing
299
300        // Clicked on the content
301        dblclickContent : function( current, event ) {
302			
303                 return current.type === 'image' ? 'zoom' : false;
304			
305        },
306		clickContent : function( current, event ) {
307			 
308                 
309			    $('.fancybox-caption-wrap').toggle();       //harrisonm 
310                return (current.type === 'image' || current.type === 'iframe') ? 'toggleControls' : false;
311			
312                return current.type === 'image' ? 'toggleControls' : false;
313				return current.type === 'iframe' ? 'toggleControls' : false;
314        },
315
316        // Clicked on the slide
317		clickSlide : function( current, event ) {
318			
319              
320			
321                $('.fancybox-caption-wrap').toggle();        
322                return (current.type === 'image' || current.type === 'iframe') ? 'toggleControls' : false;
323			
324        },
325
326        // Clicked on the background (backdrop) element
327		clickOutside : function( current, event ) {
328			
329			    
330               $('.fancybox-caption-wrap').toggle();        
331               return (current.type === 'image' || current.type === 'iframe') ? 'toggleControls' : false;
332			
333        },
334
335        // Same as previous two, but for double click
336        // dblclickContent : false,
337        dblclickSlide   : false,
338        dblclickOutside : false,
339
340
341        // Custom options when mobile device is detected
342        // =============================================
343
344        mobile : {
345            clickContent : function( current, event ) {
346                
347				 
348			    $('.fancybox-caption-wrap').toggle();        
349                return (current.type === 'image' || current.type === 'iframe') ? 'toggleControls' : false;
350			
351                return current.type === 'image' ? 'toggleControls' : false;
352				return current.type === 'iframe' ? 'toggleControls' : false;
353            },
354			clickOutside : function( current, event ) {
355			    
356			
357               $('.fancybox-caption-wrap').toggle();        
358               return (current.type === 'image' || current.type === 'iframe') ? 'toggleControls' : false;
359            },
360            clickSlide : function( current, event ) {
361                
362                $('.fancybox-caption-wrap').toggle();        
363                return (current.type === 'image' || current.type === 'iframe') ? 'toggleControls' : false;
364            },
365            dblclickContent : function( current, event ) {
366                return current.type === 'image' ? 'zoom' : false;
367            },
368            dblclickSlide : function( current, event ) {
369                return current.type === 'image' ? 'zoom' : false;
370            }
371        },
372
373
374        // Internationalization
375        // ============
376
377        lang : 'en',
378        i18n : {
379            'en' : {
380                CLOSE       : 'Close',
381                NEXT        : 'Next',
382                PREV        : 'Previous',
383                ERROR       : 'The requested content cannot be loaded. <br/> Please try again later.',
384                PLAY_START  : 'Start slideshow',
385                PLAY_STOP   : 'Pause slideshow',
386                FULL_SCREEN : 'Full screen',
387                THUMBS      : 'Thumbnails'
388            },
389            'de' : {
390                CLOSE       : 'Schliessen',
391                NEXT        : 'Weiter',
392                PREV        : 'Zurück',
393                ERROR       : 'Die angeforderten Daten konnten nicht geladen werden. <br/>
393 Bitte versuchen Sie es später nochmal.',
394                PLAY_START  : 'Diaschau starten',
395                PLAY_STOP   : 'Diaschau beenden',
396                FULL_SCREEN : 'Vollbild',
397                THUMBS      : 'Vorschaubilder'
398            }
399        }
400
401    };
402
403    // Few useful variables and methods
404    // ================================
405
406    var $W = $(window);
407    var $D = $(document);
408
409    var called = 0;
410
411
412    // Check if an object is a jQuery object and not a native JavaScript object
413    // ========================================================================
414
415    var isQuery = function ( obj ) {
416        return obj && obj.hasOwnProperty && obj instanceof $;
417    };
418
419
420    // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame"
421    // ===============================================================================
422
423    var requestAFrame = (function () {
424        return window.requestAnimationFrame ||
425                window.webkitRequestAnimationFrame ||
426                window.mozRequestAnimationFrame ||
427                window.oRequestAnimationFrame ||
428                // if all else fails, use setTimeout
429                function (callback) {
430                    return window.setTimeout(callback, 1000 / 60);
431                };
432    })();
433
434
435    // Detect the supported transition-end event property name
436    // =======================================================
437
438    var transitionEnd = (function () {
439        var t, el = document.createElement("fakeelement");
440
441        var transitions = {
442            "transition"      : "transitionend",
443            "OTransition"     : "oTransitionEnd",
444            "MozTransition"   : "transitionend",
445            "WebkitTransition": "webkitTransitionEnd"
446        };
447
448        for (t in transitions) {
449            if (el.style[t] !== undefined){
450                return transitions[t];
451            }
452        }
453    })();
454
455
456    // Force redraw on an element.
457    // This helps in cases where the browser doesn't redraw an updated element properly.
458    // =================================================================================
459
460    var forceRedraw = function( $el ) {
461        return ( $el && $el.length && $el[0].offsetHeight );
462    };
463
464
465    // Class definition
466    // ================
467
468    var FancyBox = function( content, opts, index ) {
469        var self = this;
470
471        self.opts  = $.extend( true, { index : index }, defaults, opts || {} );
472
473        // Exclude buttons option from deep merging
474        if ( opts && $.isArray( opts.buttons ) ) {
475            self.opts.buttons = opts.buttons;
476        }
477
478        self.id    = self.opts.id || ++called;
479        self.group = [];
480
481        self.currIndex = parseInt( self.opts.index, 10 ) || 0;
482        self.prevIndex = null;
483
484        self.prevPos = null;
485        self.currPos = 0;
486
487        self.firstRun = null;
488
489        // Create group elements from original item collection
490        self.createGroup( content );
491
492        if ( !self.group.length ) {
493            return;
494        }
495
496        // Save last active element and current scroll position
497        self.$lastFocus = $(document.activeElement).blur();
498
499        // Collection of gallery objects
500        self.slides = {};
501
502        self.init( content );
503
504    };
505
506    $.extend(FancyBox.prototype, {
507
508        // Create DOM structure
509        // ====================
510
511        init : function() {
512            var self = this;
513
514            var testWidth, $container, buttonStr;
515
516            var firstItemOpts = self.group[ self.currIndex ].opts;
517
518            self.scrollTop  = $D.scrollTop();
519            self.scrollLeft = $D.scrollLeft();
520
521
522            // Hide scrollbars
523            // ===============
524
525            if ( !$.fancybox.getInstance() && !$.fancybox.isMobile && $( 'body' ).css('overflow') !== 'hidden' ) {
526                testWidth = $( 'body' ).width();
527
528                $( 'html' ).addClass( 'fancybox-enabled' );
529
530                // Compare body width after applying "overflow: hidden"
531                testWidth = $( 'body' ).width() - testWidth;
532
533                // If width has changed - compensate missing scrollbars by adding right margin
534                if ( testWidth > 1 ) {
535                    $( 'head' ).append( '<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar, .fancybox-enabled body { margin-right: ' + testWidth + 'px; }</style>' );
536                }
537            }
538
539
540            // Build html markup and set references
541            // ====================================
542
543            // Build html code for buttons and insert into main template
544            buttonStr = '';
545
546            $.each( firstItemOpts.buttons, function( index, value ) {
547                buttonStr += ( firstItemOpts.btnTpl[ value ] || '' );
548            });
549
550            // Create markup from base template, it will be initially hidden to
551            // avoid unnecessary work like painting while initializing is not complete
552            $container = $( self.translate( self, firstItemOpts.baseTpl.replace( '\{\{BUTTONS\}\}', buttonStr ) ) )
553                .addClass( 'fancybox-is-hidden' )
554                .attr('id', 'fancybox-container-' + self.id)
555                .addClass( firstItemOpts.baseClass )
556                .data( 'FancyBox', self )
557                .prependTo( firstItemOpts.parentEl );
558
559            // Create object holding references to jQuery wrapped nodes
560            self.$refs = {
561                container : $container
562            };
563
564            [ 'bg', 'inner', 'infobar', 'toolbar', 'stage', 'caption' ].forEach(function(item) {
565                self.$refs[ item ] = $container.find( '.fancybox-' + item );
566            });
567
568            // Check for redundant elements
569            if ( !firstItemOpts.arrows || self.group.length < 2 ) {
570                $container.find('.fancybox-navigation').remove();
571            }
572
573            if ( !firstItemOpts.infobar ) {
574                self.$refs.infobar.remove();
575            }
576
577            if ( !firstItemOpts.toolbar ) {
578                self.$refs.toolbar.remove();
579            }
580
581            self.trigger( 'onInit' );
582
583            // Bring to front and enable events
584            self.activate();
585
586            // Build slides, load and reveal content
587            self.jumpTo( self.currIndex );
588        },
589
590
591        // Simple i18n support - replaces object keys found in template
592        // with corresponding values
593        // ============================================================
594
595        translate : function( obj, str ) {
596            var arr = obj.opts.i18n[ obj.opts.lang ];
597
598            return str.replace(/\{\{(\w+)\}\}/g, function(match, n) {
599                var value = arr[n];
600
601                if ( value === undefined ) {
602                    return match;
603                }
604
605                return value;
606            });
607        },
608
609        // Create array of gally item objects
610        // Check if each object has valid type and content
611        // ===============================================
612
613        createGroup : function ( content ) {
614            var self  = this;
615            var items = $.makeArray( content );
616
617            $.each(items, function( i, item ) {
618                var obj  = {},
619                    opts = {},
620                    data = [],
621                    $item,
622                    type,
623                    src,
624                    srcParts;
625
626                // Step 1 - Make sure we have an object
627                // ====================================
628
629                if ( $.isPlainObject( item ) ) {
630
631                    // We probably have manual usage here, something like
632                    // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] )
633
634                    obj  = item;
635                    opts = item.opts || item;
636
637                } else if ( $.type( item ) === 'object' && $( item ).length ) {
638
639                    // Here we propbably have jQuery collection returned by some selector
640
641                    $item = $( item );
642                    data  = $item.data();
643
644                    opts = 'options' in data ? data.options : {};
645                    opts = $.type( opts ) === 'object' ? opts : {};
646
647                    obj.src  = 'src' in data ? data.src : ( opts.src || $item.attr( 'href' ) );
648
649                    [ 'width', 'height', 'thumb', 'type', 'filter' ].forEach(function(item) {
650                        if ( item in data ) {
651                            opts[ item ] = data[ item ];
652                        }
653                    });
654
655                    if ( 'srcset' in data ) {
656                        opts.image = { srcset : data.srcset };
657                    }
658
659                    opts.$orig = $item;
660
661                    if ( !obj.type && !obj.src ) {
662                        obj.type = 'inline';
663                        obj.src  = item;
664                    }
665
666                } else {
667
668                    // Assume we have a simple html code, for example:
669                    // $.fancybox.open( '<div><h1>Hi!</h1></div>' );
670
671                    obj = {
672                        type : 'html',
673                        src  : item + ''
674                    };
675
676                }
677
678                // Each gallery object has full collection of options
679                obj.opts = $.extend( true, {}, self.opts, opts );
680
681                if ( $.fancybox.isMobile ) {
682                    obj.opts = $.extend( true, {}, obj.opts, obj.opts.mobile );
683                }
684
685
686                // Step 2 - Make sure we have content type, if not - try to guess
687                // ==============================================================
688
689                type = obj.type || obj.opts.type;
690                src  = obj.src || '';
691
692                if ( !type && src ) {
693                    if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) {
694                        type = 'image';
695
696                    } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) {
697                        type = 'pdf';
698
699                    } else if ( src.charAt(0) === '#' ) {
700                        type = 'inline';
701                    }
702                }
703
704                obj.type = type;
705
706
707                // Step 3 - Some adjustments
708                // =========================
709
710                obj.index = self.group.length;
711
712                // Check if $orig and $thumb objects exist
713                if ( obj.opts.$orig && !obj.opts.$orig.length ) {
714                    delete obj.opts.$orig;
715                }
716
717                if ( !obj.opts.$thumb && obj.opts.$orig ) {
718                    obj.opts.$thumb = obj.opts.$orig.find( 'img:first' );
719                }
720
721                if ( obj.opts.$thumb && !obj.opts.$thumb.length ) {
722                    delete obj.opts.$thumb;
723                }
724
725                // Caption is a "special" option, it can be passed as a method
726                if ( $.type( obj.opts.caption ) === 'function' ) {
727                    obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] );
728
729                } else if ( 'caption' in data ) {
730                    obj.opts.caption = data.caption;
731                }
732
733                // Make sure we have caption as a string
734                obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + '';
735
736                // Check if url contains "filter" used to filter the content
737                // Example: "ajax.html #something"
738                if ( type === 'ajax' ) {
739                    srcParts = src.split(/\s+/, 2);
740
741                    if ( srcParts.length > 1 ) {
742                        obj.src = srcParts.shift();
743
744                        obj.opts.filter = srcParts.shift();
745                    }
746                }
747
748                if ( obj.opts.smallBtn == 'auto' ) {
749
750                    if ( $.inArray( type, ['html', 'inline', 'ajax'] ) > -1 ) {
751                        obj.opts.toolbar  = false;
752                        obj.opts.smallBtn = true;
753
754                    } else {
755                        obj.opts.smallBtn = false;
756                    }
757
758                }
759
760                // If the type is "pdf", then simply load file into iframe
761                if ( type === 'pdf' ) {
762                    obj.type = 'iframe';
763
764                    obj.opts.iframe.preload = false;
765                }
766
767                // Hide all buttons and disable interactivity for modal items
768                if ( obj.opts.modal ) {
769
770                    obj.opts = $.extend(true, obj.opts, {
771                        // Remove buttons
772                        infobar : 0,
773                        toolbar : 0,
774
775                        smallBtn : 0,
776
777                        // Disable keyboard navigation
778                        keyboard : 0,
779
780                        // Disable some modules
781                        slideShow  : 0,
782                        fullScreen : 0,
783                        thumbs     : 0,
784                        touch      : 0,
785
786                        // Disable click event handlers
787                        clickContent    : false,
788                        clickSlide      : false,
789                        clickOutside    : false,
790                        dblclickContent : false,
791                        dblclickSlide   : false,
792                        dblclickOutside : false
793                    });
794
795                }
796
797                // Step 4 - Add processed object to group
798                // ======================================
799
800                self.group.push( obj );
801
802            });
803
804        },
805
806
807        // Attach an event handler functions for:
808        //   - navigation buttons
809        //   - browser scrolling, resizing;
810        //   - focusing
811        //   - keyboard
812        //   - detect idle
813        // ======================================
814
815        addEvents : function() {
816            var self = this;
817
818            self.removeEvents();
819
820            // Make navigation elements clickable
821            self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) {
822                e.stopPropagation();
823                e.preventDefault();
824
825                self.close( e );
826				$( 'html' ).removeClass( 'fancybox-enabled' );
827
828            }).on( 'click.fb-prev touchend.fb-prev', '[data-fancybox-prev]', function(e) {
829                e.stopPropagation();
830                e.preventDefault();
831                
832                self.previous();
833
834            }).on( 'click.fb-next touchend.fb-next', '[data-fancybox-next]', function(e) {
835                e.stopPropagation();
836                e.preventDefault();
837                
838                self.next();
839
840            });
841
842
843            // Handle page scrolling and browser resizing
844            $W.on('orientationchange.fb resize.fb', function(e) {
845
846                if ( e && e.originalEvent && e.originalEvent.type === "resize" ) {
847
848                    requestAFrame(function() {
849                        self.update();
850                    });
851
852                } else {
853
854                    self.$refs.stage.hide();
855
856                    setTimeout(function() {
857                        self.$refs.stage.show();
858
859                        self.update();
860                    }, 500);
861
862                }
863
864            });
865
866            // Trap keyboard focus inside of the modal, so the user does not accidentally tab outside of the modal
867            // (a.k.a. "escaping the modal")
868            $D.on('focusin.fb', function(e) {
869                var instance = $.fancybox ? $.fancybox.getInstance() : null;
870
871                if ( instance.isClosing || !instance.current || !instance.current.opts.trapFocus || $( e.target ).hasClass( 'fancybox-container' ) || $( e.target ).is( document ) ) {
872                    return;
873                }
874
875                if ( instance && $( e.target ).css( 'position' ) !== 'fixed' && !instance.$refs.container.has( e.target ).length ) {
876                    e.stopPropagation();
877
878                    instance.focus();
879
880                    // Sometimes page gets scrolled, set it back
881                    $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft );
882                }
883            });
884
885
886            // Enable keyboard navigation
887            $D.on('keydown.fb', function (e) {
888                var current = self.current,
889                    keycode = e.keyCode || e.which;
890
891                if ( !current || !current.opts.keyboard ) {
892                    return;
893                }
894
895                if ( $(e.target).is('input') || $(e.target).is('textarea') ) {
896                    return;
897                }
898
899                // Backspace and Esc keys
900                if ( keycode === 8 || keycode === 27 ) {
901                    e.preventDefault();
902
903                    self.close( e );
904
905                    return;
906                }
907
908                // Left arrow and Up arrow
909                if ( keycode === 37 || keycode === 38 ) {
910                    e.preventDefault();
911
912                    self.previous();
913
914                    return;
915                }
916
917                // Righ arrow and Down arrow
918                if ( keycode === 39 || keycode === 40 ) {
919                    e.preventDefault();
920
921                    self.next();
922
923                    return;
924                }
925
926                self.trigger('afterKeydown', e, keycode);
927            });
928
929
930            // Hide controls after some inactivity period
931            if ( self.group[ self.currIndex ].opts.idleTime ) {
932                self.idleSecondsCounter = 0;
933
934                $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() {
935                    self.idleSecondsCounter = 0;
936
937                    if ( self.isIdle ) {
938                        self.showControls();
939                    }
940
941                    self.isIdle = false;
942                });
943
944                self.idleInterval = window.setInterval(function() {
945
946                    self.idleSecondsCounter++;
947
948                    if ( self.idleSecondsCounter >= self.group[ self.currIndex ].opts.idleTime ) {
949                        self.isIdle = true;
950                        self.idleSecondsCounter = 0;
951
952                        self.hideControls();
953                    }
954
955                }, 1000);
956            }
957
958        },
959
960
961        // Remove events added by the core
962        // ===============================
963
964        removeEvents : function () {
965            var self = this;
966
967            $W.off( 'orientationchange.fb resize.fb' );
968            $D.off( 'focusin.fb keydown.fb .fb-idle' );
969
970            this.$refs.container.off( '.fb-close .fb-prev .fb-next' );
971
972            if ( self.idleInterval ) {
973                window.clearInterval( self.idleInterval );
974
975                self.idleInterval = null;
976            }
977        },
978
979
980        // Change to previous gallery item
981        // ===============================
982
983        previous : function( duration ) {
984            $('.fancybox-caption-wrap').show();
985            return this.jumpTo( this.currPos - 1, duration );
986        },
987
988
989        // Change to next gallery item
990        // ===========================
991
992        next : function( duration ) {
993            $('.fancybox-caption-wrap').show();
994            return this.jumpTo( this.currPos + 1, duration );
995        },
996
997
998        // Switch to selected gallery item
999        // ===============================
1000
1001        jumpTo : function ( pos, duration, slide ) {
1002            var self = this,
1003                firstRun,
1004                loop,
1005                current,
1006                previous,
1007                canvasWidth,
1008                currentPos,
1009                transitionProps;
1010
1011            var groupLen = self.group.length;
1012
1013            if ( self.isSliding || self.isClosing || ( self.isAnimating && self.firstRun ) ) {
1014                return;
1015            }
1016
1017            pos  = parseInt( pos, 10 );
1018            loop = self.current ? self.current.opts.loop : self.opts.loop;
1019
1020            if ( !loop && ( pos < 0 || pos >= groupLen ) ) {
1021                return false;
1022            }
1023
1024            firstRun = self.firstRun = ( self.firstRun === null );
1025
1026            if ( groupLen < 2 && !firstRun && !!self.isSliding ) {
1027                return;
1028            }
1029
1030            previous = self.current;
1031
1032            self.prevIndex = self.currIndex;
1033            self.prevPos   = self.currPos;
1034
1035            // Create slides
1036            current = self.createSlide( pos );
1037
1038            if ( groupLen > 1 ) {
1039                if ( loop || current.index > 0 ) {
1040                    self.createSlide( pos - 1 );
1041                }
1042
1043                if ( loop || current.index < groupLen - 1 ) {
1044                    self.createSlide( pos + 1 );
1045                }
1046            }
1047
1048            self.current   = current;
1049            self.currIndex = current.index;
1050            self.currPos   = current.pos;
1051
1052            self.trigger( 'beforeShow', firstRun );
1053
1054            self.updateControls();
1055
1056            currentPos = $.fancybox.getTranslate( current.$slide );
1057
1058            current.isMoved        = ( currentPos.left !== 0 || currentPos.top !== 0 ) && !current.$slide.hasClass( 'fancybox-animated' );
1059            current.forcedDuration = undefined;
1060
1061            if ( $.isNumeric( duration ) ) {
1062                current.forcedDuration = duration;
1063            } else {
1064                duration = current.opts[ firstRun ? 'animationDuration' : 'transitionDuration' ];
1065            }
1066
1067            duration = parseInt( duration, 10 );
1068
1069            // Fresh start - reveal container, current slide and start loading content
1070            if ( firstRun ) {
1071
1072                if ( current.opts.animationEffect && duration ) {
1073                    self.$refs.container.css( 'transition-duration', duration + 'ms' );
1074                }
1075
1076                self.$refs.container.removeClass( 'fancybox-is-hidden' );
1077
1078                forceRedraw( self.$refs.container );
1079
1080                self.$refs.container.addClass( 'fancybox-is-open' );
1081
1082                // Make first slide visible (to display loading icon, if needed)
1083                current.$slide.addClass( 'fancybox-slide--current' );
1084
1085                self.loadSlide( current );
1086
1087                self.preload();
1088
1089                return;
1090            }
1091
1092            // Clean up
1093            $.each(self.slides, function( index, slide ) {
1094                $.fancybox.stop( slide.$slide );
1095            });
1096
1097            // Make current that slide is visible even if content is still loading
1098            current.$slide.removeClass( 'fancybox-slide--next fancybox-slide--previous' ).addClass( 'fancybox-slide--current' );
1099
1100            // If slides have been dragged, animate them to correct position
1101            if ( current.isMoved ) {
1102                canvasWidth = Math.round( current.$slide.width() );
1103
1104                $.each(self.slides, function( index, slide ) {
1105                    var pos = slide.pos - current.pos;
1106
1107                    $.fancybox.animate( slide.$slide, {
1108                        top  : 0,
1109                        left : ( pos * canvasWidth ) + ( pos * slide.opts.gutter )
1110                    }, duration, function() {
1111
1112                        slide.$slide.removeAttr('style').removeClass( 'fancybox-slide--next fancybox-slide--previous' );
1113
1114                        if ( slide.pos === self.currPos ) {
1115                            current.isMoved = false;
1116
1117                            self.complete();
1118                        }
1119                    });
1120                });
1121
1122            } else {
1123                self.$refs.stage.children().removeAttr( 'style' );
1124            }
1125
1126            // Start transition that reveals current content
1127            // or wait when it will be loaded
1128
1129            if ( current.isLoaded ) {
1130                self.revealContent( current );
1131
1132            } else {
1133                self.loadSlide( current );
1134            }
1135
1136            self.preload();
1137
1138            if ( previous.pos === current.pos ) {
1139                return;
1140            }
1141
1142            // Handle previous slide
1143            // =====================
1144
1145            transitionProps = 'fancybox-slide--' + ( previous.pos > current.pos ? 'next' : 'previous' );
1146
1147            previous.$slide.removeClass( 'fancybox-slide--complete fancybox-slide--current fancybox-slide--next fancybox-slide--previous' );
1148
1149            previous.isComplete = false;
1150
1151            if ( !duration || ( !current.isMoved && !current.opts.transitionEffect ) ) {
1152                return;
1153            }
1154
1155            if ( current.isMoved ) {
1156                previous.$slide.addClass( transitionProps );
1157
1158            } else {
1159
1160                transitionProps = 'fancybox-animated ' + transitionProps + ' fancybox-fx-' + current.opts.transitionEffect;
1161
1162                $.fancybox.animate( previous.$slide, transitionProps, duration, function() {
1163                    previous.$slide.removeClass( transitionProps ).removeAttr( 'style' );
1164                });
1165
1166            }
1167
1168        },
1169
1170
1171        // Create new "slide" element
1172        // These are gallery items  that are actually added to DOM
1173        // =======================================================
1174
1175        createSlide : function( pos ) {
1176
1177            var self = this;
1178            var $slide;
1179            var index;
1180
1181            index = pos % self.group.length;
1182            index = index < 0 ? self.group.length + index : index;
1183
1184            if ( !self.slides[ pos ] && self.group[ index ] ) {
1185                $slide = $('<div class="fancybox-slide"></div>').appendTo( self.$refs.stage );
1186
1187                self.slides[ pos ] = $.extend( true, {}, self.group[ index ], {
1188                    pos      : pos,
1189                    $slide   : $slide,
1190                    isLoaded : false,
1191                });
1192
1193                self.updateSlide( self.slides[ pos ] );
1194            }
1195
1196            return self.slides[ pos ];
1197        },
1198
1199
1200        // Scale image to the actual size of the image
1201        // ===========================================
1202
1203        scaleToActual : function( x, y, duration ) {
1204
1205            var self = this;
1206
1207            var current = self.current;
1208            var $what   = current.$content;
1209
1210            var imgPos, posX, posY, scaleX, scaleY;
1211
1212            var canvasWidth  = parseInt( current.$slide.width(), 10 );
1213            var canvasHeight = parseInt( current.$slide.height(), 10 );
1214
1215            var newImgWidth  = current.width;
1216            var newImgHeight = current.height;
1217
1218            if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating) {
1219                return;
1220            }
1221
1222            $.fancybox.stop( $what );
1223
1224            self.isAnimating = true;
1225
1226            x = x === undefined ? canvasWidth  * 0.5  : x;
1227            y = y === undefined ? canvasHeight * 0.5  : y;
1228
1229            imgPos = $.fancybox.getTranslate( $what );
1230
1231            scaleX  = newImgWidth  / imgPos.width;
1232            scaleY  = newImgHeight / imgPos.height;
1233
1234            // Get center position for original image
1235            posX = ( canvasWidth * 0.5  - newImgWidth * 0.5 );
1236            posY = ( canvasHeight * 0.5 - newImgHeight * 0.5 );
1237
1238            // Make sure image does not move away from edges
1239            if ( newImgWidth > canvasWidth ) {
1240                posX = imgPos.left * scaleX - ( ( x * scaleX ) - x );
1241
1242                if ( posX > 0 ) {
1243                    posX = 0;
1244                }
1245
1246                if ( posX <  canvasWidth - newImgWidth ) {
1247                    posX = canvasWidth - newImgWidth;
1248                }
1249            }
1250
1251            if ( newImgHeight > canvasHeight) {
1252                posY = imgPos.top  * scaleY - ( ( y * scaleY ) - y );
1253
1254                if ( posY > 0 ) {
1255                    posY = 0;
1256                }
1257
1258                if ( posY <  canvasHeight - newImgHeight ) {
1259                    posY = canvasHeight - newImgHeight;
1260                }
1261            }
1262
1263            self.updateCursor( newImgWidth, newImgHeight );
1264
1265            $.fancybox.animate( $what, {
1266                top    : posY,
1267                left   : posX,
1268                scaleX : scaleX,
1269                scaleY : scaleY
1270            }, duration || 330, function() {
1271                self.isAnimating = false;
1272            });
1273
1274            // Stop slideshow
1275            if ( self.SlideShow && self.SlideShow.isActive ) {
1276                self.SlideShow.stop();
1277            }
1278        },
1279
1280
1281        // Scale image to fit inside parent element
1282        // ========================================
1283
1284        scaleToFit : function( duration ) {
1285
1286            var self = this;
1287
1288            var current = self.current;
1289            var $what   = current.$content;
1290            var end;
1291
1292            if ( !( current.type == 'image' && !current.hasError) || !$what || self.isAnimating ) {
1293                return;
1294            }
1295
1296            $.fancybox.stop( $what );
1297
1298            self.isAnimating = true;
1299
1300            end = self.getFitPos( current );
1301
1302            self.updateCursor( end.width, end.height );
1303
1304            $.fancybox.animate( $what, {
1305                top    : end.top,
1306                left   : end.left,
1307                scaleX : end.width  / $what.width(),
1308                scaleY : end.height / $what.height()
1309            }, duration || 330, function() {
1310                self.isAnimating = false;
1311            });
1312
1313        },
1314
1315        // Calculate image size to fit inside viewport
1316        // ===========================================
1317
1318        getFitPos : function( slide ) {
1319            var self  = this;
1320            var $what = slide.$content;
1321
1322            var imgWidth  = slide.width;
1323            var imgHeight = slide.height;
1324
1325            var margin = slide.opts.margin;
1326
1327            var canvasWidth, canvasHeight, minRatio, width, height;
1328
1329            if ( !$what || !$what.length || ( !imgWidth && !imgHeight) ) {
1330                return false;
1331            }
1332
1333            // Convert "margin to CSS style: [ top, right, bottom, left ]
1334            if ( $.type( margin ) === "number" ) {
1335                margin = [ margin, margin ];
1336            }
1337
1338            if ( margin.length == 2 ) {
1339                margin = [ margin[0], margin[1], margin[0], margin[1] ];
1340            }
1341
1342            if ( $W.width() < 800 ) {
1343                margin = [ 0, 0, 0, 0 ];
1344            }
1345
1346            // We can not use $slide width here, because it can have different diemensions while in transiton
1347            canvasWidth  = parseInt( self.$refs.stage.width(), 10 )  - ( margin[ 1 ] + margin[ 3 ] );
1348            canvasHeight = parseInt( self.$refs.stage.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] );
1349
1350            minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight );
1351
1352            width  = Math.floor( minRatio * imgWidth );
1353            height = Math.floor( minRatio * imgHeight );
1354
1355            // Use floor rounding to make sure it really fits
1356            return {
1357                top    : Math.floor( ( canvasHeight - height ) * 0.5 ) + margin[ 0 ],
1358                left   : Math.floor( ( canvasWidth  - width )  * 0.5 ) + margin[ 3 ],
1359                width  : width,
1360                height : height
1361            };
1362
1363        },
1364
1365
1366        // Update position and content of all slides
1367        // =========================================
1368
1369        update : function() {
1370
1371            var self = this;
1372
1373            $.each( self.slides, function( key, slide ) {
1374                self.updateSlide( slide );
1375            });
1376
1377        },
1378
1379
1380        // Update slide position and scale content to fit
1381        // ==============================================
1382
1383        updateSlide : function( slide ) {
1384
1385            var self  = this;
1386            var $what = slide.$content;
1387
1388            if ( $what && ( slide.width || slide.height ) ) {
1389                $.fancybox.stop( $what );
1390
1391                $.fancybox.setTranslate( $what, self.getFitPos( slide ) );
1392
1393                if ( slide.pos === self.currPos ) {
1394                    self.updateCursor();
1395                }
1396            }
1397
1398            slide.$slide.trigger( 'refresh' );
1399
1400            self.trigger( 'onUpdate', slide );
1401
1402        },
1403
1404        // Update cursor style depending if content can be zoomed
1405        // ======================================================
1406
1407        updateCursor : function( nextWidth, nextHeight ) {
1408
1409            var self = this;
1410            var isScaledDown;
1411
1412            var $container = self.$refs.container.removeClass('fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-drag fancybox-can-zoomOut');
1413
1414            if ( !self.current || self.isClosing ) {
1415                return;
1416            }
1417
1418            if ( self.isZoomable() ) {
1419
1420                $container.addClass( 'fancybox-is-zoomable' );
1421
1422                if ( nextWidth !== undefined && nextHeight !== undefined ) {
1423                    isScaledDown = nextWidth < self.current.width && nextHeight < self.current.height;
1424
1425                } else {
1426                    isScaledDown = self.isScaledDown();
1427                }
1428
1429                if ( isScaledDown ) {
1430
1431                    // If image is scaled down, then, obviously, it can be zoomed to full size
1432                    $container.addClass('fancybox-can-zoomIn');
1433
1434                } else {
1435
1436                    if ( self.current.opts.touch ) {
1437
1438                        // If image size ir largen than available available and touch module is not disable,
1439                        // then user can do panning
1440                        $container.addClass('fancybox-can-drag');
1441
1442                    } else {
1443                        $container.addClass('fancybox-can-zoomOut');
1444                    }
1445
1446                }
1447
1448            } else if ( self.current.opts.touch ) {
1449                $container.addClass('fancybox-can-drag');
1450            }
1451
1452        },
1453
1454
1455        // Check if current slide is zoomable
1456        // ==================================
1457
1458        isZoomable : function() {
1459
1460            var self = this;
1461
1462            var current = self.current;
1463            var fitPos;
1464
1465            if ( !current || self.isClosing ) {
1466                return;
1467            }
1468
1469            // Assume that slide is zoomable if
1470            //   - image is loaded successfuly
1471            //   - click action is "zoom"
1472            //   - actual size of the image is smaller than available area
1473            if ( current.type === 'image' && current.isLoaded && !current.hasError &&
1474                ( current.opts.clickContent === 'zoom' || ( $.isFunction( current.opts.clickContent ) && current.opts.clickContent( current ) ===  "zoom" ) )
1475            ) {
1476
1477                fitPos = self.getFitPos( current );
1478
1479                if ( current.width > fitPos.width || current.height > fitPos.height ) {
1480                    return true;
1481                }
1482
1483            }
1484
1485            return false;
1486
1487        },
1488
1489
1490        // Check if current image dimensions are smaller than actual
1491        // =========================================================
1492
1493        isScaledDown : function() {
1494
1495            var self = this;
1496
1497            var current = self.current;
1498            var $what   = current.$content;
1499
1500            var rez = false;
1501
1502            if ( $what ) {
1503                rez = $.fancybox.getTranslate( $what );
1504                rez = rez.width < current.width || rez.height < current.height;
1505            }
1506
1507            return rez;
1508
1509        },
1510
1511
1512        // Check if image dimensions exceed parent element
1513        // ===============================================
1514
1515        canPan : function() {
1516
1517            var self = this;
1518
1519            var current = self.current;
1520            var $what   = current.$content;
1521
1522            var rez = false;
1523
1524            if ( $what ) {
1525                rez = self.getFitPos( current );
1526                rez = Math.abs( $what.width() - rez.width ) > 1  || Math.abs( $what.height() - rez.height ) > 1;
1527
1528            }
1529
1530            return rez;
1531
1532        },
1533
1534
1535        // Load content into the slide
1536        // ===========================
1537
1538        loadSlide : function( slide ) {
1539
1540            var self = this, type, $slide;
1541            var ajaxLoad;
1542
1543            if ( slide.isLoading ) {
1544                return;
1545            }
1546
1547            if ( slide.isLoaded ) {
1548                return;
1549            }
1550
1551            slide.isLoading = true;
1552
1553            self.trigger( 'beforeLoad', slide );
1554
1555            type   = slide.type;
1556            $slide = slide.$slide;
1557
1558            $slide
1559                .off( 'refresh' )
1560                .trigger( 'onReset' )
1561                .addClass( 'fancybox-slide--' + ( type || 'unknown' ) )
1562                .addClass( slide.opts.slideClass );
1563
1564            // Create content depending on the type
1565
1566            switch ( type ) {
1567
1568                case 'image':
1569
1570                    self.setImage( slide );
1571
1572                break;
1573
1574                case 'iframe':
1575
1576                    self.setIframe( slide );
1577
1578                break;
1579
1580                case 'html':
1581
1582                    self.setContent( slide, slide.src || slide.content );
1583
1584                break;
1585
1586                case 'inline':
1587
1588                    if ( $( slide.src ).length ) {
1589                        self.setContent( slide, $( slide.src ) );
1590
1591                    } else {
1592                        self.setError( slide );
1593                    }
1594
1595                break;
1596
1597                case 'ajax':
1598
1599                    self.showLoading( slide );
1600
1601                    ajaxLoad = $.ajax( $.extend( {}, slide.opts.ajax.settings, {
1602                        url : slide.src,
1603                        success : function ( data, textStatus ) {
1604
1605                            if ( textStatus === 'success' ) {
1606                                self.setContent( slide, data );
1607                            }
1608
1609                        },
1610                        error : function ( jqXHR, textStatus ) {
1611
1612                            if ( jqXHR && textStatus !== 'abort' ) {
1613                                self.setError( slide );
1614                            }
1615
1616                        }
1617                    }));
1618
1619                    $slide.one( 'onReset', function () {
1620                        ajaxLoad.abort();
1621                    });
1622
1623                break;
1624
1625                default:
1626
1627                    self.setError( slide );
1628
1629                break;
1630
1631            }
1632
1633            return true;
1634
1635        },
1636
1637
1638        // Use thumbnail image, if possible
1639        // ================================
1640
1641        setImage : function( slide ) {
1642
1643            var self   = this;
1644            var srcset = slide.opts.image.srcset;
1645
1646            var found, temp, pxRatio, windowWidth;
1647
1648            // If we have "srcset", then we need to find matching "src" value.
1649            // This is necessary, because when you set an src attribute, the browser will preload the image
1650            // before any javascript or even CSS is applied.
1651            if ( srcset ) {
1652                pxRatio     = window.devicePixelRatio || 1;
1653                windowWidth = window.innerWidth  * pxRatio;
1654
1655                temp = srcset.split(',').map(function ( el ) {
1656            		var ret = {};
1657
1658            		el.trim().split(/\s+/).forEach(function ( el, i ) {
1659                        var value = parseInt( el.substring(0, el.length - 1), 10 );
1660
1661            			if ( i === 0 ) {
1662            				return ( ret.url = el );
1663            			}
1664
1665                        if ( value ) {
1666                            ret.value   = value;
1667                            ret.postfix = el[ el.length - 1 ];
1668                        }
1669
1670            		});
1671
1672            		return ret;
1673            	});
1674
1675                // Sort by value
1676                temp.sort(function (a, b) {
1677                  return a.value - b.value;
1678                });
1679
1680                // Ok, now we have an array of all srcset values
1681                for ( var j = 0; j < temp.length; j++ ) {
1682                    var el = temp[ j ];
1683
1684                    if ( ( el.postfix === 'w' && el.value >= windowWidth ) || ( el.postfix === 'x' && el.value >= pxRatio ) ) {
1685                        found = el;
1686                        break;
1687                    }
1688                }
1689
1690                // If not found, take the last one
1691                if ( !found && temp.length ) {
1692                    found = temp[ temp.length - 1 ];
1693                }
1694
1695                if ( found ) {
1696                    slide.src = found.url;
1697
1698                    // If we have default width/height values, we can calculate height for matching source
1699                    if ( slide.width && slide.height && found.postfix == 'w' ) {
1700                        slide.height = ( slide.width / slide.height ) * found.value;
1701                        slide.width  = found.value;
1702                    }
1703                }
1704            }
1705
1706            // This will be wrapper containing both ghost and actual image
1707            slide.$content = $('<div class="fancybox-image-wrap"></div>')
1708                .addClass( 'fancybox-is-hidden' )
1709                .appendTo( slide.$slide );
1710
1711
1712            // If we have a thumbnail, we can display it while actual image is loading
1713            // Users will not stare at black screen and actual image will appear gradually
1714            if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) {
1715
1716                slide.width  = slide.opts.width;
1717                slide.height = slide.opts.height;
1718
1719                slide.$ghost = $('<img />')
1720                    .one('error', function() {
1721
1722                        $(this).remove();
1723
1724                        slide.$ghost = null;
1725
1726                        self.setBigImage( slide );
1727
1728                    })
1729                    .one('load', function() {
1730
1731                        self.afterLoad( slide );
1732
1733                        self.setBigImage( slide );
1734
1735                    })
1736                    .addClass( 'fancybox-image' )
1737                    .appendTo( slide.$content )
1738                    .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) );
1739
1740            } else {
1741
1742                self.setBigImage( slide );
1743
1744            }
1745
1746        },
1747
1748
1749        // Create full-size image
1750        // ======================
1751
1752        setBigImage : function ( slide ) {
1753            var self = this;
1754            var $img = $('<img />');
1755
1756            slide.$image = $img
1757                .one('error', function() {
1758
1759                    self.setError( slide );
1760
1761                })
1762                .one('load', function() {
1763
1764                    // Clear timeout that checks if loading icon needs to be displayed
1765                    clearTimeout( slide.timouts );
1766
1767                    slide.timouts = null;
1768
1769                    if ( self.isClosing ) {
1770                        return;
1771                    }
1772
1773                    slide.width  = this.naturalWidth;
1774                    slide.height = this.naturalHeight;
1775
1776                    if ( slide.opts.image.srcset ) {
1777                        $img.attr( 'sizes', '100vw' ).attr( 'srcset', slide.opts.image.srcset );
1778                    }
1779
1780                    self.hideLoading( slide );
1781
1782                    if ( slide.$ghost ) {
1783
1784                        slide.timouts = setTimeout(function() {
1785                            slide.timouts = null;
1786
1787                            slide.$ghost.hide();
1788
1789                        }, Math.min( 300, Math.max( 1000, slide.height / 1600 ) ) );
1790
1791                    } else {
1792                        self.afterLoad( slide );
1793                    }
1794
1795                })
1796                .addClass( 'fancybox-image' )
1797                .attr('src', slide.src)
1798                .appendTo( slide.$content );
1799
1800            if ( $img[0].complete ) {
1801                  $img.trigger( 'load' );
1802
1803            } else if( $img[0].error ) {
1804                 $img.trigger( 'error' );
1805
1806            } else {
1807
1808                slide.timouts = setTimeout(function() {
1809                    if ( !$img[0].complete && !slide.hasError ) {
1810                        self.showLoading( slide );
1811                    }
1812
1813                }, 100);
1814
1815            }
1816
1817        },
1818
1819
1820        // Create iframe wrapper, iframe and bindings
1821        // ==========================================
1822
1823        setIframe : function( slide ) {
1824            var self	= this,
1825                opts    = slide.opts.iframe,
1826                $slide	= slide.$slide,
1827                $iframe;
1828
1829            slide.$content = $('<div class="fancybox-content' + ( opts.preload ? ' fancybox-is-hidden' : '' ) + '"></div>')
1830                .css( opts.css )
1831                .appendTo( $slide );
1832
1833            $iframe = $( opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) )
1834                .attr( opts.attr )
1835                .appendTo( slide.$content );
1836
1837            if ( opts.preload ) {
1838
1839                self.showLoading( slide );
1840
1841                // Unfortunately, it is not always possible to determine if iframe is successfully loaded
1842                // (due to browser security policy)
1843
1844                $iframe.on('load.fb error.fb', function(e) {
1845                    this.isReady = 1;
1846
1847                    slide.$slide.trigger( 'refresh' );
1848
1849                    self.afterLoad( slide );
1850                });
1851
1852                // Recalculate iframe content size
1853                // ===============================
1854
1855                $slide.on('refresh.fb', function() {
1856                    var $wrap = slide.$content,
1857                        $contents,
1858                        $body,
1859                        scrollWidth,
1860                        frameWidth,
1861                        frameHeight;
1862
1863                    if ( $iframe[0].isReady !== 1 ) {
1864                        return;
1865                    }
1866
1867                    // Check if content is accessible,
1868                    // it will fail if frame is not with the same origin
1869
1870                    try {
1871                        $contents = $iframe.contents();
1872                        $body     = $contents.find('body');
1873
1874                    } catch (ignore) {}
1875
1876                    // Calculate dimensions for the wrapper
1877                    if ( $body && $body.length && !( opts.css.width !== undefined && opts.css.height !== undefined ) ) {
1878
1879                        scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth;
1880
1881                        frameWidth	= Math.ceil( $body.outerWidth(true) + ( $wrap.width() - scrollWidth ) );
1882                        frameHeight	= Math.ceil( $body.outerHeight(true) );
1883
1884                        // Resize wrapper to fit iframe content
1885                        $wrap.css({
1886                            'width'  : opts.css.width  === undefined ? frameWidth  + ( $wrap.outerWidth()  - $wrap.innerWidth() )  : opts.css.width,
1887                            'height' : opts.css.height === undefined ? frameHeight + ( $wrap.outerHeight() - $wrap.innerHeight() ) : opts.css.height
1888                        });
1889
1890                    }
1891
1892                    $wrap.removeClass( 'fancybox-is-hidden' );
1893
1894                });
1895
1896            } else {
1897
1898                this.afterLoad( slide );
1899
1900            }
1901
1902            $iframe.attr( 'src', slide.src );
1903
1904            if ( slide.opts.smallBtn === true ) {
1905                slide.$content.prepend( self.translate( slide, slide.opts.btnTpl.smallBtn ) );
1906            }
1907
1908            // Remove iframe if closing or changing gallery item
1909            $slide.one( 'onReset', function () {
1910
1911                // This helps IE not to throw errors when closing
1912                try {
1913
1914                    $( this ).find( 'iframe' ).hide().attr( 'src', '//about:blank' );
1915
1916                } catch ( ignore ) {}
1917
1918                $( this ).empty();
1919
1920                slide.isLoaded = false;
1921
1922            });
1923
1924        },
1925
1926
1927        // Wrap and append content to the slide
1928        // ======================================
1929
1930        setContent : function ( slide, content ) {
1931
1932            var self = this;
1933
1934            if ( self.isClosing ) {
1935                return;
1936            }
1937
1938            self.hideLoading( slide );
1939
1940            slide.$slide.empty();
1941
1942            if ( isQuery( content ) && content.parent().length ) {
1943
1944                // If content is a jQuery object, then it will be moved to the slide.
1945                // The placeholder is created so we will know where to put it back.
1946                // If user is navigating gallery fast, then the content might be already inside fancyBox
1947                // =====================================================================================
1948
1949                // Make sure content is not already moved to fancyBox
1950                content.parent( '.fancybox-slide--inline' ).trigger( 'onReset' );
1951
1952                // Create temporary element marking original place of the content
1953                slide.$placeholder = $( '<div></div>' ).hide().insertAfter( content );
1954
1955                // Make sure content is visible
1956                content.css('display', 'inline-block');
1957
1958            } else if ( !slide.hasError ) {
1959
1960                // If content is just a plain text, try to convert it to html
1961                if ( $.type( content ) === 'string' ) {
1962                    content = $('<div>').append( $.trim( content ) ).contents();
1963
1964                    // If we have text node, then add wrapping element to make vertical alignment work
1965                    if ( content[0].nodeType === 3 ) {
1966                        content = $('<div>').html( content );
1967                    }
1968                }
1969
1970                // If "filter" option is provided, then filter content
1971                if ( slide.opts.filter ) {
1972                    content = $('<div>').html( content ).find( slide.opts.filter );
1973                }
1974
1975            }
1976
1977            slide.$slide.one('onReset', function () {
1978
1979                // Put content back
1980                if ( slide.$placeholder ) {
1981                    slide.$placeholder.after( content.hide() ).remove();
1982
1983                    slide.$placeholder = null;
1984                }
1985
1986                // Remove custom close button
1987                if ( slide.$smallBtn ) {
1988                    slide.$smallBtn.remove();
1989
1990                    slide.$smallBtn = null;
1991                }
1992
1993                // Remove content and mark slide as not loaded
1994                if ( !slide.hasError ) {
1995                    $(this).empty();
1996
1997                    slide.isLoaded = false;
1998                }
1999
2000            });
2001
2002            slide.$content = $( content ).appendTo( slide.$slide );
2003
2004            if ( slide.opts.smallBtn && !slide.$smallBtn ) {
2005                slide.$smallBtn = $( self.translate( slide, slide.opts.btnTpl.smallBtn ) ).appendTo( slide.$content );
2006            }
2007
2008            this.afterLoad( slide );
2009        },
2010
2011        // Display error message
2012        // =====================
2013
2014        setError : function ( slide ) {
2015
2016            slide.hasError = true;
2017
2018            slide.$slide.removeClass( 'fancybox-slide--' + slide.type );
2019
2020            this.setContent( slide, this.translate( slide, slide.opts.errorTpl ) );
2021
2022        },
2023
2024
2025        // Show loading icon inside the slide
2026        // ==================================
2027
2028        showLoading : function( slide ) {
2029
2030            var self = this;
2031
2032            slide = slide || self.current;
2033
2034            if ( slide && !slide.$spinner ) {
2035                slide.$spinner = $( self.opts.spinnerTpl ).appendTo( slide.$slide );
2036            }
2037
2038        },
2039
2040        // Remove loading icon from the slide
2041        // ==================================
2042
2043        hideLoading : function( slide ) {
2044
2045            var self = this;
2046
2047            slide = slide || self.current;
2048
2049            if ( slide && slide.$spinner ) {
2050                slide.$spinner.remove();
2051
2052                delete slide.$spinner;
2053            }
2054
2055        },
2056
2057
2058        // Adjustments after slide content has been loaded
2059        // ===============================================
2060
2061        afterLoad : function( slide ) {
2062
2063            var self = this;
2064
2065            if ( self.isClosing ) {
2066                return;
2067            }
2068
2069            slide.isLoading = false;
2070            slide.isLoaded  = true;
2071
2072            self.trigger( 'afterLoad', slide );
2073
2074            self.hideLoading( slide );
2075
2076            if ( slide.opts.protect && slide.$content && !slide.hasError ) {
2077
2078                // Disable right click
2079                slide.$content.on( 'contextmenu.fb', function( e ) {
2080                     if ( e.button == 2 ) {
2081                         e.preventDefault();
2082                     }
2083
2084                    return true;
2085                });
2086
2087                // Add fake element on top of the image
2088                // This makes a bit harder for user to select image
2089                if ( slide.type === 'image' ) {
2090                    $( '<div class="fancybox-spaceball"></div>' ).appendTo( slide.$content );
2091                }
2092
2093            }
2094
2095            self.revealContent( slide );
2096
2097        },
2098
2099
2100        // Make content visible
2101        // This method is called right after content has been loaded or
2102        // user navigates gallery and transition should start
2103        // ============================================================
2104
2105        revealContent : function( slide ) {
2106
2107            var self   = this;
2108            var $slide = slide.$slide;
2109
2110            var effect, effectClassName, duration, opacity, end, start = false;
2111
2112            effect   = slide.opts[ self.firstRun ? 'animationEffect'   : 'transitionEffect' ];
2113            duration = slide.opts[ self.firstRun ? 'animationDuration' : 'transitionDuration' ];
2114
2115            duration = parseInt( slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10 );
2116
2117            if ( slide.isMoved || slide.pos !== self.currPos || !duration ) {
2118                effect = false;
2119            }
2120
2121            // Check if can zoom
2122            if ( effect === 'zoom' && !( slide.pos === self.currPos && duration && slide.type === 'image' && !slide.hasError && ( start = self.getThumbPos( slide ) ) ) ) {
2123                effect = 'fade';
2124            }
2125
2126
2127            // Zoom animation
2128            // ==============
2129
2130            if ( effect === 'zoom' ) {
2131                end = self.getFitPos( slide );
2132
2133                end.scaleX = Math.round( (end.width  / start.width)  * 100 ) / 100;
2134                end.scaleY = Math.round( (end.height / start.height) * 100 ) / 100;
2135
2136                delete end.width;
2137                delete end.height;
2138
2139                // Check if we need to animate opacity
2140                opacity = slide.opts.zoomOpacity;
2141
2142                if ( opacity == 'auto' ) {
2143                    opacity = Math.abs( slide.width / slide.height - start.width / start.height ) > 0.1;
2144                }
2145
2146                if ( opacity ) {
2147                    start.opacity = 0.1;
2148                    end.opacity   = 1;
2149                }
2150
2151                // Draw image at start position
2152                $.fancybox.setTranslate( slide.$content.removeClass( 'fancybox-is-hidden' ), start );
2153
2154                forceRedraw( slide.$content );
2155
2156                // Start animation
2157                $.fancybox.animate( slide.$content, end, duration, function() {
2158                    self.complete();
2159                });
2160
2161                return;
2162            }
2163
2164
2165            self.updateSlide( slide );
2166
2167
2168            // Simply show content
2169            // ===================
2170
2171            if ( !effect ) {
2172                forceRedraw( $slide );
2173
2174                slide.$content.removeClass( 'fancybox-is-hidden' );
2175
2176                if ( slide.pos === self.currPos ) {
2177                    self.complete();
2178                }
2179
2180                return;
2181            }
2182
2183            $.fancybox.stop( $slide );
2184
2185            effectClassName = 'fancybox-animated fancybox-slide--' + ( slide.pos > self.prevPos ? 'next' : 'previous' ) + ' fancybox-fx-' + effect;
2186
2187            $slide.removeAttr( 'style' ).removeClass( 'fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ).addClass( effectClassName );
2188
2189            slide.$content.removeClass( 'fancybox-is-hidden' );
2190
2191            //Force reflow for CSS3 transitions
2192            forceRedraw( $slide );
2193
2194            $.fancybox.animate( $slide, 'fancybox-slide--current', duration, function(e) {
2195                $slide.removeClass( effectClassName ).removeAttr( 'style' );
2196
2197                if ( slide.pos === self.currPos ) {
2198                    self.complete();
2199                }
2200
2201            }, true);
2202
2203        },
2204
2205
2206        // Check if we can and have to zoom from thumbnail
2207        //================================================
2208
2209        getThumbPos : function( slide ) {
2210
2211            var self = this;
2212            var rez  = false;
2213
2214            // Check if element is inside the viewport by at least 1 pixel
2215            var isElementVisible = function( $el ) {
2216                var element = $el[0];
2217
2218                var elementRect = element.getBoundingClientRect();
2219                var parentRects = [];
2220
2221                var visibleInAllParents;
2222
2223                while ( element.parentElement !== null ) {
2224                    if ( $(element.parentElement).css('overflow') === 'hidden'  || $(element.parentElement).css('overflow') === 'auto' ) {
2225                        parentRects.push(element.parentElement.getBoundingClientRect());
2226                    }
2227
2228                    element = element.parentElement;
2229                }
2230
2231                visibleInAllParents = parentRects.every(function(parentRect){
2232                    var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left);
2233                    var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top);
2234
2235                    return visiblePixelX > 0 && visiblePixelY > 0;
2236                });
2237
2238                return visibleInAllParents &&
2239                    elementRect.bottom > 0 && elementRect.right > 0 &&
2240                    elementRect.left < $(window).width() && elementRect.top < $(window).height();
2241            };
2242
2243            var $thumb   = slide.opts.$thumb;
2244            var thumbPos = $thumb ? $thumb.offset() : 0;
2245            var slidePos;
2246
2247            if ( thumbPos && $thumb[0].ownerDocument === document && isElementVisible( $thumb ) ) {
2248                slidePos = self.$refs.stage.offset();
2249
2250                rez = {
2251                    top    : thumbPos.top  - slidePos.top  + parseFloat( $thumb.css( "border-top-width" ) || 0 ),
2252                    left   : thumbPos.left - slidePos.left + parseFloat( $thumb.css( "border-left-width" ) || 0 ),
2253                    width  : $thumb.width(),
2254                    height : $thumb.height(),
2255                    scaleX : 1,
2256                    scaleY : 1
2257                };
2258            }
2259
2260            return rez;
2261        },
2262
2263
2264        // Final adjustments after current gallery item is moved to position
2265        // and it`s content is loaded
2266        // ==================================================================
2267
2268        complete : function() {
2269
2270            var self = this;
2271
2272            var current = self.current;
2273            var slides  = {};
2274
2275            if ( current.isMoved || !current.isLoaded || current.isComplete ) {
2276                return;
2277            }
2278
2279            current.isComplete = true;
2280
2281            current.$slide.siblings().trigger( 'onReset' );
2282
2283            // Trigger any CSS3 transiton inside the slide
2284            forceRedraw( current.$slide );
2285
2286            current.$slide.addClass( 'fancybox-slide--complete' );
2287
2288            // Remove unnecessary slides
2289            $.each( self.slides, function( key, slide ) {
2290                if ( slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1 ) {
2291                    slides[ slide.pos ] = slide;
2292
2293                } else if ( slide ) {
2294
2295                    $.fancybox.stop( slide.$slide );
2296
2297                    slide.$slide.unbind().remove();
2298                }
2299            });
2300
2301            self.slides = slides;
2302
2303            self.updateCursor();
2304
2305            self.trigger( 'afterShow' );
2306
2307            // Try to focus on the first focusable element
2308            if ( $( document.activeElement ).is( '[disabled]' ) || ( current.opts.autoFocus && !( current.type == 'image' || current.type === 'iframe' ) ) ) {
2309                self.focus();
2310            }
2311
2312        },
2313
2314
2315        // Preload next and previous slides
2316        // ================================
2317
2318        preload : function() {
2319            var self = this;
2320            var next, prev;
2321
2322            if ( self.group.length < 2 ) {
2323                return;
2324            }
2325
2326            next  = self.slides[ self.currPos + 1 ];
2327            prev  = self.slides[ self.currPos - 1 ];
2328
2329            if ( next && next.type === 'image' ) {
2330                self.loadSlide( next );
2331            }
2332
2333            if ( prev && prev.type === 'image' ) {
2334                self.loadSlide( prev );
2335            }
2336
2337        },
2338
2339
2340        // Try to find and focus on the first focusable element
2341        // ====================================================
2342
2343        focus : function() {
2344            var current = this.current;
2345            var $el;
2346
2347            if ( this.isClosing ) {
2348                return;
2349            }
2350
2351            // Skip for images and iframes
2352            $el = current && current.isComplete ? current.$slide.find('button,:input,[tabindex],a').filter(':not([disabled]):visible:first') : null;
2353            $el = $el && $el.length ? $el : this.$refs.container;
2354
2355            $el.focus();
2356        },
2357
2358
2359        // Activates current instance - brings container to the front and enables keyboard,
2360        // notifies other instances about deactivating
2361        // =================================================================================
2362
2363        activate : function () {
2364            var self = this;
2365
2366            // Deactivate all instances
2367            $( '.fancybox-container' ).each(function () {
2368                var instance = $(this).data( 'FancyBox' );
2369
2370                // Skip self and closing instances
2371                if (instance && instance.uid !== self.uid && !instance.isClosing) {
2372                    instance.trigger( 'onDeactivate' );
2373                }
2374
2375            });
2376
2377            if ( self.current ) {
2378                if ( self.$refs.container.index() > 0 ) {
2379                    self.$refs.container.prependTo( document.body );
2380                }
2381
2382                self.updateControls();
2383            }
2384
2385            self.trigger( 'onActivate' );
2386
2387            self.addEvents();
2388
2389        },
2390
2391
2392        // Start closing procedure
2393        // This will start "zoom-out" animation if needed and clean everything up afterwards
2394        // =================================================================================
2395
2396        close : function( e, d ) {
2397
2398            var self    = this;
2399            var current = self.current;
2400
2401            var effect, duration;
2402            var $what, opacity, start, end;
2403
2404            var done = function() {
2405                self.cleanUp( e );
2406            };
2407
2408            if ( self.isClosing ) {
2409                return false;
2410            }
2411
2412            self.isClosing = true;
2413
2414            // If beforeClose callback prevents closing, make sure content is centered
2415            if ( self.trigger( 'beforeClose', e ) === false ) {
2416                self.isClosing = false;
2417
2418                requestAFrame(function() {
2419                    self.update();
2420                });
2421
2422                return false;
2423            }
2424
2425            // Remove all events
2426            // If there are multiple instances, they will be set again by "activate" method
2427            self.removeEvents();
2428
2429            if ( current.timouts ) {
2430                clearTimeout( current.timouts );
2431            }
2432
2433            $what    = current.$content;
2434            effect   = current.opts.animationEffect;
2435            duration = $.isNumeric( d ) ? d : ( effect ? current.opts.animationDuration : 0 );
2436
2437            // Remove other slides
2438            current.$slide.off( transitionEnd ).removeClass( 'fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated' );
2439
2440            current.$slide.siblings().trigger( 'onReset' ).remove();
2441
2442            // Trigger animations
2443            if ( duration ) {
2444                self.$refs.container.removeClass( 'fancybox-is-open' ).addClass( 'fancybox-is-closing' );
2445            }
2446
2447            // Clean up
2448            self.hideLoading( current );
2449
2450            self.hideControls();
2451
2452            self.updateCursor();
2453
2454            // Check if possible to zoom-out
2455            if ( effect === 'zoom' && !( e !== true && $what && duration && current.type === 'image' && !current.hasError && ( end = self.getThumbPos( current ) ) ) ) {
2456                effect = 'fade';
2457            }
2458            $(".fancybox-container").remove();
2459            if ( effect === 'zoom' ) {
2460                $.fancybox.stop( $what );
2461
2462                start = $.fancybox.getTranslate( $what );
2463
2464                start.width  = start.width  * start.scaleX;
2465                start.height = start.height * start.scaleY;
2466
2467                // Check if we need to animate opacity
2468                opacity = current.opts.zoomOpacity;
2469
2470                if ( opacity == 'auto' ) {
2471                    opacity = Math.abs( current.width / current.height - end.width / end.height ) > 0.1;
2472                }
2473
2474                if ( opacity ) {
2475                    end.opacity = 0;
2476                }
2477
2478                start.scaleX = start.width  / end.width;
2479                start.scaleY = start.height / end.height;
2480
2481                start.width  = end.width;
2482                start.height = end.height;
2483
2484                $.fancybox.setTranslate( current.$content, start );
2485
2486                $.fancybox.animate( current.$content, end, duration, done );
2487
2488                return true;
2489            }
2490
2491            if ( effect && duration ) {
2492
2493                // If skip animation
2494                if ( e === true ) {
2495                    setTimeout( done, duration );
2496
2497                } else {
2498                    $.fancybox.animate( current.$slide.removeClass( 'fancybox-slide--current' ), 'fancybox-animated fancybox-slide--previous fancybox-fx-' + effect, duration, done );
2499                }
2500
2501            } else {
2502                done();
2503            }
2504
2505            return true;
2506        },
2507
2508
2509        // Final adjustments after removing the instance
2510        // =============================================
2511
2512        cleanUp : function( e ) {
2513            var self = this,
2514                instance;
2515
2516            self.current.$slide.trigger( 'onReset' );
2517
2518            self.$refs.container.empty().remove();
2519
2520            self.trigger( 'afterClose', e );
2521
2522            // Place back focus
2523            if ( self.$lastFocus && !!!self.current.focusBack ) {
2524                self.$lastFocus.focus();
2525            }
2526
2527            self.current = null;
2528
2529            // Check if there are other instances
2530            instance = $.fancybox.getInstance();
2531
2532            if ( instance ) {
2533                instance.activate();
2534
2535            } else {
2536
2537                $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft );
2538
2539                $( 'html' ).removeClass( 'fancybox-enabled' );
2540
2541                $( '#fancybox-style-noscroll' ).remove();
2542            }
2543
2544        },
2545
2546
2547        // Call callback and trigger an event
2548        // ==================================
2549
2550        trigger : function( name, slide ) {
2551            var args  = Array.prototype.slice.call(arguments, 1),
2552                self  = this,
2553                obj   = slide && slide.opts ? slide : self.current,
2554                rez;
2555
2556            if ( obj ) {
2557                args.unshift( obj );
2558
2559            } else {
2560                obj = self;
2561            }
2562
2563            args.unshift( self );
2564
2565            if ( $.isFunction( obj.opts[ name ] ) ) {
2566                rez = obj.opts[ name ].apply( obj, args );
2567            }
2568
2569            if ( rez === false ) {
2570                return rez;
2571            }
2572
2573            if ( name === 'afterClose' ) {
2574                $D.trigger( name + '.fb', args );
2575
2576            } else {
2577                self.$refs.container.trigger( name + '.fb', args );
2578            }
2579
2580        },
2581
2582
2583        // Update infobar values, navigation button states and reveal caption
2584        // ==================================================================
2585
2586        updateControls : function ( force ) {
2587
2588            var self = this;
2589
2590            var current  = self.current;
2591            var index    = current.index;
2592            var opts     = current.opts;
2593            var caption  = opts.caption;
2594            var $caption = self.$refs.caption;
2595
2596            // Recalculate content dimensions
2597            current.$slide.trigger( 'refresh' );
2598
2599            self.$caption = caption && caption.length ? $caption.html( caption ) : null;
2600
2601            if ( !self.isHiddenControls ) {
2602                self.showControls();
2603            }
2604
2605            // Update info and navigation elements
2606            $('[data-fancybox-count]').html( self.group.length );
2607            $('[data-fancybox-index]').html( index + 1 );
2608
2609            $('[data-fancybox-prev]').prop('disabled', ( !opts.loop && index <= 0 ) );
2610            $('[data-fancybox-next]').prop('disabled', ( !opts.loop && index >= self.group.length - 1 ) );
2611
2612        },
2613
2614        // Hide toolbar and caption
2615        // ========================
2616
2617        hideControls : function () {
2618
2619            this.isHiddenControls = true;
2620
2621      //      this.$refs.container.removeClass('fancybox-show-infobar fancybox-show-toolbar fancybox-show-caption fancybox-show-nav');
2622	 
2623	 		  this.$refs.container.removeClass('fancybox-show-infobar fancybox-show-toolbar fancybox-show-nav');  // byb
2624
2625        },
2626
2627        showControls : function() {
2628
2629            var self = this;
2630            var opts = self.current ? self.current.opts : self.opts;
2631            var $container = self.$refs.container;
2632
2633            self.isHiddenControls   = false;
2634            self.idleSecondsCounter = 0;
2635
2636            $container
2637                .toggleClass('fancybox-show-toolbar', !!( opts.toolbar && opts.buttons ) )
2638                .toggleClass('fancybox-show-infobar', !!( opts.infobar && self.group.length > 1 ) )
2639                .toggleClass('fancybox-show-nav',     !!( opts.arrows && self.group.length > 1 ) )
2640                .toggleClass('fancybox-is-modal',     !!opts.modal );
2641
2642            if ( self.$caption ) {
2643                $container.addClass( 'fancybox-show-caption');
2644
2645            } else {
2646               $container.removeClass( 'fancybox-show-caption' );
2647           }
2648
2649       },
2650
2651
2652       // Toggle toolbar and caption
2653       // ==========================
2654
2655       toggleControls : function() {
2656
2657           if ( this.isHiddenControls ) {
2658               this.showControls();
2659
2660           } else {
2661               this.hideControls();
2662           }
2663
2664       },
2665
2666
2667    });
2668
2669
2670    $.fancybox = {
2671
2672        version  : "3.1.20",
2673        defaults : defaults,
2674
2675
2676        // Get current instance and execute a command.
2677        //
2678        // Examples of usage:
2679        //
2680        //   $instance = $.fancybox.getInstance();
2681        //   $.fancybox.getInstance().jumpTo( 1 );
2682        //   $.fancybox.getInstance( 'jumpTo', 1 );
2683        //   $.fancybox.getInstance( function() {
2684        //       console.info( this.currIndex );
2685        //   });
2686        // ======================================================
2687
2688        getInstance : function ( command ) {
2689            var instance = $('.fancybox-container:not(".fancybox-is-closing"):first').data( 'FancyBox' );
2690            var args     = Array.prototype.slice.call(arguments, 1);
2691
2692            if ( instance instanceof FancyBox ) {
2693
2694                if ( $.type( command ) === 'string' ) {
2695                    instance[ command ].apply( instance, args );
2696
2697                } else if ( $.type( command ) === 'function' ) {
2698                    command.apply( instance, args );
2699
2700                }
2701
2702                return instance;
2703            }
2704
2705            return false;
2706
2707        },
2708
2709
2710        // Create new instance
2711        // ===================
2712
2713        open : function ( items, opts, index ) {
2714            return new FancyBox( items, opts, index );
2715        },
2716
2717
2718        // Close current or all instances
2719        // ==============================
2720
2721        close : function ( all ) {
2722            var instance = this.getInstance();
2723
2724            if ( instance ) {
2725                instance.close();
2726
2727                // Try to find and close next instance
2728
2729                if ( all === true ) {
2730                    this.close();
2731                }
2732            }
2733
2734        },
2735
2736        // Close instances and unbind all events
2737        // ==============================
2738
2739        destroy : function() {
2740
2741            this.close( true );
2742
2743            $D.off( 'click.fb-start' );
2744
2745        },
2746
2747
2748        // Try to detect mobile devices
2749        // ============================
2750
2751        isMobile : document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent),
2752
2753
2754        // Detect if 'translate3d' support is available
2755        // ============================================
2756
2757        use3d : (function() {
2758            var div = document.createElement('div');
2759
2760            return window.getComputedStyle && window.getComputedStyle( div ).getPropertyValue('transform') && !(document.documentMode && document.documentMode < 11);
2761        }()),
2762
2763
2764        // Helper function to get current visual state of an element
2765        // returns array[ top, left, horizontal-scale, vertical-scale, opacity ]
2766        // =====================================================================
2767
2768        getTranslate : function( $el ) {
2769            var matrix;
2770
2771            if ( !$el || !$el.length ) {
2772                return false;
2773            }
2774
2775            matrix  = $el.eq( 0 ).css('transform');
2776
2777            if ( matrix && matrix.indexOf( 'matrix' ) !== -1 ) {
2778                matrix = matrix.split('(')[1];
2779                matrix = matrix.split(')')[0];
2780                matrix = matrix.split(',');
2781            } else {
2782                matrix = [];
2783            }
2784
2785            if ( matrix.length ) {
2786
2787                // If IE
2788                if ( matrix.length > 10 ) {
2789                    matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ];
2790
2791                } else {
2792                    matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]];
2793                }
2794
2795                matrix = matrix.map(parseFloat);
2796
2797            } else {
2798                matrix = [ 0, 0, 1, 1 ];
2799
2800                var transRegex = /\.*translate\((.*)px,(.*)px\)/i;
2801                var transRez = transRegex.exec( $el.eq( 0 ).attr('style') );
2802
2803                if ( transRez ) {
2804                    matrix[ 0 ] = parseFloat( transRez[2] );
2805                    matrix[ 1 ] = parseFloat( transRez[1] );
2806                }
2807            }
2808
2809            return {
2810                top     : matrix[ 0 ],
2811                left    : matrix[ 1 ],
2812                scaleX  : matrix[ 2 ],
2813                scaleY  : matrix[ 3 ],
2814                opacity : parseFloat( $el.css('opacity') ),
2815                width   : $el.width(),
2816                height  : $el.height()
2817            };
2818
2819        },
2820
2821
2822        // Shortcut for setting "translate3d" properties for element
2823        // Can set be used to set opacity, too
2824        // ========================================================
2825
2826        setTranslate : function( $el, props ) {
2827            var str  = '';
2828            var css  = {};
2829
2830            if ( !$el || !props ) {
2831                return;
2832            }
2833
2834            if ( props.left !== undefined || props.top !== undefined ) {
2835                str = ( props.left === undefined ? $el.position().left : props.left )  + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px';
2836
2837                if ( this.use3d ) {
2838                    str = 'translate3d(' + str + ', 0px)';
2839
2840                } else {
2841                    str = 'translate(' + str + ')';
2842                }
2843            }
2844
2845            if ( props.scaleX !== undefined && props.scaleY !== undefined ) {
2846                str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')';
2847            }
2848
2849            if ( str.length ) {
2850                css.transform = str;
2851            }
2852
2853            if ( props.opacity !== undefined ) {
2854                css.opacity = props.opacity;
2855            }
2856
2857            if ( props.width !== undefined ) {
2858                css.width = props.width;
2859            }
2860
2861            if ( props.height !== undefined ) {
2862                css.height = props.height;
2863            }
2864
2865            return $el.css( css );
2866        },
2867
2868
2869        // Simple CSS transition handler
2870        // =============================
2871
2872        animate : function ( $el, to, duration, callback, leaveAnimationName ) {
2873            var event = transitionEnd || 'transitionend';
2874
2875            if ( $.isFunction( duration ) ) {
2876                callback = duration;
2877                duration = null;
2878            }
2879
2880            if ( !$.isPlainObject( to ) ) {
2881                $el.removeAttr('style');
2882            }
2883
2884            $el.on( event, function(e) {
2885
2886                // Skip events from child elements and z-index change
2887                if ( e && e.originalEvent && ( !$el.is( e.originalEvent.target ) || e.originalEvent.propertyName == 'z-index' ) ) {
2888                    return;
2889                }
2890
2891                $el.off( event );
2892
2893                if ( $.isPlainObject( to ) ) {
2894
2895                    if ( to.scaleX !== undefined && to.scaleY !== undefined ) {
2896                        $el.css( 'transition-duration', '0ms' );
2897
2898                        to.width  = $el.width()  * to.scaleX;
2899                        to.height = $el.height() * to.scaleY;
2900
2901                        to.scaleX = 1;
2902                        to.scaleY = 1;
2903
2904                        $.fancybox.setTranslate( $el, to );
2905                    }
2906
2907                } else if ( leaveAnimationName !== true ) {
2908                    $el.removeClass( to );
2909                }
2910
2911                if ( $.isFunction( callback ) ) {
2912                    callback( e );
2913                }
2914
2915            });
2916
2917            if ( $.isNumeric( duration ) ) {
2918                $el.css( 'transition-duration', duration + 'ms' );
2919            }
2920
2921            if ( $.isPlainObject( to ) ) {
2922                $.fancybox.setTranslate( $el, to );
2923
2924            } else {
2925                $el.addClass( to );
2926            }
2927
2928            $el.data("timer", setTimeout(function() {
2929                $el.trigger( 'transitionend' );
2930            }, duration + 16));
2931
2932        },
2933
2934        stop : function( $el ) {
2935            clearTimeout( $el.data("timer") );
2936
2937            $el.off( transitionEnd );
2938        }
2939
2940    };
2941
2942
2943    // Default click handler for "fancyboxed" links
2944    // ============================================
2945
2946    function _run( e ) {
2947        var target	= e.currentTarget,
2948            opts	= e.data ? e.data.options : {},
2949            items	= e.data ? e.data.items : [],
2950            value	= $(target).attr( 'data-fancybox' ) || '',
2951            index	= 0;
2952
2953        e.preventDefault();
2954        e.stopPropagation();
2955
2956        // Get all related items and find index for clicked one
2957        if ( value ) {
2958            items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox="' + value + '"]' );
2959            index = items.index( target );
2960
2961            // Sometimes current item can not be found
2962            // (for example, when slider clones items)
2963            if ( index < 0 ) {
2964                index = 0;
2965            }
2966
2967        } else {
2968            items = [ target ];
2969        }
2970
2971        $.fancybox.open( items, opts, index );
2972    }
2973
2974
2975    // Create a jQuery plugin
2976    // ======================
2977
2978    $.fn.fancybox = function (options) {
2979        var selector;
2980
2981        options  = options || {};
2982        selector = options.selector || false;
2983
2984        if ( selector ) {
2985
2986            $( 'body' ).off( 'click.fb-start', selector ).on( 'click.fb-start', selector, {
2987                items   : $( selector ),
2988                options : options
2989            }, _run );
2990
2991        } else {
2992
2993            this.off( 'click.fb-start' ).on( 'click.fb-start', {
2994                items   : this,
2995                options : options
2996            }, _run);
2997
2998        }
2999
3000        return this;
3001    };
3002
3003
3004    // Self initializing plugin
3005    // ========================
3006
3007    $D.on( 'click.fb-start', '[data-fancybox]', _run );
3008
3009}( window, document, window.jQuery ));
3010
3011// ==========================================================================
3012//
3013// Media
3014// Adds additional media type support
3015//
3016// ==========================================================================
3017;(function ($) {
3018
3019	'use strict';
3020
3021	// Formats matching url to final form
3022
3023	var format = function (url, rez, params) {
3024		if ( !url ) {
3025			return;
3026		}
3027
3028		params = params || '';
3029
3030		if ( $.type(params) === "object" ) {
3031			params = $.param(params, true);
3032		}
3033
3034		$.each(rez, function (key, value) {
3035			url = url.replace('$' + key, value || '');
3036		});
3037
3038		if (params.length) {
3039			url += (url.indexOf('?') > 0 ? '&' : '?') + params;
3040		}               
3041		return url;
3042	};
3043
3044	// Object containing properties for each media type
3045
3046	var defaults = {
3047		youtube : {
3048			matcher : /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i,
3049			params  : {
3050				autoplay : 1,
3051				autohide : 1,
3052				fs  : 1,
3053				rel : 0,
3054				hd  : 1,
3055				wmode : 'transparent',
3056				enablejsapi : 1,
3057				html5 : 1
3058			},
3059			paramPlace : 8,
3060			type  : 'iframe',
3061			url   : '//www.youtube.com/embed/$4',
3062			thumb : '//img.youtube.com/vi/$4/hqdefault.jpg'
3063		},
3064
3065		vimeo : {
3066			matcher : /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/,
3067			params  : {
3068				autoplay : 1,
3069				hd : 1,
3070				show_title    : 1,
3071				show_byline   : 1,
3072				show_portrait : 0,
3073				fullscreen    : 1,
3074				api : 1
3075			},
3076			paramPlace : 3,
3077			type : 'iframe',
3078			url : '//player.vimeo.com/video/$2'
3079		},
3080        facebook: {
3081                matcher: /^.+facebook.com\/(.*\/)?([\d]+)(.*)?/,  
3082                params  : {
3083                    autoplay : 1,
3084                    hd : 1,
3085                    show_title    : 1,
3086                    show_byline   : 1,
3087                    show_portrait : 0,
3088                    fullscreen    : 1,
3089                    api : 1,                       
3090                },
3091                paramPlace : 3,                              
3092                type: "iframe",
3093                url: "//facebook.com/plugins/video.php?href=https://www.facebook.com/$1$2$3"
3094            },
3095		metacafe : {
3096			matcher : /metacafe.com\/watch\/(\d+)\/(.*)?/,
3097			type    : 'iframe',
3098			url     : '//www.metacafe.com/embed/$1/?ap=1'
3099		},
3100
3101		dailymotion : {
3102			matcher : /dailymotion.com\/video\/(.*)\/?(.*)/,
3103			params : {
3104				additionalInfos : 0,
3105				autoStart : 1
3106			},
3107			type : 'iframe',
3108			url  : '//www.dailymotion.com/embed/video/$1'
3109		},
3110
3111		vine : {
3112			matcher : /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/,
3113			type    : 'iframe',
3114			url     : '//vine.co/v/$1/embed/simple'
3115		},
3116
3117		instagram : {
3118			matcher : /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i,
3119			type    : 'image',
3120			url     : '//$1/p/$2/media/?size=l'
3121		},
3122
3123		// Examples:
3124		// http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
3125		// http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
3126		// https://www.google.lv/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572
3127		google_maps : {
3128			matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i,
3129			type    : 'iframe',
3130			url     : function (rez) {
3131				return '//maps.google.' + rez[2] + '/?ll=' + ( rez[9] ? rez[9] + '&z=' + Math.floor(  rez[10]  ) + ( rez[12] ? rez[12].replace(/^\//, "&") : '' )  : rez[12] ) + '&output=' + ( rez[12] && rez[12].indexOf('layer=c') > 0 ? 'svembed' : 'embed' );
3132			}
3133		}
3134	};
3135
3136	$(document).on('onInit.fb', function (e, instance) {
3137
3138		$.each(instance.group, function( i, item ) {
3139
3140			var url	 = item.src || '',
3141				type = false,
3142				media,
3143				thumb,
3144				rez,
3145				params,
3146				urlParams,
3147				o,
3148				provider;
3149
3150			// Skip items that already have content type
3151			if ( item.type ) {
3152				return;
3153			}
3154
3155			media = $.extend( true, {}, defaults, item.opts.media );
3156
3157			// Look for any matching media type
3158			$.each(media, function ( n, el ) {
3159				rez = url.match(el.matcher);
3160				o   = {};
3161				provider = n;
3162
3163				if (!rez) {
3164					return;
3165				}
3166
3167				type = el.type;
3168
3169				if ( el.paramPlace && rez[ el.paramPlace ] ) {
3170					urlParams = rez[ el.paramPlace ];
3171
3172					if ( urlParams[ 0 ] == '?' ) {
3173						urlParams = urlParams.substring(1);
3174					}
3175
3176					urlParams = urlParams.split('&');
3177
3178					for ( var m = 0; m < urlParams.length; ++m ) {
3179						var p = urlParams[ m ].split('=', 2);
3180
3181						if ( p.length == 2 ) {
3182							o[ p[0] ] = decodeURIComponent( p[1].replace(/\+/g, " ") );
3183						}
3184					}
3185				}
3186
3187				params = $.extend( true, {}, el.params, item.opts[ n ], o );
3188
3189				url   = $.type(el.url) === "function" ? el.url.call(this, rez, params, item) : format(el.url, rez, params);
3190				thumb = $.type(el.thumb) === "function" ? el.thumb.call(this, rez, params, item) : format(el.thumb, rez);
3191
3192				if ( provider === 'vimeo' ) {
3193					url = url.replace('&%23', '#');
3194				}
3195
3196				return false;
3197			});
3198
3199			// If it is found, then change content type and update the url
3200
3201			if ( type ) {
3202				item.src  = url;
3203				item.type = type;
3204
3205				if ( !item.opts.thumb && !( item.opts.$thumb && item.opts.$thumb.length ) ) {
3206					item.opts.thumb = thumb;
3207				}
3208
3209				if ( type === 'iframe' ) {
3210					$.extend(true, item.opts, {
3211						iframe : {
3212							preload : false,
3213							attr : {
3214								scrolling : "no"
3215							}
3216						}
3217					});
3218
3219					item.contentProvider = provider;
3220
3221					item.opts.slideClass += ' fancybox-slide--' + ( provider == 'google_maps' ? 'map' : 'video' );
3222				}
3223
3224			} else {
3225
3226				// If no content type is found, then set it to `image` as fallback
3227				item.type = 'image';
3228			}
3229
3230		});
3231
3232	});
3233
3234}(window.jQuery));
3235
3236// ==========================================================================
3237//
3238// Guestures
3239// Adds touch guestures, handles click and tap events
3240//
3241// ==========================================================================
3242;(function (window, document, $) {
3243	'use strict';
3244
3245	var requestAFrame = (function () {
3246        return window.requestAnimationFrame ||
3247                window.webkitRequestAnimationFrame ||
3248                window.mozRequestAnimationFrame ||
3249                window.oRequestAnimationFrame ||
3250                // if all else fails, use setTimeout
3251                function (callback) {
3252                    return window.setTimeout(callback, 1000 / 60);
3253                };
3254    })();
3255
3256
3257    var cancelAFrame = (function () {
3258        return window.cancelAnimationFrame ||
3259                window.webkitCancelAnimationFrame ||
3260                window.mozCancelAnimationFrame ||
3261                window.oCancelAnimationFrame ||
3262                function (id) {
3263                    window.clearTimeout(id);
3264                };
3265    })();
3266
3267
3268	var pointers = function( e ) {
3269		var result = [];
3270
3271		e = e.originalEvent || e || window.e;
3272		e = e.touches && e.touches.length ? e.touches : ( e.changedTouches && e.changedTouches.length ? e.changedTouches : [ e ] );
3273
3274		for ( var key in e ) {
3275
3276			if ( e[ key ].pageX ) {
3277				result.push( { x : e[ key ].pageX, y : e[ key ].pageY } );
3278
3279			} else if ( e[ key ].clientX ) {
3280				result.push( { x : e[ key ].clientX, y : e[ key ].clientY } );
3281			}
3282		}
3283
3284		return result;
3285	};
3286
3287	var distance = function( point2, point1, what ) {
3288		if ( !point1 || !point2 ) {
3289			return 0;
3290		}
3291
3292		if ( what === 'x' ) {
3293			return point2.x - point1.x;
3294
3295		} else if ( what === 'y' ) {
3296			return point2.y - point1.y;
3297		}
3298
3299		return Math.sqrt( Math.pow( point2.x - point1.x, 2 ) + Math.pow( point2.y - point1.y, 2 ) );
3300	};
3301
3302	var isClickable = function( $el ) {
3303		if ( $el.is('a,button,input,select,textarea') || $.isFunction( $el.get(0).onclick ) ) {
3304			return true;
3305		}
3306
3307		// Check for attributes like data-fancybox-next or data-fancybox-close
3308		for ( var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++ ) {
3309            if ( atts[i].nodeName.substr(0, 14) === 'data-fancybox-' ) {
3310                return true;
3311            }
3312        }
3313
3314	 	return false;
3315	};
3316
3317	var hasScrollbars = function( el ) {
3318		var overflowY = window.getComputedStyle( el )['overflow-y'];
3319		var overflowX = window.getComputedStyle( el )['overflow-x'];
3320
3321		var vertical   = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight;
3322		var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth;
3323
3324		return vertical || horizontal;
3325	};
3326
3327	var isScrollable = function ( $el ) {
3328		var rez = false;
3329
3330		while ( true ) {
3331			rez	= hasScrollbars( $el.get(0) );
3332
3333			if ( rez ) {
3334				break;
3335			}
3336
3337			$el = $el.parent();
3338
3339			if ( !$el.length || $el.hasClass( 'fancybox-stage' ) || $el.is( 'body' ) ) {
3340				break;
3341			}
3342		}
3343
3344		return rez;
3345	};
3346
3347
3348	var Guestures = function ( instance ) {
3349		var self = this;
3350
3351		self.instance = instance;
3352
3353		self.$bg        = instance.$refs.bg;
3354		self.$stage     = instance.$refs.stage;
3355		self.$container = instance.$refs.container;
3356
3357		self.destroy();
3358
3359		self.$container.on( 'touchstart.fb.touch mousedown.fb.touch', $.proxy(self, 'ontouchstart') );
3360	};
3361
3362	Guestures.prototype.destroy = function() {
3363		this.$container.off( '.fb.touch' );
3364	};
3365
3366	Guestures.prototype.ontouchstart = function( e ) {
3367		var self = this;
3368
3369		var $target  = $( e.target );
3370		var instance = self.instance;
3371		var current  = instance.current;
3372		var $content = current.$content;
3373
3374		var isTouchDevice = ( e.type == 'touchstart' );
3375
3376		// Do not respond to both events
3377		if ( isTouchDevice ) {
3378	        self.$container.off( 'mousedown.fb.touch' );
3379	    }
3380
3381		// Ignore clicks while zooming or closing
3382		if ( !current || self.instance.isAnimating || self.instance.isClosing ) {
3383			e.stopPropagation();
3384			e.preventDefault();
3385
3386			return;
3387		}
3388
3389		// Ignore right click
3390		if ( e.originalEvent && e.originalEvent.button == 2 ) {
3391			return;
3392		}
3393
3394		// Ignore taping on links, buttons, input elements
3395		if ( !$target.length || isClickable( $target ) || isClickable( $target.parent() ) ) {
3396			return;
3397		}
3398
3399		// Ignore clicks on the scrollbar
3400		if ( e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left ) {
3401			return;
3402		}
3403
3404		self.startPoints = pointers( e );
3405
3406		// Prevent zooming if already swiping
3407		if ( !self.startPoints || ( self.startPoints.length > 1 && instance.isSliding ) ) {
3408			return;
3409		}
3410
3411		self.$target  = $target;
3412		self.$content = $content;
3413		self.canTap   = true;
3414
3415		$(document).off( '.fb.touch' );
3416
3417		$(document).on( isTouchDevice ? 'touchend.fb.touch touchcancel.fb.touch' : 'mouseup.fb.touch mouseleave.fb.touch',  $.proxy(self, "ontouchend"));
3418		$(document).on( isTouchDevice ? 'touchmove.fb.touch' : 'mousemove.fb.touch',  $.proxy(self, "ontouchmove"));
3419
3420		e.stopPropagation();
3421
3422		if ( !(instance.current.opts.touch || instance.canPan() ) || !( $target.is( self.$stage ) || self.$stage.find( $target ).length ) ) {
3423
3424			// Prevent ghosting
3425			if ( $target.is('img') ) {
3426				e.preventDefault();
3427			}
3428
3429			return;
3430		}
3431
3432		if ( !( $.fancybox.isMobile && ( isScrollable( self.$target ) || isScrollable( self.$target.parent() ) ) ) ) {
3433			e.preventDefault();
3434		}
3435
3436		self.canvasWidth  = Math.round( current.$slide[0].clientWidth );
3437		self.canvasHeight = Math.round( current.$slide[0].clientHeight );
3438
3439		self.startTime = new Date().getTime();
3440		self.distanceX = self.distanceY = self.distance = 0;
3441
3442		self.isPanning = false;
3443		self.isSwiping = false;
3444		self.isZooming = false;
3445
3446		self.sliderStartPos  = self.sliderLastPos || { top: 0, left: 0 };
3447		self.contentStartPos = $.fancybox.getTranslate( self.$content );
3448		self.contentLastPos  = null;
3449
3450		if ( self.startPoints.length === 1 && !self.isZooming ) {
3451			self.canTap = !instance.isSliding;
3452
3453			if ( current.type === 'image' && ( self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1 ) ) {
3454
3455				$.fancybox.stop( self.$content );
3456
3457				self.$content.css( 'transition-duration', '0ms' );
3458
3459				self.isPanning = true;
3460
3461			} else {
3462
3463				self.isSwiping = true;
3464			}
3465
3466			self.$container.addClass('fancybox-controls--isGrabbing');
3467		}
3468
3469		if ( self.startPoints.length === 2 && !instance.isAnimating && !current.hasError && current.type === 'image' && ( current.isLoaded || current.$ghost ) ) {
3470			self.isZooming = true;
3471
3472			self.isSwiping = false;
3473			self.isPanning = false;
3474
3475			$.fancybox.stop( self.$content );
3476
3477			self.$content.css( 'transition-duration', '0ms' );
3478
3479			self.centerPointStartX = ( ( self.startPoints[0].x + self.startPoints[1].x ) * 0.5 ) - $(window).scrollLeft();
3480			self.centerPointStartY = ( ( self.startPoints[0].y + self.startPoints[1].y ) * 0.5 ) - $(window).scrollTop();
3481
3482			self.percentageOfImageAtPinchPointX = ( self.centerPointStartX - self.contentStartPos.left ) / self.contentStartPos.width;
3483			self.percentageOfImageAtPinchPointY = ( self.centerPointStartY - self.contentStartPos.top  ) / self.contentStartPos.height;
3484
3485			self.startDistanceBetweenFingers = distance( self.startPoints[0], self.startPoints[1] );
3486		}
3487
3488	};
3489
3490	Guestures.prototype.ontouchmove = function( e ) {
3491
3492		var self = this;
3493
3494		self.newPoints = pointers( e );
3495
3496		if ( $.fancybox.isMobile && ( isScrollable( self.$target ) || isScrollable( self.$target.parent() ) ) ) {
3497			e.stopPropagation();
3498
3499			self.canTap = false;
3500
3501			return;
3502		}
3503
3504		if ( !( self.instance.current.opts.touch || self.instance.canPan() ) || !self.newPoints || !self.newPoints.length ) {
3505			return;
3506		}
3507
3508		self.distanceX = distance( self.newPoints[0], self.startPoints[0], 'x' );
3509		self.distanceY = distance( self.newPoints[0], self.startPoints[0], 'y' );
3510
3511		self.distance = distance( self.newPoints[0], self.startPoints[0] );
3512
3513		// Skip false ontouchmove events (Chrome)
3514		if ( self.distance > 0 ) {
3515
3516			if ( !( self.$target.is( self.$stage ) || self.$stage.find( self.$target ).length ) ) {
3517				return;
3518			}
3519
3520			e.stopPropagation();
3521			e.preventDefault();
3522
3523			if ( self.isSwiping ) {
3524				self.onSwipe();
3525
3526			} else if ( self.isPanning ) {
3527				self.onPan();
3528
3529			} else if ( self.isZooming ) {
3530				self.onZoom();
3531			}
3532
3533		}
3534
3535	};
3536
3537	Guestures.prototype.onSwipe = function() {
3538
3539		var self = this;
3540
3541		var swiping = self.isSwiping;
3542		var left    = self.sliderStartPos.left || 0;
3543		var angle;
3544
3545		if ( swiping === true ) {
3546
3547			if ( Math.abs( self.distance ) > 10 )  {
3548
3549				self.canTap = false;
3550
3551				if ( self.instance.group.length < 2 && self.instance.opts.touch.vertical ) {
3552					self.isSwiping  = 'y';
3553
3554				} else if ( self.instance.isSliding || self.instance.opts.touch.vertical === false || ( self.instance.opts.touch.vertical === 'auto' && $( window ).width() > 800 ) ) {
3555					self.isSwiping  = 'x';
3556
3557				} else {
3558					angle = Math.abs( Math.atan2( self.distanceY, self.distanceX ) * 180 / Math.PI );
3559
3560					self.isSwiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x';
3561				}
3562
3563				self.instance.isSliding = self.isSwiping;
3564
3565				// Reset points to avoid jumping, because we dropped first swipes to calculate the angle
3566				self.startPoints = self.newPoints;
3567
3568				$.each(self.instance.slides, function( index, slide ) {
3569					$.fancybox.stop( slide.$slide );
3570
3571					slide.$slide.css( 'transition-duration', '0ms' );
3572
3573					slide.inTransition = false;
3574
3575					if ( slide.pos === self.instance.current.pos ) {
3576						self.sliderStartPos.left = $.fancybox.getTranslate( slide.$slide ).left;
3577					}
3578				});
3579
3580				//self.instance.current.isMoved = true;
3581
3582				// Stop slideshow
3583				if ( self.instance.SlideShow && self.instance.SlideShow.isActive ) {
3584					self.instance.SlideShow.stop();
3585				}
3586			}
3587
3588		} else {
3589
3590			if ( swiping == 'x' ) {
3591
3592				// Sticky edges
3593				if ( self.distanceX > 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === 0 && !self.instance.current.opts.loop ) ) ) {
3594					left = left + Math.pow( self.distanceX, 0.8 );
3595
3596				} else if ( self.distanceX < 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop ) ) ) {
3597					left = left - Math.pow( -self.distanceX, 0.8 );
3598
3599				} else {
3600					left = left + self.distanceX;
3601				}
3602
3603			}
3604
3605			self.sliderLastPos = {
3606				top  : swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY,
3607				left : left
3608			};
3609
3610			if ( self.requestId ) {
3611				cancelAFrame( self.requestId );
3612
3613				self.requestId = null;
3614			}
3615
3616			self.requestId = requestAFrame(function() {
3617
3618				if ( self.sliderLastPos ) {
3619					$.each(self.instance.slides, function( index, slide ) {
3620						var pos = slide.pos - self.instance.currPos;
3621
3622						$.fancybox.setTranslate( slide.$slide, {
3623							top  : self.sliderLastPos.top,
3624							left : self.sliderLastPos.left + ( pos * self.canvasWidth ) + ( pos * slide.opts.gutter )
3625						});
3626					});
3627
3628					self.$container.addClass( 'fancybox-is-sliding' );
3629				}
3630
3631			});
3632
3633		}
3634
3635	};
3636
3637	Guestures.prototype.onPan = function() {
3638
3639		var self = this;
3640
3641		var newOffsetX, newOffsetY, newPos;
3642
3643		self.canTap = false;
3644
3645		if ( self.contentStartPos.width > self.canvasWidth ) {
3646			newOffsetX = self.contentStartPos.left + self.distanceX;
3647
3648		} else {
3649			newOffsetX = self.contentStartPos.left;
3650		}
3651
3652		newOffsetY = self.contentStartPos.top + self.distanceY;
3653
3654		newPos = self.limitMovement( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height );
3655
3656		newPos.scaleX = self.contentStartPos.scaleX;
3657		newPos.scaleY = self.contentStartPos.scaleY;
3658
3659		self.contentLastPos = newPos;
3660
3661		if ( self.requestId ) {
3662			cancelAFrame( self.requestId );
3663
3664			self.requestId = null;
3665		}
3666
3667		self.requestId = requestAFrame(function() {
3668			$.fancybox.setTranslate( self.$content, self.contentLastPos );
3669		});
3670	};
3671
3672	// Make panning sticky to the edges
3673	Guestures.prototype.limitMovement = function( newOffsetX, newOffsetY, newWidth, newHeight ) {
3674
3675		var self = this;
3676
3677		var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY;
3678
3679		var canvasWidth  = self.canvasWidth;
3680		var canvasHeight = self.canvasHeight;
3681
3682		var currentOffsetX = self.contentStartPos.left;
3683		var currentOffsetY = self.contentStartPos.top;
3684
3685		var distanceX = self.distanceX;
3686		var distanceY = self.distanceY;
3687
3688		// Slow down proportionally to traveled distance
3689
3690		minTranslateX = Math.max(0, canvasWidth  * 0.5 - newWidth  * 0.5 );
3691		minTranslateY = Math.max(0, canvasHeight * 0.5 - newHeight * 0.5 );
3692
3693		maxTranslateX = Math.min( canvasWidth  - newWidth,  canvasWidth  * 0.5 - newWidth  * 0.5 );
3694		maxTranslateY = Math.min( canvasHeight - newHeight, canvasHeight * 0.5 - newHeight * 0.5 );
3695
3696		if ( newWidth > canvasWidth ) {
3697
3698			//   ->
3699			if ( distanceX > 0 && newOffsetX > minTranslateX ) {
3700				newOffsetX = minTranslateX - 1 + Math.pow( -minTranslateX + currentOffsetX + distanceX, 0.8 ) || 0;
3701			}
3702
3703			//    <-
3704			if ( distanceX  < 0 && newOffsetX < maxTranslateX ) {
3705				newOffsetX = maxTranslateX + 1 - Math.pow( maxTranslateX - currentOffsetX - distanceX, 0.8 ) || 0;
3706			}
3707
3708		}
3709
3710		if ( newHeight > canvasHeight ) {
3711
3712			//   \/
3713			if ( distanceY > 0 && newOffsetY > minTranslateY ) {
3714				newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8 ) || 0;
3715			}
3716
3717			//   /\
3718			if ( distanceY < 0 && newOffsetY < maxTranslateY ) {
3719				newOffsetY = maxTranslateY + 1 - Math.pow ( maxTranslateY - currentOffsetY - distanceY, 0.8 ) || 0;
3720			}
3721
3722		}
3723
3724		return {
3725			top  : newOffsetY,
3726			left : newOffsetX
3727		};
3728
3729	};
3730
3731
3732	Guestures.prototype.limitPosition = function( newOffsetX, newOffsetY, newWidth, newHeight ) {
3733
3734		var self = this;
3735
3736		var canvasWidth  = self.canvasWidth;
3737		var canvasHeight = self.canvasHeight;
3738
3739		if ( newWidth > canvasWidth ) {
3740			newOffsetX = newOffsetX > 0 ? 0 : newOffsetX;
3741			newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX;
3742
3743		} else {
3744
3745			// Center horizontally
3746			newOffsetX = Math.max( 0, canvasWidth / 2 - newWidth / 2 );
3747
3748		}
3749
3750		if ( newHeight > canvasHeight ) {
3751			newOffsetY = newOffsetY > 0 ? 0 : newOffsetY;
3752			newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY;
3753
3754		} else {
3755
3756			// Center vertically
3757			newOffsetY = Math.max( 0, canvasHeight / 2 - newHeight / 2 );
3758
3759		}
3760
3761		return {
3762			top  : newOffsetY,
3763			left : newOffsetX
3764		};
3765
3766	};
3767
3768	Guestures.prototype.onZoom = function() {
3769
3770		var self = this;
3771
3772		// Calculate current distance between points to get pinch ratio and new width and height
3773
3774		var currentWidth  = self.contentStartPos.width;
3775		var currentHeight = self.contentStartPos.height;
3776
3777		var currentOffsetX = self.contentStartPos.left;
3778		var currentOffsetY = self.contentStartPos.top;
3779
3780		var endDistanceBetweenFingers = distance( self.newPoints[0], self.newPoints[1] );
3781
3782		var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers;
3783
3784		var newWidth  = Math.floor( currentWidth  * pinchRatio );
3785		var newHeight = Math.floor( currentHeight * pinchRatio );
3786
3787		// This is the translation due to pinch-zooming
3788		var translateFromZoomingX = (currentWidth  - newWidth)  * self.percentageOfImageAtPinchPointX;
3789		var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY;
3790
3791		//Point between the two touches
3792
3793		var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft();
3794		var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop();
3795
3796		// And this is the translation due to translation of the centerpoint
3797		// between the two fingers
3798
3799		var translateFromTranslatingX = centerPointEndX - self.centerPointStartX;
3800		var translateFromTranslatingY = centerPointEndY - self.centerPointStartY;
3801
3802		// The new offset is the old/current one plus the total translation
3803
3804		var newOffsetX = currentOffsetX + ( translateFromZoomingX + translateFromTranslatingX );
3805		var newOffsetY = currentOffsetY + ( translateFromZoomingY + translateFromTranslatingY );
3806
3807		var newPos = {
3808			top    : newOffsetY,
3809			left   : newOffsetX,
3810			scaleX : self.contentStartPos.scaleX * pinchRatio,
3811			scaleY : self.contentStartPos.scaleY * pinchRatio
3812		};
3813
3814		self.canTap = false;
3815
3816		self.newWidth  = newWidth;
3817		self.newHeight = newHeight;
3818
3819		self.contentLastPos = newPos;
3820
3821		if ( self.requestId ) {
3822			cancelAFrame( self.requestId );
3823
3824			self.requestId = null;
3825		}
3826
3827		self.requestId = requestAFrame(function() {
3828			$.fancybox.setTranslate( self.$content, self.contentLastPos );
3829		});
3830
3831	};
3832
3833	Guestures.prototype.ontouchend = function( e ) {
3834
3835		var self = this;
3836		var dMs  = Math.max( (new Date().getTime() ) - self.startTime, 1);
3837
3838		var swiping = self.isSwiping;
3839		var panning = self.isPanning;
3840		var zooming = self.isZooming;
3841
3842		self.endPoints = pointers( e );
3843
3844		self.$container.removeClass( 'fancybox-controls--isGrabbing' );
3845
3846		$(document).off( '.fb.touch' );
3847
3848		if ( self.requestId ) {
3849			cancelAFrame( self.requestId );
3850
3851			self.requestId = null;
3852		}
3853
3854		self.isSwiping = false;
3855		self.isPanning = false;
3856		self.isZooming = false;
3857
3858		if ( self.canTap )  {
3859			return self.onTap( e );
3860		}
3861
3862		self.speed = 366;
3863
3864		// Speed in px/ms
3865		self.velocityX = self.distanceX / dMs * 0.5;
3866		self.velocityY = self.distanceY / dMs * 0.5;
3867
3868		self.speedX = Math.max( self.speed * 0.5, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityX ) ) * self.speed ) );
3869
3870		if ( panning ) {
3871			self.endPanning();
3872
3873		} else if ( zooming ) {
3874			self.endZooming();
3875
3876		} else {
3877			self.endSwiping( swiping );
3878		}
3879
3880		return;
3881	};
3882
3883	Guestures.prototype.endSwiping = function( swiping ) {
3884
3885		var self = this;
3886		var ret = false;
3887
3888		self.instance.isSliding = false;
3889		self.sliderLastPos      = null;
3890
3891		// Close if swiped vertically / navigate if horizontally
3892		if ( swiping == 'y' && Math.abs( self.distanceY ) > 50 ) {
3893
3894			// Continue vertical movement
3895			$.fancybox.animate( self.instance.current.$slide, {
3896				top     : self.sliderStartPos.top + self.distanceY + ( self.velocityY * 150 ),
3897				opacity : 0
3898			}, 150 );
3899
3900			ret = self.instance.close( true, 300 );
3901
3902		} else if ( swiping == 'x' && self.distanceX > 50 && self.instance.group.length > 1 ) {
3903			ret = self.instance.previous( self.speedX );
3904
3905		} else if ( swiping == 'x' && self.distanceX < -50  && self.instance.group.length > 1 ) {
3906			ret = self.instance.next( self.speedX );
3907		}
3908
3909		if ( ret === false && ( swiping == 'x' || swiping == 'y' ) ) {
3910			self.instance.jumpTo( self.instance.current.index, 150 );
3911		}
3912
3913		self.$container.removeClass( 'fancybox-is-sliding' );
3914
3915	};
3916
3917	// Limit panning from edges
3918	// ========================
3919
3920	Guestures.prototype.endPanning = function() {
3921
3922		var self = this;
3923		var newOffsetX, newOffsetY, newPos;
3924
3925		if ( !self.contentLastPos ) {
3926			return;
3927		}
3928
3929		if ( self.instance.current.opts.touch.momentum === false ) {
3930			newOffsetX = self.contentLastPos.left;
3931			newOffsetY = self.contentLastPos.top;
3932
3933		} else {
3934
3935			// Continue movement
3936			newOffsetX = self.contentLastPos.left + ( self.velocityX * self.speed );
3937			newOffsetY = self.contentLastPos.top  + ( self.velocityY * self.speed );
3938		}
3939
3940		newPos = self.limitPosition( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height );
3941
3942		 newPos.width  = self.contentStartPos.width;
3943		 newPos.height = self.contentStartPos.height;
3944
3945		$.fancybox.animate( self.$content, newPos, 330 );
3946	};
3947
3948
3949	Guestures.prototype.endZooming = function() {
3950
3951		var self = this;
3952
3953		var current = self.instance.current;
3954
3955		var newOffsetX, newOffsetY, newPos, reset;
3956
3957		var newWidth  = self.newWidth;
3958		var newHeight = self.newHeight;
3959
3960		if ( !self.contentLastPos ) {
3961			return;
3962		}
3963
3964		newOffsetX = self.contentLastPos.left;
3965		newOffsetY = self.contentLastPos.top;
3966
3967		reset = {
3968		   	top    : newOffsetY,
3969		   	left   : newOffsetX,
3970		   	width  : newWidth,
3971		   	height : newHeight,
3972			scaleX : 1,
3973			scaleY : 1
3974	   };
3975
3976	   // Reset scalex/scaleY values; this helps for perfomance and does not break animation
3977	   $.fancybox.setTranslate( self.$content, reset );
3978
3979		if ( newWidth < self.canvasWidth && newHeight < self.canvasHeight ) {
3980			self.instance.scaleToFit( 150 );
3981
3982		} else if ( newWidth > current.width || newHeight > current.height ) {
3983			self.instance.scaleToActual( self.centerPointStartX, self.centerPointStartY, 150 );
3984
3985		} else {
3986
3987			newPos = self.limitPosition( newOffsetX, newOffsetY, newWidth, newHeight );
3988
3989			// Switch from scale() to width/height or animation will not work correctly
3990			$.fancybox.setTranslate( self.content, $.fancybox.getTranslate( self.$content ) );
3991
3992			$.fancybox.animate( self.$content, newPos, 150 );
3993		}
3994
3995	};
3996
3997	Guestures.prototype.onTap = function(e) {
3998		var self    = this;
3999		var $target = $( e.target );
4000
4001		var instance = self.instance;
4002		var current  = instance.current;
4003
4004		var endPoints = ( e && pointers( e ) ) || self.startPoints;
4005
4006		var tapX = endPoints[0] ? endPoints[0].x - self.$stage.offset().left : 0;
4007		var tapY = endPoints[0] ? endPoints[0].y - self.$stage.offset().top  : 0;
4008
4009		var where;
4010
4011		var process = function ( prefix ) {
4012
4013			var action = current.opts[ prefix ];
4014
4015			if ( $.isFunction( action ) ) {
4016				action = action.apply( instance, [ current, e ] );
4017			}
4018
4019			if ( !action) {
4020				return;
4021			}
4022
4023			switch ( action ) {
4024
4025				case "close" :
4026
4027					instance.close( self.startEvent );
4028
4029				break;
4030
4031				case "toggleControls" :
4032
4033					instance.toggleControls( true );
4034
4035				break;
4036
4037				case "next" :
4038
4039					instance.next();
4040
4041				break;
4042
4043				case "nextOrClose" :
4044
4045					if ( instance.group.length > 1 ) {
4046						instance.next();
4047
4048					} else {
4049						instance.close( self.startEvent );
4050					}
4051
4052				break;
4053
4054				case "zoom" :
4055
4056					if ( current.type == 'image' && ( current.isLoaded || current.$ghost ) ) {
4057
4058						if ( instance.canPan() ) {
4059							instance.scaleToFit();
4060
4061						} else if ( instance.isScaledDown() ) {
4062							instance.scaleToActual( tapX, tapY );
4063
4064						} else if ( instance.group.length < 2 ) {
4065							instance.close( self.startEvent );
4066						}
4067					}
4068
4069				break;
4070			}
4071
4072		};
4073
4074		// Ignore right click
4075		if ( e.originalEvent && e.originalEvent.button == 2 ) {
4076			return;
4077		}
4078
4079		// Skip if current slide is not in the center
4080		if ( instance.isSliding ) {
4081			return;
4082		}
4083
4084		// Skip if clicked on the scrollbar
4085		if ( tapX > $target[0].clientWidth + $target.offset().left ) {
4086			return;
4087		}
4088
4089		// Check where is clicked
4090		if ( $target.is( '.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container' ) ) {
4091			where = 'Outside';
4092
4093		} else if ( $target.is( '.fancybox-slide' ) ) {
4094			where = 'Slide';
4095
4096		} else if  ( instance.current.$content && instance.current.$content.has( e.target ).length ) {
4097		 	where = 'Content';
4098
4099		} else {
4100			return;
4101		}
4102
4103		// Check if this is a double tap
4104		if ( self.tapped ) {
4105
4106			// Stop previously created single tap
4107			clearTimeout( self.tapped );
4108			self.tapped = null;
4109
4110			// Skip if distance between taps is too big
4111			if ( Math.abs( tapX - self.tapX ) > 50 || Math.abs( tapY - self.tapY ) > 50 || instance.isSliding ) {
4112				return this;
4113			}
4114
4115			// OK, now we assume that this is a double-tap
4116			process( 'dblclick' + where );
4117
4118		} else {
4119
4120			// Single tap will be processed if user has not clicked second time within 300ms
4121			// or there is no need to wait for double-tap
4122			self.tapX = tapX;
4123			self.tapY = tapY;
4124
4125			if ( current.opts[ 'dblclick' + where ] && current.opts[ 'dblclick' + where ] !== current.opts[ 'click' + where ] ) {
4126				self.tapped = setTimeout(function() {
4127					self.tapped = null;
4128
4129					process( 'click' + where );
4130
4131				}, 300);
4132
4133			} else {
4134				process( 'click' + where );
4135			}
4136
4137		}
4138
4139		return this;
4140	};
4141
4142	$(document).on('onActivate.fb', function (e, instance) {
4143		if ( instance && !instance.Guestures ) {
4144			instance.Guestures = new Guestures( instance );
4145		}
4146	});
4147
4148	$(document).on('beforeClose.fb', function (e, instance) {
4149		if ( instance && instance.Guestures ) {
4150			instance.Guestures.destroy();
4151		}
4152	});
4153
4154
4155}(window, document, window.jQuery));
4156
4157// ==========================================================================
4158//
4159// SlideShow
4160// Enables slideshow functionality
4161//
4162// Example of usage:
4163// $.fancybox.getInstance().SlideShow.start()
4164//
4165// ==========================================================================
4166;(function (document, $) {
4167	'use strict';
4168
4169	var SlideShow = function( instance ) {
4170		this.instance = instance;
4171		this.init();
4172	};
4173
4174	$.extend( SlideShow.prototype, {
4175		timer    : null,
4176		isActive : false,
4177		$button  : null,
4178		speed    : 3000,
4179
4180		init : function() {
4181			var self = this;
4182
4183			self.$button = self.instance.$refs.toolbar.find('[data-fancybox-play]').on('click', function() {
4184				self.toggle();
4185			});
4186
4187			if ( self.instance.group.length < 2 || !self.instance.group[ self.instance.currIndex ].opts.slideShow ) {
4188				self.$button.hide();
4189			}
4190		},
4191
4192		set : function() {
4193			var self = this;
4194
4195			// Check if reached last element
4196			if ( self.instance && self.instance.current && (self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) {
4197				self.timer = setTimeout(function() {
4198					self.instance.next();
4199
4200				}, self.instance.current.opts.slideShow.speed || self.speed);
4201
4202			} else {
4203				self.stop();
4204				self.instance.idleSecondsCounter = 0;
4205				self.instance.showControls();
4206			}
4207
4208		},
4209
4210		clear : function() {
4211			var self = this;
4212
4213			clearTimeout( self.timer );
4214
4215			self.timer = null;
4216		},
4217
4218		start : function() {
4219			var self = this;
4220			var current = self.instance.current;
4221
4222			if ( self.instance && current && ( current.opts.loop || current.index < self.instance.group.length - 1 )) {
4223
4224				self.isActive = true;
4225
4226				self.$button
4227					.attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_STOP )
4228					.addClass( 'fancybox-button--pause' );
4229
4230				if ( current.isComplete ) {
4231					self.set();
4232				}
4233			}
4234		},
4235
4236		stop : function() {
4237			var self = this;
4238			var current = self.instance.current;
4239
4240			self.clear();
4241
4242			self.$button
4243				.attr( 'title', current.opts.i18n[ current.opts.lang ].PLAY_START )
4244				.removeClass( 'fancybox-button--pause' );
4245
4246			self.isActive = false;
4247		},
4248
4249		toggle : function() {
4250			var self = this;
4251
4252			if ( self.isActive ) {
4253				self.stop();
4254
4255			} else {
4256				self.start();
4257			}
4258		}
4259
4260	});
4261
4262	$(document).on({
4263		'onInit.fb' : function(e, instance) {
4264			if ( instance && !instance.SlideShow ) {
4265				instance.SlideShow = new SlideShow( instance );
4266			}
4267		},
4268
4269		'beforeShow.fb' : function(e, instance, current, firstRun) {
4270			var SlideShow = instance && instance.SlideShow;
4271
4272			if ( firstRun ) {
4273
4274				if ( SlideShow && current.opts.slideShow.autoStart ) {
4275					SlideShow.start();
4276				}
4277
4278			} else if ( SlideShow && SlideShow.isActive )  {
4279				SlideShow.clear();
4280			}
4281		},
4282
4283		'afterShow.fb' : function(e, instance, current) {
4284			var SlideShow = instance && instance.SlideShow;
4285
4286			if ( SlideShow && SlideShow.isActive ) {
4287				SlideShow.set();
4288			}
4289		},
4290
4291		'afterKeydown.fb' : function(e, instance, current, keypress, keycode) {
4292			var SlideShow = instance && instance.SlideShow;
4293
4294			// "P" or Spacebar
4295			if ( SlideShow && current.opts.slideShow && ( keycode === 80 || keycode === 32 ) && !$(document.activeElement).is( 'button,a,input' ) ) {
4296				keypress.preventDefault();
4297
4298				SlideShow.toggle();
4299			}
4300		},
4301
4302		'beforeClose.fb onDeactivate.fb' : function(e, instance) {
4303			var SlideShow = instance && instance.SlideShow;
4304
4305			if ( SlideShow ) {
4306				SlideShow.stop();
4307			}
4308		}
4309	});
4310
4311	// Page Visibility API to pause slideshow when window is not active
4312	$(document).on("visibilitychange", function() {
4313		var instance  = $.fancybox.getInstance();
4314		var SlideShow = instance && instance.SlideShow;
4315
4316		if ( SlideShow && SlideShow.isActive ) {
4317			if ( document.hidden ) {
4318				SlideShow.clear();
4319
4320			} else {
4321				SlideShow.set();
4322			}
4323		}
4324	});
4325
4326}(document, window.jQuery));
4327
4328// ==========================================================================
4329//
4330// FullScreen
4331// Adds fullscreen functionality
4332//
4333// ==========================================================================
4334;(function (document, $) {
4335	'use strict';
4336
4337	// Collection of methods supported by user browser
4338	var fn = (function () {
4339
4340		var fnMap = [
4341			[
4342				'requestFullscreen',
4343				'exitFullscreen',
4344				'fullscreenElement',
4345				'fullscreenEnabled',
4346				'fullscreenchange',
4347				'fullscreenerror'
4348			],
4349			// new WebKit
4350			[
4351				'webkitRequestFullscreen',
4352				'webkitExitFullscreen',
4353				'webkitFullscreenElement',
4354				'webkitFullscreenEnabled',
4355				'webkitfullscreenchange',
4356				'webkitfullscreenerror'
4357
4358			],
4359			// old WebKit (Safari 5.1)
4360			[
4361				'webkitRequestFullScreen',
4362				'webkitCancelFullScreen',
4363				'webkitCurrentFullScreenElement',
4364				'webkitCancelFullScreen',
4365				'webkitfullscreenchange',
4366				'webkitfullscreenerror'
4367
4368			],
4369			[
4370				'mozRequestFullScreen',
4371				'mozCancelFullScreen',
4372				'mozFullScreenElement',
4373				'mozFullScreenEnabled',
4374				'mozfullscreenchange',
4375				'mozfullscreenerror'
4376			],
4377			[
4378				'msRequestFullscreen',
4379				'msExitFullscreen',
4380				'msFullscreenElement',
4381				'msFullscreenEnabled',
4382				'MSFullscreenChange',
4383				'MSFullscreenError'
4384			]
4385		];
4386
4387		var val;
4388		var ret = {};
4389		var i, j;
4390
4391		for ( i = 0; i < fnMap.length; i++ ) {
4392			val = fnMap[ i ];
4393
4394			if ( val && val[ 1 ] in document ) {
4395				for ( j = 0; j < val.length; j++ ) {
4396					ret[ fnMap[ 0 ][ j ] ] = val[ j ];
4397				}
4398
4399				return ret;
4400			}
4401		}
4402
4403		return false;
4404	})();
4405
4406	// If browser does not have Full Screen API, then simply unset default button template and stop
4407	if ( !fn ) {
4408		$.fancybox.defaults.btnTpl.fullScreen = false;
4409
4410		return;
4411	}
4412
4413	var FullScreen = {
4414		request : function ( elem ) {
4415
4416			elem = elem || document.documentElement;
4417
4418			elem[ fn.requestFullscreen ]( elem.ALLOW_KEYBOARD_INPUT );
4419
4420		},
4421		exit : function () {
4422
4423			document[ fn.exitFullscreen ]();
4424
4425		},
4426		toggle : function ( elem ) {
4427
4428			elem = elem || document.documentElement;
4429
4430			if ( this.isFullscreen() ) {
4431				this.exit();
4432
4433			} else {
4434				this.request( elem );
4435			}
4436
4437		},
4438		isFullscreen : function()  {
4439
4440			return Boolean( document[ fn.fullscreenElement ] );
4441
4442		},
4443		enabled : function()  {
4444
4445			return Boolean( document[ fn.fullscreenEnabled ] );
4446
4447		}
4448	};
4449
4450	$(document).on({
4451		'onInit.fb' : function(e, instance) {
4452			var $container;
4453
4454			var $button = instance.$refs.toolbar.find('[data-fancybox-fullscreen]');
4455
4456			if ( instance && !instance.FullScreen && instance.group[ instance.currIndex ].opts.fullScreen ) {
4457				$container = instance.$refs.container;
4458
4459				$container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) {
4460
4461					e.stopPropagation();
4462					e.preventDefault();
4463
4464					FullScreen.toggle( $container[ 0 ] );
4465
4466				});
4467
4468				if ( instance.opts.fullScreen && instance.opts.fullScreen.autoStart === true ) {
4469					FullScreen.request( $container[ 0 ] );
4470				}
4471
4472				// Expose API
4473				instance.FullScreen = FullScreen;
4474
4475			} else {
4476				$button.hide();
4477			}
4478
4479		},
4480
4481		'afterKeydown.fb' : function(e, instance, current, keypress, keycode) {
4482
4483			// "P" or Spacebar
4484			if ( instance && instance.FullScreen && keycode === 70 ) {
4485				keypress.preventDefault();
4486
4487				instance.FullScreen.toggle( instance.$refs.container[ 0 ] );
4488			}
4489
4490		},
4491
4492		'beforeClose.fb' : function( instance ) {
4493			if ( instance && instance.FullScreen ) {
4494				FullScreen.exit();
4495			}
4496		}
4497	});
4498
4499	$(document).on(fn.fullscreenchange, function() {
4500		var instance = $.fancybox.getInstance();
4501
4502		// If image is zooming, then force to stop and reposition properly
4503		if ( instance.current && instance.current.type === 'image' && instance.isAnimating ) {
4504			instance.current.$content.css( 'transition', 'none' );
4505
4506			instance.isAnimating = false;
4507
4508			instance.update( true, true, 0 );
4509		}
4510
4511	});
4512
4513}(document, window.jQuery));
4514
4515// ==========================================================================
4516//
4517// Thumbs
4518// Displays thumbnails in a grid
4519//
4520// ==========================================================================
4521;(function (document, $) {
4522	'use strict';
4523
4524	var FancyThumbs = function( instance ) {
4525		this.instance = instance;
4526		this.init();
4527	};
4528
4529	$.extend( FancyThumbs.prototype, {
4530
4531		$button		: null,
4532		$grid		: null,
4533		$list		: null,
4534		isVisible	: false,
4535
4536		init : function() {
4537			var self = this;
4538
4539			var first  = self.instance.group[0],
4540				second = self.instance.group[1];
4541
4542			self.$button = self.instance.$refs.toolbar.find( '[data-fancybox-thumbs]' );
4543
4544			if ( self.instance.group.length > 1 && self.instance.group[ self.instance.currIndex ].opts.thumbs && (
4545		    		( first.type == 'image'  || first.opts.thumb  || first.opts.$thumb ) &&
4546		    		( second.type == 'image' || second.opts.thumb || second.opts.$thumb )
4547			)) {
4548
4549				self.$button.on('click', function() {
4550					self.toggle();
4551				});
4552
4553				self.isActive = true;
4554
4555			} else {
4556				self.$button.hide();
4557
4558				self.isActive = false;
4559			}
4560
4561		},
4562
4563		create : function() {
4564			var instance = this.instance,
4565				list,
4566				src;
4567
4568			this.$grid = $('<div class="fancybox-thumbs"></div>').appendTo( instance.$refs.container );
4569
4570			list = '<ul>';
4571
4572			$.each(instance.group, function( i, item ) {
4573
4574				src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr('src') : null );
4575
4576				if ( !src && item.type === 'image' ) {
4577					src = item.src;
4578				}
4579
4580				if ( src && src.length ) {
4581					list += '<li data-index="' + i + '"  tabindex="0" class="fancybox-thumbs-loading"><img data-src="' + src + '" /></li>';
4582				}
4583
4584			});
4585
4586			list += '</ul>';
4587
4588			this.$list = $( list ).appendTo( this.$grid ).on('click', 'li', function() {
4589				instance.jumpTo( $(this).data('index') );
4590			});
4591
4592			this.$list.find('img').hide().one('load', function() {
4593
4594				var $parent		= $(this).parent().removeClass('fancybox-thumbs-loading'),
4595					thumbWidth	= $parent.outerWidth(),
4596					thumbHeight	= $parent.outerHeight(),
4597					width,
4598					height,
4599					widthRatio,
4600					heightRatio;
4601
4602				width  = this.naturalWidth	|| this.width;
4603				height = this.naturalHeight	|| this.height;
4604
4605				//Calculate thumbnail width/height and center it
4606
4607				widthRatio  = width  / thumbWidth;
4608				heightRatio = height / thumbHeight;
4609
4610				if (widthRatio >= 1 && heightRatio >= 1) {
4611					if (widthRatio > heightRatio) {
4612						width  = width / heightRatio;
4613						height = thumbHeight;
4614
4615					} else {
4616						width  = thumbWidth;
4617						height = height / widthRatio;
4618					}
4619				}
4620
4621				$(this).css({
4622					width         : Math.floor(width),
4623					height        : Math.floor(height),
4624					'margin-top'  : Math.min( 0, Math.floor(thumbHeight * 0.3 - height * 0.3 ) ),
4625					'margin-left' : Math.min( 0, Math.floor(thumbWidth  * 0.5 - width  * 0.5 ) )
4626				}).show();
4627
4628			})
4629			.each(function() {
4630				this.src = $( this ).data( 'src' );
4631			});
4632
4633		},
4634
4635		focus : function() {
4636
4637			if ( this.instance.current ) {
4638				this.$list
4639					.children()
4640					.removeClass('fancybox-thumbs-active')
4641					.filter('[data-index="' + this.instance.current.index  + '"]')
4642					.addClass('fancybox-thumbs-active')
4643					.focus();
4644			}
4645
4646		},
4647
4648		close : function() {
4649			this.$grid.hide();
4650		},
4651
4652		update : function() {
4653
4654			this.instance.$refs.container.toggleClass( 'fancybox-show-thumbs', this.isVisible );
4655
4656			if ( this.isVisible ) {
4657
4658				if ( !this.$grid ) {
4659					this.create();
4660				}
4661
4662				this.instance.trigger( 'onThumbsShow' );
4663
4664				this.focus();
4665
4666			} else if ( this.$grid ) {
4667				this.instance.trigger( 'onThumbsHide' );
4668			}
4669
4670			// Update content position
4671			this.instance.update();
4672
4673		},
4674
4675		hide : function() {
4676			this.isVisible = false;
4677			this.update();
4678		},
4679
4680		show : function() {
4681			this.isVisible = true;
4682			this.update();
4683		},
4684
4685		toggle : function() {
4686			this.isVisible = !this.isVisible;
4687			this.update();
4688		}
4689
4690	});
4691
4692	$(document).on({
4693
4694		'onInit.fb' : function(e, instance) {
4695			if ( instance && !instance.Thumbs ) {
4696				instance.Thumbs = new FancyThumbs( instance );
4697			}
4698		},
4699
4700		'beforeShow.fb' : function(e, instance, item, firstRun) {
4701			var Thumbs = instance && instance.Thumbs;
4702
4703			if ( !Thumbs || !Thumbs.isActive ) {
4704				return;
4705			}
4706
4707			if ( item.modal ) {
4708				Thumbs.$button.hide();
4709
4710				Thumbs.hide();
4711
4712				return;
4713			}
4714
4715			if ( firstRun && instance.opts.thumbs.autoStart === true ) {
4716				Thumbs.show();
4717			}
4718
4719			if ( Thumbs.isVisible ) {
4720				Thumbs.focus();
4721			}
4722		},
4723
4724		'afterKeydown.fb' : function(e, instance, current, keypress, keycode) {
4725			var Thumbs = instance && instance.Thumbs;
4726
4727			// "G"
4728			if ( Thumbs && Thumbs.isActive && keycode === 71 ) {
4729				keypress.preventDefault();
4730
4731				Thumbs.toggle();
4732			}
4733		},
4734
4735		'beforeClose.fb' : function( e, instance ) {
4736			var Thumbs = instance && instance.Thumbs;
4737
4738			if ( Thumbs && Thumbs.isVisible && instance.opts.thumbs.hideOnClose !== false ) {
4739				Thumbs.close();
4740			}
4741		}
4742
4743	});
4744
4745}(document, window.jQuery));
4746
4747// ==========================================================================
4748//
4749// Hash
4750// Enables linking to each modal
4751//
4752// ==========================================================================
4753;(function (document, window, $) {
4754	'use strict';
4755
4756	// Simple $.escapeSelector polyfill (for jQuery prior v3)
4757	if ( !$.escapeSelector ) {
4758		$.escapeSelector = function( sel ) {
4759			var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
4760			var fcssescape = function( ch, asCodePoint ) {
4761				if ( asCodePoint ) {
4762					// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
4763					if ( ch === "\0" ) {
4764						return "\uFFFD";
4765					}
4766
4767					// Control characters and (dependent upon position) numbers get escaped as code points
4768					return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
4769				}
4770
4771				// Other potentially-special ASCII characters get backslash-escaped
4772				return "\\" + ch;
4773			};
4774
4775			return ( sel + "" ).replace( rcssescape, fcssescape );
4776		};
4777	}
4778
4779	// Variable containing last hash value set by fancyBox
4780	// It will be used to determine if fancyBox needs to close after hash change is detected
4781    var currentHash = null;
4782
4783	// Throtlling the history change
4784	var timerID = null;
4785
4786	// Get info about gallery name and current index from url
4787    function parseUrl() {
4788        var hash    = window.location.hash.substr( 1 );
4789        var rez     = hash.split( '-' );
4790        var index   = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseInt( rez.pop( -1 ), 10 ) || 1 : 1;
4791        var gallery = rez.join( '-' );
4792
4793		// Index is starting from 1
4794		if ( index < 1 ) {
4795			index = 1;
4796		}
4797
4798        return {
4799            hash    : hash,
4800            index   : index,
4801            gallery : gallery
4802        };
4803    }
4804
4805	// Trigger click evnt on links to open new fancyBox instance
4806	function triggerFromUrl( url ) {
4807		var $el;
4808
4809        if ( url.gallery !== '' ) {
4810
4811			// If we can find element matching 'data-fancybox' atribute, then trigger click event for that ..
4812			$el = $( "[data-fancybox='" + $.escapeSelector( url.gallery ) + "']" ).eq( url.index - 1 );
4813
4814            if ( $el.length ) {
4815				$el.trigger( 'click' );
4816
4817			} else {
4818
4819				// .. if not, try finding element by ID
4820				$( "#" + $.escapeSelector( url.gallery ) + "" ).trigger( 'click' );
4821
4822			}
4823
4824        }
4825	}
4826
4827	// Get gallery name from current instance
4828	function getGallery( instance ) {
4829		var opts;
4830
4831		if ( !instance ) {
4832			return false;
vendor: 23,449 bytes, lines 4833-5782
4833		}
4834
4835		opts = instance.current ? instance.current.opts : instance.opts;
4836
4837		return opts.$orig ? opts.$orig.data( 'fancybox' ) : ( opts.hash || '' );
4838	}
4839
4840	// Star when DOM becomes ready
4841    $(function() {
4842
4843		// Small delay is used to allow other scripts to process "dom ready" event
4844		setTimeout(function() {/*
4845
4846			// Check if this module is not disabled
4847			if ( $.fancybox.defaults.hash === false ) {
4848				return;
4849			}
4850
4851			// Update hash when opening/closing fancyBox
4852		    $(document).on({
4853				'onInit.fb' : function( e, instance ) {
4854					var url, gallery;
4855
4856					if ( instance.group[ instance.currIndex ].opts.hash === false ) {
4857						return;
4858					}
4859
4860					url     = parseUrl();
4861					gallery = getGallery( instance );
4862
4863					// Make sure gallery start index matches index from hash
4864					if ( gallery && url.gallery && gallery == url.gallery ) {
4865						instance.currIndex = url.index - 1;
4866					}
4867
4868				},
4869
4870				'beforeShow.fb' : function( e, instance, current, firstRun ) {
4871					var gallery;
4872
4873					if ( current.opts.hash === false ) {
4874						return;
4875					}
4876
4877		            gallery = getGallery( instance );
4878
4879		            // Update window hash
4880		            if ( gallery && gallery !== '' ) {
4881
4882						if ( window.location.hash.indexOf( gallery ) < 0 ) {
4883			                instance.opts.origHash = window.location.hash;
4884			            }
4885
4886						currentHash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' );
4887
4888						if ( 'replaceState' in window.history ) {
4889							if ( timerID ) {
4890								clearTimeout( timerID );
4891							}
4892
4893							timerID = setTimeout(function() {
4894								window.history[ firstRun ? 'pushState' : 'replaceState' ]( {} , document.title, window.location.pathname + window.location.search + '#' +  currentHash );
4895
4896								timerID = null;
4897
4898							}, 300);
4899
4900						} else {
4901							window.location.hash = currentHash;
4902						}
4903
4904		            }
4905
4906		        },
4907
4908				'beforeClose.fb' : function( e, instance, current ) {
4909					var gallery, origHash;
4910
4911					if ( timerID ) {
4912						clearTimeout( timerID );
4913					}
4914
4915					if ( current.opts.hash === false ) {
4916						return;
4917					}
4918
4919					gallery  = getGallery( instance );
4920					origHash = instance && instance.opts.origHash ? instance.opts.origHash : '';
4921
4922		            // Remove hash from location bar
4923		            if ( gallery && gallery !== '' ) {
4924
4925		                if ( 'replaceState' in history ) {
4926							window.history.replaceState( {} , document.title, window.location.pathname + window.location.search + origHash );
4927
4928		                } else {
4929							window.location.hash = origHash;
4930
4931							// Keep original scroll position
4932							$( window ).scrollTop( instance.scrollTop ).scrollLeft( instance.scrollLeft );
4933		                }
4934		            }
4935
4936					currentHash = null;
4937		        }
4938		    });
4939
4940			// Check if need to close after url has changed
4941			$(window).on('hashchange.fb', function() {
4942				var url = parseUrl();
4943
4944				if ( $.fancybox.getInstance() ) {
4945					if ( currentHash && currentHash !== url.gallery + '-' + url.index && !( url.index === 1 && currentHash == url.gallery ) ) {
4946						currentHash = null;
4947
4948						$.fancybox.close();
4949					}
4950
4951				} else if ( url.gallery !== '' ) {
4952					triggerFromUrl( url );
4953				}
4954			});
4955
4956			// If navigating away from current page
4957			$(window).one('unload.fb popstate.fb', function() {
4958				$.fancybox.getInstance( 'close', true, 0 );
4959			});
4960      
4961      
4962
4963			// Check current hash and trigger click event on matching element to start fancyBox, if needed
4964			triggerFromUrl( parseUrl() );
4965
4966		Comment by Bhaveh */ 
4967    
4968    $(window).on('mousemove.fancybox-stage', function() {                  
4969								$.fancybox.getInstance( 'showControls', true, 0 ); 
4970					self.idleSecondsCounter = 4;
4971                }); }, 50);
4972
4973    });
4974
4975
4976}(document, window, window.jQuery));
4977/*!
4978 * Masonry PACKAGED v4.0.0
4979 * Cascading grid layout library
4980 * http://masonry.desandro.com
4981 * MIT License
4982 * by David DeSandro
4983 */
4984
4985/**
4986 * Bridget makes jQuery widgets
4987 * v2.0.0
4988 * MIT license
4989 */
4990
4991/* jshint browser: true, strict: true, undef: true, unused: true */
4992
4993( function( window, factory ) {
4994  'use strict';
4995  /* globals define: false, module: false, require: false */
4996
4997  if ( typeof define == 'function' && define.amd ) {
4998    // AMD
4999    define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
5000      factory( window, jQuery );
5001    });
5002  } else if ( typeof module == 'object' && module.exports ) {
5003    // CommonJS
5004    module.exports = factory(
5005      window,
5006      require('jquery')
5007    );
5008  } else {
5009    // browser global
5010    window.jQueryBridget = factory(
5011      window,
5012      window.jQuery
5013    );
5014  }
5015
5016}( window, function factory( window, jQuery ) {
5017'use strict';
5018
5019// ----- utils ----- //
5020
5021var arraySlice = Array.prototype.slice;
5022
5023// helper function for logging errors
5024// $.error breaks jQuery chaining
5025var console = window.console;
5026var logError = typeof console == 'undefined' ? function() {} :
5027  function( message ) {
5028    console.error( message );
5029  };
5030
5031// ----- jQueryBridget ----- //
5032
5033function jQueryBridget( namespace, PluginClass, $ ) {
5034  $ = $ || jQuery || window.jQuery;
5035  if ( !$ ) {
5036    return;
5037  }
5038
5039  // add option method -> $().plugin('option', {...})
5040  if ( !PluginClass.prototype.option ) {
5041    // option setter
5042    PluginClass.prototype.option = function( opts ) {
5043      // bail out if not an object
5044      if ( !$.isPlainObject( opts ) ){
5045        return;
5046      }
5047      this.options = $.extend( true, this.options, opts );
5048    };
5049  }
5050
5051  // make jQuery plugin
5052  $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
5053    if ( typeof arg0 == 'string' ) {
5054      // method call $().plugin( 'methodName', { options } )
5055      // shift arguments by 1
5056      var args = arraySlice.call( arguments, 1 );
5057      return methodCall( this, arg0, args );
5058    }
5059    // just $().plugin({ options })
5060    plainCall( this, arg0 );
5061    return this;
5062  };
5063
5064  // $().plugin('methodName')
5065  function methodCall( $elems, methodName, args ) {
5066    var returnValue;
5067    var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
5068
5069    $elems.each( function( i, elem ) {
5070      // get instance
5071      var instance = $.data( elem, namespace );
5072      if ( !instance ) {
5073        logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
5074          pluginMethodStr );
5075        return;
5076      }
5077
5078      var method = instance[ methodName ];
5079      if ( !method || methodName.charAt(0) == '_' ) {
5080        logError( pluginMethodStr + ' is not a valid method' );
5081        return;
5082      }
5083
5084      // apply method, get return value
5085      var value = method.apply( instance, args );
5086      // set return value if value is returned, use only first value
5087      returnValue = returnValue === undefined ? value : returnValue;
5088    });
5089
5090    return returnValue !== undefined ? returnValue : $elems;
5091  }
5092
5093  function plainCall( $elems, options ) {
5094    $elems.each( function( i, elem ) {
5095      var instance = $.data( elem, namespace );
5096      if ( instance ) {
5097        // set options & init
5098        instance.option( options );
5099        instance._init();
5100      } else {
5101        // initialize new instance
5102        instance = new PluginClass( elem, options );
5103        $.data( elem, namespace, instance );
5104      }
5105    });
5106  }
5107
5108  updateJQuery( $ );
5109
5110}
5111
5112// ----- updateJQuery ----- //
5113
5114// set $.bridget for v1 backwards compatibility
5115function updateJQuery( $ ) {
5116  if ( !$ || ( $ && $.bridget ) ) {
5117    return;
5118  }
5119  $.bridget = jQueryBridget;
5120}
5121
5122updateJQuery( jQuery || window.jQuery );
5123
5124// -----  ----- //
5125
5126return jQueryBridget;
5127
5128}));
5129
5130/**
5131 * EvEmitter v1.0.1
5132 * Lil' event emitter
5133 * MIT License
5134 */
5135
5136/* jshint unused: true, undef: true, strict: true */
5137
5138( function( global, factory ) {
5139  // universal module definition
5140  /* jshint strict: false */ /* globals define, module */
5141  if ( typeof define == 'function' && define.amd ) {
5142    // AMD - RequireJS
5143    define( 'ev-emitter/ev-emitter',factory );
5144  } else if ( typeof module == 'object' && module.exports ) {
5145    // CommonJS - Browserify, Webpack
5146    module.exports = factory();
5147  } else {
5148    // Browser globals
5149    global.EvEmitter = factory();
5150  }
5151
5152}( this, function() {
5153
5154
5155
5156function EvEmitter() {}
5157
5158var proto = EvEmitter.prototype;
5159
5160proto.on = function( eventName, listener ) {
5161  if ( !eventName || !listener ) {
5162    return;
5163  }
5164  // set events hash
5165  var events = this._events = this._events || {};
5166  // set listeners array
5167  var listeners = events[ eventName ] = events[ eventName ] || [];
5168  // only add once
5169  if ( listeners.indexOf( listener ) == -1 ) {
5170    listeners.push( listener );
5171  }
5172
5173  return this;
5174};
5175
5176proto.once = function( eventName, listener ) {
5177  if ( !eventName || !listener ) {
5178    return;
5179  }
5180  // add event
5181  this.on( eventName, listener );
5182  // set once flag
5183  // set onceEvents hash
5184  var onceEvents = this._onceEvents = this._onceEvents || {};
5185  // set onceListeners array
5186  var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || [];
5187  // set flag
5188  onceListeners[ listener ] = true;
5189
5190  return this;
5191};
5192
5193proto.off = function( eventName, listener ) {
5194  var listeners = this._events && this._events[ eventName ];
5195  if ( !listeners || !listeners.length ) {
5196    return;
5197  }
5198  var index = listeners.indexOf( listener );
5199  if ( index != -1 ) {
5200    listeners.splice( index, 1 );
5201  }
5202
5203  return this;
5204};
5205
5206proto.emitEvent = function( eventName, args ) {
5207  var listeners = this._events && this._events[ eventName ];
5208  if ( !listeners || !listeners.length ) {
5209    return;
5210  }
5211  var i = 0;
5212  var listener = listeners[i];
5213  args = args || [];
5214  // once stuff
5215  var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
5216
5217  while ( listener ) {
5218    var isOnce = onceListeners && onceListeners[ listener ];
5219    if ( isOnce ) {
5220      // remove listener
5221      // remove before trigger to prevent recursion
5222      this.off( eventName, listener );
5223      // unset once flag
5224      delete onceListeners[ listener ];
5225    }
5226    // trigger listener
5227    listener.apply( this, args );
5228    // get next listener
5229    i += isOnce ? 0 : 1;
5230    listener = listeners[i];
5231  }
5232
5233  return this;
5234};
5235
5236return EvEmitter;
5237
5238}));
5239
5240/*!
5241 * getSize v2.0.2
5242 * measure size of elements
5243 * MIT license
5244 */
5245
5246/*jshint browser: true, strict: true, undef: true, unused: true */
5247/*global define: false, module: false, console: false */
5248
5249( function( window, factory ) {
5250  'use strict';
5251
5252  if ( typeof define == 'function' && define.amd ) {
5253    // AMD
5254    define( 'get-size/get-size',[],function() {
5255      return factory();
5256    });
5257  } else if ( typeof module == 'object' && module.exports ) {
5258    // CommonJS
5259    module.exports = factory();
5260  } else {
5261    // browser global
5262    window.getSize = factory();
5263  }
5264
5265})( window, function factory() {
5266'use strict';
5267
5268// -------------------------- helpers -------------------------- //
5269
5270// get a number from a string, not a percentage
5271function getStyleSize( value ) {
5272  var num = parseFloat( value );
5273  // not a percent like '100%', and a number
5274  var isValid = value.indexOf('%') == -1 && !isNaN( num );
5275  return isValid && num;
5276}
5277
5278function noop() {}
5279
5280var logError = typeof console == 'undefined' ? noop :
5281  function( message ) {
5282    console.error( message );
5283  };
5284
5285// -------------------------- measurements -------------------------- //
5286
5287var measurements = [
5288  'paddingLeft',
5289  'paddingRight',
5290  'paddingTop',
5291  'paddingBottom',
5292  'marginLeft',
5293  'marginRight',
5294  'marginTop',
5295  'marginBottom',
5296  'borderLeftWidth',
5297  'borderRightWidth',
5298  'borderTopWidth',
5299  'borderBottomWidth'
5300];
5301
5302var measurementsLength = measurements.length;
5303
5304function getZeroSize() {
5305  var size = {
5306    width: 0,
5307    height: 0,
5308    innerWidth: 0,
5309    innerHeight: 0,
5310    outerWidth: 0,
5311    outerHeight: 0
5312  };
5313  for ( var i=0; i < measurementsLength; i++ ) {
5314    var measurement = measurements[i];
5315    size[ measurement ] = 0;
5316  }
5317  return size;
5318}
5319
5320// -------------------------- getStyle -------------------------- //
5321
5322/**
5323 * getStyle, get style of element, check for Firefox bug
5324 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
5325 */
5326function getStyle( elem ) {
5327  var style = getComputedStyle( elem );
5328  if ( !style ) {
5329    logError( 'Style returned ' + style +
5330      '. Are you running this code in a hidden iframe on Firefox? ' +
5331      'See http://bit.ly/getsizebug1' );
5332  }
5333  return style;
5334}
5335
5336// -------------------------- setup -------------------------- //
5337
5338var isSetup = false;
5339
5340var isBoxSizeOuter;
5341
5342/**
5343 * setup
5344 * check isBoxSizerOuter
5345 * do on first getSize() rather than on page load for Firefox bug
5346 */
5347function setup() {
5348  // setup once
5349  if ( isSetup ) {
5350    return;
5351  }
5352  isSetup = true;
5353
5354  // -------------------------- box sizing -------------------------- //
5355
5356  /**
5357   * WebKit measures the outer-width on style.width on border-box elems
5358   * IE & Firefox<29 measures the inner-width
5359   */
5360  var div = document.createElement('div');
5361  div.style.width = '200px';
5362  div.style.padding = '1px 2px 3px 4px';
5363  div.style.borderStyle = 'solid';
5364  div.style.borderWidth = '1px 2px 3px 4px';
5365  div.style.boxSizing = 'border-box';
5366
5367  var body = document.body || document.documentElement;
5368  body.appendChild( div );
5369  var style = getStyle( div );
5370
5371  getSize.isBoxSizeOuter = isBoxSizeOuter = getStyleSize( style.width ) == 200;
5372  body.removeChild( div );
5373
5374}
5375
5376// -------------------------- getSize -------------------------- //
5377
5378function getSize( elem ) {
5379  setup();
5380
5381  // use querySeletor if elem is string
5382  if ( typeof elem == 'string' ) {
5383    elem = document.querySelector( elem );
5384  }
5385
5386  // do not proceed on non-objects
5387  if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
5388    return;
5389  }
5390
5391  var style = getStyle( elem );
5392
5393  // if hidden, everything is 0
5394  if ( style.display == 'none' ) {
5395    return getZeroSize();
5396  }
5397
5398  var size = {};
5399  size.width = elem.offsetWidth;
5400  size.height = elem.offsetHeight;
5401
5402  var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
5403
5404  // get all measurements
5405  for ( var i=0; i < measurementsLength; i++ ) {
5406    var measurement = measurements[i];
5407    var value = style[ measurement ];
5408    var num = parseFloat( value );
5409    // any 'auto', 'medium' value will be 0
5410    size[ measurement ] = !isNaN( num ) ? num : 0;
5411  }
5412
5413  var paddingWidth = size.paddingLeft + size.paddingRight;
5414  var paddingHeight = size.paddingTop + size.paddingBottom;
5415  var marginWidth = size.marginLeft + size.marginRight;
5416  var marginHeight = size.marginTop + size.marginBottom;
5417  var borderWidth = size.borderLeftWidth + size.borderRightWidth;
5418  var borderHeight = size.borderTopWidth + size.borderBottomWidth;
5419
5420  var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
5421
5422  // overwrite width and height if we can get it from style
5423  var styleWidth = getStyleSize( style.width );
5424  if ( styleWidth !== false ) {
5425    size.width = styleWidth +
5426      // add padding and border unless it's already including it
5427      ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
5428  }
5429
5430  var styleHeight = getStyleSize( style.height );
5431  if ( styleHeight !== false ) {
5432    size.height = styleHeight +
5433      // add padding and border unless it's already including it
5434      ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
5435  }
5436
5437  size.innerWidth = size.width - ( paddingWidth + borderWidth );
5438  size.innerHeight = size.height - ( paddingHeight + borderHeight );
5439
5440  size.outerWidth = size.width + marginWidth;
5441  size.outerHeight = size.height + marginHeight;
5442
5443  return size;
5444}
5445
5446return getSize;
5447
5448});
5449
5450/**
5451 * matchesSelector v2.0.1
5452 * matchesSelector( element, '.selector' )
5453 * MIT license
5454 */
5455
5456/*jshint browser: true, strict: true, undef: true, unused: true */
5457
5458( function( window, factory ) {
5459  /*global define: false, module: false */
5460  'use strict';
5461  // universal module definition
5462  if ( typeof define == 'function' && define.amd ) {
5463    // AMD
5464    define( 'matches-selector/matches-selector',factory );
5465  } else if ( typeof module == 'object' && module.exports ) {
5466    // CommonJS
5467    module.exports = factory();
5468  } else {
5469    // browser global
5470    window.matchesSelector = factory();
5471  }
5472
5473}( window, function factory() {
5474  'use strict';
5475
5476  var matchesMethod = ( function() {
5477    var ElemProto = Element.prototype;
5478    // check for the standard method name first
5479    if ( ElemProto.matches ) {
5480      return 'matches';
5481    }
5482    // check un-prefixed
5483    if ( ElemProto.matchesSelector ) {
5484      return 'matchesSelector';
5485    }
5486    // check vendor prefixes
5487    var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
5488
5489    for ( var i=0; i < prefixes.length; i++ ) {
5490      var prefix = prefixes[i];
5491      var method = prefix + 'MatchesSelector';
5492      if ( ElemProto[ method ] ) {
5493        return method;
5494      }
5495    }
5496  })();
5497
5498  return function matchesSelector( elem, selector ) {
5499    return elem[ matchesMethod ]( selector );
5500  };
5501
5502}));
5503
5504/**
5505 * Fizzy UI utils v2.0.0
5506 * MIT license
5507 */
5508
5509/*jshint browser: true, undef: true, unused: true, strict: true */
5510
5511( function( window, factory ) {
5512  /*global define: false, module: false, require: false */
5513  'use strict';
5514  // universal module definition
5515
5516  if ( typeof define == 'function' && define.amd ) {
5517    // AMD
5518    define( 'fizzy-ui-utils/utils',[
5519      'matches-selector/matches-selector'
5520    ], function( matchesSelector ) {
5521      return factory( window, matchesSelector );
5522    });
5523  } else if ( typeof module == 'object' && module.exports ) {
5524    // CommonJS
5525    module.exports = factory(
5526      window,
5527      require('desandro-matches-selector')
5528    );
5529  } else {
5530    // browser global
5531    window.fizzyUIUtils = factory(
5532      window,
5533      window.matchesSelector
5534    );
5535  }
5536
5537}( window, function factory( window, matchesSelector ) {
5538
5539
5540
5541var utils = {};
5542
5543// ----- extend ----- //
5544
5545// extends objects
5546utils.extend = function( a, b ) {
5547  for ( var prop in b ) {
5548    a[ prop ] = b[ prop ];
5549  }
5550  return a;
5551};
5552
5553// ----- modulo ----- //
5554
5555utils.modulo = function( num, div ) {
5556  return ( ( num % div ) + div ) % div;
5557};
5558
5559// ----- makeArray ----- //
5560
5561// turn element or nodeList into an array
5562utils.makeArray = function( obj ) {
5563  var ary = [];
5564  if ( Array.isArray( obj ) ) {
5565    // use object if already an array
5566    ary = obj;
5567  } else if ( obj && typeof obj.length == 'number' ) {
5568    // convert nodeList to array
5569    for ( var i=0; i < obj.length; i++ ) {
5570      ary.push( obj[i] );
5571    }
5572  } else {
5573    // array of single index
5574    ary.push( obj );
5575  }
5576  return ary;
5577};
5578
5579// ----- removeFrom ----- //
5580
5581utils.removeFrom = function( ary, obj ) {
5582  var index = ary.indexOf( obj );
5583  if ( index != -1 ) {
5584    ary.splice( index, 1 );
5585  }
5586};
5587
5588// ----- getParent ----- //
5589
5590utils.getParent = function( elem, selector ) {
5591  while ( elem != document.body ) {
5592    elem = elem.parentNode;
5593    if ( matchesSelector( elem, selector ) ) {
5594      return elem;
5595    }
5596  }
5597};
5598
5599// ----- getQueryElement ----- //
5600
5601// use element as selector string
5602utils.getQueryElement = function( elem ) {
5603  if ( typeof elem == 'string' ) {
5604    return document.querySelector( elem );
5605  }
5606  return elem;
5607};
5608
5609// ----- handleEvent ----- //
5610
5611// enable .ontype to trigger from .addEventListener( elem, 'type' )
5612utils.handleEvent = function( event ) {
5613  var method = 'on' + event.type;
5614  if ( this[ method ] ) {
5615    this[ method ]( event );
5616  }
5617};
5618
5619// ----- filterFindElements ----- //
5620
5621utils.filterFindElements = function( elems, selector ) {
5622  // make array of elems
5623  elems = utils.makeArray( elems );
5624  var ffElems = [];
5625
5626  elems.forEach( function( elem ) {
5627    // check that elem is an actual element
5628    if ( !( elem instanceof HTMLElement ) ) {
5629      return;
5630    }
5631    // add elem if no selector
5632    if ( !selector ) {
5633      ffElems.push( elem );
5634      return;
5635    }
5636    // filter & find items if we have a selector
5637    // filter
5638    if ( matchesSelector( elem, selector ) ) {
5639      ffElems.push( elem );
5640    }
5641    // find children
5642    var childElems = elem.querySelectorAll( selector );
5643    // concat childElems to filterFound array
5644    for ( var i=0; i < childElems.length; i++ ) {
5645      ffElems.push( childElems[i] );
5646    }
5647  });
5648
5649  return ffElems;
5650};
5651
5652// ----- debounceMethod ----- //
5653
5654utils.debounceMethod = function( _class, methodName, threshold ) {
5655  // original method
5656  var method = _class.prototype[ methodName ];
5657  var timeoutName = methodName + 'Timeout';
5658
5659  _class.prototype[ methodName ] = function() {
5660    var timeout = this[ timeoutName ];
5661    if ( timeout ) {
5662      clearTimeout( timeout );
5663    }
5664    var args = arguments;
5665
5666    var _this = this;
5667    this[ timeoutName ] = setTimeout( function() {
5668      method.apply( _this, args );
5669      delete _this[ timeoutName ];
5670    }, threshold || 100 );
5671  };
5672};
5673
5674// ----- docReady ----- //
5675
5676utils.docReady = function( callback ) {
5677  if ( document.readyState == 'complete' ) {
5678    callback();
5679  } else {
5680    document.addEventListener( 'DOMContentLoaded', callback );
5681  }
5682};
5683
5684// ----- htmlInit ----- //
5685
5686// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
5687utils.toDashed = function( str ) {
5688  return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
5689    return $1 + '-' + $2;
5690  }).toLowerCase();
5691};
5692
5693var console = window.console;
5694/**
5695 * allow user to initialize classes via [data-namespace] or .js-namespace class
5696 * htmlInit( Widget, 'widgetName' )
5697 * options are parsed from data-namespace-options
5698 */
5699utils.htmlInit = function( WidgetClass, namespace ) {
5700  utils.docReady( function() {
5701    var dashedNamespace = utils.toDashed( namespace );
5702    var dataAttr = 'data-' + dashedNamespace;
5703    var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
5704    var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
5705    var elems = utils.makeArray( dataAttrElems )
5706      .concat( utils.makeArray( jsDashElems ) );
5707    var dataOptionsAttr = dataAttr + '-options';
5708    var jQuery = window.jQuery;
5709
5710    elems.forEach( function( elem ) {
5711      var attr = elem.getAttribute( dataAttr ) ||
5712        elem.getAttribute( dataOptionsAttr );
5713      var options;
5714      try {
5715        options = attr && JSON.parse( attr );
5716      } catch ( error ) {
5717        // log error, do not initialize
5718        if ( console ) {
5719          console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
5720          ': ' + error );
5721        }
5722        return;
5723      }
5724      // initialize
5725      var instance = new WidgetClass( elem, options );
5726      // make available via $().data('layoutname')
5727      if ( jQuery ) {
5728        jQuery.data( elem, namespace, instance );
5729      }
5730    });
5731
5732  });
5733};
5734
5735// -----  ----- //
5736
5737return utils;
5738
5739}));
5740
5741/**
5742 * Outlayer Item
5743 */
5744
5745( function( window, factory ) {
5746  // universal module definition
5747  /* jshint strict: false */ /* globals define, module, require */
5748  if ( typeof define == 'function' && define.amd ) {
5749    // AMD - RequireJS
5750    define( 'outlayer/item',[
5751        'ev-emitter/ev-emitter',
5752        'get-size/get-size'
5753      ],
5754      function( EvEmitter, getSize ) {
5755        return factory( window, EvEmitter, getSize );
5756      }
5757    );
5758  } else if ( typeof module == 'object' && module.exports ) {
5759    // CommonJS - Browserify, Webpack
5760    module.exports = factory(
5761      window,
5762      require('ev-emitter'),
5763      require('get-size')
5764    );
5765  } else {
5766    // browser global
5767    window.Outlayer = {};
5768    window.Outlayer.Item = factory(
5769      window,
5770      window.EvEmitter,
5771      window.getSize
5772    );
5773  }
5774
5775}( window, function factory( window, EvEmitter, getSize ) {
5776'use strict';
5777
5778// ----- helpers ----- //
5779
5780function isEmptyObj( obj ) {
5781  for ( var prop in obj ) {
5782    return false;
vendor: 4,257 bytes, lines 5783-5938
5783  }
5784  prop = null;
5785  return true;
5786}
5787
5788// -------------------------- CSS3 support -------------------------- //
5789
5790
5791var docElemStyle = document.documentElement.style;
5792
5793var transitionProperty = typeof docElemStyle.transition == 'string' ?
5794  'transition' : 'WebkitTransition';
5795var transformProperty = typeof docElemStyle.transform == 'string' ?
5796  'transform' : 'WebkitTransform';
5797
5798var transitionEndEvent = {
5799  WebkitTransition: 'webkitTransitionEnd',
5800  transition: 'transitionend'
5801}[ transitionProperty ];
5802
5803// cache all vendor properties
5804var vendorProperties = [
5805  transformProperty,
5806  transitionProperty,
5807  transitionProperty + 'Duration',
5808  transitionProperty + 'Property'
5809];
5810
5811// -------------------------- Item -------------------------- //
5812
5813function Item( element, layout ) {
5814  if ( !element ) {
5815    return;
5816  }
5817
5818  this.element = element;
5819  // parent layout class, i.e. Masonry, Isotope, or Packery
5820  this.layout = layout;
5821  this.position = {
5822    x: 0,
5823    y: 0
5824  };
5825
5826  this._create();
5827}
5828
5829// inherit EvEmitter
5830var proto = Item.prototype = Object.create( EvEmitter.prototype );
5831proto.constructor = Item;
5832
5833proto._create = function() {
5834  // transition objects
5835  this._transn = {
5836    ingProperties: {},
5837    clean: {},
5838    onEnd: {}
5839  };
5840
5841  this.css({
5842    position: 'absolute'
5843  });
5844};
5845
5846// trigger specified handler for event type
5847proto.handleEvent = function( event ) {
5848  var method = 'on' + event.type;
5849  if ( this[ method ] ) {
5850    this[ method ]( event );
5851  }
5852};
5853
5854proto.getSize = function() {
5855  this.size = getSize( this.element );
5856};
5857
5858/**
5859 * apply CSS styles to element
5860 * @param {Object} style
5861 */
5862proto.css = function( style ) {
5863  var elemStyle = this.element.style;
5864
5865  for ( var prop in style ) {
5866    // use vendor property if available
5867    var supportedProp = vendorProperties[ prop ] || prop;
5868    elemStyle[ supportedProp ] = style[ prop ];
5869  }
5870};
5871
5872 // measure position, and sets it
5873proto.getPosition = function() {
5874  var style = getComputedStyle( this.element );
5875  var isOriginLeft = this.layout._getOption('originLeft');
5876  var isOriginTop = this.layout._getOption('originTop');
5877  var xValue = style[ isOriginLeft ? 'left' : 'right' ];
5878  var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
5879  // convert percent to pixels
5880  var layoutSize = this.layout.size;
5881  var x = xValue.indexOf('%') != -1 ?
5882    ( parseFloat( xValue ) / 100 ) * layoutSize.width : parseInt( xValue, 10 );
5883  var y = yValue.indexOf('%') != -1 ?
5884    ( parseFloat( yValue ) / 100 ) * layoutSize.height : parseInt( yValue, 10 );
5885
5886  // clean up 'auto' or other non-integer values
5887  x = isNaN( x ) ? 0 : x;
5888  y = isNaN( y ) ? 0 : y;
5889  // remove padding from measurement
5890  x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
5891  y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
5892
5893  this.position.x = x;
5894  this.position.y = y;
5895};
5896
5897// set settled position, apply padding
5898proto.layoutPosition = function() {
5899  var layoutSize = this.layout.size;
5900  var style = {};
5901  var isOriginLeft = this.layout._getOption('originLeft');
5902  var isOriginTop = this.layout._getOption('originTop');
5903
5904  // x
5905  var xPadding = isOriginLeft ? 'paddingLeft' : 'paddingRight';
5906  var xProperty = isOriginLeft ? 'left' : 'right';
5907  var xResetProperty = isOriginLeft ? 'right' : 'left';
5908
5909  var x = this.position.x + layoutSize[ xPadding ];
5910  // set in percentage or pixels
5911  style[ xProperty ] = this.getXValue( x );
5912  // reset other property
5913  style[ xResetProperty ] = '';
5914
5915  // y
5916  var yPadding = isOriginTop ? 'paddingTop' : 'paddingBottom';
5917  var yProperty = isOriginTop ? 'top' : 'bottom';
5918  var yResetProperty = isOriginTop ? 'bottom' : 'top';
5919
5920  var y = this.position.y + layoutSize[ yPadding ];
5921  // set in percentage or pixels
5922  style[ yProperty ] = this.getYValue( y );
5923  // reset other property
5924  style[ yResetProperty ] = '';
5925
5926  this.css( style );
5927  this.emitEvent( 'layout', [ this ] );
5928};
5929
5930proto.getXValue = function( x ) {
5931  var isHorizontal = this.layout._getOption('horizontal');
5932  return this.layout.options.percentPosition && !isHorizontal ?
5933    ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
5934};
5935
5936proto.getYValue = function( y ) {
5937  var isHorizontal = this.layout._getOption('horizontal');
5938  return this.layout.options.percentPosition && isHorizontal ?
5939    ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
5940};
5941
5942proto._transitionTo = function( x, y ) {
5943  this.getPosition();
5944  // get current x & y from top/left
5945  var curX = this.position.x;
5946  var curY = this.position.y;
5947
5948  var compareX = parseInt( x, 10 );
5949  var compareY = parseInt( y, 10 );
5950  var didNotMove = compareX === this.position.x && compareY === this.position.y;
5951
5952  // save end position
5953  this.setPosition( x, y );
5954
5955  // if did not move and not transitioning, just go to layout
5956  if ( didNotMove && !this.isTransitioning ) {
5957    this.layoutPosition();
5958    return;
5959  }
5960
5961  var transX = x - curX;
5962  var transY = y - curY;
5963  var transitionStyle = {};
5964  transitionStyle.transform = this.getTranslate( transX, transY );
5965
5966  this.transition({
5967    to: transitionStyle,
5968    onTransitionEnd: {
5969      transform: this.layoutPosition
5970    },
5971    isCleaning: true
5972  });
5973};
5974
5975proto.getTranslate = function( x, y ) {
5976  // flip cooridinates if origin on right or bottom
5977  var isOriginLeft = this.layout._getOption('originLeft');
5978  var isOriginTop = this.layout._getOption('originTop');
5979  x = isOriginLeft ? x : -x;
5980  y = isOriginTop ? y : -y;
5981  return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
5982};
5983
5984// non transition + transform support
5985proto.goTo = function( x, y ) {
5986  this.setPosition( x, y );
5987  this.layoutPosition();
5988};
5989
5990proto.moveTo = proto._transitionTo;
5991
5992proto.setPosition = function( x, y ) {
5993  this.position.x = parseInt( x, 10 );
5994  this.position.y = parseInt( y, 10 );
5995};
5996
5997// ----- transition ----- //
5998
5999/**
6000 * @param {Object} style - CSS
6001 * @param {Function} onTransitionEnd
6002 */
6003
6004// non transition, just trigger callback
6005proto._nonTransition = function( args ) {
6006  this.css( args.to );
6007  if ( args.isCleaning ) {
6008    this._removeStyles( args.to );
6009  }
6010  for ( var prop in args.onTransitionEnd ) {
6011    args.onTransitionEnd[ prop ].call( this );
6012  }
6013};
6014
6015/**
6016 * proper transition
6017 * @param {Object} args - arguments
6018 *   @param {Object} to - style to transition to
6019 *   @param {Object} from - style to start transition from
6020 *   @param {Boolean} isCleaning - removes transition styles after transition
6021 *   @param {Function} onTransitionEnd - callback
6022 */
6023proto._transition = function( args ) {
6024  // redirect to nonTransition if no transition duration
6025  if ( !parseFloat( this.layout.options.transitionDuration ) ) {
6026    this._nonTransition( args );
6027    return;
6028  }
6029
6030  var _transition = this._transn;
6031  // keep track of onTransitionEnd callback by css property
6032  for ( var prop in args.onTransitionEnd ) {
6033    _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
6034  }
6035  // keep track of properties that are transitioning
6036  for ( prop in args.to ) {
6037    _transition.ingProperties[ prop ] = true;
6038    // keep track of properties to clean up when transition is done
6039    if ( args.isCleaning ) {
6040      _transition.clean[ prop ] = true;
6041    }
6042  }
6043
6044  // set from styles
6045  if ( args.from ) {
6046    this.css( args.from );
6047    // force redraw. http://blog.alexmaccaw.com/css-transitions
6048    var h = this.element.offsetHeight;
6049    // hack for JSHint to hush about unused var
6050    h = null;
6051  }
6052  // enable transition
6053  this.enableTransition( args.to );
6054  // set styles that are transitioning
6055  this.css( args.to );
6056
6057  this.isTransitioning = true;
6058
6059};
6060
6061// dash before all cap letters, including first for
6062// WebkitTransform => -webkit-transform
6063function toDashedAll( str ) {
6064  return str.replace( /([A-Z])/g, function( $1 ) {
6065    return '-' + $1.toLowerCase();
6066  });
6067}
6068
6069var transitionProps = 'opacity,' +
6070  toDashedAll( vendorProperties.transform || 'transform' );
6071
6072proto.enableTransition = function(/* style */) {
6073  // HACK changing transitionProperty during a transition
6074  // will cause transition to jump
6075  if ( this.isTransitioning ) {
6076    return;
6077  }
6078
6079  // make `transition: foo, bar, baz` from style object
6080  // HACK un-comment this when enableTransition can work
6081  // while a transition is happening
6082  // var transitionValues = [];
6083  // for ( var prop in style ) {
6084  //   // dash-ify camelCased properties like WebkitTransition
6085  //   prop = vendorProperties[ prop ] || prop;
6086  //   transitionValues.push( toDashedAll( prop ) );
6087  // }
6088  // enable transition styles
6089  this.css({
6090    transitionProperty: transitionProps,
6091    transitionDuration: this.layout.options.transitionDuration
6092  });
6093  // listen for transition end event
6094  this.element.addEventListener( transitionEndEvent, this, false );
6095};
6096
6097proto.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ];
6098
6099// ----- events ----- //
6100
6101proto.onwebkitTransitionEnd = function( event ) {
6102  this.ontransitionend( event );
6103};
6104
6105proto.onotransitionend = function( event ) {
6106  this.ontransitionend( event );
6107};
6108
6109// properties that I munge to make my life easier
6110var dashedVendorProperties = {
6111  '-webkit-transform': 'transform'
6112};
6113
6114proto.ontransitionend = function( event ) {
6115  // disregard bubbled events from children
6116  if ( event.target !== this.element ) {
6117    return;
6118  }
6119  var _transition = this._transn;
6120  // get property name of transitioned property, convert to prefix-free
6121  var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
6122
6123  // remove property that has completed transitioning
6124  delete _transition.ingProperties[ propertyName ];
6125  // check if any properties are still transitioning
6126  if ( isEmptyObj( _transition.ingProperties ) ) {
6127    // all properties have completed transitioning
6128    this.disableTransition();
6129  }
6130  // clean style
6131  if ( propertyName in _transition.clean ) {
6132    // clean up style
6133    this.element.style[ event.propertyName ] = '';
6134    delete _transition.clean[ propertyName ];
6135  }
6136  // trigger onTransitionEnd callback
6137  if ( propertyName in _transition.onEnd ) {
6138    var onTransitionEnd = _transition.onEnd[ propertyName ];
6139    onTransitionEnd.call( this );
6140    delete _transition.onEnd[ propertyName ];
6141  }
6142
6143  this.emitEvent( 'transitionEnd', [ this ] );
6144};
6145
6146proto.disableTransition = function() {
6147  this.removeTransitionStyles();
6148  this.element.removeEventListener( transitionEndEvent, this, false );
6149  this.isTransitioning = false;
vendor: 8,827 bytes, lines 6150-6532
6150};
6151
6152/**
6153 * removes style property from element
6154 * @param {Object} style
6155**/
6156proto._removeStyles = function( style ) {
6157  // clean up transition styles
6158  var cleanStyle = {};
6159  for ( var prop in style ) {
6160    cleanStyle[ prop ] = '';
6161  }
6162  this.css( cleanStyle );
6163};
6164
6165var cleanTransitionStyle = {
6166  transitionProperty: '',
6167  transitionDuration: ''
6168};
6169
6170proto.removeTransitionStyles = function() {
6171  // remove transition
6172  this.css( cleanTransitionStyle );
6173};
6174
6175// ----- show/hide/remove ----- //
6176
6177// remove element from DOM
6178proto.removeElem = function() {
6179  this.element.parentNode.removeChild( this.element );
6180  // remove display: none
6181  this.css({ display: '' });
6182  this.emitEvent( 'remove', [ this ] );
6183};
6184
6185proto.remove = function() {
6186  // just remove element if no transition support or no transition
6187  if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
6188    this.removeElem();
6189    return;
6190  }
6191
6192  // start transition
6193  this.once( 'transitionEnd', function() {
6194    this.removeElem();
6195  });
6196  this.hide();
6197};
6198
6199proto.reveal = function() {
6200  delete this.isHidden;
6201  // remove display: none
6202  this.css({ display: '' });
6203
6204  var options = this.layout.options;
6205
6206  var onTransitionEnd = {};
6207  var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
6208  onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
6209
6210  this.transition({
6211    from: options.hiddenStyle,
6212    to: options.visibleStyle,
6213    isCleaning: true,
6214    onTransitionEnd: onTransitionEnd
6215  });
6216};
6217
6218proto.onRevealTransitionEnd = function() {
6219  // check if still visible
6220  // during transition, item may have been hidden
6221  if ( !this.isHidden ) {
6222    this.emitEvent('reveal');
6223  }
6224};
6225
6226/**
6227 * get style property use for hide/reveal transition end
6228 * @param {String} styleProperty - hiddenStyle/visibleStyle
6229 * @returns {String}
6230 */
6231proto.getHideRevealTransitionEndProperty = function( styleProperty ) {
6232  var optionStyle = this.layout.options[ styleProperty ];
6233  // use opacity
6234  if ( optionStyle.opacity ) {
6235    return 'opacity';
6236  }
6237  // get first property
6238  for ( var prop in optionStyle ) {
6239    return prop;
6240  }
6241};
6242
6243proto.hide = function() {
6244  // set flag
6245  this.isHidden = true;
6246  // remove display: none
6247  this.css({ display: '' });
6248
6249  var options = this.layout.options;
6250
6251  var onTransitionEnd = {};
6252  var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
6253  onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
6254
6255  this.transition({
6256    from: options.visibleStyle,
6257    to: options.hiddenStyle,
6258    // keep hidden stuff hidden
6259    isCleaning: true,
6260    onTransitionEnd: onTransitionEnd
6261  });
6262};
6263
6264proto.onHideTransitionEnd = function() {
6265  // check if still hidden
6266  // during transition, item may have been un-hidden
6267  if ( this.isHidden ) {
6268    this.css({ display: 'none' });
6269    this.emitEvent('hide');
6270  }
6271};
6272
6273proto.destroy = function() {
6274  this.css({
6275    position: '',
6276    left: '',
6277    right: '',
6278    top: '',
6279    bottom: '',
6280    transition: '',
6281    transform: ''
6282  });
6283};
6284
6285return Item;
6286
6287}));
6288
6289/*!
6290 * Outlayer v2.0.0
6291 * the brains and guts of a layout library
6292 * MIT license
6293 */
6294
6295( function( window, factory ) {
6296  'use strict';
6297  // universal module definition
6298  /* jshint strict: false */ /* globals define, module, require */
6299  if ( typeof define == 'function' && define.amd ) {
6300    // AMD - RequireJS
6301    define( 'outlayer/outlayer',[
6302        'ev-emitter/ev-emitter',
6303        'get-size/get-size',
6304        'fizzy-ui-utils/utils',
6305        './item'
6306      ],
6307      function( EvEmitter, getSize, utils, Item ) {
6308        return factory( window, EvEmitter, getSize, utils, Item);
6309      }
6310    );
6311  } else if ( typeof module == 'object' && module.exports ) {
6312    // CommonJS - Browserify, Webpack
6313    module.exports = factory(
6314      window,
6315      require('ev-emitter'),
6316      require('get-size'),
6317      require('fizzy-ui-utils'),
6318      require('./item')
6319    );
6320  } else {
6321    // browser global
6322    window.Outlayer = factory(
6323      window,
6324      window.EvEmitter,
6325      window.getSize,
6326      window.fizzyUIUtils,
6327      window.Outlayer.Item
6328    );
6329  }
6330
6331}( window, function factory( window, EvEmitter, getSize, utils, Item ) {
6332'use strict';
6333
6334// ----- vars ----- //
6335
6336var console = window.console;
6337var jQuery = window.jQuery;
6338var noop = function() {};
6339
6340// -------------------------- Outlayer -------------------------- //
6341
6342// globally unique identifiers
6343var GUID = 0;
6344// internal store of all Outlayer intances
6345var instances = {};
6346
6347
6348/**
6349 * @param {Element, String} element
6350 * @param {Object} options
6351 * @constructor
6352 */
6353function Outlayer( element, options ) {
6354  var queryElement = utils.getQueryElement( element );
6355  if ( !queryElement ) {
6356    if ( console ) {
6357      console.error( 'Bad element for ' + this.constructor.namespace +
6358        ': ' + ( queryElement || element ) );
6359    }
6360    return;
6361  }
6362  this.element = queryElement;
6363  // add jQuery
6364  if ( jQuery ) {
6365    this.$element = jQuery( this.element );
6366  }
6367
6368  // options
6369  this.options = utils.extend( {}, this.constructor.defaults );
6370  this.option( options );
6371
6372  // add id for Outlayer.getFromElement
6373  var id = ++GUID;
6374  this.element.outlayerGUID = id; // expando
6375  instances[ id ] = this; // associate via id
6376
6377  // kick it off
6378  this._create();
6379
6380  var isInitLayout = this._getOption('initLayout');
6381  if ( isInitLayout ) {
6382    this.layout();
6383  }
6384}
6385
6386// settings are for internal use only
6387Outlayer.namespace = 'outlayer';
6388Outlayer.Item = Item;
6389
6390// default options
6391Outlayer.defaults = {
6392  containerStyle: {
6393    position: 'relative'
6394  },
6395  initLayout: true,
6396  originLeft: true,
6397  originTop: true,
6398  resize: true,
6399  resizeContainer: true,
6400  // item options
6401  transitionDuration: '0.4s',
6402  hiddenStyle: {
6403    opacity: 0,
6404    transform: 'scale(0.001)'
6405  },
6406  visibleStyle: {
6407    opacity: 1,
6408    transform: 'scale(1)'
6409  }
6410};
6411
6412var proto = Outlayer.prototype;
6413// inherit EvEmitter
6414utils.extend( proto, EvEmitter.prototype );
6415
6416/**
6417 * set options
6418 * @param {Object} opts
6419 */
6420proto.option = function( opts ) {
6421  utils.extend( this.options, opts );
6422};
6423
6424/**
6425 * get backwards compatible option value, check old name
6426 */
6427proto._getOption = function( option ) {
6428  var oldOption = this.constructor.compatOptions[ option ];
6429  return oldOption && this.options[ oldOption ] !== undefined ?
6430    this.options[ oldOption ] : this.options[ option ];
6431};
6432
6433Outlayer.compatOptions = {
6434  // currentName: oldName
6435  initLayout: 'isInitLayout',
6436  horizontal: 'isHorizontal',
6437  layoutInstant: 'isLayoutInstant',
6438  originLeft: 'isOriginLeft',
6439  originTop: 'isOriginTop',
6440  resize: 'isResizeBound',
6441  resizeContainer: 'isResizingContainer'
6442};
6443
6444proto._create = function() {
6445  // get items from children
6446  this.reloadItems();
6447  // elements that affect layout, but are not laid out
6448  this.stamps = [];
6449  this.stamp( this.options.stamp );
6450  // set container style
6451  utils.extend( this.element.style, this.options.containerStyle );
6452
6453  // bind resize method
6454  var canBindResize = this._getOption('resize');
6455  if ( canBindResize ) {
6456    this.bindResize();
6457  }
6458};
6459
6460// goes through all children again and gets bricks in proper order
6461proto.reloadItems = function() {
6462  // collection of item elements
6463  this.items = this._itemize( this.element.children );
6464};
6465
6466
6467/**
6468 * turn elements into Outlayer.Items to be used in layout
6469 * @param {Array or NodeList or HTMLElement} elems
6470 * @returns {Array} items - collection of new Outlayer Items
6471 */
6472proto._itemize = function( elems ) {
6473
6474  var itemElems = this._filterFindItemElements( elems );
6475  var Item = this.constructor.Item;
6476
6477  // create new Outlayer Items for collection
6478  var items = [];
6479  for ( var i=0; i < itemElems.length; i++ ) {
6480    var elem = itemElems[i];
6481    var item = new Item( elem, this );
6482    items.push( item );
6483  }
6484
6485  return items;
6486};
6487
6488/**
6489 * get item elements to be used in layout
6490 * @param {Array or NodeList or HTMLElement} elems
6491 * @returns {Array} items - item elements
6492 */
6493proto._filterFindItemElements = function( elems ) {
6494  return utils.filterFindElements( elems, this.options.itemSelector );
6495};
6496
6497/**
6498 * getter method for getting item elements
6499 * @returns {Array} elems - collection of item elements
6500 */
6501proto.getItemElements = function() {
6502  return this.items.map( function( item ) {
6503    return item.element;
6504  });
6505};
6506
6507// ----- init & layout ----- //
6508
6509/**
6510 * lays out all items
6511 */
6512proto.layout = function() {
6513  this._resetLayout();
6514  this._manageStamps();
6515
6516  // don't animate first layout
6517  var layoutInstant = this._getOption('layoutInstant');
6518  var isInstant = layoutInstant !== undefined ?
6519    layoutInstant : !this._isLayoutInited;
6520  this.layoutItems( this.items, isInstant );
6521
6522  // flag for initalized
6523  this._isLayoutInited = true;
6524};
6525
6526// _init is alias for layout
6527proto._init = proto.layout;
6528
6529/**
6530 * logic before any new layout
6531 */
6532proto._resetLayout = function() {
vendor: 2,902 bytes, lines 6533-6655
6533  this.getSize();
6534};
6535
6536
6537proto.getSize = function() {
6538  this.size = getSize( this.element );
6539};
6540
6541/**
6542 * get measurement from option, for columnWidth, rowHeight, gutter
6543 * if option is String -> get element from selector string, & get size of element
6544 * if option is Element -> get size of element
6545 * else use option as a number
6546 *
6547 * @param {String} measurement
6548 * @param {String} size - width or height
6549 * @private
6550 */
6551proto._getMeasurement = function( measurement, size ) {
6552  var option = this.options[ measurement ];
6553  var elem;
6554  if ( !option ) {
6555    // default to 0
6556    this[ measurement ] = 0;
6557  } else {
6558    // use option as an element
6559    if ( typeof option == 'string' ) {
6560      elem = this.element.querySelector( option );
6561    } else if ( option instanceof HTMLElement ) {
6562      elem = option;
6563    }
6564    // use size of element, if element
6565    this[ measurement ] = elem ? getSize( elem )[ size ] : option;
6566  }
6567};
6568
6569/**
6570 * layout a collection of item elements
6571 * @api public
6572 */
6573proto.layoutItems = function( items, isInstant ) {
6574  items = this._getItemsForLayout( items );
6575
6576  this._layoutItems( items, isInstant );
6577
6578  this._postLayout();
6579};
6580
6581/**
6582 * get the items to be laid out
6583 * you may want to skip over some items
6584 * @param {Array} items
6585 * @returns {Array} items
6586 */
6587proto._getItemsForLayout = function( items ) {
6588  return items.filter( function( item ) {
6589    return !item.isIgnored;
6590  });
6591};
6592
6593/**
6594 * layout items
6595 * @param {Array} items
6596 * @param {Boolean} isInstant
6597 */
6598proto._layoutItems = function( items, isInstant ) {
6599  this._emitCompleteOnItems( 'layout', items );
6600
6601  if ( !items || !items.length ) {
6602    // no items, emit event with empty array
6603    return;
6604  }
6605
6606  var queue = [];
6607
6608  items.forEach( function( item ) {
6609    // get x/y object from method
6610    var position = this._getItemLayoutPosition( item );
6611    // enqueue
6612    position.item = item;
6613    position.isInstant = isInstant || item.isLayoutInstant;
6614    queue.push( position );
6615  }, this );
6616
6617  this._processLayoutQueue( queue );
6618};
6619
6620/**
6621 * get item layout position
6622 * @param {Outlayer.Item} item
6623 * @returns {Object} x and y position
6624 */
6625proto._getItemLayoutPosition = function( /* item */ ) {
6626  return {
6627    x: 0,
6628    y: 0
6629  };
6630};
6631
6632/**
6633 * iterate over array and position each item
6634 * Reason being - separating this logic prevents 'layout invalidation'
6635 * thx @paul_irish
6636 * @param {Array} queue
6637 */
6638proto._processLayoutQueue = function( queue ) {
6639  queue.forEach( function( obj ) {
6640    this._positionItem( obj.item, obj.x, obj.y, obj.isInstant );
6641  }, this );
6642};
6643
6644/**
6645 * Sets position of item in DOM
6646 * @param {Outlayer.Item} item
6647 * @param {Number} x - horizontal position
6648 * @param {Number} y - vertical position
6649 * @param {Boolean} isInstant - disables transitions
6650 */
6651proto._positionItem = function( item, x, y, isInstant ) {
6652  if ( isInstant ) {
6653    // if not transition, just set CSS
6654    item.goTo( x, y );
6655  } else {
vendor: 6,326 bytes, lines 6655-6918
6655
6656    item.moveTo( x, y );
6657  }
6658};
6659
6660/**
6661 * Any logic you want to do after each layout,
6662 * i.e. size the container
6663 */
6664proto._postLayout = function() {
6665  this.resizeContainer();
6666};
6667
6668proto.resizeContainer = function() {
6669  var isResizingContainer = this._getOption('resizeContainer');
6670  if ( !isResizingContainer ) {
6671    return;
6672  }
6673  var size = this._getContainerSize();
6674  if ( size ) {
6675    this._setContainerMeasure( size.width, true );
6676    this._setContainerMeasure( size.height, false );
6677  }
6678};
6679
6680/**
6681 * Sets width or height of container if returned
6682 * @returns {Object} size
6683 *   @param {Number} width
6684 *   @param {Number} height
6685 */
6686proto._getContainerSize = noop;
6687
6688/**
6689 * @param {Number} measure - size of width or height
6690 * @param {Boolean} isWidth
6691 */
6692proto._setContainerMeasure = function( measure, isWidth ) {
6693  if ( measure === undefined ) {
6694    return;
6695  }
6696
6697  var elemSize = this.size;
6698  // add padding and border width if border box
6699  if ( elemSize.isBorderBox ) {
6700    measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
6701      elemSize.borderLeftWidth + elemSize.borderRightWidth :
6702      elemSize.paddingBottom + elemSize.paddingTop +
6703      elemSize.borderTopWidth + elemSize.borderBottomWidth;
6704  }
6705
6706  measure = Math.max( measure, 0 );
6707  this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
6708};
6709
6710/**
6711 * emit eventComplete on a collection of items events
6712 * @param {String} eventName
6713 * @param {Array} items - Outlayer.Items
6714 */
6715proto._emitCompleteOnItems = function( eventName, items ) {
6716  var _this = this;
6717  function onComplete() {
6718    _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
6719  }
6720
6721  var count = items.length;
6722  if ( !items || !count ) {
6723    onComplete();
6724    return;
6725  }
6726
6727  var doneCount = 0;
6728  function tick() {
6729    doneCount++;
6730    if ( doneCount == count ) {
6731      onComplete();
6732    }
6733  }
6734
6735  // bind callback
6736  items.forEach( function( item ) {
6737    item.once( eventName, tick );
6738  });
6739};
6740
6741/**
6742 * emits events via EvEmitter and jQuery events
6743 * @param {String} type - name of event
6744 * @param {Event} event - original event
6745 * @param {Array} args - extra arguments
6746 */
6747proto.dispatchEvent = function( type, event, args ) {
6748  // add original event to arguments
6749  var emitArgs = event ? [ event ].concat( args ) : args;
6750  this.emitEvent( type, emitArgs );
6751
6752  if ( jQuery ) {
6753    // set this.$element
6754    this.$element = this.$element || jQuery( this.element );
6755    if ( event ) {
6756      // create jQuery event
6757      var $event = jQuery.Event( event );
6758      $event.type = type;
6759      this.$element.trigger( $event, args );
6760    } else {
6761      // just trigger with type if no event available
6762      this.$element.trigger( type, args );
6763    }
6764  }
6765};
6766
6767// -------------------------- ignore & stamps -------------------------- //
6768
6769
6770/**
6771 * keep item in collection, but do not lay it out
6772 * ignored items do not get skipped in layout
6773 * @param {Element} elem
6774 */
6775proto.ignore = function( elem ) {
6776  var item = this.getItem( elem );
6777  if ( item ) {
6778    item.isIgnored = true;
6779  }
6780};
6781
6782/**
6783 * return item to layout collection
6784 * @param {Element} elem
6785 */
6786proto.unignore = function( elem ) {
6787  var item = this.getItem( elem );
6788  if ( item ) {
6789    delete item.isIgnored;
6790  }
6791};
6792
6793/**
6794 * adds elements to stamps
6795 * @param {NodeList, Array, Element, or String} elems
6796 */
6797proto.stamp = function( elems ) {
6798  elems = this._find( elems );
6799  if ( !elems ) {
6800    return;
6801  }
6802
6803  this.stamps = this.stamps.concat( elems );
6804  // ignore
6805  elems.forEach( this.ignore, this );
6806};
6807
6808/**
6809 * removes elements to stamps
6810 * @param {NodeList, Array, or Element} elems
6811 */
6812proto.unstamp = function( elems ) {
6813  elems = this._find( elems );
6814  if ( !elems ){
6815    return;
6816  }
6817
6818  elems.forEach( function( elem ) {
6819    // filter out removed stamp elements
6820    utils.removeFrom( this.stamps, elem );
6821    this.unignore( elem );
6822  }, this );
6823};
6824
6825/**
6826 * finds child elements
6827 * @param {NodeList, Array, Element, or String} elems
6828 * @returns {Array} elems
6829 */
6830proto._find = function( elems ) {
6831  if ( !elems ) {
6832    return;
6833  }
6834  // if string, use argument as selector string
6835  if ( typeof elems == 'string' ) {
6836    elems = this.element.querySelectorAll( elems );
6837  }
6838  elems = utils.makeArray( elems );
6839  return elems;
6840};
6841
6842proto._manageStamps = function() {
6843  if ( !this.stamps || !this.stamps.length ) {
6844    return;
6845  }
6846
6847  this._getBoundingRect();
6848
6849  this.stamps.forEach( this._manageStamp, this );
6850};
6851
6852// update boundingLeft / Top
6853proto._getBoundingRect = function() {
6854  // get bounding rect for container element
6855  var boundingRect = this.element.getBoundingClientRect();
6856  var size = this.size;
6857  this._boundingRect = {
6858    left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
6859    top: boundingRect.top + size.paddingTop + size.borderTopWidth,
6860    right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
6861    bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
6862  };
6863};
6864
6865/**
6866 * @param {Element} stamp
6867**/
6868proto._manageStamp = noop;
6869
6870/**
6871 * get x/y position of element relative to container element
6872 * @param {Element} elem
6873 * @returns {Object} offset - has left, top, right, bottom
6874 */
6875proto._getElementOffset = function( elem ) {
6876  var boundingRect = elem.getBoundingClientRect();
6877  var thisRect = this._boundingRect;
6878  var size = getSize( elem );
6879  var offset = {
6880    left: boundingRect.left - thisRect.left - size.marginLeft,
6881    top: boundingRect.top - thisRect.top - size.marginTop,
6882    right: thisRect.right - boundingRect.right - size.marginRight,
6883    bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
6884  };
6885  return offset;
6886};
6887
6888// -------------------------- resize -------------------------- //
6889
6890// enable event handlers for listeners
6891// i.e. resize -> onresize
6892proto.handleEvent = utils.handleEvent;
6893
6894/**
6895 * Bind layout to window resizing
6896 */
6897proto.bindResize = function() {
6898  window.addEventListener( 'resize', this );
6899  this.isResizeBound = true;
6900};
6901
6902/**
6903 * Unbind layout to window resizing
6904 */
6905proto.unbindResize = function() {
6906  window.removeEventListener( 'resize', this );
6907  this.isResizeBound = false;
6908};
6909
6910proto.onresize = function() {
6911  this.resize();
6912};
6913
6914utils.debounceMethod( Outlayer, 'onresize', 100 );
6915
6916proto.resize = function() {
6917  // don't trigger if size did not change
6918  // or if resize was unbound. See #9
vendor: 4,167 bytes, lines 6918-7101
6918    
6919  if ( !this.isResizeBound || !this.needsResizeLayout() ) {
6920    return;
6921  }
6922
6923  this.layout();
6924};
6925
6926/**
6927 * check if layout is needed post layout
6928 * @returns Boolean
6929 */
6930proto.needsResizeLayout = function() {
6931  var size = getSize( this.element );
6932  // check that this.size and size are there
6933  // IE8 triggers resize on body size change, so they might not be
6934  var hasSizes = this.size && size;
6935  return hasSizes && size.innerWidth !== this.size.innerWidth;
6936};
6937
6938// -------------------------- methods -------------------------- //
6939
6940/**
6941 * add items to Outlayer instance
6942 * @param {Array or NodeList or Element} elems
6943 * @returns {Array} items - Outlayer.Items
6944**/
6945proto.addItems = function( elems ) {
6946  var items = this._itemize( elems );
6947  // add items to collection
6948  if ( items.length ) {
6949    this.items = this.items.concat( items );
6950  }
6951  return items;
6952};
6953
6954/**
6955 * Layout newly-appended item elements
6956 * @param {Array or NodeList or Element} elems
6957 */
6958proto.appended = function( elems ) {
6959  var items = this.addItems( elems );
6960  if ( !items.length ) {
6961    return;
6962  }
6963  // layout and reveal just the new items
6964  this.layoutItems( items, true );
6965  this.reveal( items );
6966};
6967
6968/**
6969 * Layout prepended elements
6970 * @param {Array or NodeList or Element} elems
6971 */
6972proto.prepended = function( elems ) {
6973  var items = this._itemize( elems );
6974  if ( !items.length ) {
6975    return;
6976  }
6977  // add items to beginning of collection
6978  var previousItems = this.items.slice(0);
6979  this.items = items.concat( previousItems );
6980  // start new layout
6981  this._resetLayout();
6982  this._manageStamps();
6983  // layout new stuff without transition
6984  this.layoutItems( items, true );
6985  this.reveal( items );
6986  // layout previous items
6987  this.layoutItems( previousItems );
6988};
6989
6990/**
6991 * reveal a collection of items
6992 * @param {Array of Outlayer.Items} items
6993 */
6994proto.reveal = function( items ) {
6995  this._emitCompleteOnItems( 'reveal', items );
6996  if ( !items || !items.length ) {
6997    return;
6998  }
6999  items.forEach( function( item ) {
7000    item.reveal();
7001  });
7002};
7003
7004/**
7005 * hide a collection of items
7006 * @param {Array of Outlayer.Items} items
7007 */
7008proto.hide = function( items ) {
7009  this._emitCompleteOnItems( 'hide', items );
7010  if ( !items || !items.length ) {
7011    return;
7012  }
7013  items.forEach( function( item ) {
7014    item.hide();
7015  });
7016};
7017
7018/**
7019 * reveal item elements
7020 * @param {Array}, {Element}, {NodeList} items
7021 */
7022proto.revealItemElements = function( elems ) {
7023  var items = this.getItems( elems );
7024  this.reveal( items );
7025};
7026
7027/**
7028 * hide item elements
7029 * @param {Array}, {Element}, {NodeList} items
7030 */
7031proto.hideItemElements = function( elems ) {
7032  var items = this.getItems( elems );
7033  this.hide( items );
7034};
7035
7036/**
7037 * get Outlayer.Item, given an Element
7038 * @param {Element} elem
7039 * @param {Function} callback
7040 * @returns {Outlayer.Item} item
7041 */
7042proto.getItem = function( elem ) {
7043  // loop through items to get the one that matches
7044  for ( var i=0; i < this.items.length; i++ ) {
7045    var item = this.items[i];
7046    if ( item.element == elem ) {
7047      // return item
7048      return item;
7049    }
7050  }
7051};
7052
7053/**
7054 * get collection of Outlayer.Items, given Elements
7055 * @param {Array} elems
7056 * @returns {Array} items - Outlayer.Items
7057 */
7058proto.getItems = function( elems ) {
7059  elems = utils.makeArray( elems );
7060  var items = [];
7061  elems.forEach( function( elem ) {
7062    var item = this.getItem( elem );
7063    if ( item ) {
7064      items.push( item );
7065    }
7066  }, this );
7067
7068  return items;
7069};
7070
7071/**
7072 * remove element(s) from instance and DOM
7073 * @param {Array or NodeList or Element} elems
7074 */
7075proto.remove = function( elems ) {
7076  var removeItems = this.getItems( elems );
7077
7078  this._emitCompleteOnItems( 'remove', removeItems );
7079
7080  // bail if no items to remove
7081  if ( !removeItems || !removeItems.length ) {
7082    return;
7083  }
7084
7085  removeItems.forEach( function( item ) {
7086    item.remove();
7087    // remove item from collection
7088    utils.removeFrom( this.items, item );
7089  }, this );
7090};
7091
7092// ----- destroy ----- //
7093
7094// remove and disable Outlayer instance
7095proto.destroy = function() {
7096  // clean up dynamic styles
7097  var style = this.element.style;
7098  style.height = '';
7099  style.position = '';
7100  style.width = '';
7101  // destroy items
vendor: 2,836 bytes, lines 7102-7227
7102  this.items.forEach( function( item ) {
7103    item.destroy();
7104  });
7105
7106  this.unbindResize();
7107
7108  var id = this.element.outlayerGUID;
7109  delete instances[ id ]; // remove reference to instance by id
7110  delete this.element.outlayerGUID;
7111  // remove data for jQuery
7112  if ( jQuery ) {
7113    jQuery.removeData( this.element, this.constructor.namespace );
7114  }
7115
7116};
7117
7118// -------------------------- data -------------------------- //
7119
7120/**
7121 * get Outlayer instance from element
7122 * @param {Element} elem
7123 * @returns {Outlayer}
7124 */
7125Outlayer.data = function( elem ) {
7126  elem = utils.getQueryElement( elem );
7127  var id = elem && elem.outlayerGUID;
7128  return id && instances[ id ];
7129};
7130
7131
7132// -------------------------- create Outlayer class -------------------------- //
7133
7134/**
7135 * create a layout class
7136 * @param {String} namespace
7137 */
7138Outlayer.create = function( namespace, options ) {
7139  // sub-class Outlayer
7140  var Layout = subclass( Outlayer );
7141  // apply new options and compatOptions
7142  Layout.defaults = utils.extend( {}, Outlayer.defaults );
7143  utils.extend( Layout.defaults, options );
7144  Layout.compatOptions = utils.extend( {}, Outlayer.compatOptions  );
7145
7146  Layout.namespace = namespace;
7147
7148  Layout.data = Outlayer.data;
7149
7150  // sub-class Item
7151  Layout.Item = subclass( Item );
7152
7153  // -------------------------- declarative -------------------------- //
7154
7155  utils.htmlInit( Layout, namespace );
7156
7157  // -------------------------- jQuery bridge -------------------------- //
7158
7159  // make into jQuery plugin
7160  if ( jQuery && jQuery.bridget ) {
7161    jQuery.bridget( namespace, Layout );
7162  }
7163
7164  return Layout;
7165};
7166
7167function subclass( Parent ) {
7168  function SubClass() {
7169    Parent.apply( this, arguments );
7170  }
7171
7172  SubClass.prototype = Object.create( Parent.prototype );
7173  SubClass.prototype.constructor = SubClass;
7174
7175  return SubClass;
7176}
7177
7178// ----- fin ----- //
7179
7180// back in global
7181Outlayer.Item = Item;
7182
7183return Outlayer;
7184
7185}));
7186
7187/*!
7188 * Masonry v4.0.0
7189 * Cascading grid layout library
7190 * http://masonry.desandro.com
7191 * MIT License
7192 * by David DeSandro
7193 */
7194
7195( function( window, factory ) {
7196  // universal module definition
7197  /* jshint strict: false */ /*globals define, module, require */
7198  if ( typeof define == 'function' && define.amd ) {
7199    // AMD
7200    define( [
7201        'outlayer/outlayer',
7202        'get-size/get-size'
7203      ],
7204      factory );
7205  } else if ( typeof module == 'object' && module.exports ) {
7206    // CommonJS
7207    module.exports = factory(
7208      require('outlayer'),
7209      require('get-size')
7210    );
7211  } else {
7212    // browser global
7213    window.Masonry = factory(
7214      window.Outlayer,
7215      window.getSize
7216    );
7217  }
7218
7219}( window, function factory( Outlayer, getSize ) {
7220
7221
7222
7223// -------------------------- masonryDefinition -------------------------- //
7224
7225  // create an Outlayer layout class
7226  var Masonry = Outlayer.create('masonry');
7227  // isFitWidth -> fitWidth
7228  Masonry.compatOptions.fitWidth = 'isFitWidth';
7229
7230  Masonry.prototype._resetLayout = function() {
7231    this.getSize();
7232    this._getMeasurement( 'columnWidth', 'outerWidth' );
7233    this._getMeasurement( 'gutter', 'outerWidth' );
7234    this.measureColumns();
7235
7236    // reset column Y
7237    this.colYs = [];
7238    for ( var i=0; i < this.cols; i++ ) {
7239      this.colYs.push( 0 );
7240    }
7241
7242    this.maxY = 0;
7243  };
7244
7245  Masonry.prototype.measureColumns = function() {
7246    this.getContainerWidth();
7247    // if columnWidth is 0, default to outerWidth of first item
7248    if ( !this.columnWidth ) {
7249      var firstItem = this.items[0];
7250      var firstItemElem = firstItem && firstItem.element;
7251      // columnWidth fall back to item of first element
7252      this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
7253        // if first elem has no width, default to size of container
7254        this.containerWidth;
7255    }
7256
7257    var columnWidth = this.columnWidth += this.gutter;
7258
7259    // calculate columns
7260    var containerWidth = this.containerWidth + this.gutter;
7261    var cols = containerWidth / columnWidth;
7262    // fix rounding errors, typically with gutters
7263    var excess = columnWidth - containerWidth % columnWidth;
7264    // if overshoot is less than a pixel, round up, otherwise floor it
7265    var mathMethod = excess && excess < 1 ? 'round' : 'floor';
7266    cols = Math[ mathMethod ]( cols );
7267    this.cols = Math.max( cols, 1 );
7268  };
7269
7270  Masonry.prototype.getContainerWidth = function() {
7271    // container is parent if fit width
7272    var isFitWidth = this._getOption('fitWidth');
7273    var container = isFitWidth ? this.element.parentNode : this.element;
7274    // check that this.size and size are there
7275    // IE8 triggers resize on body size change, so they might not be
7276    var size = getSize( container );
7277    this.containerWidth = size && size.innerWidth;
7278  };
7279
7280  Masonry.prototype._getItemLayoutPosition = function( item ) {
7281    item.getSize();
7282    // how many columns does this brick span
7283    var remainder = item.size.outerWidth % this.columnWidth;
7284    var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
7285    // round if off by 1 pixel, otherwise use ceil
7286    var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
7287    colSpan = Math.min( colSpan, this.cols );
7288
7289    var colGroup = this._getColGroup( colSpan );
7290    // get the minimum Y value from the columns
7291    var minimumY = Math.min.apply( Math, colGroup );
7292    var shortColIndex = colGroup.indexOf( minimumY );
7293
7294    // position the brick
7295    var position = {
7296      x: this.columnWidth * shortColIndex,
7297      y: minimumY
7298    };
7299
7300    // apply setHeight to necessary columns
7301    var setHeight = minimumY + item.size.outerHeight;
7302    var setSpan = this.cols + 1 - colGroup.length;
7303    for ( var i = 0; i < setSpan; i++ ) {
7304      this.colYs[ shortColIndex + i ] = setHeight;
7305    }
7306
7307    return position;
7308  };
7309
7310  /**
7311   * @param {Number} colSpan - number of columns the element spans
7312   * @returns {Array} colGroup
7313   */
7314  Masonry.prototype._getColGroup = function( colSpan ) {
7315    if ( colSpan < 2 ) {
7316      // if brick spans only one column, use all the column Ys
7317      return this.colYs;
7318    }
7319
7320    var colGroup = [];
7321    // how many different places could this brick fit horizontally
7322    var groupCount = this.cols + 1 - colSpan;
7323    // for each group potential horizontal position
7324    for ( var i = 0; i < groupCount; i++ ) {
7325      // make an array of colY values for that one group
7326      var groupColYs = this.colYs.slice( i, i + colSpan );
7327      // and get the max value of the array
7328      colGroup[i] = Math.max.apply( Math, groupColYs );
7329    }
7330    return colGroup;
7331  };
7332
7333  Masonry.prototype._manageStamp = function( stamp ) {
7334    var stampSize = getSize( stamp );
7335    var offset = this._getElementOffset( stamp );
7336    // get the columns that this stamp affects
7337    var isOriginLeft = this._getOption('originLeft');
7338    var firstX = isOriginLeft ? offset.left : offset.right;
7339    var lastX = firstX + stampSize.outerWidth;
7340    var firstCol = Math.floor( firstX / this.columnWidth );
7341    firstCol = Math.max( 0, firstCol );
7342    var lastCol = Math.floor( lastX / this.columnWidth );
7343    // lastCol should not go over if multiple of columnWidth #425
7344    lastCol -= lastX % this.columnWidth ? 0 : 1;
7345    lastCol = Math.min( this.cols - 1, lastCol );
7346    // set colYs to bottom of the stamp
7347
7348    var isOriginTop = this._getOption('originTop');
7349    var stampMaxY = ( isOriginTop ? offset.top : offset.bottom ) +
7350      stampSize.outerHeight;
7351    for ( var i = firstCol; i <= lastCol; i++ ) {
7352      this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
7353    }
7354  };
7355
7356  Masonry.prototype._getContainerSize = function() {
7357    this.maxY = Math.max.apply( Math, this.colYs );
7358    var size = {
7359      height: this.maxY
7360    };
7361
7362    if ( this._getOption('fitWidth') ) {
7363      size.width = this._getContainerFitWidth();
7364    }
7365
7366    return size;
7367  };
7368
7369  Masonry.prototype._getContainerFitWidth = function() {
7370    var unusedCols = 0;
7371    // count unused columns
7372    var i = this.cols;
7373    while ( --i ) {
7374      if ( this.colYs[i] !== 0 ) {
7375        break;
7376      }
7377      unusedCols++;
7378    }
7379    // fit container to columns that have been used
7380    return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
7381  };
7382
7383  Masonry.prototype.needsResizeLayout = function() {
7384    var previousWidth = this.containerWidth;
7385    this.getContainerWidth();
7386    return previousWidth != this.containerWidth;
7387  };
7388
7389  return Masonry;
7390
7391}));
7392
7393
7394// Menu toggle
7395$(document).ready(function(){
7396		 $(".layout4menu ul").slideUp();
7397		 $(".layout4menulink").click(function(e){
7398    	 e.stopPropagation();
7399    		$(this).addClass('opendropdownmenu');
7400        $(".layout4menu ul").slideToggle();
7401    });
7402    $(document).click(function () {
7403			if($(".layout4menulink").hasClass('opendropdownmenu'))
7404    	{
7405    		$(".layout4menulink").removeClass('opendropdownmenu');
7406    		$(".layout4menu ul").slideToggle();
7407    	}
7408   });
7409});
7410// Open Fancybox Content 
7411$("a[rel='portfolio']").each(function(e) {
7412				var title = $(this).attr('title');
7413			
7414				$(this).mouseover(function() {
7415				    $(this).removeAttr('title','');
7416				 }).mouseout(function() {
7417				    $(this).removeAttr('title', title);
7418				});
7419				$(this).click(function() {
7420				    $(this).attr('title', title);
7421				});
7422});
7423$( document ).ready(function() {
7424   jQuery(".ResponsiveFancyGallery2").fancybox({
7425				titlePosition: "over",
7426				beforeShow : function(){
7427			
7428				if($(this.element).data("title")!=undefined && $(this.element).data("caption")!=undefined)
7429				{
7430				this.title =  $(this.element).data("title") + " <br> " + $(this.element).data("caption");
7431				}
7432				else if($(this.element).data("title")!=undefined && $(this.element).data("caption")==undefined)
7433				{
7434				this.title =  $(this.element).data("title");
7435				}
7436				else if($(this.element).data("title")==undefined && $(this.element).data("caption")!=undefined)
7437				{
7438				this.title =  $(this.element).data("caption");
7439				}
7440				else
7441				{
7442				this.title =  '';
7443				}
7444				},
7445					onUpdate:function(){
7446			   
7447			    var posXY = '';
7448			    $('.fancybox-skin').draggable({
7449				    axis: "x",
7450				    drag: function(event,ui){
7451
7452                        posXY = ui.position.left;
7453
7454                        if(posXY > 150){return false;}
7455					    if(posXY < -150){return false;}
7456				    },
7457				    stop: function(){
7458
7459					    if(posXY > 110){$.fancybox.prev();}
7460					    if(posXY < -110){$.fancybox.next();}
7461				    }
7462			    });
7463		    },
7464				afterClose : function()
7465				{             
7466            $( window ).focus();                       
7467				},
7468				
7469			});
7470});
7471
7472

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.