ant-http/build/htdocs/dist/packages/default/Broadway/broadway.js
2016-10-29 14:34:29 +02:00

1172 lines
51 KiB
JavaScript

/**
* This a rewrite of GTK broadway.js
*
* OS.js - JavaScript Cloud/Web Desktop Platform
*
* Copyright (c) 2011-2016, Anders Evenrud <andersevenrud@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Anders Evenrud <andersevenrud@gmail.com>
* @licence Simplified BSD License
*/
(function() {
var connected = false;
var lastTimeStamp = 0;
var lastState;
var inputSocket = null;
var globalOpts = {};
var connection;
var outstandingCommands = [];
var keyDownList = [];
var lastSerial = 0;
var surfaces = {};
/////////////////////////////////////////////////////////////////////////////
// CONSTANTS
/////////////////////////////////////////////////////////////////////////////
var ON_KEYDOWN = 1 << 0; /* Report on keydown, otherwise wait until keypress */
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;
var specialKeyTable = {
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
};
/* 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
};
/////////////////////////////////////////////////////////////////////////////
// HELPERS
/////////////////////////////////////////////////////////////////////////////
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 (!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;
}
function getEventKeySym(ev) {
if (typeof ev.which !== 'undefined' && ev.which > 0)
return ev.which;
return ev.keyCode;
}
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 getKeysym(ev) {
var keysym = getEventKeySym(ev);
if ((keysym > 255) && (keysym < 0xFF00)) { // Map Unicode outside Latin 1 to gdk keysyms
keysym = (typeof keysym === 'undefined') ? 0 : unicodeTable[keysym];
}
return keysym;
}
function getRelativeLayer(id, ev, opts) {
opts = opts || {};
var cid = id;
if ( ev.target ) {
var tmp = ev.target.getAttribute('data-surface-id');
if ( tmp ) {
cid = parseInt(tmp, 10);
}
}
if ( cid !== id ) {
var surface = surfaces[cid];
if ( surface && surface.canvas ) {
opts.mx -= surface.canvas.offsetLeft;
opts.my -= surface.canvas.offsetTop;
}
}
return cid;
}
/////////////////////////////////////////////////////////////////////////////
// WRAPPERS
/////////////////////////////////////////////////////////////////////////////
/**
* Command for binary/arraybuffer connection
*/
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;
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 ([new Uint8Array (this.arraybuffer, this.pos, size)], {type:'image/png'});
var url;
if (window.webkitURL) {
url = window.webkitURL.createObjectURL(png_blob);
} else {
url = window.URL.createObjectURL(png_blob, {oneTimeOnly: true});
}
this.pos = this.pos + size;
return url;
};
BinCommands.prototype.free_image_url = function(url) {
URL.revokeObjectURL(url);
};
/////////////////////////////////////////////////////////////////////////////
// ACTIONS
/////////////////////////////////////////////////////////////////////////////
function doLogin() {
// TODO
}
function doLoggedIn() {
// TODO
}
function doDisconnect() {
// TODO
}
/**
* Closes a surface
*/
function closeSurface(id) {
if ( surfaces[id] ) {
sendInput('W', [id]);
}
}
/**
* Moves a surface
*/
function moveSurface(id, x, y) {
if ( surfaces[id] ) {
var surface = surfaces[id];
surface.x = x;
surface.y = y;
sendConfigureNotify(surface);
}
}
/**
* Resizes a surface
*/
function resizeSurface(id, w, h) {
if ( surfaces[id] ) {
var surface = surfaces[id];
surface.width = w;
surface.height = h;
console.log(surface);
sendConfigureNotify(surface);
// sendInput('m', [id, 1, surface.x, surface.y, 2, surface.width, surface.height]);
}
}
/////////////////////////////////////////////////////////////////////////////
// EVENTS
/////////////////////////////////////////////////////////////////////////////
/**
* On Create Surface Event
*/
function onCreateSurface(cmd) {
var id = cmd.get_16();
var x = cmd.get_16s();
var y = cmd.get_16s();
var w = cmd.get_16();
var h = cmd.get_16();
var isTemp = cmd.get_bool();
var surface = { id: id, x: x, y:y, width: w, height: h, isTemp: isTemp };
surface.positioned = isTemp;
surface.drawQueue = [];
surface.transientParent = 0;
surface.visible = false;
surfaces[id] = surface;
sendConfigureNotify(surface);
console.debug('Broadway', 'onCreateSurface()', id, x, y, w, h, isTemp);
if ( isTemp ) {
surface.canvas = document.createElement('canvas');
surface.canvas.width = w;
surface.canvas.height = h;
surface.canvas.style.position = 'absolute';
surface.canvas.style.left = x + 'px';
surface.canvas.style.top = y + 'px';
surface.canvas.style.zIndex = '9999999';
surface.canvas.style.display = 'none';
surface.canvas.setAttribute('data-surface-id', id.toString());
} else {
if ( globalOpts.onCreateSurface ) {
var canvas = globalOpts.onCreateSurface(id, surface);
if ( canvas ) {
canvas.surfaceId = id;
}
}
}
}
/**
* On Show Surface Event
*/
function onShowSurface(cmd) {
var id = cmd.get_16();
var surface = surfaces[id];
if ( surface ) {
surface.visible = true;
if ( surface.canvas ) {
surface.canvas.style.display = 'inline';
}
console.debug('Broadway', 'onShowSurface()', id);
if ( globalOpts.onShowSurface ) {
globalOpts.onShowSurface(id);
}
}
}
/**
* On Hide Surface Event
*/
function onHideSurface(cmd) {
var id = cmd.get_16();
var surface = surfaces[id];
if ( surface ) {
surface.visible = false;
if ( surface.canvas ) {
surface.canvas.style.display = 'none';
}
console.debug('Broadway', 'onHideSurface()', id);
if ( globalOpts.onHideSurface ) {
globalOpts.onHideSurface(id);
}
}
}
/**
* On Set Transient Event
*/
function onSetTransient(cmd) {
var id = cmd.get_16();
var parentId = cmd.get_16();
var surface = surfaces[id];
if ( surface ) {
console.debug('Broadway', 'onSetTransient()', id, parentId);
surface.transientParent = parentId;
if ( globalOpts.onSetTransient ) {
globalOpts.onSetTransient(id, parentId, surface);
}
}
}
/**
* On Delete Surface Event
*/
function onDeleteSurface(cmd) {
var id = cmd.get_16();
var surface = surfaces[id];
console.debug('Broadway', 'onDeleteSurface()', id);
if ( surface ) {
if ( surface.canvas ) {
if ( surface.canvas.parentNode ) {
surface.canvas.parentNode.removeChild(surface.canvas);
}
} else {
if ( globalOpts.onDeleteSurface ) {
globalOpts.onDeleteSurface(id);
}
}
delete surfaces[id];
}
}
/**
* On Move Surface Event
*/
function onMoveSurface(cmd) {
var id = cmd.get_16();
var ops = cmd.get_flags();
var surface = surfaces[id];
if ( !surface ) { return; }
var has_pos = ops & 1;
if (has_pos) {
surface.x = cmd.get_16s();
surface.y = cmd.get_16s();
}
var has_size = ops & 2;
if (has_size) {
surface.width = cmd.get_16();
surface.height = cmd.get_16();
}
console.debug('Broadway', 'onMoveSurface()', id, has_pos, has_size, surface);
if ( surface.isTemp ) {
if ( has_pos ) {
var par = surfaces[surface.transientParent] || {x: 0, y: 0};
console.log([surface.x, par.x], [surface.y, par.y]);
surface.canvas.style.left = (surface.x - par.x) + 'px';
surface.canvas.style.top = (surface.y - par.y) + 'px';
}
} else {
if ( globalOpts.onMoveSurface ) {
globalOpts.onMoveSurface(id, has_pos, has_size, surface);
}
}
sendConfigureNotify(surface);
}
/**
* On Image Data Event
*/
function onImageData(cmd) {
var q = {
op: 'i',
id: cmd.get_16(),
x: cmd.get_16(),
y: cmd.get_16()
};
var url = cmd.get_image_url();
q.img = new Image();
q.img.src = url;
//console.debug('Broadway', 'onImageData()', 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);
return true;
}
/**
* On Copy Rects Event
*/
function onCopyRects(cmd) {
var id = cmd.get_16();
if ( surfaces[id] ) {
console.debug('Broadway', 'onCopyRects()', id);
var q = {
op: 'b',
id: id,
rects: []
};
var nrects = cmd.get_16();
for (var r = 0; r < nrects; r++) {
q.rects.push({
x: cmd.get_16(),
y: cmd.get_16(),
w: cmd.get_16(),
h: cmd.get_16()
});
}
q.dx = cmd.get_16s();
q.dy = cmd.get_16s();
surfaces[q.id].drawQueue.push(q);
}
}
/**
* On Flush Sufrace Event
*/
function onFlushSurface(cmd) {
var id = cmd.get_16();
var surface = surfaces[id];
if ( surface ) {
//console.debug('Broadway', 'onFlushSurface()', id);
var canvas = surface.canvas;
if ( !surface.isTemp && globalOpts.onFlushSurface ) {
canvas = globalOpts.onFlushSurface(id);
}
if ( surface && canvas ) {
var commands = surface.drawQueue;
var context = canvas.getContext('2d');
context.globalCompositeOperation = 'source-over';
var i = 0;
var cmd, j, rect;
for (i; i < commands.length; i++) {
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();
for (j = 0; j < cmd.rects.length; j++) {
rect = cmd.rects[j];
context.rect(rect.x, rect.y, rect.w, rect.h);
}
context.clip();
context.drawImage(surface.canvas, cmd.dx, cmd.dy);
context.restore();
break;
default:
console.warn('Broadway', 'onFlushSurface()', 'Invalid command', cmd.op, cmd);
}
}
}
}
}
/**
* On Grab Pointer Event
*/
function onGrabPointer(cmd) {
var id = cmd.get_16();
var ownerEvents = cmd.get_bool();
// TODO
sendInput('g', []);
console.debug('Broadway', 'onGrabPointer()', id, ownerEvents);
}
/**
* On Ungrab Pointer Event
*/
function onUngrabPointer() {
sendInput('u', []);
// TODO
console.debug('Broadway', 'onUngrabPointer()');
}
/////////////////////////////////////////////////////////////////////////////
// ACTIONS
/////////////////////////////////////////////////////////////////////////////
/**
* Handle outstanding commands
*/
function handleOutstanding() {
while ( outstandingCommands.length > 0 ) {
var cmd = outstandingCommands.shift();
if ( !handleCommands(cmd) ) {
outstandingCommands.unshift(cmd);
return;
}
}
}
/**
* Handles a list of commands
*/
function handleCommands(cmd) {
var mapping = {
'l': doLogin,
'L': doLoggedIn,
'D': doDisconnect,
's': onCreateSurface,
'S': onShowSurface,
'H': onHideSurface,
'p': onSetTransient,
'd': onDeleteSurface,
'm': onMoveSurface,
'i': onImageData,
'b': onCopyRects,
'f': onFlushSurface,
'g': onGrabPointer,
'u': onUngrabPointer
};
while (cmd.pos < cmd.length) {
var command = cmd.get_char();
lastSerial = cmd.get_32();
//console.debug('Broadway', 'handleCommands()', command);
if ( mapping[command] ) {
if ( mapping[command](cmd) === false ) {
return false;
}
} else {
console.error('Invalid command', command);
}
}
return true;
}
/**
* Send configuration notification
*/
function sendConfigureNotify(surface) {
if ( !connected ) { return; }
sendInput('w', [surface.id, surface.x, surface.y, surface.width, surface.height]);
}
/**
* Send input
*/
function sendInput(cmd, args) {
if ( !connected ) { return; }
if ( inputSocket != null ) {
inputSocket.send(cmd + ([lastSerial, lastTimeStamp].concat(args)).join(','));
}
}
/**
* Update last input state
*/
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;
}
/**
* Get GDK button mask from DOM Event
*/
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;
}
/**
* Get GDK button
*/
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;
}
/**
* Cancels an event
*/
function cancelEvent(ev) {
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;
}
/**
* Copy the key event object
*/
function copyKeyEvent(ev) {
var members = ['type', 'keyCode', 'charCode', 'which', 'altKey', 'ctrlKey', 'shiftKey', 'keyLocation', 'keyIdentifier'];
var i, obj = {};
for (i = 0; i < members.length; i++) {
if (typeof ev[members[i]] !== 'undefined')
obj[members[i]] = ev[members[i]];
}
return obj;
}
/**
* Keyboard Down
*/
function handleKeyDown(id, ev) {
updateForEvent(ev);
if ( surfaces[id] ) {
var suppress = false;
var fev = copyKeyEvent(ev);
var keysym = getKeysymSpecial(ev);
fev.keysym = keysym;
console.debug('Broadway', 'handleKeyDown()', fev, keysym);
if ( keysym ) {
if ( !ignoreKeyEvent(ev) ) {
sendInput('k', [id, keysym, lastState]);
}
suppress = true;
}
if ( !ignoreKeyEvent(ev) ) {
keyDownList.push(fev);
}
if ( suppress ) {
return cancelEvent(ev);
}
}
return true;
}
/**
* Keyboard Up
*/
function handleKeyUp(id, ev) {
updateForEvent(ev);
if ( surfaces[id] ) {
var fev = getKeyEvent(ev.keyCode, true);
var keysym = fev ? fev.keysym : 0;
console.debug('Broadway', 'handleKeyUp()', fev, keysym);
if ( keysym > 0 ) {
sendInput('K', [id, keysym, lastState]);
}
}
return cancelEvent(ev);
}
/**
* Keyboard Press
*/
function handleKeyPress(id, ev) {
updateForEvent(ev);
if ( surfaces[id] ) {
if (((ev.which !== 'undefined') && (ev.which === 0)) || getKeysymSpecial(ev)) {
return cancelEvent(ev);
}
var keysym = getKeysym(ev);
var kdlen = keyDownList.length;
console.debug('Broadway', 'handleKeyPress()', keysym);
if ( kdlen > 0 ) {
keyDownList[kdlen-1].keysym = keysym;
}
if ( keysym > 0 ) {
sendInput('k', [id, keysym, lastState]);
}
}
return cancelEvent(ev);
}
/**
* Mouse Wheel
*/
function handleMouseWheel(id, ev, opts) {
updateForEvent(ev);
if ( surfaces[id] ) {
var offset = ev.detail ? ev.detail : -ev.wheelDelta;
var dir = offset > 0 ? 1 : 0;
var cid = getRelativeLayer(id, ev, opts);
console.debug('Broadway', 'handleMouseWheel()', dir);
sendInput('s', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, dir]);
}
return true;
}
/**
* Mouse Movment
*/
function handleMouseMove(id, ev, opts) {
updateForEvent(ev);
if ( surfaces[id] ) {
var cid = getRelativeLayer(id, ev, opts);
//console.debug('Broadway', 'handleMouseMove()', opts);
sendInput('m', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState]);
}
return true;
}
/**
* Mouse Down
*/
function handleMouseDown(id, ev, opts) {
updateForEvent(ev);
var button = ev.button + 1;
var cid = getRelativeLayer(id, ev, opts);
lastState = lastState | getButtonMask (button);
if ( surfaces[id] ) {
console.debug('Broadway', 'handleMouseDown()', opts);
sendInput('b', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, button]);
}
return true;
}
/**
* Mouse Up
*/
function handleMouseUp(id, ev, opts) {
updateForEvent(ev);
var button = ev.button + 1;
var cid = getRelativeLayer(id, ev, opts);
lastState = lastState & ~getButtonMask (button);
if ( surfaces[id] ) {
console.debug('Broadway', 'handleMouseUp()', opts);
sendInput('B', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, button]);
}
return true;
}
function handleMouseOver(id, ev, opts) {
updateForEvent(ev);
var cid = getRelativeLayer(id, ev, opts);
if ( surfaces[id] ) {
//console.debug('Broadway', 'handleMouseOver()', opts);
sendInput('e', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, GDK_CROSSING_NORMAL]);
}
return true;
}
function handleMouseOut(id, ev, opts) {
updateForEvent(ev);
var cid = getRelativeLayer(id, ev, opts);
if ( surfaces[id] ) {
//console.debug('Broadway', 'handleMouseOut()', opts);
sendInput('l', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, GDK_CROSSING_NORMAL]);
}
return true;
}
function handleResize(ev) {
if ( connected ) {
sendInput('d', [window.innerWidth, window.innerHeight]);
}
}
/////////////////////////////////////////////////////////////////////////////
// API
/////////////////////////////////////////////////////////////////////////////
/**
* On message() in socket
*/
function onSocketMessage(message) {
var cmd = new BinCommands(message);
outstandingCommands.push(cmd);
if ( outstandingCommands.length === 1 ) {
handleOutstanding();
}
}
/**
* Connects to Broadway server
*/
function connect(hostname, cb, cbclose) {
cb = cb || function() {};
cbclose = cbclose || function() {};
function onSocketOpen() {
connected = true;
handleResize();
if ( globalOpts.onSocketOpen ) {
globalOpts.onSocketOpen();
}
cb(false);
}
function onSocketClose() {
if ( globalOpts.onSocketClose ) {
globalOpts.onSocketClose();
}
}
console.info('Broadway', 'Connecting to', hostname);
connection = new WebSocket(hostname, 'broadway');
connection.binaryType = 'arraybuffer';
connection.onerror = function() {
if ( !connected ) {
cb('Connection timeout?');
}
};
connection.onopen = function() {
inputSocket = connection;
onSocketOpen();
};
connection.onclose = function() {
onSocketClose();
connection = null;
inputSocket = null;
cbclose();
};
connection.onmessage = function(ev) {
onSocketMessage(ev.data);
};
}
/////////////////////////////////////////////////////////////////////////////
// EXPORTS
/////////////////////////////////////////////////////////////////////////////
window.GTK = window.GTK || {};
window.GTK.disconnect = function() {
if ( connection && connected ) {
connection.close();
}
Object.keys(surfaces).forEach(function(i) {
if ( i && surfaces[i] ) {
if ( globalOpts.onDeleteSurface ) {
globalOpts.onDeleteSurface(i);
}
}
});
connected = false;
surfaces = {};
lastTimeStamp = 0;
lastState = null;
keyDownList = [];
lastSerial = 0;
outstandingCommands = [];
connection.onclose();
};
/**
* Connect to broadway
*
* Available callbacks in opts:
*
* onImageData REQUIRED Return your canvas object here
*
* onCreateSurface
* onShowSurface
* onHideSurface
* onSetTransient
* onDeleteSurface
* onMoveSurface
* onFlushSurface
*
* onSocketOpen
* onSocketClose
*
*/
window.GTK.connect = function(host, opts, cb, cbclose) {
if ( connection ) {
console.error('Broadway', 'Only one connection allowed!');
return;
}
globalOpts = opts || {};
connect(host, cb, cbclose);
};
/**
* Closes a surface
*/
window.GTK.close = function(id) {
closeSurface(id);
}
/**
* Moves a surface
*/
window.GTK.move = function(id, x, y) {
moveSurface(id, x, y);
};
/**
* Resizes a surface
*/
window.GTK.resize = function(id, w, h) {
resizeSurface(id, w, h);
};
/**
* Inject keyboard/mouse event
*/
window.GTK.inject = function(id, type, ev, opts) {
if ( !connection ) {
console.error('Broadway', 'No connections created!');
return false;
}
switch ( type ) {
case 'resize' :
return handleResize();
break;
case 'mousewheel' :
return handleMouseWheel(id, ev, opts);
break;
case 'mousemove' :
return handleMouseMove(id, ev, opts);
break;
case 'mousedown' :
return handleMouseDown(id, ev, opts);
break;
case 'mouseup' :
return handleMouseUp(id, ev, opts);
break;
case 'keypress' :
return handleKeyPress(id, ev, opts);
break;
case 'keyup' :
return handleKeyUp(id, ev, opts);
break;
case 'keydown' :
return handleKeyDown(id, ev, opts);
break;
case 'mouseout' :
return handleMouseOver(id, ev, opts);
break;
case 'mouseover' :
return handleMouseOver(id, ev, opts);
break;
default:
console.warn('Broadway', 'inject()', 'invalid type', type);
break;
}
return true;
};
})();