PageSourceSearch

https://www.sindigraficos.com.br/wp-content/plugins/calculadora_ap…/dist/jquery.fancybox.js?ver=122804

js sindigraficos.com.br collected 2026-09-27 03:28:47 UTC 110,786 bytes, 4,116 lines download raw bytes

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

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