source: trunk/termtv/js/emulator.js@ 19905

Last change on this file since 19905 was 17409, checked in by tbretz, 11 years ago
Enable strict mode.
File size: 32.2 KB
Line 
1'use strict';
2
3/**
4 * @constructor
5 */
6function TTVEmulator(opts)
7{
8 // somewhat vt102, somewhat xterm
9
10 var emu = this;
11
12 emu.changeCallback = opts.change;
13 emu.specialCallback = opts.special;
14 emu.cursorCallback = opts.cursor;
15 emu.debugLevel = opts.debugLevel;
16
17 emu.debug = function(txt)
18 {
19 opts.debug ? opts.debug("emulate.js: "+txt) : alert("emulate.js: "+txt);
20 }
21
22
23 emu.width = opts.width || 80;
24 emu.height = opts.height || 24;
25
26 emu.initialize();
27
28 return emu;
29}
30
31TTVEmulator.prototype.initialize = function ()
32{
33 this.scr = {};
34
35 // All properties need initialization because of cloning
36 this.cursor = {};
37 this.cursor.x = 0;
38 this.cursor.y = 0;
39 this.cursor.bold = false;
40 this.cursor.underline = false;
41 this.cursor.lowintensity = false;
42 this.cursor.blink = false;
43 this.cursor.reversed = false; // state, fcolor and bcolor are flipped when this is
44 this.cursor.invisible = false; // TODO: implement
45 this.cursor.fcolor = 7;
46 this.cursor.bcolor = 0;
47 this.cursor.visible = true;
48
49 // character-wide
50 this.scr.c = {};
51 this.scr.c.text = [];
52 this.scr.c.bold = [];
53 this.scr.c.underline = [];
54 this.scr.c.lowintensity = [];
55 this.scr.c.blink = [];
56 this.scr.c.fcolor = [];
57 this.scr.c.bcolor = [];
58 this.scr.reverseScreen = false;
59 this.scr.cursorStorage = clone(this.cursor);
60 this.scr.cursorPosStorage = { };
61 this.scr.cursorPosStorage.x = 0;
62 this.scr.cursorPosStorage.y = 0;
63 this.scr.autoWrap = true;
64
65 // FIXME: An array of cursor-type object would significantly decrease
66 // code size and most probably increase efficiency (except 'reverseScreen')
67 for (var i=0; i<this.width*this.height; i++)
68 {
69 this.scr.c.text.push(' ');
70 this.scr.c.bold.push(false);
71 this.scr.c.underline.push(false);
72 this.scr.c.lowintensity.push(false);
73 this.scr.c.blink.push(false);
74 this.scr.c.fcolor.push(7);
75 this.scr.c.bcolor.push(0);
76 }
77
78 this.scralt = clone(this.scr);
79
80 this.mode = {};
81 this.mode.cursorKeyANSI = true;
82 this.mode.scroll = 'jump'; // | smooth
83 this.mode.reverseScreen = false;
84 this.mode.originMode = 'screen'; // | marginHome
85 this.mode.autoRepeat = true;
86 this.mode.mouseTrackingDown = false;
87 this.mode.mouseTrackingUp = false;
88 this.mode.currentScreen = 1;
89 this.mode.keyboardLocked = false;
90 this.mode.insert = false;
91 this.mode.insertLimited = 0;
92 this.mode.localEcho = true;
93 this.mode.newLineMode = 'cr'; // | crlf
94
95 this.margins = {};
96 this.margins.top = 0;
97 this.margins.bottom = this.height-1;
98
99 this.tabs = {};
100 for (var t=0; t<this.width; t++)
101 this.tabs[t] = t % 8 == 0;
102
103 this.windowTitle = '';
104 this.iconTitle = '';
105
106 this.charsets = {};
107 this.charsets.g0 = 'us';
108 this.charsets.g1 = 'line';
109 this.charsets.active = 'g0';
110}
111
112TTVEmulator.prototype.freeze = function ()
113{
114 // Clone the object
115 return clone(this);
116};
117
118TTVEmulator.prototype.thaw = function (obj)
119{
120 this.scr = clone(obj.scr);
121 this.scralt = clone(obj.scralt);
122
123 this.mode = clone(obj.mode);
124 this.cursor = clone(obj.cursor);
125 this.margins = clone(obj.margins);
126 this.tabs = clone(obj.tabs);
127 this.charsets = clone(obj.charsets);
128
129 this.windowTitle = obj.windowTitle;
130 this.iconTitle = obj.iconTitle;
131
132 for (var y=0; y<this.height; y++)
133 this.postChange(y, 0, this.height-1);
134
135 this.postSpecial({ 'title': obj.windowTitle, 'icon': obj.iconTitle });
136 this.postCursor();
137};
138
139function unpack_unicode(hex)
140{
141 return String.fromCharCode(parseInt(hex, 16));
142}
143
144TTVEmulator.prototype.charmap =
145{
146 us: { }, // not existing implies consistent with unicode
147 uk: {
148 '#': unpack_unicode("A3") // pound symbol
149 },
150 line: {
151 '_': ' ',
152 '`': unpack_unicode("2666"), // diamond
153 'a': unpack_unicode("2591"), // checkerboard
154 'b': unpack_unicode("2409"), // HT
155 'c': unpack_unicode("240C"), // FF
156 'd': unpack_unicode("240D"), // CR
157 'e': unpack_unicode("240A"), // LF
158 'f': unpack_unicode("B0"), // degree symbol
159 'g': unpack_unicode("B1"), // plusminus
160 'h': unpack_unicode("2424"), // NL
161 'i': unpack_unicode("240B"), // VT
162 'j': unpack_unicode("2518"), // corner lr
163 'k': unpack_unicode("2510"), // corner ur
164 'l': unpack_unicode("250C"), // corner ul
165 'm': unpack_unicode("2514"), // corner ll
166 'n': unpack_unicode("253C"), // meeting +
167 //'o': unpack_unicode(""), // scan 1 horizontal
168 //'p': unpack_unicode(""), // scan 3 horizontal
169 'q': unpack_unicode("2500"), // scan 5 horizontal
170 //'r': unpack_unicode(""), // scan 7 horizontal
171 //'s': unpack_unicode(""), // scan 9 horizontal
172 't': unpack_unicode("2524"), // vertical meet right
173 'u': unpack_unicode("251C"), // vertical meet left
174 'v': unpack_unicode("2534"), // horizontal meet top
175 'w': unpack_unicode("252C"), // horizontal meet bottom
176 'x': unpack_unicode("2502"), // vertical bar
177 'y': unpack_unicode("2264"), // less than or equal to
178 'z': unpack_unicode("2265"), // greater than or equal to
179 '{': unpack_unicode("3C0"), // pi
180 '|': unpack_unicode("2260"), // not equal to
181 '}': unpack_unicode("A3"), // pound symbol
182 '~': unpack_unicode("B7") // center dot
183 }
184};
185
186TTVEmulator.prototype.postChange = function (y, minx, maxx)
187{
188 if (this.changeCallback)
189 this.changeCallback(y, minx, maxx);
190};
191
192TTVEmulator.prototype.postSpecial = function (obj)
193{
194 if (this.specialCallback)
195 this.specialCallback(obj);
196};
197
198TTVEmulator.prototype.postCursor = function()
199{
200 if (this.cursorCallback)
201 this.cursorCallback(this.cursor.x, this.cursor.y, this.cursor.visible);
202},
203
204TTVEmulator.prototype.ev_setWindowTitle = function(title)
205{
206 if (this.debugLevel>2) this.debug("SET_WINDOW_TITLE= "+title);
207
208 this.windowTitle = title;
209 this.postSpecial({ title: title });
210};
211
212TTVEmulator.prototype.ev_setIconTitle = function(title)
213{
214 if (this.debugLevel>2) this.debug("SET_ICON_TITLE= "+title);
215 this.iconTitle = title;
216 this.postSpecial({ icon: title });
217};
218
219TTVEmulator.prototype.ev_setWindowIconTitle = function(title)
220{
221 if (this.debugLevel>2) this.debug("SET_TITLE= "+title);
222 this.windowTitle = title;
223 this.iconTitle = title;
224 this.postSpecial({ title: title, icon:title });
225};
226
227TTVEmulator.prototype.ev_resetMargins = function ()
228{
229 if (this.debugLevel>2) this.debug("RESET_MARGIN=1 "+this.height);
230 this.ev_setMargins(1,this.height, true);
231};
232
233TTVEmulator.prototype.ev_setMargins = function(top, bottom, shown)
234{
235 if (!shown && this.debugLevel>2)
236 this.debug("SET_MARGIN="+top+" "+bottom);
237
238 top -= 1;
239 bottom -= 1;
240
241 if (top+1>=bottom)
242 top = bottom-1;
243
244 if (top<0)
245 top = 0;
246
247 if (top>this.height-2)
248 top = this.height-2;
249
250 if (bottom<1)
251 bottom = 1;
252
253 if (bottom>this.height-1)
254 bottom = this.height-1;
255
256 if (top+1>=bottom)
257 this.debug("numbers do not obey the laws of arithmetic in setMargins");
258
259 this.margins.top = top;
260 this.margins.bottom = bottom;
261
262 this.ev_goto(1, 1);
263};
264
265TTVEmulator.prototype.ev_cursorStack = function(action, all)
266{
267 if (this.debugLevel>2) this.debug("CURSOR_STACK="+action+" [+attrib="+all+"]");
268
269 if (action=='push')
270 {
271 if (all)
272 this.scr.cursorStorage = clone(this.cursor);
273 else
274 {
275 this.scr.cursorPosStorage.x = this.cursor.x;
276 this.scr.cursorPosStorage.y = this.cursor.y;
277 }
278 }
279 if (action=='pop')
280 {
281 if (all)
282 this.cursor = clone(this.scr.cursorStorage);
283 else
284 {
285 this.cursor.x = this.scr.cursorPosStorage.x;
286 this.cursor.y = this.scr.cursorPosStorage.y;;
287 }
288 }
289
290 this.postCursor();
291
292 //this.debug("Can't do cursorStack action "+action);
293};
294
295TTVEmulator.prototype.ev_setAttribute = function (attr, shown)
296{
297 if (!shown && this.debugLevel>2)
298 this.debug("SET_ATTRIB="+(!attr[0]?'0':attr));
299
300 // Would that be faster?
301 // attr[0] = parseInt(attr[0], 10)
302
303 /*
304 0 Reset / Normal all attributes off
305 1 Bold or increased intensity
306 2 Faint (decreased intensity)
307 3 Italic
308 4 Single underline
309 5 Blink (slow < 150/min)
310 6 Blink (rapid >=150/min)
311 7 Negative
312 8 Conceal
313 9 Crossed out
314 10 Primary (default) font
315 11-19 nth alternate font (11==first, ...)
316 20 Fraktur
317 21 Bold off or underline double
318 22 Normal color (neither bold nor faint)
319 23 italic / fraktur off
320 24 underline single / underline double off
321 25 blink off
322 27 inverse off
323 28 conceal off
324 29 crossed out off
325 30-37 foreground color
326 38 forground 256-color/24bit color
327 39 default foreground color
328 40-47 background color
329 38 background 256-color/24bit color
330 39 default background color
331 51 framed
332 52 encircled
333 53 overlined
334 54 framed/circled off
335 55 overlined off
336 60 idiogram underline or right side line
337 61 idiogram double underline or double right side line
338 62 idiogram overline or left side line
339 63 idiogram double overline or double left side line
340 64 idiogram stress marking
341 90-99 foreground color, high intensity
342 100-109 background color, low intensity
343 */
344
345 var fg, bg;
346
347 // This is sorted roughly with the frequency of appearance
348 if (!attr[0] || attr[0]==0) {
349 this.cursor.bold = false;
350 this.cursor.underline = false;
351 this.cursor.lowintensity = false;
352 this.cursor.blink = false; // term uses this as 'bold' for the background color
353 this.cursor.reversed = false;
354 this.cursor.invisible = false; // not supported by konsole
355 fg = 7;
356 bg = 0;
357 } else if (attr[0]>=30 && attr[0]<=37) {
358 fg = attr[0]-30;
359 } else if (attr[0]==39) {
360 fg = 7;
361 } else if (attr[0]>=40 && attr[0]<=47) {
362 bg = attr[0]-40;
363 } else if (attr[0]==49 ) {
364 bg = 0;
365 } else if (attr[0]>=90 && attr[0]<=97) {
366 fg = attr[0]-90+10; // Set foreground color, high intensity
367 } else if (attr[0]==99) {
368 fg = 7;
369 } else if (attr[0]>=100 && attr[0]<=107) {
370 bg = attr[0]-100+10; // Set background color, high intensity
371 } else if (attr[0]==109) {
372 bg = 0;
373 } else if (attr[0]==1) { // suggest: lowintense=false
374 this.cursor.bold = true;
375 this.cursor.lowintensity = false;
376 } else if (attr[0]==2) { // suggest: bold=false
377 this.cursor.lowintensity = true; /* not widely supported */
378 this.cursor.bold= false; /* not widely supported */
379 } else if (attr[0]==21) { // suggest: same as 22
380 // this.cursor.bold = false; /* not widely supported */
381 } else if (attr[0]==22) {
382 this.cursor.lowintensity = false;
383 this.cursor.bold = false;
384 } else if (attr[0]==7 || attr[0]==27) {
385 // There is only action needed when the stae changed.
386
387 if (attr[0]==7 && !this.cursor.reversed)
388 {
389 this.cursor.reversed = true;
390 fg = this.cursor.fcolor;
391 bg = this.cursor.bcolor;
392 }
393 if (attr[0]==27 && this.cursor.reversed)
394 {
395 this.cursor.reversed = false;
396 bg = this.cursor.fcolor;
397 fg = this.cursor.bcolor;
398 }
399 } else if (attr[0]==4 || attr[0]==24) {
400 this.cursor.underline = attr[0]==4;
401 } else if (attr[0]==5 || attr[0]==25) {
402 this.cursor.blink = attr[0]==5;
403 } else if (attr[0]==8 || attr[0]==28) {
404 this.cursor.invisible = attr == 8;
405 } else if (attr[0]==10) {
406 this.ev_switchCharset('g0', undefined); // linux console
407 } else if (attr[0]==11) {
408 this.ev_switchCharset('g1', undefined); // linux console
409 } else if (attr[0]==38 && attr[1]==5) {
410 if (attr[2]<8)
411 // 0x00-0x07: standard colors (as in ESC [ 30..37 m)
412 fg = attr[2];
413 else if (attr[2]<16)
414 // 0x08-0x0f: high intensity colors (as in ESC [ 90..97 m)
415 fg = attr[2]-2;
416 else if (attr[3]<232) {
417 // 0x10-0xe7: 6*6*6=216 colors: 16 + 36*r + 6*g + b (0 <= r,g,b <= 5)
418 var b = ((attr[3] - 16)%6);
419 var g = ((attr[3] - 16)/6) %6;
420 var r = ((attr[3] - 16)/36)%6;
421 } else {
422 // 0xe8-0xff: grayscale from black to white in 24 steps
423 var b = (attr[3] - 232)*11; // 0 - 253
424 var g = (attr[3] - 232)*11; // 0 - 253
425 var r = (attr[3] - 232)*11; // 0 - 253
426 }
427 this.debug("Warning: 256-foreground color (" + attr[2] + ") not supported.");
428 attr = attr.slice(2);
429 } else if (attr[0]==48 && attr[1]==5) {
430 if (attr[2]<8)
431 bg = attr[2];
432 else if (attr[2]<0x10)
433 bg = attr[2]-2;
434 this.debug("Warning: 256-background color (" + attr[2] + ") not supported.");
435 attr = attr.slice(2);
436 } else if (attr[0]==38 && attr[1]==2) {
437 var b = attr[5];
438 var g = attr[4];
439 var r = attr[3];
440 this.debug("Warning: 24bit color (" + attr.slice(2) + ") not supported.");
441 attr = attr.slice(4);
442 } else if (attr[0]==48 && attr[1]==2) {
443 this.debug("Warning: 24bit color (" + attr.slice(2) + ") not supported.");
444 attr = attr.slice(4);
445 } else {
446 this.debug("Warning: ignoring setAttribute(" + attr + ")");
447 }
448
449 if (this.cursor.reversed ^ this.scr.reverseScreen)
450 {
451 var x = fg;
452 fg = bg;
453 bg = x;
454 }
455
456 if (fg!=undefined)
457 this.cursor.fcolor = fg;
458 if (bg!=undefined)
459 this.cursor.bcolor = bg;
460
461 if (attr.length>1)
462 this.ev_setAttribute(attr.slice(1), true);
463};
464
465TTVEmulator.prototype.ev_normalString = function (str)
466{
467 if (this.debugLevel>2)
468 this.debug("STRING=["+this.cursor.x+"/"+this.cursor.y+";"+this.cursor.fcolor+";"+this.cursor.bcolor+";"+str+"]["+str.length+"]");
469
470 for (var i=0; i<str.length; i++)
471 this.ev_normalChar(str[i], true);
472};
473
474TTVEmulator.prototype.ev_normalChar = function(ch, shown)
475{
476 if (/*!shown &&*/ this.debugLevel>2)
477 this.debug("CHAR=["+this.cursor.x+"/"+this.cursor.y+";"+this.cursor.fcolor+";"+this.cursor.bcolor+";"+ch+"]");
478
479 // charmapping
480 if ( this.charsets.active &&
481 this.charsets[this.charsets.active] &&
482 this.charmap[this.charsets[this.charsets.active]] &&
483 this.charmap[this.charsets[this.charsets.active]][ch] )
484 ch = this.charmap[this.charsets[this.charsets.active]][ch];
485
486 // wrapping
487 if (this.cursor.x==this.width)
488 {
489 // cursor is on the margin, we can't put a character there
490 if (this.scr.autoWrap)
491 {
492 var b = this.mode.originMode == 'screen' ? this.height : this.margins.bottom+1;
493
494 this.cursor.x = 0;
495 this.cursor.y++;
496 if (this.cursor.y>=b)
497 {
498 if (this.cursor.y==b)
499 this.scroll(1);
500 this.cursor.y = b-1;
501 }
502 }
503 else
504 {
505 // temporarily
506 this.cursor.x--;
507 }
508 }
509
510 // put on screen
511 if (this.mode.insert)
512 {
513 this.removeCharAt(this.width-1, this.cursor.y);
514 this.insertCharAt(ch, this.cursor.x, this.cursor.y);
515
516 this.postChange(this.cursor.y, this.cursor.x, this.width-1);
517 }
518 else
519 {
520 this.overwriteCharAt(ch, this.cursor.x, this.cursor.y);
521 this.postChange(this.cursor.y, this.cursor.x, this.cursor.x);
522 }
523
524 // stepping
525 this.cursor.x++;
526 this.postCursor();
527};
528
529TTVEmulator.prototype.ev_specialChar = function (key)
530{
531 if (this.debugLevel>2)
532 this.debug("SPECIAL_CHAR="+key);
533
534 switch (key)
535 {
536 case 'carriageReturn':
537 this.cursor.x = 0;
538 this.postCursor();
539 break;
540
541 case 'backspace':
542 if (this.cursor.x>0)
543 {
544 this.cursor.x--;
545 this.postCursor();
546 }
547 break;
548
549 case 'lineFeed':
550 case 'formFeed':
551 case 'verticalTab':
552 this.cursor.y++;
553 if (this.cursor.y==this.margins.bottom+1)
554 {
555 this.scroll(1);
556 this.cursor.y = this.margins.bottom;
557 }
558 if (this.cursor.y>=this.height) {
559 this.cursor.y = this.height-1;
560 }
561 if (this.mode.newLineMode=='crlf')
562 {
563 this.cursor.x = 0;
564 }
565 this.postCursor();
566 break;
567
568 case 'horizontalTab':
569 do {
570 this.cursor.x++;
571 } while (this.cursor.x<this.width && !this.tabs[this.cursor.x]);
572
573 this.postCursor();
574 break;
575
576 case 'bell':
577 this.postSpecial({ 'bell': 'bell' });
578 break;
579
580 default:
581 this.debug("Warning: skipping specialChar event for key "+key);
582 }
583};
584
585TTVEmulator.prototype.ev_arrow = function (dir, count)
586{
587 if (this.debugLevel>2) this.debug("ARROW=["+this.cursor.x+"/"+this.cursor.y+"] + "+dir+" "+count);
588
589 var t = this.mode.originMode == 'screen' ? 0 : this.margins.top;
590 var b = this.mode.originMode == 'screen' ? this.height : this.margins.bottom+1;
591
592 switch ( dir )
593 {
594 case 'up':
595 this.cursor.y -= count;
596 if (this.cursor.y<t)
597 this.cursor.y = t;
598 break;
599
600 case 'down':
601 this.cursor.y += count;
602 if (this.cursor.y>=b)
603 this.cursor.y = b-1;
604 break;
605
606 case 'left':
607 this.cursor.x -= count;
608 if (this.cursor.x<0)
609 this.cursor.x = 0;
610 break;
611
612 case 'right':
613 this.cursor.x += count;
614 if (this.cursor.x>=this.width)
615 this.cursor.x = this.width-1;
616 break;
617
618 default:
619 this.debug("Can't handle arrow event with direction "+dir);
620 return;
621 }
622
623 this.postCursor();
624}
625
626TTVEmulator.prototype.ev_insertChars = function (count)
627{
628 if (this.debugLevel>2) this.debug("INSERT_CHARS="+count);
629 //this.mode.insertLimited = value;
630
631 // FIXME: The removal can be done in a single step
632 for (var i=0; i<count; i++)
633 {
634 this.removeCharAt(this.width-1, this.cursor.y);
635 this.insertCharAt(' ', this.cursor.x, this.cursor.y);
636 }
637
638 this.postChange(this.cursor.y, this.cursor.x, this.width-1);
639}
640
641TTVEmulator.prototype.ev_deleteChars = function (count)
642{
643 if (this.debugLevel>2) this.debug("DELETE_CHARS="+count);
644
645 // FIXME: The removal can be done in a single step
646 for (var i=0; i<count; i++)
647 {
648 this.insertDefaultCharAt(this.width-1, this.cursor.y);
649 this.removeCharAt(this.cursor.x, this.cursor.y);
650 }
651
652 this.postChange(this.cursor.y, this.cursor.x, this.width-1);
653}
654
655TTVEmulator.prototype.ev_deleteLines = function(count)
656{
657 if (this.debugLevel>2) this.debug("DELETE_LINES="+count);
658
659 if (this.cursor.y>this.margins.bottom)
660 return;
661
662 if (this.cursor.y<this.margins.top)
663 return;
664
665 // FIXME: This can be done in a single step
666 for (var i=0; i<count; i++)
667 {
668 for (var y=this.cursor.y; y<this.margins.bottom; y++)
669 this.moveLine(y+1, y); // copy line from y+1 to y
670
671 for (var x=0; x<this.width; x++)
672 this.setCharToDefaultAt(x, this.margins.bottom);
673 }
674
675 for (var y=this.cursor.y; y<=this.margins.bottom; y++)
676 this.postChange(y, 0, this.width-1);
677}
678
679TTVEmulator.prototype.ev_insertLines = function (count)
680{
681 if (this.debugLevel>2) this.debug("INSERT_LINES="+count);
682
683 if (this.cursor.y>this.margins.bottom)
684 return;
685
686 if (this.cursor.y<this.margins.top)
687 return;
688
689 // FIXME: This can be done in a single step
690 for (var i=0; i<count; i++)
691 {
692 for (var y=this.margins.bottom; y>this.cursor.y; y--)
693 this.moveLine(y-1, y); // move line from y-1 to y
694
695 for (var x=0; x<this.width; x++)
696 this.setCharToDefaultAt(x, this.cursor.y);
697 }
698
699 for (var y=this.cursor.y; y<=this.margins.bottom; y++)
700 this.postChange(y, 0, this.width-1);
701};
702
703TTVEmulator.prototype.ev_index = function(how, count)
704{
705 if (this.debugLevel>2) this.debug("INDEX="+how);
706
707 switch (how)
708 {
709 case 'down':
710 for (var i=0; i<count; i++)
711 {
712 if (this.cursor.y==this.margins.bottom) {
713 this.scroll(1);
714 } else {
715 this.cursor.y++;
716 }
717 }
718 this.postCursor();
719 break;
720
721 case 'up':
722 for (var i=0; i<count; i++)
723 {
724 if (this.cursor.y==this.margins.top) {
725 this.scroll(-1);
726 } else {
727 this.cursor.y--;
728 }
729 }
730 this.postCursor();
731 break;
732
733 case 'nextLine':
734 this.ev_index('down', count);
735 this.cursor.x = 0;
736 this.postCursor();
737 break;
738
739 case 'prevLine':
740 this.ev_index('up', count);
741 this.cursor.x = 0;
742 this.postCursor();
743 break;
744
745 default:
746 this.debug("Can't index with method "+how);
747 }
748}
749
750TTVEmulator.prototype.ev_mode = function (key, value)
751{
752 if (this.debugLevel>2) this.debug("MODE="+key+" ["+value+"]");
753 switch ( key )
754 {
755 // mode[key] wouldn't work for the closure compiler
756 case 'insert':
757 this.mode.insert = value;
758 //this.postSpecial({ 'key': key, 'value': value });
759 break;
760
761 case 'cursorKeyANSI':
762 this.mode.cursorKeyANSI = value;
763 //this.postSpecial({ 'key': key, 'value': value });
764 break;
765
766 case 'keypad':
767 this.mode.keypad = value;
768 //this.postSpecial({ 'key': key, 'value': value });
769 break;
770
771 case 'mouseTrackingUp':
772 this.mode.mouseTrackingUp = value;
773 //this.postSpecial({ 'key': key, 'value': value });
774 break;
775
776 case 'mouseTrackingDown':
777 this.mode.mouseTrackingDown = value;
778 //this.postSpecial({ 'key': key, 'value': value });
779 break;
780
781 case 'scroll':
782 this.mode.scroll = value;
783 //this.postSpecial({ 'key': key, 'value': value });
784 break;
785
786 case 'autoWrap':
787 this.scr.autoWrap = value;
788 //this.postSpecial({ 'key': key, 'value': value });
789 break;
790
791 case 'cursor':
792 this.cursor.visible = value;
793 this.postCursor();
794 break;
795
796 case 'cursorBlink':
797 this.cursor.blink = value;
798 break;
799
800 case 'width':
801 this.debug("width="+value);
802 break;
803 case 'height':
804 this.debug("height="+value);
805 break;
806
807 case 'currentScreen':
808 if (value!=this.mode.currentScreen)
809 {
810 this.debug("Exchange screens");
811
812 var newscr = this.scralt;
813 var newscralt = this.scr;
814
815 this.scr = newscr;
816 this.scralt = newscralt;
817
818 this.mode.currentScreen = value;
819
820 for (var y=0; y<this.height; y++)
821 this.postChange(y, 0, this.width-1);
822 }
823 break;
824
825 case 'originMode':
826 this.mode.originMode = value;
827 this.ev_goto(1, 1);
828 break;
829
830 case 'reverseScreen':
831 if (value!=this.scr.reverseScreen)
832 {
833 this.debug("Reverse screen");
834
835 this.scr.reverseScreen = value;
836
837 var fg = this.scr.c.fcolor;
838 this.scr.c.fcolor = this.scr.c.bcolor;
839 this.scr.c.bcolor = fg;
840
841 var fc = this.cursor.fcolor;
842 this.cursor.fcolor = this.cursor.bcolor;
843 this.cursor.bcolor = fc;
844
845 for (var y=0; y<this.height; y++)
846 this.postChange(y, 0, this.width-1);
847 }
848 break;
849
850 case 'borderColor':
851 this.postSpecial({'border' : value});
852 this.debug("Setting border to [col="+value[0]+"; width="+value[1]+"]");
853 break;
854
855 default:
856 this.debug("Warning: can't handle mode change '"+key+"' to '"+value+"'");
857 }
858}
859
860TTVEmulator.prototype.ev_eraseInLine = function(how)
861{
862 if (this.debugLevel>2) this.debug("ERASE_IN_LINE="+how);
863
864 var beg;
865 var end;
866
867 switch (how)
868 {
869 case 'toEnd':
870 beg = this.cursor.x;
871 end = this.width;
872 break;
873
874 case 'toStart':
875 beg = 0;
876 end = this.cursor.x+1;
877 break;
878
879 case 'whole':
880 beg = 0;
881 end = this.width;
882 break;
883
884 default:
885 this.debug("Can't eraseInLine with method '" + how + "'");
886 return;
887 }
888
889 // doesn not effect cursor position
890 for (var x=beg; x<end; x++)
891 this.overwriteCharAt(' ', x, this.cursor.y);
892 //this.setCharToDefaultAt(x, this.cursor.y);
893
894 this.postChange(this.cursor.y, beg, end-1);
895}
896
897TTVEmulator.prototype.ev_eraseInDisplay = function (how)
898{
899 if (this.debugLevel>2) this.debug("ERASE_IN_DISPLAY="+how);
900
901 var begy;
902 var endy;
903
904 switch (how)
905 {
906 case 'toEnd':
907 this.ev_eraseInLine('toEnd');
908 begy = this.cursor.y+1;
909 endy = this.height;
910 break;
911
912 case 'toStart':
913 this.ev_eraseInLine('toStart');
914 begy = 0;
915 endy = this.cursor.y;
916 break;
917
918 case 'whole':
919 begy = 0;
920 endy = this.height;
921 break;
922
923 default:
924 this.debug("Can't eraseInDisplay with method '" + how + "'");
925 return;
926 }
927
928 // doesn not effect cursor position
929 for (var y=begy; y<endy; y++)
930 for (var x=0; x<this.width; x++)
931 //this.setCharToDefaultAt(x, y);
932 this.overwriteCharAt(' ', x, y);
933
934 for (var y=begy; y<endy; y++)
935 this.postChange(y, 0, this.width-1);
936}
937
938TTVEmulator.prototype.ev_goto = function (toX, toY)
939{
940 if (this.debugLevel>2) this.debug("GOTO="+toX+","+toY);
941
942 var x = toX-1;
943 var y = toY-1;
944
945 if (x<0)
946 x = 0;
947
948 if (x>this.width)
949 x = this.width;
950
951 if (y<0)
952 y = 0;
953
954 if (this.mode.originMode=='screen')
955 {
956 if (y>=this.height)
957 y = this.height-1;
958 }
959 else // originMode margin
960 {
961 y += this.margins.top;
962
963 if (y>this.margins.bottom)
964 y = this.margins.bottom;
965 }
966
967 this.cursor.x = x;
968 if (toY>=0)
969 this.cursor.y = y;
970
971 this.postCursor();
972};
973
974/*
975TTVEmulator.prototype.ev_report = function (type)
976{
977 switch (type)
978 {
979 case 'status':
980 case 'printer':
981 case 'cursorPosition':
982 case 'deviceAttributes':
983 case 'versionString':
984 // TODO
985 break;
986
987 default:
988 this.debug("Can't handle report type "+type);
989 }
990}
991*/
992
993TTVEmulator.prototype.ev_setCharset = function(which, target)
994{
995 switch (which)
996 {
997 case ')': which = 'g0'; break;
998 case '(': which = 'g1'; break;
999 case '*': which = 'g2'; break;
1000 case '+': which = 'g3'; break;
1001 }
1002
1003 switch (target)
1004 {
1005 case 'A': target = 'uk'; break;
1006 case 'B': target = 'us'; break;
1007 // case '4' dutch
1008 // case 'C' finnish
1009 // case '5' finnish
1010 // case 'R' french
1011 // case 'Q' french canadian
1012 // case 'K' german
1013 // case 'Y' italian
1014 // case 'E' norwegian / danish
1015 // case '6' norwegian / danish
1016 // case 'Z' spanish
1017 // case 'H' swedish
1018 // case '7' swedish
1019 // case '=' swiss
1020 case '0': target = 'line'; break;
1021 case '1': target = 'rom'; break;
1022 case '2': target = 'romSpecial'; break;
1023 }
1024
1025 this.charsets[which] = target;
1026}
1027
1028TTVEmulator.prototype.ev_switchCharset = function(action, which)
1029{
1030 this.charsets.active = which;
1031}
1032
1033TTVEmulator.prototype.removeCharAt = function(x, y)
1034{
1035 var idx = x + y*this.width;
1036
1037 this.scr.c.text.splice( idx, 1);
1038 this.scr.c.bold.splice( idx, 1);
1039 this.scr.c.underline.splice( idx, 1);
1040 this.scr.c.lowintensity.splice(idx, 1);
1041 this.scr.c.blink.splice( idx, 1);
1042 this.scr.c.fcolor.splice( idx, 1);
1043 this.scr.c.bcolor.splice( idx, 1);
1044}
1045
1046TTVEmulator.prototype.insertCharAt = function(ch, x, y)
1047{
1048 var idx = x + y*this.width;
1049
1050 this.scr.c.text.splice( idx, 0, ch);
1051 this.scr.c.bold.splice( idx, 0, this.cursor.bold);
1052 this.scr.c.underline.splice( idx, 0, this.cursor.underline);
1053 this.scr.c.lowintensity.splice(idx, 0, this.cursor.lowintensity);
1054 this.scr.c.blink.splice( idx, 0, this.cursor.blink);
1055 this.scr.c.fcolor.splice( idx, 0, this.cursor.fcolor);
1056 this.scr.c.bcolor.splice( idx, 0, this.cursor.bcolor);
1057}
1058
1059TTVEmulator.prototype.insertDefaultCharAt = function(x, y)
1060{
1061 var idx = x + y*this.width;
1062
1063 this.scr.c.text.splice( idx, 0, ' ');
1064 this.scr.c.bold.splice( idx, 0, false);
1065 this.scr.c.underline.splice( idx, 0, false);
1066 this.scr.c.lowintensity.splice(idx, 0, false);
1067 this.scr.c.blink.splice( idx, 0, false);
1068 this.scr.c.fcolor.splice( idx, 0, 7);
1069 this.scr.c.bcolor.splice( idx, 0, 0);
1070}
1071
1072TTVEmulator.prototype.overwriteCharAt = function(ch, x, y)
1073{
1074 var idx = x + y*this.width;
1075
1076 this.scr.c.text.splice( idx, 1, ch);
1077 this.scr.c.bold.splice( idx, 1, this.cursor.bold);
1078 this.scr.c.underline.splice( idx, 1, this.cursor.underline);
1079 this.scr.c.lowintensity.splice(idx, 1, this.cursor.lowintensity);
1080 this.scr.c.blink.splice( idx, 1, this.cursor.blink);
1081 this.scr.c.fcolor.splice( idx, 1, this.cursor.fcolor);
1082 this.scr.c.bcolor.splice( idx, 1, this.cursor.bcolor);
1083}
1084
1085TTVEmulator.prototype.setCharToDefaultAt = function(x, y)
1086{
1087 var idx = x + y*this.width;
1088
1089 this.scr.c.text[idx] = ' ';
1090 this.scr.c.bold[idx] = false;
1091 this.scr.c.underline[idx] = false;
1092 this.scr.c.lowintensity[idx] = false;
1093 this.scr.c.blink[idx] = false;
1094 this.scr.c.fcolor[idx] = 7;
1095 this.scr.c.bcolor[idx] = 0;
1096}
1097
1098TTVEmulator.prototype.copyChar = function(fromIdx, toIdx)
1099{
1100 this.scr.c.text.splice( toIdx, 1, this.scr.c.text[fromIdx]);
1101 this.scr.c.bold.splice( toIdx, 1, this.scr.c.bold[fromIdx]);
1102 this.scr.c.underline.splice( toIdx, 1, this.scr.c.underline[fromIdx]);
1103 this.scr.c.lowintensity.splice(toIdx, 1, this.scr.c.lowintensity[fromIdx]);
1104 this.scr.c.blink.splice( toIdx, 1, this.scr.c.blink[fromIdx]);
1105 this.scr.c.fcolor.splice( toIdx, 1, this.scr.c.fcolor[fromIdx]);
1106 this.scr.c.bcolor.splice( toIdx, 1, this.scr.c.bcolor[fromIdx]);
1107};
1108
1109TTVEmulator.prototype.moveLine = function(from, to)
1110{
1111 for (var x=0; x<this.width; x++)
1112 {
1113 var fromIdx = x + from*this.width;
1114 var toIdx = x + to *this.width;
1115
1116 this.copyChar(fromIdx, toIdx);
1117 }
1118}
1119
1120TTVEmulator.prototype.scroll = function (lines)
1121{
1122 if (this.debugLevel>2) this.debug("SCROLL="+lines);
1123
1124 if (lines==0 || isNaN(lines))
1125 return;
1126
1127 // FIXME: The removal can be done in a single step
1128 if (lines>0)
1129 {
1130 for (var i=0; i<lines; i++)
1131 {
1132 for (var j=0; j<this.width; j++)
1133 {
1134 this.insertCharAt(' ', 0, this.margins.bottom+1);
1135 this.removeCharAt(0, this.margins.top);
1136 }
1137 }
1138 }
1139 else
1140 for (var i=lines; i>0; i--)
1141 {
1142 for (var j=0; j<this.width; j++)
1143 {
1144 this.removeCharAt(0, this.margins.bottom);
1145 this.insertCharAt(' ', 0, this.margins.top);
1146 }
1147 }
1148
1149 for (var y=this.margins.top; y<=this.margins.bottom; y++)
1150 this.postChange(y, 0, this.width-1);
1151}
1152
1153TTVEmulator.prototype.ev_unknown = function(type, v)
1154{
1155 this.debug("Warning: Event '"+type+"' not implemented: "+v);
1156}
1157TTVEmulator.prototype.ev_hardware = function(v)
1158{
1159 this.ev_unknown("hardware", v);
1160}
1161TTVEmulator.prototype.ev_led = function(v)
1162{
1163 this.ev_unknown("led", v);
1164}
1165TTVEmulator.prototype.ev_reset = function(v)
1166{
1167 // Reset all terminal setting to default (whatever 'all' means)
1168 // (maybe 'word wrap' and all h/l settings
1169 this.ev_unknown("reset", v);
1170}
1171TTVEmulator.prototype.ev_g2char = function(v)
1172{
1173 this.ev_unknown("g2char", v);
1174}
1175TTVEmulator.prototype.ev_g3char = function(v)
1176{
1177 this.ev_unknown("g3char", v);
1178}
1179TTVEmulator.prototype.ev_softReset = function(v)
1180{
1181 this.ev_unknown("softReset", v);
1182}
1183TTVEmulator.prototype.ev_selfTestRaw = function(v)
1184{
1185 this.ev_unknown("selfTestRaw", v);
1186}
1187TTVEmulator.prototype.ev_lineAttr = function(v)
1188{
1189 this.ev_unknown("lineAttr", v);
1190}
1191TTVEmulator.prototype.ev_tabStop = function(v)
1192{
1193 this.ev_unknown("tabStop", v);
1194}
Note: See TracBrowser for help on using the repository browser.