// ==================================================
// fancybox v3.4.1
//
// 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 = {
// close existing modals
// set this to false if you do not need to stack multiple instances
closeexisting: false,
// 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"
}
},
// for html5 video only
video: {
tpl:
'",
format: "", // custom video format
autostart: true
},
// 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:
'
',
btntpl: {
download:
'' +
'' +
"",
zoom:
'",
close:
'",
// arrows
arrowleft:
'",
arrowright:
'",
// this small close button will be appended to your html/inline/ajax content by default,
// if "smallbtn" option is not set to false
smallbtn:
'"
},
// container is injected into this element
parentel: "body",
// hide browser vertical scrollbars; use at your own risk
hidescrollbar: true,
// focus handling
// ==============
// try to focus on the first focusable element after opening
autofocus: true,
// 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 panning/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: 3000
},
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ü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 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;
}
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;
if (firstitemopts.closeexisting) {
$.fancybox.close(true);
}
// hide scrollbars
// ===============
$("body").addclass("fancybox-active");
if (
!$.fancybox.getinstance() &&
firstitemopts.hidescrollbar !== false &&
!$.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(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;
}
if ($.fancybox.ismobile && obj.opts.mobile) {
obj.opts = mergeopts(obj.opts, obj.opts.mobile);
}
// 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|webm)((\?|#).*)?$/i))) {
type = "video";
if (!obj.opts.video.format) {
obj.opts.video.format = "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.length) {
obj.opts.$orig = obj.opts.$trigger;
}
}
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
// - detecting inactivity
// ======================================
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 {
if (self.current && self.current.type === "iframe") {
self.$refs.stage.hide();
}
settimeout(function() {
self.$refs.stage.show();
self.update();
}, $.fancybox.ismobile ? 600 : 250);
}
});
$d.on("keydown.fb", function(e) {
var instance = $.fancybox ? $.fancybox.getinstance() : null,
current = instance.current,
keycode = e.keycode || e.which;
// trap keyboard focus inside of the modal
// =======================================
if (keycode == 9) {
if (current.opts.trapfocus) {
self.focus(e);
}
return;
}
// enable keyboard navigation
// ==========================
if (!current.opts.keyboard || 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("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,
ismoved,
loop,
current,
previous,
canvaswidth,
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 < grouplen - 1) {
self.createslide(pos + 1);
}
if (loop || current.index > 0) {
self.createslide(pos - 1);
}
}
self.current = current;
self.currindex = current.index;
self.currpos = current.pos;
self.trigger("beforeshow", firstrun);
self.updatecontrols();
ismoved = self.ismoved(current);
// 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.addclass("fancybox-is-open");
// 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");
self.$refs.container.trigger("focus");
return;
}
// clean up all slides
$.each(self.slides, function(index, slide) {
// make sure that animation callback gets fired
$.fancybox.stop(slide.$slide, true);
slide.$slide.removeclass("fancybox-animated").removeclass(function(index, classname) {
return (classname.match(/(^|\s)fancybox-fx-\s+/g) || []).join(" ");
});
});
// make current slide 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 (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) {
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 previously active 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 || (!ismoved && !current.opts.transitioneffect)) {
return;
}
if (ismoved) {
previous.$slide.addclass(transitionprops);
} else {
transitionprops = "fancybox-animated " + transitionprops + " fancybox-fx-" + current.opts.transitioneffect;
$.fancybox.animate(previous.$slide, transitionprops, duration, null, false);
}
},
// 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,
$slide = slide.$slide,
width = slide.width || slide.opts.width,
height = slide.height || slide.opts.height,
maxwidth,
maxheight,
minratio,
aspectratio,
rez = {};
if (!slide.isloaded || !$content || !$content.length) {
return false;
}
maxwidth = $.fancybox.gettranslate(self.$refs.stage).width;
maxheight = $.fancybox.gettranslate(self.$refs.stage).height;
maxwidth -=
parsefloat($slide.css("paddingleft")) +
parsefloat($slide.css("paddingright")) +
parsefloat($content.css("marginleft")) +
parsefloat($content.css("marginright"));
maxheight -=
parsefloat($slide.css("paddingtop")) +
parsefloat($slide.css("paddingbottom")) +
parsefloat($content.css("margintop")) +
parsefloat($content.css("marginbottom"));
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) + parsefloat($slide.css("paddingtop"));
rez.left = math.floor((maxwidth - width) * 0.5) + parsefloat($slide.css("paddingleft"));
} 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) {
var self = this,
$content = slide && slide.$content,
width = slide.width || slide.opts.width,
height = slide.height || slide.opts.height,
$slide = slide.$slide;
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();
}
}
if ($slide.length) {
$slide.trigger("refresh");
self.$refs.toolbar.toggleclass("compensate-for-scrollbar", $slide.get(0).scrollheight > $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
);
}
},
// check if current slide is moved (swiped)
// ========================================
ismoved: function(slide) {
var current = slide || this.current,
currentpos = $.fancybox.gettranslate(current.$slide);
return (currentpos.left !== 0 || currentpos.top !== 0) && !current.$slide.hasclass("fancybox-animated");
},
// 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-zoomout fancybox-can-swipe fancybox-can-pan"
),
iszoomable;
if (!current || self.isclosing) {
return;
}
iszoomable = self.iszoomable();
$container.toggleclass("fancybox-is-zoomable", iszoomable);
$("[data-fancybox-zoom]").prop("disabled", !iszoomable);
if (self.canpan(nextwidth, nextheight)) {
$container.addclass("fancybox-can-pan");
} else if (
iszoomable &&
(current.opts.clickcontent === "zoom" || ($.isfunction(current.opts.clickcontent) && current.opts.clickcontent(current) == "zoom"))
) {
$container.addclass("fancybox-can-zoomin");
} else if (current.opts.touch && (current.opts.touch.vertical || self.group.length > 1) && current.contenttype !== "video") {
$container.addclass("fancybox-can-swipe");
}
},
// 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(nextwidth, nextheight) {
var self = this,
rez = false,
current = self.current,
$content,
pos;
if (current.type === "image" && ($content = current.$content) && !current.haserror) {
rez = self.getfitpos(current);
if (nextwidth !== undefined && nextheight !== undefined) {
pos = {width: nextwidth, height: nextheight};
} else {
pos = $.fancybox.gettranslate($content);
}
rez = math.abs(pos.width - rez.width) > 1.5 || math.abs(pos.height - rez.height) > 1.5;
}
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,
slide.opts.video.tpl.replace("{{src}}", slide.src).replace("{{format}}", slide.opts.videoformat || slide.opts.video.format)
);
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;
default:
self.seterror(slide);
break;
}
return true;
},
// use thumbnail image, if possible
// ================================
setimage: function(slide) {
var self = this,
srcset = slide.opts.srcset || slide.opts.image.srcset,
thumbsrc,
found,
temp,
pxratio,
windowwidth;
// check if need to show loading icon
slide.timouts = settimeout(function() {
var $img = slide.$image;
if (slide.isloading && (!$img || !$img.length || !$img[0].complete) && !slide.haserror) {
self.showloading(slide);
}
}, 350);
// if we have "srcset", then we need to find first 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;
}
slide.opts.srcset = srcset;
}
}
// this will be wrapper containing both ghost and actual image
slide.$content = $('')
.addclass("fancybox-is-hidden")
.appendto(slide.$slide.addclass("fancybox-slide--image"));
// 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
thumbsrc = slide.opts.thumb || (slide.opts.$thumb && slide.opts.$thumb.length ? slide.opts.$thumb.attr("src") : false);
if (slide.opts.preload !== false && slide.opts.width && slide.opts.height && thumbsrc) {
slide.width = slide.opts.width;
slide.height = slide.opts.height;
slide.$ghost = $("")
.one("error", function() {
$(this).remove();
slide.$ghost = null;
})
.one("load", function() {
self.afterload(slide);
})
.addclass("fancybox-image")
.appendto(slide.$content)
.attr("src", thumbsrc);
}
// start loading actual image
self.setbigimage(slide);
},
// create full-size image
// ======================
setbigimage: function(slide) {
var self = this,
$img = $("");
slide.$image = $img
.one("error", function() {
self.seterror(slide);
})
.one("load", function() {
var sizes;
if (!slide.$ghost) {
self.resolveimageslidesize(slide, this.naturalwidth, this.naturalheight);
self.afterload(slide);
}
// clear timeout that checks if loading icon needs to be displayed
if (slide.timouts) {
cleartimeout(slide.timouts);
slide.timouts = null;
}
if (self.isclosing) {
return;
}
if (slide.opts.srcset) {
sizes = slide.opts.sizes;
if (!sizes || sizes === "auto") {
sizes =
(slide.width / slide.height > 1 && $w.width() / $w.height() > 1 ? "100" : math.round((slide.width / slide.height) * 100)) +
"vw";
}
$img.attr("sizes", sizes).attr("srcset", slide.opts.srcset);
}
// hide temporary image after some delay
if (slide.$ghost) {
settimeout(function() {
if (slide.$ghost && !self.isclosing) {
slide.$ghost.hide();
}
}, math.min(300, math.max(1000, slide.height / 1600)));
}
self.hideloading(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");
}
},
// computes the slide size from image size and maxwidth/maxheight
// ==============================================================
resolveimageslidesize: function(slide, imgwidth, imgheight) {
var maxwidth = parseint(slide.opts.width, 10),
maxheight = parseint(slide.opts.height, 10);
// sets the default values from the image
slide.width = imgwidth;
slide.height = imgheight;
if (maxwidth > 0) {
slide.width = maxwidth;
slide.height = math.floor((maxwidth * imgheight) / imgwidth);
}
if (maxheight > 0) {
slide.width = math.floor((maxheight * imgwidth) / imgheight);
slide.height = maxheight;
}
},
// 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);
$slide.addclass("fancybox-slide--" + slide.contenttype);
slide.$iframe = $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 $content = slide.$content,
framewidth = opts.css.width,
frameheight = opts.css.height,
$contents,
$body;
if ($iframe[0].isready !== 1) {
return;
}
try {
$contents = $iframe.contents();
$body = $contents.find("body");
} catch (ignore) {}
// calculate contnet dimensions if it is accessible
if ($body && $body.length && $body.children().length) {
// avoid scrolling to top (if multiple instances)
$slide.css("overflow", "visible");
$content.css({
width: "100%",
height: ""
});
if (framewidth === undefined) {
framewidth = math.ceil(math.max($body[0].clientwidth, $body.outerwidth(true)));
}
if (framewidth) {
$content.width(framewidth);
}
if (frameheight === undefined) {
frameheight = math.ceil(math.max($body[0].clientheight, $body.outerheight(true)));
}
if (frameheight) {
$content.height(frameheight);
}
$slide.css("overflow", "auto");
}
$content.removeclass("fancybox-is-hidden");
});
} else {
this.afterload(slide);
}
$iframe.attr("src", slide.src);
// 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()
.unbind()
.attr("src", "//about:blank");
} catch (ignore) {}
$(this)
.off("refresh.fb")
.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);
if (slide.$content) {
$.fancybox.stop(slide.$content);
}
slide.$slide.empty();
// 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 (isquery(content) && content.parent().length) {
// make sure content is not already moved to fancybox
if (content.hasclass("fancybox-content")) {
content.parent(".fancybox-slide--html").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 "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.removeclass("fancybox-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.isrevealed = false;
}
});
$(content).appendto(slide.$slide);
if ($(content).is("video,audio")) {
$(content).addclass("fancybox-video");
$(content).wrap("");
slide.contenttype = "video";
slide.opts.width = slide.opts.width || $(content).attr("width");
slide.opts.height = slide.opts.height || $(content).attr("height");
}
slide.$content = slide.$slide
.children()
.filter("div,form,main,video,audio,article,.fancybox-content")
.first();
slide.$content.siblings().hide();
// re-check if there is a valid content
// (in some cases, ajax response can contain various elements or plain text)
if (!slide.$content.length) {
slide.$content = slide.$slide
.wrapinner("")
.children()
.first();
}
slide.$content.addclass("fancybox-content");
slide.$slide.addclass("fancybox-slide--" + slide.contenttype);
this.afterload(slide);
},
// display error message
// =====================
seterror: function(slide) {
slide.haserror = true;
slide.$slide
.trigger("onreset")
.removeclass("fancybox-slide--" + slide.contenttype)
.addclass("fancybox-slide--error");
slide.contenttype = "html";
this.setcontent(slide, this.translate(slide, slide.opts.errortpl));
if (slide.pos === this.currpos) {
this.isanimating = false;
}
},
// show loading icon inside the slide
// ==================================
showloading: function(slide) {
var self = this;
slide = slide || self.current;
if (slide && !slide.$spinner) {
slide.$spinner = $(self.translate(self, 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.pos === self.currpos) {
self.updatecursor();
}
if (slide.opts.smallbtn && (!slide.$smallbtn || !slide.$smallbtn.length)) {
slide.$smallbtn = $(self.translate(slide, slide.opts.btntpl.smallbtn)).appendto(slide.$content);
}
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,
$slide = slide.$slide,
end = false,
start = false,
ismoved = self.ismoved(slide),
isrevealed = slide.isrevealed,
effect,
effectclassname,
duration,
opacity;
if (ismoved && isrevealed) {
return;
}
slide.isrevealed = true;
effect = slide.opts[self.firstrun ? "animationeffect" : "transitioneffect"];
duration = slide.opts[self.firstrun ? "animationduration" : "transitionduration"];
duration = parseint(slide.forcedduration === undefined ? duration : slide.forcedduration, 10);
// do not animate if revealing the same slide
if (slide.pos === self.currpos) {
if (slide.iscomplete) {
effect = false;
} else {
self.isanimating = true;
}
}
if (ismoved || slide.pos !== self.currpos || !duration) {
effect = false;
}
// check if can zoom
if (effect === "zoom") {
if (slide.pos === self.currpos && duration && slide.type === "image" && !slide.haserror && (start = self.getthumbpos(slide))) {
end = self.getfitpos(slide);
} else {
effect = "fade";
}
}
// zoom animation
// ==============
if (effect === "zoom") {
end.scalex = end.width / start.width;
end.scaley = end.height / start.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.isanimating = false;
self.complete();
});
return;
}
self.updateslide(slide);
// simply show content if no effect
// ================================
if (!effect) {
forceredraw($slide);
if (!isrevealed) {
slide.$content
.removeclass("fancybox-is-hidden")
.hide()
.fadein("fast");
}
if (slide.pos === self.currpos) {
self.complete();
}
return;
}
// prepare for css transiton
// =========================
$.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
forceredraw($slide);
$.fancybox.animate(
$slide,
"fancybox-slide--current",
duration,
function() {
$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,
rez = false,
$thumb = slide.opts.$thumb,
thumbpos = $thumb && $thumb.length && $thumb[0].ownerdocument === document ? $thumb.offset() : 0,
slidepos;
// check if element is inside the viewport by at least 1 pixel
var iselementvisible = function($el) {
var element = $el[0],
elementrect = element.getboundingclientrect(),
parentrects = [],
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()
);
};
if (thumbpos && 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 = {},
$el;
if (self.ismoved() || !current.isloaded) {
return;
}
if (!current.iscomplete) {
current.iscomplete = true;
current.$slide.siblings().trigger("onreset");
self.preload("inline");
// trigger any css 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.isanimating = false;
self.updatecursor();
self.trigger("aftershow");
// autoplay first html5 video/audio
if (!!current.opts.video.autostart) {
current.$slide
.find("video,audio")
.filter(":visible:first")
.trigger("play");
}
// try to focus on the first focusable element
if (current.opts.autofocus && current.contenttype === "html") {
// look for the first input with autofocus attribute
$el = current.$content.find("input[autofocus]:enabled:visible:first");
if ($el.length) {
$el.trigger("focus");
} else {
self.focus(null, true);
}
}
// avoid jumping
current.$slide.scrolltop(0).scrollleft(0);
},
// preload next and previous slides
// ================================
preload: function(type) {
var self = this,
next = self.slides[self.currpos + 1],
prev = self.slides[self.currpos - 1];
if (prev && prev.type === type) {
self.loadslide(prev);
}
if (next && next.type === type) {
self.loadslide(next);
}
},
// try to find and focus on the first focusable element
// ====================================================
focus: function(e, firstrun) {
var self = this,
focusablestr = [
"a[href]",
"area[href]",
'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
"select:not([disabled]):not([aria-hidden])",
"textarea:not([disabled]):not([aria-hidden])",
"button:not([disabled]):not([aria-hidden])",
"iframe",
"object",
"embed",
"[contenteditable]",
'[tabindex]:not([tabindex^="-"])'
].join(","),
focusableitems,
focuseditemindex;
if (self.isclosing) {
return;
}
if (e || !self.current || !self.current.iscomplete) {
// focus on any element inside fancybox
focusableitems = self.$refs.container.find("*:visible");
} else {
// focus inside current slide
focusableitems = self.current.$slide.find("*:visible" + (firstrun ? ":not(.fancybox-close-small)" : ""));
}
focusableitems = focusableitems.filter(focusablestr).filter(function() {
return $(this).css("visibility") !== "hidden" && !$(this).hasclass("disabled");
});
if (focusableitems.length) {
focuseditemindex = focusableitems.index(document.activeelement);
if (e && e.shiftkey) {
// back tab
if (focuseditemindex < 0 || focuseditemindex == 0) {
e.preventdefault();
focusableitems.eq(focusableitems.length - 1).trigger("focus");
}
} else {
// outside or forward tab
if (focuseditemindex < 0 || focuseditemindex == focusableitems.length - 1) {
if (e) {
e.preventdefault();
}
focusableitems.eq(0).trigger("focus");
}
}
} else {
self.$refs.container.trigger("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,
current = self.current,
effect,
duration,
$content,
domrect,
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);
}
$content = 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 && $content && duration && current.type === "image" && !current.haserror && (end = self.getthumbpos(current)))
) {
effect = "fade";
}
if (effect === "zoom") {
$.fancybox.stop($content);
domrect = $.fancybox.gettranslate($content);
start = {
top: domrect.top,
left: domrect.left,
scalex: domrect.width / end.width,
scaley: domrect.height / end.height,
width: end.width,
height: end.height
};
// 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;
}
$.fancybox.settranslate($content, start);
forceredraw($content);
$.fancybox.animate($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,
instance,
$focus = self.current.opts.$orig,
x,
y;
self.current.$slide.trigger("onreset");
self.$refs.container.empty().remove();
self.trigger("afterclose", e);
// place back focus
if (!!self.current.opts.backfocus) {
if (!$focus || !$focus.length || !$focus.is(":visible")) {
$focus = self.$trigger;
}
if ($focus && $focus.length) {
x = window.scrollx;
y = window.scrolly;
$focus.trigger("focus");
$("html, body")
.scrolltop(y)
.scrollleft(x);
}
}
self.current = null;
// check if there are other instances
instance = $.fancybox.getinstance();
if (instance) {
instance.activate();
} else {
$("body").removeclass("fancybox-active compensate-for-scrollbar");
$("#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() {
var self = this,
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") {
// re-enable buttons; update download button source
$container
.find("[data-fancybox-zoom]")
.show()
.end()
.find("[data-fancybox-download]")
.attr("href", current.opts.image.src || current.src)
.show();
} else if (current.opts.toolbar) {
$container.find("[data-fancybox-download],[data-fancybox-zoom]").hide();
}
// make sure focus is not on disabled button/element
if ($(document.activeelement).is(":hidden,[disabled]")) {
self.$refs.container.trigger("focus");
}
},
// 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,
opts = self.current ? self.current.opts : self.opts,
$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-caption", !!self.$caption)
.toggleclass("fancybox-show-nav", !!(opts.arrows && self.group.length > 1))
.toggleclass("fancybox-is-modal", !!opts.modal);
},
// toggle toolbar and caption
// ==========================
togglecontrols: function() {
if (this.ishiddencontrols) {
this.showcontrols();
} else {
this.hidecontrols();
}
}
});
$.fancybox = {
version: "3.4.1",
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"),
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(all);
}
}
},
// close all instances and unbind all events
// =========================================
destroy: function() {
this.close(true);
$d.add("body").off("click.fb-start", "**");
},
// try to detect mobile devices
// ============================
ismobile: /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) &&
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 domrect;
if (!$el || !$el.length) {
return false;
}
domrect = $el[0].getboundingclientrect();
return {
top: domrect.top || 0,
left: domrect.left || 0,
width: domrect.width,
height: domrect.height,
opacity: parsefloat($el.css("opacity"))
};
},
// shortcut for setting "translate3d" properties for element
// can set be used to set opacity, too
// ========================================================
settranslate: function($el, props) {
var str = "",
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) {
var final = false,
from;
if ($.isfunction(duration)) {
callback = duration;
duration = null;
}
if (!$.isplainobject(to)) {
$el.removeattr("style");
}
$.fancybox.stop($el);
$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 (final) {
$.fancybox.settranslate($el, final);
}
if ($.isnumeric(duration)) {
$el.css("transition-duration", "");
}
if ($.isplainobject(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");
}
// start animation by changing css properties or class name
if ($.isplainobject(to)) {
if (to.scalex !== undefined && to.scaley !== undefined) {
from = $.fancybox.gettranslate($el);
final = $.extend({}, to, {
width: from.width * to.scalex,
height: from.height * to.scaley,
scalex: 1,
scaley: 1
});
delete to.width;
delete to.height;
if ($el.parent().hasclass("fancybox-slide--image")) {
$el.parent().addclass("fancybox-is-scaling");
}
}
$.fancybox.settranslate($el, to);
} else {
$el.addclass(to);
}
// make sure that `transitionend` callback gets fired
$el.data(
"timer",
settimeout(function() {
$el.trigger("transitionend");
}, duration + 16)
);
},
stop: function($el, callback) {
if ($el && $el.length) {
cleartimeout($el.data("timer"));
if (callback) {
$el.trigger(transitionend);
}
$el.off(transitionend).css("transition-duration", "");
$el.parent().removeclass("fancybox-is-scaling");
}
}
};
// default click handler for "fancyboxed" links
// ============================================
function _run(e, opts) {
var items = [],
index = 0,
$target,
value,
instance;
// avoid opening multiple times
if (e && e.isdefaultprevented()) {
return;
}
e.preventdefault();
opts = opts || {};
if (e && e.data) {
opts = mergeopts(e.data.options, opts);
}
$target = opts.$target || $(e.currenttarget).trigger("blur");
instance = $.fancybox.getinstance();
if (instance && instance.$trigger && instance.$trigger.is($target)) {
return;
}
if (opts.selector) {
items = $(opts.selector);
} else {
// get all related items and find index for clicked one
value = $target.attr("data-fancybox") || "";
if (value) {
items = e.data ? e.data.items : [];
items = items.length ? items.filter('[data-fancybox="' + value + '"]') : $('[data-fancybox="' + value + '"]');
} else {
items = [$target];
}
}
index = $(items).index($target);
// sometimes current item can not be found
if (index < 0) {
index = 0;
}
instance = $.fancybox.open(items, opts, index);
// save last active element
instance.$trigger = $target;
}
// create a jquery plugin
// ======================
$.fn.fancybox = function(options) {
var selector;
options = options || {};
selector = options.selector || false;
if (selector) {
// use body element instead of document so it executes first
$("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 for all elements having `data-fancybox` attribute
// ==========================================================================
$d.on("click.fb-start", "[data-fancybox]", _run);
// enable "trigger elements"
// =========================
$d.on("click.fb-start", "[data-fancybox-trigger]", function(e) {
$('[data-fancybox="' + $(this).attr("data-fancybox-trigger") + '"]')
.eq($(this).attr("data-fancybox-index") || 0)
.trigger("click.fb-start", {
$trigger: $(this)
});
});
// track focus event for better accessibility styling
// ==================================================
(function() {
var buttonstr = ".fancybox-button",
focusstr = "fancybox-focus",
$pressed = null;
$d.on("mousedown mouseup focus blur", buttonstr, function(e) {
switch (e.type) {
case "mousedown":
$pressed = $(this);
break;
case "mouseup":
$pressed = null;
break;
case "focusin":
$(buttonstr).removeclass(focusstr);
if (!$(this).is($pressed) && !$(this).is("[disabled]")) {
$(this).addclass(focusstr);
}
break;
case "focusout":
$(buttonstr).removeclass(focusstr);
break;
}
});
})();
})(window, document, 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-nocookie.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"
},
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/@52.2111123,2.9237542,6.61z?hl=en
// 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] + "").replace(/\?/, "&") +
"&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;
provider = providername;
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 === "youtube") {
url = url.replace(/&t=((\d+)m)?(\d+)s/, function(match, p1, m, s) {
return "&start=" + ((m ? parseint(m, 10) * 60 : 0) + parseint(s, 10));
});
} else if (providername === "vimeo") {
url = url.replace("&%23", "#");
}
return false;
});
// if it is found, then change content type and update the url
if (type) {
if (!item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length)) {
item.opts.thumb = thumb;
}
if (type === "iframe") {
item.opts = $.extend(true, item.opts, {
iframe: {
preload: false,
attr: {
scrolling: "no"
}
}
});
}
$.extend(item, {
type: type,
src: url,
origsrc: item.src,
contentsource: provider,
contenttype: type === "image" ? "image" : provider == "gmap_place" || provider == "gmap_search" ? "map" : "video"
});
} else if (url) {
item.type = item.opts.defaulttype;
}
});
})(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 getpointerxy = 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,video,audio') ||
$.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"],
overflowx = window.getcomputedstyle(el)["overflow-x"],
vertical = (overflowy === "scroll" || overflowy === "auto") && el.scrollheight > el.clientheight,
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,
$target = $(e.target),
instance = self.instance,
current = instance.current,
$slide = current.$slide,
$content = current.$content,
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 (!$slide.length || !$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 || instance.isanimating || instance.isclosing) {
e.stoppropagation();
e.preventdefault();
return;
}
self.realpoints = self.startpoints = getpointerxy(e);
if (!self.startpoints.length) {
return;
}
// allow other scripts to catch touch event if "touch" is set to false
if (current.touch) {
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.canpan = instance.canpan();
self.starttime = new date().gettime();
self.distancex = self.distancey = self.distance = 0;
self.canvaswidth = math.round($slide[0].clientwidth);
self.canvasheight = math.round($slide[0].clientheight);
self.contentlastpos = null;
self.contentstartpos = $.fancybox.gettranslate(self.$content) || {top: 0, left: 0};
self.sliderstartpos = self.sliderlastpos || $.fancybox.gettranslate($slide);
// since position will be absolute, but we need to make it relative to the stage
self.stagepos = $.fancybox.gettranslate(instance.$refs.stage);
self.sliderstartpos.top -= self.stagepos.top;
self.sliderstartpos.left -= self.stagepos.left;
self.contentstartpos.top -= self.stagepos.top;
self.contentstartpos.left -= self.stagepos.left;
$(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);
}
// skip if clicked outside the sliding area
if (!(self.opts || self.canpan) || !($target.is(self.$stage) || self.$stage.find($target).length)) {
if ($target.is(".fancybox-image")) {
e.preventdefault();
}
return;
}
self.isscrollable = isscrollable($target) || isscrollable($target.parent());
// check if element is scrollable and try to prevent default behavior (scrolling)
if (!($.fancybox.ismobile && self.isscrollable)) {
e.preventdefault();
}
// one finger or mouse click - swipe or pan an image
if (self.startpoints.length === 1 || current.haserror) {
if (self.canpan) {
$.fancybox.stop(self.$content);
self.$content.css("transition-duration", "");
self.ispanning = true;
} else {
self.isswiping = true;
}
self.$container.addclass("fancybox-is-grabbing");
}
// two fingers - zoom image
if (self.startpoints.length === 2 && 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) {
var self = this;
self.isscrolling = true;
document.removeeventlistener("scroll", self.onscroll, true);
};
guestures.prototype.ontouchmove = function(e) {
var self = this;
// make sure user has not released over iframe or disabled element
if (e.originalevent.buttons !== undefined && e.originalevent.buttons === 0) {
self.ontouchend(e);
return;
}
if (self.isscrolling) {
self.cantap = false;
return;
}
self.newpoints = getpointerxy(e);
if (!(self.opts || self.canpan) || !self.newpoints.length || !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 && self.isscrollable) {
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 - $.fancybox.gettranslate(self.instance.$refs.stage).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;
// prevent accidental movement (sometimes, when tapping casually, finger can move a bit)
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 (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 (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
};
};
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 contentstartpos = self.contentstartpos;
var currentwidth = contentstartpos.width;
var currentheight = contentstartpos.height;
var currentoffsetx = contentstartpos.left;
var currentoffsety = 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: pinchratio,
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 = getpointerxy(e);
self.$container.removeclass("fancybox-is-grabbing");
$(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
},
200
);
ret = self.instance.close(true, 200);
} 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 && getpointerxy(e)) || self.startpoints;
var tapx = endpoints[0] ? endpoints[0].x - $(window).scrollleft() - self.stagepos.left : 0;
var tapy = endpoints[0] ? endpoints[0].y - $(window).scrolltop() - self.stagepos.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, 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,
instance = self.instance,
current = instance.current,
advance = function() {
if (self.isactive) {
instance.jumpto((instance.currindex + 1) % instance.group.length);
}
};
// check if reached last element
if (current && (force === true || current.opts.loop || instance.currindex < instance.group.length - 1)) {
self.timer = settimeout(function() {
var $video;
if (self.isactive) {
$video = current.$slide.find("video,audio").filter(":visible:first");
if ($video.length) {
$video.one("ended", advance);
} else {
advance();
}
}
}, current.opts.slideshow.speed);
} else {
self.stop();
instance.idlesecondscounter = 0;
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.$button
.attr("title", current.opts.i18n[current.opts.lang].play_stop)
.removeclass("fancybox-button--play")
.addclass("fancybox-button--pause");
self.isactive = true;
if (current.iscomplete) {
self.set(true);
}
self.instance.trigger("onslideshowchange", 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;
self.instance.trigger("onslideshowchange", 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, 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 ret = {};
for (var i = 0; i < fnmap.length; i++) {
var val = fnmap[i];
if (val && val[1] in document) {
for (var j = 0; j < val.length; j++) {
ret[fnmap[0][j]] = val[j];
}
return ret;
}
}
return false;
})();
if (fn) {
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(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);
instance.$refs.toolbar
.find("[data-fancybox-fullscreen]")
.toggleclass("fancybox-button--fsenter", !isfullscreen)
.toggleclass("fancybox-button--fsexit", isfullscreen);
}
});
}
$(document).on({
"oninit.fb": function(e, instance) {
var $container;
if (!fn) {
instance.$refs.toolbar.find("[data-fancybox-fullscreen]").remove();
return;
}
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();
});
if (instance.opts.fullscreen && instance.opts.fullscreen.autostart === true) {
fullscreen.request();
}
// expose api
instance.fullscreen = fullscreen;
} else if (instance) {
instance.$refs.toolbar.find("[data-fancybox-fullscreen]").hide();
}
},
"afterkeydown.fb": function(e, instance, current, keypress, keycode) {
// "f"
if (instance && instance.fullscreen && keycode === 70) {
keypress.preventdefault();
instance.fullscreen.toggle();
}
},
"beforeclose.fb": function(e, instance) {
if (instance && instance.fullscreen && instance.$refs.container.hasclass("fancybox-is-fullscreen")) {
fullscreen.exit();
}
}
});
})(document, jquery);
// ==========================================================================
//
// thumbs
// displays thumbnails in a grid
//
// ==========================================================================
(function(document, $) {
"use strict";
var class = "fancybox-thumbs",
class_active = class + "-active";
// 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,
first,
second;
self.instance = instance;
instance.thumbs = self;
self.opts = instance.group[instance.currindex].opts.thumbs;
// enable thumbs if at least two group items have thumbnails
first = instance.group[0];
first = first.opts.thumb || (first.opts.$thumb && first.opts.$thumb.length ? first.opts.$thumb.attr("src") : false);
if (instance.group.length > 1) {
second = instance.group[1];
second = second.opts.thumb || (second.opts.$thumb && second.opts.$thumb.length ? second.opts.$thumb.attr("src") : false);
}
self.$button = instance.$refs.toolbar.find("[data-fancybox-thumbs]");
if (self.opts && first && second) {
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;
if (!self.$grid) {
// create main element
self.$grid = $('').appendto(
instance.$refs.container
.find(parentel)
.addback()
.filter(parentel)
);
// add "click" event that performs gallery navigation
self.$grid.on("click", "a", function() {
instance.jumpto($(this).attr("data-index"));
});
}
// build the list
if (!self.$list) {
self.$list = $('
').appendto(self.$grid);
}
$.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;
}
list.push(
'' : "") +
">"
);
});
self.$list[0].innerhtml = list.join("");
if (self.opts.axis === "x") {
// set fixed width for list element to enable horizontal scrolling
self.$list.width(
parseint(self.$grid.css("padding-right"), 10) +
instance.group.length *
self.$list
.children()
.eq(0)
.outerwidth(true)
);
}
},
focus: function(duration) {
var self = this,
$list = self.$list,
$grid = self.$grid,
thumb,
thumbpos;
if (!self.instance.current) {
return;
}
thumb = $list
.children()
.removeclass(class_active)
.filter('[data-index="' + self.instance.current.index + '"]')
.addclass(class_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 < $grid.scrollleft() || thumbpos.left > $grid.scrollleft() + ($grid.width() - thumb.outerwidth()))
) {
$list
.parent()
.stop()
.animate(
{
scrollleft: thumbpos.left
},
duration
);
}
},
update: function() {
var that = this;
that.instance.$refs.container.toggleclass("fancybox-show-thumbs", this.isvisible);
if (that.isvisible) {
if (!that.$grid) {
that.create();
}
that.instance.trigger("onthumbsshow");
that.focus(0);
} else if (that.$grid) {
that.instance.trigger("onthumbshide");
}
// update content position
that.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, jquery);
//// ==========================================================================
//
// share
// displays simple form for sharing current url
//
// ==========================================================================
(function(document, $) {
"use strict";
$.extend(true, $.fancybox.defaults, {
btntpl: {
share:
'"
},
share: {
url: function(instance, item) {
return (
(!instance.currenthash && !(item.type === "inline" || item.type === "html") ? item.origsrc || item.src : false) || window.location
);
},
tpl:
'