]> Pileus Git - ~andy/gtk/blob - gdk/broadway/broadway.js
[broadway] Implement keyboard event better
[~andy/gtk] / gdk / broadway / broadway.js
1 /* Helper functions for debugging */
2 var logDiv = null;
3 function log(str) {
4     if (!logDiv) {
5         logDiv = document.createElement('div');
6         document.body.appendChild(logDiv);
7         logDiv.style["position"] = "absolute";
8         logDiv.style["right"] = "0px";
9     }
10     logDiv.appendChild(document.createTextNode(str));
11     logDiv.appendChild(document.createElement('br'));
12 }
13
14 function getStackTrace()
15 {
16     var callstack = [];
17     var isCallstackPopulated = false;
18     try {
19         i.dont.exist+=0;
20     } catch(e) {
21         if (e.stack) { // Firefox
22             var lines = e.stack.split("\n");
23             for (var i=0, len=lines.length; i<len; i++) {
24                 if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
25                     callstack.push(lines[i]);
26                 }
27             }
28             // Remove call to getStackTrace()
29             callstack.shift();
30             isCallstackPopulated = true;
31         } else if (window.opera && e.message) { // Opera
32             var lines = e.message.split("\n");
33             for (var i=0, len=lines.length; i<len; i++) {
34                 if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
35                     var entry = lines[i];
36                     // Append next line also since it has the file info
37                     if (lines[i+1]) {
38                         entry += " at " + lines[i+1];
39                         i++;
40                     }
41                     callstack.push(entry);
42                 }
43             }
44             // Remove call to getStackTrace()
45             callstack.shift();
46             isCallstackPopulated = true;
47         }
48     }
49     if (!isCallstackPopulated) { //IE and Safari
50         var currentFunction = arguments.callee.caller;
51         while (currentFunction) {
52             var fn = currentFunction.toString();
53             var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf("(")) || "anonymous";
54             callstack.push(fname);
55             currentFunction = currentFunction.caller;
56         }
57     }
58     return callstack;
59 }
60
61 function logStackTrace(len) {
62     var callstack = getStackTrace();
63     var end = callstack.length;
64     if (len > 0)
65         end = Math.min(len + 1, end);
66     for (var i = 1; i < end; i++)
67         log(callstack[i]);
68 }
69
70 var base64Values = [
71     255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
72     255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
73     255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
74     52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
75     255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
76     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
77     255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
78     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255
79 ];
80
81 function base64_8(str, index) {
82     var v =
83         (base64Values[str.charCodeAt(index)]) +
84         (base64Values[str.charCodeAt(index+1)] << 6);
85     return v;
86 }
87
88 function base64_16(str, index) {
89     var v =
90         (base64Values[str.charCodeAt(index)]) +
91         (base64Values[str.charCodeAt(index+1)] << 6) +
92         (base64Values[str.charCodeAt(index+2)] << 12);
93     return v;
94 }
95
96 function base64_16s(str, index) {
97     var v = base64_16(str, index);
98     if (v > 32767)
99         return v - 65536;
100     else
101         return v;
102 }
103
104 function base64_24(str, index) {
105     var v =
106         (base64Values[str.charCodeAt(index)]) +
107         (base64Values[str.charCodeAt(index+1)] << 6) +
108         (base64Values[str.charCodeAt(index+2)] << 12) +
109         (base64Values[str.charCodeAt(index+3)] << 18);
110     return v;
111 }
112
113 function base64_32(str, index) {
114     var v =
115         (base64Values[str.charCodeAt(index)]) +
116         (base64Values[str.charCodeAt(index+1)] << 6) +
117         (base64Values[str.charCodeAt(index+2)] << 12) +
118         (base64Values[str.charCodeAt(index+3)] << 18) +
119         (base64Values[str.charCodeAt(index+4)] << 24) +
120         (base64Values[str.charCodeAt(index+5)] << 30);
121     return v;
122 }
123
124 function createXHR()
125 {
126     try { return new XMLHttpRequest(); } catch(e) {}
127     try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
128     try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
129     try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
130     try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
131
132     return null;
133 }
134
135 /* This resizes the window so the *inner* size is the specified size */
136 function resizeBrowserWindow(window, w, h) {
137     var innerW = window.innerWidth;
138     var innerH = window.innerHeight;
139
140     var outerW = window.outerWidth;
141     var outerH = window.outerHeight;
142
143     window.resizeTo(w + outerW - innerW,
144                     h + outerH - innerH);
145 }
146
147 function resizeCanvas(canvas, w, h)
148 {
149     /* Canvas resize clears the data, so we need to save it first */
150     var tmpCanvas = canvas.ownerDocument.createElement("canvas");
151     tmpCanvas.width = canvas.width;
152     tmpCanvas.height = canvas.height;
153     var tmpContext = tmpCanvas.getContext("2d");
154     tmpContext.globalCompositeOperation = "copy";
155     tmpContext.drawImage(canvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
156
157     canvas.width = w;
158     canvas.height = h;
159
160     var context = canvas.getContext("2d");
161
162     context.globalCompositeOperation = "copy";
163     context.drawImage(tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
164 }
165
166 var useToplevelWindows = false;
167 var toplevelWindows = [];
168 var grab = new Object();
169 grab.window = null;
170 grab.ownerEvents = false;
171 grab.implicit = false;
172 var localGrab = null;
173 var keyDownList = [];
174 var lastSerial = 0;
175 var lastX = 0;
176 var lastY = 0;
177 var lastState;
178 var lastTimeStamp = 0;
179 var realWindowWithMouse = 0;
180 var windowWithMouse = 0;
181 var surfaces = {};
182 var stackingOrder = [];
183 var outstandingCommands = new Array();
184 var inputSocket = null;
185
186 var GDK_CROSSING_NORMAL = 0;
187 var GDK_CROSSING_GRAB = 1;
188 var GDK_CROSSING_UNGRAB = 2;
189
190 // GdkModifierType
191 var GDK_SHIFT_MASK = 1 << 0;
192 var GDK_LOCK_MASK     = 1 << 1;
193 var GDK_CONTROL_MASK  = 1 << 2;
194 var GDK_MOD1_MASK     = 1 << 3;
195 var GDK_MOD2_MASK     = 1 << 4;
196 var GDK_MOD3_MASK     = 1 << 5;
197 var GDK_MOD4_MASK     = 1 << 6;
198 var GDK_MOD5_MASK     = 1 << 7;
199 var GDK_BUTTON1_MASK  = 1 << 8;
200 var GDK_BUTTON2_MASK  = 1 << 9;
201 var GDK_BUTTON3_MASK  = 1 << 10;
202 var GDK_BUTTON4_MASK  = 1 << 11;
203 var GDK_BUTTON5_MASK  = 1 << 12;
204 var GDK_SUPER_MASK    = 1 << 26;
205 var GDK_HYPER_MASK    = 1 << 27;
206 var GDK_META_MASK     = 1 << 28;
207 var GDK_RELEASE_MASK  = 1 << 30;
208
209 function getButtonMask (button) {
210     if (button == 1)
211         return GDK_BUTTON1_MASK;
212     if (button == 2)
213         return GDK_BUTTON2_MASK;
214     if (button == 3)
215         return GDK_BUTTON3_MASK;
216     if (button == 4)
217         return GDK_BUTTON4_MASK;
218     if (button == 5)
219         return GDK_BUTTON5_MASK;
220     return 0;
221 }
222
223 function flushSurface(surface)
224 {
225     var commands = surface.drawQueue;
226     surface.queue = [];
227     var context = surface.canvas.getContext("2d");
228     context.globalCompositeOperation = "source-over";
229     var i = 0;
230     for (i = 0; i < commands.length; i++) {
231         var cmd = commands[i];
232         switch (cmd.op) {
233         case 'i': // put image data surface
234             context.globalCompositeOperation = "source-over";
235             context.drawImage(cmd.img, cmd.x, cmd.y);
236             break;
237
238         case 'b': // copy rects
239             context.save();
240             context.beginPath();
241
242             var minx;
243             var miny;
244             var maxx;
245             var maxy;
246             for (var j = 0; j < cmd.rects.length; j++) {
247                 var rect = cmd.rects[j];
248                 context.rect(rect.x, rect.y, rect.w, rect.h);
249                 if (j == 0) {
250                     minx = rect.x;
251                     miny = rect.y;
252                     maxx = rect.x + rect.w;
253                     maxy = rect.y + rect.h;
254                 } else {
255                     if (rect.x < minx)
256                         minx = rect.x;
257                     if (rect.y < miny)
258                         miny = rect.y;
259                     if (rect.x + rect.w > maxx)
260                         maxx = rect.x + rect.w;
261                     if (rect.y + rect.h > maxy)
262                         maxy = rect.y + rect.h;
263                 }
264             }
265             context.clip();
266             context.globalCompositeOperation = "copy";
267             context.drawImage(context.canvas,
268                               minx - cmd.dx, miny - cmd.dy, maxx - minx, maxy - miny,
269                               minx, miny, maxx - minx, maxy - miny);
270             context.restore();
271             break;
272
273         default:
274             alert("Unknown drawing op " + cmd.op);
275         }
276     }
277 }
278
279 function ensureSurfaceInDocument(surface, doc)
280 {
281     if (surface.document != doc) {
282         var oldCanvas = surface.canvas;
283         var canvas = doc.importNode(oldCanvas, false);
284         doc.body.appendChild(canvas);
285         canvas.surface = surface;
286         oldCanvas.parentNode.removeChild(oldCanvas);
287
288         surface.canvas = canvas;
289         if (surface.toplevelElement == oldCanvas)
290             surface.toplevelElement = canvas;
291         surface.document = doc;
292     }
293 }
294
295 function sendConfigureNotify(surface)
296 {
297     sendInput("w", [surface.id, surface.x, surface.y, surface.width, surface.height]);
298 }
299
300 var windowGeometryTimeout = null;
301
302 function updateBrowserWindowGeometry(win, alwaysSendConfigure) {
303     if (win.closed)
304         return;
305
306     var surface = win.surface;
307
308     var innerW = win.innerWidth;
309     var innerH = win.innerHeight;
310
311     var x = surface.x;
312     var y = surface.y;
313
314     if (win.mozInnerScreenX != undefined) {
315         x = win.mozInnerScreenX;
316         y = win.mozInnerScreenY;
317     } else if (win.screenTop != undefined) {
318         x = win.screenTop;
319         y = win.screenLeft;
320     } else {
321         alert("No implementation to get window position");
322     }
323
324     if (alwaysSendConfigure || x != surface.x || y != surface.y ||
325        innerW != surface.width || innerH != surface.height) {
326         var oldX = surface.x;
327         var oldY = surface.y;
328         surface.x = x;
329         surface.y = y;
330         if (surface.width != innerW || surface.height != innerH)
331             resizeCanvas(surface.canvas, innerW, innerH);
332         surface.width = innerW;
333         surface.height = innerH;
334         sendConfigureNotify(surface);
335         for (id in surfaces) {
336             var childSurface = surfaces[id];
337             var transientToplevel = getTransientToplevel(childSurface);
338             if (transientToplevel != null && transientToplevel == surface) {
339                 childSurface.x += surface.x - oldX;
340                 childSurface.y += surface.y - oldY;
341                 sendConfigureNotify(childSurface);
342             }
343         }
344     }
345 }
346
347 function browserWindowClosed(win) {
348     var surface = win.surface;
349
350     sendInput ("W", [surface.id]);
351     for (id in surfaces) {
352         var childSurface = surfaces[id];
353         var transientToplevel = getTransientToplevel(childSurface);
354         if (transientToplevel != null && transientToplevel == surface) {
355             sendInput ("W", [childSurface.id]);
356         }
357     }
358 }
359
360 function registerWindow(win)
361 {
362     toplevelWindows.push(win);
363     win.onresize = function(ev) { updateBrowserWindowGeometry(ev.target, false); };
364     if (!windowGeometryTimeout)
365         windowGeometryTimeout = setInterval(function () {
366                                                 for (var i = 0; i < toplevelWindows.length; i++)
367                                                     updateBrowserWindowGeometry(toplevelWindows[i], false);
368                                             }, 2000);
369     win.onunload = function(ev) { browserWindowClosed(ev.target.defaultView); };
370 }
371
372 function unregisterWindow(win)
373 {
374     var i = toplevelWindows.indexOf(win);
375     if (i >= 0)
376         toplevelWindows.splice(i, 1);
377
378     if (windowGeometryTimeout && toplevelWindows.length == 0) {
379         clearInterval(windowGeometryTimeout);
380         windowGeometryTimeout = null;
381     }
382 }
383
384 function getTransientToplevel(surface)
385 {
386     while (surface && surface.transientParent != 0) {
387         surface = surfaces[surface.transientParent];
388         if (surface && surface.window)
389             return surface;
390     }
391     return null;
392 }
393
394 function getStyle(el, styleProp)
395 {
396     if (el.currentStyle) {
397         return el.currentStyle[styleProp];
398     }  else if (window.getComputedStyle) {
399         var win = el.ownerDocument.defaultView;
400         return win.getComputedStyle(el, null).getPropertyValue(styleProp);
401     }
402     return undefined;
403 }
404
405 function parseOffset(value)
406 {
407     var px = value.indexOf("px");
408     if (px > 0)
409         return parseInt(value.slice(0,px));
410     return 0;
411 }
412
413 function getFrameOffset(surface) {
414     var x = 0;
415     var y = 0;
416     var el = surface.canvas;
417     while (el != null && el != surface.frame) {
418         x += el.offsetLeft;
419         y += el.offsetTop;
420
421         /* For some reason the border is not includes in the offsets.. */
422         x += parseOffset(getStyle(el, "border-left-width"));
423         y += parseOffset(getStyle(el, "border-top-width"));
424
425         el = el.offsetParent;
426     }
427
428     /* Also include frame border as per above */
429     x += parseOffset(getStyle(el, "border-left-width"));
430     y += parseOffset(getStyle(el, "border-top-width"));
431
432     return {x: x, y: y};
433 }
434
435 var positionIndex = 0;
436 function cmdCreateSurface(id, x, y, width, height, isTemp)
437 {
438     var surface = { id: id, x: x, y:y, width: width, height: height, isTemp: isTemp };
439     surface.positioned = isTemp;
440     surface.drawQueue = [];
441     surface.transientParent = 0;
442     surface.visible = false;
443     surface.window = null;
444     surface.document = document;
445     surface.frame = null;
446
447     var canvas = document.createElement("canvas");
448     canvas.width = width;
449     canvas.height = height;
450     canvas.surface = surface;
451     surface.canvas = canvas;
452     var toplevelElement;
453
454     if (useToplevelWindows || isTemp) {
455         toplevelElement = canvas;
456         document.body.appendChild(canvas);
457     } else {
458         var frame = document.createElement("div");
459         frame.frameFor = surface;
460         frame.className = "frame-window";
461         surface.frame = frame;
462
463         var button = document.createElement("center");
464         var X = document.createTextNode("X");
465         button.appendChild(X);
466         button.className = "frame-close";
467         frame.appendChild(button);
468
469         var contents = document.createElement("div");
470         contents.className = "frame-contents";
471         frame.appendChild(contents);
472
473         canvas.style["display"] = "block";
474         contents.appendChild(canvas);
475
476         toplevelElement = frame;
477         document.body.appendChild(frame);
478
479         surface.x = 100 + positionIndex * 10;
480         surface.y = 100 + positionIndex * 10;
481         positionIndex = (positionIndex + 1) % 20;
482     }
483
484     surface.toplevelElement = toplevelElement;
485     toplevelElement.style["position"] = "absolute";
486     /* This positioning isn't strictly right for apps in another topwindow,
487      * but that will be fixed up when showing. */
488     toplevelElement.style["left"] = surface.x + "px";
489     toplevelElement.style["top"] = surface.y + "px";
490     toplevelElement.style["display"] = "inline";
491
492     /* We hide the frame with visibility rather than display none
493      * so getFrameOffset still works with hidden windows. */
494     toplevelElement.style["visibility"] = "hidden";
495
496     surfaces[id] = surface;
497     stackingOrder.push(surface);
498
499     sendConfigureNotify(surface);
500 }
501
502 function cmdShowSurface(id)
503 {
504     var surface = surfaces[id];
505
506     if (surface.visible)
507         return;
508     surface.visible = true;
509
510     var xOffset = surface.x;
511     var yOffset = surface.y;
512
513     if (useToplevelWindows) {
514         var doc = document;
515         if (!surface.isTemp) {
516             var options =
517                 'width='+surface.width+',height='+surface.height+
518                 ',location=no,menubar=no,scrollbars=no,toolbar=no';
519             if (surface.positioned)
520                 options = options +
521                 ',left='+surface.x+',top='+surface.y+',screenX='+surface.x+',screenY='+surface.y;
522             var win = window.open('','_blank', options);
523             win.surface = surface;
524             registerWindow(win);
525             doc = win.document;
526             doc.open();
527             doc.write("<body></body>");
528             setupDocument(doc);
529
530             surface.window = win;
531             xOffset = 0;
532             yOffset = 0;
533         } else {
534             var transientToplevel = getTransientToplevel(surface);
535             if (transientToplevel) {
536                 doc = transientToplevel.window.document;
537                 xOffset = surface.x - transientToplevel.x;
538                 yOffset = surface.y - transientToplevel.y;
539             }
540         }
541
542         ensureSurfaceInDocument(surface, doc);
543     } else {
544         if (surface.frame) {
545             var offset = getFrameOffset(surface);
546             xOffset -= offset.x;
547             yOffset -= offset.y;
548         }
549     }
550
551     surface.toplevelElement.style["left"] = xOffset + "px";
552     surface.toplevelElement.style["top"] = yOffset + "px";
553     surface.toplevelElement.style["visibility"] = "visible";
554
555     restackWindows();
556
557     if (surface.window)
558         updateBrowserWindowGeometry(surface.window, false);
559 }
560
561 function cmdHideSurface(id)
562 {
563     var surface = surfaces[id];
564
565     if (!surface.visible)
566         return;
567     surface.visible = false;
568
569     var element = surface.toplevelElement;
570
571     element.style["visibility"] = "hidden";
572
573     // Import the canvas into the main document
574     ensureSurfaceInDocument(surface, document);
575
576     if (surface.window) {
577         unregisterWindow(surface.window);
578         surface.window.close();
579         surface.window = null;
580     }
581 }
582
583 function cmdSetTransientFor(id, parentId)
584 {
585     var surface = surfaces[id];
586
587     if (surface.transientParent == parentId)
588         return;
589
590     surface.transientParent = parentId;
591     if (parentId != 0 && surfaces[parentId]) {
592         moveToHelper(surface, stackingOrder.indexOf(surfaces[parentId])+1);
593     }
594
595     if (surface.visible) {
596         restackWindows();
597     }
598 }
599
600 function restackWindows() {
601     for (var i = 0; i < stackingOrder.length; i++) {
602         var surface = stackingOrder[i];
603         surface.toplevelElement.style.zIndex = i;
604     }
605 }
606
607 function moveToHelper(surface, position) {
608     var i = stackingOrder.indexOf(surface);
609     stackingOrder.splice(i, 1);
610     if (position != undefined)
611         stackingOrder.splice(position, 0, surface);
612     else
613         stackingOrder.push(surface);
614
615     for (var cid in surfaces) {
616         var child = surfaces[cid];
617         if (child.transientParent == surface.id)
618             moveToHelper(child, stackingOrder.indexOf(surface) + 1);
619     }
620 }
621
622 function moveToTop(surface) {
623     moveToHelper(surface);
624     restackWindows();
625 }
626
627
628 function cmdDeleteSurface(id)
629 {
630     var surface = surfaces[id];
631     var i = stackingOrder.indexOf(surface);
632     if (i >= 0)
633         stackingOrder.splice(i, 1);
634     var canvas = surface.canvas;
635     canvas.parentNode.removeChild(canvas);
636     var frame = surface.frame;
637     if (frame)
638         frame.parentNode.removeChild(frame);
639     delete surfaces[id];
640 }
641
642 function cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h)
643 {
644     var surface = surfaces[id];
645     if (has_pos) {
646         surface.positioned = true;
647         surface.x = x;
648         surface.y = y;
649     }
650     if (has_size) {
651         surface.width = w;
652         surface.height = h;
653     }
654
655     /* Flush any outstanding draw ops before (possibly) changing size */
656     flushSurface(surface);
657
658     if (has_size)
659         resizeCanvas(surface.canvas, w, h);
660
661     if (surface.visible) {
662         if (surface.window) {
663             /* TODO: This moves the outer frame position, we really want the inner position.
664              * However this isn't *strictly* invalid, as any WM could have done whatever it
665              * wanted with the positioning of the window.
666              */
667             if (has_pos)
668                 surface.window.moveTo(surface.x, surface.y);
669             if (has_size)
670                 resizeBrowserWindow(surface.window, w, h);
671         } else {
672             if (has_pos) {
673                 var xOffset = surface.x;
674                 var yOffset = surface.y;
675
676                 var transientToplevel = getTransientToplevel(surface);
677                 if (transientToplevel) {
678                     xOffset = surface.x - transientToplevel.x;
679                     yOffset = surface.y - transientToplevel.y;
680                 }
681
682                 var element = surface.canvas;
683                 if (surface.frame) {
684                     element = surface.frame;
685                     var offset = getFrameOffset(surface);
686                     xOffset -= offset.x;
687                     yOffset -= offset.y;
688                 }
689
690                 element.style["left"] = xOffset + "px";
691                 element.style["top"] = yOffset + "px";
692             }
693         }
694     }
695
696     if (surface.window) {
697         updateBrowserWindowGeometry(surface.window, true);
698     } else {
699         sendConfigureNotify(surface);
700     }
701 }
702
703 function cmdFlushSurface(id)
704 {
705     flushSurface(surfaces[id]);
706 }
707
708 function cmdGrabPointer(id, ownerEvents)
709 {
710     doGrab(id, ownerEvents, false);
711     sendInput ("g", []);
712 }
713
714 function cmdUngrabPointer()
715 {
716     sendInput ("u", []);
717
718     grab.window = null;
719 }
720
721 function handleCommands(cmdObj)
722 {
723     var cmd = cmdObj.data;
724     var i = cmdObj.pos;
725
726     while (i < cmd.length) {
727         var id, x, y, w, h, q;
728         var command = cmd[i++];
729         lastSerial = base64_32(cmd, i);
730         i = i + 6;
731         switch (command) {
732         case 's': // create new surface
733             id = base64_16(cmd, i);
734             i = i + 3;
735             x = base64_16s(cmd, i);
736             i = i + 3;
737             y = base64_16s(cmd, i);
738             i = i + 3;
739             w = base64_16(cmd, i);
740             i = i + 3;
741             h = base64_16(cmd, i);
742             i = i + 3;
743             var isTemp = cmd[i] == '1';
744             i = i + 1;
745             cmdCreateSurface(id, x, y, w, h, isTemp);
746             break;
747
748         case 'S': // Show a surface
749             id = base64_16(cmd, i);
750             i = i + 3;
751             cmdShowSurface(id);
752             break;
753
754         case 'H': // Hide a surface
755             id = base64_16(cmd, i);
756             i = i + 3;
757             cmdHideSurface(id);
758             break;
759
760         case 'p': // Set transient parent
761             id = base64_16(cmd, i);
762             i = i + 3;
763             var parentId = base64_16(cmd, i);
764             i = i + 3;
765             cmdSetTransientFor(id, parentId);
766             break;
767
768         case 'd': // Delete surface
769             id = base64_16(cmd, i);
770             i = i + 3;
771             cmdDeleteSurface(id);
772             break;
773
774         case 'm': // Move a surface
775             id = base64_16(cmd, i);
776             i = i + 3;
777             var ops = cmd.charCodeAt(i++) - 48;
778             var has_pos = ops & 1;
779             if (has_pos) {
780                 x = base64_16s(cmd, i);
781                 i = i + 3;
782                 y = base64_16s(cmd, i);
783                 i = i + 3;
784             }
785             var has_size = ops & 2;
786             if (has_size) {
787                 w = base64_16(cmd, i);
788                 i = i + 3;
789                 h = base64_16(cmd, i);
790                 i = i + 3;
791             }
792             cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h);
793             break;
794
795         case 'i': // Put image data surface
796             q = new Object();
797             q.op = 'i';
798             q.id = base64_16(cmd, i);
799             i = i + 3;
800             q.x = base64_16(cmd, i);
801             i = i + 3;
802             q.y = base64_16(cmd, i);
803             i = i + 3;
804             var size = base64_32(cmd, i);
805             i = i + 6;
806             var url = cmd.slice(i, i + size);
807             i = i + size;
808             q.img = new Image();
809             q.img.src = url;
810             surfaces[q.id].drawQueue.push(q);
811             if (!q.img.complete) {
812                 cmdObj.pos = i;
813                 q.img.onload = function() { handleOutstanding(); };
814                 return false;
815             }
816             break;
817
818         case 'b': // Copy rects
819             q = new Object();
820             q.op = 'b';
821             q.id = base64_16(cmd, i);
822             i = i + 3;
823
824             var nrects = base64_16(cmd, i);
825             i = i + 3;
826
827             q.rects = [];
828             for (var r = 0; r < nrects; r++) {
829                 var rect = new Object();
830                 rect.x = base64_16(cmd, i);
831                 i = i + 3;
832                 rect.y = base64_16(cmd, i);
833                 i = i + 3;
834                 rect.w = base64_16(cmd, i);
835                 i = i + 3;
836                 rect.h = base64_16(cmd, i);
837                 i = i + 3;
838                 q.rects.push (rect);
839             }
840
841             q.dx = base64_16s(cmd, i);
842             i = i + 3;
843             q.dy = base64_16s(cmd, i);
844             i = i + 3;
845             surfaces[q.id].drawQueue.push(q);
846             break;
847
848         case 'f': // Flush surface
849             id = base64_16(cmd, i);
850             i = i + 3;
851
852             cmdFlushSurface(id);
853             break;
854
855         case 'g': // Grab
856             id = base64_16(cmd, i);
857             i = i + 3;
858             var ownerEvents = cmd[i++] == '1';
859
860             cmdGrabPointer(id, ownerEvents);
861             break;
862
863         case 'u': // Ungrab
864             cmdUngrabPointer();
865             break;
866         default:
867             alert("Unknown op " + command);
868         }
869     }
870     return true;
871 }
872
873 function handleOutstanding()
874 {
875     while (outstandingCommands.length > 0) {
876         var cmd = outstandingCommands.shift();
877         if (!handleCommands(cmd)) {
878             outstandingCommands.unshift(cmd);
879             return;
880         }
881     }
882 }
883
884 function handleLoad(event)
885 {
886     var cmdObj = {};
887     cmdObj.data = event.target.responseText;
888     cmdObj.pos = 0;
889
890     outstandingCommands.push(cmdObj);
891     if (outstandingCommands.length == 1) {
892         handleOutstanding();
893     }
894 }
895
896 function getSurfaceId(ev) {
897     var surface = ev.target.surface;
898     if (surface != undefined)
899         return surface.id;
900     return 0;
901 }
902
903 function sendInput(cmd, args)
904 {
905     if (inputSocket != null) {
906         inputSocket.send(cmd + ([lastSerial, lastTimeStamp].concat(args)).join(","));
907     }
908 }
909
910 function getPositionsFromAbsCoord(absX, absY, relativeId) {
911     var res = Object();
912
913     res.rootX = absX;
914     res.rootY = absY;
915     res.winX = absX;
916     res.winY = absY;
917     if (relativeId != 0) {
918         var surface = surfaces[relativeId];
919         res.winX = res.winX - surface.x;
920         res.winY = res.winY - surface.y;
921     }
922
923     return res;
924 }
925
926 function getPositionsFromEvent(ev, relativeId) {
927     var absX, absY;
928     if (useToplevelWindows) {
929         absX = ev.screenX;
930         absY = ev.screenY;
931     } else {
932         absX = ev.pageX;
933         absY = ev.pageY;
934     }
935     var res = getPositionsFromAbsCoord(absX, absY, relativeId);
936
937     lastX = res.rootX;
938     lastY = res.rootY;
939
940     return res;
941 }
942
943 function getEffectiveEventTarget (id) {
944     if (grab.window != null) {
945         if (!grab.ownerEvents)
946             return grab.window;
947         if (id == 0)
948             return grab.window;
949     }
950     return id;
951 }
952
953 function updateForEvent(ev) {
954     lastTimeStamp = ev.timeStamp;
955     if (ev.target.surface && ev.target.surface.window) {
956         var win = ev.target.surface.window;
957         updateBrowserWindowGeometry(win, false);
958     }
959 }
960
961 function onMouseMove (ev) {
962     updateForEvent(ev);
963     if (localGrab) {
964         var dx = ev.pageX - localGrab.lastX;
965         var dy = ev.pageY - localGrab.lastY;
966         var surface = localGrab.frame.frameFor;
967         surface.x += dx;
968         surface.y += dy;
969         var offset = getFrameOffset(surface);
970         localGrab.frame.style["left"] = (surface.x - offset.x) + "px";
971         localGrab.frame.style["top"] = (surface.y - offset.y) + "px";
972         sendConfigureNotify(surface);
973         localGrab.lastX = ev.pageX;
974         localGrab.lastY = ev.pageY;
975         return;
976     }
977     var id = getSurfaceId(ev);
978     id = getEffectiveEventTarget (id);
979     var pos = getPositionsFromEvent(ev, id);
980     sendInput ("m", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState]);
981 }
982
983 function onMouseOver (ev) {
984     updateForEvent(ev);
985     if (localGrab)
986         return;
987     var id = getSurfaceId(ev);
988     realWindowWithMouse = id;
989     id = getEffectiveEventTarget (id);
990     var pos = getPositionsFromEvent(ev, id);
991     windowWithMouse = id;
992     if (windowWithMouse != 0) {
993         sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
994     }
995 }
996
997 function onMouseOut (ev) {
998     updateForEvent(ev);
999     if (localGrab)
1000         return;
1001     var id = getSurfaceId(ev);
1002     var origId = id;
1003     id = getEffectiveEventTarget (id);
1004     var pos = getPositionsFromEvent(ev, id);
1005
1006     if (id != 0) {
1007         sendInput ("l", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
1008     }
1009     realWindowWithMouse = 0;
1010     windowWithMouse = 0;
1011 }
1012
1013 function doGrab(id, ownerEvents, implicit) {
1014     var pos;
1015
1016     if (windowWithMouse != id) {
1017         if (windowWithMouse != 0) {
1018             pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
1019             sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
1020         }
1021         pos = getPositionsFromAbsCoord(lastX, lastY, id);
1022         sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
1023         windowWithMouse = id;
1024     }
1025
1026     grab.window = id;
1027     grab.ownerEvents = ownerEvents;
1028     grab.implicit = implicit;
1029 }
1030
1031 function doUngrab() {
1032     var pos;
1033     if (realWindowWithMouse != windowWithMouse) {
1034         if (windowWithMouse != 0) {
1035             pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
1036             sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
1037         }
1038         if (realWindowWithMouse != 0) {
1039             pos = getPositionsFromAbsCoord(lastX, lastY, realWindowWithMouse);
1040             sendInput ("e", [realWindowWithMouse, realWindowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
1041         }
1042         windowWithMouse = realWindowWithMouse;
1043     }
1044     grab.window = null;
1045 }
1046
1047 function onMouseDown (ev) {
1048     updateForEvent(ev);
1049     var button = ev.button + 1;
1050     lastState = lastState | getButtonMask (button);
1051     var id = getSurfaceId(ev);
1052     id = getEffectiveEventTarget (id);
1053
1054     if (id == 0 && ev.target.frameFor) { /* mouse click on frame */
1055         localGrab = new Object();
1056         localGrab.frame = ev.target;
1057         localGrab.lastX = ev.pageX;
1058         localGrab.lastY = ev.pageY;
1059         moveToTop(localGrab.frame.frameFor);
1060         return;
1061     }
1062
1063     var pos = getPositionsFromEvent(ev, id);
1064     if (grab.window == null)
1065         doGrab (id, false, true);
1066     sendInput ("b", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
1067 }
1068
1069 function onMouseUp (ev) {
1070     updateForEvent(ev);
1071     var button = ev.button + 1;
1072     lastState = lastState & ~getButtonMask (button);
1073     var evId = getSurfaceId(ev);
1074     id = getEffectiveEventTarget (evId);
1075     var pos = getPositionsFromEvent(ev, id);
1076
1077     if (localGrab) {
1078         localGrab = null;
1079         realWindowWithMouse = evId;
1080         if (windowWithMouse != id) {
1081             if (windowWithMouse != 0) {
1082                 sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
1083             }
1084             windowWithMouse = id;
1085             if (windowWithMouse != 0) {
1086                 sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
1087             }
1088         }
1089         return;
1090     }
1091
1092     sendInput ("B", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
1093
1094     if (grab.window != null && grab.implicit)
1095         doUngrab(ev.timeStamp);
1096 }
1097
1098 /* Some of the keyboard handling code is from noVNC and
1099  * (c) Joel Martin (github@martintribe.org), used with permission
1100  *  Original code at:
1101  * https://github.com/kanaka/noVNC/blob/master/include/input.js
1102  */
1103
1104 var unicodeTable = {
1105     0x0104: 0x01a1,
1106     0x02D8: 0x01a2,
1107     0x0141: 0x01a3,
1108     0x013D: 0x01a5,
1109     0x015A: 0x01a6,
1110     0x0160: 0x01a9,
1111     0x015E: 0x01aa,
1112     0x0164: 0x01ab,
1113     0x0179: 0x01ac,
1114     0x017D: 0x01ae,
1115     0x017B: 0x01af,
1116     0x0105: 0x01b1,
1117     0x02DB: 0x01b2,
1118     0x0142: 0x01b3,
1119     0x013E: 0x01b5,
1120     0x015B: 0x01b6,
1121     0x02C7: 0x01b7,
1122     0x0161: 0x01b9,
1123     0x015F: 0x01ba,
1124     0x0165: 0x01bb,
1125     0x017A: 0x01bc,
1126     0x02DD: 0x01bd,
1127     0x017E: 0x01be,
1128     0x017C: 0x01bf,
1129     0x0154: 0x01c0,
1130     0x0102: 0x01c3,
1131     0x0139: 0x01c5,
1132     0x0106: 0x01c6,
1133     0x010C: 0x01c8,
1134     0x0118: 0x01ca,
1135     0x011A: 0x01cc,
1136     0x010E: 0x01cf,
1137     0x0110: 0x01d0,
1138     0x0143: 0x01d1,
1139     0x0147: 0x01d2,
1140     0x0150: 0x01d5,
1141     0x0158: 0x01d8,
1142     0x016E: 0x01d9,
1143     0x0170: 0x01db,
1144     0x0162: 0x01de,
1145     0x0155: 0x01e0,
1146     0x0103: 0x01e3,
1147     0x013A: 0x01e5,
1148     0x0107: 0x01e6,
1149     0x010D: 0x01e8,
1150     0x0119: 0x01ea,
1151     0x011B: 0x01ec,
1152     0x010F: 0x01ef,
1153     0x0111: 0x01f0,
1154     0x0144: 0x01f1,
1155     0x0148: 0x01f2,
1156     0x0151: 0x01f5,
1157     0x0171: 0x01fb,
1158     0x0159: 0x01f8,
1159     0x016F: 0x01f9,
1160     0x0163: 0x01fe,
1161     0x02D9: 0x01ff,
1162     0x0126: 0x02a1,
1163     0x0124: 0x02a6,
1164     0x0130: 0x02a9,
1165     0x011E: 0x02ab,
1166     0x0134: 0x02ac,
1167     0x0127: 0x02b1,
1168     0x0125: 0x02b6,
1169     0x0131: 0x02b9,
1170     0x011F: 0x02bb,
1171     0x0135: 0x02bc,
1172     0x010A: 0x02c5,
1173     0x0108: 0x02c6,
1174     0x0120: 0x02d5,
1175     0x011C: 0x02d8,
1176     0x016C: 0x02dd,
1177     0x015C: 0x02de,
1178     0x010B: 0x02e5,
1179     0x0109: 0x02e6,
1180     0x0121: 0x02f5,
1181     0x011D: 0x02f8,
1182     0x016D: 0x02fd,
1183     0x015D: 0x02fe,
1184     0x0138: 0x03a2,
1185     0x0156: 0x03a3,
1186     0x0128: 0x03a5,
1187     0x013B: 0x03a6,
1188     0x0112: 0x03aa,
1189     0x0122: 0x03ab,
1190     0x0166: 0x03ac,
1191     0x0157: 0x03b3,
1192     0x0129: 0x03b5,
1193     0x013C: 0x03b6,
1194     0x0113: 0x03ba,
1195     0x0123: 0x03bb,
1196     0x0167: 0x03bc,
1197     0x014A: 0x03bd,
1198     0x014B: 0x03bf,
1199     0x0100: 0x03c0,
1200     0x012E: 0x03c7,
1201     0x0116: 0x03cc,
1202     0x012A: 0x03cf,
1203     0x0145: 0x03d1,
1204     0x014C: 0x03d2,
1205     0x0136: 0x03d3,
1206     0x0172: 0x03d9,
1207     0x0168: 0x03dd,
1208     0x016A: 0x03de,
1209     0x0101: 0x03e0,
1210     0x012F: 0x03e7,
1211     0x0117: 0x03ec,
1212     0x012B: 0x03ef,
1213     0x0146: 0x03f1,
1214     0x014D: 0x03f2,
1215     0x0137: 0x03f3,
1216     0x0173: 0x03f9,
1217     0x0169: 0x03fd,
1218     0x016B: 0x03fe,
1219     0x1E02: 0x1001e02,
1220     0x1E03: 0x1001e03,
1221     0x1E0A: 0x1001e0a,
1222     0x1E80: 0x1001e80,
1223     0x1E82: 0x1001e82,
1224     0x1E0B: 0x1001e0b,
1225     0x1EF2: 0x1001ef2,
1226     0x1E1E: 0x1001e1e,
1227     0x1E1F: 0x1001e1f,
1228     0x1E40: 0x1001e40,
1229     0x1E41: 0x1001e41,
1230     0x1E56: 0x1001e56,
1231     0x1E81: 0x1001e81,
1232     0x1E57: 0x1001e57,
1233     0x1E83: 0x1001e83,
1234     0x1E60: 0x1001e60,
1235     0x1EF3: 0x1001ef3,
1236     0x1E84: 0x1001e84,
1237     0x1E85: 0x1001e85,
1238     0x1E61: 0x1001e61,
1239     0x0174: 0x1000174,
1240     0x1E6A: 0x1001e6a,
1241     0x0176: 0x1000176,
1242     0x0175: 0x1000175,
1243     0x1E6B: 0x1001e6b,
1244     0x0177: 0x1000177,
1245     0x0152: 0x13bc,
1246     0x0153: 0x13bd,
1247     0x0178: 0x13be,
1248     0x203E: 0x047e,
1249     0x3002: 0x04a1,
1250     0x300C: 0x04a2,
1251     0x300D: 0x04a3,
1252     0x3001: 0x04a4,
1253     0x30FB: 0x04a5,
1254     0x30F2: 0x04a6,
1255     0x30A1: 0x04a7,
1256     0x30A3: 0x04a8,
1257     0x30A5: 0x04a9,
1258     0x30A7: 0x04aa,
1259     0x30A9: 0x04ab,
1260     0x30E3: 0x04ac,
1261     0x30E5: 0x04ad,
1262     0x30E7: 0x04ae,
1263     0x30C3: 0x04af,
1264     0x30FC: 0x04b0,
1265     0x30A2: 0x04b1,
1266     0x30A4: 0x04b2,
1267     0x30A6: 0x04b3,
1268     0x30A8: 0x04b4,
1269     0x30AA: 0x04b5,
1270     0x30AB: 0x04b6,
1271     0x30AD: 0x04b7,
1272     0x30AF: 0x04b8,
1273     0x30B1: 0x04b9,
1274     0x30B3: 0x04ba,
1275     0x30B5: 0x04bb,
1276     0x30B7: 0x04bc,
1277     0x30B9: 0x04bd,
1278     0x30BB: 0x04be,
1279     0x30BD: 0x04bf,
1280     0x30BF: 0x04c0,
1281     0x30C1: 0x04c1,
1282     0x30C4: 0x04c2,
1283     0x30C6: 0x04c3,
1284     0x30C8: 0x04c4,
1285     0x30CA: 0x04c5,
1286     0x30CB: 0x04c6,
1287     0x30CC: 0x04c7,
1288     0x30CD: 0x04c8,
1289     0x30CE: 0x04c9,
1290     0x30CF: 0x04ca,
1291     0x30D2: 0x04cb,
1292     0x30D5: 0x04cc,
1293     0x30D8: 0x04cd,
1294     0x30DB: 0x04ce,
1295     0x30DE: 0x04cf,
1296     0x30DF: 0x04d0,
1297     0x30E0: 0x04d1,
1298     0x30E1: 0x04d2,
1299     0x30E2: 0x04d3,
1300     0x30E4: 0x04d4,
1301     0x30E6: 0x04d5,
1302     0x30E8: 0x04d6,
1303     0x30E9: 0x04d7,
1304     0x30EA: 0x04d8,
1305     0x30EB: 0x04d9,
1306     0x30EC: 0x04da,
1307     0x30ED: 0x04db,
1308     0x30EF: 0x04dc,
1309     0x30F3: 0x04dd,
1310     0x309B: 0x04de,
1311     0x309C: 0x04df,
1312     0x06F0: 0x10006f0,
1313     0x06F1: 0x10006f1,
1314     0x06F2: 0x10006f2,
1315     0x06F3: 0x10006f3,
1316     0x06F4: 0x10006f4,
1317     0x06F5: 0x10006f5,
1318     0x06F6: 0x10006f6,
1319     0x06F7: 0x10006f7,
1320     0x06F8: 0x10006f8,
1321     0x06F9: 0x10006f9,
1322     0x066A: 0x100066a,
1323     0x0670: 0x1000670,
1324     0x0679: 0x1000679,
1325     0x067E: 0x100067e,
1326     0x0686: 0x1000686,
1327     0x0688: 0x1000688,
1328     0x0691: 0x1000691,
1329     0x060C: 0x05ac,
1330     0x06D4: 0x10006d4,
1331     0x0660: 0x1000660,
1332     0x0661: 0x1000661,
1333     0x0662: 0x1000662,
1334     0x0663: 0x1000663,
1335     0x0664: 0x1000664,
1336     0x0665: 0x1000665,
1337     0x0666: 0x1000666,
1338     0x0667: 0x1000667,
1339     0x0668: 0x1000668,
1340     0x0669: 0x1000669,
1341     0x061B: 0x05bb,
1342     0x061F: 0x05bf,
1343     0x0621: 0x05c1,
1344     0x0622: 0x05c2,
1345     0x0623: 0x05c3,
1346     0x0624: 0x05c4,
1347     0x0625: 0x05c5,
1348     0x0626: 0x05c6,
1349     0x0627: 0x05c7,
1350     0x0628: 0x05c8,
1351     0x0629: 0x05c9,
1352     0x062A: 0x05ca,
1353     0x062B: 0x05cb,
1354     0x062C: 0x05cc,
1355     0x062D: 0x05cd,
1356     0x062E: 0x05ce,
1357     0x062F: 0x05cf,
1358     0x0630: 0x05d0,
1359     0x0631: 0x05d1,
1360     0x0632: 0x05d2,
1361     0x0633: 0x05d3,
1362     0x0634: 0x05d4,
1363     0x0635: 0x05d5,
1364     0x0636: 0x05d6,
1365     0x0637: 0x05d7,
1366     0x0638: 0x05d8,
1367     0x0639: 0x05d9,
1368     0x063A: 0x05da,
1369     0x0640: 0x05e0,
1370     0x0641: 0x05e1,
1371     0x0642: 0x05e2,
1372     0x0643: 0x05e3,
1373     0x0644: 0x05e4,
1374     0x0645: 0x05e5,
1375     0x0646: 0x05e6,
1376     0x0647: 0x05e7,
1377     0x0648: 0x05e8,
1378     0x0649: 0x05e9,
1379     0x064A: 0x05ea,
1380     0x064B: 0x05eb,
1381     0x064C: 0x05ec,
1382     0x064D: 0x05ed,
1383     0x064E: 0x05ee,
1384     0x064F: 0x05ef,
1385     0x0650: 0x05f0,
1386     0x0651: 0x05f1,
1387     0x0652: 0x05f2,
1388     0x0653: 0x1000653,
1389     0x0654: 0x1000654,
1390     0x0655: 0x1000655,
1391     0x0698: 0x1000698,
1392     0x06A4: 0x10006a4,
1393     0x06A9: 0x10006a9,
1394     0x06AF: 0x10006af,
1395     0x06BA: 0x10006ba,
1396     0x06BE: 0x10006be,
1397     0x06CC: 0x10006cc,
1398     0x06D2: 0x10006d2,
1399     0x06C1: 0x10006c1,
1400     0x0492: 0x1000492,
1401     0x0493: 0x1000493,
1402     0x0496: 0x1000496,
1403     0x0497: 0x1000497,
1404     0x049A: 0x100049a,
1405     0x049B: 0x100049b,
1406     0x049C: 0x100049c,
1407     0x049D: 0x100049d,
1408     0x04A2: 0x10004a2,
1409     0x04A3: 0x10004a3,
1410     0x04AE: 0x10004ae,
1411     0x04AF: 0x10004af,
1412     0x04B0: 0x10004b0,
1413     0x04B1: 0x10004b1,
1414     0x04B2: 0x10004b2,
1415     0x04B3: 0x10004b3,
1416     0x04B6: 0x10004b6,
1417     0x04B7: 0x10004b7,
1418     0x04B8: 0x10004b8,
1419     0x04B9: 0x10004b9,
1420     0x04BA: 0x10004ba,
1421     0x04BB: 0x10004bb,
1422     0x04D8: 0x10004d8,
1423     0x04D9: 0x10004d9,
1424     0x04E2: 0x10004e2,
1425     0x04E3: 0x10004e3,
1426     0x04E8: 0x10004e8,
1427     0x04E9: 0x10004e9,
1428     0x04EE: 0x10004ee,
1429     0x04EF: 0x10004ef,
1430     0x0452: 0x06a1,
1431     0x0453: 0x06a2,
1432     0x0451: 0x06a3,
1433     0x0454: 0x06a4,
1434     0x0455: 0x06a5,
1435     0x0456: 0x06a6,
1436     0x0457: 0x06a7,
1437     0x0458: 0x06a8,
1438     0x0459: 0x06a9,
1439     0x045A: 0x06aa,
1440     0x045B: 0x06ab,
1441     0x045C: 0x06ac,
1442     0x0491: 0x06ad,
1443     0x045E: 0x06ae,
1444     0x045F: 0x06af,
1445     0x2116: 0x06b0,
1446     0x0402: 0x06b1,
1447     0x0403: 0x06b2,
1448     0x0401: 0x06b3,
1449     0x0404: 0x06b4,
1450     0x0405: 0x06b5,
1451     0x0406: 0x06b6,
1452     0x0407: 0x06b7,
1453     0x0408: 0x06b8,
1454     0x0409: 0x06b9,
1455     0x040A: 0x06ba,
1456     0x040B: 0x06bb,
1457     0x040C: 0x06bc,
1458     0x0490: 0x06bd,
1459     0x040E: 0x06be,
1460     0x040F: 0x06bf,
1461     0x044E: 0x06c0,
1462     0x0430: 0x06c1,
1463     0x0431: 0x06c2,
1464     0x0446: 0x06c3,
1465     0x0434: 0x06c4,
1466     0x0435: 0x06c5,
1467     0x0444: 0x06c6,
1468     0x0433: 0x06c7,
1469     0x0445: 0x06c8,
1470     0x0438: 0x06c9,
1471     0x0439: 0x06ca,
1472     0x043A: 0x06cb,
1473     0x043B: 0x06cc,
1474     0x043C: 0x06cd,
1475     0x043D: 0x06ce,
1476     0x043E: 0x06cf,
1477     0x043F: 0x06d0,
1478     0x044F: 0x06d1,
1479     0x0440: 0x06d2,
1480     0x0441: 0x06d3,
1481     0x0442: 0x06d4,
1482     0x0443: 0x06d5,
1483     0x0436: 0x06d6,
1484     0x0432: 0x06d7,
1485     0x044C: 0x06d8,
1486     0x044B: 0x06d9,
1487     0x0437: 0x06da,
1488     0x0448: 0x06db,
1489     0x044D: 0x06dc,
1490     0x0449: 0x06dd,
1491     0x0447: 0x06de,
1492     0x044A: 0x06df,
1493     0x042E: 0x06e0,
1494     0x0410: 0x06e1,
1495     0x0411: 0x06e2,
1496     0x0426: 0x06e3,
1497     0x0414: 0x06e4,
1498     0x0415: 0x06e5,
1499     0x0424: 0x06e6,
1500     0x0413: 0x06e7,
1501     0x0425: 0x06e8,
1502     0x0418: 0x06e9,
1503     0x0419: 0x06ea,
1504     0x041A: 0x06eb,
1505     0x041B: 0x06ec,
1506     0x041C: 0x06ed,
1507     0x041D: 0x06ee,
1508     0x041E: 0x06ef,
1509     0x041F: 0x06f0,
1510     0x042F: 0x06f1,
1511     0x0420: 0x06f2,
1512     0x0421: 0x06f3,
1513     0x0422: 0x06f4,
1514     0x0423: 0x06f5,
1515     0x0416: 0x06f6,
1516     0x0412: 0x06f7,
1517     0x042C: 0x06f8,
1518     0x042B: 0x06f9,
1519     0x0417: 0x06fa,
1520     0x0428: 0x06fb,
1521     0x042D: 0x06fc,
1522     0x0429: 0x06fd,
1523     0x0427: 0x06fe,
1524     0x042A: 0x06ff,
1525     0x0386: 0x07a1,
1526     0x0388: 0x07a2,
1527     0x0389: 0x07a3,
1528     0x038A: 0x07a4,
1529     0x03AA: 0x07a5,
1530     0x038C: 0x07a7,
1531     0x038E: 0x07a8,
1532     0x03AB: 0x07a9,
1533     0x038F: 0x07ab,
1534     0x0385: 0x07ae,
1535     0x2015: 0x07af,
1536     0x03AC: 0x07b1,
1537     0x03AD: 0x07b2,
1538     0x03AE: 0x07b3,
1539     0x03AF: 0x07b4,
1540     0x03CA: 0x07b5,
1541     0x0390: 0x07b6,
1542     0x03CC: 0x07b7,
1543     0x03CD: 0x07b8,
1544     0x03CB: 0x07b9,
1545     0x03B0: 0x07ba,
1546     0x03CE: 0x07bb,
1547     0x0391: 0x07c1,
1548     0x0392: 0x07c2,
1549     0x0393: 0x07c3,
1550     0x0394: 0x07c4,
1551     0x0395: 0x07c5,
1552     0x0396: 0x07c6,
1553     0x0397: 0x07c7,
1554     0x0398: 0x07c8,
1555     0x0399: 0x07c9,
1556     0x039A: 0x07ca,
1557     0x039B: 0x07cb,
1558     0x039C: 0x07cc,
1559     0x039D: 0x07cd,
1560     0x039E: 0x07ce,
1561     0x039F: 0x07cf,
1562     0x03A0: 0x07d0,
1563     0x03A1: 0x07d1,
1564     0x03A3: 0x07d2,
1565     0x03A4: 0x07d4,
1566     0x03A5: 0x07d5,
1567     0x03A6: 0x07d6,
1568     0x03A7: 0x07d7,
1569     0x03A8: 0x07d8,
1570     0x03A9: 0x07d9,
1571     0x03B1: 0x07e1,
1572     0x03B2: 0x07e2,
1573     0x03B3: 0x07e3,
1574     0x03B4: 0x07e4,
1575     0x03B5: 0x07e5,
1576     0x03B6: 0x07e6,
1577     0x03B7: 0x07e7,
1578     0x03B8: 0x07e8,
1579     0x03B9: 0x07e9,
1580     0x03BA: 0x07ea,
1581     0x03BB: 0x07eb,
1582     0x03BC: 0x07ec,
1583     0x03BD: 0x07ed,
1584     0x03BE: 0x07ee,
1585     0x03BF: 0x07ef,
1586     0x03C0: 0x07f0,
1587     0x03C1: 0x07f1,
1588     0x03C3: 0x07f2,
1589     0x03C2: 0x07f3,
1590     0x03C4: 0x07f4,
1591     0x03C5: 0x07f5,
1592     0x03C6: 0x07f6,
1593     0x03C7: 0x07f7,
1594     0x03C8: 0x07f8,
1595     0x03C9: 0x07f9,
1596     0x23B7: 0x08a1,
1597     0x2320: 0x08a4,
1598     0x2321: 0x08a5,
1599     0x23A1: 0x08a7,
1600     0x23A3: 0x08a8,
1601     0x23A4: 0x08a9,
1602     0x23A6: 0x08aa,
1603     0x239B: 0x08ab,
1604     0x239D: 0x08ac,
1605     0x239E: 0x08ad,
1606     0x23A0: 0x08ae,
1607     0x23A8: 0x08af,
1608     0x23AC: 0x08b0,
1609     0x2264: 0x08bc,
1610     0x2260: 0x08bd,
1611     0x2265: 0x08be,
1612     0x222B: 0x08bf,
1613     0x2234: 0x08c0,
1614     0x221D: 0x08c1,
1615     0x221E: 0x08c2,
1616     0x2207: 0x08c5,
1617     0x223C: 0x08c8,
1618     0x2243: 0x08c9,
1619     0x21D4: 0x08cd,
1620     0x21D2: 0x08ce,
1621     0x2261: 0x08cf,
1622     0x221A: 0x08d6,
1623     0x2282: 0x08da,
1624     0x2283: 0x08db,
1625     0x2229: 0x08dc,
1626     0x222A: 0x08dd,
1627     0x2227: 0x08de,
1628     0x2228: 0x08df,
1629     0x2202: 0x08ef,
1630     0x0192: 0x08f6,
1631     0x2190: 0x08fb,
1632     0x2191: 0x08fc,
1633     0x2192: 0x08fd,
1634     0x2193: 0x08fe,
1635     0x25C6: 0x09e0,
1636     0x2592: 0x09e1,
1637     0x2409: 0x09e2,
1638     0x240C: 0x09e3,
1639     0x240D: 0x09e4,
1640     0x240A: 0x09e5,
1641     0x2424: 0x09e8,
1642     0x240B: 0x09e9,
1643     0x2518: 0x09ea,
1644     0x2510: 0x09eb,
1645     0x250C: 0x09ec,
1646     0x2514: 0x09ed,
1647     0x253C: 0x09ee,
1648     0x23BA: 0x09ef,
1649     0x23BB: 0x09f0,
1650     0x2500: 0x09f1,
1651     0x23BC: 0x09f2,
1652     0x23BD: 0x09f3,
1653     0x251C: 0x09f4,
1654     0x2524: 0x09f5,
1655     0x2534: 0x09f6,
1656     0x252C: 0x09f7,
1657     0x2502: 0x09f8,
1658     0x2003: 0x0aa1,
1659     0x2002: 0x0aa2,
1660     0x2004: 0x0aa3,
1661     0x2005: 0x0aa4,
1662     0x2007: 0x0aa5,
1663     0x2008: 0x0aa6,
1664     0x2009: 0x0aa7,
1665     0x200A: 0x0aa8,
1666     0x2014: 0x0aa9,
1667     0x2013: 0x0aaa,
1668     0x2026: 0x0aae,
1669     0x2025: 0x0aaf,
1670     0x2153: 0x0ab0,
1671     0x2154: 0x0ab1,
1672     0x2155: 0x0ab2,
1673     0x2156: 0x0ab3,
1674     0x2157: 0x0ab4,
1675     0x2158: 0x0ab5,
1676     0x2159: 0x0ab6,
1677     0x215A: 0x0ab7,
1678     0x2105: 0x0ab8,
1679     0x2012: 0x0abb,
1680     0x215B: 0x0ac3,
1681     0x215C: 0x0ac4,
1682     0x215D: 0x0ac5,
1683     0x215E: 0x0ac6,
1684     0x2122: 0x0ac9,
1685     0x2018: 0x0ad0,
1686     0x2019: 0x0ad1,
1687     0x201C: 0x0ad2,
1688     0x201D: 0x0ad3,
1689     0x211E: 0x0ad4,
1690     0x2032: 0x0ad6,
1691     0x2033: 0x0ad7,
1692     0x271D: 0x0ad9,
1693     0x2663: 0x0aec,
1694     0x2666: 0x0aed,
1695     0x2665: 0x0aee,
1696     0x2720: 0x0af0,
1697     0x2020: 0x0af1,
1698     0x2021: 0x0af2,
1699     0x2713: 0x0af3,
1700     0x2717: 0x0af4,
1701     0x266F: 0x0af5,
1702     0x266D: 0x0af6,
1703     0x2642: 0x0af7,
1704     0x2640: 0x0af8,
1705     0x260E: 0x0af9,
1706     0x2315: 0x0afa,
1707     0x2117: 0x0afb,
1708     0x2038: 0x0afc,
1709     0x201A: 0x0afd,
1710     0x201E: 0x0afe,
1711     0x22A4: 0x0bc2,
1712     0x230A: 0x0bc4,
1713     0x2218: 0x0bca,
1714     0x2395: 0x0bcc,
1715     0x22A5: 0x0bce,
1716     0x25CB: 0x0bcf,
1717     0x2308: 0x0bd3,
1718     0x22A3: 0x0bdc,
1719     0x22A2: 0x0bfc,
1720     0x2017: 0x0cdf,
1721     0x05D0: 0x0ce0,
1722     0x05D1: 0x0ce1,
1723     0x05D2: 0x0ce2,
1724     0x05D3: 0x0ce3,
1725     0x05D4: 0x0ce4,
1726     0x05D5: 0x0ce5,
1727     0x05D6: 0x0ce6,
1728     0x05D7: 0x0ce7,
1729     0x05D8: 0x0ce8,
1730     0x05D9: 0x0ce9,
1731     0x05DA: 0x0cea,
1732     0x05DB: 0x0ceb,
1733     0x05DC: 0x0cec,
1734     0x05DD: 0x0ced,
1735     0x05DE: 0x0cee,
1736     0x05DF: 0x0cef,
1737     0x05E0: 0x0cf0,
1738     0x05E1: 0x0cf1,
1739     0x05E2: 0x0cf2,
1740     0x05E3: 0x0cf3,
1741     0x05E4: 0x0cf4,
1742     0x05E5: 0x0cf5,
1743     0x05E6: 0x0cf6,
1744     0x05E7: 0x0cf7,
1745     0x05E8: 0x0cf8,
1746     0x05E9: 0x0cf9,
1747     0x05EA: 0x0cfa,
1748     0x0E01: 0x0da1,
1749     0x0E02: 0x0da2,
1750     0x0E03: 0x0da3,
1751     0x0E04: 0x0da4,
1752     0x0E05: 0x0da5,
1753     0x0E06: 0x0da6,
1754     0x0E07: 0x0da7,
1755     0x0E08: 0x0da8,
1756     0x0E09: 0x0da9,
1757     0x0E0A: 0x0daa,
1758     0x0E0B: 0x0dab,
1759     0x0E0C: 0x0dac,
1760     0x0E0D: 0x0dad,
1761     0x0E0E: 0x0dae,
1762     0x0E0F: 0x0daf,
1763     0x0E10: 0x0db0,
1764     0x0E11: 0x0db1,
1765     0x0E12: 0x0db2,
1766     0x0E13: 0x0db3,
1767     0x0E14: 0x0db4,
1768     0x0E15: 0x0db5,
1769     0x0E16: 0x0db6,
1770     0x0E17: 0x0db7,
1771     0x0E18: 0x0db8,
1772     0x0E19: 0x0db9,
1773     0x0E1A: 0x0dba,
1774     0x0E1B: 0x0dbb,
1775     0x0E1C: 0x0dbc,
1776     0x0E1D: 0x0dbd,
1777     0x0E1E: 0x0dbe,
1778     0x0E1F: 0x0dbf,
1779     0x0E20: 0x0dc0,
1780     0x0E21: 0x0dc1,
1781     0x0E22: 0x0dc2,
1782     0x0E23: 0x0dc3,
1783     0x0E24: 0x0dc4,
1784     0x0E25: 0x0dc5,
1785     0x0E26: 0x0dc6,
1786     0x0E27: 0x0dc7,
1787     0x0E28: 0x0dc8,
1788     0x0E29: 0x0dc9,
1789     0x0E2A: 0x0dca,
1790     0x0E2B: 0x0dcb,
1791     0x0E2C: 0x0dcc,
1792     0x0E2D: 0x0dcd,
1793     0x0E2E: 0x0dce,
1794     0x0E2F: 0x0dcf,
1795     0x0E30: 0x0dd0,
1796     0x0E31: 0x0dd1,
1797     0x0E32: 0x0dd2,
1798     0x0E33: 0x0dd3,
1799     0x0E34: 0x0dd4,
1800     0x0E35: 0x0dd5,
1801     0x0E36: 0x0dd6,
1802     0x0E37: 0x0dd7,
1803     0x0E38: 0x0dd8,
1804     0x0E39: 0x0dd9,
1805     0x0E3A: 0x0dda,
1806     0x0E3F: 0x0ddf,
1807     0x0E40: 0x0de0,
1808     0x0E41: 0x0de1,
1809     0x0E42: 0x0de2,
1810     0x0E43: 0x0de3,
1811     0x0E44: 0x0de4,
1812     0x0E45: 0x0de5,
1813     0x0E46: 0x0de6,
1814     0x0E47: 0x0de7,
1815     0x0E48: 0x0de8,
1816     0x0E49: 0x0de9,
1817     0x0E4A: 0x0dea,
1818     0x0E4B: 0x0deb,
1819     0x0E4C: 0x0dec,
1820     0x0E4D: 0x0ded,
1821     0x0E50: 0x0df0,
1822     0x0E51: 0x0df1,
1823     0x0E52: 0x0df2,
1824     0x0E53: 0x0df3,
1825     0x0E54: 0x0df4,
1826     0x0E55: 0x0df5,
1827     0x0E56: 0x0df6,
1828     0x0E57: 0x0df7,
1829     0x0E58: 0x0df8,
1830     0x0E59: 0x0df9,
1831     0x0587: 0x1000587,
1832     0x0589: 0x1000589,
1833     0x055D: 0x100055d,
1834     0x058A: 0x100058a,
1835     0x055C: 0x100055c,
1836     0x055B: 0x100055b,
1837     0x055E: 0x100055e,
1838     0x0531: 0x1000531,
1839     0x0561: 0x1000561,
1840     0x0532: 0x1000532,
1841     0x0562: 0x1000562,
1842     0x0533: 0x1000533,
1843     0x0563: 0x1000563,
1844     0x0534: 0x1000534,
1845     0x0564: 0x1000564,
1846     0x0535: 0x1000535,
1847     0x0565: 0x1000565,
1848     0x0536: 0x1000536,
1849     0x0566: 0x1000566,
1850     0x0537: 0x1000537,
1851     0x0567: 0x1000567,
1852     0x0538: 0x1000538,
1853     0x0568: 0x1000568,
1854     0x0539: 0x1000539,
1855     0x0569: 0x1000569,
1856     0x053A: 0x100053a,
1857     0x056A: 0x100056a,
1858     0x053B: 0x100053b,
1859     0x056B: 0x100056b,
1860     0x053C: 0x100053c,
1861     0x056C: 0x100056c,
1862     0x053D: 0x100053d,
1863     0x056D: 0x100056d,
1864     0x053E: 0x100053e,
1865     0x056E: 0x100056e,
1866     0x053F: 0x100053f,
1867     0x056F: 0x100056f,
1868     0x0540: 0x1000540,
1869     0x0570: 0x1000570,
1870     0x0541: 0x1000541,
1871     0x0571: 0x1000571,
1872     0x0542: 0x1000542,
1873     0x0572: 0x1000572,
1874     0x0543: 0x1000543,
1875     0x0573: 0x1000573,
1876     0x0544: 0x1000544,
1877     0x0574: 0x1000574,
1878     0x0545: 0x1000545,
1879     0x0575: 0x1000575,
1880     0x0546: 0x1000546,
1881     0x0576: 0x1000576,
1882     0x0547: 0x1000547,
1883     0x0577: 0x1000577,
1884     0x0548: 0x1000548,
1885     0x0578: 0x1000578,
1886     0x0549: 0x1000549,
1887     0x0579: 0x1000579,
1888     0x054A: 0x100054a,
1889     0x057A: 0x100057a,
1890     0x054B: 0x100054b,
1891     0x057B: 0x100057b,
1892     0x054C: 0x100054c,
1893     0x057C: 0x100057c,
1894     0x054D: 0x100054d,
1895     0x057D: 0x100057d,
1896     0x054E: 0x100054e,
1897     0x057E: 0x100057e,
1898     0x054F: 0x100054f,
1899     0x057F: 0x100057f,
1900     0x0550: 0x1000550,
1901     0x0580: 0x1000580,
1902     0x0551: 0x1000551,
1903     0x0581: 0x1000581,
1904     0x0552: 0x1000552,
1905     0x0582: 0x1000582,
1906     0x0553: 0x1000553,
1907     0x0583: 0x1000583,
1908     0x0554: 0x1000554,
1909     0x0584: 0x1000584,
1910     0x0555: 0x1000555,
1911     0x0585: 0x1000585,
1912     0x0556: 0x1000556,
1913     0x0586: 0x1000586,
1914     0x055A: 0x100055a,
1915     0x10D0: 0x10010d0,
1916     0x10D1: 0x10010d1,
1917     0x10D2: 0x10010d2,
1918     0x10D3: 0x10010d3,
1919     0x10D4: 0x10010d4,
1920     0x10D5: 0x10010d5,
1921     0x10D6: 0x10010d6,
1922     0x10D7: 0x10010d7,
1923     0x10D8: 0x10010d8,
1924     0x10D9: 0x10010d9,
1925     0x10DA: 0x10010da,
1926     0x10DB: 0x10010db,
1927     0x10DC: 0x10010dc,
1928     0x10DD: 0x10010dd,
1929     0x10DE: 0x10010de,
1930     0x10DF: 0x10010df,
1931     0x10E0: 0x10010e0,
1932     0x10E1: 0x10010e1,
1933     0x10E2: 0x10010e2,
1934     0x10E3: 0x10010e3,
1935     0x10E4: 0x10010e4,
1936     0x10E5: 0x10010e5,
1937     0x10E6: 0x10010e6,
1938     0x10E7: 0x10010e7,
1939     0x10E8: 0x10010e8,
1940     0x10E9: 0x10010e9,
1941     0x10EA: 0x10010ea,
1942     0x10EB: 0x10010eb,
1943     0x10EC: 0x10010ec,
1944     0x10ED: 0x10010ed,
1945     0x10EE: 0x10010ee,
1946     0x10EF: 0x10010ef,
1947     0x10F0: 0x10010f0,
1948     0x10F1: 0x10010f1,
1949     0x10F2: 0x10010f2,
1950     0x10F3: 0x10010f3,
1951     0x10F4: 0x10010f4,
1952     0x10F5: 0x10010f5,
1953     0x10F6: 0x10010f6,
1954     0x1E8A: 0x1001e8a,
1955     0x012C: 0x100012c,
1956     0x01B5: 0x10001b5,
1957     0x01E6: 0x10001e6,
1958     0x01D2: 0x10001d1,
1959     0x019F: 0x100019f,
1960     0x1E8B: 0x1001e8b,
1961     0x012D: 0x100012d,
1962     0x01B6: 0x10001b6,
1963     0x01E7: 0x10001e7,
1964     0x01D2: 0x10001d2,
1965     0x0275: 0x1000275,
1966     0x018F: 0x100018f,
1967     0x0259: 0x1000259,
1968     0x1E36: 0x1001e36,
1969     0x1E37: 0x1001e37,
1970     0x1EA0: 0x1001ea0,
1971     0x1EA1: 0x1001ea1,
1972     0x1EA2: 0x1001ea2,
1973     0x1EA3: 0x1001ea3,
1974     0x1EA4: 0x1001ea4,
1975     0x1EA5: 0x1001ea5,
1976     0x1EA6: 0x1001ea6,
1977     0x1EA7: 0x1001ea7,
1978     0x1EA8: 0x1001ea8,
1979     0x1EA9: 0x1001ea9,
1980     0x1EAA: 0x1001eaa,
1981     0x1EAB: 0x1001eab,
1982     0x1EAC: 0x1001eac,
1983     0x1EAD: 0x1001ead,
1984     0x1EAE: 0x1001eae,
1985     0x1EAF: 0x1001eaf,
1986     0x1EB0: 0x1001eb0,
1987     0x1EB1: 0x1001eb1,
1988     0x1EB2: 0x1001eb2,
1989     0x1EB3: 0x1001eb3,
1990     0x1EB4: 0x1001eb4,
1991     0x1EB5: 0x1001eb5,
1992     0x1EB6: 0x1001eb6,
1993     0x1EB7: 0x1001eb7,
1994     0x1EB8: 0x1001eb8,
1995     0x1EB9: 0x1001eb9,
1996     0x1EBA: 0x1001eba,
1997     0x1EBB: 0x1001ebb,
1998     0x1EBC: 0x1001ebc,
1999     0x1EBD: 0x1001ebd,
2000     0x1EBE: 0x1001ebe,
2001     0x1EBF: 0x1001ebf,
2002     0x1EC0: 0x1001ec0,
2003     0x1EC1: 0x1001ec1,
2004     0x1EC2: 0x1001ec2,
2005     0x1EC3: 0x1001ec3,
2006     0x1EC4: 0x1001ec4,
2007     0x1EC5: 0x1001ec5,
2008     0x1EC6: 0x1001ec6,
2009     0x1EC7: 0x1001ec7,
2010     0x1EC8: 0x1001ec8,
2011     0x1EC9: 0x1001ec9,
2012     0x1ECA: 0x1001eca,
2013     0x1ECB: 0x1001ecb,
2014     0x1ECC: 0x1001ecc,
2015     0x1ECD: 0x1001ecd,
2016     0x1ECE: 0x1001ece,
2017     0x1ECF: 0x1001ecf,
2018     0x1ED0: 0x1001ed0,
2019     0x1ED1: 0x1001ed1,
2020     0x1ED2: 0x1001ed2,
2021     0x1ED3: 0x1001ed3,
2022     0x1ED4: 0x1001ed4,
2023     0x1ED5: 0x1001ed5,
2024     0x1ED6: 0x1001ed6,
2025     0x1ED7: 0x1001ed7,
2026     0x1ED8: 0x1001ed8,
2027     0x1ED9: 0x1001ed9,
2028     0x1EDA: 0x1001eda,
2029     0x1EDB: 0x1001edb,
2030     0x1EDC: 0x1001edc,
2031     0x1EDD: 0x1001edd,
2032     0x1EDE: 0x1001ede,
2033     0x1EDF: 0x1001edf,
2034     0x1EE0: 0x1001ee0,
2035     0x1EE1: 0x1001ee1,
2036     0x1EE2: 0x1001ee2,
2037     0x1EE3: 0x1001ee3,
2038     0x1EE4: 0x1001ee4,
2039     0x1EE5: 0x1001ee5,
2040     0x1EE6: 0x1001ee6,
2041     0x1EE7: 0x1001ee7,
2042     0x1EE8: 0x1001ee8,
2043     0x1EE9: 0x1001ee9,
2044     0x1EEA: 0x1001eea,
2045     0x1EEB: 0x1001eeb,
2046     0x1EEC: 0x1001eec,
2047     0x1EED: 0x1001eed,
2048     0x1EEE: 0x1001eee,
2049     0x1EEF: 0x1001eef,
2050     0x1EF0: 0x1001ef0,
2051     0x1EF1: 0x1001ef1,
2052     0x1EF4: 0x1001ef4,
2053     0x1EF5: 0x1001ef5,
2054     0x1EF6: 0x1001ef6,
2055     0x1EF7: 0x1001ef7,
2056     0x1EF8: 0x1001ef8,
2057     0x1EF9: 0x1001ef9,
2058     0x01A0: 0x10001a0,
2059     0x01A1: 0x10001a1,
2060     0x01AF: 0x10001af,
2061     0x01B0: 0x10001b0,
2062     0x20A0: 0x10020a0,
2063     0x20A1: 0x10020a1,
2064     0x20A2: 0x10020a2,
2065     0x20A3: 0x10020a3,
2066     0x20A4: 0x10020a4,
2067     0x20A5: 0x10020a5,
2068     0x20A6: 0x10020a6,
2069     0x20A7: 0x10020a7,
2070     0x20A8: 0x10020a8,
2071     0x20A9: 0x10020a9,
2072     0x20AA: 0x10020aa,
2073     0x20AB: 0x10020ab,
2074     0x20AC: 0x20ac,
2075     0x2070: 0x1002070,
2076     0x2074: 0x1002074,
2077     0x2075: 0x1002075,
2078     0x2076: 0x1002076,
2079     0x2077: 0x1002077,
2080     0x2078: 0x1002078,
2081     0x2079: 0x1002079,
2082     0x2080: 0x1002080,
2083     0x2081: 0x1002081,
2084     0x2082: 0x1002082,
2085     0x2083: 0x1002083,
2086     0x2084: 0x1002084,
2087     0x2085: 0x1002085,
2088     0x2086: 0x1002086,
2089     0x2087: 0x1002087,
2090     0x2088: 0x1002088,
2091     0x2089: 0x1002089,
2092     0x2202: 0x1002202,
2093     0x2205: 0x1002205,
2094     0x2208: 0x1002208,
2095     0x2209: 0x1002209,
2096     0x220B: 0x100220B,
2097     0x221A: 0x100221A,
2098     0x221B: 0x100221B,
2099     0x221C: 0x100221C,
2100     0x222C: 0x100222C,
2101     0x222D: 0x100222D,
2102     0x2235: 0x1002235,
2103     0x2245: 0x1002248,
2104     0x2247: 0x1002247,
2105     0x2262: 0x1002262,
2106     0x2263: 0x1002263,
2107     0x2800: 0x1002800,
2108     0x2801: 0x1002801,
2109     0x2802: 0x1002802,
2110     0x2803: 0x1002803,
2111     0x2804: 0x1002804,
2112     0x2805: 0x1002805,
2113     0x2806: 0x1002806,
2114     0x2807: 0x1002807,
2115     0x2808: 0x1002808,
2116     0x2809: 0x1002809,
2117     0x280a: 0x100280a,
2118     0x280b: 0x100280b,
2119     0x280c: 0x100280c,
2120     0x280d: 0x100280d,
2121     0x280e: 0x100280e,
2122     0x280f: 0x100280f,
2123     0x2810: 0x1002810,
2124     0x2811: 0x1002811,
2125     0x2812: 0x1002812,
2126     0x2813: 0x1002813,
2127     0x2814: 0x1002814,
2128     0x2815: 0x1002815,
2129     0x2816: 0x1002816,
2130     0x2817: 0x1002817,
2131     0x2818: 0x1002818,
2132     0x2819: 0x1002819,
2133     0x281a: 0x100281a,
2134     0x281b: 0x100281b,
2135     0x281c: 0x100281c,
2136     0x281d: 0x100281d,
2137     0x281e: 0x100281e,
2138     0x281f: 0x100281f,
2139     0x2820: 0x1002820,
2140     0x2821: 0x1002821,
2141     0x2822: 0x1002822,
2142     0x2823: 0x1002823,
2143     0x2824: 0x1002824,
2144     0x2825: 0x1002825,
2145     0x2826: 0x1002826,
2146     0x2827: 0x1002827,
2147     0x2828: 0x1002828,
2148     0x2829: 0x1002829,
2149     0x282a: 0x100282a,
2150     0x282b: 0x100282b,
2151     0x282c: 0x100282c,
2152     0x282d: 0x100282d,
2153     0x282e: 0x100282e,
2154     0x282f: 0x100282f,
2155     0x2830: 0x1002830,
2156     0x2831: 0x1002831,
2157     0x2832: 0x1002832,
2158     0x2833: 0x1002833,
2159     0x2834: 0x1002834,
2160     0x2835: 0x1002835,
2161     0x2836: 0x1002836,
2162     0x2837: 0x1002837,
2163     0x2838: 0x1002838,
2164     0x2839: 0x1002839,
2165     0x283a: 0x100283a,
2166     0x283b: 0x100283b,
2167     0x283c: 0x100283c,
2168     0x283d: 0x100283d,
2169     0x283e: 0x100283e,
2170     0x283f: 0x100283f,
2171     0x2840: 0x1002840,
2172     0x2841: 0x1002841,
2173     0x2842: 0x1002842,
2174     0x2843: 0x1002843,
2175     0x2844: 0x1002844,
2176     0x2845: 0x1002845,
2177     0x2846: 0x1002846,
2178     0x2847: 0x1002847,
2179     0x2848: 0x1002848,
2180     0x2849: 0x1002849,
2181     0x284a: 0x100284a,
2182     0x284b: 0x100284b,
2183     0x284c: 0x100284c,
2184     0x284d: 0x100284d,
2185     0x284e: 0x100284e,
2186     0x284f: 0x100284f,
2187     0x2850: 0x1002850,
2188     0x2851: 0x1002851,
2189     0x2852: 0x1002852,
2190     0x2853: 0x1002853,
2191     0x2854: 0x1002854,
2192     0x2855: 0x1002855,
2193     0x2856: 0x1002856,
2194     0x2857: 0x1002857,
2195     0x2858: 0x1002858,
2196     0x2859: 0x1002859,
2197     0x285a: 0x100285a,
2198     0x285b: 0x100285b,
2199     0x285c: 0x100285c,
2200     0x285d: 0x100285d,
2201     0x285e: 0x100285e,
2202     0x285f: 0x100285f,
2203     0x2860: 0x1002860,
2204     0x2861: 0x1002861,
2205     0x2862: 0x1002862,
2206     0x2863: 0x1002863,
2207     0x2864: 0x1002864,
2208     0x2865: 0x1002865,
2209     0x2866: 0x1002866,
2210     0x2867: 0x1002867,
2211     0x2868: 0x1002868,
2212     0x2869: 0x1002869,
2213     0x286a: 0x100286a,
2214     0x286b: 0x100286b,
2215     0x286c: 0x100286c,
2216     0x286d: 0x100286d,
2217     0x286e: 0x100286e,
2218     0x286f: 0x100286f,
2219     0x2870: 0x1002870,
2220     0x2871: 0x1002871,
2221     0x2872: 0x1002872,
2222     0x2873: 0x1002873,
2223     0x2874: 0x1002874,
2224     0x2875: 0x1002875,
2225     0x2876: 0x1002876,
2226     0x2877: 0x1002877,
2227     0x2878: 0x1002878,
2228     0x2879: 0x1002879,
2229     0x287a: 0x100287a,
2230     0x287b: 0x100287b,
2231     0x287c: 0x100287c,
2232     0x287d: 0x100287d,
2233     0x287e: 0x100287e,
2234     0x287f: 0x100287f,
2235     0x2880: 0x1002880,
2236     0x2881: 0x1002881,
2237     0x2882: 0x1002882,
2238     0x2883: 0x1002883,
2239     0x2884: 0x1002884,
2240     0x2885: 0x1002885,
2241     0x2886: 0x1002886,
2242     0x2887: 0x1002887,
2243     0x2888: 0x1002888,
2244     0x2889: 0x1002889,
2245     0x288a: 0x100288a,
2246     0x288b: 0x100288b,
2247     0x288c: 0x100288c,
2248     0x288d: 0x100288d,
2249     0x288e: 0x100288e,
2250     0x288f: 0x100288f,
2251     0x2890: 0x1002890,
2252     0x2891: 0x1002891,
2253     0x2892: 0x1002892,
2254     0x2893: 0x1002893,
2255     0x2894: 0x1002894,
2256     0x2895: 0x1002895,
2257     0x2896: 0x1002896,
2258     0x2897: 0x1002897,
2259     0x2898: 0x1002898,
2260     0x2899: 0x1002899,
2261     0x289a: 0x100289a,
2262     0x289b: 0x100289b,
2263     0x289c: 0x100289c,
2264     0x289d: 0x100289d,
2265     0x289e: 0x100289e,
2266     0x289f: 0x100289f,
2267     0x28a0: 0x10028a0,
2268     0x28a1: 0x10028a1,
2269     0x28a2: 0x10028a2,
2270     0x28a3: 0x10028a3,
2271     0x28a4: 0x10028a4,
2272     0x28a5: 0x10028a5,
2273     0x28a6: 0x10028a6,
2274     0x28a7: 0x10028a7,
2275     0x28a8: 0x10028a8,
2276     0x28a9: 0x10028a9,
2277     0x28aa: 0x10028aa,
2278     0x28ab: 0x10028ab,
2279     0x28ac: 0x10028ac,
2280     0x28ad: 0x10028ad,
2281     0x28ae: 0x10028ae,
2282     0x28af: 0x10028af,
2283     0x28b0: 0x10028b0,
2284     0x28b1: 0x10028b1,
2285     0x28b2: 0x10028b2,
2286     0x28b3: 0x10028b3,
2287     0x28b4: 0x10028b4,
2288     0x28b5: 0x10028b5,
2289     0x28b6: 0x10028b6,
2290     0x28b7: 0x10028b7,
2291     0x28b8: 0x10028b8,
2292     0x28b9: 0x10028b9,
2293     0x28ba: 0x10028ba,
2294     0x28bb: 0x10028bb,
2295     0x28bc: 0x10028bc,
2296     0x28bd: 0x10028bd,
2297     0x28be: 0x10028be,
2298     0x28bf: 0x10028bf,
2299     0x28c0: 0x10028c0,
2300     0x28c1: 0x10028c1,
2301     0x28c2: 0x10028c2,
2302     0x28c3: 0x10028c3,
2303     0x28c4: 0x10028c4,
2304     0x28c5: 0x10028c5,
2305     0x28c6: 0x10028c6,
2306     0x28c7: 0x10028c7,
2307     0x28c8: 0x10028c8,
2308     0x28c9: 0x10028c9,
2309     0x28ca: 0x10028ca,
2310     0x28cb: 0x10028cb,
2311     0x28cc: 0x10028cc,
2312     0x28cd: 0x10028cd,
2313     0x28ce: 0x10028ce,
2314     0x28cf: 0x10028cf,
2315     0x28d0: 0x10028d0,
2316     0x28d1: 0x10028d1,
2317     0x28d2: 0x10028d2,
2318     0x28d3: 0x10028d3,
2319     0x28d4: 0x10028d4,
2320     0x28d5: 0x10028d5,
2321     0x28d6: 0x10028d6,
2322     0x28d7: 0x10028d7,
2323     0x28d8: 0x10028d8,
2324     0x28d9: 0x10028d9,
2325     0x28da: 0x10028da,
2326     0x28db: 0x10028db,
2327     0x28dc: 0x10028dc,
2328     0x28dd: 0x10028dd,
2329     0x28de: 0x10028de,
2330     0x28df: 0x10028df,
2331     0x28e0: 0x10028e0,
2332     0x28e1: 0x10028e1,
2333     0x28e2: 0x10028e2,
2334     0x28e3: 0x10028e3,
2335     0x28e4: 0x10028e4,
2336     0x28e5: 0x10028e5,
2337     0x28e6: 0x10028e6,
2338     0x28e7: 0x10028e7,
2339     0x28e8: 0x10028e8,
2340     0x28e9: 0x10028e9,
2341     0x28ea: 0x10028ea,
2342     0x28eb: 0x10028eb,
2343     0x28ec: 0x10028ec,
2344     0x28ed: 0x10028ed,
2345     0x28ee: 0x10028ee,
2346     0x28ef: 0x10028ef,
2347     0x28f0: 0x10028f0,
2348     0x28f1: 0x10028f1,
2349     0x28f2: 0x10028f2,
2350     0x28f3: 0x10028f3,
2351     0x28f4: 0x10028f4,
2352     0x28f5: 0x10028f5,
2353     0x28f6: 0x10028f6,
2354     0x28f7: 0x10028f7,
2355     0x28f8: 0x10028f8,
2356     0x28f9: 0x10028f9,
2357     0x28fa: 0x10028fa,
2358     0x28fb: 0x10028fb,
2359     0x28fc: 0x10028fc,
2360     0x28fd: 0x10028fd,
2361     0x28fe: 0x10028fe,
2362     0x28ff: 0x10028ff
2363 };
2364
2365 var specialKeyTable = {
2366     8: 0xFF08, // BACKSPACE
2367     13: 0xFF0D, // ENTER
2368     9: 0xFF09, // TAB
2369     27: 0xFF1B, // ESCAPE
2370     46: 0xFFFF, // DELETE
2371     36: 0xFF50, // HOME
2372     35: 0xFF57, // END
2373     33: 0xFF55, // PAGE_UP
2374     34: 0xFF56, // PAGE_DOWN
2375     45: 0xFF63, // INSERT
2376     37: 0xFF51, // LEFT
2377     38: 0xFF52, // UP
2378     39: 0xFF53, // RIGHT
2379     40: 0xFF54, // DOWN
2380     16: 0xFFE1, // SHIFT
2381     17: 0xFFE3, // CONTROL
2382     18: 0xFFE9, // Left ALT (Mac Command)
2383     112: 0xFFBE, // F1
2384     113: 0xFFBF, // F2
2385     114: 0xFFC0, // F3
2386     115: 0xFFC1, // F4
2387     116: 0xFFC2, // F5
2388     117: 0xFFC3, // F6
2389     118: 0xFFC4, // F7
2390     119: 0xFFC5, // F8
2391     120: 0xFFC6, // F9
2392     121: 0xFFC7, // F10
2393     122: 0xFFC8, // F11
2394     123: 0xFFC9  // F12
2395 };
2396
2397 function getEventKeySym(ev) {
2398     if (typeof ev.which !== "undefined" && ev.which > 0)
2399         return ev.which;
2400     return ev.keyCode;
2401 }
2402
2403 // This is based on the approach from noVNC. We handle
2404 // everything in keydown that we have all info for, and that
2405 // are not safe to pass on to the browser (as it may do something
2406 // with the key. The rest we pass on to keypress so we can get the
2407 // translated keysym.
2408 function getKeysymSpecial(ev) {
2409     // These are simple and risky to pass on to browser, handle directly
2410    if ((ev.keyCode in specialKeyTable))
2411        return specialKeyTable[ev.keyCode];
2412
2413     // If we don't hold alt or ctrl, then we should be safe to pass
2414     // on to keypressed and look at the translated data
2415     if (!ev.ctrlKey && !ev.altKey)
2416         return null;
2417
2418     var keysym = getEventKeySym(ev);
2419
2420     /* Remap symbols */
2421     switch (keysym) {
2422     case 186 : keysym = 59; break; // ; (IE)
2423     case 187 : keysym = 61; break; // = (IE)
2424     case 188 : keysym = 44; break; // , (Mozilla, IE)
2425     case 109 : // - (Mozilla, Opera)
2426         if (true /* TODO: check if browser is firefox or opera */)
2427             keysym = 45;
2428         break;
2429     case 189 : keysym = 45; break; // - (IE)
2430     case 190 : keysym = 46; break; // . (Mozilla, IE)
2431     case 191 : keysym = 47; break; // / (Mozilla, IE)
2432     case 192 : keysym = 96; break; // ` (Mozilla, IE)
2433     case 219 : keysym = 91; break; // [ (Mozilla, IE)
2434     case 220 : keysym = 92; break; // \ (Mozilla, IE)
2435     case 221 : keysym = 93; break; // ] (Mozilla, IE)
2436     case 222 : keysym = 39; break; // ' (Mozilla, IE)
2437     }
2438
2439     /* Remap shifted and unshifted keys */
2440     if (!!ev.shiftKey) {
2441         switch (keysym) {
2442         case 48 : keysym = 41 ; break; // ) (shifted 0)
2443         case 49 : keysym = 33 ; break; // ! (shifted 1)
2444         case 50 : keysym = 64 ; break; // @ (shifted 2)
2445         case 51 : keysym = 35 ; break; // # (shifted 3)
2446         case 52 : keysym = 36 ; break; // $ (shifted 4)
2447         case 53 : keysym = 37 ; break; // % (shifted 5)
2448         case 54 : keysym = 94 ; break; // ^ (shifted 6)
2449         case 55 : keysym = 38 ; break; // & (shifted 7)
2450         case 56 : keysym = 42 ; break; // * (shifted 8)
2451         case 57 : keysym = 40 ; break; // ( (shifted 9)
2452         case 59 : keysym = 58 ; break; // : (shifted `)
2453         case 61 : keysym = 43 ; break; // + (shifted ;)
2454         case 44 : keysym = 60 ; break; // < (shifted ,)
2455         case 45 : keysym = 95 ; break; // _ (shifted -)
2456         case 46 : keysym = 62 ; break; // > (shifted .)
2457         case 47 : keysym = 63 ; break; // ? (shifted /)
2458         case 96 : keysym = 126; break; // ~ (shifted `)
2459         case 91 : keysym = 123; break; // { (shifted [)
2460         case 92 : keysym = 124; break; // | (shifted \)
2461         case 93 : keysym = 125; break; // } (shifted ])
2462         case 39 : keysym = 34 ; break; // " (shifted ')
2463         }
2464     } else if ((keysym >= 65) && (keysym <=90)) {
2465         /* Remap unshifted A-Z */
2466         keysym += 32;
2467     } else if (ev.keyLocation === 3) {
2468         // numpad keys
2469         switch (keysym) {
2470         case 96 : keysym = 48; break; // 0
2471         case 97 : keysym = 49; break; // 1
2472         case 98 : keysym = 50; break; // 2
2473         case 99 : keysym = 51; break; // 3
2474         case 100: keysym = 52; break; // 4
2475         case 101: keysym = 53; break; // 5
2476         case 102: keysym = 54; break; // 6
2477         case 103: keysym = 55; break; // 7
2478         case 104: keysym = 56; break; // 8
2479         case 105: keysym = 57; break; // 9
2480         case 109: keysym = 45; break; // -
2481         case 110: keysym = 46; break; // .
2482         case 111: keysym = 47; break; // /
2483         }
2484     }
2485
2486     return keysym;
2487 }
2488
2489 /* Translate DOM keyPress event to keysym value */
2490 function getKeysym(ev) {
2491     var keysym, msg;
2492
2493     keysym = getEventKeySym(ev);
2494
2495     if ((keysym > 255) && (keysym < 0xFF00)) {
2496         // Map Unicode outside Latin 1 to gdk keysyms
2497         keysym = unicodeTable[keysym];
2498         if (typeof keysym === 'undefined')
2499             keysym = 0;
2500     }
2501
2502     return keysym;
2503 }
2504
2505 function copyKeyEvent(ev) {
2506     var members = ['type', 'keyCode', 'charCode', 'which',
2507                    'altKey', 'ctrlKey', 'shiftKey',
2508                    'keyLocation', 'keyIdentifier'], i, obj = {};
2509     for (i = 0; i < members.length; i++) {
2510         if (typeof ev[members[i]] !== "undefined")
2511             obj[members[i]] = ev[members[i]];
2512     }
2513     return obj;
2514 }
2515
2516 function pushKeyEvent(fev) {
2517     keyDownList.push(fev);
2518 }
2519
2520 function getKeyEvent(keyCode, pop) {
2521     var i, fev = null;
2522     for (i = keyDownList.length-1; i >= 0; i--) {
2523         if (keyDownList[i].keyCode === keyCode) {
2524             if ((typeof pop !== "undefined") && pop)
2525                 fev = keyDownList.splice(i, 1)[0];
2526             else
2527                 fev = keyDownList[i];
2528             break;
2529         }
2530     }
2531     return fev;
2532 }
2533
2534 function ignoreKeyEvent(ev) {
2535     // Blarg. Some keys have a different keyCode on keyDown vs keyUp
2536     if (ev.keyCode === 229) {
2537         // French AZERTY keyboard dead key.
2538         // Lame thing is that the respective keyUp is 219 so we can't
2539         // properly ignore the keyUp event
2540         return true;
2541     }
2542     return false;
2543 }
2544
2545 function handleKeyDown(e) {
2546     var fev = null, ev = (e ? e : window.event), keysym = null, suppress = false;
2547
2548     fev = copyKeyEvent(ev);
2549
2550     keysym = getKeysymSpecial(ev);
2551     // Save keysym decoding for use in keyUp
2552     fev.keysym = keysym;
2553     if (keysym) {
2554         // If it is a key or key combination that might trigger
2555         // browser behaviors or it has no corresponding keyPress
2556         // event, then send it immediately
2557         if (!ignoreKeyEvent(ev))
2558             sendInput("k", [keysym]);
2559         suppress = true;
2560     }
2561
2562     if (! ignoreKeyEvent(ev)) {
2563         // Add it to the list of depressed keys
2564         pushKeyEvent(fev);
2565     }
2566
2567     if (suppress) {
2568         // Suppress bubbling/default actions
2569         return cancelEvent(ev);
2570     }
2571
2572     // Allow the event to bubble and become a keyPress event which
2573     // will have the character code translated
2574     return true;
2575 }
2576
2577 function handleKeyPress(e) {
2578     var ev = (e ? e : window.event), kdlen = keyDownList.length, keysym = null;
2579
2580     if (((ev.which !== "undefined") && (ev.which === 0)) ||
2581         getKeysymSpecial(ev)) {
2582         // Firefox and Opera generate a keyPress event even if keyDown
2583         // is suppressed. But the keys we want to suppress will have
2584         // either:
2585         // - the which attribute set to 0
2586         // - getKeysymSpecial() will identify it
2587         return cancelEvent(ev);
2588     }
2589
2590     keysym = getKeysym(ev);
2591
2592     // Modify the the which attribute in the depressed keys list so
2593     // that the keyUp event will be able to have the character code
2594     // translation available.
2595     if (kdlen > 0) {
2596         keyDownList[kdlen-1].keysym = keysym;
2597     } else {
2598         log("keyDownList empty when keyPress triggered");
2599     }
2600
2601     // Send the translated keysym
2602     if (keysym > 0)
2603         sendInput ("k", [keysym]);
2604
2605     // Stop keypress events just in case
2606     return cancelEvent(ev);
2607 }
2608
2609 function handleKeyUp(e) {
2610     var fev = null, ev = (e ? e : window.event), i, keysym;
2611
2612     fev = getKeyEvent(ev.keyCode, true);
2613
2614     if (fev)
2615         keysym = fev.keysym;
2616     else {
2617         log("Key event (keyCode = " + ev.keyCode + ") not found on keyDownList");
2618         keysym = 0;
2619     }
2620
2621     if (keysym > 0)
2622         sendInput ("K", [keysym]);
2623     return cancelEvent(ev);
2624 }
2625
2626 function onKeyDown (ev) {
2627     updateForEvent(ev);
2628     if (localGrab)
2629         return cancelEvent(ev);
2630     return handleKeyDown(ev);
2631 }
2632
2633 function onKeyPress(ev) {
2634     updateForEvent(ev);
2635     if (localGrab)
2636         return cancelEvent(ev);
2637     return handleKeyPress(ev);
2638 }
2639
2640 function onKeyUp (ev) {
2641     updateForEvent(ev);
2642     if (localGrab)
2643         return cancelEvent(ev);
2644     return handleKeyUp(ev);
2645 }
2646
2647 function cancelEvent(ev)
2648 {
2649     ev = ev ? ev : window.event;
2650     if (ev.stopPropagation)
2651         ev.stopPropagation();
2652     if (ev.preventDefault)
2653         ev.preventDefault();
2654     ev.cancelBubble = true;
2655     ev.cancel = true;
2656     ev.returnValue = false;
2657     return false;
2658 }
2659
2660 function onMouseWheel(ev)
2661 {
2662     updateForEvent(ev);
2663     if (localGrab)
2664         return false;
2665     ev = ev ? ev : window.event;
2666
2667     var id = getSurfaceId(ev);
2668     var pos = getPositionsFromEvent(ev, id);
2669
2670     var offset = ev.detail ? ev.detail : ev.wheelDelta;
2671     var dir = 0;
2672     if (offset > 0)
2673         dir = 1;
2674     sendInput ("s", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, dir]);
2675
2676     return cancelEvent(ev);
2677 }
2678
2679 function setupDocument(document)
2680 {
2681     document.oncontextmenu = function () { return false; };
2682     document.onmousemove = onMouseMove;
2683     document.onmouseover = onMouseOver;
2684     document.onmouseout = onMouseOut;
2685     document.onmousedown = onMouseDown;
2686     document.onmouseup = onMouseUp;
2687     document.onkeydown = onKeyDown;
2688     document.onkeypress = onKeyPress;
2689     document.onkeyup = onKeyUp;
2690
2691     if (document.addEventListener) {
2692       document.addEventListener('DOMMouseScroll', onMouseWheel, false);
2693       document.addEventListener('mousewheel', onMouseWheel, false);
2694     } else if (document.attachEvent) {
2695       element.attachEvent("onmousewheel", onMouseWheel);
2696     }
2697 }
2698
2699 function connect()
2700 {
2701     var url = window.location.toString();
2702     var query_string = url.split("?");
2703     if (query_string.length > 1) {
2704         var params = query_string[1].split("&");
2705         if (params[0].indexOf("toplevel") != -1)
2706             useToplevelWindows = true;
2707     }
2708     var xhr = createXHR();
2709     if (xhr) {
2710         if (typeof xhr.multipart == 'undefined') {
2711             alert("Sorry, this example only works in browsers that support multipart.");
2712             return;
2713         }
2714
2715         xhr.multipart = true;
2716         xhr.open("GET", "/output", true);
2717         xhr.onload = handleLoad;
2718         xhr.send(null);
2719     }
2720
2721     if ("WebSocket" in window) {
2722         var loc = window.location.toString().replace("http:", "ws:");
2723         loc = loc.substr(0, loc.lastIndexOf('/')) + "/input";
2724         var ws = new WebSocket(loc, "broadway");
2725         ws.onopen = function() {
2726             inputSocket = ws;
2727             var w, h;
2728             if (useToplevelWindows) {
2729                 w = window.screen.width;
2730                 h = window.screen.height;
2731             } else {
2732                 w = window.innerWidth;
2733                 h = window.innerHeight;
2734                 window.onresize = function(ev) {
2735                     var w, h;
2736                     w = window.innerWidth;
2737                     h = window.innerHeight;
2738                     sendInput ("d", [w, h]);
2739                 };
2740             }
2741             sendInput ("d", [w, h]);
2742         };
2743         ws.onclose = function() {
2744             inputSocket = null;
2745         };
2746     } else {
2747         alert("WebSocket not supported, input will not work!");
2748     }
2749     setupDocument(document);
2750     window.onunload = function (ev) {
2751         for (var i = 0; i < toplevelWindows.length; i++)
2752             toplevelWindows[i].close();
2753     };
2754 }