]>
Commit | Line | Data |
---|---|---|
6d6f0db0 SR |
1 | /* |
2 | * noVNC: HTML5 VNC client | |
3 | * Copyright (C) 2012 Joel Martin | |
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 | ||
6d6f0db0 SR |
13 | export function getPointerEvent (e) { |
14 | return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e; | |
8727f598 | 15 | } |
6d6f0db0 SR |
16 | |
17 | export function stopEvent (e) { | |
18 | e.stopPropagation(); | |
19 | e.preventDefault(); | |
8727f598 | 20 | } |
6d6f0db0 SR |
21 | |
22 | // Emulate Element.setCapture() when not supported | |
2b5f94fa JD |
23 | let _captureRecursion = false; |
24 | let _captureElem = null; | |
858ea4a7 | 25 | function _captureProxy(e) { |
6d6f0db0 SR |
26 | // Recursion protection as we'll see our own event |
27 | if (_captureRecursion) return; | |
28 | ||
29 | // Clone the event as we cannot dispatch an already dispatched event | |
2b5f94fa | 30 | const newEv = new e.constructor(e.type, e); |
6d6f0db0 SR |
31 | |
32 | _captureRecursion = true; | |
33 | _captureElem.dispatchEvent(newEv); | |
34 | _captureRecursion = false; | |
35 | ||
36 | // Avoid double events | |
37 | e.stopPropagation(); | |
38 | ||
39 | // Respect the wishes of the redirected event handlers | |
40 | if (newEv.defaultPrevented) { | |
41 | e.preventDefault(); | |
42 | } | |
43 | ||
44 | // Implicitly release the capture on button release | |
333ad45c | 45 | if (e.type === "mouseup") { |
6d6f0db0 SR |
46 | releaseCapture(); |
47 | } | |
8727f598 | 48 | } |
6d6f0db0 SR |
49 | |
50 | // Follow cursor style of target element | |
858ea4a7 | 51 | function _captureElemChanged() { |
2b5f94fa | 52 | const captureElem = document.getElementById("noVNC_mouse_capture_elem"); |
6d6f0db0 | 53 | captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor; |
8727f598 | 54 | } |
6d6f0db0 | 55 | |
2b5f94fa JD |
56 | const _captureObserver = new MutationObserver(_captureElemChanged); |
57 | ||
58 | let _captureIndex = 0; | |
6d6f0db0 SR |
59 | |
60 | export function setCapture (elem) { | |
61 | if (elem.setCapture) { | |
62 | ||
63 | elem.setCapture(); | |
64 | ||
65 | // IE releases capture on 'click' events which might not trigger | |
66 | elem.addEventListener('mouseup', releaseCapture); | |
6d6f0db0 SR |
67 | |
68 | } else { | |
69 | // Release any existing capture in case this method is | |
70 | // called multiple times without coordination | |
71 | releaseCapture(); | |
72 | ||
2b5f94fa | 73 | let captureElem = document.getElementById("noVNC_mouse_capture_elem"); |
6d6f0db0 SR |
74 | |
75 | if (captureElem === null) { | |
76 | captureElem = document.createElement("div"); | |
77 | captureElem.id = "noVNC_mouse_capture_elem"; | |
78 | captureElem.style.position = "fixed"; | |
79 | captureElem.style.top = "0px"; | |
80 | captureElem.style.left = "0px"; | |
81 | captureElem.style.width = "100%"; | |
82 | captureElem.style.height = "100%"; | |
83 | captureElem.style.zIndex = 10000; | |
84 | captureElem.style.display = "none"; | |
85 | document.body.appendChild(captureElem); | |
86 | ||
87 | // This is to make sure callers don't get confused by having | |
88 | // our blocking element as the target | |
89 | captureElem.addEventListener('contextmenu', _captureProxy); | |
90 | ||
91 | captureElem.addEventListener('mousemove', _captureProxy); | |
92 | captureElem.addEventListener('mouseup', _captureProxy); | |
6d6f0db0 SR |
93 | } |
94 | ||
95 | _captureElem = elem; | |
96 | _captureIndex++; | |
97 | ||
98 | // Track cursor and get initial cursor | |
99 | _captureObserver.observe(elem, {attributes:true}); | |
100 | _captureElemChanged(); | |
101 | ||
41c958d4 | 102 | captureElem.style.display = ""; |
6d6f0db0 SR |
103 | |
104 | // We listen to events on window in order to keep tracking if it | |
105 | // happens to leave the viewport | |
106 | window.addEventListener('mousemove', _captureProxy); | |
107 | window.addEventListener('mouseup', _captureProxy); | |
6d6f0db0 | 108 | } |
8727f598 | 109 | } |
6d6f0db0 SR |
110 | |
111 | export function releaseCapture () { | |
112 | if (document.releaseCapture) { | |
113 | ||
114 | document.releaseCapture(); | |
115 | ||
116 | } else { | |
117 | if (!_captureElem) { | |
118 | return; | |
119 | } | |
120 | ||
121 | // There might be events already queued, so we need to wait for | |
122 | // them to flush. E.g. contextmenu in Microsoft Edge | |
123 | window.setTimeout(function(expected) { | |
124 | // Only clear it if it's the expected grab (i.e. no one | |
125 | // else has initiated a new grab) | |
126 | if (_captureIndex === expected) { | |
127 | _captureElem = null; | |
128 | } | |
129 | }, 0, _captureIndex); | |
130 | ||
131 | _captureObserver.disconnect(); | |
132 | ||
2b5f94fa | 133 | const captureElem = document.getElementById("noVNC_mouse_capture_elem"); |
6d6f0db0 SR |
134 | captureElem.style.display = "none"; |
135 | ||
136 | window.removeEventListener('mousemove', _captureProxy); | |
137 | window.removeEventListener('mouseup', _captureProxy); | |
6d6f0db0 | 138 | } |
8727f598 | 139 | } |