// ================================================== // fancybox v3.2.10 // // licensed gplv3 for open source use // or fancybox commercial license for commercial use // // http://fancyapps.com/fancybox/ // copyright 2017 fancyapps // // ================================================== ;(function (window, document, $, undefined) { 'use strict'; // if there's no jquery, fancybox can't work // ========================================= if ( !$ ) { return; } // check if fancybox is already initialized // ======================================== if ( $.fn.fancybox ) { if ( 'console' in window ) { console.log( 'fancybox already initialized' ); } return; } // private default settings // ======================== var defaults = { // enable infinite gallery navigation loop : false, // space around image, ignored if zoomed-in or viewport width is smaller than 800px margin : [44, 0], // horizontal space between slides gutter : 50, // enable keyboard navigation keyboard : true, // should display navigation arrows at the screen edges arrows : true, // should display infobar (counter and arrows at the top) infobar : true, // should display toolbar (buttons at the top) toolbar : true, // what buttons should appear in the top right corner. // buttons will be created using templates from `btntpl` option // and they will be placed into toolbar (class="fancybox-toolbar"` element) buttons : [ 'slideshow', 'fullscreen', 'thumbs', 'share', //'download', //'zoom', 'close' ], // detect "idle" time in seconds idletime : 3, // should display buttons at top right corner of the content // if 'auto' - they will be created for content having type 'html', 'inline' or 'ajax' // use template from `btntpl.smallbtn` for customization smallbtn : 'auto', // disable right-click and use simple image protection for images protect : false, // shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc modal : false, image : { // wait for images to load before displaying // requires predefined image dimensions // if 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found preload : "auto" }, ajax : { // object containing settings for ajax request settings : { // this helps to indicate that request comes from the modal // feel free to change naming data : { fancybox : true } } }, iframe : { // iframe template tpl : '', // preload iframe before displaying it // this allows to calculate iframe content width and height // (note: due to "same origin policy", you can't get cross domain data). preload : true, // custom css styling for iframe wrapping element // you can use this to set custom iframe dimensions css : {}, // iframe tag attributes attr : { scrolling : 'auto' } }, // default content type if cannot be detected automatically defaulttype : 'image', // open/close animation type // possible values: // false - disable // "zoom" - zoom images from/to thumbnail // "fade" // "zoom-in-out" // animationeffect : "zoom", // duration in ms for open/close animation animationduration : 500, // should image change opacity while zooming // if opacity is "auto", then opacity will be changed if image and thumbnail have different aspect ratios zoomopacity : "auto", // transition effect between slides // // possible values: // false - disable // "fade' // "slide' // "circular' // "tube' // "zoom-in-out' // "rotate' // transitioneffect : "fade", // duration in ms for transition animation transitionduration : 366, // custom css class for slide element slideclass : '', // custom css class for layout baseclass : '', // base template for layout basetpl : '', // loading indicator template spinnertpl : '
', // error message template errortpl : '

{{error}}

', btntpl : { download : '' + '' + '' + '' + '', zoom : '', close : '', // this small close button will be appended to your html/inline/ajax content by default, // if "smallbtn" option is not set to false smallbtn : '', // arrows arrowleft : '', arrowright : '' }, // container is injected into this element parentel : 'body', // focus handling // ============== // try to focus on the first focusable element after opening autofocus : false, // put focus back to active element after closing backfocus : true, // do not let user to focus on element outside modal content trapfocus : true, // module specific options // ======================= fullscreen : { autostart : false, }, // set `touch: false` to disable dragging/swiping touch : { vertical : true, // allow to drag content vertically momentum : true // continue movement after releasing mouse/touch when panning }, // hash value when initializing manually, // set `false` to disable hash change hash : null, // customize or add new media types // example: /* media : { youtube : { params : { autoplay : 0 } } } */ media : {}, slideshow : { autostart : false, speed : 4000 }, thumbs : { autostart : false, // display thumbnails on opening hideonclose : true, // hide thumbnail grid when closing animation starts parentel : '.fancybox-container', // container is injected into this element axis : 'y' // vertical (y) or horizontal (x) scrolling }, // use mousewheel to navigate gallery // if 'auto' - enabled for images only wheel : 'auto', // callbacks //========== // see documentation/api/events for more information // example: /* aftershow: function( instance, current ) { console.info( 'clicked element:' ); console.info( current.opts.$orig ); } */ oninit : $.noop, // when instance has been initialized beforeload : $.noop, // before the content of a slide is being loaded afterload : $.noop, // when the content of a slide is done loading beforeshow : $.noop, // before open animation starts aftershow : $.noop, // when content is done loading and animating beforeclose : $.noop, // before the instance attempts to close. return false to cancel the close. afterclose : $.noop, // after instance has been closed onactivate : $.noop, // when instance is brought to front ondeactivate : $.noop, // when other instance has been activated // interaction // =========== // use options below to customize taken action when user clicks or double clicks on the fancybox area, // each option can be string or method that returns value. // // possible values: // "close" - close instance // "next" - move to next gallery item // "nextorclose" - move to next gallery item or close if gallery has only one item // "togglecontrols" - show/hide controls // "zoom" - zoom image (if loaded) // false - do nothing // clicked on the content clickcontent : function( current, event ) { return current.type === 'image' ? 'zoom' : false; }, // clicked on the slide clickslide : 'close', // clicked on the background (backdrop) element clickoutside : 'close', // same as previous two, but for double click dblclickcontent : false, dblclickslide : false, dblclickoutside : false, // custom options when mobile device is detected // ============================================= mobile : { idletime : false, margin : 0, clickcontent : function( current, event ) { return current.type === 'image' ? 'togglecontrols' : false; }, clickslide : function( current, event ) { return current.type === 'image' ? 'togglecontrols' : 'close'; }, dblclickcontent : function( current, event ) { return current.type === 'image' ? 'zoom' : false; }, dblclickslide : function( current, event ) { return current.type === 'image' ? 'zoom' : false; } }, // internationalization // ============ lang : 'en', i18n : { 'en' : { close : 'close', next : 'next', prev : 'previous', error : 'the requested content cannot be loaded.
please try again later.', play_start : 'start slideshow', play_stop : 'pause slideshow', full_screen : 'full screen', thumbs : 'thumbnails', download : 'download', share : 'share', zoom : 'zoom' }, 'de' : { close : 'schliessen', next : 'weiter', prev : 'zurück', error : 'die angeforderten daten konnten nicht geladen werden.
bitte versuchen sie es später nochmal.', play_start : 'diaschau starten', play_stop : 'diaschau beenden', full_screen : 'vollbild', thumbs : 'vorschaubilder', download : 'herunterladen', share : 'teilen', zoom : 'maßstab' } } }; // few useful variables and methods // ================================ var $w = $(window); var $d = $(document); var called = 0; // check if an object is a jquery object and not a native javascript object // ======================================================================== var isquery = function ( obj ) { return obj && obj.hasownproperty && obj instanceof $; }; // handle multiple browsers for "requestanimationframe" and "cancelanimationframe" // =============================================================================== var requestaframe = (function () { return window.requestanimationframe || window.webkitrequestanimationframe || window.mozrequestanimationframe || window.orequestanimationframe || // if all else fails, use settimeout function (callback) { return window.settimeout(callback, 1000 / 60); }; })(); // detect the supported transition-end event property name // ======================================================= var transitionend = (function () { var t, el = document.createelement("fakeelement"); var transitions = { "transition" : "transitionend", "otransition" : "otransitionend", "moztransition" : "transitionend", "webkittransition": "webkittransitionend" }; for (t in transitions) { if (el.style[t] !== undefined){ return transitions[t]; } } return 'transitionend'; })(); // force redraw on an element. // this helps in cases where the browser doesn't redraw an updated element properly. // ================================================================================= var forceredraw = function( $el ) { return ( $el && $el.length && $el[0].offsetheight ); }; // class definition // ================ var fancybox = function( content, opts, index ) { var self = this; self.opts = $.extend( true, { index : index }, $.fancybox.defaults, opts || {} ); if ( $.fancybox.ismobile ) { self.opts = $.extend( true, {}, self.opts, self.opts.mobile ); } // exclude buttons option from deep merging if ( opts && $.isarray( opts.buttons ) ) { self.opts.buttons = opts.buttons; } self.id = self.opts.id || ++called; self.group = []; self.currindex = parseint( self.opts.index, 10 ) || 0; self.previndex = null; self.prevpos = null; self.currpos = 0; self.firstrun = null; // create group elements from original item collection self.creategroup( content ); if ( !self.group.length ) { return; } // save last active element and current scroll position self.$lastfocus = $(document.activeelement).blur(); // collection of gallery objects self.slides = {}; self.init(); }; $.extend(fancybox.prototype, { // create dom structure // ==================== init : function() { var self = this, firstitem = self.group[ self.currindex ], firstitemopts = firstitem.opts, scrollbarwidth = $.fancybox.scrollbarwidth, $scrolldiv, $container, buttonstr; self.scrolltop = $d.scrolltop(); self.scrollleft = $d.scrollleft(); // hide scrollbars // =============== if ( !$.fancybox.getinstance() ) { $( 'body' ).addclass( 'fancybox-active' ); // ios hack if ( /ipad|iphone|ipod/.test(navigator.useragent) && !window.msstream ) { // ios has problems for input elements inside fixed containers, // the workaround is to apply `position: fixed` to `` element, // unfortunately, this makes it lose the scrollbars and forces address bar to appear. if ( firstitem.type !== 'image' ) { $( 'body' ).css( 'top', $( 'body' ).scrolltop() * -1 ).addclass( 'fancybox-iosfix' ); } } else if ( !$.fancybox.ismobile && document.body.scrollheight > window.innerheight ) { if ( scrollbarwidth === undefined ) { $scrolldiv = $('
').appendto( 'body' ); scrollbarwidth = $.fancybox.scrollbarwidth = $scrolldiv[0].offsetwidth - $scrolldiv[0].clientwidth; $scrolldiv.remove(); } $( 'head' ).append( '' ); $( 'body' ).addclass( 'compensate-for-scrollbar' ); } } // build html markup and set references // ==================================== // build html code for buttons and insert into main template buttonstr = ''; $.each( firstitemopts.buttons, function( index, value ) { buttonstr += ( firstitemopts.btntpl[ value ] || '' ); }); // create markup from base template, it will be initially hidden to // avoid unnecessary work like painting while initializing is not complete $container = $( self.translate( self, firstitemopts.basetpl .replace( '\{\{buttons\}\}', buttonstr ) .replace( '\{\{arrows\}\}', firstitemopts.btntpl.arrowleft + firstitemopts.btntpl.arrowright ) ) ) .attr( 'id', 'fancybox-container-' + self.id ) .addclass( 'fancybox-is-hidden' ) .addclass( firstitemopts.baseclass ) .data( 'fancybox', self ) .appendto( firstitemopts.parentel ); // create object holding references to jquery wrapped nodes self.$refs = { container : $container }; [ 'bg', 'inner', 'infobar', 'toolbar', 'stage', 'caption', 'navigation' ].foreach(function(item) { self.$refs[ item ] = $container.find( '.fancybox-' + item ); }); self.trigger( 'oninit' ); // enable events, deactive previous instances self.activate(); // build slides, load and reveal content self.jumpto( self.currindex ); }, // simple i18n support - replaces object keys found in template // with corresponding values // ============================================================ translate : function( obj, str ) { var arr = obj.opts.i18n[ obj.opts.lang ]; return str.replace(/\{\{(\w+)\}\}/g, function(match, n) { var value = arr[n]; if ( value === undefined ) { return match; } return value; }); }, // create array of gally item objects // check if each object has valid type and content // =============================================== creategroup : function ( content ) { var self = this; var items = $.makearray( content ); $.each(items, function( i, item ) { var obj = {}, opts = {}, $item, type, found, src, srcparts; // step 1 - make sure we have an object // ==================================== if ( $.isplainobject( item ) ) { // we probably have manual usage here, something like // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] ) obj = item; opts = item.opts || item; } else if ( $.type( item ) === 'object' && $( item ).length ) { // here we probably have jquery collection returned by some selector $item = $( item ); opts = $item.data(); opts = $.extend( {}, opts, opts.options || {} ); // here we store clicked element opts.$orig = $item; obj.src = opts.src || $item.attr( 'href' ); // assume that simple syntax is used, for example: // `$.fancybox.open( $("#test"), {} );` if ( !obj.type && !obj.src ) { obj.type = 'inline'; obj.src = item; } } else { // assume we have a simple html code, for example: // $.fancybox.open( '

hi!

