]>
Commit | Line | Data |
---|---|---|
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 | ||
13 | export function getPointerEvent(e) { | |
14 | return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e; | |
15 | } | |
16 | ||
17 | export function stopEvent(e) { | |
18 | e.stopPropagation(); | |
19 | e.preventDefault(); | |
20 | } | |
21 | ||
22 | // Emulate Element.setCapture() when not supported | |
23 | let _captureRecursion = false; | |
24 | let _elementForUnflushedEvents = null; | |
25 | document.captureElement = null; | |
26 | function _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 | |
56 | function _capturedElemChanged() { | |
57 | const proxyElem = document.getElementById("noVNC_mouse_capture_elem"); | |
58 | proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor; | |
59 | } | |
60 | ||
61 | const _captureObserver = new MutationObserver(_capturedElemChanged); | |
62 | ||
63 | export 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 | ||
110 | export 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 | } |