// ================================================== // fancybox v3.3.5 // // licensed gplv3 for open source use // or fancybox commercial license for commercial use // // http://fancyapps.com/fancybox/ // copyright 2018 fancyapps // // ================================================== (function(window, document, $, undefined) { "use strict"; window.console = window.console || { info: function(stuff) {} }; // if there's no jquery, fancybox can't work // ========================================= if (!$) { return; } // check if fancybox is already initialized // ======================================== if ($.fn.fancybox) { console.info("fancybox already initialized"); return; } // private default settings // ======================== var defaults = { // enable infinite gallery navigation loop: false, // horizontal space between slides gutter: 50, // enable keyboard navigation keyboard: true, // should display navigation arrows at the screen edges arrows: true, // should display counter at the top left corner infobar: true, // should display close button (using `btntpl.smallbtn` template) over the content // can be true, false, "auto" // if "auto" - will be automatically enabled for "html", "inline" or "ajax" items smallbtn: "auto", // should display toolbar (buttons at the top) // can be true, false, "auto" // if "auto" - will be automatically hidden if "smallbtn" is enabled toolbar: "auto", // 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: [ "zoom", //"share", //"slideshow", //"fullscreen", //"download", "thumbs", "close" ], // detect "idle" time in seconds idletime: 3, // 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 // true - wait for image to load and then display; // false - display thumbnail and load the full-sized image over top, // requires predefined image dimensions (`data-width` and `data-height` attributes) preload: false }, 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: 366, // 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; // if you have not changed the layout, then most likely you need to use `clickslide` option 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, 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鐪塩k", error: "die angeforderten daten konnten nicht geladen werden.
bitte versuchen sie es sp鐩瞭er nochmal.", play_start: "diaschau starten", play_stop: "diaschau beenden", full_screen: "vollbild", thumbs: "vorschaubilder", download: "herunterladen", share: "teilen", zoom: "ma鑴絪tab" } } }; // 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 el = document.createelement("fakeelement"), t; 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; }; // exclude array (`buttons`) options from deep merging // =================================================== var mergeopts = function(opts1, opts2) { var rez = $.extend(true, {}, opts1, opts2); $.each(opts2, function(key, value) { if ($.isarray(value)) { rez[key] = value; } }); return rez; }; // class definition // ================ var fancybox = function(content, opts, index) { var self = this; self.opts = mergeopts({index: index}, $.fancybox.defaults); if ($.isplainobject(opts)) { self.opts = mergeopts(self.opts, opts); } if ($.fancybox.ismobile) { self.opts = mergeopts(self.opts, self.opts.mobile); } self.id = self.opts.id || ++called; self.currindex = parseint(self.opts.index, 10) || 0; self.previndex = null; self.prevpos = null; self.currpos = 0; self.firstrun = true; // all group items self.group = []; // existing slides (for current, next and previous gallery items) self.slides = {}; // create group elements self.addcontent(content); if (!self.group.length) { return; } // save last active element self.$lastfocus = $(document.activeelement).trigger("blur"); 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; // hide scrollbars // =============== if (!$.fancybox.getinstance() && firstitemopts.hidescrollbar !== false) { $("body").addclass("fancybox-active"); 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; }); }, // populate current group with fresh content // check if each object has valid type and content // =============================================== addcontent: function(content) { var self = this, items = $.makearray(content), thumbs; $.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); // support attributes like `data-options='{"touch" : false}'` and `data-touch='false'` opts = $item.data() || {}; opts = $.extend(true, {}, opts, opts.options); // here we store clicked element opts.$orig = $item; obj.src = self.opts.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 ((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.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 = "iframe"; } else if (src.charat(0) === "#") { type = "inline"; } } if (type) { obj.type = type; } else { self.trigger("objectneedstype", obj); } if (!obj.contenttype) { obj.contenttype = $.inarray(obj.type, ["html", "inline", "ajax"]) > -1 ? "html" : obj.type; } // step 3 - some adjustments // ========================= obj.index = self.group.length; if (obj.opts.smallbtn == "auto") { obj.opts.smallbtn = $.inarray(obj.type, ["html", "inline", "ajax"]) > -1; } if (obj.opts.toolbar === "auto") { obj.opts.toolbar = !obj.opts.smallbtn; } // find thumbnail image if (obj.opts.$trigger && obj.index === self.opts.index) { obj.opts.$thumb = obj.opts.$trigger.find("img:first"); } if ((!obj.opts.$thumb || !obj.opts.$thumb.length) && obj.opts.$orig) { obj.opts.$thumb = obj.opts.$orig.find("img:first"); } // "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 (obj.type === "ajax") { srcparts = src.split(/\s+/, 2); if (srcparts.length > 1) { obj.src = srcparts.shift(); obj.opts.filter = srcparts.shift(); } } // 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); }); // update controls if gallery is already opened if (object.keys(self.slides).length) { self.updatecontrols(); // update thumbnails, if needed thumbs = self.thumbs; if (thumbs && thumbs.isactive) { thumbs.create(); thumbs.focus(); } } }, // 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("touchstart.fb-prev click.fb-prev", "[data-fancybox-prev]", function(e) { e.stoppropagation(); e.preventdefault(); self.previous(); }) .on("touchstart.fb-next click.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(); }, $.fancybox.ismobile ? 600 : 250); } }); // 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(); } }); // 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.ctrlkey || e.altkey || e.shiftkey || $(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) { var self = this, grouplen = self.group.length, firstrun, loop, current, previous, canvaswidth, currentpos, transitionprops; if (self.isdragging || self.isclosing || (self.isanimating && self.firstrun)) { return; } pos = parseint(pos, 10); // should loop? loop = self.current ? self.current.opts.loop : self.opts.loop; if (!loop && (pos < 0 || pos >= grouplen)) { return false; } firstrun = self.firstrun = !object.keys(self.slides).length; 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"); // validate duration length 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"); forceredraw(self.$refs.container); // make current slide visible current.$slide.addclass("fancybox-slide--previous"); // attempt to load content into slide; // at this point image would start loading, but inline/html content would load immediately self.loadslide(current); current.$slide.removeclass("fancybox-slide--previous").addclass("fancybox-slide--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, $slide, 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; // x and y values should be relative to the slide // ============================================== scaletoactual: function(x, y, duration) { var self = this, current = self.current, $content = current.$content, canvaswidth = $.fancybox.gettranslate(current.$slide).width, canvasheight = $.fancybox.gettranslate(current.$slide).height, newimgwidth = current.width, newimgheight = current.height, imgpos, posx, posy, scalex, scaley; if (self.isanimating || !$content || !(current.type == "image" && current.isloaded && !current.haserror)) { return; } $.fancybox.stop($content); self.isanimating = true; x = x === undefined ? canvaswidth * 0.5 : x; y = y === undefined ? canvasheight * 0.5 : y; imgpos = $.fancybox.gettranslate($content); imgpos.top -= $.fancybox.gettranslate(current.$slide).top; imgpos.left -= $.fancybox.gettranslate(current.$slide).left; 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( $content, { 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, current = self.current, $content = current.$content, end; if (self.isanimating || !$content || !(current.type == "image" && current.isloaded && !current.haserror)) { return; } $.fancybox.stop($content); self.isanimating = true; end = self.getfitpos(current); self.updatecursor(end.width, end.height); $.fancybox.animate( $content, { top: end.top, left: end.left, scalex: end.width / $content.width(), scaley: end.height / $content.height() }, duration || 330, function() { self.isanimating = false; } ); }, // calculate image size to fit inside viewport // =========================================== getfitpos: function(slide) { var self = this, $content = slide.$content, width = slide.width || slide.opts.width, height = slide.height || slide.opts.height, maxwidth, maxheight, minratio, margin, aspectratio, rez = {}; if (!slide.isloaded || !$content || !$content.length) { return false; } margin = { top: parseint(slide.$slide.css("paddingtop"), 10), right: parseint(slide.$slide.css("paddingright"), 10), bottom: parseint(slide.$slide.css("paddingbottom"), 10), left: parseint(slide.$slide.css("paddingleft"), 10) }; // we can not use $slide width here, because it can have different diemensions while in transiton maxwidth = parseint(self.$refs.stage.width(), 10) - (margin.left + margin.right); maxheight = parseint(self.$refs.stage.height(), 10) - (margin.top + margin.bottom); if (!width || !height) { width = maxwidth; height = maxheight; } minratio = math.min(1, maxwidth / width, maxheight / height); // use floor rounding to make sure it really fits width = math.floor(minratio * width); height = math.floor(minratio * height); if (slide.type === "image") { rez.top = math.floor((maxheight - height) * 0.5) + margin.top; rez.left = math.floor((maxwidth - width) * 0.5) + margin.left; } else if (slide.contenttype === "video") { // force aspect ratio for the video // "i say the whole world must learn of our peaceful ways閳ワ拷 by force!" aspectratio = slide.opts.width && slide.opts.height ? width / height : slide.opts.ratio || 16 / 9; if (height > width / aspectratio) { height = width / aspectratio; } else if (width > height * aspectratio) { width = height * aspectratio; } } rez.width = width; rez.height = height; return rez; }, // 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, $content = slide && slide.$content, width = slide.width || slide.opts.width, height = slide.height || slide.opts.height; if ($content && (width || height || slide.contenttype === "video") && !slide.haserror) { $.fancybox.stop($content); $.fancybox.settranslate($content, self.getfitpos(slide)); if (slide.pos === self.currpos) { self.isanimating = false; self.updatecursor(); } } slide.$slide.trigger("refresh"); self.$refs.toolbar.toggleclass("compensate-for-scrollbar", slide.$slide.get(0).scrollheight > slide.$slide.get(0).clientheight); 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, current = self.current, $container = self.$refs.container.removeclass("fancybox-is-zoomable fancybox-can-zoomin fancybox-can-drag fancybox-can-zoomout"), iszoomable; if (!current || self.isclosing) { return; } iszoomable = self.iszoomable(); $container.toggleclass("fancybox-is-zoomable", iszoomable); $("[data-fancybox-zoom]").prop("disabled", !iszoomable); // set cursor to zoom in/out if click event is 'zoom' if ( iszoomable && (current.opts.clickcontent === "zoom" || ($.isfunction(current.opts.clickcontent) && current.opts.clickcontent(current) === "zoom")) ) { if (self.isscaleddown(nextwidth, nextheight)) { // if image is scaled down, then, obviously, it can be zoomed to full size $container.addclass("fancybox-can-zoomin"); } else { if (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 (current.opts.touch && current.contenttype !== "video") { $container.addclass("fancybox-can-drag"); } }, // check if current slide is zoomable // ================================== iszoomable: function() { var self = this, current = self.current, fitpos; // assume that slide is zoomable if: // - image is still loading // - actual size of the image is smaller than available area if (current && !self.isclosing && current.type === "image" && !current.haserror) { if (!current.isloaded) { return true; } 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(nextwidth, nextheight) { var self = this, rez = false, current = self.current, $content = current.$content; if (nextwidth !== undefined && nextheight !== undefined) { rez = nextwidth < current.width && nextheight < current.height; } else if ($content) { rez = $.fancybox.gettranslate($content); rez = rez.width < current.width && rez.height < current.height; } return rez; }, // check if image dimensions exceed parent element // =============================================== canpan: function() { var self = this, rez = false, current = self.current, $content; if (current.type === "image" && ($content = current.$content) && !current.haserror) { rez = self.getfitpos(current); rez = math.abs($content.width() - rez.width) > 1 || math.abs($content.height() - rez.height) > 1; } return rez; }, // load content into the slide // =========================== loadslide: function(slide) { var self = this, type, $slide, ajaxload; if (slide.isloading || slide.isloaded) { return; } slide.isloading = true; self.trigger("beforeload", slide); type = slide.type; $slide = slide.$slide; $slide .off("refresh") .trigger("onreset") .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 "video": self.setcontent( slide, '