Mercurial > hg > rlgwebd
comparison shterm.js @ 0:bd412f63ce0d
Put this project under version control, finally.
author | John "Elwin" Edwards <elwin@sdf.org> |
---|---|
date | Sun, 06 May 2012 08:45:40 -0700 |
parents | |
children | 9bef0941c6dd |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:bd412f63ce0d |
---|---|
1 // A state machine that keeps track of polling the server. | |
2 var ajaxstate = { | |
3 state: 0, | |
4 timerID: null, | |
5 clear: function () { | |
6 if (this.timerID != null) { | |
7 window.clearTimeout(this.timerID); | |
8 this.timerID = null; | |
9 } | |
10 }, | |
11 set: function (ms) { | |
12 this.clear(); | |
13 this.timerID = window.setTimeout(getData, ms); | |
14 }, | |
15 gotdata: function () { | |
16 this.set(100); | |
17 this.state = 0; | |
18 }, | |
19 gotnothing: function () { | |
20 if (this.state == 0) { | |
21 this.set(100); | |
22 this.state = 1; | |
23 } | |
24 else if (this.state == 1) { | |
25 this.set(300); | |
26 this.state = 2; | |
27 } | |
28 else if (this.state == 2) { | |
29 this.set(1000); | |
30 this.state = 3; | |
31 } | |
32 else { | |
33 this.set(5000); | |
34 this.state = 3; | |
35 } | |
36 }, | |
37 posted: function () { | |
38 this.set(100); | |
39 this.state = 0; | |
40 } | |
41 }; | |
42 | |
43 function writeData(hexstr) { | |
44 var codenum; | |
45 var codes = []; | |
46 var nc; | |
47 var u8wait = 0; /* Stores bits from previous bytes of multibyte sequences. */ | |
48 var expect = 0; /* The number of 10------ bytes expected. */ | |
49 /* UTF-8 translation. */ | |
50 for (var i = 0; i < hexstr.length; i += 2) { | |
51 nc = Number("0x" + hexstr.substr(i, 2)); | |
52 if (nc < 0x7F) { | |
53 /* 0------- */ | |
54 codes.push(nc); | |
55 /* Any incomplete sequence will be discarded. */ | |
56 u8wait = 0; | |
57 expect = 0; | |
58 } | |
59 else if (nc < 0xC0) { | |
60 /* 10------ : part of a multibyte sequence */ | |
61 if (expect > 0) { | |
62 u8wait <<= 6; | |
63 u8wait += (nc & 0x3F); | |
64 expect--; | |
65 if (expect == 0) { | |
66 codes.push(u8wait); | |
67 u8wait = 0; | |
68 } | |
69 } | |
70 else { | |
71 /* Assume an initial byte was missed. */ | |
72 u8wait = 0; | |
73 } | |
74 } | |
75 /* These will all discard any incomplete sequence. */ | |
76 else if (nc < 0xE0) { | |
77 /* 110----- : introduces 2-byte sequence */ | |
78 u8wait = (nc & 0x1F); | |
79 expect = 1; | |
80 } | |
81 else if (nc < 0xF0) { | |
82 /* 1110---- : introduces 3-byte sequence */ | |
83 u8wait = (nc & 0x0F); | |
84 expect = 2; | |
85 } | |
86 else if (nc < 0xF8) { | |
87 /* 11110--- : introduces 4-byte sequence */ | |
88 u8wait = (nc & 0x07); | |
89 expect = 3; | |
90 } | |
91 else if (nc < 0xFC) { | |
92 /* 111110-- : introduces 5-byte sequence */ | |
93 u8wait = (nc & 0x03); | |
94 expect = 4; | |
95 } | |
96 else if (nc < 0xFE) { | |
97 /* 1111110- : introduces 6-byte sequence */ | |
98 u8wait = (nc & 0x01); | |
99 expect = 5; | |
100 } | |
101 else { | |
102 /* 1111111- : should never appear */ | |
103 u8wait = 0; | |
104 expect = 0; | |
105 } | |
106 /* Supporting all 31 bits is probably overkill... */ | |
107 } | |
108 termemu.write(codes); | |
109 return; | |
110 } | |
111 | |
112 function getData() { | |
113 if (!termemu.alive) | |
114 return; | |
115 var datareq = new XMLHttpRequest(); | |
116 datareq.onreadystatechange = function () { | |
117 if (datareq.readyState == 4 && datareq.status == 200) { | |
118 var datalines = datareq.responseText.split("\n"); | |
119 if (!datalines[0]) { | |
120 return; | |
121 } | |
122 else if (datalines[0] == "E1") { | |
123 termemu.alive = false; | |
124 return; | |
125 } | |
126 else if (datalines[0].charAt(0) != 'd') { | |
127 return; | |
128 } | |
129 if (datalines[1]) { | |
130 writeData(datalines[1]); | |
131 ajaxstate.gotdata(); | |
132 } | |
133 else { | |
134 ajaxstate.gotnothing(); | |
135 } | |
136 return; | |
137 } | |
138 }; | |
139 datareq.open('GET', '/feed', true); | |
140 datareq.send(null); | |
141 return; | |
142 } | |
143 | |
144 function postResponseHandler() { | |
145 if (this.readyState == 4 && this.status == 200) { | |
146 var datalines = this.responseText.split("\n"); | |
147 if (!datalines[0]) | |
148 return; | |
149 else if (datalines[0] == "E1") { | |
150 termemu.alive = false; | |
151 return; | |
152 } | |
153 else if (datalines[0].charAt(0) != "d") | |
154 return; | |
155 /* It is a data message */ | |
156 if (datalines[1]) { | |
157 writeData(datalines[1]); | |
158 } | |
159 ajaxstate.posted(); | |
160 return; | |
161 } | |
162 } | |
163 | |
164 function sendback(str) { | |
165 /* For responding to terminal queries. */ | |
166 var datareq = new XMLHttpRequest(); | |
167 datareq.onreadystatechange = postResponseHandler; | |
168 datareq.open('POST', '/feed', true); | |
169 datareq.send("keys=" + str); | |
170 return; | |
171 } | |
172 | |
173 /* ASCII values of keys 0-9. */ | |
174 var numShifts = [41, 33, 64, 35, 36, 37, 94, 38, 42, 40]; | |
175 | |
176 var keyHexCodes = { | |
177 init: function () { | |
178 this[KeyboardEvent.DOM_VK_RETURN] = ["0d", "0d"]; | |
179 this[KeyboardEvent.DOM_VK_SPACE] = ["20", "20"]; | |
180 this[KeyboardEvent.DOM_VK_TAB] = ["09", "09"]; | |
181 this[KeyboardEvent.DOM_VK_BACK_QUOTE] = ["60", "7e"]; | |
182 this[KeyboardEvent.DOM_VK_OPEN_BRACKET] = ["5b", "7b"]; | |
183 this[KeyboardEvent.DOM_VK_CLOSE_BRACKET] = ["5d", "7d"]; | |
184 this[KeyboardEvent.DOM_VK_BACK_SLASH] = ["5c", "7c"]; | |
185 this[KeyboardEvent.DOM_VK_SEMICOLON] = ["3b", "3a"]; | |
186 this[KeyboardEvent.DOM_VK_QUOTE] = ["27", "22"]; | |
187 this[KeyboardEvent.DOM_VK_COMMA] = ["2c", "3c"]; | |
188 this[KeyboardEvent.DOM_VK_PERIOD] = ["2e", "3e"]; | |
189 this[KeyboardEvent.DOM_VK_SLASH] = ["2f", "3f"]; | |
190 this[KeyboardEvent.DOM_VK_EQUALS] = ["3d", "2b"]; | |
191 this[KeyboardEvent.DOM_VK_SUBTRACT] = ["2d", "5f"]; | |
192 this[KeyboardEvent.DOM_VK_BACK_SPACE] = ["08", "08"]; | |
193 this[KeyboardEvent.DOM_VK_ESCAPE] = ["1b", "1b"]; | |
194 this[KeyboardEvent.DOM_VK_PAGE_UP] = ["1b5b357e", "1b5b357e"]; | |
195 this[KeyboardEvent.DOM_VK_PAGE_DOWN] = ["1b5b367e", "1b5b367e"]; | |
196 this.appCursor(false); | |
197 this.appKeypad(false); | |
198 }, | |
199 /* Multi-char control sequences! Neat! */ | |
200 appCursor: function (on) { | |
201 /* Aren't special keys vile? */ | |
202 if (on) { | |
203 this[KeyboardEvent.DOM_VK_LEFT] = ["1b4f44", "1b4f44"]; | |
204 this[KeyboardEvent.DOM_VK_RIGHT] = ["1b4f43", "1b4f43"]; | |
205 this[KeyboardEvent.DOM_VK_UP] = ["1b4f41", "1b4f41"]; | |
206 this[KeyboardEvent.DOM_VK_DOWN] = ["1b4f42", "1b4f42"]; | |
207 this[KeyboardEvent.DOM_VK_END] = ["1b4f46", "1b4f46"]; | |
208 this[KeyboardEvent.DOM_VK_HOME] = ["1b4f48", "1b4f48"]; | |
209 } | |
210 else { | |
211 this[KeyboardEvent.DOM_VK_LEFT] = ["1b5b44", "1b5b44"]; | |
212 this[KeyboardEvent.DOM_VK_RIGHT] = ["1b5b43", "1b5b43"]; | |
213 this[KeyboardEvent.DOM_VK_UP] = ["1b5b41", "1b5b41"]; | |
214 this[KeyboardEvent.DOM_VK_DOWN] = ["1b5b42", "1b5b42"]; | |
215 this[KeyboardEvent.DOM_VK_END] = ["1b5b46", "1b5b46"]; | |
216 this[KeyboardEvent.DOM_VK_HOME] = ["1b5b48", "1b5b48"]; | |
217 } | |
218 }, | |
219 appKeypad: function (on) { | |
220 /* In theory, these should produce either numerals or the k[a-c][1-3] | |
221 * sequences. Since we can't count on the terminfo description actually | |
222 * containing those sequences, pretend they're just arrow keys etc. | |
223 */ | |
224 this[KeyboardEvent.DOM_VK_NUMPAD1] = ["1b4f46", "1b4f46"]; | |
225 this[KeyboardEvent.DOM_VK_NUMPAD2] = ["1b4f42", "1b4f42"]; | |
226 this[KeyboardEvent.DOM_VK_NUMPAD3] = ["1b5b367e", "1b5b367e"]; | |
227 this[KeyboardEvent.DOM_VK_NUMPAD4] = ["1b4f44", "1b4f44"]; | |
228 this[KeyboardEvent.DOM_VK_NUMPAD5] = ["1b5b45", "1b5b45"]; | |
229 this[KeyboardEvent.DOM_VK_NUMPAD6] = ["1b4f43", "1b4f43"]; | |
230 this[KeyboardEvent.DOM_VK_NUMPAD7] = ["1b4f48", "1b4f48"]; | |
231 this[KeyboardEvent.DOM_VK_NUMPAD8] = ["1b4f41", "1b4f41"]; | |
232 this[KeyboardEvent.DOM_VK_NUMPAD9] = ["1b5b357e", "1b5b357e"]; | |
233 return; | |
234 } | |
235 }; | |
236 | |
237 function sendkey(ev) { | |
238 var keynum = ev.keyCode; | |
239 var code; | |
240 if (keynum >= ev.DOM_VK_A && keynum <= ev.DOM_VK_Z) { | |
241 /* Letters. This assumes the codes are 65-90. */ | |
242 if (ev.ctrlKey) | |
243 keynum -= 64; | |
244 else if (!ev.shiftKey) | |
245 keynum += 32; | |
246 code = keynum.toString(16); | |
247 if (code.length < 2) | |
248 code = "0" + code; | |
249 } | |
250 else if (keynum >= ev.DOM_VK_0 && keynum <= ev.DOM_VK_9) { | |
251 /* The number row. */ | |
252 if (ev.shiftKey) { | |
253 code = numShifts[keynum - 48].toString(16); | |
254 } | |
255 else { | |
256 code = keynum.toString(16); | |
257 } | |
258 } | |
259 else if (keynum in keyHexCodes) { | |
260 if (ev.shiftKey) | |
261 code = keyHexCodes[keynum][1]; | |
262 else | |
263 code = keyHexCodes[keynum][0]; | |
264 } | |
265 else if (keynum == ev.DOM_VK_SHIFT || keynum == ev.DOM_VK_CONTROL || | |
266 keynum == ev.DOM_VK_ALT || keynum == ev.DOM_VK_CAPS_LOCK) { | |
267 return; | |
268 } | |
269 else { | |
270 debug(1, "Ignoring keycode " + keynum); | |
271 return; | |
272 } | |
273 if (termemu.alive) | |
274 ev.preventDefault(); | |
275 var datareq = new XMLHttpRequest(); | |
276 datareq.onreadystatechange = postResponseHandler; | |
277 datareq.open('POST', '/feed', true); | |
278 datareq.send("keys=" + code); | |
279 //dkey(code); | |
280 return; | |
281 } | |
282 | |
283 var charshifts = { '-': "5f", '=': "2b", '[': "7b", ']': "7d", '\\': "7c", | |
284 ';': "3a", '\'': "22", ',': "3c", '.': "3e", '/': "3f", '`': "7e" | |
285 } | |
286 | |
287 function vkey(c) { | |
288 var keystr; | |
289 if (c.match(/^[a-z]$/)) { | |
290 if (termemu.ctrlp()) { | |
291 var n = c.charCodeAt(0) - 96; | |
292 keystr = n.toString(16); | |
293 if (keystr.length < 2) | |
294 keystr = "0" + keystr; | |
295 } | |
296 else if (termemu.shiftp()) | |
297 keystr = c.toUpperCase().charCodeAt(0).toString(16); | |
298 else | |
299 keystr = c.charCodeAt(0).toString(16); | |
300 } | |
301 else if (c.match(/^[0-9]$/)) { | |
302 if (termemu.shiftp()) | |
303 keystr = numShifts[c.charCodeAt(0) - 48].toString(16); | |
304 else | |
305 keystr = c.charCodeAt(0).toString(16); | |
306 } | |
307 else if (c == '\n') | |
308 keystr = "0a"; | |
309 else if (c == '\t') | |
310 keystr = "09"; | |
311 else if (c == '\b') | |
312 keystr = "08"; | |
313 else if (c == ' ') | |
314 keystr = "20"; | |
315 else if (c in charshifts) { | |
316 if (termemu.shiftp()) | |
317 keystr = charshifts[c]; | |
318 else | |
319 keystr = c.charCodeAt(0).toString(16); | |
320 } | |
321 else | |
322 return; | |
323 //writeData("Sending " + keystr); | |
324 var datareq = new XMLHttpRequest(); | |
325 datareq.onreadystatechange = postResponseHandler; | |
326 datareq.open('POST', '/feed', true); | |
327 datareq.send("keys=" + keystr); | |
328 return; | |
329 } | |
330 | |
331 function setup() { | |
332 keyHexCodes.init(); | |
333 termemu.init("termwrap"); | |
334 setTitle("Not connected."); | |
335 return; | |
336 } | |
337 | |
338 function toggleshift() { | |
339 termemu.toggleshift(); | |
340 keydiv = document.getElementById("shiftkey"); | |
341 if (termemu.shiftp()) | |
342 keydiv.className = "keysel"; | |
343 else | |
344 keydiv.className = "key"; | |
345 return; | |
346 } | |
347 | |
348 function togglectrl() { | |
349 termemu.togglectrl(); | |
350 keydiv = document.getElementById("ctrlkey"); | |
351 if (termemu.ctrlp()) | |
352 keydiv.className = "keysel"; | |
353 else | |
354 keydiv.className = "key"; | |
355 return; | |
356 } | |
357 | |
358 function login() { | |
359 if (termemu.alive) | |
360 return; | |
361 var req = new XMLHttpRequest(); | |
362 req.onreadystatechange = function () { | |
363 if (req.readyState == 4 && req.status == 200) { | |
364 var datalines = req.responseText.split("\n"); | |