]>
Commit | Line | Data |
---|---|---|
6d6f0db0 SR |
1 | /* |
2 | * noVNC: HTML5 VNC client | |
84586c0f | 3 | * Copyright (C) 2018 The noVNC Authors |
6d6f0db0 SR |
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 | ||
2c5491e1 | 13 | export function getPointerEvent(e) { |
6d6f0db0 | 14 | return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e; |
8727f598 | 15 | } |
6d6f0db0 | 16 | |
2c5491e1 | 17 | export function stopEvent(e) { |
6d6f0db0 SR |
18 | e.stopPropagation(); |
19 | e.preventDefault(); | |
8727f598 | 20 | } |
6d6f0db0 SR |
21 | |
22 | // Emulate Element.setCapture() when not supported | |
2b5f94fa | 23 | let _captureRecursion = false; |
7a96fc37 | 24 | let _elementForUnflushedEvents = null; |
0c4b3e80 | 25 | document.captureElement = null; |
858ea4a7 | 26 | function _captureProxy(e) { |
6d6f0db0 SR |
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 | |
2b5f94fa | 31 | const newEv = new e.constructor(e.type, e); |
6d6f0db0 SR |
32 | |
33 | _captureRecursion = true; | |
0c4b3e80 SM |
34 | if (document.captureElement) { |
35 | document.captureElement.dispatchEvent(newEv); | |
7a96fc37 SM |
36 | } else { |
37 | _elementForUnflushedEvents.dispatchEvent(newEv); | |
38 | } | |
6d6f0db0 SR |
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 | |
333ad45c | 50 | if (e.type === "mouseup") { |
6d6f0db0 SR |
51 | releaseCapture(); |
52 | } | |
8727f598 | 53 | } |
6d6f0db0 SR |
54 | |
55 | // Follow cursor style of target element | |
fcd99d04 SM |
56 | function _capturedElemChanged() { |
57 | const proxyElem = document.getElementById("noVNC_mouse_capture_elem"); | |
0c4b3e80 | 58 | proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor; |
8727f598 | 59 | } |
6d6f0db0 | 60 | |
fcd99d04 | 61 | const _captureObserver = new MutationObserver(_capturedElemChanged); |
2b5f94fa | 62 | |
fcd99d04 SM |
63 | export function setCapture(target) { |
64 | if (target.setCapture) { | |
6d6f0db0 | 65 | |
fcd99d04 | 66 | target.setCapture(); |
0c4b3e80 | 67 | document.captureElement = target; |
6d6f0db0 SR |
68 | } else { |
69 | // Release any existing capture in case this method is | |
70 | // called multiple times without coordination | |
71 | releaseCapture(); | |
72 | ||
fcd99d04 | 73 | let proxyElem = document.getElementById("noVNC_mouse_capture_elem"); |
6d6f0db0 | 74 | |
fcd99d04 SM |
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); | |
6d6f0db0 SR |
86 | |
87 | // This is to make sure callers don't get confused by having | |
88 | // our blocking element as the target | |
fcd99d04 | 89 | proxyElem.addEventListener('contextmenu', _captureProxy); |
6d6f0db0 | 90 | |
fcd99d04 SM |
91 | proxyElem.addEventListener('mousemove', _captureProxy); |
92 | proxyElem.addEventListener('mouseup', _captureProxy); | |
6d6f0db0 SR |
93 | } |
94 | ||
0c4b3e80 | 95 | document.captureElement = target; |
6d6f0db0 SR |
96 | |
97 | // Track cursor and get initial cursor | |
fcd99d04 SM |
98 | _captureObserver.observe(target, {attributes: true}); |
99 | _capturedElemChanged(); | |
6d6f0db0 | 100 | |
fcd99d04 | 101 | proxyElem.style.display = ""; |
6d6f0db0 SR |
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); | |
6d6f0db0 | 107 | } |
8727f598 | 108 | } |
6d6f0db0 | 109 | |
2c5491e1 | 110 | export function releaseCapture() { |
6d6f0db0 SR |
111 | if (document.releaseCapture) { |
112 | ||
113 | document.releaseCapture(); | |
0c4b3e80 | 114 | document.captureElement = null; |
6d6f0db0 SR |
115 | |
116 | } else { | |
0c4b3e80 | 117 | if (!document.captureElement) { |
6d6f0db0 SR |
118 | return; |
119 | } | |
120 | ||
7a96fc37 SM |
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. | |
0c4b3e80 SM |
127 | _elementForUnflushedEvents = document.captureElement; |
128 | document.captureElement = null; | |
6d6f0db0 SR |
129 | |
130 | _captureObserver.disconnect(); | |
131 | ||
fcd99d04 SM |
132 | const proxyElem = document.getElementById("noVNC_mouse_capture_elem"); |
133 | proxyElem.style.display = "none"; | |
6d6f0db0 SR |
134 | |
135 | window.removeEventListener('mousemove', _captureProxy); | |
136 | window.removeEventListener('mouseup', _captureProxy); | |
6d6f0db0 | 137 | } |
8727f598 | 138 | } |