-var base64_val = [
- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
- 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
- 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
- 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255
-]
+/* Helper functions for debugging */
+var logDiv = null;
+function log(str) {
+ if (!logDiv) {
+ logDiv = document.createElement('div');
+ document.body.appendChild(logDiv);
+ logDiv.style["position"] = "absolute";
+ logDiv.style["right"] = "0px";
+ }
+ logDiv.appendChild(document.createTextNode(str));
+ logDiv.appendChild(document.createElement('br'));
+}
+
+function getStackTrace()
+{
+ var callstack = [];
+ var isCallstackPopulated = false;
+ try {
+ i.dont.exist+=0;
+ } catch(e) {
+ if (e.stack) { // Firefox
+ var lines = e.stack.split("\n");
+ for (var i=0, len=lines.length; i<len; i++) {
+ if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
+ callstack.push(lines[i]);
+ }
+ }
+ // Remove call to getStackTrace()
+ callstack.shift();
+ isCallstackPopulated = true;
+ } else if (window.opera && e.message) { // Opera
+ var lines = e.message.split("\n");
+ for (var i=0, len=lines.length; i<len; i++) {
+ if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
+ var entry = lines[i];
+ // Append next line also since it has the file info
+ if (lines[i+1]) {
+ entry += " at " + lines[i+1];
+ i++;
+ }
+ callstack.push(entry);
+ }
+ }
+ // Remove call to getStackTrace()
+ callstack.shift();
+ isCallstackPopulated = true;
+ }
+ }
+ if (!isCallstackPopulated) { //IE and Safari
+ var currentFunction = arguments.callee.caller;
+ while (currentFunction) {
+ var fn = currentFunction.toString();
+ var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf("(")) || "anonymous";
+ callstack.push(fname);
+ currentFunction = currentFunction.caller;
+ }
+ }
+ return callstack;
+}
+
+function logStackTrace(len) {
+ var callstack = getStackTrace();
+ var end = callstack.length;
+ if (len > 0)
+ end = Math.min(len + 1, end);
+ for (var i = 1; i < end; i++)
+ log(callstack[i]);
+}
+
+var base64Values = [
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
+ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255
+];
function base64_8(str, index) {
- var v =
- (base64_val[str.charCodeAt(index)]) +
- (base64_val[str.charCodeAt(index+1)] << 6);
- return v;
+ var v =
+ (base64Values[str.charCodeAt(index)]) +
+ (base64Values[str.charCodeAt(index+1)] << 6);
+ return v;
}
function base64_16(str, index) {
- var v =
- (base64_val[str.charCodeAt(index)]) +
- (base64_val[str.charCodeAt(index+1)] << 6) +
- (base64_val[str.charCodeAt(index+2)] << 12);
- return v;
+ var v =
+ (base64Values[str.charCodeAt(index)]) +
+ (base64Values[str.charCodeAt(index+1)] << 6) +
+ (base64Values[str.charCodeAt(index+2)] << 12);
+ return v;
}
function base64_16s(str, index) {
- var v = base64_16(str, index);
- if (v > 32767)
- return v - 65536;
- else
- return v;
+ var v = base64_16(str, index);
+ if (v > 32767)
+ return v - 65536;
+ else
+ return v;
}
function base64_24(str, index) {
- var v =
- (base64_val[str.charCodeAt(index)]) +
- (base64_val[str.charCodeAt(index+1)] << 6) +
- (base64_val[str.charCodeAt(index+2)] << 12) +
- (base64_val[str.charCodeAt(index+3)] << 18);
- return v;
+ var v =
+ (base64Values[str.charCodeAt(index)]) +
+ (base64Values[str.charCodeAt(index+1)] << 6) +
+ (base64Values[str.charCodeAt(index+2)] << 12) +
+ (base64Values[str.charCodeAt(index+3)] << 18);
+ return v;
}
function base64_32(str, index) {
- var v =
- (base64_val[str.charCodeAt(index)]) +
- (base64_val[str.charCodeAt(index+1)] << 6) +
- (base64_val[str.charCodeAt(index+2)] << 12) +
- (base64_val[str.charCodeAt(index+3)] << 18) +
- (base64_val[str.charCodeAt(index+4)] << 24) +
- (base64_val[str.charCodeAt(index+5)] << 30);
- return v;
+ var v =
+ (base64Values[str.charCodeAt(index)]) +
+ (base64Values[str.charCodeAt(index+1)] << 6) +
+ (base64Values[str.charCodeAt(index+2)] << 12) +
+ (base64Values[str.charCodeAt(index+3)] << 18) +
+ (base64Values[str.charCodeAt(index+4)] << 24) +
+ (base64Values[str.charCodeAt(index+5)] << 30);
+ return v;
}
function createXHR()
{
- try { return new XMLHttpRequest(); } catch(e) {}
- try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
- try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
- try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
- try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
+ try { return new XMLHttpRequest(); } catch(e) {}
+ try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
+ try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
+ try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
+ try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
+
+ return null;
+}
+
+function resizeCanvas(canvas, w, h)
+{
+ /* Canvas resize clears the data, so we need to save it first */
+ var tmpCanvas = canvas.ownerDocument.createElement("canvas");
+ tmpCanvas.width = canvas.width;
+ tmpCanvas.height = canvas.height;
+ var tmpContext = tmpCanvas.getContext("2d");
+ tmpContext.globalCompositeOperation = "copy";
+ tmpContext.drawImage(canvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
+
+ canvas.width = w;
+ canvas.height = h;
- return null;
+ var context = canvas.getContext("2d");
+
+ context.globalCompositeOperation = "copy";
+ context.drawImage(tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
}
+var grab = new Object();
+grab.window = null;
+grab.ownerEvents = false;
+grab.implicit = false;
+var localGrab = null;
+var keyDownList = [];
+var lastSerial = 0;
+var lastX = 0;
+var lastY = 0;
+var lastState;
+var lastTimeStamp = 0;
+var realWindowWithMouse = 0;
+var windowWithMouse = 0;
var surfaces = {};
-var outstanding_commands = new Array();
-var input_socket = null;
+var stackingOrder = [];
+var outstandingCommands = new Array();
+var inputSocket = null;
+
+var GDK_CROSSING_NORMAL = 0;
+var GDK_CROSSING_GRAB = 1;
+var GDK_CROSSING_UNGRAB = 2;
+
+// GdkModifierType
+var GDK_SHIFT_MASK = 1 << 0;
+var GDK_LOCK_MASK = 1 << 1;
+var GDK_CONTROL_MASK = 1 << 2;
+var GDK_MOD1_MASK = 1 << 3;
+var GDK_MOD2_MASK = 1 << 4;
+var GDK_MOD3_MASK = 1 << 5;
+var GDK_MOD4_MASK = 1 << 6;
+var GDK_MOD5_MASK = 1 << 7;
+var GDK_BUTTON1_MASK = 1 << 8;
+var GDK_BUTTON2_MASK = 1 << 9;
+var GDK_BUTTON3_MASK = 1 << 10;
+var GDK_BUTTON4_MASK = 1 << 11;
+var GDK_BUTTON5_MASK = 1 << 12;
+var GDK_SUPER_MASK = 1 << 26;
+var GDK_HYPER_MASK = 1 << 27;
+var GDK_META_MASK = 1 << 28;
+var GDK_RELEASE_MASK = 1 << 30;
+
+function getButtonMask (button) {
+ if (button == 1)
+ return GDK_BUTTON1_MASK;
+ if (button == 2)
+ return GDK_BUTTON2_MASK;
+ if (button == 3)
+ return GDK_BUTTON3_MASK;
+ if (button == 4)
+ return GDK_BUTTON4_MASK;
+ if (button == 5)
+ return GDK_BUTTON5_MASK;
+ return 0;
+}
+
+function flushSurface(surface)
+{
+ var commands = surface.drawQueue;
+ surface.queue = [];
+ var context = surface.canvas.getContext("2d");
+ context.globalCompositeOperation = "source-over";
+ var i = 0;
+ for (i = 0; i < commands.length; i++) {
+ var cmd = commands[i];
+ switch (cmd.op) {
+ case 'i': // put image data surface
+ context.globalCompositeOperation = "source-over";
+ context.drawImage(cmd.img, cmd.x, cmd.y);
+ break;
+
+ case 'b': // copy rects
+ context.save();
+ context.beginPath();
+
+ var minx;
+ var miny;
+ var maxx;
+ var maxy;
+ for (var j = 0; j < cmd.rects.length; j++) {
+ var rect = cmd.rects[j];
+ context.rect(rect.x, rect.y, rect.w, rect.h);
+ if (j == 0) {
+ minx = rect.x;
+ miny = rect.y;
+ maxx = rect.x + rect.w;
+ maxy = rect.y + rect.h;
+ } else {
+ if (rect.x < minx)
+ minx = rect.x;
+ if (rect.y < miny)
+ miny = rect.y;
+ if (rect.x + rect.w > maxx)
+ maxx = rect.x + rect.w;
+ if (rect.y + rect.h > maxy)
+ maxy = rect.y + rect.h;
+ }
+ }
+ context.clip();
+ context.globalCompositeOperation = "copy";
+ context.drawImage(context.canvas,
+ minx - cmd.dx, miny - cmd.dy, maxx - minx, maxy - miny,
+ minx, miny, maxx - minx, maxy - miny);
+ context.restore();
+ break;
+
+ default:
+ alert("Unknown drawing op " + cmd.op);
+ }
+ }
+}
+
+function sendConfigureNotify(surface)
+{
+ sendInput("w", [surface.id, surface.x, surface.y, surface.width, surface.height]);
+}
+
+function getStyle(el, styleProp)
+{
+ if (el.currentStyle) {
+ return el.currentStyle[styleProp];
+ } else if (window.getComputedStyle) {
+ var win = el.ownerDocument.defaultView;
+ return win.getComputedStyle(el, null).getPropertyValue(styleProp);
+ }
+ return undefined;
+}
+
+function parseOffset(value)
+{
+ var px = value.indexOf("px");
+ if (px > 0)
+ return parseInt(value.slice(0,px));
+ return 0;
+}
+
+function getFrameOffset(surface) {
+ var x = 0;
+ var y = 0;
+ var el = surface.canvas;
+ while (el != null && el != surface.frame) {
+ x += el.offsetLeft;
+ y += el.offsetTop;
+
+ /* For some reason the border is not includes in the offsets.. */
+ x += parseOffset(getStyle(el, "border-left-width"));
+ y += parseOffset(getStyle(el, "border-top-width"));
+
+ el = el.offsetParent;
+ }
+
+ /* Also include frame border as per above */
+ x += parseOffset(getStyle(el, "border-left-width"));
+ y += parseOffset(getStyle(el, "border-top-width"));
+
+ return {x: x, y: y};
+}
+
+var positionIndex = 0;
+function cmdCreateSurface(id, x, y, width, height, isTemp)
+{
+ var surface = { id: id, x: x, y:y, width: width, height: height, isTemp: isTemp };
+ surface.positioned = isTemp;
+ surface.drawQueue = [];
+ surface.transientParent = 0;
+ surface.visible = false;
+ surface.frame = null;
+
+ var canvas = document.createElement("canvas");
+ canvas.width = width;
+ canvas.height = height;
+ canvas.surface = surface;
+ surface.canvas = canvas;
+ var toplevelElement;
+
+ if (isTemp) {
+ toplevelElement = canvas;
+ document.body.appendChild(canvas);
+ } else {
+ var frame = document.createElement("div");
+ frame.frameFor = surface;
+ frame.className = "frame-window";
+ surface.frame = frame;
+
+ var button = document.createElement("center");
+ button.closeFor = surface;
+ var X = document.createTextNode("\u00d7");
+ button.appendChild(X);
+ button.className = "frame-close";
+ frame.appendChild(button);
+
+ var contents = document.createElement("div");
+ contents.className = "frame-contents";
+ frame.appendChild(contents);
+
+ canvas.style["display"] = "block";
+ contents.appendChild(canvas);
+
+ toplevelElement = frame;
+ document.body.appendChild(frame);
+ }
+
+ surface.toplevelElement = toplevelElement;
+ toplevelElement.style["position"] = "absolute";
+ /* This positioning isn't strictly right for apps in another topwindow,
+ * but that will be fixed up when showing. */
+ toplevelElement.style["left"] = surface.x + "px";
+ toplevelElement.style["top"] = surface.y + "px";
+ toplevelElement.style["display"] = "inline";
+
+ /* We hide the frame with visibility rather than display none
+ * so getFrameOffset still works with hidden windows. */
+ toplevelElement.style["visibility"] = "hidden";
+
+ surfaces[id] = surface;
+ stackingOrder.push(surface);
+
+ sendConfigureNotify(surface);
+}
+
+function cmdShowSurface(id)
+{
+ var surface = surfaces[id];
+
+ if (surface.visible)
+ return;
+ surface.visible = true;
+
+ var xOffset = surface.x;
+ var yOffset = surface.y;
+
+ if (surface.frame) {
+ var offset = getFrameOffset(surface);
+ xOffset -= offset.x;
+ yOffset -= offset.y;
+ }
+
+ surface.toplevelElement.style["left"] = xOffset + "px";
+ surface.toplevelElement.style["top"] = yOffset + "px";
+ surface.toplevelElement.style["visibility"] = "visible";
+
+ restackWindows();
+}
-function initContext(canvas, x, y, id)
+function cmdHideSurface(id)
{
- canvas.surface_id = id;
- canvas.style["position"] = "absolute"
- canvas.style["left"] = x + "px"
- canvas.style["top"] = y + "px"
- canvas.style["display"] = "none"
- context = canvas.getContext("2d")
- context.globalCompositeOperation = "src-over"
- context.fillRect(0, 0, canvas.width, canvas.height);
- document.body.appendChild(canvas)
+ if (grab.window == id)
+ doUngrab();
- return context
+ var surface = surfaces[id];
+
+ if (!surface.visible)
+ return;
+ surface.visible = false;
+
+ var element = surface.toplevelElement;
+
+ element.style["visibility"] = "hidden";
+}
+
+function cmdSetTransientFor(id, parentId)
+{
+ var surface = surfaces[id];
+
+ if (surface.transientParent == parentId)
+ return;
+
+ surface.transientParent = parentId;
+ if (parentId != 0 && surfaces[parentId]) {
+ moveToHelper(surface, stackingOrder.indexOf(surfaces[parentId])+1);
+ }
+
+ if (surface.visible) {
+ restackWindows();
+ }
+}
+
+function restackWindows() {
+ for (var i = 0; i < stackingOrder.length; i++) {
+ var surface = stackingOrder[i];
+ surface.toplevelElement.style.zIndex = i;
+ }
+}
+
+function moveToHelper(surface, position) {
+ var i = stackingOrder.indexOf(surface);
+ stackingOrder.splice(i, 1);
+ if (position != undefined)
+ stackingOrder.splice(position, 0, surface);
+ else
+ stackingOrder.push(surface);
+
+ for (var cid in surfaces) {
+ var child = surfaces[cid];
+ if (child.transientParent == surface.id)
+ moveToHelper(child, stackingOrder.indexOf(surface) + 1);
+ }
+}
+
+function moveToTop(surface) {
+ moveToHelper(surface);
+ restackWindows();
+}
+
+
+function cmdDeleteSurface(id)
+{
+ if (grab.window == id)
+ doUngrab();
+
+ var surface = surfaces[id];
+ var i = stackingOrder.indexOf(surface);
+ if (i >= 0)
+ stackingOrder.splice(i, 1);
+ var canvas = surface.canvas;
+ canvas.parentNode.removeChild(canvas);
+ var frame = surface.frame;
+ if (frame)
+ frame.parentNode.removeChild(frame);
+ delete surfaces[id];
}
-function handleCommands(cmd_obj)
+function cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h)
{
- var cmd = cmd_obj.data;
- var i = cmd_obj.pos;
-
- while (i < cmd.length) {
- var command = cmd[i++];
- switch (command) {
- /* create new surface */
- case 's':
- var id = base64_16(cmd, i);
- i = i + 3;
- var x = base64_16(cmd, i);
- i = i + 3;
- var y = base64_16(cmd, i);
- i = i + 3;
- var w = base64_16(cmd, i);
- i = i + 3;
- var h = base64_16(cmd, i);
- i = i + 3;
- var surface = document.createElement("canvas");
+ var surface = surfaces[id];
+ if (has_pos) {
+ surface.positioned = true;
+ surface.x = x;
+ surface.y = y;
+ }
+ if (has_size) {
surface.width = w;
surface.height = h;
- surfaces[id] = initContext(surface, x, y, id);
- break;
-
- /* show a surface */
- case 'S':
- var id = base64_16(cmd, i);
- i = i + 3;
- surfaces[id].canvas.style["display"] = "inline";
- break;
-
- /* hide a surface */
- case 'H':
- var id = base64_16(cmd, i);
- i = i + 3;
- surfaces[id].canvas.style["display"] = "inline";
- break;
-
- /* delete surface */
- case 'd':
- var id = base64_16(cmd, i);
- i = i + 3;
- var canvas = surfaces[id].canvas
- delete surfaces[id]
- canvas.parentNode.removeChild(canvas);
-
- break;
-
- /* move a surface */
- case 'm':
- var id = base64_16(cmd, i);
- i = i + 3;
- var x = base64_16(cmd, i);
- i = i + 3;
- var y = base64_16(cmd, i);
- i = i + 3;
- surfaces[id].canvas.style["left"] = x + "px";
- surfaces[id].canvas.style["top"] = y + "px";
- break;
-
- /* put image data surface */
- case 'i':
- var id = base64_16(cmd, i);
- i = i + 3;
- var x = base64_16(cmd, i);
- i = i + 3;
- var y = base64_16(cmd, i);
- i = i + 3;
- var size = base64_32(cmd, i);
- i = i + 6;
- var url = cmd.slice(i, i + size);
- i = i + size;
- var img = new Image();
- img.src = url
- if (img.complete) {
- surfaces[id].drawImage(img, x, y);
- } else {
- cmd_obj.pos = i;
- img.onload = function() { surfaces[id].drawImage(img, x, y); handleOutstanding(); }
- return false
- }
+ }
+
+ /* Flush any outstanding draw ops before (possibly) changing size */
+ flushSurface(surface);
- break;
-
- /* copy rects */
- case 'b':
- var id = base64_16(cmd, i);
- i = i + 3;
-
- var nrects = base64_16(cmd, i);
- i = i + 3;
-
- var context = surfaces[id];
- context.save();
-
- var minx;
- var miny;
- var maxx;
- var maxy;
- for (var r = 0; r < nrects; r++) {
- var x = base64_16(cmd, i);
- i = i + 3;
- var y = base64_16(cmd, i);
- i = i + 3;
- var w = base64_16(cmd, i);
- i = i + 3;
- var h = base64_16(cmd, i);
- i = i + 3;
- context.rect(x, y, w, h);
-
- if (r == 0) {
- minx = x;
- miny = y;
- maxx = x + w;
- maxy = y + h;
- } else {
- if (x < minx)
- minx = x;
- if (y < miny)
- miny = y;
- if (x + w > maxx)
- maxx = x + w;
- if (y + h > maxy)
- maxy = y + h;
- }
+ if (has_size)
+ resizeCanvas(surface.canvas, w, h);
+
+ if (surface.visible) {
+ if (has_pos) {
+ var xOffset = surface.x;
+ var yOffset = surface.y;
+
+ var transientToplevel = getTransientToplevel(surface);
+ if (transientToplevel) {
+ xOffset = surface.x - transientToplevel.x;
+ yOffset = surface.y - transientToplevel.y;
+ }
+
+ var element = surface.canvas;
+ if (surface.frame) {
+ element = surface.frame;
+ var offset = getFrameOffset(surface);
+ xOffset -= offset.x;
+ yOffset -= offset.y;
+ }
+
+ element.style["left"] = xOffset + "px";
+ element.style["top"] = yOffset + "px";
}
+ }
- context.clip()
+ sendConfigureNotify(surface);
+}
- var dx = base64_16s(cmd, i);
- i = i + 3;
- var dy = base64_16s(cmd, i);
- i = i + 3;
+function cmdFlushSurface(id)
+{
+ flushSurface(surfaces[id]);
+}
- context.drawImage(context.canvas,
- minx - dx, miny - dy, maxx - minx, maxy - miny,
- minx, miny, maxx - minx, maxy - miny);
+function cmdGrabPointer(id, ownerEvents)
+{
+ doGrab(id, ownerEvents, false);
+ sendInput ("g", []);
+}
+
+function cmdUngrabPointer()
+{
+ sendInput ("u", []);
+ if (grab.window)
+ doUngrab();
+}
+
+function handleCommands(cmd)
+{
+ while (cmd.pos < cmd.length) {
+ var id, x, y, w, h, q;
+ var command = cmd.get_char();
+ lastSerial = cmd.get_32();
+ switch (command) {
+ case 's': // create new surface
+ id = cmd.get_16();
+ x = cmd.get_16s();
+ y = cmd.get_16s();
+ w = cmd.get_16();
+ h = cmd.get_16();
+ var isTemp = cmd.get_bool();
+ cmdCreateSurface(id, x, y, w, h, isTemp);
+ break;
+
+ case 'S': // Show a surface
+ id = cmd.get_16();
+ cmdShowSurface(id);
+ break;
+
+ case 'H': // Hide a surface
+ id = cmd.get_16();
+ cmdHideSurface(id);
+ break;
+
+ case 'p': // Set transient parent
+ id = cmd.get_16();
+ var parentId = cmd.get_16();
+ cmdSetTransientFor(id, parentId);
+ break;
+
+ case 'd': // Delete surface
+ id = cmd.get_16();
+ cmdDeleteSurface(id);
+ break;
- context.restore();
- break;
+ case 'm': // Move a surface
+ id = cmd.get_16();
+ var ops = cmd.get_flags();
+ var has_pos = ops & 1;
+ if (has_pos) {
+ x = cmd.get_16s();
+ y = cmd.get_16s();
+ }
+ var has_size = ops & 2;
+ if (has_size) {
+ w = cmd.get_16();
+ h = cmd.get_16();
+ }
+ cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h);
+ break;
- default:
- alert("Unknown op " + command);
+ case 'i': // Put image data surface
+ q = new Object();
+ q.op = 'i';
+ q.id = cmd.get_16();
+ q.x = cmd.get_16();
+ q.y = cmd.get_16();
+ var url = cmd.get_image_url ();
+ q.img = new Image();
+ q.img.src = url;
+ surfaces[q.id].drawQueue.push(q);
+ if (!q.img.complete) {
+ q.img.onload = function() { cmd.free_image_url (url); handleOutstanding(); };
+ return false;
+ }
+ cmd.free_image_url (url);
+ break;
+
+ case 'b': // Copy rects
+ q = new Object();
+ q.op = 'b';
+ q.id = cmd.get_16();
+ var nrects = cmd.get_16();
+
+ q.rects = [];
+ for (var r = 0; r < nrects; r++) {
+ var rect = new Object();
+ rect.x = cmd.get_16();
+ rect.y = cmd.get_16();
+ rect.w = cmd.get_16();
+ rect.h = cmd.get_16();
+ q.rects.push (rect);
+ }
+
+ q.dx = cmd.get_16s();
+ q.dy = cmd.get_16s();
+ surfaces[q.id].drawQueue.push(q);
+ break;
+
+ case 'f': // Flush surface
+ id = cmd.get_16();
+
+ cmdFlushSurface(id);
+ break;
+
+ case 'g': // Grab
+ id = cmd.get_16();
+ var ownerEvents = cmd.get_bool ();
+
+ cmdGrabPointer(id, ownerEvents);
+ break;
+
+ case 'u': // Ungrab
+ cmdUngrabPointer();
+ break;
+ default:
+ alert("Unknown op " + command);
+ }
}
- }
- return true;
+ return true;
}
function handleOutstanding()
{
- while (outstanding_commands.length > 0) {
- var cmd = outstanding_commands.shift();
- if (!handleCommands(cmd)) {
- outstanding_commands.unshift(cmd);
- return;
+ while (outstandingCommands.length > 0) {
+ var cmd = outstandingCommands.shift();
+ if (!handleCommands(cmd)) {
+ outstandingCommands.unshift(cmd);
+ return;
+ }
}
- }
}
-function handleLoad(event)
+function TextCommands(message) {
+ this.data = message;
+ this.length = message.length;
+ this.pos = 0;
+}
+
+TextCommands.prototype.get_char = function() {
+ return this.data[this.pos++];
+};
+TextCommands.prototype.get_bool = function() {
+ return this.get_char() == '1';
+};
+TextCommands.prototype.get_flags = function() {
+ return this.get_char() - 48;
+}
+TextCommands.prototype.get_16 = function() {
+ var n = base64_16(this.data, this.pos);
+ this.pos = this.pos + 3;
+ return n;
+};
+TextCommands.prototype.get_16s = function() {
+ var n = base64_16s(this.data, this.pos);
+ this.pos = this.pos + 3;
+ return n;
+};
+TextCommands.prototype.get_32 = function() {
+ var n = base64_32(this.data, this.pos);
+ this.pos = this.pos + 6;
+ return n;
+};
+TextCommands.prototype.get_image_url = function() {
+ var size = this.get_32();
+ var url = this.data.slice(this.pos, this.pos + size);
+ this.pos = this.pos + size;
+ return url;
+};
+TextCommands.prototype.free_image_url = function(url) {
+};
+
+function BinCommands(message) {
+ this.arraybuffer = message;
+ this.u8 = new Uint8Array(message);
+ this.length = this.u8.length;
+ this.pos = 0;
+}
+
+BinCommands.prototype.get_char = function() {
+ return String.fromCharCode(this.u8[this.pos++]);
+};
+BinCommands.prototype.get_bool = function() {
+ return this.u8[this.pos++] != 0;
+};
+BinCommands.prototype.get_flags = function() {
+ return this.u8[this.pos++];
+}
+BinCommands.prototype.get_16 = function() {
+ var v =
+ this.u8[this.pos] +
+ (this.u8[this.pos+1] << 8);
+ this.pos = this.pos + 2;
+ return v;
+};
+BinCommands.prototype.get_16s = function() {
+ var v = this.get_16 ();
+ if (v > 32767)
+ return v - 65536;
+ else
+ return v;
+};
+BinCommands.prototype.get_32 = function() {
+ var v =
+ this.u8[this.pos] +
+ (this.u8[this.pos+1] << 8) +
+ (this.u8[this.pos+2] << 16) +
+ (this.u8[this.pos+3] << 24);
+ this.pos = this.pos + 4;
+ return v;
+};
+BinCommands.prototype.get_image_url = function() {
+ var size = this.get_32();
+ var png_blob = new Blob ([this.arraybuffer.slice (this.pos, this.pos + size)], {type:"image/png"});
+ var url = URL.createObjectURL(png_blob, {oneTimeOnly: true});
+ this.pos = this.pos + size;
+ return url;
+};
+BinCommands.prototype.free_image_url = function(url) {
+ URL.revokeObjectURL(url);
+};
+
+function handleMessage(message)
{
- var cmd_obj = {};
- cmd_obj.data = event.target.responseText;
- cmd_obj.pos = 0;
+ var cmd;
+ if (message instanceof ArrayBuffer)
+ cmd = new BinCommands(message);
+ else
+ cmd = new TextCommands(message);
+ outstandingCommands.push(cmd);
+ if (outstandingCommands.length == 1) {
+ handleOutstanding();
+ }
+}
- outstanding_commands.push(cmd_obj);
- if (outstanding_commands.length == 1) {
- handleOutstanding()
- }
+function getSurfaceId(ev) {
+ var surface = ev.target.surface;
+ if (surface != undefined)
+ return surface.id;
+ return 0;
+}
- if ("WebSocket" in window) {
- var loc = window.location.toString().replace("http:", "ws:");
- loc = loc.substr(0, loc.lastIndexOf('/')) + "/input";
- var ws = new WebSocket(loc, "broadway");
- ws.onopen = function() {
- input_socket = ws;
- };
- ws.onclose = function() {
- input_socket = null;
- };
- } else {
- alert("WebSocket not supported, input will not work!");
- }
+function sendInput(cmd, args)
+{
+ if (inputSocket != null) {
+ inputSocket.send(cmd + ([lastSerial, lastTimeStamp].concat(args)).join(","));
+ }
+}
+
+function getPositionsFromAbsCoord(absX, absY, relativeId) {
+ var res = Object();
+
+ res.rootX = absX;
+ res.rootY = absY;
+ res.winX = absX;
+ res.winY = absY;
+ if (relativeId != 0) {
+ var surface = surfaces[relativeId];
+ res.winX = res.winX - surface.x;
+ res.winY = res.winY - surface.y;
+ }
+
+ return res;
}
-function get_surface_id(ev) {
- var id = ev.target.surface_id;
- if (id != undefined)
+function getPositionsFromEvent(ev, relativeId) {
+ var absX, absY;
+ absX = ev.pageX;
+ absY = ev.pageY;
+ var res = getPositionsFromAbsCoord(absX, absY, relativeId);
+
+ lastX = res.rootX;
+ lastY = res.rootY;
+
+ return res;
+}
+
+function getEffectiveEventTarget (id) {
+ if (grab.window != null) {
+ if (!grab.ownerEvents)
+ return grab.window;
+ if (id == 0)
+ return grab.window;
+ }
return id;
- return 0;
}
-function send_input(cmd, args)
+function updateForEvent(ev) {
+ lastState &= ~(GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK);
+ if (ev.shiftKey)
+ lastState |= GDK_SHIFT_MASK;
+ if (ev.ctrlKey)
+ lastState |= GDK_CONTROL_MASK;
+ if (ev.altKey)
+ lastState |= GDK_MOD1_MASK;
+
+ lastTimeStamp = ev.timeStamp;
+}
+
+function onMouseMove (ev) {
+ updateForEvent(ev);
+ if (localGrab) {
+ if (localGrab.type == "move") {
+ var dx = ev.pageX - localGrab.lastX;
+ var dy = ev.pageY - localGrab.lastY;
+ var surface = localGrab.surface;
+ surface.x += dx;
+ surface.y += dy;
+ var offset = getFrameOffset(surface);
+ if (surface.y < offset.y)
+ surface.y = offset.y;
+ localGrab.frame.style["left"] = (surface.x - offset.x) + "px";
+ localGrab.frame.style["top"] = (surface.y - offset.y) + "px";
+ sendConfigureNotify(surface);
+ localGrab.lastX = ev.pageX;
+ localGrab.lastY = ev.pageY;
+ }
+ return;
+ }
+ var id = getSurfaceId(ev);
+ id = getEffectiveEventTarget (id);
+ var pos = getPositionsFromEvent(ev, id);
+ sendInput ("m", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState]);
+}
+
+function onMouseOver (ev) {
+ updateForEvent(ev);
+
+ if (!grab.window && ev.target.closeFor) {
+ ev.target.className = ev.target.className + " frame-hover";
+ if (ev.target.isDown)
+ ev.target.className = ev.target.className + " frame-active";
+ }
+
+ if (localGrab)
+ return;
+ var id = getSurfaceId(ev);
+ realWindowWithMouse = id;
+ id = getEffectiveEventTarget (id);
+ var pos = getPositionsFromEvent(ev, id);
+ windowWithMouse = id;
+ if (windowWithMouse != 0) {
+ sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
+ }
+}
+
+function onMouseOut (ev) {
+ updateForEvent(ev);
+ if (ev.target.closeFor) {
+ ev.target.className = ev.target.className.replace(" frame-hover", "");
+ if (ev.target.isDown)
+ ev.target.className = ev.target.className.replace(" frame-active", "");
+ }
+ if (localGrab)
+ return;
+ var id = getSurfaceId(ev);
+ var origId = id;
+ id = getEffectiveEventTarget (id);
+ var pos = getPositionsFromEvent(ev, id);
+
+ if (id != 0) {
+ sendInput ("l", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
+ }
+ realWindowWithMouse = 0;
+ windowWithMouse = 0;
+}
+
+function doGrab(id, ownerEvents, implicit) {
+ var pos;
+
+ if (windowWithMouse != id) {
+ if (windowWithMouse != 0) {
+ pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
+ sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
+ }
+ pos = getPositionsFromAbsCoord(lastX, lastY, id);
+ sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
+ windowWithMouse = id;
+ }
+
+ grab.window = id;
+ grab.ownerEvents = ownerEvents;
+ grab.implicit = implicit;
+}
+
+function doUngrab() {
+ var pos;
+ if (realWindowWithMouse != windowWithMouse) {
+ if (windowWithMouse != 0) {
+ pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
+ sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
+ }
+ if (realWindowWithMouse != 0) {
+ pos = getPositionsFromAbsCoord(lastX, lastY, realWindowWithMouse);
+ sendInput ("e", [realWindowWithMouse, realWindowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
+ }
+ windowWithMouse = realWindowWithMouse;
+ }
+ grab.window = null;
+}
+
+function onMouseDown (ev) {
+ updateForEvent(ev);
+ var button = ev.button + 1;
+ lastState = lastState | getButtonMask (button);
+ var id = getSurfaceId(ev);
+ id = getEffectiveEventTarget (id);
+
+ if (id == 0 && ev.target.frameFor) { /* mouse click on frame */
+ localGrab = new Object();
+ localGrab.surface = ev.target.frameFor;
+ localGrab.type = "move";
+ localGrab.frame = ev.target;
+ localGrab.lastX = ev.pageX;
+ localGrab.lastY = ev.pageY;
+ moveToTop(localGrab.frame.frameFor);
+ return false;
+ }
+
+ if (id == 0 && ev.target.closeFor) { /* mouse click on frame */
+ ev.target.isDown = true;
+ ev.target.className = ev.target.className + " frame-active";
+ localGrab = new Object();
+ localGrab.surface = ev.target.closeFor;
+ localGrab.type = "close";
+ localGrab.button = ev.target;
+ localGrab.lastX = ev.pageX;
+ localGrab.lastY = ev.pageY;
+ return false;
+ }
+
+ var pos = getPositionsFromEvent(ev, id);
+ if (grab.window == null)
+ doGrab (id, false, true);
+ sendInput ("b", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
+ return false;
+}
+
+function onMouseUp (ev) {
+ updateForEvent(ev);
+ var button = ev.button + 1;
+ lastState = lastState & ~getButtonMask (button);
+ var evId = getSurfaceId(ev);
+ id = getEffectiveEventTarget (evId);
+ var pos = getPositionsFromEvent(ev, id);
+
+ if (localGrab) {
+ realWindowWithMouse = evId;
+ if (windowWithMouse != id) {
+ if (windowWithMouse != 0) {
+ sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
+ }
+ windowWithMouse = id;
+ if (windowWithMouse != 0) {
+ sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
+ }
+ }
+
+ if (localGrab.type == "close") {
+ localGrab.button.isDown = false;
+ localGrab.button.className = localGrab.button.className.replace( " frame-active", "");
+ if (ev.target == localGrab.button)
+ sendInput ("W", [localGrab.surface.id]);
+ }
+ localGrab = null;
+ return false;
+ }
+
+ sendInput ("B", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
+
+ if (grab.window != null && grab.implicit)
+ doUngrab();
+
+ return false;
+}
+
+/* Some of the keyboard handling code is from noVNC and
+ * (c) Joel Martin (github@martintribe.org), used with permission
+ * Original code at:
+ * https://github.com/kanaka/noVNC/blob/master/include/input.js
+ */
+
+var unicodeTable = {
+ 0x0104: 0x01a1,
+ 0x02D8: 0x01a2,
+ 0x0141: 0x01a3,
+ 0x013D: 0x01a5,
+ 0x015A: 0x01a6,
+ 0x0160: 0x01a9,
+ 0x015E: 0x01aa,
+ 0x0164: 0x01ab,
+ 0x0179: 0x01ac,
+ 0x017D: 0x01ae,
+ 0x017B: 0x01af,
+ 0x0105: 0x01b1,
+ 0x02DB: 0x01b2,
+ 0x0142: 0x01b3,
+ 0x013E: 0x01b5,
+ 0x015B: 0x01b6,
+ 0x02C7: 0x01b7,
+ 0x0161: 0x01b9,
+ 0x015F: 0x01ba,
+ 0x0165: 0x01bb,
+ 0x017A: 0x01bc,
+ 0x02DD: 0x01bd,
+ 0x017E: 0x01be,
+ 0x017C: 0x01bf,
+ 0x0154: 0x01c0,
+ 0x0102: 0x01c3,
+ 0x0139: 0x01c5,
+ 0x0106: 0x01c6,
+ 0x010C: 0x01c8,
+ 0x0118: 0x01ca,
+ 0x011A: 0x01cc,
+ 0x010E: 0x01cf,
+ 0x0110: 0x01d0,
+ 0x0143: 0x01d1,
+ 0x0147: 0x01d2,
+ 0x0150: 0x01d5,
+ 0x0158: 0x01d8,
+ 0x016E: 0x01d9,
+ 0x0170: 0x01db,
+ 0x0162: 0x01de,
+ 0x0155: 0x01e0,
+ 0x0103: 0x01e3,
+ 0x013A: 0x01e5,
+ 0x0107: 0x01e6,
+ 0x010D: 0x01e8,
+ 0x0119: 0x01ea,
+ 0x011B: 0x01ec,
+ 0x010F: 0x01ef,
+ 0x0111: 0x01f0,
+ 0x0144: 0x01f1,
+ 0x0148: 0x01f2,
+ 0x0151: 0x01f5,
+ 0x0171: 0x01fb,
+ 0x0159: 0x01f8,
+ 0x016F: 0x01f9,
+ 0x0163: 0x01fe,
+ 0x02D9: 0x01ff,
+ 0x0126: 0x02a1,
+ 0x0124: 0x02a6,
+ 0x0130: 0x02a9,
+ 0x011E: 0x02ab,
+ 0x0134: 0x02ac,
+ 0x0127: 0x02b1,
+ 0x0125: 0x02b6,
+ 0x0131: 0x02b9,
+ 0x011F: 0x02bb,
+ 0x0135: 0x02bc,
+ 0x010A: 0x02c5,
+ 0x0108: 0x02c6,
+ 0x0120: 0x02d5,
+ 0x011C: 0x02d8,
+ 0x016C: 0x02dd,
+ 0x015C: 0x02de,
+ 0x010B: 0x02e5,
+ 0x0109: 0x02e6,
+ 0x0121: 0x02f5,
+ 0x011D: 0x02f8,
+ 0x016D: 0x02fd,
+ 0x015D: 0x02fe,
+ 0x0138: 0x03a2,
+ 0x0156: 0x03a3,
+ 0x0128: 0x03a5,
+ 0x013B: 0x03a6,
+ 0x0112: 0x03aa,
+ 0x0122: 0x03ab,
+ 0x0166: 0x03ac,
+ 0x0157: 0x03b3,
+ 0x0129: 0x03b5,
+ 0x013C: 0x03b6,
+ 0x0113: 0x03ba,
+ 0x0123: 0x03bb,
+ 0x0167: 0x03bc,
+ 0x014A: 0x03bd,
+ 0x014B: 0x03bf,
+ 0x0100: 0x03c0,
+ 0x012E: 0x03c7,
+ 0x0116: 0x03cc,
+ 0x012A: 0x03cf,
+ 0x0145: 0x03d1,
+ 0x014C: 0x03d2,
+ 0x0136: 0x03d3,
+ 0x0172: 0x03d9,
+ 0x0168: 0x03dd,
+ 0x016A: 0x03de,
+ 0x0101: 0x03e0,
+ 0x012F: 0x03e7,
+ 0x0117: 0x03ec,
+ 0x012B: 0x03ef,
+ 0x0146: 0x03f1,
+ 0x014D: 0x03f2,
+ 0x0137: 0x03f3,
+ 0x0173: 0x03f9,
+ 0x0169: 0x03fd,
+ 0x016B: 0x03fe,
+ 0x1E02: 0x1001e02,
+ 0x1E03: 0x1001e03,
+ 0x1E0A: 0x1001e0a,
+ 0x1E80: 0x1001e80,
+ 0x1E82: 0x1001e82,
+ 0x1E0B: 0x1001e0b,
+ 0x1EF2: 0x1001ef2,
+ 0x1E1E: 0x1001e1e,
+ 0x1E1F: 0x1001e1f,
+ 0x1E40: 0x1001e40,
+ 0x1E41: 0x1001e41,
+ 0x1E56: 0x1001e56,
+ 0x1E81: 0x1001e81,
+ 0x1E57: 0x1001e57,
+ 0x1E83: 0x1001e83,
+ 0x1E60: 0x1001e60,
+ 0x1EF3: 0x1001ef3,
+ 0x1E84: 0x1001e84,
+ 0x1E85: 0x1001e85,
+ 0x1E61: 0x1001e61,
+ 0x0174: 0x1000174,
+ 0x1E6A: 0x1001e6a,
+ 0x0176: 0x1000176,
+ 0x0175: 0x1000175,
+ 0x1E6B: 0x1001e6b,
+ 0x0177: 0x1000177,
+ 0x0152: 0x13bc,
+ 0x0153: 0x13bd,
+ 0x0178: 0x13be,
+ 0x203E: 0x047e,
+ 0x3002: 0x04a1,
+ 0x300C: 0x04a2,
+ 0x300D: 0x04a3,
+ 0x3001: 0x04a4,
+ 0x30FB: 0x04a5,
+ 0x30F2: 0x04a6,
+ 0x30A1: 0x04a7,
+ 0x30A3: 0x04a8,
+ 0x30A5: 0x04a9,
+ 0x30A7: 0x04aa,
+ 0x30A9: 0x04ab,
+ 0x30E3: 0x04ac,
+ 0x30E5: 0x04ad,
+ 0x30E7: 0x04ae,
+ 0x30C3: 0x04af,
+ 0x30FC: 0x04b0,
+ 0x30A2: 0x04b1,
+ 0x30A4: 0x04b2,
+ 0x30A6: 0x04b3,
+ 0x30A8: 0x04b4,
+ 0x30AA: 0x04b5,
+ 0x30AB: 0x04b6,
+ 0x30AD: 0x04b7,
+ 0x30AF: 0x04b8,
+ 0x30B1: 0x04b9,
+ 0x30B3: 0x04ba,
+ 0x30B5: 0x04bb,
+ 0x30B7: 0x04bc,
+ 0x30B9: 0x04bd,
+ 0x30BB: 0x04be,
+ 0x30BD: 0x04bf,
+ 0x30BF: 0x04c0,
+ 0x30C1: 0x04c1,
+ 0x30C4: 0x04c2,
+ 0x30C6: 0x04c3,
+ 0x30C8: 0x04c4,
+ 0x30CA: 0x04c5,
+ 0x30CB: 0x04c6,
+ 0x30CC: 0x04c7,
+ 0x30CD: 0x04c8,
+ 0x30CE: 0x04c9,
+ 0x30CF: 0x04ca,
+ 0x30D2: 0x04cb,
+ 0x30D5: 0x04cc,
+ 0x30D8: 0x04cd,
+ 0x30DB: 0x04ce,
+ 0x30DE: 0x04cf,
+ 0x30DF: 0x04d0,
+ 0x30E0: 0x04d1,
+ 0x30E1: 0x04d2,
+ 0x30E2: 0x04d3,
+ 0x30E4: 0x04d4,
+ 0x30E6: 0x04d5,
+ 0x30E8: 0x04d6,
+ 0x30E9: 0x04d7,
+ 0x30EA: 0x04d8,
+ 0x30EB: 0x04d9,
+ 0x30EC: 0x04da,
+ 0x30ED: 0x04db,
+ 0x30EF: 0x04dc,
+ 0x30F3: 0x04dd,
+ 0x309B: 0x04de,
+ 0x309C: 0x04df,
+ 0x06F0: 0x10006f0,
+ 0x06F1: 0x10006f1,
+ 0x06F2: 0x10006f2,
+ 0x06F3: 0x10006f3,
+ 0x06F4: 0x10006f4,
+ 0x06F5: 0x10006f5,
+ 0x06F6: 0x10006f6,
+ 0x06F7: 0x10006f7,
+ 0x06F8: 0x10006f8,
+ 0x06F9: 0x10006f9,
+ 0x066A: 0x100066a,
+ 0x0670: 0x1000670,
+ 0x0679: 0x1000679,
+ 0x067E: 0x100067e,
+ 0x0686: 0x1000686,
+ 0x0688: 0x1000688,
+ 0x0691: 0x1000691,
+ 0x060C: 0x05ac,
+ 0x06D4: 0x10006d4,
+ 0x0660: 0x1000660,
+ 0x0661: 0x1000661,
+ 0x0662: 0x1000662,
+ 0x0663: 0x1000663,
+ 0x0664: 0x1000664,
+ 0x0665: 0x1000665,
+ 0x0666: 0x1000666,
+ 0x0667: 0x1000667,
+ 0x0668: 0x1000668,
+ 0x0669: 0x1000669,
+ 0x061B: 0x05bb,
+ 0x061F: 0x05bf,
+ 0x0621: 0x05c1,
+ 0x0622: 0x05c2,
+ 0x0623: 0x05c3,
+ 0x0624: 0x05c4,
+ 0x0625: 0x05c5,
+ 0x0626: 0x05c6,
+ 0x0627: 0x05c7,
+ 0x0628: 0x05c8,
+ 0x0629: 0x05c9,
+ 0x062A: 0x05ca,
+ 0x062B: 0x05cb,
+ 0x062C: 0x05cc,
+ 0x062D: 0x05cd,
+ 0x062E: 0x05ce,
+ 0x062F: 0x05cf,
+ 0x0630: 0x05d0,
+ 0x0631: 0x05d1,
+ 0x0632: 0x05d2,
+ 0x0633: 0x05d3,
+ 0x0634: 0x05d4,
+ 0x0635: 0x05d5,
+ 0x0636: 0x05d6,
+ 0x0637: 0x05d7,
+ 0x0638: 0x05d8,
+ 0x0639: 0x05d9,
+ 0x063A: 0x05da,
+ 0x0640: 0x05e0,
+ 0x0641: 0x05e1,
+ 0x0642: 0x05e2,
+ 0x0643: 0x05e3,
+ 0x0644: 0x05e4,
+ 0x0645: 0x05e5,
+ 0x0646: 0x05e6,
+ 0x0647: 0x05e7,
+ 0x0648: 0x05e8,
+ 0x0649: 0x05e9,
+ 0x064A: 0x05ea,
+ 0x064B: 0x05eb,
+ 0x064C: 0x05ec,
+ 0x064D: 0x05ed,
+ 0x064E: 0x05ee,
+ 0x064F: 0x05ef,
+ 0x0650: 0x05f0,
+ 0x0651: 0x05f1,
+ 0x0652: 0x05f2,
+ 0x0653: 0x1000653,
+ 0x0654: 0x1000654,
+ 0x0655: 0x1000655,
+ 0x0698: 0x1000698,
+ 0x06A4: 0x10006a4,
+ 0x06A9: 0x10006a9,
+ 0x06AF: 0x10006af,
+ 0x06BA: 0x10006ba,
+ 0x06BE: 0x10006be,
+ 0x06CC: 0x10006cc,
+ 0x06D2: 0x10006d2,
+ 0x06C1: 0x10006c1,
+ 0x0492: 0x1000492,
+ 0x0493: 0x1000493,
+ 0x0496: 0x1000496,
+ 0x0497: 0x1000497,
+ 0x049A: 0x100049a,
+ 0x049B: 0x100049b,
+ 0x049C: 0x100049c,
+ 0x049D: 0x100049d,
+ 0x04A2: 0x10004a2,
+ 0x04A3: 0x10004a3,
+ 0x04AE: 0x10004ae,
+ 0x04AF: 0x10004af,
+ 0x04B0: 0x10004b0,
+ 0x04B1: 0x10004b1,
+ 0x04B2: 0x10004b2,
+ 0x04B3: 0x10004b3,
+ 0x04B6: 0x10004b6,
+ 0x04B7: 0x10004b7,
+ 0x04B8: 0x10004b8,
+ 0x04B9: 0x10004b9,
+ 0x04BA: 0x10004ba,
+ 0x04BB: 0x10004bb,
+ 0x04D8: 0x10004d8,
+ 0x04D9: 0x10004d9,
+ 0x04E2: 0x10004e2,
+ 0x04E3: 0x10004e3,
+ 0x04E8: 0x10004e8,
+ 0x04E9: 0x10004e9,
+ 0x04EE: 0x10004ee,
+ 0x04EF: 0x10004ef,
+ 0x0452: 0x06a1,
+ 0x0453: 0x06a2,
+ 0x0451: 0x06a3,
+ 0x0454: 0x06a4,
+ 0x0455: 0x06a5,
+ 0x0456: 0x06a6,
+ 0x0457: 0x06a7,
+ 0x0458: 0x06a8,
+ 0x0459: 0x06a9,
+ 0x045A: 0x06aa,
+ 0x045B: 0x06ab,
+ 0x045C: 0x06ac,
+ 0x0491: 0x06ad,
+ 0x045E: 0x06ae,
+ 0x045F: 0x06af,
+ 0x2116: 0x06b0,
+ 0x0402: 0x06b1,
+ 0x0403: 0x06b2,
+ 0x0401: 0x06b3,
+ 0x0404: 0x06b4,
+ 0x0405: 0x06b5,
+ 0x0406: 0x06b6,
+ 0x0407: 0x06b7,
+ 0x0408: 0x06b8,
+ 0x0409: 0x06b9,
+ 0x040A: 0x06ba,
+ 0x040B: 0x06bb,
+ 0x040C: 0x06bc,
+ 0x0490: 0x06bd,
+ 0x040E: 0x06be,
+ 0x040F: 0x06bf,
+ 0x044E: 0x06c0,
+ 0x0430: 0x06c1,
+ 0x0431: 0x06c2,
+ 0x0446: 0x06c3,
+ 0x0434: 0x06c4,
+ 0x0435: 0x06c5,
+ 0x0444: 0x06c6,
+ 0x0433: 0x06c7,
+ 0x0445: 0x06c8,
+ 0x0438: 0x06c9,
+ 0x0439: 0x06ca,
+ 0x043A: 0x06cb,
+ 0x043B: 0x06cc,
+ 0x043C: 0x06cd,
+ 0x043D: 0x06ce,
+ 0x043E: 0x06cf,
+ 0x043F: 0x06d0,
+ 0x044F: 0x06d1,
+ 0x0440: 0x06d2,
+ 0x0441: 0x06d3,
+ 0x0442: 0x06d4,
+ 0x0443: 0x06d5,
+ 0x0436: 0x06d6,
+ 0x0432: 0x06d7,
+ 0x044C: 0x06d8,
+ 0x044B: 0x06d9,
+ 0x0437: 0x06da,
+ 0x0448: 0x06db,
+ 0x044D: 0x06dc,
+ 0x0449: 0x06dd,
+ 0x0447: 0x06de,
+ 0x044A: 0x06df,
+ 0x042E: 0x06e0,
+ 0x0410: 0x06e1,
+ 0x0411: 0x06e2,
+ 0x0426: 0x06e3,
+ 0x0414: 0x06e4,
+ 0x0415: 0x06e5,
+ 0x0424: 0x06e6,
+ 0x0413: 0x06e7,
+ 0x0425: 0x06e8,
+ 0x0418: 0x06e9,
+ 0x0419: 0x06ea,
+ 0x041A: 0x06eb,
+ 0x041B: 0x06ec,
+ 0x041C: 0x06ed,
+ 0x041D: 0x06ee,
+ 0x041E: 0x06ef,
+ 0x041F: 0x06f0,
+ 0x042F: 0x06f1,
+ 0x0420: 0x06f2,
+ 0x0421: 0x06f3,
+ 0x0422: 0x06f4,
+ 0x0423: 0x06f5,
+ 0x0416: 0x06f6,
+ 0x0412: 0x06f7,
+ 0x042C: 0x06f8,
+ 0x042B: 0x06f9,
+ 0x0417: 0x06fa,
+ 0x0428: 0x06fb,
+ 0x042D: 0x06fc,
+ 0x0429: 0x06fd,
+ 0x0427: 0x06fe,
+ 0x042A: 0x06ff,
+ 0x0386: 0x07a1,
+ 0x0388: 0x07a2,
+ 0x0389: 0x07a3,
+ 0x038A: 0x07a4,
+ 0x03AA: 0x07a5,
+ 0x038C: 0x07a7,
+ 0x038E: 0x07a8,
+ 0x03AB: 0x07a9,
+ 0x038F: 0x07ab,
+ 0x0385: 0x07ae,
+ 0x2015: 0x07af,
+ 0x03AC: 0x07b1,
+ 0x03AD: 0x07b2,
+ 0x03AE: 0x07b3,
+ 0x03AF: 0x07b4,
+ 0x03CA: 0x07b5,
+ 0x0390: 0x07b6,
+ 0x03CC: 0x07b7,
+ 0x03CD: 0x07b8,
+ 0x03CB: 0x07b9,
+ 0x03B0: 0x07ba,
+ 0x03CE: 0x07bb,
+ 0x0391: 0x07c1,
+ 0x0392: 0x07c2,
+ 0x0393: 0x07c3,
+ 0x0394: 0x07c4,
+ 0x0395: 0x07c5,
+ 0x0396: 0x07c6,
+ 0x0397: 0x07c7,
+ 0x0398: 0x07c8,
+ 0x0399: 0x07c9,
+ 0x039A: 0x07ca,
+ 0x039B: 0x07cb,
+ 0x039C: 0x07cc,
+ 0x039D: 0x07cd,
+ 0x039E: 0x07ce,
+ 0x039F: 0x07cf,
+ 0x03A0: 0x07d0,
+ 0x03A1: 0x07d1,
+ 0x03A3: 0x07d2,
+ 0x03A4: 0x07d4,
+ 0x03A5: 0x07d5,
+ 0x03A6: 0x07d6,
+ 0x03A7: 0x07d7,
+ 0x03A8: 0x07d8,
+ 0x03A9: 0x07d9,
+ 0x03B1: 0x07e1,
+ 0x03B2: 0x07e2,
+ 0x03B3: 0x07e3,
+ 0x03B4: 0x07e4,
+ 0x03B5: 0x07e5,
+ 0x03B6: 0x07e6,
+ 0x03B7: 0x07e7,
+ 0x03B8: 0x07e8,
+ 0x03B9: 0x07e9,
+ 0x03BA: 0x07ea,
+ 0x03BB: 0x07eb,
+ 0x03BC: 0x07ec,
+ 0x03BD: 0x07ed,
+ 0x03BE: 0x07ee,
+ 0x03BF: 0x07ef,
+ 0x03C0: 0x07f0,
+ 0x03C1: 0x07f1,
+ 0x03C3: 0x07f2,
+ 0x03C2: 0x07f3,
+ 0x03C4: 0x07f4,
+ 0x03C5: 0x07f5,
+ 0x03C6: 0x07f6,
+ 0x03C7: 0x07f7,
+ 0x03C8: 0x07f8,
+ 0x03C9: 0x07f9,
+ 0x23B7: 0x08a1,
+ 0x2320: 0x08a4,
+ 0x2321: 0x08a5,
+ 0x23A1: 0x08a7,
+ 0x23A3: 0x08a8,
+ 0x23A4: 0x08a9,
+ 0x23A6: 0x08aa,
+ 0x239B: 0x08ab,
+ 0x239D: 0x08ac,
+ 0x239E: 0x08ad,
+ 0x23A0: 0x08ae,
+ 0x23A8: 0x08af,
+ 0x23AC: 0x08b0,
+ 0x2264: 0x08bc,
+ 0x2260: 0x08bd,
+ 0x2265: 0x08be,
+ 0x222B: 0x08bf,
+ 0x2234: 0x08c0,
+ 0x221D: 0x08c1,
+ 0x221E: 0x08c2,
+ 0x2207: 0x08c5,
+ 0x223C: 0x08c8,
+ 0x2243: 0x08c9,
+ 0x21D4: 0x08cd,
+ 0x21D2: 0x08ce,
+ 0x2261: 0x08cf,
+ 0x221A: 0x08d6,
+ 0x2282: 0x08da,
+ 0x2283: 0x08db,
+ 0x2229: 0x08dc,
+ 0x222A: 0x08dd,
+ 0x2227: 0x08de,
+ 0x2228: 0x08df,
+ 0x2202: 0x08ef,
+ 0x0192: 0x08f6,
+ 0x2190: 0x08fb,
+ 0x2191: 0x08fc,
+ 0x2192: 0x08fd,
+ 0x2193: 0x08fe,
+ 0x25C6: 0x09e0,
+ 0x2592: 0x09e1,
+ 0x2409: 0x09e2,
+ 0x240C: 0x09e3,
+ 0x240D: 0x09e4,
+ 0x240A: 0x09e5,
+ 0x2424: 0x09e8,
+ 0x240B: 0x09e9,
+ 0x2518: 0x09ea,
+ 0x2510: 0x09eb,
+ 0x250C: 0x09ec,
+ 0x2514: 0x09ed,
+ 0x253C: 0x09ee,
+ 0x23BA: 0x09ef,
+ 0x23BB: 0x09f0,
+ 0x2500: 0x09f1,
+ 0x23BC: 0x09f2,
+ 0x23BD: 0x09f3,
+ 0x251C: 0x09f4,
+ 0x2524: 0x09f5,
+ 0x2534: 0x09f6,
+ 0x252C: 0x09f7,
+ 0x2502: 0x09f8,
+ 0x2003: 0x0aa1,
+ 0x2002: 0x0aa2,
+ 0x2004: 0x0aa3,
+ 0x2005: 0x0aa4,
+ 0x2007: 0x0aa5,
+ 0x2008: 0x0aa6,
+ 0x2009: 0x0aa7,
+ 0x200A: 0x0aa8,
+ 0x2014: 0x0aa9,
+ 0x2013: 0x0aaa,
+ 0x2026: 0x0aae,
+ 0x2025: 0x0aaf,
+ 0x2153: 0x0ab0,
+ 0x2154: 0x0ab1,
+ 0x2155: 0x0ab2,
+ 0x2156: 0x0ab3,
+ 0x2157: 0x0ab4,
+ 0x2158: 0x0ab5,
+ 0x2159: 0x0ab6,
+ 0x215A: 0x0ab7,
+ 0x2105: 0x0ab8,
+ 0x2012: 0x0abb,
+ 0x215B: 0x0ac3,
+ 0x215C: 0x0ac4,
+ 0x215D: 0x0ac5,
+ 0x215E: 0x0ac6,
+ 0x2122: 0x0ac9,
+ 0x2018: 0x0ad0,
+ 0x2019: 0x0ad1,
+ 0x201C: 0x0ad2,
+ 0x201D: 0x0ad3,
+ 0x211E: 0x0ad4,
+ 0x2032: 0x0ad6,
+ 0x2033: 0x0ad7,
+ 0x271D: 0x0ad9,
+ 0x2663: 0x0aec,
+ 0x2666: 0x0aed,
+ 0x2665: 0x0aee,
+ 0x2720: 0x0af0,
+ 0x2020: 0x0af1,
+ 0x2021: 0x0af2,
+ 0x2713: 0x0af3,
+ 0x2717: 0x0af4,
+ 0x266F: 0x0af5,
+ 0x266D: 0x0af6,
+ 0x2642: 0x0af7,
+ 0x2640: 0x0af8,
+ 0x260E: 0x0af9,
+ 0x2315: 0x0afa,
+ 0x2117: 0x0afb,
+ 0x2038: 0x0afc,
+ 0x201A: 0x0afd,
+ 0x201E: 0x0afe,
+ 0x22A4: 0x0bc2,
+ 0x230A: 0x0bc4,
+ 0x2218: 0x0bca,
+ 0x2395: 0x0bcc,
+ 0x22A5: 0x0bce,
+ 0x25CB: 0x0bcf,
+ 0x2308: 0x0bd3,
+ 0x22A3: 0x0bdc,
+ 0x22A2: 0x0bfc,
+ 0x2017: 0x0cdf,
+ 0x05D0: 0x0ce0,
+ 0x05D1: 0x0ce1,
+ 0x05D2: 0x0ce2,
+ 0x05D3: 0x0ce3,
+ 0x05D4: 0x0ce4,
+ 0x05D5: 0x0ce5,
+ 0x05D6: 0x0ce6,
+ 0x05D7: 0x0ce7,
+ 0x05D8: 0x0ce8,
+ 0x05D9: 0x0ce9,
+ 0x05DA: 0x0cea,
+ 0x05DB: 0x0ceb,
+ 0x05DC: 0x0cec,
+ 0x05DD: 0x0ced,
+ 0x05DE: 0x0cee,
+ 0x05DF: 0x0cef,
+ 0x05E0: 0x0cf0,
+ 0x05E1: 0x0cf1,
+ 0x05E2: 0x0cf2,
+ 0x05E3: 0x0cf3,
+ 0x05E4: 0x0cf4,
+ 0x05E5: 0x0cf5,
+ 0x05E6: 0x0cf6,
+ 0x05E7: 0x0cf7,
+ 0x05E8: 0x0cf8,
+ 0x05E9: 0x0cf9,
+ 0x05EA: 0x0cfa,
+ 0x0E01: 0x0da1,
+ 0x0E02: 0x0da2,
+ 0x0E03: 0x0da3,
+ 0x0E04: 0x0da4,
+ 0x0E05: 0x0da5,
+ 0x0E06: 0x0da6,
+ 0x0E07: 0x0da7,
+ 0x0E08: 0x0da8,
+ 0x0E09: 0x0da9,
+ 0x0E0A: 0x0daa,
+ 0x0E0B: 0x0dab,
+ 0x0E0C: 0x0dac,
+ 0x0E0D: 0x0dad,
+ 0x0E0E: 0x0dae,
+ 0x0E0F: 0x0daf,
+ 0x0E10: 0x0db0,
+ 0x0E11: 0x0db1,
+ 0x0E12: 0x0db2,
+ 0x0E13: 0x0db3,
+ 0x0E14: 0x0db4,
+ 0x0E15: 0x0db5,
+ 0x0E16: 0x0db6,
+ 0x0E17: 0x0db7,
+ 0x0E18: 0x0db8,
+ 0x0E19: 0x0db9,
+ 0x0E1A: 0x0dba,
+ 0x0E1B: 0x0dbb,
+ 0x0E1C: 0x0dbc,
+ 0x0E1D: 0x0dbd,
+ 0x0E1E: 0x0dbe,
+ 0x0E1F: 0x0dbf,
+ 0x0E20: 0x0dc0,
+ 0x0E21: 0x0dc1,
+ 0x0E22: 0x0dc2,
+ 0x0E23: 0x0dc3,
+ 0x0E24: 0x0dc4,
+ 0x0E25: 0x0dc5,
+ 0x0E26: 0x0dc6,
+ 0x0E27: 0x0dc7,
+ 0x0E28: 0x0dc8,
+ 0x0E29: 0x0dc9,
+ 0x0E2A: 0x0dca,
+ 0x0E2B: 0x0dcb,
+ 0x0E2C: 0x0dcc,
+ 0x0E2D: 0x0dcd,
+ 0x0E2E: 0x0dce,
+ 0x0E2F: 0x0dcf,
+ 0x0E30: 0x0dd0,
+ 0x0E31: 0x0dd1,
+ 0x0E32: 0x0dd2,
+ 0x0E33: 0x0dd3,
+ 0x0E34: 0x0dd4,
+ 0x0E35: 0x0dd5,
+ 0x0E36: 0x0dd6,
+ 0x0E37: 0x0dd7,
+ 0x0E38: 0x0dd8,
+ 0x0E39: 0x0dd9,
+ 0x0E3A: 0x0dda,
+ 0x0E3F: 0x0ddf,
+ 0x0E40: 0x0de0,
+ 0x0E41: 0x0de1,
+ 0x0E42: 0x0de2,
+ 0x0E43: 0x0de3,
+ 0x0E44: 0x0de4,
+ 0x0E45: 0x0de5,
+ 0x0E46: 0x0de6,
+ 0x0E47: 0x0de7,
+ 0x0E48: 0x0de8,
+ 0x0E49: 0x0de9,
+ 0x0E4A: 0x0dea,
+ 0x0E4B: 0x0deb,
+ 0x0E4C: 0x0dec,
+ 0x0E4D: 0x0ded,
+ 0x0E50: 0x0df0,
+ 0x0E51: 0x0df1,
+ 0x0E52: 0x0df2,
+ 0x0E53: 0x0df3,
+ 0x0E54: 0x0df4,
+ 0x0E55: 0x0df5,
+ 0x0E56: 0x0df6,
+ 0x0E57: 0x0df7,
+ 0x0E58: 0x0df8,
+ 0x0E59: 0x0df9,
+ 0x0587: 0x1000587,
+ 0x0589: 0x1000589,
+ 0x055D: 0x100055d,
+ 0x058A: 0x100058a,
+ 0x055C: 0x100055c,
+ 0x055B: 0x100055b,
+ 0x055E: 0x100055e,
+ 0x0531: 0x1000531,
+ 0x0561: 0x1000561,
+ 0x0532: 0x1000532,
+ 0x0562: 0x1000562,
+ 0x0533: 0x1000533,
+ 0x0563: 0x1000563,
+ 0x0534: 0x1000534,
+ 0x0564: 0x1000564,
+ 0x0535: 0x1000535,
+ 0x0565: 0x1000565,
+ 0x0536: 0x1000536,
+ 0x0566: 0x1000566,
+ 0x0537: 0x1000537,
+ 0x0567: 0x1000567,
+ 0x0538: 0x1000538,
+ 0x0568: 0x1000568,
+ 0x0539: 0x1000539,
+ 0x0569: 0x1000569,
+ 0x053A: 0x100053a,
+ 0x056A: 0x100056a,
+ 0x053B: 0x100053b,
+ 0x056B: 0x100056b,
+ 0x053C: 0x100053c,
+ 0x056C: 0x100056c,
+ 0x053D: 0x100053d,
+ 0x056D: 0x100056d,
+ 0x053E: 0x100053e,
+ 0x056E: 0x100056e,
+ 0x053F: 0x100053f,
+ 0x056F: 0x100056f,
+ 0x0540: 0x1000540,
+ 0x0570: 0x1000570,
+ 0x0541: 0x1000541,
+ 0x0571: 0x1000571,
+ 0x0542: 0x1000542,
+ 0x0572: 0x1000572,
+ 0x0543: 0x1000543,
+ 0x0573: 0x1000573,
+ 0x0544: 0x1000544,
+ 0x0574: 0x1000574,
+ 0x0545: 0x1000545,
+ 0x0575: 0x1000575,
+ 0x0546: 0x1000546,
+ 0x0576: 0x1000576,
+ 0x0547: 0x1000547,
+ 0x0577: 0x1000577,
+ 0x0548: 0x1000548,
+ 0x0578: 0x1000578,
+ 0x0549: 0x1000549,
+ 0x0579: 0x1000579,
+ 0x054A: 0x100054a,
+ 0x057A: 0x100057a,
+ 0x054B: 0x100054b,
+ 0x057B: 0x100057b,
+ 0x054C: 0x100054c,
+ 0x057C: 0x100057c,
+ 0x054D: 0x100054d,
+ 0x057D: 0x100057d,
+ 0x054E: 0x100054e,
+ 0x057E: 0x100057e,
+ 0x054F: 0x100054f,
+ 0x057F: 0x100057f,
+ 0x0550: 0x1000550,
+ 0x0580: 0x1000580,
+ 0x0551: 0x1000551,
+ 0x0581: 0x1000581,
+ 0x0552: 0x1000552,
+ 0x0582: 0x1000582,
+ 0x0553: 0x1000553,
+ 0x0583: 0x1000583,
+ 0x0554: 0x1000554,
+ 0x0584: 0x1000584,
+ 0x0555: 0x1000555,
+ 0x0585: 0x1000585,
+ 0x0556: 0x1000556,
+ 0x0586: 0x1000586,
+ 0x055A: 0x100055a,
+ 0x10D0: 0x10010d0,
+ 0x10D1: 0x10010d1,
+ 0x10D2: 0x10010d2,
+ 0x10D3: 0x10010d3,
+ 0x10D4: 0x10010d4,
+ 0x10D5: 0x10010d5,
+ 0x10D6: 0x10010d6,
+ 0x10D7: 0x10010d7,
+ 0x10D8: 0x10010d8,
+ 0x10D9: 0x10010d9,
+ 0x10DA: 0x10010da,
+ 0x10DB: 0x10010db,
+ 0x10DC: 0x10010dc,
+ 0x10DD: 0x10010dd,
+ 0x10DE: 0x10010de,
+ 0x10DF: 0x10010df,
+ 0x10E0: 0x10010e0,
+ 0x10E1: 0x10010e1,
+ 0x10E2: 0x10010e2,
+ 0x10E3: 0x10010e3,
+ 0x10E4: 0x10010e4,
+ 0x10E5: 0x10010e5,
+ 0x10E6: 0x10010e6,
+ 0x10E7: 0x10010e7,
+ 0x10E8: 0x10010e8,
+ 0x10E9: 0x10010e9,
+ 0x10EA: 0x10010ea,
+ 0x10EB: 0x10010eb,
+ 0x10EC: 0x10010ec,
+ 0x10ED: 0x10010ed,
+ 0x10EE: 0x10010ee,
+ 0x10EF: 0x10010ef,
+ 0x10F0: 0x10010f0,
+ 0x10F1: 0x10010f1,
+ 0x10F2: 0x10010f2,
+ 0x10F3: 0x10010f3,
+ 0x10F4: 0x10010f4,
+ 0x10F5: 0x10010f5,
+ 0x10F6: 0x10010f6,
+ 0x1E8A: 0x1001e8a,
+ 0x012C: 0x100012c,
+ 0x01B5: 0x10001b5,
+ 0x01E6: 0x10001e6,
+ 0x01D2: 0x10001d1,
+ 0x019F: 0x100019f,
+ 0x1E8B: 0x1001e8b,
+ 0x012D: 0x100012d,
+ 0x01B6: 0x10001b6,
+ 0x01E7: 0x10001e7,
+ 0x01D2: 0x10001d2,
+ 0x0275: 0x1000275,
+ 0x018F: 0x100018f,
+ 0x0259: 0x1000259,
+ 0x1E36: 0x1001e36,
+ 0x1E37: 0x1001e37,
+ 0x1EA0: 0x1001ea0,
+ 0x1EA1: 0x1001ea1,
+ 0x1EA2: 0x1001ea2,
+ 0x1EA3: 0x1001ea3,
+ 0x1EA4: 0x1001ea4,
+ 0x1EA5: 0x1001ea5,
+ 0x1EA6: 0x1001ea6,
+ 0x1EA7: 0x1001ea7,
+ 0x1EA8: 0x1001ea8,
+ 0x1EA9: 0x1001ea9,
+ 0x1EAA: 0x1001eaa,
+ 0x1EAB: 0x1001eab,
+ 0x1EAC: 0x1001eac,
+ 0x1EAD: 0x1001ead,
+ 0x1EAE: 0x1001eae,
+ 0x1EAF: 0x1001eaf,
+ 0x1EB0: 0x1001eb0,
+ 0x1EB1: 0x1001eb1,
+ 0x1EB2: 0x1001eb2,
+ 0x1EB3: 0x1001eb3,
+ 0x1EB4: 0x1001eb4,
+ 0x1EB5: 0x1001eb5,
+ 0x1EB6: 0x1001eb6,
+ 0x1EB7: 0x1001eb7,
+ 0x1EB8: 0x1001eb8,
+ 0x1EB9: 0x1001eb9,
+ 0x1EBA: 0x1001eba,
+ 0x1EBB: 0x1001ebb,
+ 0x1EBC: 0x1001ebc,
+ 0x1EBD: 0x1001ebd,
+ 0x1EBE: 0x1001ebe,
+ 0x1EBF: 0x1001ebf,
+ 0x1EC0: 0x1001ec0,
+ 0x1EC1: 0x1001ec1,
+ 0x1EC2: 0x1001ec2,
+ 0x1EC3: 0x1001ec3,
+ 0x1EC4: 0x1001ec4,
+ 0x1EC5: 0x1001ec5,
+ 0x1EC6: 0x1001ec6,
+ 0x1EC7: 0x1001ec7,
+ 0x1EC8: 0x1001ec8,
+ 0x1EC9: 0x1001ec9,
+ 0x1ECA: 0x1001eca,
+ 0x1ECB: 0x1001ecb,
+ 0x1ECC: 0x1001ecc,
+ 0x1ECD: 0x1001ecd,
+ 0x1ECE: 0x1001ece,
+ 0x1ECF: 0x1001ecf,
+ 0x1ED0: 0x1001ed0,
+ 0x1ED1: 0x1001ed1,
+ 0x1ED2: 0x1001ed2,
+ 0x1ED3: 0x1001ed3,
+ 0x1ED4: 0x1001ed4,
+ 0x1ED5: 0x1001ed5,
+ 0x1ED6: 0x1001ed6,
+ 0x1ED7: 0x1001ed7,
+ 0x1ED8: 0x1001ed8,
+ 0x1ED9: 0x1001ed9,
+ 0x1EDA: 0x1001eda,
+ 0x1EDB: 0x1001edb,
+ 0x1EDC: 0x1001edc,
+ 0x1EDD: 0x1001edd,
+ 0x1EDE: 0x1001ede,
+ 0x1EDF: 0x1001edf,
+ 0x1EE0: 0x1001ee0,
+ 0x1EE1: 0x1001ee1,
+ 0x1EE2: 0x1001ee2,
+ 0x1EE3: 0x1001ee3,
+ 0x1EE4: 0x1001ee4,
+ 0x1EE5: 0x1001ee5,
+ 0x1EE6: 0x1001ee6,
+ 0x1EE7: 0x1001ee7,
+ 0x1EE8: 0x1001ee8,
+ 0x1EE9: 0x1001ee9,
+ 0x1EEA: 0x1001eea,
+ 0x1EEB: 0x1001eeb,
+ 0x1EEC: 0x1001eec,
+ 0x1EED: 0x1001eed,
+ 0x1EEE: 0x1001eee,
+ 0x1EEF: 0x1001eef,
+ 0x1EF0: 0x1001ef0,
+ 0x1EF1: 0x1001ef1,
+ 0x1EF4: 0x1001ef4,
+ 0x1EF5: 0x1001ef5,
+ 0x1EF6: 0x1001ef6,
+ 0x1EF7: 0x1001ef7,
+ 0x1EF8: 0x1001ef8,
+ 0x1EF9: 0x1001ef9,
+ 0x01A0: 0x10001a0,
+ 0x01A1: 0x10001a1,
+ 0x01AF: 0x10001af,
+ 0x01B0: 0x10001b0,
+ 0x20A0: 0x10020a0,
+ 0x20A1: 0x10020a1,
+ 0x20A2: 0x10020a2,
+ 0x20A3: 0x10020a3,
+ 0x20A4: 0x10020a4,
+ 0x20A5: 0x10020a5,
+ 0x20A6: 0x10020a6,
+ 0x20A7: 0x10020a7,
+ 0x20A8: 0x10020a8,
+ 0x20A9: 0x10020a9,
+ 0x20AA: 0x10020aa,
+ 0x20AB: 0x10020ab,
+ 0x20AC: 0x20ac,
+ 0x2070: 0x1002070,
+ 0x2074: 0x1002074,
+ 0x2075: 0x1002075,
+ 0x2076: 0x1002076,
+ 0x2077: 0x1002077,
+ 0x2078: 0x1002078,
+ 0x2079: 0x1002079,
+ 0x2080: 0x1002080,
+ 0x2081: 0x1002081,
+ 0x2082: 0x1002082,
+ 0x2083: 0x1002083,
+ 0x2084: 0x1002084,
+ 0x2085: 0x1002085,
+ 0x2086: 0x1002086,
+ 0x2087: 0x1002087,
+ 0x2088: 0x1002088,
+ 0x2089: 0x1002089,
+ 0x2202: 0x1002202,
+ 0x2205: 0x1002205,
+ 0x2208: 0x1002208,
+ 0x2209: 0x1002209,
+ 0x220B: 0x100220B,
+ 0x221A: 0x100221A,
+ 0x221B: 0x100221B,
+ 0x221C: 0x100221C,
+ 0x222C: 0x100222C,
+ 0x222D: 0x100222D,
+ 0x2235: 0x1002235,
+ 0x2245: 0x1002248,
+ 0x2247: 0x1002247,
+ 0x2262: 0x1002262,
+ 0x2263: 0x1002263,
+ 0x2800: 0x1002800,
+ 0x2801: 0x1002801,
+ 0x2802: 0x1002802,
+ 0x2803: 0x1002803,
+ 0x2804: 0x1002804,
+ 0x2805: 0x1002805,
+ 0x2806: 0x1002806,
+ 0x2807: 0x1002807,
+ 0x2808: 0x1002808,
+ 0x2809: 0x1002809,
+ 0x280a: 0x100280a,
+ 0x280b: 0x100280b,
+ 0x280c: 0x100280c,
+ 0x280d: 0x100280d,
+ 0x280e: 0x100280e,
+ 0x280f: 0x100280f,
+ 0x2810: 0x1002810,
+ 0x2811: 0x1002811,
+ 0x2812: 0x1002812,
+ 0x2813: 0x1002813,
+ 0x2814: 0x1002814,
+ 0x2815: 0x1002815,
+ 0x2816: 0x1002816,
+ 0x2817: 0x1002817,
+ 0x2818: 0x1002818,
+ 0x2819: 0x1002819,
+ 0x281a: 0x100281a,
+ 0x281b: 0x100281b,
+ 0x281c: 0x100281c,
+ 0x281d: 0x100281d,
+ 0x281e: 0x100281e,
+ 0x281f: 0x100281f,
+ 0x2820: 0x1002820,
+ 0x2821: 0x1002821,
+ 0x2822: 0x1002822,
+ 0x2823: 0x1002823,
+ 0x2824: 0x1002824,
+ 0x2825: 0x1002825,
+ 0x2826: 0x1002826,
+ 0x2827: 0x1002827,
+ 0x2828: 0x1002828,
+ 0x2829: 0x1002829,
+ 0x282a: 0x100282a,
+ 0x282b: 0x100282b,
+ 0x282c: 0x100282c,
+ 0x282d: 0x100282d,
+ 0x282e: 0x100282e,
+ 0x282f: 0x100282f,
+ 0x2830: 0x1002830,
+ 0x2831: 0x1002831,
+ 0x2832: 0x1002832,
+ 0x2833: 0x1002833,
+ 0x2834: 0x1002834,
+ 0x2835: 0x1002835,
+ 0x2836: 0x1002836,
+ 0x2837: 0x1002837,
+ 0x2838: 0x1002838,
+ 0x2839: 0x1002839,
+ 0x283a: 0x100283a,
+ 0x283b: 0x100283b,
+ 0x283c: 0x100283c,
+ 0x283d: 0x100283d,
+ 0x283e: 0x100283e,
+ 0x283f: 0x100283f,
+ 0x2840: 0x1002840,
+ 0x2841: 0x1002841,
+ 0x2842: 0x1002842,
+ 0x2843: 0x1002843,
+ 0x2844: 0x1002844,
+ 0x2845: 0x1002845,
+ 0x2846: 0x1002846,
+ 0x2847: 0x1002847,
+ 0x2848: 0x1002848,
+ 0x2849: 0x1002849,
+ 0x284a: 0x100284a,
+ 0x284b: 0x100284b,
+ 0x284c: 0x100284c,
+ 0x284d: 0x100284d,
+ 0x284e: 0x100284e,
+ 0x284f: 0x100284f,
+ 0x2850: 0x1002850,
+ 0x2851: 0x1002851,
+ 0x2852: 0x1002852,
+ 0x2853: 0x1002853,
+ 0x2854: 0x1002854,
+ 0x2855: 0x1002855,
+ 0x2856: 0x1002856,
+ 0x2857: 0x1002857,
+ 0x2858: 0x1002858,
+ 0x2859: 0x1002859,
+ 0x285a: 0x100285a,
+ 0x285b: 0x100285b,
+ 0x285c: 0x100285c,
+ 0x285d: 0x100285d,
+ 0x285e: 0x100285e,
+ 0x285f: 0x100285f,
+ 0x2860: 0x1002860,
+ 0x2861: 0x1002861,
+ 0x2862: 0x1002862,
+ 0x2863: 0x1002863,
+ 0x2864: 0x1002864,
+ 0x2865: 0x1002865,
+ 0x2866: 0x1002866,
+ 0x2867: 0x1002867,
+ 0x2868: 0x1002868,
+ 0x2869: 0x1002869,
+ 0x286a: 0x100286a,
+ 0x286b: 0x100286b,
+ 0x286c: 0x100286c,
+ 0x286d: 0x100286d,
+ 0x286e: 0x100286e,
+ 0x286f: 0x100286f,
+ 0x2870: 0x1002870,
+ 0x2871: 0x1002871,
+ 0x2872: 0x1002872,
+ 0x2873: 0x1002873,
+ 0x2874: 0x1002874,
+ 0x2875: 0x1002875,
+ 0x2876: 0x1002876,
+ 0x2877: 0x1002877,
+ 0x2878: 0x1002878,
+ 0x2879: 0x1002879,
+ 0x287a: 0x100287a,
+ 0x287b: 0x100287b,
+ 0x287c: 0x100287c,
+ 0x287d: 0x100287d,
+ 0x287e: 0x100287e,
+ 0x287f: 0x100287f,
+ 0x2880: 0x1002880,
+ 0x2881: 0x1002881,
+ 0x2882: 0x1002882,
+ 0x2883: 0x1002883,
+ 0x2884: 0x1002884,
+ 0x2885: 0x1002885,
+ 0x2886: 0x1002886,
+ 0x2887: 0x1002887,
+ 0x2888: 0x1002888,
+ 0x2889: 0x1002889,
+ 0x288a: 0x100288a,
+ 0x288b: 0x100288b,
+ 0x288c: 0x100288c,
+ 0x288d: 0x100288d,
+ 0x288e: 0x100288e,
+ 0x288f: 0x100288f,
+ 0x2890: 0x1002890,
+ 0x2891: 0x1002891,
+ 0x2892: 0x1002892,
+ 0x2893: 0x1002893,
+ 0x2894: 0x1002894,
+ 0x2895: 0x1002895,
+ 0x2896: 0x1002896,
+ 0x2897: 0x1002897,
+ 0x2898: 0x1002898,
+ 0x2899: 0x1002899,
+ 0x289a: 0x100289a,
+ 0x289b: 0x100289b,
+ 0x289c: 0x100289c,
+ 0x289d: 0x100289d,
+ 0x289e: 0x100289e,
+ 0x289f: 0x100289f,
+ 0x28a0: 0x10028a0,
+ 0x28a1: 0x10028a1,
+ 0x28a2: 0x10028a2,
+ 0x28a3: 0x10028a3,
+ 0x28a4: 0x10028a4,
+ 0x28a5: 0x10028a5,
+ 0x28a6: 0x10028a6,
+ 0x28a7: 0x10028a7,
+ 0x28a8: 0x10028a8,
+ 0x28a9: 0x10028a9,
+ 0x28aa: 0x10028aa,
+ 0x28ab: 0x10028ab,
+ 0x28ac: 0x10028ac,
+ 0x28ad: 0x10028ad,
+ 0x28ae: 0x10028ae,
+ 0x28af: 0x10028af,
+ 0x28b0: 0x10028b0,
+ 0x28b1: 0x10028b1,
+ 0x28b2: 0x10028b2,
+ 0x28b3: 0x10028b3,
+ 0x28b4: 0x10028b4,
+ 0x28b5: 0x10028b5,
+ 0x28b6: 0x10028b6,
+ 0x28b7: 0x10028b7,
+ 0x28b8: 0x10028b8,
+ 0x28b9: 0x10028b9,
+ 0x28ba: 0x10028ba,
+ 0x28bb: 0x10028bb,
+ 0x28bc: 0x10028bc,
+ 0x28bd: 0x10028bd,
+ 0x28be: 0x10028be,
+ 0x28bf: 0x10028bf,
+ 0x28c0: 0x10028c0,
+ 0x28c1: 0x10028c1,
+ 0x28c2: 0x10028c2,
+ 0x28c3: 0x10028c3,
+ 0x28c4: 0x10028c4,
+ 0x28c5: 0x10028c5,
+ 0x28c6: 0x10028c6,
+ 0x28c7: 0x10028c7,
+ 0x28c8: 0x10028c8,
+ 0x28c9: 0x10028c9,
+ 0x28ca: 0x10028ca,
+ 0x28cb: 0x10028cb,
+ 0x28cc: 0x10028cc,
+ 0x28cd: 0x10028cd,
+ 0x28ce: 0x10028ce,
+ 0x28cf: 0x10028cf,
+ 0x28d0: 0x10028d0,
+ 0x28d1: 0x10028d1,
+ 0x28d2: 0x10028d2,
+ 0x28d3: 0x10028d3,
+ 0x28d4: 0x10028d4,
+ 0x28d5: 0x10028d5,
+ 0x28d6: 0x10028d6,
+ 0x28d7: 0x10028d7,
+ 0x28d8: 0x10028d8,
+ 0x28d9: 0x10028d9,
+ 0x28da: 0x10028da,
+ 0x28db: 0x10028db,
+ 0x28dc: 0x10028dc,
+ 0x28dd: 0x10028dd,
+ 0x28de: 0x10028de,
+ 0x28df: 0x10028df,
+ 0x28e0: 0x10028e0,
+ 0x28e1: 0x10028e1,
+ 0x28e2: 0x10028e2,
+ 0x28e3: 0x10028e3,
+ 0x28e4: 0x10028e4,
+ 0x28e5: 0x10028e5,
+ 0x28e6: 0x10028e6,
+ 0x28e7: 0x10028e7,
+ 0x28e8: 0x10028e8,
+ 0x28e9: 0x10028e9,
+ 0x28ea: 0x10028ea,
+ 0x28eb: 0x10028eb,
+ 0x28ec: 0x10028ec,
+ 0x28ed: 0x10028ed,
+ 0x28ee: 0x10028ee,
+ 0x28ef: 0x10028ef,
+ 0x28f0: 0x10028f0,
+ 0x28f1: 0x10028f1,
+ 0x28f2: 0x10028f2,
+ 0x28f3: 0x10028f3,
+ 0x28f4: 0x10028f4,
+ 0x28f5: 0x10028f5,
+ 0x28f6: 0x10028f6,
+ 0x28f7: 0x10028f7,
+ 0x28f8: 0x10028f8,
+ 0x28f9: 0x10028f9,
+ 0x28fa: 0x10028fa,
+ 0x28fb: 0x10028fb,
+ 0x28fc: 0x10028fc,
+ 0x28fd: 0x10028fd,
+ 0x28fe: 0x10028fe,
+ 0x28ff: 0x10028ff
+};
+
+var ON_KEYDOWN = 1 << 0; /* Report on keydown, otherwise wait until keypress */
+
+
+var specialKeyTable = {
+ // These generate a keyDown and keyPress in Firefox and Opera
+ 8: [0xFF08, ON_KEYDOWN], // BACKSPACE
+ 13: [0xFF0D, ON_KEYDOWN], // ENTER
+
+ // This generates a keyDown and keyPress in Opera
+ 9: [0xFF09, ON_KEYDOWN], // TAB
+
+ 27: 0xFF1B, // ESCAPE
+ 46: 0xFFFF, // DELETE
+ 36: 0xFF50, // HOME
+ 35: 0xFF57, // END
+ 33: 0xFF55, // PAGE_UP
+ 34: 0xFF56, // PAGE_DOWN
+ 45: 0xFF63, // INSERT
+ 37: 0xFF51, // LEFT
+ 38: 0xFF52, // UP
+ 39: 0xFF53, // RIGHT
+ 40: 0xFF54, // DOWN
+ 16: 0xFFE1, // SHIFT
+ 17: 0xFFE3, // CONTROL
+ 18: 0xFFE9, // Left ALT (Mac Command)
+ 112: 0xFFBE, // F1
+ 113: 0xFFBF, // F2
+ 114: 0xFFC0, // F3
+ 115: 0xFFC1, // F4
+ 116: 0xFFC2, // F5
+ 117: 0xFFC3, // F6
+ 118: 0xFFC4, // F7
+ 119: 0xFFC5, // F8
+ 120: 0xFFC6, // F9
+ 121: 0xFFC7, // F10
+ 122: 0xFFC8, // F11
+ 123: 0xFFC9 // F12
+};
+
+function getEventKeySym(ev) {
+ if (typeof ev.which !== "undefined" && ev.which > 0)
+ return ev.which;
+ return ev.keyCode;
+}
+
+// This is based on the approach from noVNC. We handle
+// everything in keydown that we have all info for, and that
+// are not safe to pass on to the browser (as it may do something
+// with the key. The rest we pass on to keypress so we can get the
+// translated keysym.
+function getKeysymSpecial(ev) {
+ if (ev.keyCode in specialKeyTable) {
+ var r = specialKeyTable[ev.keyCode];
+ var flags = 0;
+ if (typeof r != 'number') {
+ flags = r[1];
+ r = r[0];
+ }
+ if (ev.type === 'keydown' || flags & ON_KEYDOWN)
+ return r;
+ }
+ // If we don't hold alt or ctrl, then we should be safe to pass
+ // on to keypressed and look at the translated data
+ if (!ev.ctrlKey && !ev.altKey)
+ return null;
+
+ var keysym = getEventKeySym(ev);
+
+ /* Remap symbols */
+ switch (keysym) {
+ case 186 : keysym = 59; break; // ; (IE)
+ case 187 : keysym = 61; break; // = (IE)
+ case 188 : keysym = 44; break; // , (Mozilla, IE)
+ case 109 : // - (Mozilla, Opera)
+ if (true /* TODO: check if browser is firefox or opera */)
+ keysym = 45;
+ break;
+ case 189 : keysym = 45; break; // - (IE)
+ case 190 : keysym = 46; break; // . (Mozilla, IE)
+ case 191 : keysym = 47; break; // / (Mozilla, IE)
+ case 192 : keysym = 96; break; // ` (Mozilla, IE)
+ case 219 : keysym = 91; break; // [ (Mozilla, IE)
+ case 220 : keysym = 92; break; // \ (Mozilla, IE)
+ case 221 : keysym = 93; break; // ] (Mozilla, IE)
+ case 222 : keysym = 39; break; // ' (Mozilla, IE)
+ }
+
+ /* Remap shifted and unshifted keys */
+ if (!!ev.shiftKey) {
+ switch (keysym) {
+ case 48 : keysym = 41 ; break; // ) (shifted 0)
+ case 49 : keysym = 33 ; break; // ! (shifted 1)
+ case 50 : keysym = 64 ; break; // @ (shifted 2)
+ case 51 : keysym = 35 ; break; // # (shifted 3)
+ case 52 : keysym = 36 ; break; // $ (shifted 4)
+ case 53 : keysym = 37 ; break; // % (shifted 5)
+ case 54 : keysym = 94 ; break; // ^ (shifted 6)
+ case 55 : keysym = 38 ; break; // & (shifted 7)
+ case 56 : keysym = 42 ; break; // * (shifted 8)
+ case 57 : keysym = 40 ; break; // ( (shifted 9)
+ case 59 : keysym = 58 ; break; // : (shifted `)
+ case 61 : keysym = 43 ; break; // + (shifted ;)
+ case 44 : keysym = 60 ; break; // < (shifted ,)
+ case 45 : keysym = 95 ; break; // _ (shifted -)
+ case 46 : keysym = 62 ; break; // > (shifted .)
+ case 47 : keysym = 63 ; break; // ? (shifted /)
+ case 96 : keysym = 126; break; // ~ (shifted `)
+ case 91 : keysym = 123; break; // { (shifted [)
+ case 92 : keysym = 124; break; // | (shifted \)
+ case 93 : keysym = 125; break; // } (shifted ])
+ case 39 : keysym = 34 ; break; // " (shifted ')
+ }
+ } else if ((keysym >= 65) && (keysym <=90)) {
+ /* Remap unshifted A-Z */
+ keysym += 32;
+ } else if (ev.keyLocation === 3) {
+ // numpad keys
+ switch (keysym) {
+ case 96 : keysym = 48; break; // 0
+ case 97 : keysym = 49; break; // 1
+ case 98 : keysym = 50; break; // 2
+ case 99 : keysym = 51; break; // 3
+ case 100: keysym = 52; break; // 4
+ case 101: keysym = 53; break; // 5
+ case 102: keysym = 54; break; // 6
+ case 103: keysym = 55; break; // 7
+ case 104: keysym = 56; break; // 8
+ case 105: keysym = 57; break; // 9
+ case 109: keysym = 45; break; // -
+ case 110: keysym = 46; break; // .
+ case 111: keysym = 47; break; // /
+ }
+ }
+
+ return keysym;
+}
+
+/* Translate DOM keyPress event to keysym value */
+function getKeysym(ev) {
+ var keysym, msg;
+
+ keysym = getEventKeySym(ev);
+
+ if ((keysym > 255) && (keysym < 0xFF00)) {
+ // Map Unicode outside Latin 1 to gdk keysyms
+ keysym = unicodeTable[keysym];
+ if (typeof keysym === 'undefined')
+ keysym = 0;
+ }
+
+ return keysym;
+}
+
+function copyKeyEvent(ev) {
+ var members = ['type', 'keyCode', 'charCode', 'which',
+ 'altKey', 'ctrlKey', 'shiftKey',
+ 'keyLocation', 'keyIdentifier'], i, obj = {};
+ for (i = 0; i < members.length; i++) {
+ if (typeof ev[members[i]] !== "undefined")
+ obj[members[i]] = ev[members[i]];
+ }
+ return obj;
+}
+
+function pushKeyEvent(fev) {
+ keyDownList.push(fev);
+}
+
+function getKeyEvent(keyCode, pop) {
+ var i, fev = null;
+ for (i = keyDownList.length-1; i >= 0; i--) {
+ if (keyDownList[i].keyCode === keyCode) {
+ if ((typeof pop !== "undefined") && pop)
+ fev = keyDownList.splice(i, 1)[0];
+ else
+ fev = keyDownList[i];
+ break;
+ }
+ }
+ return fev;
+}
+
+function ignoreKeyEvent(ev) {
+ // Blarg. Some keys have a different keyCode on keyDown vs keyUp
+ if (ev.keyCode === 229) {
+ // French AZERTY keyboard dead key.
+ // Lame thing is that the respective keyUp is 219 so we can't
+ // properly ignore the keyUp event
+ return true;
+ }
+ return false;
+}
+
+function handleKeyDown(e) {
+ var fev = null, ev = (e ? e : window.event), keysym = null, suppress = false;
+
+ fev = copyKeyEvent(ev);
+
+ keysym = getKeysymSpecial(ev);
+ // Save keysym decoding for use in keyUp
+ fev.keysym = keysym;
+ if (keysym) {
+ // If it is a key or key combination that might trigger
+ // browser behaviors or it has no corresponding keyPress
+ // event, then send it immediately
+ if (!ignoreKeyEvent(ev))
+ sendInput("k", [realWindowWithMouse, keysym, lastState]);
+ suppress = true;
+ }
+
+ if (! ignoreKeyEvent(ev)) {
+ // Add it to the list of depressed keys
+ pushKeyEvent(fev);
+ }
+
+ if (suppress) {
+ // Suppress bubbling/default actions
+ return cancelEvent(ev);
+ }
+
+ // Allow the event to bubble and become a keyPress event which
+ // will have the character code translated
+ return true;
+}
+
+function handleKeyPress(e) {
+ var ev = (e ? e : window.event), kdlen = keyDownList.length, keysym = null;
+
+ if (((ev.which !== "undefined") && (ev.which === 0)) ||
+ getKeysymSpecial(ev)) {
+ // Firefox and Opera generate a keyPress event even if keyDown
+ // is suppressed. But the keys we want to suppress will have
+ // either:
+ // - the which attribute set to 0
+ // - getKeysymSpecial() will identify it
+ return cancelEvent(ev);
+ }
+
+ keysym = getKeysym(ev);
+
+ // Modify the the which attribute in the depressed keys list so
+ // that the keyUp event will be able to have the character code
+ // translation available.
+ if (kdlen > 0) {
+ keyDownList[kdlen-1].keysym = keysym;
+ } else {
+ //log("keyDownList empty when keyPress triggered");
+ }
+
+ // Send the translated keysym
+ if (keysym > 0)
+ sendInput ("k", [realWindowWithMouse, keysym, lastState]);
+
+ // Stop keypress events just in case
+ return cancelEvent(ev);
+}
+
+function handleKeyUp(e) {
+ var fev = null, ev = (e ? e : window.event), i, keysym;
+
+ fev = getKeyEvent(ev.keyCode, true);
+
+ if (fev)
+ keysym = fev.keysym;
+ else {
+ //log("Key event (keyCode = " + ev.keyCode + ") not found on keyDownList");
+ keysym = 0;
+ }
+
+ if (keysym > 0)
+ sendInput ("K", [realWindowWithMouse, keysym, lastState]);
+ return cancelEvent(ev);
+}
+
+function onKeyDown (ev) {
+ updateForEvent(ev);
+ if (localGrab)
+ return cancelEvent(ev);
+ return handleKeyDown(ev);
+}
+
+function onKeyPress(ev) {
+ updateForEvent(ev);
+ if (localGrab)
+ return cancelEvent(ev);
+ return handleKeyPress(ev);
+}
+
+function onKeyUp (ev) {
+ updateForEvent(ev);
+ if (localGrab)
+ return cancelEvent(ev);
+ return handleKeyUp(ev);
+}
+
+function cancelEvent(ev)
{
- if (input_socket != null) {
- input_socket.send(cmd + args.join(","));
- }
+ ev = ev ? ev : window.event;
+ if (ev.stopPropagation)
+ ev.stopPropagation();
+ if (ev.preventDefault)
+ ev.preventDefault();
+ ev.cancelBubble = true;
+ ev.cancel = true;
+ ev.returnValue = false;
+ return false;
}
-function on_mouse_move (ev) {
- send_input ("m", [get_surface_id(ev), ev.pageX, ev.pageY, ev.timeStamp])
+function onMouseWheel(ev)
+{
+ updateForEvent(ev);
+ if (localGrab)
+ return false;
+ ev = ev ? ev : window.event;
+
+ var id = getSurfaceId(ev);
+ var pos = getPositionsFromEvent(ev, id);
+
+ var offset = ev.detail ? ev.detail : ev.wheelDelta;
+ var dir = 0;
+ if (offset > 0)
+ dir = 1;
+ sendInput ("s", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, dir]);
+
+ return cancelEvent(ev);
}
-function on_mouse_down (ev) {
- send_input ("b", [get_surface_id(ev), ev.pageX, ev.pageY, ev.button, ev.timeStamp])
+function setupDocument(document)
+{
+ document.oncontextmenu = function () { return false; };
+ document.onmousemove = onMouseMove;
+ document.onmouseover = onMouseOver;
+ document.onmouseout = onMouseOut;
+ document.onmousedown = onMouseDown;
+ document.onmouseup = onMouseUp;
+ document.onkeydown = onKeyDown;
+ document.onkeypress = onKeyPress;
+ document.onkeyup = onKeyUp;
+
+ if (document.addEventListener) {
+ document.addEventListener('DOMMouseScroll', onMouseWheel, false);
+ document.addEventListener('mousewheel', onMouseWheel, false);
+ } else if (document.attachEvent) {
+ element.attachEvent("onmousewheel", onMouseWheel);
+ }
}
-function on_mouse_up (ev) {
- send_input ("B", [get_surface_id(ev), ev.pageX, ev.pageY, ev.button, ev.timeStamp])
+function newWS(loc) {
+ var ws = null;
+ if ("WebSocket" in window) {
+ ws = new WebSocket(loc, "broadway");
+ } else if ("MozWebSocket" in window) { // Firefox 6
+ ws = new MozWebSocket(loc);
+ } else {
+ alert("WebSocket not supported, broadway will not work!");
+ }
+ return ws;
}
function connect()
{
- var xhr = createXHR();
- if (xhr) {
- if (typeof xhr.multipart == 'undefined') {
- alert("Sorry, this example only works in browsers that support multipart.");
- return;
- }
-
- xhr.multipart = true;
- xhr.open("GET", "/output", true);
- xhr.onload = handleLoad;
- xhr.send(null);
- }
-
- document.onmousemove = on_mouse_move;
- document.onmousedown = on_mouse_down;
- document.onmouseup = on_mouse_up;
+ var url = window.location.toString();
+ var query_string = url.split("?");
+ if (query_string.length > 1) {
+ var params = query_string[1].split("&");
+ }
+
+ var loc = window.location.toString().replace("http:", "ws:");
+ loc = loc.substr(0, loc.lastIndexOf('/')) + "/socket";
+
+ var supports_binary = newWS (loc + "-test").binaryType == "blob";
+ if (supports_binary) {
+ ws = newWS (loc + "-bin");
+ ws.binaryType = "arraybuffer";
+ } else {
+ ws = newWS (loc);
+ }
+
+ ws.onopen = function() {
+ inputSocket = ws;
+ var w, h;
+ w = window.innerWidth;
+ h = window.innerHeight;
+ window.onresize = function(ev) {
+ var w, h;
+ w = window.innerWidth;
+ h = window.innerHeight;
+ sendInput ("d", [w, h]);
+ };
+ sendInput ("d", [w, h]);
+ };
+ ws.onclose = function() {
+ inputSocket = null;
+ };
+ ws.onmessage = function(event) {
+ handleMessage(event.data);
+ };
+
+ setupDocument(document);
}