main
Raw Download raw file
  1/*
  2 * noVNC: HTML5 VNC client
  3 * Copyright (C) 2018 The noVNC authors
  4 * Licensed under MPL 2.0 (see LICENSE.txt)
  5 *
  6 * See README.md for usage and integration instructions.
  7 */
  8
  9/*
 10 * Cross-browser event and position routines
 11 */
 12
 13export function getPointerEvent(e) {
 14    return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
 15}
 16
 17export function stopEvent(e) {
 18    e.stopPropagation();
 19    e.preventDefault();
 20}
 21
 22// Emulate Element.setCapture() when not supported
 23let _captureRecursion = false;
 24let _elementForUnflushedEvents = null;
 25document.captureElement = null;
 26function _captureProxy(e) {
 27    // Recursion protection as we'll see our own event
 28    if (_captureRecursion) return;
 29
 30    // Clone the event as we cannot dispatch an already dispatched event
 31    const newEv = new e.constructor(e.type, e);
 32
 33    _captureRecursion = true;
 34    if (document.captureElement) {
 35        document.captureElement.dispatchEvent(newEv);
 36    } else {
 37        _elementForUnflushedEvents.dispatchEvent(newEv);
 38    }
 39    _captureRecursion = false;
 40
 41    // Avoid double events
 42    e.stopPropagation();
 43
 44    // Respect the wishes of the redirected event handlers
 45    if (newEv.defaultPrevented) {
 46        e.preventDefault();
 47    }
 48
 49    // Implicitly release the capture on button release
 50    if (e.type === "mouseup") {
 51        releaseCapture();
 52    }
 53}
 54
 55// Follow cursor style of target element
 56function _capturedElemChanged() {
 57    const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
 58    proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor;
 59}
 60
 61const _captureObserver = new MutationObserver(_capturedElemChanged);
 62
 63export function setCapture(target) {
 64    if (target.setCapture) {
 65
 66        target.setCapture();
 67        document.captureElement = target;
 68    } else {
 69        // Release any existing capture in case this method is
 70        // called multiple times without coordination
 71        releaseCapture();
 72
 73        let proxyElem = document.getElementById("noVNC_mouse_capture_elem");
 74
 75        if (proxyElem === null) {
 76            proxyElem = document.createElement("div");
 77            proxyElem.id = "noVNC_mouse_capture_elem";
 78            proxyElem.style.position = "fixed";
 79            proxyElem.style.top = "0px";
 80            proxyElem.style.left = "0px";
 81            proxyElem.style.width = "100%";
 82            proxyElem.style.height = "100%";
 83            proxyElem.style.zIndex = 10000;
 84            proxyElem.style.display = "none";
 85            document.body.appendChild(proxyElem);
 86
 87            // This is to make sure callers don't get confused by having
 88            // our blocking element as the target
 89            proxyElem.addEventListener('contextmenu', _captureProxy);
 90
 91            proxyElem.addEventListener('mousemove', _captureProxy);
 92            proxyElem.addEventListener('mouseup', _captureProxy);
 93        }
 94
 95        document.captureElement = target;
 96
 97        // Track cursor and get initial cursor
 98        _captureObserver.observe(target, {attributes: true});
 99        _capturedElemChanged();
100
101        proxyElem.style.display = "";
102
103        // We listen to events on window in order to keep tracking if it
104        // happens to leave the viewport
105        window.addEventListener('mousemove', _captureProxy);
106        window.addEventListener('mouseup', _captureProxy);
107    }
108}
109
110export function releaseCapture() {
111    if (document.releaseCapture) {
112
113        document.releaseCapture();
114        document.captureElement = null;
115
116    } else {
117        if (!document.captureElement) {
118            return;
119        }
120
121        // There might be events already queued. The event proxy needs
122        // access to the captured element for these queued events.
123        // E.g. contextmenu (right-click) in Microsoft Edge
124        //
125        // Before removing the capturedElem pointer we save it to a
126        // temporary variable that the unflushed events can use.
127        _elementForUnflushedEvents = document.captureElement;
128        document.captureElement = null;
129
130        _captureObserver.disconnect();
131
132        const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
133        proxyElem.style.display = "none";
134
135        window.removeEventListener('mousemove', _captureProxy);
136        window.removeEventListener('mouseup', _captureProxy);
137    }
138}