' ); obj = { type : 'html', src : item + '' }; } // each gallery object has full collection of options obj.opts = $.extend( true, {}, self.opts, opts ); // do not merge buttons array if ( $.isarray( opts.buttons ) ) { obj.opts.buttons = opts.buttons; } // step 2 - make sure we have content type, if not - try to guess // ============================================================== type = obj.type || obj.opts.type; src = obj.src || ''; if ( !type && src ) { if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) { type = 'image'; } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) { type = 'pdf'; } else if ( found = src.match(/\.(mp4|mov|ogv)((\?|#).*)?$/i) ) { type = 'video'; if ( !obj.opts.videoformat ) { obj.opts.videoformat = 'video/' + ( found[1] === 'ogv' ? 'ogg' : found[1] ); } } else if ( src.charat(0) === '#' ) { type = 'inline'; } } if ( type ) { obj.type = type; } else { self.trigger( 'objectneedstype', obj ); } // step 3 - some adjustments // ========================= obj.index = self.group.length; // check if $orig and $thumb objects exist if ( obj.opts.$orig && !obj.opts.$orig.length ) { delete obj.opts.$orig; } if ( !obj.opts.$thumb && obj.opts.$orig ) { obj.opts.$thumb = obj.opts.$orig.find( 'img:first' ); } if ( obj.opts.$thumb && !obj.opts.$thumb.length ) { delete obj.opts.$thumb; } // "caption" is a "special" option, it can be used to customize caption per gallery item .. if ( $.type( obj.opts.caption ) === 'function' ) { obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] ); } if ( $.type( self.opts.caption ) === 'function' ) { obj.opts.caption = self.opts.caption.apply( item, [ self, obj ] ); } // make sure we have caption as a string or jquery object if ( !( obj.opts.caption instanceof $ ) ) { obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + ''; } // check if url contains "filter" used to filter the content // example: "ajax.html #something" if ( type === 'ajax' ) { srcparts = src.split(/\s+/, 2); if ( srcparts.length > 1 ) { obj.src = srcparts.shift(); obj.opts.filter = srcparts.shift(); } } if ( obj.opts.smallbtn == 'auto' ) { if ( $.inarray( type, ['html', 'inline', 'ajax'] ) > -1 ) { obj.opts.toolbar = false; obj.opts.smallbtn = true; } else { obj.opts.smallbtn = false; } } // if the type is "pdf", then simply load file into iframe if ( type === 'pdf' ) { obj.type = 'iframe'; obj.opts.iframe.preload = false; } // hide all buttons and disable interactivity for modal items if ( obj.opts.modal ) { obj.opts = $.extend(true, obj.opts, { // remove buttons infobar : 0, toolbar : 0, smallbtn : 0, // disable keyboard navigation keyboard : 0, // disable some modules slideshow : 0, fullscreen : 0, thumbs : 0, touch : 0, // disable click event handlers clickcontent : false, clickslide : false, clickoutside : false, dblclickcontent : false, dblclickslide : false, dblclickoutside : false }); } // step 4 - add processed object to group // ====================================== self.group.push( obj ); }); }, // attach an event handler functions for: // - navigation buttons // - browser scrolling, resizing; // - focusing // - keyboard // - detect idle // ====================================== addevents : function() { var self = this; self.removeevents(); // make navigation elements clickable self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) { e.stoppropagation(); e.preventdefault(); self.close( e ); }).on( 'click.fb-prev touchend.fb-prev', '[data-fancybox-prev]', function(e) { e.stoppropagation(); e.preventdefault(); self.previous(); }).on( 'click.fb-next touchend.fb-next', '[data-fancybox-next]', function(e) { e.stoppropagation(); e.preventdefault(); self.next(); }).on( 'click.fb', '[data-fancybox-zoom]', function(e) { // click handler for zoom button self[ self.isscaleddown() ? 'scaletoactual' : 'scaletofit' ](); }); // handle page scrolling and browser resizing $w.on('orientationchange.fb resize.fb', function(e) { if ( e && e.originalevent && e.originalevent.type === "resize" ) { requestaframe(function() { self.update(); }); } else { self.$refs.stage.hide(); settimeout(function() { self.$refs.stage.show(); self.update(); }, 600); } }); // trap keyboard focus inside of the modal, so the user does not accidentally tab outside of the modal // (a.k.a. "escaping the modal") $d.on('focusin.fb', function(e) { var instance = $.fancybox ? $.fancybox.getinstance() : null; if ( instance.isclosing || !instance.current || !instance.current.opts.trapfocus || $( e.target ).hasclass( 'fancybox-container' ) || $( e.target ).is( document ) ) { return; } if ( instance && $( e.target ).css( 'position' ) !== 'fixed' && !instance.$refs.container.has( e.target ).length ) { e.stoppropagation(); instance.focus(); // sometimes page gets scrolled, set it back $w.scrolltop( self.scrolltop ).scrollleft( self.scrollleft ); } }); // enable keyboard navigation $d.on('keydown.fb', function (e) { var current = self.current, keycode = e.keycode || e.which; if ( !current || !current.opts.keyboard ) { return; } if ( $(e.target).is('input') || $(e.target).is('textarea') ) { return; } // backspace and esc keys if ( keycode === 8 || keycode === 27 ) { e.preventdefault(); self.close( e ); return; } // left arrow and up arrow if ( keycode === 37 || keycode === 38 ) { e.preventdefault(); self.previous(); return; } // righ arrow and down arrow if ( keycode === 39 || keycode === 40 ) { e.preventdefault(); self.next(); return; } self.trigger('afterkeydown', e, keycode); }); // hide controls after some inactivity period if ( self.group[ self.currindex ].opts.idletime ) { self.idlesecondscounter = 0; $d.on('mousemove.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle', function(e) { self.idlesecondscounter = 0; if ( self.isidle ) { self.showcontrols(); } self.isidle = false; }); self.idleinterval = window.setinterval(function() { self.idlesecondscounter++; if ( self.idlesecondscounter >= self.group[ self.currindex ].opts.idletime && !self.isdragging ) { self.isidle = true; self.idlesecondscounter = 0; self.hidecontrols(); } }, 1000); } }, // remove events added by the core // =============================== removeevents : function() { var self = this; $w.off( 'orientationchange.fb resize.fb' ); $d.off( 'focusin.fb keydown.fb .fb-idle' ); this.$refs.container.off( '.fb-close .fb-prev .fb-next' ); if ( self.idleinterval ) { window.clearinterval( self.idleinterval ); self.idleinterval = null; } }, // change to previous gallery item // =============================== previous : function( duration ) { return this.jumpto( this.currpos - 1, duration ); }, // change to next gallery item // =========================== next : function( duration ) { return this.jumpto( this.currpos + 1, duration ); }, // switch to selected gallery item // =============================== jumpto : function ( pos, duration, slide ) { var self = this, firstrun, loop, current, previous, canvaswidth, currentpos, transitionprops; var grouplen = self.group.length; if ( self.isdragging || self.isclosing || ( self.isanimating && self.firstrun ) ) { return; } pos = parseint( pos, 10 ); loop = self.current ? self.current.opts.loop : self.opts.loop; if ( !loop && ( pos < 0 || pos >= grouplen ) ) { return false; } firstrun = self.firstrun = ( self.firstrun === null ); if ( grouplen < 2 && !firstrun && !!self.isdragging ) { return; } previous = self.current; self.previndex = self.currindex; self.prevpos = self.currpos; // create slides current = self.createslide( pos ); if ( grouplen > 1 ) { if ( loop || current.index > 0 ) { self.createslide( pos - 1 ); } if ( loop || current.index < grouplen - 1 ) { self.createslide( pos + 1 ); } } self.current = current; self.currindex = current.index; self.currpos = current.pos; self.trigger( 'beforeshow', firstrun ); self.updatecontrols(); currentpos = $.fancybox.gettranslate( current.$slide ); current.ismoved = ( currentpos.left !== 0 || currentpos.top !== 0 ) && !current.$slide.hasclass( 'fancybox-animated' ); current.forcedduration = undefined; if ( $.isnumeric( duration ) ) { current.forcedduration = duration; } else { duration = current.opts[ firstrun ? 'animationduration' : 'transitionduration' ]; } duration = parseint( duration, 10 ); // fresh start - reveal container, current slide and start loading content if ( firstrun ) { if ( current.opts.animationeffect && duration ) { self.$refs.container.css( 'transition-duration', duration + 'ms' ); } self.$refs.container.removeclass( 'fancybox-is-hidden' ); forceredraw( self.$refs.container ); self.$refs.container.addclass( 'fancybox-is-open' ); // make first slide visible (to display loading icon, if needed) current.$slide.addclass( 'fancybox-slide--current' ); self.loadslide( current ); self.preload( 'image' ); return; } // clean up $.each(self.slides, function( index, slide ) { $.fancybox.stop( slide.$slide ); }); // make current that slide is visible even if content is still loading current.$slide.removeclass( 'fancybox-slide--next fancybox-slide--previous' ).addclass( 'fancybox-slide--current' ); // if slides have been dragged, animate them to correct position if ( current.ismoved ) { canvaswidth = math.round( current.$slide.width() ); $.each(self.slides, function( index, slide ) { var pos = slide.pos - current.pos; $.fancybox.animate( slide.$slide, { top : 0, left : ( pos * canvaswidth ) + ( pos * slide.opts.gutter ) }, duration, function() { slide.$slide.removeattr('style').removeclass( 'fancybox-slide--next fancybox-slide--previous' ); if ( slide.pos === self.currpos ) { current.ismoved = false; self.complete(); } }); }); } else { self.$refs.stage.children().removeattr( 'style' ); } // start transition that reveals current content // or wait when it will be loaded if ( current.isloaded ) { self.revealcontent( current ); } else { self.loadslide( current ); } self.preload( 'image' ); if ( previous.pos === current.pos ) { return; } // handle previous slide // ===================== transitionprops = 'fancybox-slide--' + ( previous.pos > current.pos ? 'next' : 'previous' ); previous.$slide.removeclass( 'fancybox-slide--complete fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ); previous.iscomplete = false; if ( !duration || ( !current.ismoved && !current.opts.transitioneffect ) ) { return; } if ( current.ismoved ) { previous.$slide.addclass( transitionprops ); } else { transitionprops = 'fancybox-animated ' + transitionprops + ' fancybox-fx-' + current.opts.transitioneffect; $.fancybox.animate( previous.$slide, transitionprops, duration, function() { previous.$slide.removeclass( transitionprops ).removeattr( 'style' ); }); } }, // create new "slide" element // these are gallery items that are actually added to dom // ======================================================= createslide : function( pos ) { var self = this; var $slide; var index; index = pos % self.group.length; index = index < 0 ? self.group.length + index : index; if ( !self.slides[ pos ] && self.group[ index ] ) { $slide = $('
').appendto( self.$refs.stage ); self.slides[ pos ] = $.extend( true, {}, self.group[ index ], { pos : pos, $slide : $slide, isloaded : false, }); self.updateslide( self.slides[ pos ] ); } return self.slides[ pos ]; }, // scale image to the actual size of the image // =========================================== scaletoactual : function( x, y, duration ) { var self = this; var current = self.current; var $what = current.$content; var imgpos, posx, posy, scalex, scaley; var canvaswidth = parseint( current.$slide.width(), 10 ); var canvasheight = parseint( current.$slide.height(), 10 ); var newimgwidth = current.width; var newimgheight = current.height; if ( !( current.type == 'image' && !current.haserror) || !$what || self.isanimating ) { return; } $.fancybox.stop( $what ); self.isanimating = true; x = x === undefined ? canvaswidth * 0.5 : x; y = y === undefined ? canvasheight * 0.5 : y; imgpos = $.fancybox.gettranslate( $what ); scalex = newimgwidth / imgpos.width; scaley = newimgheight / imgpos.height; // get center position for original image posx = ( canvaswidth * 0.5 - newimgwidth * 0.5 ); posy = ( canvasheight * 0.5 - newimgheight * 0.5 ); // make sure image does not move away from edges if ( newimgwidth > canvaswidth ) { posx = imgpos.left * scalex - ( ( x * scalex ) - x ); if ( posx > 0 ) { posx = 0; } if ( posx < canvaswidth - newimgwidth ) { posx = canvaswidth - newimgwidth; } } if ( newimgheight > canvasheight) { posy = imgpos.top * scaley - ( ( y * scaley ) - y ); if ( posy > 0 ) { posy = 0; } if ( posy < canvasheight - newimgheight ) { posy = canvasheight - newimgheight; } } self.updatecursor( newimgwidth, newimgheight ); $.fancybox.animate( $what, { top : posy, left : posx, scalex : scalex, scaley : scaley }, duration || 330, function() { self.isanimating = false; }); // stop slideshow if ( self.slideshow && self.slideshow.isactive ) { self.slideshow.stop(); } }, // scale image to fit inside parent element // ======================================== scaletofit : function( duration ) { var self = this; var current = self.current; var $what = current.$content; var end; if ( !( current.type == 'image' && !current.haserror) || !$what || self.isanimating ) { return; } $.fancybox.stop( $what ); self.isanimating = true; end = self.getfitpos( current ); self.updatecursor( end.width, end.height ); $.fancybox.animate( $what, { top : end.top, left : end.left, scalex : end.width / $what.width(), scaley : end.height / $what.height() }, duration || 330, function() { self.isanimating = false; }); }, // calculate image size to fit inside viewport // =========================================== getfitpos : function( slide ) { var self = this; var $what = slide.$content; var imgwidth = slide.width; var imgheight = slide.height; var margin = slide.opts.margin; var canvaswidth, canvasheight, minratio, width, height; if ( !$what || !$what.length || ( !imgwidth && !imgheight) ) { return false; } // convert "margin to css style: [ top, right, bottom, left ] if ( $.type( margin ) === "number" ) { margin = [ margin, margin ]; } if ( margin.length == 2 ) { margin = [ margin[0], margin[1], margin[0], margin[1] ]; } // we can not use $slide width here, because it can have different diemensions while in transiton canvaswidth = parseint( self.$refs.stage.width(), 10 ) - ( margin[ 1 ] + margin[ 3 ] ); canvasheight = parseint( self.$refs.stage.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] ); minratio = math.min(1, canvaswidth / imgwidth, canvasheight / imgheight ); width = math.floor( minratio * imgwidth ); height = math.floor( minratio * imgheight ); // use floor rounding to make sure it really fits return { top : math.floor( ( canvasheight - height ) * 0.5 ) + margin[ 0 ], left : math.floor( ( canvaswidth - width ) * 0.5 ) + margin[ 3 ], width : width, height : height }; }, // update content size and position for all slides // ============================================== update : function() { var self = this; $.each( self.slides, function( key, slide ) { self.updateslide( slide ); }); }, // update slide content position and size // ====================================== updateslide : function( slide, duration ) { var self = this, $what = slide && slide.$content; if ( $what && ( slide.width || slide.height ) ) { self.isanimating = false; $.fancybox.stop( $what ); $.fancybox.settranslate( $what, self.getfitpos( slide ) ); if ( slide.pos === self.currpos ) { self.updatecursor(); } } slide.$slide.trigger( 'refresh' ); self.trigger( 'onupdate', slide ); }, // horizontally center slide // ========================= centerslide : function( slide, duration ) { var self = this, canvaswidth, pos; if ( self.current ) { canvaswidth = math.round( slide.$slide.width() ); pos = slide.pos - self.current.pos; $.fancybox.animate( slide.$slide, { top : 0, left : ( pos * canvaswidth ) + ( pos * slide.opts.gutter ), opacity : 1 }, duration === undefined ? 0 : duration, null, false); } }, // update cursor style depending if content can be zoomed // ====================================================== updatecursor : function( nextwidth, nextheight ) { var self = this; var isscaleddown; var $container = self.$refs.container.removeclass( 'fancybox-is-zoomable fancybox-can-zoomin fancybox-can-drag fancybox-can-zoomout' ); if ( !self.current || self.isclosing ) { return; } if ( self.iszoomable() ) { $container.addclass( 'fancybox-is-zoomable' ); if ( nextwidth !== undefined && nextheight !== undefined ) { isscaleddown = nextwidth < self.current.width && nextheight < self.current.height; } else { isscaleddown = self.isscaleddown(); } if ( isscaleddown ) { // if image is scaled down, then, obviously, it can be zoomed to full size $container.addclass( 'fancybox-can-zoomin' ); } else { if ( self.current.opts.touch ) { // if image size ir largen than available available and touch module is not disable, // then user can do panning $container.addclass( 'fancybox-can-drag' ); } else { $container.addclass( 'fancybox-can-zoomout' ); } } } else if ( self.current.opts.touch ) { $container.addclass( 'fancybox-can-drag' ); } }, // check if current slide is zoomable // ================================== iszoomable : function() { var self = this; var current = self.current; var fitpos; if ( !current || self.isclosing ) { return; } // assume that slide is zoomable if // - image is loaded successfuly // - click action is "zoom" // - actual size of the image is smaller than available area if ( current.type === 'image' && current.isloaded && !current.haserror && ( current.opts.clickcontent === 'zoom' || ( $.isfunction( current.opts.clickcontent ) && current.opts.clickcontent( current ) === "zoom" ) ) ) { fitpos = self.getfitpos( current ); if ( current.width > fitpos.width || current.height > fitpos.height ) { return true; } } return false; }, // check if current image dimensions are smaller than actual // ========================================================= isscaleddown : function() { var self = this; var current = self.current; var $what = current.$content; var rez = false; if ( $what ) { rez = $.fancybox.gettranslate( $what ); rez = rez.width < current.width || rez.height < current.height; } return rez; }, // check if image dimensions exceed parent element // =============================================== canpan : function() { var self = this; var current = self.current; var $what = current.$content; var rez = false; if ( $what ) { rez = self.getfitpos( current ); rez = math.abs( $what.width() - rez.width ) > 1 || math.abs( $what.height() - rez.height ) > 1; } return rez; }, // load content into the slide // =========================== loadslide : function( slide ) { var self = this, type, $slide; var ajaxload; if ( slide.isloading ) { return; } if ( slide.isloaded ) { return; } slide.isloading = true; self.trigger( 'beforeload', slide ); type = slide.type; $slide = slide.$slide; $slide .off( 'refresh' ) .trigger( 'onreset' ) .addclass( 'fancybox-slide--' + ( type || 'unknown' ) ) .addclass( slide.opts.slideclass ); // create content depending on the type switch ( type ) { case 'image': self.setimage( slide ); break; case 'iframe': self.setiframe( slide ); break; case 'html': self.setcontent( slide, slide.src || slide.content ); break; case 'inline': if ( $( slide.src ).length ) { self.setcontent( slide, $( slide.src ) ); } else { self.seterror( slide ); } break; case 'ajax': self.showloading( slide ); ajaxload = $.ajax( $.extend( {}, slide.opts.ajax.settings, { url : slide.src, success : function ( data, textstatus ) { if ( textstatus === 'success' ) { self.setcontent( slide, data ); } }, error : function ( jqxhr, textstatus ) { if ( jqxhr && textstatus !== 'abort' ) { self.seterror( slide ); } } })); $slide.one( 'onreset', function () { ajaxload.abort(); }); break; case 'video' : self.setcontent( slide, '' ); break; default: self.seterror( slide ); break; } return true; }, // use thumbnail image, if possible // ================================ setimage : function( slide ) { var self = this; var srcset = slide.opts.srcset || slide.opts.image.srcset; var found, temp, pxratio, windowwidth; // if we have "srcset", then we need to find matching "src" value. // this is necessary, because when you set an src attribute, the browser will preload the image // before any javascript or even css is applied. if ( srcset ) { pxratio = window.devicepixelratio || 1; windowwidth = window.innerwidth * pxratio; temp = srcset.split(',').map(function ( el ) { var ret = {}; el.trim().split(/\s+/).foreach(function ( el, i ) { var value = parseint( el.substring(0, el.length - 1), 10 ); if ( i === 0 ) { return ( ret.url = el ); } if ( value ) { ret.value = value; ret.postfix = el[ el.length - 1 ]; } }); return ret; }); // sort by value temp.sort(function (a, b) { return a.value - b.value; }); // ok, now we have an array of all srcset values for ( var j = 0; j < temp.length; j++ ) { var el = temp[ j ]; if ( ( el.postfix === 'w' && el.value >= windowwidth ) || ( el.postfix === 'x' && el.value >= pxratio ) ) { found = el; break; } } // if not found, take the last one if ( !found && temp.length ) { found = temp[ temp.length - 1 ]; } if ( found ) { slide.src = found.url; // if we have default width/height values, we can calculate height for matching source if ( slide.width && slide.height && found.postfix == 'w' ) { slide.height = ( slide.width / slide.height ) * found.value; slide.width = found.value; } } } // this will be wrapper containing both ghost and actual image slide.$content = $('
') .addclass( 'fancybox-is-hidden' ) .appendto( slide.$slide ); // if we have a thumbnail, we can display it while actual image is loading // users will not stare at black screen and actual image will appear gradually if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) { slide.width = slide.opts.width; slide.height = slide.opts.height; slide.$ghost = $('') .one('error', function() { $(this).remove(); slide.$ghost = null; self.setbigimage( slide ); }) .one('load', function() { self.afterload( slide ); self.setbigimage( slide ); }) .addclass( 'fancybox-image' ) .appendto( slide.$content ) .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) ); } else { self.setbigimage( slide ); } }, // create full-size image // ====================== setbigimage : function ( slide ) { var self = this; var $img = $(''); slide.$image = $img .one('error', function() { self.seterror( slide ); }) .one('load', function() { // clear timeout that checks if loading icon needs to be displayed cleartimeout( slide.timouts ); slide.timouts = null; if ( self.isclosing ) { return; } slide.width = slide.opts.width || this.naturalwidth; slide.height = slide.opts.height || this.naturalheight; if ( slide.opts.image.srcset ) { $img.attr( 'sizes', '100vw' ).attr( 'srcset', slide.opts.image.srcset ); } self.hideloading( slide ); if ( slide.$ghost ) { slide.timouts = settimeout(function() { slide.timouts = null; slide.$ghost.hide(); }, math.min( 300, math.max( 1000, slide.height / 1600 ) ) ); } else { self.afterload( slide ); } }) .addclass( 'fancybox-image' ) .attr('src', slide.src) .appendto( slide.$content ); if ( ( $img[0].complete || $img[0].readystate == "complete" ) && $img[0].naturalwidth && $img[0].naturalheight ) { $img.trigger( 'load' ); } else if( $img[0].error ) { $img.trigger( 'error' ); } else { slide.timouts = settimeout(function() { if ( !$img[0].complete && !slide.haserror ) { self.showloading( slide ); } }, 100); } }, // create iframe wrapper, iframe and bindings // ========================================== setiframe : function( slide ) { var self = this, opts = slide.opts.iframe, $slide = slide.$slide, $iframe; slide.$content = $('
') .css( opts.css ) .appendto( $slide ); $iframe = $( opts.tpl.replace(/\{rnd\}/g, new date().gettime()) ) .attr( opts.attr ) .appendto( slide.$content ); if ( opts.preload ) { self.showloading( slide ); // unfortunately, it is not always possible to determine if iframe is successfully loaded // (due to browser security policy) $iframe.on('load.fb error.fb', function(e) { this.isready = 1; slide.$slide.trigger( 'refresh' ); self.afterload( slide ); }); // recalculate iframe content size // =============================== $slide.on('refresh.fb', function() { var $wrap = slide.$content, framewidth = opts.css.width, frameheight = opts.css.height, scrollwidth, $contents, $body; if ( $iframe[0].isready !== 1 ) { return; } // check if content is accessible, // it will fail if frame is not with the same origin try { $contents = $iframe.contents(); $body = $contents.find('body'); } catch (ignore) {} // calculate dimensions for the wrapper if ( $body && $body.length ) { if ( framewidth === undefined ) { scrollwidth = $iframe[0].contentwindow.document.documentelement.scrollwidth; framewidth = math.ceil( $body.outerwidth(true) + ( $wrap.width() - scrollwidth ) ); framewidth += $wrap.outerwidth() - $wrap.innerwidth(); } if ( frameheight === undefined ) { frameheight = math.ceil( $body.outerheight(true) ); frameheight += $wrap.outerheight() - $wrap.innerheight(); } // resize wrapper to fit iframe content if ( framewidth ) { $wrap.width( framewidth ); } if ( frameheight ) { $wrap.height( frameheight ); } } $wrap.removeclass( 'fancybox-is-hidden' ); }); } else { this.afterload( slide ); } $iframe.attr( 'src', slide.src ); if ( slide.opts.smallbtn === true ) { slide.$content.prepend( self.translate( slide, slide.opts.btntpl.smallbtn ) ); } // remove iframe if closing or changing gallery item $slide.one( 'onreset', function () { // this helps ie not to throw errors when closing try { $( this ).find( 'iframe' ).hide().attr( 'src', '//about:blank' ); } catch ( ignore ) {} $( this ).empty(); slide.isloaded = false; }); }, // wrap and append content to the slide // ====================================== setcontent : function ( slide, content ) { var self = this; if ( self.isclosing ) { return; } self.hideloading( slide ); slide.$slide.empty(); if ( isquery( content ) && content.parent().length ) { // if content is a jquery object, then it will be moved to the slide. // the placeholder is created so we will know where to put it back. // if user is navigating gallery fast, then the content might be already inside fancybox // ===================================================================================== // make sure content is not already moved to fancybox content.parent( '.fancybox-slide--inline' ).trigger( 'onreset' ); // create temporary element marking original place of the content slide.$placeholder = $( '
' ).hide().insertafter( content ); // make sure content is visible content.css('display', 'inline-block'); } else if ( !slide.haserror ) { // if content is just a plain text, try to convert it to html if ( $.type( content ) === 'string' ) { content = $('
').append( $.trim( content ) ).contents(); // if we have text node, then add wrapping element to make vertical alignment work if ( content[0].nodetype === 3 ) { content = $('
').html( content ); } } // if "filter" option is provided, then filter content if ( slide.opts.filter ) { content = $('
').html( content ).find( slide.opts.filter ); } } slide.$slide.one('onreset', function () { // pause all html5 video/audio $( this ).find( 'video,audio' ).trigger( 'pause' ); // put content back if ( slide.$placeholder ) { slide.$placeholder.after( content.hide() ).remove(); slide.$placeholder = null; } // remove custom close button if ( slide.$smallbtn ) { slide.$smallbtn.remove(); slide.$smallbtn = null; } // remove content and mark slide as not loaded if ( !slide.haserror ) { $(this).empty(); slide.isloaded = false; } }); slide.$content = $( content ).appendto( slide.$slide ); this.afterload( slide ); }, // display error message // ===================== seterror : function ( slide ) { slide.haserror = true; slide.$slide.removeclass( 'fancybox-slide--' + slide.type ); this.setcontent( slide, this.translate( slide, slide.opts.errortpl ) ); }, // show loading icon inside the slide // ================================== showloading : function( slide ) { var self = this; slide = slide || self.current; if ( slide && !slide.$spinner ) { slide.$spinner = $( self.opts.spinnertpl ).appendto( slide.$slide ); } }, // remove loading icon from the slide // ================================== hideloading : function( slide ) { var self = this; slide = slide || self.current; if ( slide && slide.$spinner ) { slide.$spinner.remove(); delete slide.$spinner; } }, // adjustments after slide content has been loaded // =============================================== afterload : function( slide ) { var self = this; if ( self.isclosing ) { return; } slide.isloading = false; slide.isloaded = true; self.trigger( 'afterload', slide ); self.hideloading( slide ); if ( slide.opts.smallbtn && !slide.$smallbtn ) { slide.$smallbtn = $( self.translate( slide, slide.opts.btntpl.smallbtn ) ).appendto( slide.$content.filter('div,form').first() ); } if ( slide.opts.protect && slide.$content && !slide.haserror ) { // disable right click slide.$content.on( 'contextmenu.fb', function( e ) { if ( e.button == 2 ) { e.preventdefault(); } return true; }); // add fake element on top of the image // this makes a bit harder for user to select image if ( slide.type === 'image' ) { $( '
' ).appendto( slide.$content ); } } self.revealcontent( slide ); }, // make content visible // this method is called right after content has been loaded or // user navigates gallery and transition should start // ============================================================ revealcontent : function( slide ) { var self = this; var $slide = slide.$slide; var effect, effectclassname, duration, opacity, end, start = false; effect = slide.opts[ self.firstrun ? 'animationeffect' : 'transitioneffect' ]; duration = slide.opts[ self.firstrun ? 'animationduration' : 'transitionduration' ]; duration = parseint( slide.forcedduration === undefined ? duration : slide.forcedduration, 10 ); if ( slide.ismoved || slide.pos !== self.currpos || !duration ) { effect = false; } // check if can zoom if ( effect === 'zoom' && !( slide.pos === self.currpos && duration && slide.type === 'image' && !slide.haserror && ( start = self.getthumbpos( slide ) ) ) ) { effect = 'fade'; } // zoom animation // ============== if ( effect === 'zoom' ) { end = self.getfitpos( slide ); end.scalex = end.width / start.width; end.scaley = end.height / start.height; delete end.width; delete end.height; // check if we need to animate opacity opacity = slide.opts.zoomopacity; if ( opacity == 'auto' ) { opacity = math.abs( slide.width / slide.height - start.width / start.height ) > 0.1; } if ( opacity ) { start.opacity = 0.1; end.opacity = 1; } // draw image at start position $.fancybox.settranslate( slide.$content.removeclass( 'fancybox-is-hidden' ), start ); forceredraw( slide.$content ); // start animation $.fancybox.animate( slide.$content, end, duration, function() { self.complete(); }); return; } self.updateslide( slide ); // simply show content // =================== if ( !effect ) { forceredraw( $slide ); slide.$content.removeclass( 'fancybox-is-hidden' ); if ( slide.pos === self.currpos ) { self.complete(); } return; } $.fancybox.stop( $slide ); effectclassname = 'fancybox-animated fancybox-slide--' + ( slide.pos >= self.prevpos ? 'next' : 'previous' ) + ' fancybox-fx-' + effect; $slide.removeattr( 'style' ).removeclass( 'fancybox-slide--current fancybox-slide--next fancybox-slide--previous' ).addclass( effectclassname ); slide.$content.removeclass( 'fancybox-is-hidden' ); //force reflow for css3 transitions forceredraw( $slide ); $.fancybox.animate( $slide, 'fancybox-slide--current', duration, function(e) { $slide.removeclass( effectclassname ).removeattr( 'style' ); if ( slide.pos === self.currpos ) { self.complete(); } }, true); }, // check if we can and have to zoom from thumbnail //================================================ getthumbpos : function( slide ) { var self = this; var rez = false; // check if element is inside the viewport by at least 1 pixel var iselementvisible = function( $el ) { var element = $el[0]; var elementrect = element.getboundingclientrect(); var parentrects = []; var visibleinallparents; while ( element.parentelement !== null ) { if ( $(element.parentelement).css('overflow') === 'hidden' || $(element.parentelement).css('overflow') === 'auto' ) { parentrects.push(element.parentelement.getboundingclientrect()); } element = element.parentelement; } visibleinallparents = parentrects.every(function(parentrect){ var visiblepixelx = math.min(elementrect.right, parentrect.right) - math.max(elementrect.left, parentrect.left); var visiblepixely = math.min(elementrect.bottom, parentrect.bottom) - math.max(elementrect.top, parentrect.top); return visiblepixelx > 0 && visiblepixely > 0; }); return visibleinallparents && elementrect.bottom > 0 && elementrect.right > 0 && elementrect.left < $(window).width() && elementrect.top < $(window).height(); }; var $thumb = slide.opts.$thumb; var thumbpos = $thumb ? $thumb.offset() : 0; var slidepos; if ( thumbpos && $thumb[0].ownerdocument === document && iselementvisible( $thumb ) ) { slidepos = self.$refs.stage.offset(); rez = { top : thumbpos.top - slidepos.top + parsefloat( $thumb.css( "border-top-width" ) || 0 ), left : thumbpos.left - slidepos.left + parsefloat( $thumb.css( "border-left-width" ) || 0 ), width : $thumb.width(), height : $thumb.height(), scalex : 1, scaley : 1 }; } return rez; }, // final adjustments after current gallery item is moved to position // and it`s content is loaded // ================================================================== complete : function() { var self = this, current = self.current, slides = {}, promise; if ( current.ismoved || !current.isloaded || current.iscomplete ) { return; } current.iscomplete = true; current.$slide.siblings().trigger( 'onreset' ); self.preload( 'inline' ); // trigger any css3 transiton inside the slide forceredraw( current.$slide ); current.$slide.addclass( 'fancybox-slide--complete' ); // remove unnecessary slides $.each( self.slides, function( key, slide ) { if ( slide.pos >= self.currpos - 1 && slide.pos <= self.currpos + 1 ) { slides[ slide.pos ] = slide; } else if ( slide ) { $.fancybox.stop( slide.$slide ); slide.$slide.off().remove(); } }); self.slides = slides; self.updatecursor(); self.trigger( 'aftershow' ); // play first html5 video/audio current.$slide.find( 'video,audio' ).first().trigger( 'play' ); // try to focus on the first focusable element if ( $( document.activeelement ).is( '[disabled]' ) || ( current.opts.autofocus && !( current.type == 'image' || current.type === 'iframe' ) ) ) { self.focus(); } }, // preload next and previous slides // ================================ preload : function( type ) { var self = this, next = self.slides[ self.currpos + 1 ], prev = self.slides[ self.currpos - 1 ]; if ( next && next.type === type ) { self.loadslide( next ); } if ( prev && prev.type === type ) { self.loadslide( prev ); } }, // try to find and focus on the first focusable element // ==================================================== focus : function() { var current = this.current; var $el; if ( this.isclosing ) { return; } if ( current && current.iscomplete ) { // look for first input with autofocus attribute $el = current.$slide.find('input[autofocus]:enabled:visible:first'); if ( !$el.length ) { $el = current.$slide.find('button,:input,[tabindex],a').filter(':enabled:visible:first'); } } $el = $el && $el.length ? $el : this.$refs.container; $el.focus(); }, // activates current instance - brings container to the front and enables keyboard, // notifies other instances about deactivating // ================================================================================= activate : function () { var self = this; // deactivate all instances $( '.fancybox-container' ).each(function () { var instance = $(this).data( 'fancybox' ); // skip self and closing instances if (instance && instance.id !== self.id && !instance.isclosing) { instance.trigger( 'ondeactivate' ); instance.removeevents(); instance.isvisible = false; } }); self.isvisible = true; if ( self.current || self.isidle ) { self.update(); self.updatecontrols(); } self.trigger( 'onactivate' ); self.addevents(); }, // start closing procedure // this will start "zoom-out" animation if needed and clean everything up afterwards // ================================================================================= close : function( e, d ) { var self = this; var current = self.current; var effect, duration; var $what, opacity, start, end; var done = function() { self.cleanup( e ); }; if ( self.isclosing ) { return false; } self.isclosing = true; // if beforeclose callback prevents closing, make sure content is centered if ( self.trigger( 'beforeclose', e ) === false ) { self.isclosing = false; requestaframe(function() { self.update(); }); return false; } // remove all events // if there are multiple instances, they will be set again by "activate" method self.removeevents(); if ( current.timouts ) { cleartimeout( current.timouts ); } $what = current.$content; effect = current.opts.animationeffect; duration = $.isnumeric( d ) ? d : ( effect ? current.opts.animationduration : 0 ); // remove other slides current.$slide.off( transitionend ).removeclass( 'fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated' ); current.$slide.siblings().trigger( 'onreset' ).remove(); // trigger animations if ( duration ) { self.$refs.container.removeclass( 'fancybox-is-open' ).addclass( 'fancybox-is-closing' ); } // clean up self.hideloading( current ); self.hidecontrols(); self.updatecursor(); // check if possible to zoom-out if ( effect === 'zoom' && !( e !== true && $what && duration && current.type === 'image' && !current.haserror && ( end = self.getthumbpos( current ) ) ) ) { effect = 'fade'; } if ( effect === 'zoom' ) { $.fancybox.stop( $what ); start = $.fancybox.gettranslate( $what ); start.width = start.width * start.scalex; start.height = start.height * start.scaley; // check if we need to animate opacity opacity = current.opts.zoomopacity; if ( opacity == 'auto' ) { opacity = math.abs( current.width / current.height - end.width / end.height ) > 0.1; } if ( opacity ) { end.opacity = 0; } start.scalex = start.width / end.width; start.scaley = start.height / end.height; start.width = end.width; start.height = end.height; $.fancybox.settranslate( current.$content, start ); forceredraw( current.$content ); $.fancybox.animate( current.$content, end, duration, done ); return true; } if ( effect && duration ) { // if skip animation if ( e === true ) { settimeout( done, duration ); } else { $.fancybox.animate( current.$slide.removeclass( 'fancybox-slide--current' ), 'fancybox-animated fancybox-slide--previous fancybox-fx-' + effect, duration, done ); } } else { done(); } return true; }, // final adjustments after removing the instance // ============================================= cleanup : function( e ) { var self = this, $body = $( 'body' ), instance, offset; self.current.$slide.trigger( 'onreset' ); self.$refs.container.empty().remove(); self.trigger( 'afterclose', e ); // place back focus if ( self.$lastfocus && !!self.current.opts.backfocus ) { self.$lastfocus.focus(); } self.current = null; // check if there are other instances instance = $.fancybox.getinstance(); if ( instance ) { instance.activate(); } else { $w.scrolltop( self.scrolltop ).scrollleft( self.scrollleft ); $body.removeclass( 'fancybox-active compensate-for-scrollbar' ); if ( $body.hasclass( 'fancybox-iosfix' ) ) { offset = parseint(document.body.style.top, 10); $body.removeclass( 'fancybox-iosfix' ).css( 'top', '' ).scrolltop( offset * -1 ); } $( '#fancybox-style-noscroll' ).remove(); } }, // call callback and trigger an event // ================================== trigger : function( name, slide ) { var args = array.prototype.slice.call(arguments, 1), self = this, obj = slide && slide.opts ? slide : self.current, rez; if ( obj ) { args.unshift( obj ); } else { obj = self; } args.unshift( self ); if ( $.isfunction( obj.opts[ name ] ) ) { rez = obj.opts[ name ].apply( obj, args ); } if ( rez === false ) { return rez; } if ( name === 'afterclose' || !self.$refs ) { $d.trigger( name + '.fb', args ); } else { self.$refs.container.trigger( name + '.fb', args ); } }, // update infobar values, navigation button states and reveal caption // ================================================================== updatecontrols : function ( force ) { var self = this; var current = self.current, index = current.index, caption = current.opts.caption, $container = self.$refs.container, $caption = self.$refs.caption; // recalculate content dimensions current.$slide.trigger( 'refresh' ); self.$caption = caption && caption.length ? $caption.html( caption ) : null; if ( !self.ishiddencontrols && !self.isidle ) { self.showcontrols(); } // update info and navigation elements $container.find('[data-fancybox-count]').html( self.group.length ); $container.find('[data-fancybox-index]').html( index + 1 ); $container.find('[data-fancybox-prev]').prop( 'disabled', ( !current.opts.loop && index <= 0 ) ); $container.find('[data-fancybox-next]').prop( 'disabled', ( !current.opts.loop && index >= self.group.length - 1 ) ); if ( current.type === 'image' ) { // update download button source $container.find('[data-fancybox-download]').attr( 'href', current.opts.image.src || current.src ).show(); } else { $container.find('[data-fancybox-download],[data-fancybox-zoom]').hide(); } }, // hide toolbar and caption // ======================== hidecontrols : function () { this.ishiddencontrols = true; this.$refs.container.removeclass( 'fancybox-show-infobar fancybox-show-toolbar fancybox-show-caption fancybox-show-nav' ); }, showcontrols : function() { var self = this; var opts = self.current ? self.current.opts : self.opts; var $container = self.$refs.container; self.ishiddencontrols = false; self.idlesecondscounter = 0; $container .toggleclass( 'fancybox-show-toolbar', !!( opts.toolbar && opts.buttons ) ) .toggleclass( 'fancybox-show-infobar', !!( opts.infobar && self.group.length > 1 ) ) .toggleclass( 'fancybox-show-nav', !!( opts.arrows && self.group.length > 1 ) ) .toggleclass( 'fancybox-is-modal', !!opts.modal ); if ( self.$caption ) { $container.addclass( 'fancybox-show-caption '); } else { $container.removeclass( 'fancybox-show-caption' ); } }, // toggle toolbar and caption // ========================== togglecontrols : function() { if ( this.ishiddencontrols ) { this.showcontrols(); } else { this.hidecontrols(); } }, }); $.fancybox = { version : "3.2.10", defaults : defaults, // get current instance and execute a command. // // examples of usage: // // $instance = $.fancybox.getinstance(); // $.fancybox.getinstance().jumpto( 1 ); // $.fancybox.getinstance( 'jumpto', 1 ); // $.fancybox.getinstance( function() { // console.info( this.currindex ); // }); // ====================================================== getinstance : function ( command ) { var instance = $('.fancybox-container:not(".fancybox-is-closing"):last').data( 'fancybox' ); var args = array.prototype.slice.call(arguments, 1); if ( instance instanceof fancybox ) { if ( $.type( command ) === 'string' ) { instance[ command ].apply( instance, args ); } else if ( $.type( command ) === 'function' ) { command.apply( instance, args ); } return instance; } return false; }, // create new instance // =================== open : function ( items, opts, index ) { return new fancybox( items, opts, index ); }, // close current or all instances // ============================== close : function ( all ) { var instance = this.getinstance(); if ( instance ) { instance.close(); // try to find and close next instance if ( all === true ) { this.close(); } } }, // close instances and unbind all events // ============================== destroy : function() { this.close( true ); $d.off( 'click.fb-start' ); }, // try to detect mobile devices // ============================ ismobile : document.createtouch !== undefined && /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.useragent), // detect if 'translate3d' support is available // ============================================ use3d : (function() { var div = document.createelement('div'); return window.getcomputedstyle && window.getcomputedstyle( div ).getpropertyvalue('transform') && !(document.documentmode && document.documentmode < 11); }()), // helper function to get current visual state of an element // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] // ===================================================================== gettranslate : function( $el ) { var matrix; if ( !$el || !$el.length ) { return false; } matrix = $el.eq( 0 ).css('transform'); if ( matrix && matrix.indexof( 'matrix' ) !== -1 ) { matrix = matrix.split('(')[1]; matrix = matrix.split(')')[0]; matrix = matrix.split(','); } else { matrix = []; } if ( matrix.length ) { // if ie if ( matrix.length > 10 ) { matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ]; } else { matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]]; } matrix = matrix.map(parsefloat); } else { matrix = [ 0, 0, 1, 1 ]; var transregex = /\.*translate\((.*)px,(.*)px\)/i; var transrez = transregex.exec( $el.eq( 0 ).attr('style') ); if ( transrez ) { matrix[ 0 ] = parsefloat( transrez[2] ); matrix[ 1 ] = parsefloat( transrez[1] ); } } return { top : matrix[ 0 ], left : matrix[ 1 ], scalex : matrix[ 2 ], scaley : matrix[ 3 ], opacity : parsefloat( $el.css('opacity') ), width : $el.width(), height : $el.height() }; }, // shortcut for setting "translate3d" properties for element // can set be used to set opacity, too // ======================================================== settranslate : function( $el, props ) { var str = ''; var css = {}; if ( !$el || !props ) { return; } if ( props.left !== undefined || props.top !== undefined ) { str = ( props.left === undefined ? $el.position().left : props.left ) + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px'; if ( this.use3d ) { str = 'translate3d(' + str + ', 0px)'; } else { str = 'translate(' + str + ')'; } } if ( props.scalex !== undefined && props.scaley !== undefined ) { str = (str.length ? str + ' ' : '') + 'scale(' + props.scalex + ', ' + props.scaley + ')'; } if ( str.length ) { css.transform = str; } if ( props.opacity !== undefined ) { css.opacity = props.opacity; } if ( props.width !== undefined ) { css.width = props.width; } if ( props.height !== undefined ) { css.height = props.height; } return $el.css( css ); }, // simple css transition handler // ============================= animate : function ( $el, to, duration, callback, leaveanimationname ) { if ( $.isfunction( duration ) ) { callback = duration; duration = null; } if ( !$.isplainobject( to ) ) { $el.removeattr( 'style' ); } $el.on( transitionend, function(e) { // skip events from child elements and z-index change if ( e && e.originalevent && ( !$el.is( e.originalevent.target ) || e.originalevent.propertyname == 'z-index' ) ) { return; } $.fancybox.stop( $el ); if ( $.isplainobject( to ) ) { if ( to.scalex !== undefined && to.scaley !== undefined ) { $el.css( 'transition-duration', '' ); to.width = math.round( $el.width() * to.scalex ); to.height = math.round( $el.height() * to.scaley ); to.scalex = 1; to.scaley = 1; $.fancybox.settranslate( $el, to ); } if ( leaveanimationname === false ) { $el.removeattr( 'style' ); } } else if ( leaveanimationname !== true ) { $el.removeclass( to ); } if ( $.isfunction( callback ) ) { callback( e ); } }); if ( $.isnumeric( duration ) ) { $el.css( 'transition-duration', duration + 'ms' ); } if ( $.isplainobject( to ) ) { $.fancybox.settranslate( $el, to ); } else { $el.addclass( to ); } if ( to.scalex && $el.hasclass( 'fancybox-image-wrap' ) ) { $el.parent().addclass( 'fancybox-is-scaling' ); } // make sure that `transitionend` callback gets fired $el.data("timer", settimeout(function() { $el.trigger( 'transitionend' ); }, duration + 16)); }, stop : function( $el ) { cleartimeout( $el.data("timer") ); $el.off( 'transitionend' ).css( 'transition-duration', '' ); if ( $el.hasclass( 'fancybox-image-wrap' ) ) { $el.parent().removeclass( 'fancybox-is-scaling' ); } } }; // default click handler for "fancyboxed" links // ============================================ function _run( e ) { var $target = $( e.currenttarget ), opts = e.data ? e.data.options : {}, value = $target.attr( 'data-fancybox' ) || '', index = 0, items = []; // avoid opening multiple times if ( e.isdefaultprevented() ) { return; } e.preventdefault(); // get all related items and find index for clicked one if ( value ) { items = opts.selector ? $( opts.selector ) : ( e.data ? e.data.items : [] ); items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox="' + value + '"]' ); index = items.index( $target ); // sometimes current item can not be found // (for example, when slider clones items) if ( index < 0 ) { index = 0; } } else { items = [ $target ]; } $.fancybox.open( items, opts, index ); } // create a jquery plugin // ====================== $.fn.fancybox = function (options) { var selector; options = options || {}; selector = options.selector || false; if ( selector ) { $( 'body' ).off( 'click.fb-start', selector ).on( 'click.fb-start', selector, { options : options }, _run ); } else { this.off( 'click.fb-start' ).on( 'click.fb-start', { items : this, options : options }, _run); } return this; }; // self initializing plugin // ======================== $d.on( 'click.fb-start', '[data-fancybox]', _run ); }( window, document, window.jquery || jquery )); // ========================================================================== // // media // adds additional media type support // // ========================================================================== ;(function ($) { 'use strict'; // formats matching url to final form var format = function (url, rez, params) { if ( !url ) { return; } params = params || ''; if ( $.type(params) === "object" ) { params = $.param(params, true); } $.each(rez, function (key, value) { url = url.replace('$' + key, value || ''); }); if (params.length) { url += (url.indexof('?') > 0 ? '&' : '?') + params; } return url; }; // object containing properties for each media type var defaults = { youtube : { matcher : /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listtype=(.*)&list=(.*))(.*)/i, params : { autoplay : 1, autohide : 1, fs : 1, rel : 0, hd : 1, wmode : 'transparent', enablejsapi : 1, html5 : 1 }, paramplace : 8, type : 'iframe', url : '//www.youtube.com/embed/$4', thumb : '//img.youtube.com/vi/$4/hqdefault.jpg' }, vimeo : { matcher : /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, params : { autoplay : 1, hd : 1, show_title : 1, show_byline : 1, show_portrait : 0, fullscreen : 1, api : 1 }, paramplace : 3, type : 'iframe', url : '//player.vimeo.com/video/$2' }, metacafe : { matcher : /metacafe.com\/watch\/(\d+)\/(.*)?/, type : 'iframe', url : '//www.metacafe.com/embed/$1/?ap=1' }, dailymotion : { matcher : /dailymotion.com\/video\/(.*)\/?(.*)/, params : { additionalinfos : 0, autostart : 1 }, type : 'iframe', url : '//www.dailymotion.com/embed/video/$1' }, vine : { matcher : /vine.co\/v\/([a-za-z0-9\?\=\-]+)/, type : 'iframe', url : '//vine.co/v/$1/embed/simple' }, instagram : { matcher : /(instagr\.am|instagram\.com)\/p\/([a-za-z0-9_\-]+)\/?/i, type : 'image', url : '//$1/p/$2/media/?size=l' }, // examples: // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z // https://www.google.com/maps/place/googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 gmap_place : { matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, type : 'iframe', url : function (rez) { 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' ); } }, // examples: // https://www.google.com/maps/search/empire+state+building/ // https://www.google.com/maps/search/?api=1&query=centurylink+field // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393 gmap_search : { matcher : /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i, type : 'iframe', url : function (rez) { return '//maps.google.' + rez[2] + '/maps?q=' + rez[5].replace('query=', 'q=').replace('api=1', '') + '&output=embed'; } } }; $(document).on('objectneedstype.fb', function (e, instance, item) { var url = item.src || '', type = false, media, thumb, rez, params, urlparams, paramobj, provider; media = $.extend( true, {}, defaults, item.opts.media ); // look for any matching media type $.each(media, function ( providername, provideropts ) { rez = url.match( provideropts.matcher ); if ( !rez ) { return; } type = provideropts.type; paramobj = {}; if ( provideropts.paramplace && rez[ provideropts.paramplace ] ) { urlparams = rez[ provideropts.paramplace ]; if ( urlparams[ 0 ] == '?' ) { urlparams = urlparams.substring(1); } urlparams = urlparams.split('&'); for ( var m = 0; m < urlparams.length; ++m ) { var p = urlparams[ m ].split('=', 2); if ( p.length == 2 ) { paramobj[ p[0] ] = decodeuricomponent( p[1].replace(/\+/g, " ") ); } } } params = $.extend( true, {}, provideropts.params, item.opts[ providername ], paramobj ); url = $.type( provideropts.url ) === "function" ? provideropts.url.call( this, rez, params, item ) : format( provideropts.url, rez, params ); thumb = $.type( provideropts.thumb ) === "function" ? provideropts.thumb.call( this, rez, params, item ) : format( provideropts.thumb, rez ); if ( providername === 'vimeo' ) { url = url.replace('&%23', '#'); } return false; }); // if it is found, then change content type and update the url if ( type ) { item.src = url; item.type = type; if ( !item.opts.thumb && !( item.opts.$thumb && item.opts.$thumb.length ) ) { item.opts.thumb = thumb; } if ( type === 'iframe' ) { $.extend(true, item.opts, { iframe : { preload : false, attr : { scrolling : "no" } } }); item.contentprovider = provider; item.opts.slideclass += ' fancybox-slide--' + ( provider == 'gmap_place' || provider == 'gmap_search' ? 'map' : 'video' ); } } else if ( url ) { item.type = item.opts.defaulttype; } }); }( window.jquery || jquery )); // ========================================================================== // // guestures // adds touch guestures, handles click and tap events // // ========================================================================== ;(function (window, document, $) { 'use strict'; var requestaframe = (function () { return window.requestanimationframe || window.webkitrequestanimationframe || window.mozrequestanimationframe || window.orequestanimationframe || // if all else fails, use settimeout function (callback) { return window.settimeout(callback, 1000 / 60); }; })(); var cancelaframe = (function () { return window.cancelanimationframe || window.webkitcancelanimationframe || window.mozcancelanimationframe || window.ocancelanimationframe || function (id) { window.cleartimeout(id); }; })(); var pointers = function( e ) { var result = []; e = e.originalevent || e || window.e; e = e.touches && e.touches.length ? e.touches : ( e.changedtouches && e.changedtouches.length ? e.changedtouches : [ e ] ); for ( var key in e ) { if ( e[ key ].pagex ) { result.push( { x : e[ key ].pagex, y : e[ key ].pagey } ); } else if ( e[ key ].clientx ) { result.push( { x : e[ key ].clientx, y : e[ key ].clienty } ); } } return result; }; var distance = function( point2, point1, what ) { if ( !point1 || !point2 ) { return 0; } if ( what === 'x' ) { return point2.x - point1.x; } else if ( what === 'y' ) { return point2.y - point1.y; } return math.sqrt( math.pow( point2.x - point1.x, 2 ) + math.pow( point2.y - point1.y, 2 ) ); }; var isclickable = function( $el ) { if ( $el.is('a,area,button,[role="button"],input,label,select,summary,textarea') || $.isfunction( $el.get(0).onclick ) || $el.data('selectable') ) { return true; } // check for attributes like data-fancybox-next or data-fancybox-close for ( var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++ ) { if ( atts[i].nodename.substr(0, 14) === 'data-fancybox-' ) { return true; } } return false; }; var hasscrollbars = function( el ) { var overflowy = window.getcomputedstyle( el )['overflow-y']; var overflowx = window.getcomputedstyle( el )['overflow-x']; var vertical = (overflowy === 'scroll' || overflowy === 'auto') && el.scrollheight > el.clientheight; var horizontal = (overflowx === 'scroll' || overflowx === 'auto') && el.scrollwidth > el.clientwidth; return vertical || horizontal; }; var isscrollable = function ( $el ) { var rez = false; while ( true ) { rez = hasscrollbars( $el.get(0) ); if ( rez ) { break; } $el = $el.parent(); if ( !$el.length || $el.hasclass( 'fancybox-stage' ) || $el.is( 'body' ) ) { break; } } return rez; }; var guestures = function ( instance ) { var self = this; self.instance = instance; self.$bg = instance.$refs.bg; self.$stage = instance.$refs.stage; self.$container = instance.$refs.container; self.destroy(); self.$container.on( 'touchstart.fb.touch mousedown.fb.touch', $.proxy(self, 'ontouchstart') ); }; guestures.prototype.destroy = function() { this.$container.off( '.fb.touch' ); }; guestures.prototype.ontouchstart = function( e ) { var self = this; var $target = $( e.target ); var instance = self.instance; var current = instance.current; var $content = current.$content; var istouchdevice = ( e.type == 'touchstart' ); // do not respond to both (touch and mouse) events if ( istouchdevice ) { self.$container.off( 'mousedown.fb.touch' ); } // ignore right click if ( e.originalevent && e.originalevent.button == 2 ) { return; } // ignore taping on links, buttons, input elements if ( !$target.length || isclickable( $target ) || isclickable( $target.parent() ) ) { return; } // ignore clicks on the scrollbar if ( !$target.is('img') && e.originalevent.clientx > $target[0].clientwidth + $target.offset().left ) { return; } // ignore clicks while zooming or closing if ( !current || self.instance.isanimating || self.instance.isclosing ) { e.stoppropagation(); e.preventdefault(); return; } self.realpoints = self.startpoints = pointers( e ); if ( !self.startpoints ) { return; } e.stoppropagation(); self.startevent = e; self.cantap = true; self.$target = $target; self.$content = $content; self.opts = current.opts.touch; self.ispanning = false; self.isswiping = false; self.iszooming = false; self.isscrolling = false; self.sliderstartpos = self.sliderlastpos || { top: 0, left: 0 }; self.contentstartpos = $.fancybox.gettranslate( self.$content ); self.contentlastpos = null; self.starttime = new date().gettime(); self.distancex = self.distancey = self.distance = 0; self.canvaswidth = math.round( current.$slide[0].clientwidth ); self.canvasheight = math.round( current.$slide[0].clientheight ); $(document) .off( '.fb.touch' ) .on( istouchdevice ? 'touchend.fb.touch touchcancel.fb.touch' : 'mouseup.fb.touch mouseleave.fb.touch', $.proxy(self, "ontouchend")) .on( istouchdevice ? 'touchmove.fb.touch' : 'mousemove.fb.touch', $.proxy(self, "ontouchmove")); if ( $.fancybox.ismobile ) { document.addeventlistener('scroll', self.onscroll, true); } if ( !(self.opts || instance.canpan() ) || !( $target.is( self.$stage ) || self.$stage.find( $target ).length ) ) { // prevent image ghosting while dragging if ( $target.is('img') ) { e.preventdefault(); } return; } if ( !( $.fancybox.ismobile && ( isscrollable( $target ) || isscrollable( $target.parent() ) ) ) ) { e.preventdefault(); } if ( self.startpoints.length === 1 ) { if ( current.type === 'image' && ( self.contentstartpos.width > self.canvaswidth + 1 || self.contentstartpos.height > self.canvasheight + 1 ) ) { $.fancybox.stop( self.$content ); self.$content.css( 'transition-duration', '' ); self.ispanning = true; } else { self.isswiping = true; } self.$container.addclass( 'fancybox-controls--isgrabbing' ); } if ( self.startpoints.length === 2 && !instance.isanimating && !current.haserror && current.type === 'image' && ( current.isloaded || current.$ghost ) ) { self.cantap = false; self.isswiping = false; self.ispanning = false; self.iszooming = true; $.fancybox.stop( self.$content ); self.$content.css( 'transition-duration', '' ); self.centerpointstartx = ( ( self.startpoints[0].x + self.startpoints[1].x ) * 0.5 ) - $(window).scrollleft(); self.centerpointstarty = ( ( self.startpoints[0].y + self.startpoints[1].y ) * 0.5 ) - $(window).scrolltop(); self.percentageofimageatpinchpointx = ( self.centerpointstartx - self.contentstartpos.left ) / self.contentstartpos.width; self.percentageofimageatpinchpointy = ( self.centerpointstarty - self.contentstartpos.top ) / self.contentstartpos.height; self.startdistancebetweenfingers = distance( self.startpoints[0], self.startpoints[1] ); } }; guestures.prototype.onscroll = function(e) { self.isscrolling = true; }; guestures.prototype.ontouchmove = function( e ) { var self = this, $target = $(e.target); if ( self.isscrolling || !( $target.is( self.$stage ) || self.$stage.find( $target ).length ) ) { self.cantap = false; return; } self.newpoints = pointers( e ); if ( !( self.opts || self.instance.canpan() ) || !self.newpoints || !self.newpoints.length ) { return; } if ( !(self.isswiping && self.isswiping === true) ) { e.preventdefault(); } self.distancex = distance( self.newpoints[0], self.startpoints[0], 'x' ); self.distancey = distance( self.newpoints[0], self.startpoints[0], 'y' ); self.distance = distance( self.newpoints[0], self.startpoints[0] ) // skip false ontouchmove events (chrome) if ( self.distance > 0 ) { if ( self.isswiping ) { self.onswipe(e); } else if ( self.ispanning ) { self.onpan(); } else if ( self.iszooming ) { self.onzoom(); } } }; guestures.prototype.onswipe = function(e) { var self = this, swiping = self.isswiping, left = self.sliderstartpos.left || 0, angle; // if direction is not yet determined if ( swiping === true ) { // we need at least 10px distance to correctly calculate an angle if ( math.abs( self.distance ) > 10 ) { self.cantap = false; if ( self.instance.group.length < 2 && self.opts.vertical ) { self.isswiping = 'y'; } else if ( self.instance.isdragging || self.opts.vertical === false || ( self.opts.vertical === 'auto' && $( window ).width() > 800 ) ) { self.isswiping = 'x'; } else { angle = math.abs( math.atan2( self.distancey, self.distancex ) * 180 / math.pi ); self.isswiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x'; } self.cantap = false; if ( self.isswiping === 'y' && $.fancybox.ismobile && ( isscrollable( self.$target ) || isscrollable( self.$target.parent() ) ) ) { self.isscrolling = true; return; } self.instance.isdragging = self.isswiping; // reset points to avoid jumping, because we dropped first swipes to calculate the angle self.startpoints = self.newpoints; $.each(self.instance.slides, function( index, slide ) { $.fancybox.stop( slide.$slide ); slide.$slide.css( 'transition-duration', '' ); slide.intransition = false; if ( slide.pos === self.instance.current.pos ) { self.sliderstartpos.left = $.fancybox.gettranslate( slide.$slide ).left; } }); // stop slideshow if ( self.instance.slideshow && self.instance.slideshow.isactive ) { self.instance.slideshow.stop(); } } return; } // sticky edges if ( swiping == 'x' ) { if ( self.distancex > 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === 0 && !self.instance.current.opts.loop ) ) ) { left = left + math.pow( self.distancex, 0.8 ); } else if ( self.distancex < 0 && ( self.instance.group.length < 2 || ( self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop ) ) ) { left = left - math.pow( -self.distancex, 0.8 ); } else { left = left + self.distancex; } } self.sliderlastpos = { top : swiping == 'x' ? 0 : self.sliderstartpos.top + self.distancey, left : left }; if ( self.requestid ) { cancelaframe( self.requestid ); self.requestid = null; } self.requestid = requestaframe(function() { if ( self.sliderlastpos ) { $.each(self.instance.slides, function( index, slide ) { var pos = slide.pos - self.instance.currpos; $.fancybox.settranslate( slide.$slide, { top : self.sliderlastpos.top, left : self.sliderlastpos.left + ( pos * self.canvaswidth ) + ( pos * slide.opts.gutter ) }); }); self.$container.addclass( 'fancybox-is-sliding' ); } }); }; guestures.prototype.onpan = function() { var self = this; // sometimes, when tapping causally, image can move a bit and that breaks double tapping if ( distance( self.newpoints[0], self.realpoints[0] ) < ($.fancybox.ismobile ? 10 : 5) ) { self.startpoints = self.newpoints; return; } self.cantap = false; self.contentlastpos = self.limitmovement(); if ( self.requestid ) { cancelaframe( self.requestid ); self.requestid = null; } self.requestid = requestaframe(function() { $.fancybox.settranslate( self.$content, self.contentlastpos ); }); }; // make panning sticky to the edges guestures.prototype.limitmovement = function() { var self = this; var canvaswidth = self.canvaswidth; var canvasheight = self.canvasheight; var distancex = self.distancex; var distancey = self.distancey; var contentstartpos = self.contentstartpos; var currentoffsetx = contentstartpos.left; var currentoffsety = contentstartpos.top; var currentwidth = contentstartpos.width; var currentheight = contentstartpos.height; var mintranslatex, mintranslatey, maxtranslatex, maxtranslatey, newoffsetx, newoffsety; if ( currentwidth > canvaswidth ) { newoffsetx = currentoffsetx + distancex; } else { newoffsetx = currentoffsetx; } newoffsety = currentoffsety + distancey; // slow down proportionally to traveled distance mintranslatex = math.max( 0, canvaswidth * 0.5 - currentwidth * 0.5 ); mintranslatey = math.max( 0, canvasheight * 0.5 - currentheight * 0.5 ); maxtranslatex = math.min( canvaswidth - currentwidth, canvaswidth * 0.5 - currentwidth * 0.5 ); maxtranslatey = math.min( canvasheight - currentheight, canvasheight * 0.5 - currentheight * 0.5 ); if ( currentwidth > canvaswidth ) { // -> if ( distancex > 0 && newoffsetx > mintranslatex ) { newoffsetx = mintranslatex - 1 + math.pow( -mintranslatex + currentoffsetx + distancex, 0.8 ) || 0; } // <- if ( distancex < 0 && newoffsetx < maxtranslatex ) { newoffsetx = maxtranslatex + 1 - math.pow( maxtranslatex - currentoffsetx - distancex, 0.8 ) || 0; } } if ( currentheight > canvasheight ) { // \/ if ( distancey > 0 && newoffsety > mintranslatey ) { newoffsety = mintranslatey - 1 + math.pow(-mintranslatey + currentoffsety + distancey, 0.8 ) || 0; } // /\ if ( distancey < 0 && newoffsety < maxtranslatey ) { newoffsety = maxtranslatey + 1 - math.pow ( maxtranslatey - currentoffsety - distancey, 0.8 ) || 0; } } return { top : newoffsety, left : newoffsetx, scalex : contentstartpos.scalex, scaley : contentstartpos.scaley }; }; guestures.prototype.limitposition = function( newoffsetx, newoffsety, newwidth, newheight ) { var self = this; var canvaswidth = self.canvaswidth; var canvasheight = self.canvasheight; if ( newwidth > canvaswidth ) { newoffsetx = newoffsetx > 0 ? 0 : newoffsetx; newoffsetx = newoffsetx < canvaswidth - newwidth ? canvaswidth - newwidth : newoffsetx; } else { // center horizontally newoffsetx = math.max( 0, canvaswidth / 2 - newwidth / 2 ); } if ( newheight > canvasheight ) { newoffsety = newoffsety > 0 ? 0 : newoffsety; newoffsety = newoffsety < canvasheight - newheight ? canvasheight - newheight : newoffsety; } else { // center vertically newoffsety = math.max( 0, canvasheight / 2 - newheight / 2 ); } return { top : newoffsety, left : newoffsetx }; }; guestures.prototype.onzoom = function() { var self = this; // calculate current distance between points to get pinch ratio and new width and height var currentwidth = self.contentstartpos.width; var currentheight = self.contentstartpos.height; var currentoffsetx = self.contentstartpos.left; var currentoffsety = self.contentstartpos.top; var enddistancebetweenfingers = distance( self.newpoints[0], self.newpoints[1] ); var pinchratio = enddistancebetweenfingers / self.startdistancebetweenfingers; var newwidth = math.floor( currentwidth * pinchratio ); var newheight = math.floor( currentheight * pinchratio ); // this is the translation due to pinch-zooming var translatefromzoomingx = (currentwidth - newwidth) * self.percentageofimageatpinchpointx; var translatefromzoomingy = (currentheight - newheight) * self.percentageofimageatpinchpointy; //point between the two touches var centerpointendx = ((self.newpoints[0].x + self.newpoints[1].x) / 2) - $(window).scrollleft(); var centerpointendy = ((self.newpoints[0].y + self.newpoints[1].y) / 2) - $(window).scrolltop(); // and this is the translation due to translation of the centerpoint // between the two fingers var translatefromtranslatingx = centerpointendx - self.centerpointstartx; var translatefromtranslatingy = centerpointendy - self.centerpointstarty; // the new offset is the old/current one plus the total translation var newoffsetx = currentoffsetx + ( translatefromzoomingx + translatefromtranslatingx ); var newoffsety = currentoffsety + ( translatefromzoomingy + translatefromtranslatingy ); var newpos = { top : newoffsety, left : newoffsetx, scalex : self.contentstartpos.scalex * pinchratio, scaley : self.contentstartpos.scaley * pinchratio }; self.cantap = false; self.newwidth = newwidth; self.newheight = newheight; self.contentlastpos = newpos; if ( self.requestid ) { cancelaframe( self.requestid ); self.requestid = null; } self.requestid = requestaframe(function() { $.fancybox.settranslate( self.$content, self.contentlastpos ); }); }; guestures.prototype.ontouchend = function( e ) { var self = this; var dms = math.max( (new date().gettime() ) - self.starttime, 1); var swiping = self.isswiping; var panning = self.ispanning; var zooming = self.iszooming; var scrolling = self.isscrolling; self.endpoints = pointers( e ); self.$container.removeclass( 'fancybox-controls--isgrabbing' ); $(document).off( '.fb.touch' ); document.removeeventlistener('scroll', self.onscroll, true); if ( self.requestid ) { cancelaframe( self.requestid ); self.requestid = null; } self.isswiping = false; self.ispanning = false; self.iszooming = false; self.isscrolling = false; self.instance.isdragging = false; if ( self.cantap ) { return self.ontap( e ); } self.speed = 366; // speed in px/ms self.velocityx = self.distancex / dms * 0.5; self.velocityy = self.distancey / dms * 0.5; self.speedx = math.max( self.speed * 0.5, math.min( self.speed * 1.5, ( 1 / math.abs( self.velocityx ) ) * self.speed ) ); if ( panning ) { self.endpanning(); } else if ( zooming ) { self.endzooming(); } else { self.endswiping( swiping, scrolling ); } return; }; guestures.prototype.endswiping = function( swiping, scrolling ) { var self = this, ret = false, len = self.instance.group.length; self.sliderlastpos = null; // close if swiped vertically / navigate if horizontally if ( swiping == 'y' && !scrolling && math.abs( self.distancey ) > 50 ) { // continue vertical movement $.fancybox.animate( self.instance.current.$slide, { top : self.sliderstartpos.top + self.distancey + ( self.velocityy * 150 ), opacity : 0 }, 150 ); ret = self.instance.close( true, 300 ); } else if ( swiping == 'x' && self.distancex > 50 && len > 1 ) { ret = self.instance.previous( self.speedx ); } else if ( swiping == 'x' && self.distancex < -50 && len > 1 ) { ret = self.instance.next( self.speedx ); } if ( ret === false && ( swiping == 'x' || swiping == 'y' ) ) { if ( scrolling || len < 2 ) { self.instance.centerslide( self.instance.current, 150 ); } else { self.instance.jumpto( self.instance.current.index ); } } self.$container.removeclass( 'fancybox-is-sliding' ); }; // limit panning from edges // ======================== guestures.prototype.endpanning = function() { var self = this; var newoffsetx, newoffsety, newpos; if ( !self.contentlastpos ) { return; } if ( self.opts.momentum === false ) { newoffsetx = self.contentlastpos.left; newoffsety = self.contentlastpos.top; } else { // continue movement newoffsetx = self.contentlastpos.left + ( self.velocityx * self.speed ); newoffsety = self.contentlastpos.top + ( self.velocityy * self.speed ); } newpos = self.limitposition( newoffsetx, newoffsety, self.contentstartpos.width, self.contentstartpos.height ); newpos.width = self.contentstartpos.width; newpos.height = self.contentstartpos.height; $.fancybox.animate( self.$content, newpos, 330 ); }; guestures.prototype.endzooming = function() { var self = this; var current = self.instance.current; var newoffsetx, newoffsety, newpos, reset; var newwidth = self.newwidth; var newheight = self.newheight; if ( !self.contentlastpos ) { return; } newoffsetx = self.contentlastpos.left; newoffsety = self.contentlastpos.top; reset = { top : newoffsety, left : newoffsetx, width : newwidth, height : newheight, scalex : 1, scaley : 1 }; // reset scalex/scaley values; this helps for perfomance and does not break animation $.fancybox.settranslate( self.$content, reset ); if ( newwidth < self.canvaswidth && newheight < self.canvasheight ) { self.instance.scaletofit( 150 ); } else if ( newwidth > current.width || newheight > current.height ) { self.instance.scaletoactual( self.centerpointstartx, self.centerpointstarty, 150 ); } else { newpos = self.limitposition( newoffsetx, newoffsety, newwidth, newheight ); // switch from scale() to width/height or animation will not work correctly $.fancybox.settranslate( self.content, $.fancybox.gettranslate( self.$content ) ); $.fancybox.animate( self.$content, newpos, 150 ); } }; guestures.prototype.ontap = function(e) { var self = this; var $target = $( e.target ); var instance = self.instance; var current = instance.current; var endpoints = ( e && pointers( e ) ) || self.startpoints; var tapx = endpoints[0] ? endpoints[0].x - self.$stage.offset().left : 0; var tapy = endpoints[0] ? endpoints[0].y - self.$stage.offset().top : 0; var where; var process = function ( prefix ) { var action = current.opts[ prefix ]; if ( $.isfunction( action ) ) { action = action.apply( instance, [ current, e ] ); } if ( !action) { return; } switch ( action ) { case "close" : instance.close( self.startevent ); break; case "togglecontrols" : instance.togglecontrols( true ); break; case "next" : instance.next(); break; case "nextorclose" : if ( instance.group.length > 1 ) { instance.next(); } else { instance.close( self.startevent ); } break; case "zoom" : if ( current.type == 'image' && ( current.isloaded || current.$ghost ) ) { if ( instance.canpan() ) { instance.scaletofit(); } else if ( instance.isscaleddown() ) { instance.scaletoactual( tapx, tapy ); } else if ( instance.group.length < 2 ) { instance.close( self.startevent ); } } break; } }; // ignore right click if ( e.originalevent && e.originalevent.button == 2 ) { return; } // skip if clicked on the scrollbar if ( !$target.is('img') && tapx > $target[0].clientwidth + $target.offset().left ) { return; } // check where is clicked if ( $target.is( '.fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container' ) ) { where = 'outside'; } else if ( $target.is( '.fancybox-slide' ) ) { where = 'slide'; } else if ( instance.current.$content && instance.current.$content.find( $target ).addback().filter( $target ).length ) { where = 'content'; } else { return; } // check if this is a double tap if ( self.tapped ) { // stop previously created single tap cleartimeout( self.tapped ); self.tapped = null; // skip if distance between taps is too big if ( math.abs( tapx - self.tapx ) > 50 || math.abs( tapy - self.tapy ) > 50 ) { return this; } // ok, now we assume that this is a double-tap process( 'dblclick' + where ); } else { // single tap will be processed if user has not clicked second time within 300ms // or there is no need to wait for double-tap self.tapx = tapx; self.tapy = tapy; if ( current.opts[ 'dblclick' + where ] && current.opts[ 'dblclick' + where ] !== current.opts[ 'click' + where ] ) { self.tapped = settimeout(function() { self.tapped = null; process( 'click' + where ); }, 500); } else { process( 'click' + where ); } } return this; }; $(document).on('onactivate.fb', function (e, instance) { if ( instance && !instance.guestures ) { instance.guestures = new guestures( instance ); } }); }( window, document, window.jquery || jquery )); // ========================================================================== // // slideshow // enables slideshow functionality // // example of usage: // $.fancybox.getinstance().slideshow.start() // // ========================================================================== ;(function (document, $) { 'use strict'; $.extend(true, $.fancybox.defaults, { btntpl : { slideshow : '' }, slideshow : { autostart : false, speed : 3000 } }); var slideshow = function( instance ) { this.instance = instance; this.init(); }; $.extend( slideshow.prototype, { timer : null, isactive : false, $button : null, init : function() { var self = this; self.$button = self.instance.$refs.toolbar.find('[data-fancybox-play]').on('click', function() { self.toggle(); }); if ( self.instance.group.length < 2 || !self.instance.group[ self.instance.currindex ].opts.slideshow ) { self.$button.hide(); } }, set : function( force ) { var self = this; // check if reached last element if ( self.instance && self.instance.current && (force === true || self.instance.current.opts.loop || self.instance.currindex < self.instance.group.length - 1 )) { self.timer = settimeout(function() { if ( self.isactive ) { self.instance.jumpto( (self.instance.currindex + 1) % self.instance.group.length ); } }, self.instance.current.opts.slideshow.speed); } else { self.stop(); self.instance.idlesecondscounter = 0; self.instance.showcontrols(); } }, clear : function() { var self = this; cleartimeout( self.timer ); self.timer = null; }, start : function() { var self = this; var current = self.instance.current; if ( current ) { self.isactive = true; self.$button .attr( 'title', current.opts.i18n[ current.opts.lang ].play_stop ) .removeclass( 'fancybox-button--play' ) .addclass( 'fancybox-button--pause' ); self.set( true ); } }, stop : function() { var self = this; var current = self.instance.current; self.clear(); self.$button .attr( 'title', current.opts.i18n[ current.opts.lang ].play_start ) .removeclass( 'fancybox-button--pause' ) .addclass( 'fancybox-button--play' ); self.isactive = false; }, toggle : function() { var self = this; if ( self.isactive ) { self.stop(); } else { self.start(); } } }); $(document).on({ 'oninit.fb' : function(e, instance) { if ( instance && !instance.slideshow ) { instance.slideshow = new slideshow( instance ); } }, 'beforeshow.fb' : function(e, instance, current, firstrun) { var slideshow = instance && instance.slideshow; if ( firstrun ) { if ( slideshow && current.opts.slideshow.autostart ) { slideshow.start(); } } else if ( slideshow && slideshow.isactive ) { slideshow.clear(); } }, 'aftershow.fb' : function(e, instance, current) { var slideshow = instance && instance.slideshow; if ( slideshow && slideshow.isactive ) { slideshow.set(); } }, 'afterkeydown.fb' : function(e, instance, current, keypress, keycode) { var slideshow = instance && instance.slideshow; // "p" or spacebar if ( slideshow && current.opts.slideshow && ( keycode === 80 || keycode === 32 ) && !$(document.activeelement).is( 'button,a,input' ) ) { keypress.preventdefault(); slideshow.toggle(); } }, 'beforeclose.fb ondeactivate.fb' : function(e, instance) { var slideshow = instance && instance.slideshow; if ( slideshow ) { slideshow.stop(); } } }); // page visibility api to pause slideshow when window is not active $(document).on("visibilitychange", function() { var instance = $.fancybox.getinstance(); var slideshow = instance && instance.slideshow; if ( slideshow && slideshow.isactive ) { if ( document.hidden ) { slideshow.clear(); } else { slideshow.set(); } } }); }( document, window.jquery || jquery )); // ========================================================================== // // fullscreen // adds fullscreen functionality // // ========================================================================== ;(function (document, $) { 'use strict'; // collection of methods supported by user browser var fn = (function () { var fnmap = [ [ 'requestfullscreen', 'exitfullscreen', 'fullscreenelement', 'fullscreenenabled', 'fullscreenchange', 'fullscreenerror' ], // new webkit [ 'webkitrequestfullscreen', 'webkitexitfullscreen', 'webkitfullscreenelement', 'webkitfullscreenenabled', 'webkitfullscreenchange', 'webkitfullscreenerror' ], // old webkit (safari 5.1) [ 'webkitrequestfullscreen', 'webkitcancelfullscreen', 'webkitcurrentfullscreenelement', 'webkitcancelfullscreen', 'webkitfullscreenchange', 'webkitfullscreenerror' ], [ 'mozrequestfullscreen', 'mozcancelfullscreen', 'mozfullscreenelement', 'mozfullscreenenabled', 'mozfullscreenchange', 'mozfullscreenerror' ], [ 'msrequestfullscreen', 'msexitfullscreen', 'msfullscreenelement', 'msfullscreenenabled', 'msfullscreenchange', 'msfullscreenerror' ] ]; var val; var ret = {}; var i, j; for ( i = 0; i < fnmap.length; i++ ) { val = fnmap[ i ]; if ( val && val[ 1 ] in document ) { for ( j = 0; j < val.length; j++ ) { ret[ fnmap[ 0 ][ j ] ] = val[ j ]; } return ret; } } return false; })(); // if browser does not have full screen api, then simply unset default button template and stop if ( !fn ) { if ( $ && $.fancybox ) { $.fancybox.defaults.btntpl.fullscreen = false; } return; } var fullscreen = { request : function ( elem ) { elem = elem || document.documentelement; elem[ fn.requestfullscreen ]( elem.allow_keyboard_input ); }, exit : function () { document[ fn.exitfullscreen ](); }, toggle : function ( elem ) { elem = elem || document.documentelement; if ( this.isfullscreen() ) { this.exit(); } else { this.request( elem ); } }, isfullscreen : function() { return boolean( document[ fn.fullscreenelement ] ); }, enabled : function() { return boolean( document[ fn.fullscreenenabled ] ); } }; $.extend(true, $.fancybox.defaults, { btntpl : { fullscreen : '' }, fullscreen : { autostart : false } }); $(document).on({ 'oninit.fb' : function(e, instance) { var $container; if ( instance && instance.group[ instance.currindex ].opts.fullscreen ) { $container = instance.$refs.container; $container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) { e.stoppropagation(); e.preventdefault(); fullscreen.toggle( $container[ 0 ] ); }); if ( instance.opts.fullscreen && instance.opts.fullscreen.autostart === true ) { fullscreen.request( $container[ 0 ] ); } // expose api instance.fullscreen = fullscreen; } else if ( instance ) { instance.$refs.toolbar.find('[data-fancybox-fullscreen]').hide(); } }, 'afterkeydown.fb' : function(e, instance, current, keypress, keycode) { // "p" or spacebar if ( instance && instance.fullscreen && keycode === 70 ) { keypress.preventdefault(); instance.fullscreen.toggle( instance.$refs.container[ 0 ] ); } }, 'beforeclose.fb' : function( instance ) { if ( instance && instance.fullscreen ) { fullscreen.exit(); } } }); $(document).on(fn.fullscreenchange, function() { var isfullscreen = fullscreen.isfullscreen(), instance = $.fancybox.getinstance(); if ( instance ) { // if image is zooming, then force to stop and reposition properly if ( instance.current && instance.current.type === 'image' && instance.isanimating ) { instance.current.$content.css( 'transition', 'none' ); instance.isanimating = false; instance.update( true, true, 0 ); } instance.trigger( 'onfullscreenchange', isfullscreen ); instance.$refs.container.toggleclass( 'fancybox-is-fullscreen', isfullscreen ); } }); }( document, window.jquery || jquery )); // ========================================================================== // // thumbs // displays thumbnails in a grid // // ========================================================================== ;(function (document, $) { 'use strict'; // make sure there are default values $.fancybox.defaults = $.extend(true, { btntpl : { thumbs : '' }, thumbs : { autostart : false, // display thumbnails on opening hideonclose : true, // hide thumbnail grid when closing animation starts parentel : '.fancybox-container', // container is injected into this element axis : 'y' // vertical (y) or horizontal (x) scrolling } }, $.fancybox.defaults); var fancythumbs = function( instance ) { this.init( instance ); }; $.extend( fancythumbs.prototype, { $button : null, $grid : null, $list : null, isvisible : false, isactive : false, init : function( instance ) { var self = this; self.instance = instance; instance.thumbs = self; // enable thumbs if at least two group items have thumbnails var first = instance.group[0], second = instance.group[1]; self.opts = instance.group[ instance.currindex ].opts.thumbs; self.$button = instance.$refs.toolbar.find( '[data-fancybox-thumbs]' ); if ( self.opts && first && second && ( ( first.type == 'image' || first.opts.thumb || first.opts.$thumb ) && ( second.type == 'image' || second.opts.thumb || second.opts.$thumb ) )) { self.$button.show().on('click', function() { self.toggle(); }); self.isactive = true; } else { self.$button.hide(); } }, create : function() { var self = this, instance = self.instance, parentel = self.opts.parentel, list, src; self.$grid = $('
').appendto( instance.$refs.container.find( parentel ).addback().filter( parentel ) ); // build list html list = '
    '; $.each(instance.group, function( i, item ) { src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr( 'src' ) : null ); if ( !src && item.type === 'image' ) { src = item.src; } if ( src && src.length ) { list += '
  • '; } }); list += '
