├── LICENSE ├── README.md ├── doc └── jsmodem.txt ├── emulator ├── Makefile ├── cpux86-ta.js ├── cpux86.js ├── include │ ├── base64.js │ ├── util.js │ ├── web-socket-js │ │ ├── README.txt │ │ ├── WebSocketMain.swf │ │ ├── swfobject.js │ │ └── web_socket.js │ ├── websock.js │ └── webutil.js ├── index.html ├── jslinux.js ├── jsmodem.js ├── linuxstart.bin ├── masq.sh ├── patch_jslinux.js ├── root.bin ├── serve.sh ├── stop.sh ├── term.js ├── utils.js ├── vmlinux-2.6.20.bin ├── websocket.py └── websockify └── linuxstart ├── Makefile ├── README ├── config_linux-2.6.20 ├── config_linux-2.6.36 ├── libc.c ├── libc.h ├── linuxstart.c ├── linuxstart.h ├── linuxstart_head.S └── patch_linux-2.6.20 /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Yauhen Yakimovich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## JSModem 2 | 3 | Provides a virtual modem device for JSLinux platform. It is a result of 4 | studying project conducted on PC emulator that was implemented in JS by Fabrice 5 | Bellard (see http://bellard.org/jslinux). There is a screencast discussing this 6 | project http://www.youtube.com/watch?v=MEsmgHrKQYM. 7 | 8 | ### Licensing 9 | 10 | * The software behind jslinux emulator is the intellectual property of Fabrice 11 | Bellard (see jslinux.org for further details). 12 | * The rest of the patch is licensed under MIT license (see copy of it in 13 | LICENSE). 14 | 15 | ### Installation 16 | 17 | * Get a copy by cloning the repo from `git@github.com:ewiger/jsmodem.git` 18 | 19 | * Run make all in jsmodem/emulator to download and patch JSLinux from 20 | http://bellard.org/jslinux (This step is still valid but left as a fallback. 21 | Right now the jslinux version is in sync with the version on the website 22 | of Aug 20, 2011). 23 | 24 | * Start serving JSLinux locally with `sudo ./serve.sh` 25 | 26 | * Navigate to `http://localhost:2080/` This should boot the guest system. 27 | 28 | * Login as root and run ./ppp_up to bring up the ppp interface. Check it with 29 | ifconfig and ping. 30 | 31 | ### Requirements 32 | 33 | JSModem script is heavily based on JSLinux project, hence browser must be 34 | modern enough to support it. Another project *websockify* provides websockets 35 | abstraction with fallback to flash. 36 | 37 | To serve on host system one will need 38 | 39 | * python 2.6+ 40 | 41 | * bash 42 | 43 | * sed 44 | 45 | * gawk 46 | 47 | * grep 48 | 49 | * pppd 50 | 51 | * socat 52 | 53 | * iptables 54 | 55 | ### Tested 56 | 57 | Internet connection was tested on 58 | 59 | * *host server*: linux 2.6.35 - ubuntu 10.10 Maverick, 60 | linux 2.6.38-11 - 11.04 Natty Narwhal 61 | *host client:* Chrome 12.0.742.112 (linux), FF 3.6.8 (linux), 62 | Chrome 14.0.835.202 (linux) 63 | 64 | 65 | ### Feedback 66 | 67 | For now, best way to give some feedback is to email eugeny dot yakimovitch at 68 | gmail dot com with subject jsmodem. Test reports are greatly appreciated. 69 | 70 | wbr, 71 | yy 72 | -------------------------------------------------------------------------------- /doc/jsmodem.txt: -------------------------------------------------------------------------------- 1 | sudo socat -d -d PTY,link=/dev/vppp,raw,echo=0 TCP-LISTEN:2001 2 | sudo pppd /dev/vppp debug nodetach noauth passive 10.0.5.1:10.0.5.2 3 | 4 | pppd /dev/ttyS1 noauth defaultroute usepeerdns 5 | 6 | >> port read LSR: 96 -> 1100000 (THR is empty, and line is idle) 7 | >> port read RBR: 0 -> no more bytes are present. 8 | >> port read IIR: 1 -> 1 (No interrupt pending) 9 | >> port read MSR: 0 -> reset LSB 10 | >> port read LSR: 96 -> 1100000 (THR is empty, and line is idle) 11 | >> port read IIR: 1 -> 1 (No interrupt pending) 12 | >> port write LCR: 3 -> 11 (Data word length 1 1 8 bits) 13 | >> port write MCR: 8 -> 1000 (Auxiliary output 2) 14 | >> port write IER: 2 -> 10 (Transmitter holding register empty) 15 | >> port read LSR: 96 -> 1100000 (THR is empty, and line is idle) 16 | >> port read IIR: 2 -> 10 (Transmitter holding register empty) 17 | >> port write IER: 0 -> disable interrupts 18 | >> port read IIR: 1 -> 1 (No interrupt pending) 19 | >> port write IER: 5 -> enable interrupts (Received data available, Receiver line status register change) 20 | >> port read LSR: 96 -> 1100000 (THR is empty, and line is idle) 21 | >> port read RBR: 0 -> no more bytes are present. 22 | >> port read IIR: 1 -> 1 (No interrupt pending) 23 | >> port read MSR: 0 -> reset LSB 24 | >> port write IER: 5 -> enable interrupts (Received data available, Receiver line status register change) 25 | >> port write LCR: 147 -> 10010011 (set word length to 8 bits, No parity, Break signal disabled, DLAB : DLL and DLM accessible) 26 | >> port write THR: 12 -> 00001100 (transmit) 27 | >> port write IER: 0 -> disable interrupts 28 | >> port write LCR: 19 -> 00010011 (set word length to 8 bits, No parity, Break signal disabled, DLAB : RBR, THR and IER accessible) 29 | >> port write FCR: 0 -> 0 (Disable transmit FIFO) 30 | >> port write MCR: 8 -> 1000 (Auxiliary output 2) 31 | >> port write MCR: 11 -> 1011 (Data terminal ready, Request to send, Auxiliary output 2) 32 | >> port read LSR: 96 -> 1100000 (THR is empty, and line is idle) 33 | >> port write IER: 13 -> 1101 enable interrupts (Received data available, Receiver line status register change, Modem status register change) 34 | >> port write LCR: 147 -> 10010011 (set word length to 8 bits, No parity, Break signal disabled, DLAB : DLL and DLM accessible) 35 | >> port write THR: 12 -> 00001100 (transmit) 36 | >> port write IER: 0 -> disable interrupts 37 | >> port write LCR: 19 -> 00010011 (set word length to 8 bits, No parity, Break signal disabled, DLAB : RBR, THR and IER accessible) 38 | >> port write FCR: 0 -> 0 (Disable transmit FIFO) 39 | >> port write MCR: 11 -> 1011 (Data terminal ready, Request to send, Auxiliary output 2) 40 | >> port write IER: 13 -> 1101 enable interrupts (Received data available, Receiver line status register change, Modem status register change) 41 | >> port write MSR: 0 -> is RO ? 42 | 43 | -------------------------------------------------------------------------------- /emulator/Makefile: -------------------------------------------------------------------------------- 1 | WGET=/usr/bin/wget 2 | JSLINUX_URL=http://bellard.org/jslinux/ 3 | # Patch jslinux.js? 4 | ifeq ($(shell if [ -e jslinux.js ] ; then echo 1 ; else echo 0 ; fi), 0) 5 | PATCH_JSLINUX=1 6 | else 7 | ifeq ($(shell if test "jslinux.js" -ot "jsmodem.js" ; then echo 1 ; else echo 0 ; fi), 1) 8 | PATCH_JSLINUX=1 9 | endif 10 | endif 11 | 12 | 13 | all: jslinux 14 | 15 | jslinux: cpux86.js cpux86-ta.js jslinux.js term.js utils.js 16 | ifeq (${PATCH_JSLINUX},1) 17 | patch -p1 < ./patch_jslinux.js 18 | endif 19 | 20 | cpux86.js: 21 | ${WGET} ${JSLINUX_URL}cpux86.js 22 | 23 | cpux86-ta.js: 24 | ${WGET} ${JSLINUX_URL}cpux86-ta.js 25 | 26 | jslinux.js: 27 | ${WGET} ${JSLINUX_URL}jslinux.js 28 | 29 | term.js: 30 | ${WGET} ${JSLINUX_URL}term.js 31 | 32 | utils.js: 33 | ${WGET} ${JSLINUX_URL}utils.js 34 | 35 | 36 | clean: 37 | rm -f cpux86.js cpux86-ta.js jslinux.js term.js utils.js 38 | 39 | -------------------------------------------------------------------------------- /emulator/cpux86.js: -------------------------------------------------------------------------------- 1 | /* 2 | PC Emulator wrapper 3 | 4 | Copyright (c) 2012 Fabrice Bellard 5 | 6 | Redistribution or commercial use is prohibited without the author's 7 | permission. 8 | */ 9 | "use strict"; 10 | 11 | function test_typed_arrays() 12 | { 13 | return (window.Uint8Array && 14 | window.Uint16Array && 15 | window.Int32Array && 16 | window.ArrayBuffer); 17 | } 18 | 19 | if (test_typed_arrays()) { 20 | include("cpux86-ta.js"); 21 | } else { 22 | include("cpux86-std.js"); 23 | document.write(''); 24 | } 25 | -------------------------------------------------------------------------------- /emulator/include/base64.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Modified from: 3 | * http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956 4 | */ 5 | 6 | /* ***** BEGIN LICENSE BLOCK ***** 7 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 8 | * 9 | * The contents of this file are subject to the Mozilla Public License Version 10 | * 1.1 (the "License"); you may not use this file except in compliance with 11 | * the License. You may obtain a copy of the License at 12 | * http://www.mozilla.org/MPL/ 13 | * 14 | * Software distributed under the License is distributed on an "AS IS" basis, 15 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 16 | * for the specific language governing rights and limitations under the 17 | * License. 18 | * 19 | * The Original Code is Mozilla XML-RPC Client component. 20 | * 21 | * The Initial Developer of the Original Code is 22 | * Digital Creations 2, Inc. 23 | * Portions created by the Initial Developer are Copyright (C) 2000 24 | * the Initial Developer. All Rights Reserved. 25 | * 26 | * Contributor(s): 27 | * Martijn Pieters (original author) 28 | * Samuel Sieb 29 | * 30 | * Alternatively, the contents of this file may be used under the terms of 31 | * either the GNU General Public License Version 2 or later (the "GPL"), or 32 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 33 | * in which case the provisions of the GPL or the LGPL are applicable instead 34 | * of those above. If you wish to allow use of your version of this file only 35 | * under the terms of either the GPL or the LGPL, and not to allow others to 36 | * use your version of this file under the terms of the MPL, indicate your 37 | * decision by deleting the provisions above and replace them with the notice 38 | * and other provisions required by the GPL or the LGPL. If you do not delete 39 | * the provisions above, a recipient may use your version of this file under 40 | * the terms of any one of the MPL, the GPL or the LGPL. 41 | * 42 | * ***** END LICENSE BLOCK ***** */ 43 | 44 | "use strict"; 45 | /*jslint white: false, bitwise: false, plusplus: false */ 46 | /*global console */ 47 | 48 | var Base64 = { 49 | 50 | /* Convert data (an array of integers) to a Base64 string. */ 51 | toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', 52 | base64Pad : '=', 53 | 54 | encode: function (data) { 55 | var result = '', 56 | chrTable = Base64.toBase64Table.split(''), 57 | pad = Base64.base64Pad, 58 | length = data.length, 59 | i; 60 | // Convert every three bytes to 4 ascii characters. 61 | for (i = 0; i < (length - 2); i += 3) { 62 | result += chrTable[data[i] >> 2]; 63 | result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; 64 | result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; 65 | result += chrTable[data[i+2] & 0x3f]; 66 | } 67 | 68 | // Convert the remaining 1 or 2 bytes, pad out to 4 characters. 69 | if (length%3) { 70 | i = length - (length%3); 71 | result += chrTable[data[i] >> 2]; 72 | if ((length%3) === 2) { 73 | result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; 74 | result += chrTable[(data[i+1] & 0x0f) << 2]; 75 | result += pad; 76 | } else { 77 | result += chrTable[(data[i] & 0x03) << 4]; 78 | result += pad + pad; 79 | } 80 | } 81 | 82 | return result; 83 | }, 84 | 85 | /* Convert Base64 data to a string */ 86 | toBinaryTable : [ 87 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 88 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 89 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 90 | 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, 91 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 92 | 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, 93 | -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 94 | 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 95 | ], 96 | 97 | decode: function (data, offset) { 98 | offset = typeof(offset) !== 'undefined' ? offset : 0; 99 | var binTable = Base64.toBinaryTable, 100 | pad = Base64.base64Pad, 101 | result, result_length, idx, i, c, padding, 102 | leftbits = 0, // number of bits decoded, but yet to be appended 103 | leftdata = 0, // bits decoded, but yet to be appended 104 | data_length = data.indexOf('=') - offset; 105 | 106 | if (data_length < 0) { data_length = data.length - offset; } 107 | 108 | /* Every four characters is 3 resulting numbers */ 109 | result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5); 110 | result = new Array(result_length); 111 | 112 | // Convert one by one. 113 | for (idx = 0, i = offset; i < data.length; i++) { 114 | c = binTable[data.charCodeAt(i) & 0x7f]; 115 | padding = (data.charAt(i) === pad); 116 | // Skip illegal characters and whitespace 117 | if (c === -1) { 118 | console.error("Illegal character '" + data.charCodeAt(i) + "'"); 119 | continue; 120 | } 121 | 122 | // Collect data into leftdata, update bitcount 123 | leftdata = (leftdata << 6) | c; 124 | leftbits += 6; 125 | 126 | // If we have 8 or more bits, append 8 bits to the result 127 | if (leftbits >= 8) { 128 | leftbits -= 8; 129 | // Append if not padding. 130 | if (!padding) { 131 | result[idx++] = (leftdata >> leftbits) & 0xff; 132 | } 133 | leftdata &= (1 << leftbits) - 1; 134 | } 135 | } 136 | 137 | // If there are any bits left, the base64 string was corrupted 138 | if (leftbits) { 139 | throw {name: 'Base64-Error', 140 | message: 'Corrupted base64 string'}; 141 | } 142 | 143 | return result; 144 | } 145 | 146 | }; /* End of Base64 namespace */ 147 | -------------------------------------------------------------------------------- /emulator/include/util.js: -------------------------------------------------------------------------------- 1 | /* 2 | * from noVNC: HTML5 VNC client 3 | * Copyright (C) 2010 Joel Martin 4 | * Licensed under LGPL-3 (see LICENSE.txt) 5 | * 6 | * See README.md for usage and integration instructions. 7 | */ 8 | 9 | "use strict"; 10 | /*jslint bitwise: false, white: false */ 11 | /*global window, console, document, navigator, ActiveXObject */ 12 | 13 | // Globals defined here 14 | var Util = {}, $D; 15 | 16 | 17 | /* 18 | * Make arrays quack 19 | */ 20 | 21 | Array.prototype.push8 = function (num) { 22 | this.push(num & 0xFF); 23 | }; 24 | 25 | Array.prototype.push16 = function (num) { 26 | this.push((num >> 8) & 0xFF, 27 | (num ) & 0xFF ); 28 | }; 29 | Array.prototype.push32 = function (num) { 30 | this.push((num >> 24) & 0xFF, 31 | (num >> 16) & 0xFF, 32 | (num >> 8) & 0xFF, 33 | (num ) & 0xFF ); 34 | }; 35 | 36 | /* 37 | * ------------------------------------------------------ 38 | * Namespaced in Util 39 | * ------------------------------------------------------ 40 | */ 41 | 42 | /* 43 | * Logging/debug routines 44 | */ 45 | 46 | Util._log_level = 'warn'; 47 | Util.init_logging = function (level) { 48 | if (typeof level === 'undefined') { 49 | level = Util._log_level; 50 | } else { 51 | Util._log_level = level; 52 | } 53 | if (typeof window.console === "undefined") { 54 | if (typeof window.opera !== "undefined") { 55 | window.console = { 56 | 'log' : window.opera.postError, 57 | 'warn' : window.opera.postError, 58 | 'error': window.opera.postError }; 59 | } else { 60 | window.console = { 61 | 'log' : function(m) {}, 62 | 'warn' : function(m) {}, 63 | 'error': function(m) {}}; 64 | } 65 | } 66 | 67 | Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {}; 68 | switch (level) { 69 | case 'debug': Util.Debug = function (msg) { console.log(msg); }; 70 | case 'info': Util.Info = function (msg) { console.log(msg); }; 71 | case 'warn': Util.Warn = function (msg) { console.warn(msg); }; 72 | case 'error': Util.Error = function (msg) { console.error(msg); }; 73 | case 'none': 74 | break; 75 | default: 76 | throw("invalid logging type '" + level + "'"); 77 | } 78 | }; 79 | Util.get_logging = function () { 80 | return Util._log_level; 81 | } 82 | // Initialize logging level 83 | Util.init_logging(); 84 | 85 | 86 | // Set defaults for Crockford style function namespaces 87 | Util.conf_default = function(cfg, api, v, type, defval, desc) { 88 | // Description 89 | api['get_' + v + '_desc'] = desc; 90 | // Default getter 91 | if (typeof api['get_' + v] === 'undefined') { 92 | api['get_' + v] = function () { 93 | return cfg[v]; 94 | }; 95 | } 96 | // Default setter 97 | if (typeof api['set_' + v] === 'undefined') { 98 | api['set_' + v] = function (val) { 99 | if (type in {'boolean':1, 'bool':1}) { 100 | if ((!val) || (val in {'0':1, 'no':1, 'false':1})) { 101 | val = false; 102 | } else { 103 | val = true; 104 | } 105 | } else if (type in {'integer':1, 'int':1}) { 106 | val = parseInt(val, 10); 107 | } 108 | cfg[v] = val; 109 | }; 110 | } 111 | 112 | if (typeof cfg[v] === 'undefined') { 113 | // Set to default 114 | api['set_' + v](defval); 115 | } else { 116 | // Coerce existing setting to the right type 117 | api['set_' + v](cfg[v]); 118 | } 119 | }; 120 | 121 | 122 | 123 | /* 124 | * Cross-browser routines 125 | */ 126 | 127 | // Get DOM element position on page 128 | Util.getPosition = function (obj) { 129 | var x = 0, y = 0; 130 | if (obj.offsetParent) { 131 | do { 132 | x += obj.offsetLeft; 133 | y += obj.offsetTop; 134 | obj = obj.offsetParent; 135 | } while (obj); 136 | } 137 | return {'x': x, 'y': y}; 138 | }; 139 | 140 | // Get mouse event position in DOM element 141 | Util.getEventPosition = function (e, obj, scale) { 142 | var evt, docX, docY, pos; 143 | //if (!e) evt = window.event; 144 | evt = (e ? e : window.event); 145 | if (evt.pageX || evt.pageY) { 146 | docX = evt.pageX; 147 | docY = evt.pageY; 148 | } else if (evt.clientX || evt.clientY) { 149 | docX = evt.clientX + document.body.scrollLeft + 150 | document.documentElement.scrollLeft; 151 | docY = evt.clientY + document.body.scrollTop + 152 | document.documentElement.scrollTop; 153 | } 154 | pos = Util.getPosition(obj); 155 | if (typeof scale === "undefined") { 156 | scale = 1; 157 | } 158 | return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale}; 159 | }; 160 | 161 | 162 | // Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events 163 | Util.addEvent = function (obj, evType, fn){ 164 | if (obj.attachEvent){ 165 | var r = obj.attachEvent("on"+evType, fn); 166 | return r; 167 | } else if (obj.addEventListener){ 168 | obj.addEventListener(evType, fn, false); 169 | return true; 170 | } else { 171 | throw("Handler could not be attached"); 172 | } 173 | }; 174 | 175 | Util.removeEvent = function(obj, evType, fn){ 176 | if (obj.detachEvent){ 177 | var r = obj.detachEvent("on"+evType, fn); 178 | return r; 179 | } else if (obj.removeEventListener){ 180 | obj.removeEventListener(evType, fn, false); 181 | return true; 182 | } else { 183 | throw("Handler could not be removed"); 184 | } 185 | }; 186 | 187 | Util.stopEvent = function(e) { 188 | if (e.stopPropagation) { e.stopPropagation(); } 189 | else { e.cancelBubble = true; } 190 | 191 | if (e.preventDefault) { e.preventDefault(); } 192 | else { e.returnValue = false; } 193 | }; 194 | 195 | 196 | // Set browser engine versions. Based on mootools. 197 | Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}; 198 | 199 | Util.Engine = { 200 | 'presto': (function() { 201 | return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()), 202 | 'trident': (function() { 203 | return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()), 204 | 'webkit': (function() { 205 | try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()), 206 | //'webkit': (function() { 207 | // return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()), 208 | 'gecko': (function() { 209 | return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }()) 210 | }; 211 | if (Util.Engine.webkit) { 212 | // Extract actual webkit version if available 213 | Util.Engine.webkit = (function(v) { 214 | var re = new RegExp('WebKit/([0-9\.]*) '); 215 | v = (navigator.userAgent.match(re) || ['', v])[1]; 216 | return parseFloat(v, 10); 217 | })(Util.Engine.webkit); 218 | } 219 | 220 | Util.Flash = (function(){ 221 | var v, version; 222 | try { 223 | v = navigator.plugins['Shockwave Flash'].description; 224 | } catch(err1) { 225 | try { 226 | v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); 227 | } catch(err2) { 228 | v = '0 r0'; 229 | } 230 | } 231 | version = v.match(/\d+/g); 232 | return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; 233 | }()); 234 | -------------------------------------------------------------------------------- /emulator/include/web-socket-js/README.txt: -------------------------------------------------------------------------------- 1 | * How to try 2 | 3 | Assuming you have Web server (e.g. Apache) running at http://example.com/ . 4 | 5 | - Download web_socket.rb from: 6 | http://github.com/gimite/web-socket-ruby/tree/master 7 | - Run sample Web Socket server (echo server) in example.com with: (#1) 8 | $ ruby web-socket-ruby/samples/echo_server.rb example.com 10081 9 | - If your server already provides socket policy file at port 843, modify the file to allow access to port 10081. Otherwise you can skip this step. See below for details. 10 | - Publish the web-socket-js directory with your Web server (e.g. put it in ~/public_html). 11 | - Change ws://localhost:10081 to ws://example.com:10081 in sample.html. 12 | - Open sample.html in your browser. 13 | - After "onopen" is shown, input something, click [Send] and confirm echo back. 14 | 15 | #1: First argument of echo_server.rb means that it accepts Web Socket connection from HTML pages in example.com. 16 | 17 | 18 | * Troubleshooting 19 | 20 | If it doesn't work, try these: 21 | 22 | 1. Try Chrome and Firefox 3.x. 23 | - It doesn't work on Chrome: 24 | -- It's likely an issue of your code or the server. Debug your code as usual e.g. using console.log. 25 | - It works on Chrome but it doesn't work on Firefox: 26 | -- It's likely an issue of web-socket-js specific configuration (e.g. 3 and 4 below). 27 | - It works on both Chrome and Firefox, but it doesn't work on your browser: 28 | -- Check "Supported environment" section below. Your browser may not be supported by web-socket-js. 29 | 30 | 2. Add this line before your code: 31 | WEB_SOCKET_DEBUG = true; 32 | and use Developer Tools (Chrome/Safari) or Firebug (Firefox) to see if console.log outputs any errors. 33 | 34 | 3. Make sure you do NOT open your HTML page as local file e.g. file:///.../sample.html. web-socket-js doesn't work on local file. Open it via Web server e.g. http:///.../sample.html. 35 | 36 | 4. If you are NOT using web-socket-ruby as your WebSocket server, you need to place Flash socket policy file on your server. See "Flash socket policy file" section below for details. 37 | 38 | 5. Check if sample.html bundled with web-socket-js works. 39 | 40 | 6. Make sure the port used for WebSocket (10081 in example above) is not blocked by your server/client's firewall. 41 | 42 | 7. Install debugger version of Flash Player available here to see Flash errors: 43 | http://www.adobe.com/support/flashplayer/downloads.html 44 | 45 | 46 | * Supported environments 47 | 48 | It should work on: 49 | - Google Chrome 4 or later (just uses native implementation) 50 | - Firefox 3.x, Internet Explorer 8 + Flash Player 9 or later 51 | 52 | It may or may not work on other browsers such as Safari, Opera or IE 6. Patch for these browsers are appreciated, but I will not work on fixing issues specific to these browsers by myself. 53 | 54 | 55 | * Flash socket policy file 56 | 57 | This implementation uses Flash's socket, which means that your server must provide Flash socket policy file to declare the server accepts connections from Flash. 58 | 59 | If you use web-socket-ruby available at 60 | http://github.com/gimite/web-socket-ruby/tree/master 61 | , you don't need anything special, because web-socket-ruby handles Flash socket policy file request. But if you already provide socket policy file at port 843, you need to modify the file to allow access to Web Socket port, because it precedes what web-socket-ruby provides. 62 | 63 | If you use other Web Socket server implementation, you need to provide socket policy file yourself. See 64 | http://www.lightsphere.com/dev/articles/flash_socket_policy.html 65 | for details and sample script to run socket policy file server. node.js implementation is available here: 66 | http://github.com/LearnBoost/Socket.IO-node/blob/master/lib/socket.io/transports/flashsocket.js 67 | 68 | Actually, it's still better to provide socket policy file at port 843 even if you use web-socket-ruby. Flash always try to connect to port 843 first, so providing the file at port 843 makes startup faster. 69 | 70 | 71 | * Cookie considerations 72 | 73 | Cookie is sent if Web Socket host is the same as the origin of JavaScript. Otherwise it is not sent, because I don't know way to send right Cookie (which is Cookie of the host of Web Socket, I heard). 74 | 75 | Note that it's technically possible that client sends arbitrary string as Cookie and any other headers (by modifying this library for example) once you place Flash socket policy file in your server. So don't trust Cookie and other headers if you allow connection from untrusted origin. 76 | 77 | 78 | * Proxy considerations 79 | 80 | The WebSocket spec (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol) specifies instructions for User Agents to support proxied connections by implementing the HTTP CONNECT method. 81 | 82 | The AS3 Socket class doesn't implement this mechanism, which renders it useless for the scenarios where the user trying to open a socket is behind a proxy. 83 | 84 | The class RFC2817Socket (by Christian Cantrell) effectively lets us implement this, as long as the proxy settings are known and provided by the interface that instantiates the WebSocket. As such, if you want to support proxied conncetions, you'll have to supply this information to the WebSocket constructor when Flash is being used. One way to go about it would be to ask the user for proxy settings information if the initial connection fails. 85 | 86 | 87 | * How to host HTML file and SWF file in different domains 88 | 89 | By default, HTML file and SWF file must be in the same domain. You can follow steps below to allow hosting them in different domain. 90 | 91 | WARNING: If you use the method below, HTML files in ANY domains can send arbitrary TCP data to your WebSocket server, regardless of configuration in Flash socket policy file. Arbitrary TCP data means that they can even fake request headers including Origin and Cookie. 92 | 93 | - Unzip WebSocketMainInsecure.zip to extract WebSocketMainInsecure.swf. 94 | - Put WebSocketMainInsecure.swf on your server, instead of WebSocketMain.swf. 95 | - In JavaScript, set WEB_SOCKET_SWF_LOCATION to URL of your WebSocketMainInsecure.swf. 96 | 97 | 98 | * How to build WebSocketMain.swf 99 | 100 | Install Flex 4 SDK: 101 | http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4 102 | 103 | $ cd flash-src 104 | $ ./build.sh 105 | 106 | 107 | * License 108 | 109 | New BSD License. 110 | -------------------------------------------------------------------------------- /emulator/include/web-socket-js/WebSocketMain.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewiger/jsmodem/98915f0f51cd6cab32690cba6e5bcefd93f6d29c/emulator/include/web-socket-js/WebSocketMain.swf -------------------------------------------------------------------------------- /emulator/include/web-socket-js/swfobject.js: -------------------------------------------------------------------------------- 1 | /* SWFObject v2.2 2 | is released under the MIT License 3 | */ 4 | var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab 2 | // License: New BSD License 3 | // Reference: http://dev.w3.org/html5/websockets/ 4 | // Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol 5 | 6 | (function() { 7 | 8 | if (window.WebSocket) return; 9 | 10 | var console = window.console; 11 | if (!console || !console.log || !console.error) { 12 | console = {log: function(){ }, error: function(){ }}; 13 | } 14 | 15 | if (!swfobject.hasFlashPlayerVersion("10.0.0")) { 16 | console.error("Flash Player >= 10.0.0 is required."); 17 | return; 18 | } 19 | if (location.protocol == "file:") { 20 | console.error( 21 | "WARNING: web-socket-js doesn't work in file:///... URL " + 22 | "unless you set Flash Security Settings properly. " + 23 | "Open the page via Web server i.e. http://..."); 24 | } 25 | 26 | /** 27 | * This class represents a faux web socket. 28 | * @param {string} url 29 | * @param {string} protocol 30 | * @param {string} proxyHost 31 | * @param {int} proxyPort 32 | * @param {string} headers 33 | */ 34 | WebSocket = function(url, protocol, proxyHost, proxyPort, headers) { 35 | var self = this; 36 | self.__id = WebSocket.__nextId++; 37 | WebSocket.__instances[self.__id] = self; 38 | self.readyState = WebSocket.CONNECTING; 39 | self.bufferedAmount = 0; 40 | self.__events = {}; 41 | // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. 42 | // Otherwise, when onopen fires immediately, onopen is called before it is set. 43 | setTimeout(function() { 44 | WebSocket.__addTask(function() { 45 | WebSocket.__flash.create( 46 | self.__id, url, protocol, proxyHost || null, proxyPort || 0, headers || null); 47 | }); 48 | }, 0); 49 | }; 50 | 51 | /** 52 | * Send data to the web socket. 53 | * @param {string} data The data to send to the socket. 54 | * @return {boolean} True for success, false for failure. 55 | */ 56 | WebSocket.prototype.send = function(data) { 57 | if (this.readyState == WebSocket.CONNECTING) { 58 | throw "INVALID_STATE_ERR: Web Socket connection has not been established"; 59 | } 60 | // We use encodeURIComponent() here, because FABridge doesn't work if 61 | // the argument includes some characters. We don't use escape() here 62 | // because of this: 63 | // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions 64 | // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't 65 | // preserve all Unicode characters either e.g. "\uffff" in Firefox. 66 | // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require 67 | // additional testing. 68 | var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data)); 69 | if (result < 0) { // success 70 | return true; 71 | } else { 72 | this.bufferedAmount += result; 73 | return false; 74 | } 75 | }; 76 | 77 | /** 78 | * Close this web socket gracefully. 79 | */ 80 | WebSocket.prototype.close = function() { 81 | if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { 82 | return; 83 | } 84 | this.readyState = WebSocket.CLOSING; 85 | WebSocket.__flash.close(this.__id); 86 | }; 87 | 88 | /** 89 | * Implementation of {@link DOM 2 EventTarget Interface} 90 | * 91 | * @param {string} type 92 | * @param {function} listener 93 | * @param {boolean} useCapture 94 | * @return void 95 | */ 96 | WebSocket.prototype.addEventListener = function(type, listener, useCapture) { 97 | if (!(type in this.__events)) { 98 | this.__events[type] = []; 99 | } 100 | this.__events[type].push(listener); 101 | }; 102 | 103 | /** 104 | * Implementation of {@link DOM 2 EventTarget Interface} 105 | * 106 | * @param {string} type 107 | * @param {function} listener 108 | * @param {boolean} useCapture 109 | * @return void 110 | */ 111 | WebSocket.prototype.removeEventListener = function(type, listener, useCapture) { 112 | if (!(type in this.__events)) return; 113 | var events = this.__events[type]; 114 | for (var i = events.length - 1; i >= 0; --i) { 115 | if (events[i] === listener) { 116 | events.splice(i, 1); 117 | break; 118 | } 119 | } 120 | }; 121 | 122 | /** 123 | * Implementation of {@link DOM 2 EventTarget Interface} 124 | * 125 | * @param {Event} event 126 | * @return void 127 | */ 128 | WebSocket.prototype.dispatchEvent = function(event) { 129 | var events = this.__events[event.type] || []; 130 | for (var i = 0; i < events.length; ++i) { 131 | events[i](event); 132 | } 133 | var handler = this["on" + event.type]; 134 | if (handler) handler(event); 135 | }; 136 | 137 | /** 138 | * Handles an event from Flash. 139 | * @param {Object} flashEvent 140 | */ 141 | WebSocket.prototype.__handleEvent = function(flashEvent) { 142 | if ("readyState" in flashEvent) { 143 | this.readyState = flashEvent.readyState; 144 | } 145 | 146 | var jsEvent; 147 | if (flashEvent.type == "open" || flashEvent.type == "error") { 148 | jsEvent = this.__createSimpleEvent(flashEvent.type); 149 | } else if (flashEvent.type == "close") { 150 | // TODO implement jsEvent.wasClean 151 | jsEvent = this.__createSimpleEvent("close"); 152 | } else if (flashEvent.type == "message") { 153 | var data = decodeURIComponent(flashEvent.message); 154 | jsEvent = this.__createMessageEvent("message", data); 155 | } else { 156 | throw "unknown event type: " + flashEvent.type; 157 | } 158 | 159 | this.dispatchEvent(jsEvent); 160 | }; 161 | 162 | WebSocket.prototype.__createSimpleEvent = function(type) { 163 | if (document.createEvent && window.Event) { 164 | var event = document.createEvent("Event"); 165 | event.initEvent(type, false, false); 166 | return event; 167 | } else { 168 | return {type: type, bubbles: false, cancelable: false}; 169 | } 170 | }; 171 | 172 | WebSocket.prototype.__createMessageEvent = function(type, data) { 173 | if (document.createEvent && window.MessageEvent && !window.opera) { 174 | var event = document.createEvent("MessageEvent"); 175 | event.initMessageEvent("message", false, false, data, null, null, window, null); 176 | return event; 177 | } else { 178 | // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes. 179 | return {type: type, data: data, bubbles: false, cancelable: false}; 180 | } 181 | }; 182 | 183 | /** 184 | * Define the WebSocket readyState enumeration. 185 | */ 186 | WebSocket.CONNECTING = 0; 187 | WebSocket.OPEN = 1; 188 | WebSocket.CLOSING = 2; 189 | WebSocket.CLOSED = 3; 190 | 191 | WebSocket.__flash = null; 192 | WebSocket.__instances = {}; 193 | WebSocket.__tasks = []; 194 | WebSocket.__nextId = 0; 195 | 196 | /** 197 | * Load a new flash security policy file. 198 | * @param {string} url 199 | */ 200 | WebSocket.loadFlashPolicyFile = function(url){ 201 | WebSocket.__addTask(function() { 202 | WebSocket.__flash.loadManualPolicyFile(url); 203 | }); 204 | }; 205 | 206 | /** 207 | * Loads WebSocketMain.swf and creates WebSocketMain object in Flash. 208 | */ 209 | WebSocket.__initialize = function() { 210 | if (WebSocket.__flash) return; 211 | 212 | if (WebSocket.__swfLocation) { 213 | // For backword compatibility. 214 | window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; 215 | } 216 | if (!window.WEB_SOCKET_SWF_LOCATION) { 217 | console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); 218 | return; 219 | } 220 | var container = document.createElement("div"); 221 | container.id = "webSocketContainer"; 222 | // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents 223 | // Flash from loading at least in IE. So we move it out of the screen at (-100, -100). 224 | // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash 225 | // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is 226 | // the best we can do as far as we know now. 227 | container.style.position = "absolute"; 228 | if (WebSocket.__isFlashLite()) { 229 | container.style.left = "0px"; 230 | container.style.top = "0px"; 231 | } else { 232 | container.style.left = "-100px"; 233 | container.style.top = "-100px"; 234 | } 235 | var holder = document.createElement("div"); 236 | holder.id = "webSocketFlash"; 237 | container.appendChild(holder); 238 | document.body.appendChild(container); 239 | // See this article for hasPriority: 240 | // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html 241 | swfobject.embedSWF( 242 | WEB_SOCKET_SWF_LOCATION, 243 | "webSocketFlash", 244 | "1" /* width */, 245 | "1" /* height */, 246 | "10.0.0" /* SWF version */, 247 | null, 248 | null, 249 | {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"}, 250 | null, 251 | function(e) { 252 | if (!e.success) { 253 | console.error("[WebSocket] swfobject.embedSWF failed"); 254 | } 255 | }); 256 | }; 257 | 258 | /** 259 | * Called by Flash to notify JS that it's fully loaded and ready 260 | * for communication. 261 | */ 262 | WebSocket.__onFlashInitialized = function() { 263 | // We need to set a timeout here to avoid round-trip calls 264 | // to flash during the initialization process. 265 | setTimeout(function() { 266 | WebSocket.__flash = document.getElementById("webSocketFlash"); 267 | WebSocket.__flash.setCallerUrl(location.href); 268 | WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); 269 | for (var i = 0; i < WebSocket.__tasks.length; ++i) { 270 | WebSocket.__tasks[i](); 271 | } 272 | WebSocket.__tasks = []; 273 | }, 0); 274 | }; 275 | 276 | /** 277 | * Called by Flash to notify WebSockets events are fired. 278 | */ 279 | WebSocket.__onFlashEvent = function() { 280 | setTimeout(function() { 281 | try { 282 | // Gets events using receiveEvents() instead of getting it from event object 283 | // of Flash event. This is to make sure to keep message order. 284 | // It seems sometimes Flash events don't arrive in the same order as they are sent. 285 | var events = WebSocket.__flash.receiveEvents(); 286 | for (var i = 0; i < events.length; ++i) { 287 | WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); 288 | } 289 | } catch (e) { 290 | console.error(e); 291 | } 292 | }, 0); 293 | return true; 294 | }; 295 | 296 | // Called by Flash. 297 | WebSocket.__log = function(message) { 298 | console.log(decodeURIComponent(message)); 299 | }; 300 | 301 | // Called by Flash. 302 | WebSocket.__error = function(message) { 303 | console.error(decodeURIComponent(message)); 304 | }; 305 | 306 | WebSocket.__addTask = function(task) { 307 | if (WebSocket.__flash) { 308 | task(); 309 | } else { 310 | WebSocket.__tasks.push(task); 311 | } 312 | }; 313 | 314 | /** 315 | * Test if the browser is running flash lite. 316 | * @return {boolean} True if flash lite is running, false otherwise. 317 | */ 318 | WebSocket.__isFlashLite = function() { 319 | if (!window.navigator || !window.navigator.mimeTypes) { 320 | return false; 321 | } 322 | var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"]; 323 | if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) { 324 | return false; 325 | } 326 | return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; 327 | }; 328 | 329 | if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { 330 | if (window.addEventListener) { 331 | window.addEventListener("load", function(){ 332 | WebSocket.__initialize(); 333 | }, false); 334 | } else { 335 | window.attachEvent("onload", function(){ 336 | WebSocket.__initialize(); 337 | }); 338 | } 339 | } 340 | 341 | })(); 342 | -------------------------------------------------------------------------------- /emulator/include/websock.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Websock: high-performance binary WebSockets 3 | * Copyright (C) 2011 Joel Martin 4 | * Licensed under LGPL-3 (see LICENSE.txt) 5 | * 6 | * Websock is similar to the standard WebSocket object but Websock 7 | * enables communication with raw TCP sockets (i.e. the binary stream) 8 | * via websockify. This is accomplished by base64 encoding the data 9 | * stream between Websock and websockify. 10 | * 11 | * Websock has built-in receive queue buffering; the message event 12 | * does not contain actual data but is simply a notification that 13 | * there is new data available. Several rQ* methods are available to 14 | * read binary data off of the receive queue. 15 | */ 16 | 17 | 18 | // Load Flash WebSocket emulator if needed 19 | 20 | if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) { 21 | Websock_native = true; 22 | } else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) { 23 | Websock_native = true; 24 | window.WebSocket = window.MozWebSocket; 25 | } else { 26 | /* no builtin WebSocket so load web_socket.js */ 27 | Websock_native = false; 28 | (function () { 29 | function get_INCLUDE_URI() { 30 | return (typeof INCLUDE_URI !== "undefined") ? 31 | INCLUDE_URI : "include/"; 32 | } 33 | 34 | var start = " 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 |
38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /emulator/jslinux.js: -------------------------------------------------------------------------------- 1 | /* 2 | Linux launcher 3 | 4 | Copyright (c) 2011-2012 Fabrice Bellard 5 | 6 | Redistribution or commercial use is prohibited without the author's 7 | permission. 8 | */ 9 | "use strict"; 10 | 11 | var term, pc, boot_start_time, init_state; 12 | 13 | function term_start() 14 | { 15 | term = new Term(80, 30, term_handler); 16 | 17 | term.open(); 18 | } 19 | 20 | /* send chars to the serial port */ 21 | function term_handler(str) 22 | { 23 | pc.serial.send_chars(str); 24 | } 25 | 26 | function clipboard_set(val) 27 | { 28 | var el; 29 | el = document.getElementById("text_clipboard"); 30 | el.value = val; 31 | } 32 | 33 | function clipboard_get() 34 | { 35 | var el; 36 | el = document.getElementById("text_clipboard"); 37 | return el.value; 38 | } 39 | 40 | function clear_clipboard() 41 | { 42 | var el; 43 | el = document.getElementById("text_clipboard"); 44 | el.value = ""; 45 | } 46 | 47 | /* just used to display the boot time in the VM */ 48 | function get_boot_time() 49 | { 50 | return (+new Date()) - boot_start_time; 51 | } 52 | 53 | function start() 54 | { 55 | var params; 56 | 57 | init_state = new Object(); 58 | 59 | params = new Object(); 60 | 61 | /* serial output chars */ 62 | params.serial_write = term.write.bind(term); 63 | 64 | /* memory size (in bytes) */ 65 | params.mem_size = 16 * 1024 * 1024; 66 | 67 | /* clipboard I/O */ 68 | params.clipboard_get = clipboard_get; 69 | params.clipboard_set = clipboard_set; 70 | 71 | params.get_boot_time = get_boot_time; 72 | 73 | /* IDE drive. The raw disk image is split into files of 74 | * 'block_size' KB. 75 | */ 76 | //params.hda = { url: "hda%d.bin", block_size: 64, nb_blocks: 912 }; 77 | 78 | pc = new PCEmulator(params); 79 | 80 | init_state.params = params; 81 | 82 | /* Add JSModem for networking: 83 | * - register ttyS2 as COM2 with io port 2f8 and irq 3; 84 | * - connect websocket to server at localhost:2080 (will be 85 | * redirected on the server-side). 86 | */ 87 | var modem = new JSModem(pc); 88 | modem.connect('localhost', 2080); 89 | 90 | pc.load_binary("vmlinux-2.6.20.bin", 0x00100000, start2); 91 | } 92 | 93 | function start2(ret) 94 | { 95 | if (ret < 0) 96 | return; 97 | init_state.start_addr = 0x10000; 98 | init_state.initrd_size = 0; 99 | //pc.load_binary("linuxstart.bin", init_state.start_addr, start3); 100 | pc.load_binary("linuxstart.bin", init_state.start_addr, start3_); 101 | } 102 | 103 | function start3(ret) 104 | { 105 | var block_list; 106 | if (ret < 0) 107 | return; 108 | /* Preload blocks so that the boot time does not depend on the 109 | * time to load the required disk data (optional) */ 110 | block_list = [ 0, 7, 3, 643, 720, 256, 336, 644, 781, 387, 464, 475, 131, 589, 468, 472, 474, 776, 777, 778, 779, 465, 466, 473, 467, 469, 470, 512, 592, 471, 691, 697, 708, 792, 775, 769 ]; 111 | pc.ide0.drives[0].bs.preload(block_list, start4); 112 | } 113 | 114 | function start3_(ret) 115 | { 116 | if (ret < 0) 117 | return; 118 | pc.load_binary("root.bin", 0x00400000, start4); 119 | } 120 | 121 | function start4(ret) 122 | { 123 | var cmdline_addr; 124 | 125 | if (ret < 0) 126 | return; 127 | 128 | /* Assume booting from /dev/ram0 - result of previous load_binary("root.bin") call equals to the 129 | * size of the ram image. 130 | */ 131 | init_state.initrd_size = ret; 132 | 133 | /* set the Linux kernel command line */ 134 | cmdline_addr = 0xf800; 135 | //pc.cpu.write_string(cmdline_addr, "console=ttyS0 root=/dev/hda ro init=/sbin/init notsc=1 hdb=none"); 136 | pc.cpu.write_string(cmdline_addr, "console=ttyS0 root=/dev/ram0 rw init=/sbin/init notsc=1"); 137 | 138 | pc.cpu.eip = init_state.start_addr; 139 | pc.cpu.regs[0] = init_state.params.mem_size; /* eax */ 140 | pc.cpu.regs[3] = init_state.initrd_size; /* ebx = initrd_size (optional ram disk - old jslinux booting) */ 141 | pc.cpu.regs[1] = cmdline_addr; /* ecx */ 142 | 143 | boot_start_time = (+new Date()); 144 | 145 | pc.start(); 146 | } 147 | 148 | term_start(); 149 | -------------------------------------------------------------------------------- /emulator/jsmodem.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Web Modem device emulation for JS/Linux. 4 | 5 | Copyright (c) 2011 by Yauhen Yakimovich. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY YAUHEN YAKIMOVICH``AS IS'' AND ANY EXPRESS OR 18 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | EVENT SHALL YAUHEN YAKIMOVICH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | The views and conclusions contained in the software and documentation are those 29 | of the authors and should not be interpreted as representing official policies, 30 | either expressed or implied, of Yauhen Yakimovich. 31 | 32 | */ 33 | 34 | var include = function (filename) { 35 | document.write('