Mercurial > hg > rlgwebd
comparison termemu.js @ 4:ee22eb9ab009
Client: don't assume the terminal is 24x80.
| author | John "Elwin" Edwards <elwin@sdf.org> |
|---|---|
| date | Mon, 07 May 2012 11:09:14 -0700 |
| parents | bd412f63ce0d |
| children | 826a7ced69f8 |
comparison
equal
deleted
inserted
replaced
| 3:bfdc775a574f | 4:ee22eb9ab009 |
|---|---|
| 59 view: null, // The div holding the terminal screen | 59 view: null, // The div holding the terminal screen |
| 60 screen: null, // The div representing the active screen area | 60 screen: null, // The div representing the active screen area |
| 61 normbuf: null, // The normal screen buffer | 61 normbuf: null, // The normal screen buffer |
| 62 altbuf: null, // The alternate screen buffer | 62 altbuf: null, // The alternate screen buffer |
| 63 histbuf: null, // The screen history buffer | 63 histbuf: null, // The screen history buffer |
| 64 /* Attributes */ | |
| 65 w: 0, // Screen width | |
| 66 h: 0, // Screen height | |
| 64 fgColor: "#b2b2b2", // Default color for text | 67 fgColor: "#b2b2b2", // Default color for text |
| 65 bgColor: "black", // Default background color | 68 bgColor: "black", // Default background color |
| 69 scrT: 0, // top and bottom of scrolling region | |
| 70 scrB: 0, // init() will set this properly | |
| 66 c: null, // Contains cursor position and text attributes | 71 c: null, // Contains cursor position and text attributes |
| 67 offedge: false, // Going off the edge doesn't mean adding a new line | 72 offedge: false, // Going off the edge doesn't mean adding a new line |
| 68 clearAttrs: function () { | 73 clearAttrs: function () { |
| 69 /* Make sure to reset ALL attribute properties and NOTHING else. */ | 74 /* Make sure to reset ALL attribute properties and NOTHING else. */ |
| 70 this.c.bold = false; | 75 this.c.bold = false; |
| 121 return "#" + colstr + colstr + colstr; | 126 return "#" + colstr + colstr + colstr; |
| 122 } | 127 } |
| 123 else | 128 else |
| 124 return fallback; | 129 return fallback; |
| 125 }, | 130 }, |
| 126 scrT: 0, // top and bottom of scrolling region | |
| 127 scrB: 23, | |
| 128 // These keyboard-related things don't really belong here. | 131 // These keyboard-related things don't really belong here. |
| 129 shift: false, | 132 shift: false, |
| 130 shiftp: function () { | 133 shiftp: function () { |
| 131 return this.shift; | 134 return this.shift; |
| 132 }, | 135 }, |
| 138 return this.ctrl; | 141 return this.ctrl; |
| 139 }, | 142 }, |
| 140 togglectrl: function () { | 143 togglectrl: function () { |
| 141 this.ctrl = !this.ctrl; | 144 this.ctrl = !this.ctrl; |
| 142 }, | 145 }, |
| 143 init: function (divID) { | 146 init: function (divID, h, w) { |
| 144 /* Makes a div full of character cells. */ | 147 /* Makes a div full of character cells. */ |
| 145 if (this.screen != null) | 148 if (this.screen != null) |
| 146 return; | 149 return; |
| 147 var owrap = document.getElementById(divID); | 150 var owrap = document.getElementById(divID); |
| 148 if (!owrap) | 151 if (!owrap) |
| 149 return; | 152 return; |
| 150 while (owrap.firstChild != null) | 153 while (owrap.firstChild != null) |
| 151 owrap.removeChild(owrap.firstChild); | 154 owrap.removeChild(owrap.firstChild); |
| 152 this.c = new Cursor(null); | 155 this.c = new Cursor(null); |
| 156 /* Set up the sizes. */ | |
| 157 var tn; | |
| 158 tn = Number(h); | |
| 159 if (tn > 0 && tn < 256) | |
| 160 this.h = tn; | |
| 161 else | |
| 162 this.h = 24; | |
| 163 tn = Number(w); | |
| 164 if (tn > 0 && tn < 256) | |
| 165 this.w = tn; | |
| 166 else | |
| 167 this.w = 80; | |
| 168 this.scrB = this.h - 1; | |
| 153 /* Create the contents of the terminal div */ | 169 /* Create the contents of the terminal div */ |
| 154 this.inwrap = document.createElement("div"); | 170 this.inwrap = document.createElement("div"); |
| 155 this.inwrap.id = "inwrap"; | 171 this.inwrap.id = "inwrap"; |
| 156 owrap.appendChild(this.inwrap); | 172 owrap.appendChild(this.inwrap); |
| 157 var termdiv = document.createElement("div"); | 173 var termdiv = document.createElement("div"); |
| 163 this.histbuf.id = "histbuf"; | 179 this.histbuf.id = "histbuf"; |
| 164 termdiv.appendChild(this.histbuf); | 180 termdiv.appendChild(this.histbuf); |
| 165 this.normbuf = document.createElement("div"); | 181 this.normbuf = document.createElement("div"); |
| 166 this.normbuf.id = "normbuf"; | 182 this.normbuf.id = "normbuf"; |
| 167 termdiv.appendChild(this.normbuf); | 183 termdiv.appendChild(this.normbuf); |
| 168 for (var row = 0; row < 24; row++) { | 184 for (var row = 0; row < this.h; row++) { |
| 169 this.normbuf.appendChild(this.makeRow()); | 185 this.normbuf.appendChild(this.makeRow()); |
| 170 } | 186 } |
| 171 this.altbuf = document.createElement("div"); | 187 this.altbuf = document.createElement("div"); |
| 172 this.altbuf.id = "altbuf"; | 188 this.altbuf.id = "altbuf"; |
| 173 termdiv.appendChild(this.altbuf); | 189 termdiv.appendChild(this.altbuf); |
| 225 toAltBuf: function () { | 241 toAltBuf: function () { |
| 226 if (this.screen == this.altbuf) | 242 if (this.screen == this.altbuf) |
| 227 return; | 243 return; |
| 228 while (this.altbuf.firstChild != null) | 244 while (this.altbuf.firstChild != null) |
| 229 this.altbuf.removeChild(this.altbuf.firstChild); | 245 this.altbuf.removeChild(this.altbuf.firstChild); |
| 230 for (var i = 0; i < 24; i++) { | 246 for (var i = 0; i < this.h; i++) { |
| 231 this.altbuf.appendChild(this.makeRow()); | 247 this.altbuf.appendChild(this.makeRow()); |
| 232 } | 248 } |
| 233 this.normc = new Cursor(this.c); | 249 this.normc = new Cursor(this.c); |
| 234 this.altbuf.style.display = "table-row-group"; | 250 this.altbuf.style.display = "table-row-group"; |
| 235 this.normbuf.style.display = "none"; | 251 this.normbuf.style.display = "none"; |
| 264 } | 280 } |
| 265 else { | 281 else { |
| 266 this.offedge = false; | 282 this.offedge = false; |
| 267 if (x < 0) | 283 if (x < 0) |
| 268 x = 0; | 284 x = 0; |
| 269 else if (x > 79) | 285 else if (x >= this.w) |
| 270 x = 79; | 286 x = this.w - 1; |
| 271 } | 287 } |
| 272 if (y == null) { | 288 if (y == null) { |
| 273 if (this.c.y != null) | 289 if (this.c.y != null) |
| 274 y = this.c.y; | 290 y = this.c.y; |
| 275 else | 291 else |
| 276 return; | 292 return; |
| 277 } | 293 } |
| 278 else if (y < 0) | 294 else if (y < 0) |
| 279 y = 0; | 295 y = 0; |
| 280 else if (y > 23) | 296 else if (y >= this.h) |
| 281 y = 23; | 297 y = this.h - 1; |
| 282 /* Un-reverse video the current location. */ | 298 /* Un-reverse video the current location. */ |
| 283 this.flipCursor(); | 299 this.flipCursor(); |
| 284 this.c.x = x; | 300 this.c.x = x; |
| 285 this.c.y = y; | 301 this.c.y = y; |
| 286 /* Reverse-video the new location. */ | 302 /* Reverse-video the new location. */ |
| 312 this.flipCursor(); | 328 this.flipCursor(); |
| 313 while (count > 0) { | 329 while (count > 0) { |
| 314 var blankrow = this.makeRow(); | 330 var blankrow = this.makeRow(); |
| 315 /* Careful with the order */ | 331 /* Careful with the order */ |
| 316 if (lines > 0) { | 332 if (lines > 0) { |
| 317 if (this.scrB == 23) | 333 if (this.scrB == this.h - 1) |
| 318 this.screen.appendChild(blankrow); | 334 this.screen.appendChild(blankrow); |
| 319 else | 335 else |
| 320 this.screen.insertBefore(blankrow, this.screen.childNodes[this.scrB | 336 this.screen.insertBefore(blankrow, this.screen.childNodes[this.scrB |
| 321 + 1]); | 337 + 1]); |
| 322 this.historize(this.scrT); | 338 this.historize(this.scrT); |
| 332 this.flipCursor(); | 348 this.flipCursor(); |
| 333 return; | 349 return; |
| 334 }, | 350 }, |
| 335 newline: function (doReturn) { | 351 newline: function (doReturn) { |
| 336 if (this.c.y == this.scrB) | 352 if (this.c.y == this.scrB) |
| 337 this.scroll(1) | 353 this.scroll(1); |
| 338 else if (this.c.y < 23) | 354 else if (this.c.y < this.h - 1) |
| 339 this.cmove(this.c.y + 1, null); | 355 this.cmove(this.c.y + 1, null); |
| 340 /* If the cursor is at the bottom but outside the scrolling region, | 356 /* If the cursor is at the bottom but outside the scrolling region, |
| 341 * nothing can be done. */ | 357 * nothing can be done. */ |
| 342 if (doReturn) { | 358 if (doReturn) { |
| 343 this.cmove(null, 0); | 359 this.cmove(null, 0); |
| 348 this.scroll(-1); | 364 this.scroll(-1); |
| 349 else if (this.c.y > 0) | 365 else if (this.c.y > 0) |
| 350 this.cmove(this.c.y - 1, null); | 366 this.cmove(this.c.y - 1, null); |
| 351 }, | 367 }, |
| 352 advance: function () { | 368 advance: function () { |
| 353 if (this.c.x < 79) | 369 if (this.c.x < this.w - 1) |
| 354 this.cmove(null, this.c.x + 1); | 370 this.cmove(null, this.c.x + 1); |
| 355 else { | 371 else { |
| 356 this.offedge = true; | 372 this.offedge = true; |
| 357 } | 373 } |
| 358 }, | 374 }, |
| 376 this.c.cset = 'B'; | 392 this.c.cset = 'B'; |
| 377 this.cmove(0, 0); | 393 this.cmove(0, 0); |
| 378 this.saved = null; | 394 this.saved = null; |
| 379 this.normc = null; | 395 this.normc = null; |
| 380 this.scrT = 0; | 396 this.scrT = 0; |
| 381 this.scrB = 23; | 397 this.scrB = this.h - 1; |
| 382 while (this.histbuf.firstChild != null) { | 398 while (this.histbuf.firstChild != null) { |
| 383 this.histbuf.removeChild(this.histbuf.firstChild); | 399 this.histbuf.removeChild(this.histbuf.firstChild); |
| 384 } | 400 } |
| 385 for (var i = 0; i < 24; i++) { | 401 for (var i = 0; i < this.h; i++) { |
| 386 this.screen.replaceChild(this.makeRow(), this.screen.childNodes[i]); | 402 this.screen.replaceChild(this.makeRow(), this.screen.childNodes[i]); |
| 387 } | 403 } |
| 388 this.flipCursor(); // make it appear in the new row | 404 this.flipCursor(); // make it appear in the new row |
| 389 return; | 405 return; |
| 390 }, | 406 }, |
| 555 this.cmove(null, this.c.x - 1); | 571 this.cmove(null, this.c.x - 1); |
| 556 } | 572 } |
| 557 else if (codes[i] == 9) { | 573 else if (codes[i] == 9) { |
| 558 /* tab */ | 574 /* tab */ |
| 559 var xnew; | 575 var xnew; |
| 560 if (this.c.x < 79) { | 576 if (this.c.x < this.w - 1) { |
| 561 xnew = 8 * (Math.floor(this.c.x / 8) + 1); | 577 xnew = 8 * (Math.floor(this.c.x / 8) + 1); |
| 562 if (xnew > 79) | 578 if (xnew >= this.w) |
| 563 xnew = 79; | 579 xnew = this.w - 1; |
| 564 this.cmove(null, xnew); | 580 this.cmove(null, xnew); |
| 565 } | 581 } |
| 566 else { | 582 else { |
| 567 this.offedge = true; | 583 this.offedge = true; |
| 568 } | 584 } |
| 699 } | 715 } |
| 700 if (params[0]) | 716 if (params[0]) |
| 701 y = params[0] - 1; | 717 y = params[0] - 1; |
| 702 if (params[1]) | 718 if (params[1]) |
| 703 x = params[1] - 1; | 719 x = params[1] - 1; |
| 704 if (y > 23) | 720 if (y >= this.h) |
| 705 y = 23; | 721 y = this.h - 1; |
| 706 if (x > 79) | 722 if (x >= this.w) |
| 707 x = 79; | 723 x = this.w - 1; |
| 708 debug(0, "Moving to row " + y + ", col " + x); | 724 debug(0, "Moving to row " + y + ", col " + x); |
| 709 this.cmove(y, x); | 725 this.cmove(y, x); |
| 710 } | 726 } |
| 711 else if (c == 73) { | 727 else if (c == 73) { |
| 712 /* I - move forward by tabs */ | 728 /* I - move forward by tabs */ |
| 715 debug(1, "Invalid CSI I sequence: " + comstr); | 731 debug(1, "Invalid CSI I sequence: " + comstr); |
| 716 return; | 732 return; |
| 717 } | 733 } |
| 718 while (count > 0) { | 734 while (count > 0) { |
| 719 x = 8 * (Math.floor(x / 8) + 1); | 735 x = 8 * (Math.floor(x / 8) + 1); |
| 720 if (x > 79) { | 736 if (x >= this.w) { |
| 721 x = 79; | 737 x = this.w - 1; |
| 722 break; | 738 break; |
| 723 } | 739 } |
| 724 count--; | 740 count--; |
| 725 } | 741 } |
| 726 this.cmove(null, x); | 742 this.cmove(null, x); |
| 737 return; | 753 return; |
| 738 } | 754 } |
| 739 if (!params[0]) { | 755 if (!params[0]) { |
| 740 /* Either 0 or not given */ | 756 /* Either 0 or not given */ |
| 741 start = this.c.y + 1; | 757 start = this.c.y + 1; |
| 742 end = 23; | 758 end = this.h - 1; |
| 743 cols = 1; | 759 cols = 1; |
| 744 } | 760 } |
| 745 else if (params[0] == 1) { | 761 else if (params[0] == 1) { |
| 746 start = 0; | 762 start = 0; |
| 747 end = this.c.y - 1; | 763 end = this.c.y - 1; |
| 748 cols = -1; | 764 cols = -1; |
| 749 } | 765 } |
| 750 else if (params[0] == 2) { | 766 else if (params[0] == 2) { |
| 751 start = 0; | 767 start = 0; |
| 752 end = 23; | 768 end = this.h - 1; |
| 753 cols = 0; | 769 cols = 0; |
| 754 } | 770 } |
| 755 else { | 771 else { |
| 756 debug(1, "Unimplemented parameter in CSI J sequence: " + comstr); | 772 debug(1, "Unimplemented parameter in CSI J sequence: " + comstr); |
| 757 return; | 773 return; |
| 761 } | 777 } |
| 762 if (cols != 0) { | 778 if (cols != 0) { |
| 763 /* Otherwise, the whole screen was erased and the active row doesn't | 779 /* Otherwise, the whole screen was erased and the active row doesn't |
| 764 * need special treatment. */ | 780 * need special treatment. */ |
| 765 var cursrow = this.screen.childNodes[this.c.y]; | 781 var cursrow = this.screen.childNodes[this.c.y]; |
| 766 for (var ncol = this.c.x; ncol >= 0 && ncol < 80; ncol += cols) { | 782 for (var ncol = this.c.x; ncol >= 0 && ncol < this.w; ncol += cols) { |
| 767 cursrow.replaceChild(this.makeCell(' '), cursrow.childNodes[ncol]); | 783 cursrow.replaceChild(this.makeCell(' '), cursrow.childNodes[ncol]); |
| 768 } | 784 } |
| 769 } | 785 } |
| 770 this.offedge = false; | 786 this.offedge = false; |
| 771 /* Always flip after replacing the active position. */ | 787 /* Always flip after replacing the active position. */ |
| 788 start = 0; | 804 start = 0; |
| 789 end = this.c.x; | 805 end = this.c.x; |
| 790 } | 806 } |
| 791 else if (params[0] == 2) { | 807 else if (params[0] == 2) { |
| 792 start = 0; | 808 start = 0; |
| 793 end = 79; | 809 end = this.w - 1; |
| 794 } | 810 } |
| 795 else { | 811 else { |
| 796 start = this.c.x; | 812 start = this.c.x; |
| 797 end = 79; | 813 end = this.w - 1; |
| 798 } | 814 } |
| 799 var rowdiv = this.screen.childNodes[this.c.y]; | 815 var rowdiv = this.screen.childNodes[this.c.y]; |
| 800 for (var i = start; i <= end; i++) { | 816 for (var i = start; i <= end; i++) { |
| 801 rowdiv.replaceChild(this.makeCell(' '), rowdiv.childNodes[i]); | 817 rowdiv.replaceChild(this.makeCell(' '), rowdiv.childNodes[i]); |
| 802 } | 818 } |
| 822 if (c == 76) { | 838 if (c == 76) { |
| 823 this.historize(this.scrB); | 839 this.historize(this.scrB); |
| 824 this.screen.insertBefore(blankrow, this.screen.childNodes[this.c.y]); | 840 this.screen.insertBefore(blankrow, this.screen.childNodes[this.c.y]); |
| 825 } | 841 } |
| 826 else { | 842 else { |
| 827 if (this.scrB == 23) | 843 if (this.scrB == this.h - 1) |
| 828 this.screen.appendChild(blankrow); | 844 this.screen.appendChild(blankrow); |
| 829 else | 845 else |
| 830 this.screen.insertBefore(blankrow, this.screen.childNodes[this.scrB | 846 this.screen.insertBefore(blankrow, this.screen.childNodes[this.scrB |
| 831 + 1]); | 847 + 1]); |
| 832 this.historize(this.c.y); | 848 this.historize(this.c.y); |
| 868 if (prefix || postfix) { | 884 if (prefix || postfix) { |
| 869 debug(1, "Invalid CSI sequence: " + comstr); | 885 debug(1, "Invalid CSI sequence: " + comstr); |
| 870 return; | 886 return; |
| 871 } | 887 } |
| 872 var row = this.screen.childNodes[this.c.y]; | 888 var row = this.screen.childNodes[this.c.y]; |
| 873 for (var i = 0; i < count && this.c.x + i < 80; i++) { | 889 for (var i = 0; i < count && this.c.x + i < this.w; i++) { |
| 874 row.replaceChild(this.makeCell(' '), row.childNodes[this.c.x + i]); | 890 row.replaceChild(this.makeCell(' '), row.childNodes[this.c.x + i]); |
| 875 } | 891 } |
| 876 this.flipCursor(); | 892 this.flipCursor(); |
| 877 } | 893 } |
| 878 else if (c == 90) { | 894 else if (c == 90) { |
| 1010 } | 1026 } |
| 1011 } | 1027 } |
| 1012 else if (c == 114) { | 1028 else if (c == 114) { |
| 1013 /* r - set scrolling region */ | 1029 /* r - set scrolling region */ |
| 1014 var t = 0; | 1030 var t = 0; |
| 1015 var b = 23; | 1031 var b = this.h - 1; |
| 1016 if (params[0] && params[0] <= 23) | 1032 if (params[0] && params[0] <= this.h - 1) |
| 1017 t = params[0] - 1; | 1033 t = params[0] - 1; |
| 1018 if (params[1] && params[1] <= 24) | 1034 if (params[1] && params[1] <= this.h) |
| 1019 b = params[1] - 1; | 1035 b = params[1] - 1; |
| 1020 if (b <= t) | 1036 if (b <= t) |
| 1021 return; | 1037 return; |
| 1022 this.scrT = t; | 1038 this.scrT = t; |
| 1023 this.scrB = b; | 1039 this.scrB = b; |
| 1074 return cell; | 1090 return cell; |
| 1075 }, | 1091 }, |
| 1076 makeRow: function() { | 1092 makeRow: function() { |
| 1077 var blankrow = document.createElement("div"); | 1093 var blankrow = document.createElement("div"); |
| 1078 blankrow.className = "termrow"; | 1094 blankrow.className = "termrow"; |
| 1079 for (var i = 0; i < 80; i++) | 1095 for (var i = 0; i < this.w; i++) |
| 1080 blankrow.appendChild(this.makeCell(' ')); | 1096 blankrow.appendChild(this.makeCell(' ')); |
| 1081 return blankrow; | 1097 return blankrow; |
| 1082 } | 1098 } |
| 1083 }; | 1099 }; |
| 1084 | 1100 |
