").html(content);
}
}
// If "filter" option is provided, then filter content
if (slide.opts.filter) {
content = $("
")
.html(content)
.find(slide.opts.filter);
}
}
slide.$slide.one("onReset", function() {
// Pause all html5 video/audio
$(this)
.find("video,audio")
.trigger("pause");
// Put content back
if (slide.$placeholder) {
slide.$placeholder.after(content.hide()).remove();
slide.$placeholder = null;
}
// Remove custom close button
if (slide.$smallBtn) {
slide.$smallBtn.remove();
slide.$smallBtn = null;
}
// Remove content and mark slide as not loaded
if (!slide.hasError) {
$(this).empty();
slide.isLoaded = false;
}
});
$(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")
.first()
.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)).prependTo(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,
effect,
effectClassName,
duration,
opacity;
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 (slide.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 (!effect) {
forceRedraw($slide);
slide.$content.removeClass("fancybox-is-hidden");
if (slide.pos === self.currPos) {
self.complete();
}
return;
}
$.fancybox.stop($slide);
effectClassName = "fancybox-animated fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-fx-" + effect;
$slide
.removeAttr("style")
.removeClass("fancybox-slide--current fancybox-slide--next fancybox-slide--previous")
.addClass(effectClassName);
slide.$content.removeClass("fancybox-is-hidden");
// Force reflow for CSS3 transitions
forceRedraw($slide);
$.fancybox.animate(
$slide,
"fancybox-slide--current",
duration,
function(e) {
$slide.removeClass(effectClassName).removeAttr("style");
if (slide.pos === self.currPos) {
self.complete();
}
},
true
);
},
// Check if we can and have to zoom from thumbnail
//================================================
getThumbPos: function(slide) {
var self = this,
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 = {};
if (current.isMoved || !current.isLoaded) {
return;
}
if (!current.isComplete) {
current.isComplete = true;
current.$slide.siblings().trigger("onReset");
self.preload("inline");
// Trigger any CSS3 transiton inside the slide
forceRedraw(current.$slide);
current.$slide.addClass("fancybox-slide--complete");
// Remove unnecessary slides
$.each(self.slides, function(key, slide) {
if (slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1) {
slides[slide.pos] = slide;
} else if (slide) {
$.fancybox.stop(slide.$slide);
slide.$slide.off().remove();
}
});
self.slides = slides;
}
self.isAnimating = false;
self.updateCursor();
self.trigger("afterShow");
// Play first html5 video/audio
current.$slide
.find("video,audio")
.filter(":visible:first")
.trigger("play");
// Try to focus on the first focusable element
if (
$(document.activeElement).is("[disabled]") ||
(current.opts.autoFocus && !(current.type == "image" || current.type === "iframe"))
) {
self.focus();
}
},
// Preload next and previous slides
// ================================
preload: function(type) {
var self = this,
next = self.slides[self.currPos + 1],
prev = self.slides[self.currPos - 1];
if (next && next.type === type) {
self.loadSlide(next);
}
if (prev && prev.type === type) {
self.loadSlide(prev);
}
},
// Try to find and focus on the first focusable element
// ====================================================
focus: function() {
var current = this.current,
$el;
if (this.isClosing) {
return;
}
if (current && current.isComplete && current.$content) {
// Look for first input with autofocus attribute
$el = current.$content.find("input[autofocus]:enabled:visible:first");
if (!$el.length) {
$el = current.$content.find("button,:input,[tabindex],a").filter(":enabled:visible:first");
}
$el = $el && $el.length ? $el : current.$content;
$el.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,
$body = $("body"),
instance,
scrollTop;
self.current.$slide.trigger("onReset");
self.$refs.container.empty().remove();
self.trigger("afterClose", e);
// Place back focus
if (self.$lastFocus && !!self.current.opts.backFocus) {
self.$lastFocus.trigger("focus");
}
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(force) {
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]").toggleClass("disabled", !current.opts.loop && index <= 0);
$container.find("[data-fancybox-next]").toggleClass("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();
}
},
// 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-nav", !!(opts.arrows && self.group.length > 1))
.toggleClass("fancybox-is-modal", !!opts.modal);
if (self.$caption) {
$container.addClass("fancybox-show-caption ");
} else {
$container.removeClass("fancybox-show-caption");
}
},
// Toggle toolbar and caption
// ==========================
toggleControls: function() {
if (this.isHiddenControls) {
this.showControls();
} else {
this.hideControls();
}
}
});
$.fancybox = {
version: "3.3.5",
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();
}
}
},
// 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:
document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
// Detect if 'translate3d' support is available
// ============================================
use3d: (function() {
var div = document.createElement("div");
return (
window.getComputedStyle &&
window.getComputedStyle(div) &&
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;
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 ($.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) {
final = $.extend({}, to, {
width: $el.width() * to.scaleX,
height: $el.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) {
if ($el && $el.length) {
clearTimeout($el.data("timer"));
$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;
// Avoid opening multiple times
if (e && e.isDefaultPrevented()) {
return;
}
e.preventDefault();
opts = e && e.data ? e.data.options : opts || {};
$target = opts.$target || $(e.currentTarget);
value = $target.attr("data-fancybox") || "";
// Get all related items and find index for clicked one
if (value) {
items = opts.selector ? $(opts.selector) : e.data ? e.data.items : [];
items = items.length ? items.filter('[data-fancybox="' + value + '"]') : $('[data-fancybox="' + value + '"]');
index = items.index($target);
// Sometimes current item can not be found (for example, if some script clones items)
if (index < 0) {
index = 0;
}
} else {
items = [$target];
}
$.fancybox.open(items, opts, index);
}
// Create a jQuery plugin
// ======================
$.fn.fancybox = function(options) {
var selector;
options = options || {};
selector = options.selector || false;
if (selector) {
// 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-trigger]", function(e) {
_run(e, {
$target: $('[data-fancybox="' + $(e.currentTarget).attr("data-trigger") + '"]').eq($(e.currentTarget).attr("data-index") || 0),
$trigger: $(this)
});
});
})(window, document, window.jQuery || jQuery);
// ==========================================================================
//
// Media
// Adds additional media type support
//
// ==========================================================================
(function($) {
"use strict";
// Formats matching url to final form
var format = function(url, rez, params) {
if (!url) {
return;
}
params = params || "";
if ($.type(params) === "object") {
params = $.param(params, true);
}
$.each(rez, function(key, value) {
url = url.replace("$" + key, value || "");
});
if (params.length) {
url += (url.indexOf("?") > 0 ? "&" : "?") + params;
}
return url;
};
// Object containing properties for each media type
var defaults = {
youtube: {
matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i,
params: {
autoplay: 1,
autohide: 1,
fs: 1,
rel: 0,
hd: 1,
wmode: "transparent",
enablejsapi: 1,
html5: 1
},
paramPlace: 8,
type: "iframe",
url: "//www.youtube.com/embed/$4",
thumb: "//img.youtube.com/vi/$4/hqdefault.jpg"
},
vimeo: {
matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/,
params: {
autoplay: 1,
hd: 1,
show_title: 1,
show_byline: 1,
show_portrait: 0,
fullscreen: 1,
api: 1
},
paramPlace: 3,
type: "iframe",
url: "//player.vimeo.com/video/$2"
},
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;
}
});
})(window.jQuery || jQuery);
// ==========================================================================
//
// Guestures
// Adds touch guestures, handles click and tap events
//
// ==========================================================================
(function(window, document, $) {
"use strict";
var requestAFrame = (function() {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
// if all else fails, use setTimeout
function(callback) {
return window.setTimeout(callback, 1000 / 60);
}
);
})();
var cancelAFrame = (function() {
return (
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
function(id) {
window.clearTimeout(id);
}
);
})();
var 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,
$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 (!$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;
}
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.startTime = new Date().getTime();
self.distanceX = self.distanceY = self.distance = 0;
self.canvasWidth = Math.round(current.$slide[0].clientWidth);
self.canvasHeight = Math.round(current.$slide[0].clientHeight);
self.contentLastPos = null;
self.contentStartPos = $.fancybox.getTranslate(self.$content) || {top: 0, left: 0};
self.sliderStartPos = self.sliderLastPos || $.fancybox.getTranslate(current.$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);
}
if (!(self.opts || instance.canPan()) || !($target.is(self.$stage) || self.$stage.find($target).length)) {
if ($target.is(".fancybox-image")) {
e.preventDefault();
}
return;
}
if (!($.fancybox.isMobile && (isScrollable($target) || isScrollable($target.parent())))) {
e.preventDefault();
}
if (self.startPoints.length === 1 || current.hasError) {
if (self.instance.canPan()) {
$.fancybox.stop(self.$content);
self.$content.css("transition-duration", "");
self.isPanning = true;
} else {
self.isSwiping = true;
}
self.$container.addClass("fancybox-controls--isGrabbing");
}
if (self.startPoints.length === 2 && 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,
$target = $(e.target);
// 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 || !($target.is(self.$stage) || self.$stage.find($target).length)) {
self.canTap = false;
return;
}
self.newPoints = getPointerXY(e);
if (!(self.opts || self.instance.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 && (isScrollable(self.$target) || isScrollable(self.$target.parent()))) {
self.isScrolling = true;
return;
}
self.instance.isDragging = self.isSwiping;
// Reset points to avoid jumping, because we dropped first swipes to calculate the angle
self.startPoints = self.newPoints;
$.each(self.instance.slides, function(index, slide) {
$.fancybox.stop(slide.$slide);
slide.$slide.css("transition-duration", "");
slide.inTransition = false;
if (slide.pos === self.instance.current.pos) {
self.sliderStartPos.left = $.fancybox.getTranslate(slide.$slide).left - $.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-controls--isGrabbing");
$(document).off(".fb.touch");
document.removeEventListener("scroll", self.onscroll, true);
if (self.requestId) {
cancelAFrame(self.requestId);
self.requestId = null;
}
self.isSwiping = false;
self.isPanning = false;
self.isZooming = false;
self.isScrolling = false;
self.instance.isDragging = false;
if (self.canTap) {
return self.onTap(e);
}
self.speed = 366;
// Speed in px/ms
self.velocityX = self.distanceX / dMs * 0.5;
self.velocityY = self.distanceY / dMs * 0.5;
self.speedX = Math.max(self.speed * 0.5, Math.min(self.speed * 1.5, 1 / Math.abs(self.velocityX) * self.speed));
if (panning) {
self.endPanning();
} else if (zooming) {
self.endZooming();
} else {
self.endSwiping(swiping, scrolling);
}
return;
};
Guestures.prototype.endSwiping = function(swiping, scrolling) {
var self = this,
ret = false,
len = self.instance.group.length;
self.sliderLastPos = null;
// Close if swiped vertically / navigate if horizontally
if (swiping == "y" && !scrolling && Math.abs(self.distanceY) > 50) {
// Continue vertical movement
$.fancybox.animate(
self.instance.current.$slide,
{
top: self.sliderStartPos.top + self.distanceY + self.velocityY * 150,
opacity: 0
},
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, window.jQuery || jQuery);
// ==========================================================================
//
// SlideShow
// Enables slideshow functionality
//
// Example of usage:
// $.fancybox.getInstance().SlideShow.start()
//
// ==========================================================================
(function(document, $) {
"use strict";
$.extend(true, $.fancybox.defaults, {
btnTpl: {
slideShow:
'
"
},
slideShow: {
autoStart: false,
speed: 3000
}
});
var SlideShow = function(instance) {
this.instance = instance;
this.init();
};
$.extend(SlideShow.prototype, {
timer: null,
isActive: false,
$button: null,
init: function() {
var self = this;
self.$button = self.instance.$refs.toolbar.find("[data-fancybox-play]").on("click", function() {
self.toggle();
});
if (self.instance.group.length < 2 || !self.instance.group[self.instance.currIndex].opts.slideShow) {
self.$button.hide();
}
},
set: function(force) {
var self = this;
// Check if reached last element
if (
self.instance &&
self.instance.current &&
(force === true || self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1)
) {
self.timer = setTimeout(function() {
if (self.isActive) {
self.instance.jumpTo((self.instance.currIndex + 1) % self.instance.group.length);
}
}, self.instance.current.opts.slideShow.speed);
} else {
self.stop();
self.instance.idleSecondsCounter = 0;
self.instance.showControls();
}
},
clear: function() {
var self = this;
clearTimeout(self.timer);
self.timer = null;
},
start: function() {
var self = this;
var current = self.instance.current;
if (current) {
self.isActive = true;
self.$button
.attr("title", current.opts.i18n[current.opts.lang].PLAY_STOP)
.removeClass("fancybox-button--play")
.addClass("fancybox-button--pause");
self.set(true);
}
},
stop: function() {
var self = this;
var current = self.instance.current;
self.clear();
self.$button
.attr("title", current.opts.i18n[current.opts.lang].PLAY_START)
.removeClass("fancybox-button--pause")
.addClass("fancybox-button--play");
self.isActive = false;
},
toggle: function() {
var self = this;
if (self.isActive) {
self.stop();
} else {
self.start();
}
}
});
$(document).on({
"onInit.fb": function(e, instance) {
if (instance && !instance.SlideShow) {
instance.SlideShow = new SlideShow(instance);
}
},
"beforeShow.fb": function(e, instance, current, firstRun) {
var SlideShow = instance && instance.SlideShow;
if (firstRun) {
if (SlideShow && current.opts.slideShow.autoStart) {
SlideShow.start();
}
} else if (SlideShow && SlideShow.isActive) {
SlideShow.clear();
}
},
"afterShow.fb": function(e, instance, current) {
var SlideShow = instance && instance.SlideShow;
if (SlideShow && SlideShow.isActive) {
SlideShow.set();
}
},
"afterKeydown.fb": function(e, instance, current, keypress, keycode) {
var SlideShow = instance && instance.SlideShow;
// "P" or Spacebar
if (SlideShow && current.opts.slideShow && (keycode === 80 || keycode === 32) && !$(document.activeElement).is("button,a,input")) {
keypress.preventDefault();
SlideShow.toggle();
}
},
"beforeClose.fb onDeactivate.fb": function(e, instance) {
var SlideShow = instance && instance.SlideShow;
if (SlideShow) {
SlideShow.stop();
}
}
});
// Page Visibility API to pause slideshow when window is not active
$(document).on("visibilitychange", function() {
var instance = $.fancybox.getInstance();
var SlideShow = instance && instance.SlideShow;
if (SlideShow && SlideShow.isActive) {
if (document.hidden) {
SlideShow.clear();
} else {
SlideShow.set();
}
}
});
})(document, window.jQuery || jQuery);
// ==========================================================================
//
// FullScreen
// Adds fullscreen functionality
//
// ==========================================================================
(function(document, $) {
"use strict";
// Collection of methods supported by user browser
var fn = (function() {
var fnMap = [
["requestFullscreen", "exitFullscreen", "fullscreenElement", "fullscreenEnabled", "fullscreenchange", "fullscreenerror"],
// new WebKit
[
"webkitRequestFullscreen",
"webkitExitFullscreen",
"webkitFullscreenElement",
"webkitFullscreenEnabled",
"webkitfullscreenchange",
"webkitfullscreenerror"
],
// old WebKit (Safari 5.1)
[
"webkitRequestFullScreen",
"webkitCancelFullScreen",
"webkitCurrentFullScreenElement",
"webkitCancelFullScreen",
"webkitfullscreenchange",
"webkitfullscreenerror"
],
[
"mozRequestFullScreen",
"mozCancelFullScreen",
"mozFullScreenElement",
"mozFullScreenEnabled",
"mozfullscreenchange",
"mozfullscreenerror"
],
["msRequestFullscreen", "msExitFullscreen", "msFullscreenElement", "msFullscreenEnabled", "MSFullscreenChange", "MSFullscreenError"]
];
var 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 browser does not have Full Screen API, then simply unset default button template and stop
if (!fn) {
if ($ && $.fancybox) {
$.fancybox.defaults.btnTpl.fullScreen = false;
}
return;
}
var FullScreen = {
request: function(elem) {
elem = elem || document.documentElement;
elem[fn.requestFullscreen](elem.ALLOW_KEYBOARD_INPUT);
},
exit: function() {
document[fn.exitFullscreen]();
},
toggle: function(elem) {
elem = elem || document.documentElement;
if (this.isFullscreen()) {
this.exit();
} else {
this.request(elem);
}
},
isFullscreen: function() {
return Boolean(document[fn.fullscreenElement]);
},
enabled: function() {
return Boolean(document[fn.fullscreenEnabled]);
}
};
$.extend(true, $.fancybox.defaults, {
btnTpl: {
fullScreen:
'
"
},
fullScreen: {
autoStart: false
}
});
$(document).on({
"onInit.fb": function(e, instance) {
var $container;
if (instance && instance.group[instance.currIndex].opts.fullScreen) {
$container = instance.$refs.container;
$container.on("click.fb-fullscreen", "[data-fancybox-fullscreen]", function(e) {
e.stopPropagation();
e.preventDefault();
FullScreen.toggle();
});
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).on(fn.fullscreenchange, function() {
var isFullscreen = FullScreen.isFullscreen(),
instance = $.fancybox.getInstance();
if (instance) {
// If image is zooming, then force to stop and reposition properly
if (instance.current && instance.current.type === "image" && instance.isAnimating) {
instance.current.$content.css("transition", "none");
instance.isAnimating = false;
instance.update(true, true, 0);
}
instance.trigger("onFullscreenChange", isFullscreen);
instance.$refs.container.toggleClass("fancybox-is-fullscreen", isFullscreen);
}
});
})(document, window.jQuery || jQuery);
// ==========================================================================
//
// Thumbs
// Displays thumbnails in a grid
//
// ==========================================================================
(function(document, $) {
"use strict";
var CLASS = "fancybox-thumbs",
CLASS_ACTIVE = CLASS + "-active",
CLASS_LOAD = CLASS + "-loading";
// 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 && 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", "li", function() {
instance.jumpTo($(this).attr("data-index"));
});
}
// Build the list
if (!self.$list) {
self.$list = $("