Fixed layer resize(), avoid multiple handlePengingTasks() calls
[guacamole-common-js.git] / src / main / resources / mouse.js
1
2 /*
3  *  Guacamole - Clientless Remote Desktop
4  *  Copyright (C) 2010  Michael Jumper
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU Affero General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Affero General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Affero General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 // Guacamole namespace
21 var Guacamole = Guacamole || {};
22
23 /**
24  * Provides cross-browser mouse events for a given element. The events of
25  * the given element are automatically populated with handlers that translate
26  * mouse events into a non-browser-specific event provided by the
27  * Guacamole.Mouse instance.
28  * 
29  * Touch event support is planned, but currently only in testing (translate
30  * touch events into mouse events).
31  * 
32  * @constructor
33  * @param {Element} element The Element to use to provide mouse events.
34  */
35 Guacamole.Mouse = function(element) {
36
37     /**
38      * Reference to this Guacamole.Mouse.
39      * @private
40      */
41     var guac_mouse = this;
42
43     /**
44      * The current mouse state. The properties of this state are updated when
45      * mouse events fire. This state object is also passed in as a parameter to
46      * the handler of any mouse events.
47      * 
48      * @type Guacamole.Mouse.State
49      */
50     this.currentState = new Guacamole.Mouse.State(
51         0, 0, 
52         false, false, false, false, false
53     );
54
55     /**
56      * Fired whenever the user presses a mouse button down over the element
57      * associated with this Guacamole.Mouse.
58      * 
59      * @event
60      * @param {Guacamole.Mouse.State} state The current mouse state.
61      */
62         this.onmousedown = null;
63
64     /**
65      * Fired whenever the user releases a mouse button down over the element
66      * associated with this Guacamole.Mouse.
67      * 
68      * @event
69      * @param {Guacamole.Mouse.State} state The current mouse state.
70      */
71         this.onmouseup = null;
72
73     /**
74      * Fired whenever the user moves the mouse over the element associated with
75      * this Guacamole.Mouse.
76      * 
77      * @event
78      * @param {Guacamole.Mouse.State} state The current mouse state.
79      */
80         this.onmousemove = null;
81
82     // Block context menu so right-click gets sent properly
83     element.oncontextmenu = function(e) {
84         return false;
85     };
86
87     element.onmousemove = function(e) {
88
89         e.stopPropagation();
90
91         var absoluteMouseX = e.pageX;
92         var absoluteMouseY = e.pageY;
93
94         guac_mouse.currentState.x = absoluteMouseX - element.offsetLeft;
95         guac_mouse.currentState.y = absoluteMouseY - element.offsetTop;
96
97         // This is all JUST so we can get the mouse position within the element
98         var parent = element.offsetParent;
99         while (parent) {
100             if (parent.offsetLeft && parent.offsetTop) {
101                 guac_mouse.currentState.x -= parent.offsetLeft;
102                 guac_mouse.currentState.y -= parent.offsetTop;
103             }
104             parent = parent.offsetParent;
105         }
106
107         if (guac_mouse.onmousemove)
108             guac_mouse.onmousemove(guac_mouse.currentState);
109
110     };
111
112
113     element.onmousedown = function(e) {
114
115         e.stopPropagation();
116
117         switch (e.button) {
118             case 0:
119                 guac_mouse.currentState.left = true;
120                 break;
121             case 1:
122                 guac_mouse.currentState.middle = true;
123                 break;
124             case 2:
125                 guac_mouse.currentState.right = true;
126                 break;
127         }
128
129         if (guac_mouse.onmousedown)
130             guac_mouse.onmousedown(guac_mouse.currentState);
131
132     };
133
134
135     element.onmouseup = function(e) {
136
137         e.stopPropagation();
138
139         switch (e.button) {
140             case 0:
141                 guac_mouse.currentState.left = false;
142                 break;
143             case 1:
144                 guac_mouse.currentState.middle = false;
145                 break;
146             case 2:
147                 guac_mouse.currentState.right = false;
148                 break;
149         }
150
151         if (guac_mouse.onmouseup)
152             guac_mouse.onmouseup(guac_mouse.currentState);
153
154     };
155
156     element.onmouseout = function(e) {
157
158         e.stopPropagation();
159
160         // Release all buttons
161         if (guac_mouse.currentState.left
162             || guac_mouse.currentState.middle
163             || guac_mouse.currentState.right) {
164
165             guac_mouse.currentState.left = false;
166             guac_mouse.currentState.middle = false;
167             guac_mouse.currentState.right = false;
168
169             if (guac_mouse.onmouseup)
170                 guac_mouse.onmouseup(guac_mouse.currentState);
171         }
172
173     };
174
175     // Override selection on mouse event element.
176     element.onselectstart = function() {
177         return false;
178     };
179
180     // Scroll wheel support
181     function handleScroll(e) {
182
183         var delta = 0;
184         if (e.detail)
185             delta = e.detail;
186         else if (e.wheelDelta)
187             delta = -event.wheelDelta;
188
189         // Up
190         if (delta < 0) {
191             if (guac_mouse.onmousedown) {
192                 guac_mouse.currentState.up = true;
193                 guac_mouse.onmousedown(guac_mouse.currentState);
194             }
195
196             if (guac_mouse.onmouseup) {
197                 guac_mouse.currentState.up = false;
198                 guac_mouse.onmouseup(guac_mouse.currentState);
199             }
200         }
201
202         // Down
203         if (delta > 0) {
204             if (guac_mouse.onmousedown) {
205                 guac_mouse.currentState.down = true;
206                 guac_mouse.onmousedown(guac_mouse.currentState);
207             }
208
209             if (guac_mouse.onmouseup) {
210                 guac_mouse.currentState.down = false;
211                 guac_mouse.onmouseup(guac_mouse.currentState);
212             }
213         }
214
215         if (e.preventDefault)
216             e.preventDefault();
217
218         e.returnValue = false;
219     }
220
221     element.addEventListener('DOMMouseScroll', handleScroll, false);
222
223     element.onmousewheel = function(e) {
224         handleScroll(e);
225     };
226
227 };
228
229 /**
230  * Simple container for properties describing the state of a mouse.
231  * 
232  * @constructor
233  * @param {Number} x The X position of the mouse pointer in pixels.
234  * @param {Number} y The Y position of the mouse pointer in pixels.
235  * @param {Boolean} left Whether the left mouse button is pressed. 
236  * @param {Boolean} middle Whether the middle mouse button is pressed. 
237  * @param {Boolean} right Whether the right mouse button is pressed. 
238  * @param {Boolean} up Whether the up mouse button is pressed (the fourth
239  *                     button, usually part of a scroll wheel). 
240  * @param {Boolean} down Whether the down mouse button is pressed (the fifth
241  *                       button, usually part of a scroll wheel). 
242  */
243 Guacamole.Mouse.State = function(x, y, left, middle, right, up, down) {
244
245     /**
246      * The current X position of the mouse pointer.
247      * @type Number
248      */
249     this.x = x;
250
251     /**
252      * The current Y position of the mouse pointer.
253      * @type Number
254      */
255     this.y = y;
256
257     /**
258      * Whether the left mouse button is currently pressed.
259      * @type Boolean
260      */
261     this.left = left;
262
263     /**
264      * Whether the middle mouse button is currently pressed.
265      * @type Boolean
266      */
267     this.middle = middle
268
269     /**
270      * Whether the right mouse button is currently pressed.
271      * @type Boolean
272      */
273     this.right = right;
274
275     /**
276      * Whether the up mouse button is currently pressed. This is the fourth
277      * mouse button, associated with upward scrolling of the mouse scroll
278      * wheel.
279      * @type Boolean
280      */
281     this.up = up;
282
283     /**
284      * Whether the down mouse button is currently pressed. This is the fifth 
285      * mouse button, associated with downward scrolling of the mouse scroll
286      * wheel.
287      * @type Boolean
288      */
289     this.down = down;
290     
291 };
292