]> git.proxmox.com Git - mirror_novnc.git/blame - core/input.js
Remove unecessary event-related code from Util
[mirror_novnc.git] / core / input.js
CommitLineData
d3796c14
JM
1/*
2 * noVNC: HTML5 VNC client
1d728ace 3 * Copyright (C) 2012 Joel Martin
b2f1961a 4 * Copyright (C) 2013 Samuel Mannehed for Cendio AB
1d728ace 5 * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
d3796c14
JM
6 */
7
d6e281ba 8/*jslint browser: true, white: false */
d3796c14
JM
9/*global window, Util */
10
ae510306
SR
11/* [module]
12 * import Util from "./util";
13 * import KeyboardUtil from "./keyboard";
14 */
15
16/* [module] export */ var Keyboard;
d6e281ba
SR
17
18(function () {
19 "use strict";
20
21 //
22 // Keyboard event handler
23 //
24
25 Keyboard = function (defaults) {
26 this._keyDownList = []; // List of depressed keys
27 // (even if they are happy)
28
29 Util.set_defaults(this, defaults, {
30 'target': document,
31 'focused': true
32 });
33
34 // create the keyboard handler
ae510306
SR
35 this._handler = new KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(),
36 KeyboardUtil.VerifyCharModifier( /* jshint newcap: false */
37 KeyboardUtil.TrackKeyState(
38 KeyboardUtil.EscapeModifiers(this._handleRfbEvent.bind(this))
d6e281ba
SR
39 )
40 )
41 ); /* jshint newcap: true */
42
43 // keep these here so we can refer to them later
44 this._eventHandlers = {
45 'keyup': this._handleKeyUp.bind(this),
46 'keydown': this._handleKeyDown.bind(this),
47 'keypress': this._handleKeyPress.bind(this),
48 'blur': this._allKeysUp.bind(this)
49 };
50 };
51
52 Keyboard.prototype = {
53 // private methods
54
55 _handleRfbEvent: function (e) {
56 if (this._onKeyPress) {
57 Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
58 ", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")");
8f06673a 59 this._onKeyPress(e);
d6e281ba
SR
60 }
61 },
d3796c14 62
8f06673a 63 setQEMUVNCKeyboardHandler: function () {
ae510306
SR
64 this._handler = new KeyboardUtil.QEMUKeyEventDecoder(KeyboardUtil.ModifierSync(),
65 KeyboardUtil.TrackQEMUKeyState(
8f06673a
DHB
66 this._handleRfbEvent.bind(this)
67 )
68 );
69 },
70
d6e281ba
SR
71 _handleKeyDown: function (e) {
72 if (!this._focused) { return true; }
d3796c14 73
d6e281ba
SR
74 if (this._handler.keydown(e)) {
75 // Suppress bubbling/default actions
76 Util.stopEvent(e);
77 return false;
78 } else {
79 // Allow the event to bubble and become a keyPress event which
80 // will have the character code translated
81 return true;
82 }
83 },
d3796c14 84
d6e281ba
SR
85 _handleKeyPress: function (e) {
86 if (!this._focused) { return true; }
c96f9003 87
d6e281ba
SR
88 if (this._handler.keypress(e)) {
89 // Suppress bubbling/default actions
90 Util.stopEvent(e);
91 return false;
92 } else {
93 // Allow the event to bubble and become a keyPress event which
94 // will have the character code translated
95 return true;
96 }
97 },
d3796c14 98
d6e281ba
SR
99 _handleKeyUp: function (e) {
100 if (!this._focused) { return true; }
d3796c14 101
d6e281ba
SR
102 if (this._handler.keyup(e)) {
103 // Suppress bubbling/default actions
104 Util.stopEvent(e);
105 return false;
106 } else {
107 // Allow the event to bubble and become a keyPress event which
108 // will have the character code translated
109 return true;
110 }
111 },
112
113 _allKeysUp: function () {
114 Util.Debug(">> Keyboard.allKeysUp");
115 this._handler.releaseAll();
116 Util.Debug("<< Keyboard.allKeysUp");
117 },
118
119 // Public methods
120
121 grab: function () {
122 //Util.Debug(">> Keyboard.grab");
123 var c = this._target;
124
b4ef49ea
SR
125 c.addEventListener('keydown', this._eventHandlers.keydown);
126 c.addEventListener('keyup', this._eventHandlers.keyup);
127 c.addEventListener('keypress', this._eventHandlers.keypress);
d6e281ba
SR
128
129 // Release (key up) if window loses focus
b4ef49ea 130 window.addEventListener('blur', this._eventHandlers.blur);
d6e281ba
SR
131
132 //Util.Debug("<< Keyboard.grab");
133 },
134
135 ungrab: function () {
136 //Util.Debug(">> Keyboard.ungrab");
137 var c = this._target;
138
b4ef49ea
SR
139 c.removeEventListener('keydown', this._eventHandlers.keydown);
140 c.removeEventListener('keyup', this._eventHandlers.keyup);
141 c.removeEventListener('keypress', this._eventHandlers.keypress);
142 window.removeEventListener('blur', this._eventHandlers.blur);
d3796c14 143
d6e281ba
SR
144 // Release (key up) all keys that are in a down state
145 this._allKeysUp();
d3796c14 146
d6e281ba
SR
147 //Util.Debug(">> Keyboard.ungrab");
148 },
149
150 sync: function (e) {
151 this._handler.syncModifiers(e);
152 }
153 };
154
155 Util.make_properties(Keyboard, [
156 ['target', 'wo', 'dom'], // DOM element that captures keyboard input
157 ['focused', 'rw', 'bool'], // Capture and send key events
158
159 ['onKeyPress', 'rw', 'func'] // Handler for key press/release
5210330a 160 ]);
ae510306 161})();
d3796c14 162
ae510306 163/* [module] export */ var Mouse;
d6e281ba 164
ae510306 165(function () {
d6e281ba
SR
166 Mouse = function (defaults) {
167 this._mouseCaptured = false;
168
169 this._doubleClickTimer = null;
170 this._lastTouchPos = null;
171
172 // Configuration attributes
173 Util.set_defaults(this, defaults, {
174 'target': document,
175 'focused': true,
176 'scale': 1.0,
177 'touchButton': 1
178 });
179
180 this._eventHandlers = {
181 'mousedown': this._handleMouseDown.bind(this),
182 'mouseup': this._handleMouseUp.bind(this),
183 'mousemove': this._handleMouseMove.bind(this),
184 'mousewheel': this._handleMouseWheel.bind(this),
185 'mousedisable': this._handleMouseDisable.bind(this)
186 };
187 };
188
189 Mouse.prototype = {
190 // private methods
191 _captureMouse: function () {
192 // capturing the mouse ensures we get the mouseup event
193 if (this._target.setCapture) {
194 this._target.setCapture();
195 }
196
197 // some browsers give us mouseup events regardless,
198 // so if we never captured the mouse, we can disregard the event
199 this._mouseCaptured = true;
200 },
201
202 _releaseMouse: function () {
203 if (this._target.releaseCapture) {
204 this._target.releaseCapture();
205 }
206 this._mouseCaptured = false;
207 },
208
209 _resetDoubleClickTimer: function () {
210 this._doubleClickTimer = null;
211 },
b2f1961a 212
d6e281ba
SR
213 _handleMouseButton: function (e, down) {
214 if (!this._focused) { return true; }
cf19ad37 215
d6e281ba
SR
216 if (this._notify) {
217 this._notify(e);
218 }
b2f1961a 219
d6e281ba
SR
220 var evt = (e ? e : window.event);
221 var pos = Util.getEventPosition(e, this._target, this._scale);
222
223 var bmask;
224 if (e.touches || e.changedTouches) {
225 // Touch device
226
227 // When two touches occur within 500 ms of each other and are
f52105bc 228 // close enough together a double click is triggered.
d6e281ba
SR
229 if (down == 1) {
230 if (this._doubleClickTimer === null) {
231 this._lastTouchPos = pos;
232 } else {
233 clearTimeout(this._doubleClickTimer);
234
235 // When the distance between the two touches is small enough
236 // force the position of the latter touch to the position of
237 // the first.
238
239 var xs = this._lastTouchPos.x - pos.x;
240 var ys = this._lastTouchPos.y - pos.y;
241 var d = Math.sqrt((xs * xs) + (ys * ys));
242
243 // The goal is to trigger on a certain physical width, the
244 // devicePixelRatio brings us a bit closer but is not optimal.
f52105bc 245 var threshold = 20 * (window.devicePixelRatio || 1);
246 if (d < threshold) {
d6e281ba
SR
247 pos = this._lastTouchPos;
248 }
249 }
250 this._doubleClickTimer = setTimeout(this._resetDoubleClickTimer.bind(this), 500);
a4ec2f5c 251 }
d6e281ba
SR
252 bmask = this._touchButton;
253 // If bmask is set
254 } else if (evt.which) {
255 /* everything except IE */
256 bmask = 1 << evt.button;
257 } else {
258 /* IE including 9 */
259 bmask = (evt.button & 0x1) + // Left
260 (evt.button & 0x2) * 2 + // Right
261 (evt.button & 0x4) / 2; // Middle
b2f1961a 262 }
d6e281ba
SR
263
264 if (this._onMouseButton) {
265 Util.Debug("onMouseButton " + (down ? "down" : "up") +
266 ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
267 this._onMouseButton(pos.x, pos.y, down, bmask);
268 }
269 Util.stopEvent(e);
270 return false;
271 },
272
273 _handleMouseDown: function (e) {
274 this._captureMouse();
275 this._handleMouseButton(e, 1);
276 },
277
278 _handleMouseUp: function (e) {
279 if (!this._mouseCaptured) { return; }
280
281 this._handleMouseButton(e, 0);
282 this._releaseMouse();
283 },
284
285 _handleMouseWheel: function (e) {
286 if (!this._focused) { return true; }
287
288 if (this._notify) {
289 this._notify(e);
290 }
291
292 var evt = (e ? e : window.event);
293 var pos = Util.getEventPosition(e, this._target, this._scale);
294 var wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
295 var bmask;
296 if (wheelData > 0) {
297 bmask = 1 << 3;
298 } else {
299 bmask = 1 << 4;
300 }
301
302 if (this._onMouseButton) {
303 this._onMouseButton(pos.x, pos.y, 1, bmask);
304 this._onMouseButton(pos.x, pos.y, 0, bmask);
305 }
306 Util.stopEvent(e);
307 return false;
308 },
309
310 _handleMouseMove: function (e) {
311 if (! this._focused) { return true; }
312
313 if (this._notify) {
314 this._notify(e);
315 }
316
317 var evt = (e ? e : window.event);
318 var pos = Util.getEventPosition(e, this._target, this._scale);
319 if (this._onMouseMove) {
320 this._onMouseMove(pos.x, pos.y);
321 }
322 Util.stopEvent(e);
323 return false;
324 },
325
326 _handleMouseDisable: function (e) {
327 if (!this._focused) { return true; }
328
329 var evt = (e ? e : window.event);
330 var pos = Util.getEventPosition(e, this._target, this._scale);
331
332 /* Stop propagation if inside canvas area */
333 if ((pos.realx >= 0) && (pos.realy >= 0) &&
334 (pos.realx < this._target.offsetWidth) &&
335 (pos.realy < this._target.offsetHeight)) {
336 //Util.Debug("mouse event disabled");
337 Util.stopEvent(e);
338 return false;
339 }
340
341 return true;
342 },
343
344
345 // Public methods
346 grab: function () {
347 var c = this._target;
348
349 if ('ontouchstart' in document.documentElement) {
b4ef49ea
SR
350 c.addEventListener('touchstart', this._eventHandlers.mousedown);
351 window.addEventListener('touchend', this._eventHandlers.mouseup);
352 c.addEventListener('touchend', this._eventHandlers.mouseup);
353 c.addEventListener('touchmove', this._eventHandlers.mousemove);
d6e281ba 354 } else {
b4ef49ea
SR
355 c.addEventListener('mousedown', this._eventHandlers.mousedown);
356 window.addEventListener('mouseup', this._eventHandlers.mouseup);
357 c.addEventListener('mouseup', this._eventHandlers.mouseup);
358 c.addEventListener('mousemove', this._eventHandlers.mousemove);
359 c.addEventListener((Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
d6e281ba
SR
360 this._eventHandlers.mousewheel);
361 }
362
363 /* Work around right and middle click browser behaviors */
b4ef49ea
SR
364 document.addEventListener('click', this._eventHandlers.mousedisable);
365 document.body.addEventListener('contextmenu', this._eventHandlers.mousedisable);
d6e281ba
SR
366 },
367
368 ungrab: function () {
369 var c = this._target;
370
371 if ('ontouchstart' in document.documentElement) {
b4ef49ea
SR
372 c.removeEventListener('touchstart', this._eventHandlers.mousedown);
373 window.removeEventListener('touchend', this._eventHandlers.mouseup);
374 c.removeEventListener('touchend', this._eventHandlers.mouseup);
375 c.removeEventListener('touchmove', this._eventHandlers.mousemove);
d6e281ba 376 } else {
b4ef49ea
SR
377 c.removeEventListener('mousedown', this._eventHandlers.mousedown);
378 window.removeEventListener('mouseup', this._eventHandlers.mouseup);
379 c.removeEventListener('mouseup', this._eventHandlers.mouseup);
380 c.removeEventListener('mousemove', this._eventHandlers.mousemove);
381 c.removeEventListener((Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
d6e281ba
SR
382 this._eventHandlers.mousewheel);
383 }
384
385 /* Work around right and middle click browser behaviors */
b4ef49ea
SR
386 document.removeEventListener('click', this._eventHandlers.mousedisable);
387 document.body.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
d6e281ba 388
b2f1961a 389 }
d6e281ba
SR
390 };
391
392 Util.make_properties(Mouse, [
393 ['target', 'ro', 'dom'], // DOM element that captures mouse input
394 ['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
395 ['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
396 ['scale', 'rw', 'float'], // Viewport scale factor 0.0 - 1.0
397
398 ['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
399 ['onMouseMove', 'rw', 'func'], // Handler for mouse movement
400 ['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
401 ]);
402})();