/[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

Annotation of /jsttyplay/html/streamtty.js

Parent Directory Parent Directory | Revision Log Revision Log


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

1 dpavlin 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