]> git.proxmox.com Git - mirror_novnc.git/blob - core/util/events.js
8fdf6aa6f69e4097f3a14c29a1c6714e9422024b
[mirror_novnc.git] / core / util / events.js
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
13 import * as Log from './logging.js';
14
15 export function getPointerEvent (e) {
16 return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
17 };
18
19 export function stopEvent (e) {
20 e.stopPropagation();
21 e.preventDefault();
22 };
23
24 // Emulate Element.setCapture() when not supported
25 var _captureRecursion = false;
26 var _captureElem = null;
27 const _captureProxy = function (e) {
28 // Recursion protection as we'll see our own event
29 if (_captureRecursion) return;
30
31 // Clone the event as we cannot dispatch an already dispatched event
32 var newEv = new e.constructor(e.type, e);
33
34 _captureRecursion = true;
35 _captureElem.dispatchEvent(newEv);
36 _captureRecursion = false;
37
38 // Avoid double events
39 e.stopPropagation();
40
41 // Respect the wishes of the redirected event handlers
42 if (newEv.defaultPrevented) {
43 e.preventDefault();
44 }
45
46 // Implicitly release the capture on button release
47 if ((e.type === "mouseup") || (e.type === "touchend")) {
48 releaseCapture();
49 }
50 };
51
52 // Follow cursor style of target element
53 const _captureElemChanged = function() {
54 var captureElem = document.getElementById("noVNC_mouse_capture_elem");
55 captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
56 };
57 const _captureObserver = new MutationObserver(_captureElemChanged);
58
59 var _captureIndex = 0;
60
61 export function setCapture (elem) {
62 if (elem.setCapture) {
63
64 elem.setCapture();
65
66 // IE releases capture on 'click' events which might not trigger
67 elem.addEventListener('mouseup', releaseCapture);
68 elem.addEventListener('touchend', releaseCapture);
69
70 } else {
71 // Release any existing capture in case this method is
72 // called multiple times without coordination
73 releaseCapture();
74
75 // Safari on iOS 9 has a broken constructor for TouchEvent.
76 // We are fine in this case however, since Safari seems to
77 // have some sort of implicit setCapture magic anyway.
78 if (window.TouchEvent !== undefined) {
79 try {
80 new TouchEvent("touchstart");
81 } catch (TypeError) {
82 return;
83 }
84 }
85
86 var captureElem = document.getElementById("noVNC_mouse_capture_elem");
87
88 if (captureElem === null) {
89 captureElem = document.createElement("div");
90 captureElem.id = "noVNC_mouse_capture_elem";
91 captureElem.style.position = "fixed";
92 captureElem.style.top = "0px";
93 captureElem.style.left = "0px";
94 captureElem.style.width = "100%";
95 captureElem.style.height = "100%";
96 captureElem.style.zIndex = 10000;
97 captureElem.style.display = "none";
98 document.body.appendChild(captureElem);
99
100 // This is to make sure callers don't get confused by having
101 // our blocking element as the target
102 captureElem.addEventListener('contextmenu', _captureProxy);
103
104 captureElem.addEventListener('mousemove', _captureProxy);
105 captureElem.addEventListener('mouseup', _captureProxy);
106
107 captureElem.addEventListener('touchmove', _captureProxy);
108 captureElem.addEventListener('touchend', _captureProxy);
109 }
110
111 _captureElem = elem;
112 _captureIndex++;
113
114 // Track cursor and get initial cursor
115 _captureObserver.observe(elem, {attributes:true});
116 _captureElemChanged();
117
118 captureElem.style.display = null;
119
120 // We listen to events on window in order to keep tracking if it
121 // happens to leave the viewport
122 window.addEventListener('mousemove', _captureProxy);
123 window.addEventListener('mouseup', _captureProxy);
124
125 window.addEventListener('touchmove', _captureProxy);
126 window.addEventListener('touchend', _captureProxy);
127 }
128 };
129
130 export function releaseCapture () {
131 if (document.releaseCapture) {
132
133 document.releaseCapture();
134
135 } else {
136 if (!_captureElem) {
137 return;
138 }
139
140 // There might be events already queued, so we need to wait for
141 // them to flush. E.g. contextmenu in Microsoft Edge
142 window.setTimeout(function(expected) {
143 // Only clear it if it's the expected grab (i.e. no one
144 // else has initiated a new grab)
145 if (_captureIndex === expected) {
146 _captureElem = null;
147 }
148 }, 0, _captureIndex);
149
150 _captureObserver.disconnect();
151
152 var captureElem = document.getElementById("noVNC_mouse_capture_elem");
153 captureElem.style.display = "none";
154
155 window.removeEventListener('mousemove', _captureProxy);
156 window.removeEventListener('mouseup', _captureProxy);
157
158 window.removeEventListener('touchmove', _captureProxy);
159 window.removeEventListener('touchend', _captureProxy);
160 }
161 };