766 lines
28 KiB
JavaScript
766 lines
28 KiB
JavaScript
/* -*- js-indent-level: 8; fill-column: 100 -*- */
|
|
/*
|
|
* L.Map.CalcTap is used to enable mobile taps.
|
|
*/
|
|
|
|
L.Map.mergeOptions({
|
|
touchGesture: true,
|
|
});
|
|
|
|
/* global Hammer app $ */
|
|
L.Map.TouchGesture = L.Handler.extend({
|
|
statics: {
|
|
MAP: 1,
|
|
CURSOR: 2,
|
|
GRAPHIC: 4,
|
|
MARKER: 8,
|
|
TABLE: 16
|
|
},
|
|
|
|
initialize: function (map) {
|
|
L.Handler.prototype.initialize.call(this, map);
|
|
this._state = L.Map.TouchGesture.MAP;
|
|
|
|
if (!this._hammer) {
|
|
this._hammer = new Hammer(this._map._mapPane);
|
|
this._hammer.get('swipe').set({
|
|
direction: Hammer.DIRECTION_ALL
|
|
});
|
|
this._hammer.get('pan').set({
|
|
direction: Hammer.DIRECTION_ALL
|
|
});
|
|
this._hammer.get('pinch').set({
|
|
enable: true
|
|
});
|
|
// avoid to trigger the context menu too early so the user can start panning in a relaxed way
|
|
this._hammer.get('press').set({
|
|
time: 500
|
|
});
|
|
|
|
this._hammer.get('swipe').set({
|
|
threshold: 5
|
|
});
|
|
|
|
var singleTap = this._hammer.get('tap');
|
|
var doubleTap = this._hammer.get('doubletap');
|
|
|
|
// Multi-tap detection tolerates a slight change in coordinates
|
|
// between the taps. The default of 10 is too small for our needs.
|
|
// So we use something more sensible to make it easier for users.
|
|
var posThreshold = 100;
|
|
doubleTap.options.posThreshold = posThreshold;
|
|
|
|
var tripleTap = new Hammer.Tap({event: 'tripletap', taps: 3, posThreshold: posThreshold });
|
|
this._hammer.add(tripleTap);
|
|
tripleTap.recognizeWith([doubleTap, singleTap]);
|
|
var hammer = this._hammer;
|
|
L.DomEvent.on(this._map._mapPane, 'touchstart touchmove touchcancel', window.touch.touchOnly(L.DomEvent.preventDefault));
|
|
L.DomEvent.on(this._map._mapPane, 'touchend', window.touch.touchOnly(function(e) {
|
|
// sometimes inputs get stuck in hammer and further events get mixed with the old ones
|
|
// this causes to a failure to use all the gestures properly.
|
|
// This is a workaround until it is fixed by hammer.js
|
|
if (hammer.input) {
|
|
if (hammer.input.store) {
|
|
hammer.input.store = [];
|
|
}
|
|
}
|
|
L.DomEvent.preventDefault(e);
|
|
}));
|
|
|
|
if (Hammer.prefixed(window, 'PointerEvent') !== undefined) {
|
|
L.DomEvent.on(this._map._mapPane, 'pointerdown pointermove pointerup pointercancel', window.touch.touchOnly(L.DomEvent.preventDefault));
|
|
}
|
|
|
|
// IE10 has prefixed support, and case-sensitive
|
|
if (window.MSPointerEvent && !window.PointerEvent) {
|
|
L.DomEvent.on(this._map._mapPane, 'MSPointerDown MSPointerMove MSPointerUp MSPointerCancel', window.touch.touchOnly(L.DomEvent.preventDefault));
|
|
}
|
|
|
|
L.DomEvent.on(this._map._mapPane, 'mousedown mousemove mouseup', L.DomEvent.preventDefault);
|
|
L.DomEvent.on(document, 'touchmove', L.DomEvent.preventDefault);
|
|
}
|
|
|
|
for (var events in L.Draggable.MOVE) {
|
|
L.DomEvent.on(document, L.Draggable.END[events], this._onDocUp, this);
|
|
}
|
|
|
|
/// $.contextMenu does not support touch events so,
|
|
/// attach 'touchend' menu clicks event handler
|
|
if (this._hammer.input instanceof Hammer.TouchInput) {
|
|
var $doc = $(document);
|
|
$doc.on('click.contextMenu', '.context-menu-item', function (e) {
|
|
var $elem = $(this);
|
|
|
|
if ($elem.data().contextMenu.selector === '.leaflet-layer') {
|
|
$.contextMenu.handle.itemClick.apply(this, [e]);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
addHooks: function () {
|
|
this._hammer.on('hammer.input', window.memo.bind(window.touch.touchOnly(this._onHammer), this));
|
|
this._hammer.on('tap', window.memo.bind(window.touch.touchOnly(this._onTap), this));
|
|
this._hammer.on('panstart', window.memo.bind(window.touch.touchOnly(this._onPanStart), this));
|
|
this._hammer.on('pan', window.memo.bind(window.touch.touchOnly(this._onPan), this));
|
|
this._hammer.on('panend', window.memo.bind(window.touch.touchOnly(this._onPanEnd), this));
|
|
this._hammer.on('pinchstart', window.memo.bind(window.touch.touchOnly(this._onPinchStart), this));
|
|
this._hammer.on('pinchmove', window.memo.bind(window.touch.touchOnly(this._onPinch), this));
|
|
this._hammer.on('pinchend', window.memo.bind(window.touch.touchOnly(this._onPinchEnd), this));
|
|
this._hammer.on('tripletap', window.memo.bind(window.touch.touchOnly(this._onTripleTap), this));
|
|
this._hammer.on('swipe', window.memo.bind(window.touch.touchOnly(this._onSwipe), this));
|
|
this._map.on('updatepermission', this._onPermission, this);
|
|
this._onPermission({perm: this._map._permission});
|
|
},
|
|
|
|
removeHooks: function () {
|
|
this._hammer.off('hammer.input', window.memo.bind(window.touch.touchOnly(this._onHammer), this));
|
|
this._hammer.off('tap', window.memo.bind(window.touch.touchOnly(this._onTap), this));
|
|
this._hammer.off('panstart', window.memo.bind(window.touch.touchOnly(this._onPanStart), this));
|
|
this._hammer.off('pan', window.memo.bind(window.touch.touchOnly(this._onPan), this));
|
|
this._hammer.off('panend', window.memo.bind(window.touch.touchOnly(this._onPanEnd), this));
|
|
this._hammer.off('pinchstart', window.memo.bind(window.touch.touchOnly(this._onPinchStart), this));
|
|
this._hammer.off('pinchmove', window.memo.bind(window.touch.touchOnly(this._onPinch), this));
|
|
this._hammer.off('pinchend', window.memo.bind(window.touch.touchOnly(this._onPinchEnd), this));
|
|
this._hammer.off('doubletap', window.memo.bind(window.touch.touchOnly(this._onDoubleTap), this));
|
|
this._hammer.off('press', window.memo.bind(window.touch.touchOnly(this._onPress), this));
|
|
this._hammer.off('tripletap', window.memo.bind(window.touch.touchOnly(this._onTripleTap), this));
|
|
this._hammer.off('swipe', window.memo.bind(window.touch.touchOnly(this._onSwipe), this));
|
|
this._map.off('updatepermission', this._onPermission, this);
|
|
},
|
|
|
|
_onPermission: function (e) {
|
|
if (e.perm == 'edit') {
|
|
this._hammer.on('doubletap', window.memo.bind(window.touch.touchOnly(this._onDoubleTap), this));
|
|
this._hammer.on('press', window.memo.bind(window.touch.touchOnly(this._onPress), this));
|
|
} else {
|
|
this._hammer.off('doubletap', window.memo.bind(window.touch.touchOnly(this._onDoubleTap), this));
|
|
this._hammer.off('press', window.memo.bind(window.touch.touchOnly(this._onPress), this));
|
|
}
|
|
},
|
|
|
|
_onHammer: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
app.idleHandler.notifyActive();
|
|
|
|
// Function/Formula Wizard keeps the formula cell active all the time,
|
|
// so the usual range selection doesn't work here.
|
|
// Instead, the cells are highlighted with a certain color and opacity
|
|
// to mark as selection. And that's why we are checking for it here.
|
|
// FIXME: JS-ify. This code is written by a C++ dev.
|
|
function getFuncWizRangeBounds (obj) {
|
|
for (var i in obj._map._layers) {
|
|
if (obj._map._layers[i].options && obj._map._layers[i].options.fillColor
|
|
&& obj._map._layers[i].options.fillOpacity) {
|
|
if (obj._map._layers[i].options.fillColor === '#ef0fff'
|
|
&& obj._map._layers[i].options.fillOpacity === 0.25) {
|
|
return obj._map._layers[i]._bounds;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (e.isFirst) {
|
|
var point = e.pointers[0],
|
|
containerPoint = this._map.mouseEventToContainerPoint(point),
|
|
layerPoint = this._map.containerPointToLayerPoint(containerPoint),
|
|
latlng = this._map.layerPointToLatLng(layerPoint),
|
|
funcWizardRangeBounds = getFuncWizRangeBounds(this);
|
|
|
|
let twipsPoint = this._map._docLayer._latLngToTwips(latlng);
|
|
twipsPoint = new app.definitions.simplePoint(twipsPoint.x, twipsPoint.y);
|
|
|
|
if (this._map._docLayer._graphicMarker) {
|
|
this._marker = this._map._docLayer._graphicMarker.transform.getMarker(layerPoint);
|
|
}
|
|
|
|
if (this._marker) {
|
|
this._state = L.Map.TouchGesture.MARKER;
|
|
} else if (this._map._docLayer._graphicMarker && this._map._docLayer._graphicMarker.getBounds().contains(latlng)) {
|
|
if (this._map._docLayer.hasTableSelection())
|
|
this._state = L.Map.TouchGesture.TABLE;
|
|
else
|
|
this._state = L.Map.TouchGesture.GRAPHIC;
|
|
} else if (app.calc.cellCursorVisible && app.calc.cellCursorRectangle.containsPoint(twipsPoint.toArray())) {
|
|
this._state = L.Map.TouchGesture.CURSOR;
|
|
} else if (app.calc.cellCursorVisible && funcWizardRangeBounds && funcWizardRangeBounds.contains(latlng)) {
|
|
this._state = L.Map.TouchGesture.CURSOR;
|
|
} else {
|
|
this._state = L.Map.TouchGesture.MAP;
|
|
}
|
|
this._moving = false;
|
|
}
|
|
|
|
if (e.isLast && this._state !== L.Map.TouchGesture.MAP) {
|
|
this._state = L.Map.TouchGesture.hitTest.MAP;
|
|
this._marker = undefined;
|
|
this._moving = false;
|
|
}
|
|
|
|
if ($(e.srcEvent.target).has(this._map._mapPane)) {
|
|
L.DomEvent.preventDefault(e.srcEvent);
|
|
L.DomEvent.stopPropagation(e.srcEvent);
|
|
}
|
|
},
|
|
|
|
_onDocUp: function () {
|
|
if (!this._map.touchGesture.enabled()) {
|
|
this._map.touchGesture.enable();
|
|
}
|
|
|
|
window.IgnorePanning = undefined;
|
|
},
|
|
|
|
_onPress: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
var point = e.pointers[0],
|
|
containerPoint = this._map.mouseEventToContainerPoint(point),
|
|
layerPoint = this._map.containerPointToLayerPoint(containerPoint),
|
|
latlng = this._map.layerPointToLatLng(layerPoint),
|
|
mousePos = this._map._docLayer._latLngToTwips(latlng);
|
|
|
|
let posInTwips = new app.definitions.simplePoint(mousePos.x, mousePos.y);
|
|
|
|
if (this._moving) {
|
|
return;
|
|
}
|
|
|
|
this._map.fire('closepopups');
|
|
|
|
var that = this;
|
|
var docLayer = this._map._docLayer;
|
|
|
|
var singleClick = function () {
|
|
docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 1, 0);
|
|
docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 1, 0);
|
|
};
|
|
|
|
var doubleClick = function () {
|
|
docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 2, 1, 0);
|
|
docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 2, 1, 0);
|
|
};
|
|
|
|
var rightClick = function () {
|
|
// We will only send "buttondown" event because core side fires "buttonup" event internally.
|
|
docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 4, 0);
|
|
};
|
|
|
|
var waitForSelectionMsg = function () {
|
|
// check new selection if any
|
|
var graphicSelection = docLayer._graphicSelection;
|
|
if (!docLayer._cursorAtMispelledWord
|
|
&& (!graphicSelection || !graphicSelection.contains(latlng))
|
|
&& (!app.calc.cellCursorVisible || !app.calc.cellCursorRectangle.containsPoint(posInTwips.toArray()))) {
|
|
// try to select text
|
|
doubleClick();
|
|
}
|
|
// send right click to trigger context menus
|
|
that._map._contextMenu._onMouseDown({originalEvent: e.srcEvent});
|
|
rightClick();
|
|
};
|
|
|
|
// we want to select the long touched object before triggering the context menu;
|
|
// for selecting text we need to simulate a double click, anyway for a graphic object
|
|
// a single click is enough, while a double click can lead to switch to edit mode
|
|
// (not only for an embedded ole object, even for entering text inside a shape);
|
|
// a similar problem regards spreadsheet cell: a single click moves the cell cursor,
|
|
// while a double click enables text input;
|
|
// in order to avoid these cases, we send a single click and wait for a few milliseconds
|
|
// before checking if we received a possible selection message; if no such message is received
|
|
// we simulate a double click for trying to select text and finally, in any case,
|
|
// we trigger the context menu by sending a right click
|
|
var graphicSelection = docLayer._graphicSelection;
|
|
var bContainsSel = false;
|
|
if (app.calc.cellCursorVisible)
|
|
bContainsSel = docLayer.containsSelection(latlng);
|
|
var textSelection;
|
|
if (docLayer._selectionHandles.start.rectangle && docLayer._selectionHandles.end.rectangle) {
|
|
// Oversimplication. See "inBand" function.
|
|
textSelection = new app.definitions.simpleRectangle(0, docLayer._selectionHandles.end.rectangle.y1, app.file.size.twips[0], 0);
|
|
textSelection.height = docLayer._selectionHandles.end.rectangle.y2 - docLayer._selectionHandles.start.rectangle.y1;
|
|
}
|
|
|
|
if ((textSelection && textSelection.pContainsPoint(posInTwips.toArray()))
|
|
|| (graphicSelection && graphicSelection.contains(latlng))
|
|
|| (app.calc.cellCursorVisible && app.calc.cellCursorRectangle.containsPoint(posInTwips.toArray())) || bContainsSel) {
|
|
// long touched an already selected object
|
|
// send right click to trigger context menus
|
|
this._map._contextMenu._onMouseDown({originalEvent: e.srcEvent});
|
|
rightClick();
|
|
}
|
|
else {
|
|
// try to select a graphic object or move the cell cursor
|
|
singleClick();
|
|
setTimeout(waitForSelectionMsg, 300);
|
|
}
|
|
|
|
app.idleHandler.notifyActive();
|
|
e.preventDefault();
|
|
},
|
|
|
|
_onTap: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
// We receive each tap here, even when double- and triple-taps
|
|
// are detected. This is undesirable as the subsequent taps
|
|
// processed here interfere with the double- and triple-tap
|
|
// handlers, confusing Core (and the user) as the result
|
|
// is not what's expected (objects not getting selected,
|
|
// edit mode not entered, or toggled, keyboard toggles, etc.).
|
|
// We only process the first tap and subsequent ones are handled
|
|
// by the double-tap and triple-tap handlers below.
|
|
// Note: Hammer has requireFailure() which suppresses this call
|
|
// when multi-taps are detected. This isn't working for us.
|
|
if (e.tapCount > 1)
|
|
return;
|
|
|
|
var point = e.pointers[0],
|
|
containerPoint = this._map.mouseEventToContainerPoint(point),
|
|
layerPoint = this._map.containerPointToLayerPoint(containerPoint),
|
|
latlng = this._map.layerPointToLatLng(layerPoint),
|
|
mousePos = this._map._docLayer._latLngToTwips(latlng);
|
|
|
|
let posInTwips = new app.definitions.simplePoint(mousePos.x, mousePos.y);
|
|
|
|
// clicked a hyperlink popup - not really designed for this.
|
|
if (this._map.hyperlinkPopup && e.target) {
|
|
var tapOnHyperlinkPopup = false;
|
|
this._map.hyperlinkPopup._htmlContent.childNodes.forEach(function(childNode) {
|
|
if (childNode == e.target.parentNode || childNode == e.target) {
|
|
tapOnHyperlinkPopup = true;
|
|
return;
|
|
}
|
|
});
|
|
// not forward mouse events to core if the user tap on a hyperlink popup box
|
|
// for instance on Writer that causes the text cursor to be moved
|
|
if (tapOnHyperlinkPopup) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this._map.fire('closemobilewizard');
|
|
|
|
// The validity and content control dropdown marker icon (exists in calc and writer) needs to be notified of tap events if it is the target.
|
|
var dropDownMarkers;
|
|
if (this._map._docLayer.isWriter()) {
|
|
dropDownMarkers = document.getElementsByClassName('leaflet-marker-icon writer-drop-down-marker');
|
|
} else if (this._map._docLayer.isCalc()) {
|
|
dropDownMarkers = document.getElementsByClassName('leaflet-marker-icon spreadsheet-drop-down-marker');
|
|
}
|
|
if (dropDownMarkers && dropDownMarkers.length == 1 && dropDownMarkers[0] && e.target && e.target == dropDownMarkers[0]) {
|
|
this._map.fire('dropdownmarkertapped');
|
|
// don't send the mouse-event to core
|
|
return;
|
|
}
|
|
|
|
this._map.fire('closepopups');
|
|
this._map.fire('editorgotfocus');
|
|
|
|
var docLayer = this._map._docLayer;
|
|
|
|
// unselect if anything is selected already
|
|
if (app.sectionContainer.doesSectionExist(L.CSections.CommentList.name)) {
|
|
app.sectionContainer.getSectionWithName(L.CSections.CommentList.name).unselect();
|
|
}
|
|
|
|
this._map._contextMenu._onMouseDown({originalEvent: e.srcEvent});
|
|
|
|
var acceptInput = false; // No keyboard by default.
|
|
var sendMouseEvents = true; // By default, this is a single-click.
|
|
if (docLayer) {
|
|
if (docLayer.hasGraphicSelection()) {
|
|
// Need keyboard when cursor is visible.
|
|
acceptInput = this._map._docLayer.isCursorVisible();
|
|
} else if (docLayer._docType === 'text') {
|
|
acceptInput = true; // Always show the keyboard in Writer on tap.
|
|
} else if (docLayer._docType === 'spreadsheet') {
|
|
// If the tap is in the current cell, start editing.
|
|
acceptInput = (app.calc.cellCursorVisible && app.calc.cellCursorRectangle.containsPoint(posInTwips.toArray()));
|
|
if (acceptInput) {
|
|
// Enter cell-edit mode on second tap of a selected cell.
|
|
if (this._map.isEditMode()) {
|
|
docLayer.postKeyboardEvent('input', 0, 769); // F2
|
|
sendMouseEvents = false; // Mouse events will exit editing mode.
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sendMouseEvents) {
|
|
docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 1, 0);
|
|
docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 1, 0);
|
|
}
|
|
|
|
this._cancelAutoScroll = true;
|
|
|
|
// Always move the focus to the document on tap,
|
|
// but only show the keyboard when we need editing.
|
|
this._map.focus(acceptInput);
|
|
},
|
|
|
|
_onDoubleTap: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
var point = e.pointers[0],
|
|
containerPoint = this._map.mouseEventToContainerPoint(point),
|
|
layerPoint = this._map.containerPointToLayerPoint(containerPoint),
|
|
latlng = this._map.layerPointToLatLng(layerPoint),
|
|
mousePos = this._map._docLayer._latLngToTwips(latlng);
|
|
|
|
var docLayer = this._map._docLayer;
|
|
if (docLayer) {
|
|
if (docLayer._docType === 'spreadsheet' && !docLayer.hasGraphicSelection()) {
|
|
// Enter cell-edit mode on double-taping a cell.
|
|
if (this._map.isEditMode()) {
|
|
docLayer.postKeyboardEvent('input', 0, 769); // F2
|
|
}
|
|
} else {
|
|
docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 2, 1, 0);
|
|
docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 2, 1, 0);
|
|
}
|
|
|
|
// Show keyboard when no graphic selection, or cursor is visible.
|
|
var acceptInput = !docLayer.hasGraphicSelection() || docLayer.isCursorVisible();
|
|
|
|
if (navigator.platform === 'iPhone' && docLayer._docType === 'presentation')
|
|
acceptInput = true;
|
|
|
|
this._map.focus(acceptInput);
|
|
}
|
|
},
|
|
|
|
_onTripleTap: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
var point = e.pointers[0],
|
|
containerPoint = this._map.mouseEventToContainerPoint(point),
|
|
layerPoint = this._map.containerPointToLayerPoint(containerPoint),
|
|
latlng = this._map.layerPointToLatLng(layerPoint),
|
|
mousePos = this._map._docLayer._latLngToTwips(latlng);
|
|
|
|
this._map._docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 1, 8192);
|
|
this._map._docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 1, 8192);
|
|
},
|
|
|
|
_onPanStart: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
if (window.IgnorePanning)
|
|
return;
|
|
|
|
L.Util.cancelAnimFrame(this.autoscrollAnimReq);
|
|
var point = e.pointers[0],
|
|
containerPoint = this._map.mouseEventToContainerPoint(point),
|
|
layerPoint = this._map.containerPointToLayerPoint(containerPoint),
|
|
latlng = this._map.layerPointToLatLng(layerPoint),
|
|
mousePos = this._map._docLayer._latLngToTwips(latlng);
|
|
|
|
let posInTwips = new app.definitions.simplePoint(mousePos.x, mousePos.y);
|
|
|
|
let increaseRatio = 0.40;
|
|
let increasedCellCursor = null;
|
|
if (app.calc.cellCursorVisible) {
|
|
increasedCellCursor = app.calc.cellCursorRectangle.clone();
|
|
increasedCellCursor.y1 -= increasedCellCursor.height * increaseRatio;
|
|
increasedCellCursor.y2 += increasedCellCursor.height * increaseRatio;
|
|
}
|
|
|
|
if (increasedCellCursor && increasedCellCursor.containsPoint(posInTwips.toArray())) {
|
|
if (!app.calc.cellCursorRectangle.containsPoint(posInTwips.toArray())) {
|
|
let y = posInTwips.y;
|
|
let x = posInTwips.x;
|
|
|
|
let heightBuffer = Math.abs(app.calc.cellCursorRectangle.pHeight) * increaseRatio;
|
|
|
|
if (y < app.calc.cellCursorRectangle.y2) {
|
|
y = y + heightBuffer;
|
|
}
|
|
|
|
if (y > app.calc.cellCursorRectangle.y1) {
|
|
y = y - heightBuffer;
|
|
}
|
|
|
|
mousePos = new L.Point(x, y);
|
|
}
|
|
}
|
|
|
|
if (this._state === L.Map.TouchGesture.MARKER) {
|
|
this._map._fireDOMEvent(this._marker, point, 'mousedown');
|
|
} else if (this._state === L.Map.TouchGesture.TABLE) {
|
|
this._map._docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 1, 0);
|
|
} else if (this._state === L.Map.TouchGesture.GRAPHIC) {
|
|
var mouseEvent = this._map._docLayer._createNewMouseEvent('mousedown', point);
|
|
this._map._docLayer._graphicMarker._onDragStart(mouseEvent);
|
|
} else if (this._state === L.Map.TouchGesture.CURSOR) {
|
|
this._map._docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 1, 0);
|
|
} else {
|
|
this._map.dragging._draggable._onDown(this._constructFakeEvent(point, 'mousedown'));
|
|
}
|
|
|
|
// Keep the same keyboard state
|
|
var acceptInput = this._map.canAcceptKeyboardInput();
|
|
this._map.focus(acceptInput);
|
|
},
|
|
|
|
_onPan: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
if (window.IgnorePanning)
|
|
return;
|
|
|
|
if (this._inSwipeAction && Math.abs(e.velocity) < this._hammer.get('swipe').options.velocity) {
|
|
this._cancelAutoscrollRAF();
|
|
}
|
|
|
|
var point = e.pointers[0],
|
|
containerPoint = this._map.mouseEventToContainerPoint(point),
|
|
layerPoint = this._map.containerPointToLayerPoint(containerPoint),
|
|
latlng = this._map.layerPointToLatLng(layerPoint),
|
|
mousePos = this._map._docLayer._latLngToTwips(latlng);
|
|
|
|
if (this._state === L.Map.TouchGesture.MARKER) {
|
|
this._map._fireDOMEvent(this._map, point, 'mousemove');
|
|
this._moving = true;
|
|
} else if (this._state === L.Map.TouchGesture.GRAPHIC) {
|
|
var mouseEvent = this._map._docLayer._createNewMouseEvent('mousemove', point);
|
|
this._map._docLayer._graphicMarker._onDrag(mouseEvent);
|
|
this._moving = true;
|
|
} else if (this._state === L.Map.TouchGesture.TABLE) {
|
|
this._map._docLayer._postMouseEvent('move', mousePos.x, mousePos.y, 1, 1, 0);
|
|
this._moving = true;
|
|
} else if (this._state === L.Map.TouchGesture.CURSOR) {
|
|
this._map._docLayer._postMouseEvent('move', mousePos.x, mousePos.y, 1, 1, 0);
|
|
} else if (this._map.scrollingIsHandled === false) {
|
|
this._map.dragging._draggable._onMove(this._constructFakeEvent(point, 'mousemove'));
|
|
}
|
|
},
|
|
|
|
_onPanEnd: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
if (window.IgnorePanning)
|
|
return;
|
|
|
|
var point = e.pointers[0],
|
|
containerPoint = this._map.mouseEventToContainerPoint(point),
|
|
layerPoint = this._map.containerPointToLayerPoint(containerPoint),
|
|
latlng = this._map.layerPointToLatLng(layerPoint),
|
|
mousePos = this._map._docLayer._latLngToTwips(latlng);
|
|
|
|
if (this._state === L.Map.TouchGesture.MARKER) {
|
|
this._map._fireDOMEvent(this._map, point, 'mouseup');
|
|
this._moving = false;
|
|
} else if (this._state === L.Map.TouchGesture.GRAPHIC) {
|
|
var mouseEvent = this._map._docLayer._createNewMouseEvent('mouseup', point);
|
|
this._map._docLayer._graphicMarker._onDragEnd(mouseEvent);
|
|
this._moving = false;
|
|
} else if (this._state === L.Map.TouchGesture.TABLE) {
|
|
this._map._docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 1, 0);
|
|
this._moving = false;
|
|
} else if (this._state === L.Map.TouchGesture.CURSOR) {
|
|
this._map._docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 1, 0);
|
|
} else {
|
|
this._map.dragging._draggable._onUp(this._constructFakeEvent(point, 'mouseup'));
|
|
}
|
|
|
|
// Keep the same keyboard state
|
|
var acceptInput = this._map.canAcceptKeyboardInput();
|
|
this._map.focus(acceptInput);
|
|
},
|
|
|
|
_onPinchStart: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
if (this._inSwipeAction) {
|
|
this._cancelAutoscrollRAF();
|
|
}
|
|
|
|
if (isNaN(e.center.x) || isNaN(e.center.y))
|
|
return;
|
|
|
|
this._pinchStartCenter = { x: e.center.x, y: e.center.y };
|
|
const _pinchStartLatLng = this._map.mouseEventToLatLng({ clientX: e.center.x, clientY: e.center.y });
|
|
this._map._docLayer.preZoomAnimation(_pinchStartLatLng);
|
|
},
|
|
|
|
_onPinch: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
if (!this._pinchStartCenter || isNaN(e.center.x) || isNaN(e.center.y))
|
|
return;
|
|
|
|
// we need to invert the offset or the map is moved in the opposite direction
|
|
var offset = {x: e.center.x - this._pinchStartCenter.x, y: e.center.y - this._pinchStartCenter.y};
|
|
var center = {x: this._pinchStartCenter.x - offset.x, y: this._pinchStartCenter.y - offset.y};
|
|
this._zoom = this._map._limitZoom(this._map.getScaleZoom(e.scale));
|
|
this._origCenter = this._map.mouseEventToLatLng({clientX: center.x, clientY: center.y});
|
|
|
|
|
|
if (this._map._docLayer.zoomStep) {
|
|
this._map._docLayer.zoomStep(this._zoom, this._origCenter);
|
|
}
|
|
},
|
|
|
|
_onPinchEnd: function () {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
if (!this._pinchStartCenter)
|
|
return;
|
|
|
|
var oldZoom = this._map.getZoom();
|
|
var zoomDelta = this._zoom - oldZoom;
|
|
var finalZoom = this._map._limitZoom(zoomDelta > 0 ? Math.ceil(this._zoom) : Math.floor(this._zoom));
|
|
|
|
this._pinchStartCenter = undefined;
|
|
|
|
if (this._map._docLayer.zoomStepEnd) {
|
|
var thisObj = this;
|
|
this._map._docLayer.zoomStepEnd(finalZoom, this._origCenter,
|
|
function (newMapCenter) { // mapUpdater
|
|
thisObj._map.setView(newMapCenter, finalZoom);
|
|
},
|
|
// showMarkers
|
|
function () {
|
|
thisObj._map._docLayer.postZoomAnimation();
|
|
});
|
|
}
|
|
},
|
|
|
|
_constructFakeEvent: function (evt, type) {
|
|
var fakeEvt = {
|
|
type: type,
|
|
canBubble: false,
|
|
cancelable: true,
|
|
screenX: evt.screenX,
|
|
screenY: evt.screenY,
|
|
clientX: evt.clientX,
|
|
clientY: evt.clientY,
|
|
ctrlKey: false,
|
|
altKey: false,
|
|
shiftKey: false,
|
|
metaKey: false,
|
|
button: 0,
|
|
target: evt.target,
|
|
preventDefault: function () {}
|
|
};
|
|
|
|
return fakeEvt;
|
|
},
|
|
|
|
// Code and maths for the ergonomic scrolling is inspired formul
|
|
// https://ariya.io/2013/11/javascript-kinetic-scrolling-part-2
|
|
// Some constants are changed based on the testing/experimenting/trial-error
|
|
|
|
_onSwipe: function (e) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
let velocityX = this._map._docLayer.isCalcRTL() ? -e.velocityX : e.velocityX;
|
|
let pointVelocity = new L.Point(velocityX, e.velocityY);
|
|
if (this._inSwipeAction) {
|
|
this._velocity = this._velocity.add(pointVelocity);
|
|
}
|
|
else {
|
|
this._velocity = pointVelocity;
|
|
}
|
|
this._amplitude = this._velocity.multiplyBy(32);
|
|
this._newPos = L.DomUtil.getPosition(this._map._mapPane);
|
|
var evt = this._constructFakeEvent({
|
|
clientX: e.center.x,
|
|
clientY: e.center.y,
|
|
target: this._map._mapPane
|
|
},'mousedown');
|
|
this._startSwipePoint = new L.Point(evt.clientX, evt.clientY);
|
|
this._map.dragging._draggable._onDown(evt);
|
|
this._timeStamp = Date.now();
|
|
this._inSwipeAction = true;
|
|
this.autoscrollAnimReq = L.Util.requestAnimFrame(this._autoscroll, this, true);
|
|
},
|
|
|
|
_cancelAutoscrollRAF: function () {
|
|
this._cancelAutoScroll = false;
|
|
this._inSwipeAction = false;
|
|
if (app.file.fileBasedView)
|
|
this._map._docLayer._checkSelectedPart();
|
|
L.Util.cancelAnimFrame(this.autoscrollAnimReq);
|
|
return;
|
|
},
|
|
|
|
_autoscroll: function() {
|
|
if (this._cancelAutoScroll === true) {
|
|
this._cancelAutoscrollRAF();
|
|
return;
|
|
}
|
|
|
|
var elapsed, delta;
|
|
|
|
elapsed = Date.now() - this._timeStamp;
|
|
delta = this._amplitude.multiplyBy(Math.exp(-elapsed / 650));
|
|
var e = this._constructFakeEvent({
|
|
clientX: delta.x + this._startSwipePoint.x,
|
|
clientY: delta.y + this._startSwipePoint.y,
|
|
target: this._map._mapPane,
|
|
}, 'mousemove');
|
|
e.autoscroll = true;
|
|
if (delta.x > 0.2 || delta.x < -0.2 || delta.y > 0.2 || delta.y < -0.2) {
|
|
var org = this._map.getPixelOrigin();
|
|
var docSize = this._map.getLayerMaxBounds().getSize().subtract(this._map.getSize());
|
|
var horizontalEnd, verticalEnd;
|
|
|
|
if (this._map.getDocSize().x < this._map.getSize().x) {
|
|
//don't scroll horizontally if document fits the view
|
|
delta.x = 0;
|
|
horizontalEnd = true;
|
|
} else {
|
|
horizontalEnd = Math.max(Math.min(org.x, this._newPos.x), org.x - Math.max(docSize.x, 0)) !== this._newPos.x;
|
|
}
|
|
|
|
if (this._map.getDocSize().y < this._map.getSize().y) {
|
|
//don't scroll vertically if document fits the view
|
|
delta.y = 0;
|
|
verticalEnd = true;
|
|
} else {
|
|
verticalEnd = Math.max(Math.min(org.y, this._newPos.y), org.y - Math.max(docSize.y, 0)) !== this._newPos.y;
|
|
}
|
|
|
|
this._map.dragging._draggable._startPoint = this._startSwipePoint;
|
|
this._map.dragging._draggable._startPos = this._newPos;
|
|
this._newPos._add(delta);
|
|
|
|
this._map.dragging._draggable._onMove(e);
|
|
|
|
// Prefetch border tiles for the current visible area after cancelling any scheduled calls to the prefetcher.
|
|
this._map._docLayer._clearPreFetch();
|
|
this._map._docLayer._preFetchTiles(true /* forceBorderCalc */);
|
|
|
|
if (!horizontalEnd || !verticalEnd) {
|
|
this.autoscrollAnimReq = L.Util.requestAnimFrame(this._autoscroll, this, true);
|
|
} else {
|
|
this._inSwipeAction = false;
|
|
if (app.file.fileBasedView)
|
|
this._map._docLayer._checkSelectedPart();
|
|
}
|
|
}
|
|
else {
|
|
this._map.dragging._draggable._onUp(e);
|
|
this._inSwipeAction = false;
|
|
if (app.file.fileBasedView)
|
|
this._map._docLayer._checkSelectedPart();
|
|
}
|
|
}
|
|
});
|