923 lines
26 KiB
JavaScript
923 lines
26 KiB
JavaScript
// ==========================================================================
|
|
//
|
|
// 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,iframe') ||
|
|
$.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 () {
|
|
var self = this;
|
|
|
|
self.$container.off(".fb.touch");
|
|
|
|
$(document).off(".fb.touch");
|
|
|
|
if (self.requestId) {
|
|
cancelAFrame(self.requestId);
|
|
self.requestId = null;
|
|
}
|
|
|
|
if (self.tapped) {
|
|
clearTimeout(self.tapped);
|
|
self.tapped = null;
|
|
}
|
|
};
|
|
|
|
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 || current.$slide.hasClass("fancybox-animated")) {
|
|
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 = $.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();
|
|
}
|
|
|
|
if (!($.fancybox.isMobile && $target.parents(".fancybox-caption").length)) {
|
|
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.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.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,
|
|
instance = self.instance,
|
|
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 (instance.group.length < 2 && self.opts.vertical) {
|
|
self.isSwiping = "y";
|
|
} else if (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";
|
|
}
|
|
|
|
if (self.isSwiping === "y" && $.fancybox.isMobile && self.isScrollable) {
|
|
self.isScrolling = true;
|
|
|
|
return;
|
|
}
|
|
|
|
instance.isDragging = self.isSwiping;
|
|
|
|
// Reset points to avoid jumping, because we dropped first swipes to calculate the angle
|
|
self.startPoints = self.newPoints;
|
|
|
|
$.each(instance.slides, function (index, slide) {
|
|
var slidePos, stagePos;
|
|
|
|
$.fancybox.stop(slide.$slide);
|
|
|
|
slidePos = $.fancybox.getTranslate(slide.$slide);
|
|
stagePos = $.fancybox.getTranslate(instance.$refs.stage);
|
|
|
|
slide.$slide
|
|
.css({
|
|
transform: "",
|
|
opacity: "",
|
|
"transition-duration": ""
|
|
})
|
|
.removeClass("fancybox-animated")
|
|
.removeClass(function (index, className) {
|
|
return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join(" ");
|
|
});
|
|
|
|
if (slide.pos === instance.current.pos) {
|
|
self.sliderStartPos.top = slidePos.top - stagePos.top;
|
|
self.sliderStartPos.left = slidePos.left - stagePos.left;
|
|
}
|
|
|
|
$.fancybox.setTranslate(slide.$slide, {
|
|
top: slidePos.top - stagePos.top,
|
|
left: slidePos.left - stagePos.left
|
|
});
|
|
});
|
|
|
|
// Stop slideshow
|
|
if (instance.SlideShow && instance.SlideShow.isActive) {
|
|
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 = 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 = requestAFrame(function () {
|
|
$.fancybox.setTranslate(self.$content, self.contentLastPos);
|
|
});
|
|
};
|
|
|
|
Guestures.prototype.ontouchend = function (e) {
|
|
var self = this;
|
|
|
|
var swiping = self.isSwiping;
|
|
var panning = self.isPanning;
|
|
var zooming = self.isZooming;
|
|
var scrolling = self.isScrolling;
|
|
|
|
self.endPoints = getPointerXY(e);
|
|
self.dMs = Math.max(new Date().getTime() - self.startTime, 1);
|
|
|
|
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 = 100;
|
|
|
|
// Speed in px/ms
|
|
self.velocityX = (self.distanceX / self.dMs) * 0.5;
|
|
self.velocityY = (self.distanceY / self.dMs) * 0.5;
|
|
|
|
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,
|
|
distanceX = Math.abs(self.distanceX),
|
|
canAdvance = swiping == "x" && len > 1 && ((self.dMs > 130 && distanceX > 10) || distanceX > 50),
|
|
speedX = 300;
|
|
|
|
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, 250);
|
|
} else if (canAdvance && self.distanceX > 0) {
|
|
ret = self.instance.previous(speedX);
|
|
} else if (canAdvance && self.distanceX < 0) {
|
|
ret = self.instance.next(speedX);
|
|
}
|
|
|
|
if (ret === false && (swiping == "x" || swiping == "y")) {
|
|
self.instance.centerSlide(200);
|
|
}
|
|
|
|
self.$container.removeClass("fancybox-is-sliding");
|
|
};
|
|
|
|
// Limit panning from edges
|
|
// ========================
|
|
Guestures.prototype.endPanning = function () {
|
|
var self = this,
|
|
newOffsetX,
|
|
newOffsetY,
|
|
newPos;
|
|
|
|
if (!self.contentLastPos) {
|
|
return;
|
|
}
|
|
|
|
if (self.opts.momentum === false || self.dMs > 350) {
|
|
newOffsetX = self.contentLastPos.left;
|
|
newOffsetY = self.contentLastPos.top;
|
|
} else {
|
|
// Continue movement
|
|
newOffsetX = self.contentLastPos.left + self.velocityX * 500;
|
|
newOffsetY = self.contentLastPos.top + self.velocityY * 500;
|
|
}
|
|
|
|
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, 366);
|
|
};
|
|
|
|
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);
|
|
|
|
$.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();
|
|
|
|
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;
|
|
|
|
if (!instance.isAnimating) {
|
|
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);
|
|
}
|
|
})
|
|
.on("beforeClose.fb", function (e, instance) {
|
|
if (instance && instance.Guestures) {
|
|
instance.Guestures.destroy();
|
|
}
|
|
});
|
|
})(window, document, jQuery); |