Refactor XML parse, use getElement() instead of overriding DOM.
[guacamole-common-js.git] / src / main / resources / oskeyboard.js
1
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is guacamole-common-js.
16  *
17  * The Initial Developer of the Original Code is
18  * Michael Jumper.
19  * Portions created by the Initial Developer are Copyright (C) 2010
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either the GNU General Public License Version 2 or later (the "GPL"), or
26  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
37
38 // Guacamole namespace
39 var Guacamole = Guacamole || {};
40
41 /**
42  * Dynamic on-screen keyboard. Given the URL to an XML keyboard layout file,
43  * this object will download and use the XML to construct a clickable on-screen
44  * keyboard with its own key events.
45  * 
46  * @constructor
47  * @param {String} url The URL of an XML keyboard layout file.
48  */
49 Guacamole.OnScreenKeyboard = function(url) {
50
51     // For each child of element, call handler defined in next
52     function parseChildren(element, next) {
53
54         var children = root.childNodes;
55         for (var i=0; i<children.length; i++) {
56
57             // Get child node and corresponding handler
58             var child = children[i];
59             var handler = next[child.tagName];
60
61             // Call handler if defined
62             if (handler)
63                 handler(child);
64
65             // Throw exception if no handler
66             else
67                 throw new Exception(
68                       "Unexpected " + child.tagName
69                     + " within " + element.tagName
70                 );
71
72         }
73
74     }
75
76     // Create keyboard
77     var keyboard = document.createElement("div");
78     keyboard.className = "keyboard";
79
80     // Retrieve keyboard XML
81     var xmlhttprequest = new XMLHttpRequest();
82     xmlhttprequest.open("GET", url, false);
83     xmlhttprequest.send(null);
84
85     var xml = xmlhttprequest.responseXML;
86
87     if (xml) {
88
89         function parse_row(e) {
90             
91             var row = document.createElement("div");
92             row.className = "row";
93
94             parseChildren(e, {
95                 
96                 "column": function(e) {
97                     row.appendChild(parse_column(e));
98                 },
99                 
100                 "gap": function parse_gap(e) {
101
102                     // Get attributes
103                     var gap_size = e.attributes["size"];
104
105                     // Create element
106                     var gap = document.createElement("div");
107                     gap.className = "gap";
108                     gap.textContent = " ";
109
110                     if (gap_size)
111                         gap.style.width = gap.style.height =
112                             parseFloat(gap_size.value) + "em";
113
114                 },
115                 
116                 "key": function parse_key(e) {
117                     
118                     // Get attributes
119                     var key_size = e.attributes["size"];
120
121                     parseChildren(e, {
122                         "cap": function cap(e) {
123
124                             // Get attributes
125                             var required = e.attributes["if"];
126                             var modifier = e.attributes["modifier"];
127                             var keysym   = e.attributes["keysym"];
128                             var sticky   = e.attributes["sticky"];
129                             
130
131                         }
132                     });
133
134                 }
135                 
136             });
137
138             return row;
139
140         }
141
142         function parse_column(e) {
143             
144             var col = document.createElement("div");
145             col.className = "col";
146
147             var align = col.attributes["align"];
148
149             if (align)
150                 col.style.textAlign = align.value;
151
152             // Columns can only contain rows
153             parseChildren(e, {
154                 "row": function(e) {
155                     col.appendChild(parse_row(e));
156                 }
157             });
158
159             return col;
160
161         }
162
163
164         // Parse document
165         parseChildren(xml.documentElement, {
166             
167             "keyboard": function parse_keyboard(e) {
168                 
169                 // Get attributes
170                 var keyboard_size = e.attributes["size"];
171                 
172                 parseChildren(e, {
173                     
174                     "row": function(e) {
175                         keyboard.appendChild(parse_row(e));
176                     },
177                     
178                     "column": function(e) {
179                         keyboard.appendChild(parse_column(e));
180                     }
181                     
182                 });
183
184             } // end keyboard
185                 
186         });
187
188     }
189
190     // Do not allow selection or mouse movement to propagate/register.
191     keyboard.onselectstart =
192     keyboard.onmousemove   =
193     keyboard.onmouseup     =
194     keyboard.onmousedown   =
195     function(e) {
196         e.stopPropagation();
197         return false;
198     };
199
200
201     this.onkeypressed  = null;
202     this.onkeyreleased = null;
203
204     this.getElement = function() {
205         return keyboard;
206     };
207
208 };
209
210 Guacamole.OnScreenKeyboard.Key = function() {
211
212     /**
213      * Width of the key, relative to the size of the keyboard.
214      */
215     this.size = 1;
216
217     /**
218      * Whether this key is currently pressed.
219      */
220     this.pressed = false;
221
222     /**
223      * An associative map of all caps by modifier.
224      */
225     this.caps = {};
226
227 }
228
229 Guacamole.OnScreenKeyboard.Cap = function(text, keycode, modifier) {
230     
231     /**
232      * Modifier represented by this keycap
233      */
234     this.modifier = 0;
235     
236     /**
237      * The text to be displayed within this keycap
238      */
239     this.text = text;
240
241     /**
242      * The keycode this cap sends when its associated key is pressed/released
243      */
244     this.keycode = keycode;
245
246     // Set modifier if provided
247     if (modifier) this.modifier = modifier;
248     
249 }