'; self.$list = $( list ).appendto( self.$grid ).on('click', 'li', function() { instance.jumpto( $(this).data('index') ); }); self.$list.find( 'img' ).hide().one('load', function() { var $parent = $(this).parent().removeclass( 'fancybox-thumbs-loading' ), thumbwidth = $parent.outerwidth(), thumbheight = $parent.outerheight(), width, height, widthratio, heightratio; width = this.naturalwidth || this.width; height = this.naturalheight || this.height; // calculate thumbnail dimensions; center vertically and horizontally widthratio = width / thumbwidth; heightratio = height / thumbheight; if (widthratio >= 1 && heightratio >= 1) { if (widthratio > heightratio) { width = width / heightratio; height = thumbheight; } else { width = thumbwidth; height = height / widthratio; } } $(this).css({ width : math.floor(width), height : math.floor(height), 'margin-top' : height > thumbheight ? ( math.floor(thumbheight * 0.3 - height * 0.3 ) ) : math.floor(thumbheight * 0.5 - height * 0.5 ), 'margin-left' : math.floor(thumbwidth * 0.5 - width * 0.5 ) }).show(); }) .each(function() { this.src = $( this ).data( 'src' ); }); if ( self.opts.axis === 'x' ) { self.$list.width( parseint( self.$grid.css("padding-right") ) + ( instance.group.length * self.$list.children().eq(0).outerwidth(true) ) + 'px' ); } }, focus : function( duration ) { var self = this, $list = self.$list, thumb, thumbpos; if ( self.instance.current ) { thumb = $list.children() .removeclass( 'fancybox-thumbs-active' ) .filter('[data-index="' + self.instance.current.index + '"]') .addclass('fancybox-thumbs-active'); thumbpos = thumb.position(); // check if need to scroll to make current thumb visible if ( self.opts.axis === 'y' && ( thumbpos.top < 0 || thumbpos.top > ( $list.height() - thumb.outerheight() ) ) ) { $list.stop().animate({ 'scrolltop' : $list.scrolltop() + thumbpos.top }, duration); } else if ( self.opts.axis === 'x' && ( thumbpos.left < $list.parent().scrollleft() || thumbpos.left > ( $list.parent().scrollleft() + ( $list.parent().width() - thumb.outerwidth() ) ) ) ) { $list.parent().stop().animate({ 'scrollleft' : thumbpos.left }, duration); } } }, update : function() { this.instance.$refs.container.toggleclass( 'fancybox-show-thumbs', this.isvisible ); if ( this.isvisible ) { if ( !this.$grid ) { this.create(); } this.instance.trigger( 'onthumbsshow' ); this.focus( 0 ); } else if ( this.$grid ) { this.instance.trigger( 'onthumbshide' ); } // update content position this.instance.update(); }, hide : function() { this.isvisible = false; this.update(); }, show : function() { this.isvisible = true; this.update(); }, toggle : function() { this.isvisible = !this.isvisible; this.update(); } }); $(document).on({ 'oninit.fb' : function(e, instance) { var thumbs; if ( instance && !instance.thumbs ) { thumbs = new fancythumbs( instance ); if ( thumbs.isactive && thumbs.opts.autostart === true ) { thumbs.show(); } } }, 'beforeshow.fb' : function(e, instance, item, firstrun) { var thumbs = instance && instance.thumbs; if ( thumbs && thumbs.isvisible ) { thumbs.focus( firstrun ? 0 : 250 ); } }, 'afterkeydown.fb' : function(e, instance, current, keypress, keycode) { var thumbs = instance && instance.thumbs; // "g" if ( thumbs && thumbs.isactive && keycode === 71 ) { keypress.preventdefault(); thumbs.toggle(); } }, 'beforeclose.fb' : function( e, instance ) { var thumbs = instance && instance.thumbs; if ( thumbs && thumbs.isvisible && thumbs.opts.hideonclose !== false ) { thumbs.$grid.hide(); } } }); }(document, window.jquery)); //// ========================================================================== // // share // displays simple form for sharing current url // // ========================================================================== ;(function (document, $) { 'use strict'; $.extend(true, $.fancybox.defaults, { btntpl : { share : '' }, share : { tpl : '
' + '

