/[ttyrec]/jsttyplay/html/streamtty.js
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /jsttyplay/html/streamtty.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Tue Feb 17 18:12:32 2009 UTC (15 years, 2 months ago) by dpavlin
File MIME type: application/javascript
File size: 11458 byte(s)
import upstream from http://encryptio.com/code/jsttyplay

1 // streamtty.js - a pty client for jstty
2 // Copyright 2008 Jack Christopher Kastorff
3 (function(){
4
5 var repeatString = function (str, rep) {
6 var outstr = '';
7 for (var i = 0; i < rep; i++) {
8 outstr += str;
9 }
10 return outstr;
11 };
12
13 var makeTable = function (width, height) {
14 var table = document.createElement("div");
15 var arr = [];
16 for (var j = 1; j <= height; j++) {
17 var row = document.createElement("div");
18 var arrrow = [];
19 row.style.fontFamily = '"ProFont", "Luxi Mono", "Monaco", "Courier", "Courier new", monospace';
20 row.style.margin = '0';
21 row.style.padding = '0';
22 row.style.wordSpacing = '0';
23 row.style.height = '1.2em';
24 for (var i = 1; i <= width; i++) {
25 var charelem = document.createElement("pre");
26 charelem.style.backgroundColor = '#000';
27 charelem.style.color = '#FFF';
28 charelem.style.display = 'inline';
29 charelem.style.fontWeight = 'normal';
30 charelem.style.textDecoration = 'none';
31 charelem.style.letterSpacing = '0';
32 charelem.style.margin = '0';
33 charelem.style.padding = '0 0 0.2em 0';
34 charelem.appendChild(document.createTextNode(" "));
35 row.appendChild(charelem);
36 arrrow.push(charelem);
37 }
38 table.appendChild(row);
39 arr.push(arrrow);
40 }
41 return { "arr": arr, "elem": table };
42 };
43
44 var setTextChunk = function (tb, r, index, stx) {
45 for (var i = 0; i < r.length; i++) {
46 tb.arr[index][i+stx].firstChild.replaceData(0, 1, r.charAt(i));
47 }
48 };
49
50 var setBoldChunk = function (tb, r, index, stx) {
51 for (var i = 0; i < r.length; i++) {
52 tb.arr[index][i+stx].style.fontWeight = r.charAt(i) == 0 ? 'normal' : 'bold';
53 }
54 };
55
56 var setUnderlineChunk = function (tb, r, index, stx) {
57 for (var i = 0; i < r.length; i++) {
58 tb.arr[index][i+stx].style.textDecoration = r.charAt(i) == 0 ? 'none' : 'underline';
59 }
60 };
61
62 var clut = { 0: "#000", 1: "#D00", 2: "#0D0", 3: "#DD0", 4: "#00D", 5: "#D0D", 6: "#0DD", 7: "#DDD" };
63
64 var setFcolorChunk = function (tb, r, index, stx) {
65 for (var i = 0; i < r.length; i++) {
66 tb.arr[index][i+stx].style.color = clut[r.charAt(i)];
67 }
68 };
69
70 var t = 0;
71 var setBcolorChunk = function (tb, r, index, stx) {
72 for (var i = 0; i < r.length; i++) {
73 tb.arr[index][i+stx].style.backgroundColor = clut[r.charAt(i)];
74 }
75 };
76
77 var loadIFrame = function (tb, rowcaches, fr, width, height) {
78 var d = uncompressIFrameBlock(fr.d, width);
79 for (var i = 0; i < d.length; i++) {
80 setTextChunk(tb, d[i], i, 0);
81 rowcaches.d[i] = d[i];
82 }
83 var B = uncompressIFrameBlock(fr.B, width);
84 for (var i = 0; i < B.length; i++) {
85 setBoldChunk(tb, B[i], i, 0);
86 rowcaches.B[i] = B[i];
87 }
88 var U = uncompressIFrameBlock(fr.U, width);
89 for (var i = 0; i < U.length; i++) {
90 setUnderlineChunk(tb, U[i], i, 0);
91 rowcaches.U[i] = U[i];
92 }
93 var f = uncompressIFrameBlock(fr.f, width);
94 for (var i = 0; i < f.length; i++) {
95 setFcolorChunk(tb, f[i], i, 0);
96 rowcaches.f[i] = f[i];
97 }
98 var b = uncompressIFrameBlock(fr.b, width);
99 for (var i = 0; i < b.length; i++) {
100 setBcolorChunk(tb, b[i], i, 0);
101 rowcaches.b[i] = b[i];
102 }
103 };
104
105 var uncompressIFrameBlock = function (d,width) {
106 var uncomp = [];
107 var last = null;
108 for (var i = 0; i < d.length; i++) {
109 var uncomprow = null;
110 if ( typeof d[i] == 'array' || typeof d[i] == 'object' ) {
111 if ( d[i][0] == "r" ) {
112 uncomprow = d[i][1];
113 } else if ( d[i][0] == "a" ) {
114 uncomprow = repeatString(d[i][1], width);
115 } else {
116 throw new Error ("bad iframe data: subarray is not valid");
117 }
118 } else if ( typeof d[i] == 'string' && d[i] == 'd' ) {
119 uncomprow = last;
120 } else {
121 throw new Error ("bad iframe data: unknown " + (typeof d[i]) + " in array");
122 }
123 uncomp.push(uncomprow);
124 last = uncomprow;
125 }
126 return uncomp;
127 };
128
129 var loadPFrame = function (table, rowcaches, fr, width, height) {
130 if ( fr.d ) {
131 diffPushGeneric(table, annotatedPFrameBlock(fr.d, width), rowcaches.d, setTextChunk);
132 }
133 if ( fr.B ) {
134 diffPushGeneric(table, annotatedPFrameBlock(fr.B, width), rowcaches.B, setBoldChunk);
135 }
136 if ( fr.U ) {
137 diffPushGeneric(table, annotatedPFrameBlock(fr.U, width), rowcaches.U, setUnderlineChunk);
138 }
139 if ( fr.f ) {
140 diffPushGeneric(table, annotatedPFrameBlock(fr.f, width), rowcaches.f, setFcolorChunk);
141 }
142 if ( fr.b ) {
143 diffPushGeneric(table, annotatedPFrameBlock(fr.b, width), rowcaches.b, setBcolorChunk);
144 }
145 };
146
147 var diffPushGeneric = function (table, d, rowcache, set) {
148 // convert everything to line operations
149 for (var i = 0; i < d.length; i++) {
150 var e = d[i];
151 if ( e[0] == "cp" ) {
152 set(table, rowcache[e[1]], e[2], 0);
153 rowcache[e[2]] = rowcache[e[1]];
154 } else if ( e[0] == 'char' ) {
155 var r = e[1];
156 var v = rowcache[r];
157 var da = v.slice(0, e[2]) + e[3] + v.slice(e[2]+1);
158 set(table, e[3], e[1], e[2]);
159 rowcache[r] = da;
160 } else if ( e[0] == 'chunk' ) {
161 var r = e[1];
162 var v = rowcache[r];
163 var da = v.slice(0, e[2]) + e[4] + v.slice(e[3]+1);
164 set(table, e[4], e[1], e[2]);
165 rowcache[r] = da;
166 } else if ( e[0] == 'line' ) {
167 set(table, e[2], e[1], 0);
168 rowcache[e[1]] = e[2];
169 } else {
170 throw new Error ("unknown p-frame item type " + e[0] + ", len " + e.length);
171 }
172 }
173 };
174
175 var annotatedPFrameBlock = function (frame, width) {
176 var ann = [];
177 for (var i = 0; i < frame.length; i++) {
178 var e = frame[i];
179 if ( e[0] == 'cp' ) {
180 ann.push(e);
181 } else if ( e.length == 2 ) {
182 // raw line
183 if ( typeof e[1] == 'string' ) {
184 ann.push(['line', e[0], e[1]]);
185 } else if ( e[1][0] == "a" ) {
186 ann.push(['line', e[0], repeatString(e[1][1], width)]);
187 } else {
188 throw new Error ("p-frame corrupted: invalid 2-len");
189 }
190 } else if ( e.length == 3 ) {
191 // char
192 ann.push(['char', e[0], e[1], e[2]]);
193 } else if ( e.length == 4 ) {
194 // chunk
195 if ( typeof e[3] == 'string' ) {
196 ann.push(['chunk', e[0], e[1], e[2], e[3]]);
197 } else if ( e[3][0] == 'a' ) {
198 ann.push(['chunk', e[0], e[1], e[2], repeatString(e[3][1], e[2]-e[1]+1)]);
199 } else {
200 throw new Error ("p-frame corrupted: invalid 4-len");
201 }
202 } else {
203 throw new Error ("p-frame corrupted: no such thing as a " + e.length + "-len");
204 }
205 }
206 return ann;
207 };
208
209 var handleCursor = function (table, bgcache, curpos, dx, dy) {
210 if ( typeof dx == 'number' || typeof dy == 'number' ) {
211 // make sure the old cursor position has been overwritten
212 setBcolorChunk(table, bgcache[curpos[1]-1].charAt(curpos[0]-1), curpos[1]-1, curpos[0]-1);
213 if ( typeof dx == 'number' ) {
214 curpos[0] = dx;
215 }
216 if ( typeof dy == 'number' ) {
217 curpos[1] = dy;
218 }
219 }
220
221 // draw the cursor
222 table.arr[curpos[1]-1][curpos[0]-1].style.backgroundColor = '#FFF';
223 };
224
225 var makeCache = function (ch, wid, hei) {
226 var c = [];
227 for (var y = 0; y < hei; y++) {
228 c.push( repeatString(ch, wid) );
229 }
230 return c;
231 };
232
233 var newSendBuffer = function (h) {
234 if ( ! h.activeRequest && h.sendBuffer.length > 0 ) {
235 if ( h.timer ) clearTimeout(h.timer);
236 request(h, "user input", { keys: h.sendBuffer } );
237 h.sendBuffer = '';
238 }
239 };
240
241 var request = function (h, cmd, args) {
242 if ( h.activeRequest ) return;
243 var url = '/api?';
244 args['type'] = cmd;
245 if ( h.id ) args['id'] = h.id;
246 var urlargs = [];
247 for (var k in args) {
248 urlargs.push( encodeURIComponent(k) + '=' + encodeURIComponent(args[k]) );
249 }
250 url += urlargs.join("&");
251
252 h.activeRequest = true;
253 var req = new XMLHttpRequest();
254 req.open("GET", url, true);
255 req.onreadystatechange = function () {
256 if ( req.readyState == 4 && req.status == 200 ) {
257 var data = eval('(' + req.responseText + ')');
258 if ( typeof data == "undefined" ) {
259 alert("err: undefined");
260 h.activeRequest = false;
261 req = null;
262 return;
263 }
264 if ( data.ok ) {
265 if ( data.id ) h.id = data.id;
266 if ( data.pframe ) {
267 loadPFrame(h.table, h.rowcaches, data.pframe, h.width, h.height);
268 if ( data.pframe.x && data.pframe.y ) {
269 handleCursor(h.table, h.rowcaches.b, h.curpos, data.pframe.x, data.pframe.y);
270 }
271 }
272 if ( data.iframe ) {
273 loadIFrame(h.table, h.rowcaches, data.iframe, h.width, h.height);
274 handleCursor(h.table, h.rowcaches.b, h.curpos, data.iframe.x, data.iframe.y);
275 }
276 h.timer = setTimeout( function () { request(h, "pframe", { }); }, h.timerlen.current );
277 h.timerlen.current *= h.timerlen.step;
278 if ( h.timerlen.current > h.timerlen.max ) {
279 h.timerlen.current = h.timerlen.max;
280 }
281 h.activeRequest = false;
282 newSendBuffer(h);
283 } else {
284 h.activeRequest = false;
285 alert("err: " + data.error);
286 }
287 req = null;
288 return;
289 } else if ( req.readyState == 4 && req.status != 200 ) {
290 alert("err: response code " + req.status);
291 h.activeRequest = false;
292 req = null;
293 return;
294 }
295 };
296 req.send(null);
297 };
298
299 var keyPressHandler = function (h, e) {
300 var str = String.fromCharCode(e.charCode);
301 h.sendBuffer += str;
302 h.timerlen.current = h.timerlen.min;
303 e.stopPropagation();
304 e.preventDefault();
305 newSendBuffer(h);
306 return false;
307 };
308
309 startStreamTTY = function (elem) {
310 while ( elem.firstChild ) {
311 elem.removeChild( elem.firstChild );
312 }
313
314 var width = 80;
315 var height = 24;
316
317 var table = makeTable(width, height);
318 elem.appendChild(table.elem);
319
320 var holder = {
321 'width': width,
322 'height': height,
323 'table': table,
324 'rowcaches': {
325 'd': makeCache(" ", width, height),
326 'f': makeCache("7", width, height),
327 'b': makeCache("0", width, height),
328 'B': makeCache("0", width, height),
329 'U': makeCache("0", width, height)
330 },
331 'curpos': [1,1],
332 'sendBuffer': '',
333 'timerlen': {
334 'max': 15000,
335 'current': 100,
336 'min': 100,
337 'step': 1.05
338 },
339 'activeRequest': false,
340 };
341
342 document.body.addEventListener( "keypress", function (e) { return keyPressHandler(holder, e); }, true );
343
344 request(holder, "create session", {});
345 };
346
347 }());

  ViewVC Help
Powered by ViewVC 1.1.26