collabora-online/browser/src/map/handler/Map.Mouse.js

256 lines
8.5 KiB
JavaScript

/* -*- js-indent-level: 8 -*- */
/*
* L.Map.Mouse is handling mouse interaction with the document
*/
/* global UNOModifier app */
L.Map.mergeOptions({
mouse: true
});
L.Map.Mouse = L.Handler.extend({
initialize: function (map) {
this._map = map;
this._mouseEventsQueue = [];
this._prevMousePos = null;
},
addHooks: function () {
this._map.on('mousedown mouseup mouseover mouseout mousemove dblclick trplclick qdrplclick',
this._onMouseEvent, this);
},
removeHooks: function () {
this._map.off('mousedown mouseup mouseover mouseout mousemove dblclick trplclick qdrplclick',
this._onMouseEvent, this);
},
LOButtons: {
left: 1,
middle: 2,
right: 4
},
JSButtons: {
left: 0,
middle: 1,
right: 2
},
_onMouseEvent: window.touch.mouseOnly(function (e) {
if (this._map.uiManager.isUIBlocked())
return;
app.idleHandler.notifyActive();
var docLayer = this._map._docLayer;
if (!docLayer || (this._map.slideShow && this._map.slideShow.fullscreen) || this._map.rulerActive) {
return;
}
if (docLayer._graphicMarker) {
if (docLayer._graphicMarker.isDragged) {
return;
}
if (!docLayer._isEmptyRectangle(docLayer._graphicSelection)) {
// if we have a graphic selection and the user clicks inside the rectangle
var isInside = docLayer._graphicMarker.getBounds().contains(e.latlng);
if (e.type === 'mousedown' && isInside) {
this._prevMousePos = e.latlng;
}
else if (e.type === 'mousemove' && this._mouseDown) {
if (!this._prevMousePos && isInside) {
// if the user started to drag the shape before the selection
// has been drawn
this._prevMousePos = e.latlng;
}
else {
this._prevMousePos = e.latlng;
}
}
else if (e.type === 'mouseup') {
this._prevMousePos = null;
}
}
}
var modifier = 0;
var shift = e.originalEvent.shiftKey ? UNOModifier.SHIFT : 0;
var ctrl = e.originalEvent.ctrlKey ? UNOModifier.CTRL : 0;
var alt = e.originalEvent.altKey ? UNOModifier.ALT : 0;
var cmd = e.originalEvent.metaKey ? UNOModifier.CTRLMAC : 0;
modifier = shift | ctrl | alt | cmd;
var buttons = 0;
buttons |= e.originalEvent.button === this.JSButtons.left ? this.LOButtons.left : 0;
buttons |= e.originalEvent.button === this.JSButtons.middle ? this.LOButtons.middle : 0;
buttons |= e.originalEvent.button === this.JSButtons.right ? this.LOButtons.right : 0;
// Turn ctrl-left-click into right-click for browsers on macOS
if (L.Browser.mac) {
if (modifier == UNOModifier.CTRL && buttons == this.LOButtons.left) {
modifier = 0;
buttons = this.LOButtons.right;
}
}
var mouseEnteringLeavingMap = this._map._mouseEnteringLeaving;
if (mouseEnteringLeavingMap && e.type === 'mouseover' && this._mouseDown) {
L.DomEvent.off(document, 'mousemove', this._onMouseMoveOutside, this);
L.DomEvent.off(document, 'mouseup', this._onMouseUpOutside, this);
if (this._map._resizeDetector) {
L.DomEvent.off(this._map._resizeDetector.contentWindow, 'mousemove', this._onMouseMoveOutside, this);
L.DomEvent.off(this._map._resizeDetector.contentWindow, 'mouseup', this._onMouseUpOutside, this);
}
}
else if (e.type === 'mousedown') {
docLayer._resetPreFetching();
this._mouseDown = true;
this._buttonDown = buttons;
if (this._holdMouseEvent) {
clearTimeout(this._holdMouseEvent);
}
var mousePos = docLayer._latLngToTwips(e.latlng);
this._mouseEventsQueue.push(L.bind(function() {
this._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, buttons, modifier);
}, docLayer));
this._holdMouseEvent = setTimeout(L.bind(this._executeMouseEvents, this), 500);
}
else if (e.type === 'mouseup') {
this._mouseDown = false;
if (this._map.dragging.enabled()) {
if (this._mouseEventsQueue.length === 0) {
// mouse up after panning
return;
}
}
// Core side is handling the mouseup by itself when the right button is down.
// If we fire mouseup for right button, there will be duplicate.
// Without this, selected text in a text box is un-selected via a right click. Therefore, copy / cut operations are disabled.
if (this._buttonDown === this.LOButtons.right && modifier === 0)
return;
clearTimeout(this._holdMouseEvent);
this._holdMouseEvent = null;
var timeDiff = Date.now() - this._clickTime;
if (this._clickTime && timeDiff > 1 && timeDiff <= 250) {
// double click, a click was sent already
this._mouseEventsQueue = [];
this._clickCount++;
if (this._clickCount < 4) {
// Reset the timer in order to keep resetting until
// we could have sent through a quadruple click. After this revert
// to normal behaviour so that a following single-click is treated
// as a separate click, in order to match LO desktop behaviour.
// (Clicking five times results in paragraph selection after 4 clicks,
// followed by resetting to a single cursor and no selection on the
// fifth click.)
this._clickTime = Date.now();
}
return;
}
else {
this._clickTime = Date.now();
this._clickCount = 1;
mousePos = docLayer._latLngToTwips(e.latlng);
var timeOut = 0;
this._mouseEventsQueue.push(L.bind(function() {
var docLayer = this._map._docLayer;
this._mouseEventsQueue = [];
docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, buttons, modifier);
this._map.focus();
}, this));
this._holdMouseEvent = setTimeout(L.bind(this._executeMouseEvents, this), timeOut);
}
this._map.fire('scrollvelocity', {vx: 0, vy: 0});
}
else if (e.type === 'mousemove' && this._mouseDown) {
if (this._mouseOverTimeout)
clearTimeout(this._mouseOverTimeout);
if (this._holdMouseEvent) {
clearTimeout(this._holdMouseEvent);
this._holdMouseEvent = null;
if (this._map.dragging.enabled()) {
// The user just panned the document
this._mouseEventsQueue = [];
return;
}
for (var i = 0; i < this._mouseEventsQueue.length; i++) {
// synchronously execute old mouse events so we know that
// they arrive to the server before the move command
this._mouseEventsQueue[i]();
}
this._mouseEventsQueue = [];
}
if (!this._map.dragging.enabled()) {
mousePos = docLayer._latLngToTwips(e.latlng);
docLayer._postMouseEvent('move', mousePos.x, mousePos.y, 1, buttons, modifier);
this._map.fire('handleautoscroll', {pos: e.containerPoint, map: this._map});
}
}
else if (e.type === 'mousemove' && !this._mouseDown) {
clearTimeout(this._mouseOverTimeout);
mousePos = docLayer._latLngToTwips(e.latlng);
this._mouseOverTimeout = setTimeout(L.bind(function() {
docLayer._postMouseEvent('move', mousePos.x, mousePos.y, 1, 0, modifier);
}, this),
100);
}
else if (e.type === 'dblclick' || e.type === 'trplclick' || e.type === 'qdrplclick') {
mousePos = docLayer._latLngToTwips(e.latlng);
var clicks = {
dblclick: 2,
trplclick: 3,
qdrplclick: 4
};
var count = clicks[e.type];
docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, count, buttons, modifier);
docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, count, buttons, modifier);
}
else if (mouseEnteringLeavingMap && e.type === 'mouseout' && this._mouseDown) {
if (this._map._resizeDetector) {
L.DomEvent.on(this._map._resizeDetector.contentWindow, 'mousemove', this._onMouseMoveOutside, this);
L.DomEvent.on(this._map._resizeDetector.contentWindow, 'mouseup', this._onMouseUpOutside, this);
}
L.DomEvent.on(document, 'mousemove', this._onMouseMoveOutside, this);
L.DomEvent.on(document, 'mouseup', this._onMouseUpOutside, this);
}
}),
_executeMouseEvents: function () {
this._holdMouseEvent = null;
for (var i = 0; i < this._mouseEventsQueue.length; i++) {
this._mouseEventsQueue[i]();
}
this._mouseEventsQueue = [];
},
_onMouseMoveOutside: window.touch.mouseOnly(function (e) {
this._map._handleDOMEvent(e);
if (this._map.dragging.enabled()) {
this._map.dragging._draggable._onMove(e);
}
}),
_onMouseUpOutside: window.touch.mouseOnly(function (e) {
this._mouseDown = false;
L.DomEvent.off(document, 'mousemove', this._onMouseMoveOutside, this);
L.DomEvent.off(document, 'mouseup', this._onMouseUpOutside, this);
if (this._map._resizeDetector) {
L.DomEvent.off(this._map._resizeDetector.contentWindow, 'mousemove', this._onMouseMoveOutside, this);
L.DomEvent.off(this._map._resizeDetector.contentWindow, 'mouseup', this._onMouseUpOutside, this);
}
this._map._handleDOMEvent(e);
if (this._map.dragging.enabled()) {
this._map.dragging._draggable._onUp(e);
}
})
});