{{share}}

' + '' + '

' + '
' } }); function escapehtml(string) { var entitymap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' }; return string(string).replace(/[&<>"'`=\/]/g, function (s) { return entitymap[s]; }); } $(document).on('click', '[data-fancybox-share]', function() { var f = $.fancybox.getinstance(), url, tpl; if ( f ) { url = f.current.opts.hash === false ? f.current.src : window.location; tpl = f.current.opts.share.tpl .replace( /\{\{media\}\}/g, f.current.type === 'image' ? encodeuricomponent( f.current.src ) : '' ) .replace( /\{\{url\}\}/g, encodeuricomponent( url ) ) .replace( /\{\{url_raw\}\}/g, escapehtml( url ) ) .replace( /\{\{descr\}\}/g, f.$caption ? encodeuricomponent( f.$caption.text() ) : '' ); $.fancybox.open({ src : f.translate( f, tpl ), type : 'html', opts : { animationeffect : "fade", animationduration : 250, afterload : function(instance, current) { // opening links in a popup window current.$content.find('.fancybox-share__links a').click(function() { window.open(this.href, "share", "width=550, height=450"); return false; }); } } }); } }); }( document, window.jquery || jquery )); // ========================================================================== // // hash // enables linking to each modal // // ========================================================================== ;(function (document, window, $) { 'use strict'; // simple $.escapeselector polyfill (for jquery prior v3) if ( !$.escapeselector ) { $.escapeselector = function( sel ) { var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uffff\w-]/g; var fcssescape = function( ch, ascodepoint ) { if ( ascodepoint ) { // u+0000 null becomes u+fffd replacement character if ( ch === "\0" ) { return "\ufffd"; } // control characters and (dependent upon position) numbers get escaped as code points return ch.slice( 0, -1 ) + "\\" + ch.charcodeat( ch.length - 1 ).tostring( 16 ) + " "; } // other potentially-special ascii characters get backslash-escaped return "\\" + ch; }; return ( sel + "" ).replace( rcssescape, fcssescape ); }; } // create new history entry only once var shouldcreatehistory = true; // variable containing last hash value set by fancybox // it will be used to determine if fancybox needs to close after hash change is detected var currenthash = null; // throttling the history change var timerid = null; // get info about gallery name and current index from url function parseurl() { var hash = window.location.hash.substr( 1 ); var rez = hash.split( '-' ); var index = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseint( rez.pop( -1 ), 10 ) || 1 : 1; var gallery = rez.join( '-' ); // index is starting from 1 if ( index < 1 ) { index = 1; } return { hash : hash, index : index, gallery : gallery }; } // trigger click evnt on links to open new fancybox instance function triggerfromurl( url ) { var $el; if ( url.gallery !== '' ) { // if we can find element matching 'data-fancybox' atribute, then trigger click event for that .. $el = $( "[data-fancybox='" + $.escapeselector( url.gallery ) + "']" ).eq( url.index - 1 ); if ( !$el.length ) { // .. if not, try finding element by id $el = $( "#" + $.escapeselector( url.gallery ) + "" ); } if ( $el.length ) { shouldcreatehistory = false; $el.trigger( 'click' ); } } } // get gallery name from current instance function getgalleryid( instance ) { var opts; if ( !instance ) { return false; } opts = instance.current ? instance.current.opts : instance.opts; return opts.hash || ( opts.$orig ? opts.$orig.data( 'fancybox' ) : '' ); } // start when dom becomes ready $(function() { // check if user has disabled this module if ( $.fancybox.defaults.hash === false ) { return; } // update hash when opening/closing fancybox $(document).on({ 'oninit.fb' : function( e, instance ) { var url, gallery; if ( instance.group[ instance.currindex ].opts.hash === false ) { return; } url = parseurl(); gallery = getgalleryid( instance ); // make sure gallery start index matches index from hash if ( gallery && url.gallery && gallery == url.gallery ) { instance.currindex = url.index - 1; } }, 'beforeshow.fb' : function( e, instance, current ) { var gallery; if ( !current || current.opts.hash === false ) { return; } gallery = getgalleryid( instance ); // update window hash if ( gallery && gallery !== '' ) { if ( window.location.hash.indexof( gallery ) < 0 ) { instance.opts.orighash = window.location.hash; } currenthash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' ); if ( 'replacestate' in window.history ) { if ( timerid ) { cleartimeout( timerid ); } timerid = settimeout(function() { window.history[ shouldcreatehistory ? 'pushstate' : 'replacestate' ]( {} , document.title, window.location.pathname + window.location.search + '#' + currenthash ); timerid = null; shouldcreatehistory = false; }, 300); } else { window.location.hash = currenthash; } } }, 'beforeclose.fb' : function( e, instance, current ) { var gallery, orighash; if ( timerid ) { cleartimeout( timerid ); } if ( current.opts.hash === false ) { return; } gallery = getgalleryid( instance ); orighash = instance && instance.opts.orighash ? instance.opts.orighash : ''; // remove hash from location bar if ( gallery && gallery !== '' ) { if ( 'replacestate' in history ) { window.history.replacestate( {} , document.title, window.location.pathname + window.location.search + orighash ); } else { window.location.hash = orighash; // keep original scroll position $( window ).scrolltop( instance.scrolltop ).scrollleft( instance.scrollleft ); } } currenthash = null; } }); // check if need to close after url has changed $(window).on('hashchange.fb', function() { var url = parseurl(); if ( $.fancybox.getinstance() ) { if ( currenthash && currenthash !== url.gallery + '-' + url.index && !( url.index === 1 && currenthash == url.gallery ) ) { currenthash = null; $.fancybox.close(); } } else if ( url.gallery !== '' ) { triggerfromurl( url ); } }); // check current hash and trigger click event on matching element to start fancybox, if needed settimeout(function() { triggerfromurl( parseurl() ); }, 50); }); }( document, window, window.jquery || jquery )); ;(function (document, $) { 'use strict'; var prevtime = new date().gettime(); $(document).on({ 'oninit.fb' : function( e, instance, current ) { instance.$refs.stage.on('mousewheel dommousescroll wheel mozmousepixelscroll', function(e) { var current = instance.current, currtime = new date().gettime(); if ( instance.group.length < 1 || current.opts.wheel === false || ( current.opts.wheel === 'auto' && current.type !== 'image' ) ) { return; } e.preventdefault(); e.stoppropagation(); if ( current.$slide.hasclass( 'fancybox-animated' ) ) { return; } e = e.originalevent || e; if ( currtime - prevtime < 250 ) { return; } prevtime = currtime; instance[ ( -e.deltay || -e.deltax || e.wheeldelta || -e.detail ) < 0 ? 'next' : 'previous' ](); }); } }); }( document, window.jquery || jquery ));