2018-08-14 12:11:49 +00:00
|
|
|
/*
|
|
|
|
* noVNC: HTML5 VNC client
|
2019-06-24 11:26:25 +00:00
|
|
|
* Copyright (C) 2018 The noVNC Authors
|
2018-08-14 12:11:49 +00:00
|
|
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
|
|
|
*
|
|
|
|
* See README.md for usage and integration instructions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cross-browser event and position routines
|
|
|
|
*/
|
|
|
|
|
2019-06-24 11:26:25 +00:00
|
|
|
export function getPointerEvent(e) {
|
2018-08-14 12:11:49 +00:00
|
|
|
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
|
2019-06-24 11:26:25 +00:00
|
|
|
}
|
2018-08-14 12:11:49 +00:00
|
|
|
|
2019-06-24 11:26:25 +00:00
|
|
|
export function stopEvent(e) {
|
2018-08-14 12:11:49 +00:00
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
2019-06-24 11:26:25 +00:00
|
|
|
}
|
2018-08-14 12:11:49 +00:00
|
|
|
|
|
|
|
// Emulate Element.setCapture() when not supported
|
2019-06-24 11:26:25 +00:00
|
|
|
let _captureRecursion = false;
|
|
|
|
let _captureElem = null;
|
2018-08-14 12:11:49 +00:00
|
|
|
function _captureProxy(e) {
|
|
|
|
// Recursion protection as we'll see our own event
|
|
|
|
if (_captureRecursion) return;
|
|
|
|
|
|
|
|
// Clone the event as we cannot dispatch an already dispatched event
|
2019-06-24 11:26:25 +00:00
|
|
|
const newEv = new e.constructor(e.type, e);
|
2018-08-14 12:11:49 +00:00
|
|
|
|
|
|
|
_captureRecursion = true;
|
|
|
|
_captureElem.dispatchEvent(newEv);
|
|
|
|
_captureRecursion = false;
|
|
|
|
|
|
|
|
// Avoid double events
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
// Respect the wishes of the redirected event handlers
|
|
|
|
if (newEv.defaultPrevented) {
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implicitly release the capture on button release
|
|
|
|
if (e.type === "mouseup") {
|
|
|
|
releaseCapture();
|
|
|
|
}
|
2019-06-24 11:26:25 +00:00
|
|
|
}
|
2018-08-14 12:11:49 +00:00
|
|
|
|
|
|
|
// Follow cursor style of target element
|
|
|
|
function _captureElemChanged() {
|
2019-06-24 11:26:25 +00:00
|
|
|
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
2018-08-14 12:11:49 +00:00
|
|
|
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
|
2019-06-24 11:26:25 +00:00
|
|
|
}
|
2018-08-14 12:11:49 +00:00
|
|
|
|
2019-06-24 11:26:25 +00:00
|
|
|
const _captureObserver = new MutationObserver(_captureElemChanged);
|
2018-08-14 12:11:49 +00:00
|
|
|
|
2019-06-24 11:26:25 +00:00
|
|
|
let _captureIndex = 0;
|
|
|
|
|
|
|
|
export function setCapture(elem) {
|
2018-08-14 12:11:49 +00:00
|
|
|
if (elem.setCapture) {
|
|
|
|
|
|
|
|
elem.setCapture();
|
|
|
|
|
|
|
|
// IE releases capture on 'click' events which might not trigger
|
|
|
|
elem.addEventListener('mouseup', releaseCapture);
|
2018-08-27 12:30:50 +00:00
|
|
|
|
2018-08-14 12:11:49 +00:00
|
|
|
} else {
|
|
|
|
// Release any existing capture in case this method is
|
|
|
|
// called multiple times without coordination
|
|
|
|
releaseCapture();
|
|
|
|
|
2019-06-24 11:26:25 +00:00
|
|
|
let captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
2018-08-14 12:11:49 +00:00
|
|
|
|
|
|
|
if (captureElem === null) {
|
|
|
|
captureElem = document.createElement("div");
|
|
|
|
captureElem.id = "noVNC_mouse_capture_elem";
|
|
|
|
captureElem.style.position = "fixed";
|
|
|
|
captureElem.style.top = "0px";
|
|
|
|
captureElem.style.left = "0px";
|
|
|
|
captureElem.style.width = "100%";
|
|
|
|
captureElem.style.height = "100%";
|
|
|
|
captureElem.style.zIndex = 10000;
|
|
|
|
captureElem.style.display = "none";
|
|
|
|
document.body.appendChild(captureElem);
|
|
|
|
|
|
|
|
// This is to make sure callers don't get confused by having
|
|
|
|
// our blocking element as the target
|
|
|
|
captureElem.addEventListener('contextmenu', _captureProxy);
|
|
|
|
|
|
|
|
captureElem.addEventListener('mousemove', _captureProxy);
|
|
|
|
captureElem.addEventListener('mouseup', _captureProxy);
|
|
|
|
}
|
|
|
|
|
|
|
|
_captureElem = elem;
|
|
|
|
_captureIndex++;
|
|
|
|
|
|
|
|
// Track cursor and get initial cursor
|
2019-06-24 11:26:25 +00:00
|
|
|
_captureObserver.observe(elem, {attributes: true});
|
2018-08-14 12:11:49 +00:00
|
|
|
_captureElemChanged();
|
|
|
|
|
|
|
|
captureElem.style.display = "";
|
|
|
|
|
|
|
|
// We listen to events on window in order to keep tracking if it
|
|
|
|
// happens to leave the viewport
|
|
|
|
window.addEventListener('mousemove', _captureProxy);
|
|
|
|
window.addEventListener('mouseup', _captureProxy);
|
|
|
|
}
|
2019-06-24 11:26:25 +00:00
|
|
|
}
|
2018-08-14 12:11:49 +00:00
|
|
|
|
2019-06-24 11:26:25 +00:00
|
|
|
export function releaseCapture() {
|
2018-08-14 12:11:49 +00:00
|
|
|
if (document.releaseCapture) {
|
|
|
|
|
|
|
|
document.releaseCapture();
|
2018-08-27 12:30:50 +00:00
|
|
|
|
2018-08-14 12:11:49 +00:00
|
|
|
} else {
|
|
|
|
if (!_captureElem) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// There might be events already queued, so we need to wait for
|
|
|
|
// them to flush. E.g. contextmenu in Microsoft Edge
|
2019-06-24 11:26:25 +00:00
|
|
|
window.setTimeout((expected) => {
|
2018-08-14 12:11:49 +00:00
|
|
|
// Only clear it if it's the expected grab (i.e. no one
|
|
|
|
// else has initiated a new grab)
|
|
|
|
if (_captureIndex === expected) {
|
|
|
|
_captureElem = null;
|
|
|
|
}
|
|
|
|
}, 0, _captureIndex);
|
|
|
|
|
|
|
|
_captureObserver.disconnect();
|
|
|
|
|
2019-06-24 11:26:25 +00:00
|
|
|
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
2018-08-14 12:11:49 +00:00
|
|
|
captureElem.style.display = "none";
|
|
|
|
|
|
|
|
window.removeEventListener('mousemove', _captureProxy);
|
|
|
|
window.removeEventListener('mouseup', _captureProxy);
|
|
|
|
}
|
2019-06-24 11:26:25 +00:00
|
|
|
}
|