// VD.js -- a viewdata terminal emulator // constructor function VD(wd, ht, scr_id, initscr) { var r; var c; var scr = document.getElementById(scr_id); this.wd_ = wd; this.ht_ = ht; this.text_ = new Array(ht); for (r = 0; r < ht; ++r) { this.text_[r] = new Array(wd); } this.scr_ = scr; this.cursor_vis_ = true; this.reveal_ = 0; this.getch_isr_ = undefined; this.grab_events_ = false; this.key_buf_ = []; this.esc_state_ = 0; this.col_ = 0; this.row_ = 0; // Internal debug setting. if (typeof initscr !== "undefined") { this.write("\x0c" + this.txthash(initscr) + "\x14\x1e"); } else { this.write("\x0c\x11\x1e"); } } VD.A_REVERSE = 2; VD.A_BLINK = 4; VD.browser_ie_ = (navigator.appName.indexOf("Microsoft") != -1); VD.browser_opera_ = (navigator.appName.indexOf("Opera") != -1); // class variables VD.the_vt_ = undefined; // var blinks = document.getElementsById('blink'); // var visibility = 'hidden'; // window.setInterval(function() { // for (var i = blinks.length - 1; i >= 0; i--) { // blinks[i].style.visibility = visibility; // } // visibility = (visibility === 'visible') ? 'hidden' : 'visible'; // }, 250); // object methods VD.prototype.html_colours_ = function(at_fg, at_bg, at_mode) { var co0, co1; if (at_mode & VD.A_REVERSE) { co0 = 'ff'; co1 = '00'; } else { co0 = '00'; co1 = 'ff'; } return { f: '#' + (at_fg & 1 ? co1 : co0) + (at_fg & 2 ? co1 : co0) + (at_fg & 4 ? co1 : co0), b: '#' + (at_bg & 1 ? co1 : co0) + (at_bg & 2 ? co1 : co0) + (at_bg & 4 ? co1 : co0) }; } VD.prototype.txthash = function(hashstring) { var currentcode = 0, stuff = ""; if ( hashstring.indexOf(":") > -1 ) var hashstring = hashstring.split(":")[1]; hashstring = hashstring.substring(0, 1120); for ( var p = 0; p < hashstring.length; p++ ) { var pc = hashstring.charAt(p); var pc_dec = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" .indexOf(hashstring.charAt(p)); // b is the bit in the 6-bit base-64 character. for ( var b = 0; b < 6; b++ ) { // The current bit posiiton in the character being // written to. var charbit = ( 6*p + b ) % 7; // The bit value (set or unset) of the bit we're // reading from. var b64bit = pc_dec & ( 1 << ( 5 - b ) ); if ( b64bit > 0 ) { b64bit = 1; } // Update the current code. currentcode |= b64bit << ( 6 - charbit ); // If we've reached the end of this character cell // and it's the last bit in the character we're // writing to, set the character code or place the // code. if ( charbit == 6 ) { if (currentcode < 32) { stuff += '\x1b'; currentcode += 64; } stuff += String.fromCharCode(currentcode); currentcode = 0; } } } return stuff; } VD.prototype.clear = function() { this.row_ = this.col_ = 0; for (r = 0; r < this.ht_; ++r) { for (c = 0; c < this.wd_; ++c) { this.text_[r][c] = ' '; } } } VD.prototype.curs_set = function(vis, grab, eventist) { if (vis !== undefined) this.cursor_vis_ = (vis > 0); } VD.prototype.getch = function(isr) { // this.refresh(); this.getch_isr_ = isr; setTimeout(VD.go_getch_, 0); } VD.go_getch_ = function VD_go_getch() { var vt = VD.the_vt_; if (vt === undefined) return; var isr = vt.getch_isr_; vt.getch_isr_ = undefined; if (isr === undefined) return; var ch = vt.key_buf_.shift(); if (ch === undefined) { vt.getch_isr_ = isr; return; } isr(ch, vt); } VD.prototype.move = function(r, c) { if (r < 0) r = 0; else if (r >= this.ht_) r = this.ht_ - 1; if (c < 0) c = 0; else if (c >= this.wd_) c = this.wd_ - 1; this.row_ = r; this.col_ = c; } VD.prototype.refresh = function() { var r, c, stuff = "", start_tag = "", end_tag = "", ch, chtype, pair, cr, cc, ht, wd, cv, n_fg, n_bg, n_mode, n_type, a_fg=-1, a_bg=-1, a_mode=-1, n_dblnextline=-2, n_heldchar, n_heldtype; ht = this.ht_; wd = this.wd_; cr = this.row_; cc = this.col_; cv = this.cursor_vis_; // var innerHTML = this.scr_.innerHTML; if (cc >= wd) cc = wd - 1; for (r = 0; r < ht; ++r) { if (r > 0) { stuff += '\n'; } n_fg = 7; n_bg = 0; n_mode = 0; n_type = 0; n_heldchar = 32; n_heldtype = 0; if (n_dblnextline+1 < r) n_dblnextline = -2; for (c = 0; c < wd; ++c) { if (cv && r == cr && c == cc) { // Draw the cursor here. n_mode |= VD.A_REVERSE; } else { n_mode &= ~VD.A_REVERSE; } if (r == n_dblnextline+1) { ch = this.text_[r-1][c].charCodeAt(0); } else { ch = this.text_[r][c].charCodeAt(0); } ctrlch = ch; // set-at if (ctrlch == 0x89) // steady n_mode &= ~VD.A_BLINK; if (ctrlch == 0x8c) { // normal if (n_type & 8) { n_heldchar = 32; n_heldtype = 0; } n_type &= ~8; } if (ctrlch == 0x98 && this.reveal_ == false) // conceal n_type |= 16; if (ctrlch == 0x99) // contig n_type &= ~1; if (ctrlch == 0x9a) // sep n_type |= 1; if (ctrlch == 0x9c) // black n_bg = 0; if (ctrlch == 0x9d) // newback n_bg = n_fg; if (ctrlch == 0x9e) // hold n_type |= 32; // // If the attributes changed, make a new span. if (n_mode != a_mode || n_fg != a_fg || n_bg != a_bg) { stuff += end_tag; pair = this.html_colours_(n_fg, n_bg, n_mode); stuff += '' end_tag = ""; if (n_mode & VD.A_BLINK) { stuff += ''; end_tag += ""; } a_fg = n_fg; a_bg = n_bg; a_mode = n_mode; } chtype = n_type; if (ch >= 128) { if (n_type & 32) { ch = n_heldchar; chtype = n_heldtype; } else { ch = 32; } } if (ch == 127) { if (n_type & 2) { ch = 0xe23f+((chtype & 1)*0xc0); n_heldchar = 127; } else { ch = 0xb6; } } if (n_type & 2) { if ( ch >= 0x20 && ch <= 0x3f) { n_heldchar = ch; ch = ch+0xe1e0+((chtype & 1)*0xc0); } else if ( ch >= 0x60 && ch <= 0x7e) { n_heldchar = ch; ch = ch+0xe1c0+((chtype & 1)*0xc0); } } if (n_type & 16) ch = 32; n_heldtype = chtype; switch (ch) { case 32: ch = 0xa0; break; case 0x7e: ch = 0xf7; break; case 0x5f: // _ ch = 0x23; break // # case 123: // { ch = 0xbc; break case 92: // \ ch = 0xbd; break case 125: // } ch = 0xbe; break case 0x23: // # ch = 0xa3; break } if (n_type & 8) { if (ch < 0x100) { ch = ch+0xe000+((r==n_dblnextline+1)*0x100); } else { ch = ch+0x40+((r==n_dblnextline+1)*0x40) } } else if (r==n_dblnextline+1) { ch = 0xa0; } stuff += "&#x" + ch.toString(16) + ";" // set-after if (ctrlch >= 129 && ctrlch <= 135) { if (n_type & 2) { n_heldchar=32; n_heldtype=0; } n_fg = ctrlch-128; n_type &= ~2; n_type &= ~16; } if (ctrlch >= 145 && ctrlch <= 151) { if (n_type & 2 == 0) { n_heldchar=32; n_heldtype=0; } n_fg = ctrlch-144; n_type |= 2; n_type &= ~16; } if (ctrlch == 0x88) // flash n_mode |= VD.A_BLINK; if (ctrlch == 0x8d) { // double if (n_type & 8 == 0) { n_heldchar = 32; n_heldtype = 0; } n_type |= 8; if (n_dblnextline < 0) n_dblnextline = r; } if (ctrlch == 0x9f) // release n_type &= ~32; // } } stuff += end_tag this.scr_.innerHTML = "" + stuff + "\n"; } VD.prototype.write = function(stuff) { var ch, i; for (i = 0; i < stuff.length; ++i) { ch = stuff.charCodeAt(i); if (this.esc_state_ && ch > 31) ch = (ch % 32) + 128; this.esc_state_ = 0; switch (ch) { case 8: if (this.col_ != 0) { --this.col_; } else { this.col_ = this.wd_-1; if (this.row_ == 0) { this.row_ = this.ht_-1; } else { --this.row_; } } break; case 9: if (this.col_ >= this.wd_) { this.col_ = 0; if (this.row_ == this.ht_-1) { this.row_ = 0; } else { ++this.row_; } } else { ++this.col_; } break; case 10: if (this.row_ >= this.ht_-1) { this.row_ = 0; } else { ++this.row_; } break; case 11: if (this.row_ == 0) { this.row_ = this.ht_-1; } else { --this.row_; } break; case 12: this.clear(); this.move(0, 0); this.reveal_ = false; break; case 13: this.col_ = 0; break; case 17: this.curs_set(1); break; case 20: this.curs_set(0); break; case 27: this.esc_state_ = 1; break; case 30: this.move(0, 0); break; default: if (ch > 31) { this.text_[this.row_][this.col_] = String.fromCharCode(ch); if (this.col_ >= this.wd_-1) { this.col_ = 0; if (this.row_ >= this.ht_-1) { this.row_ = 0; } else { ++this.row_; } } else { ++this.col_; } } } } this.refresh(); }