converted the websockify include symbolic link in the test cases to static files so docker build will success, Docker does not follow symbolic links with ADD/COPY commands on windows

This commit is contained in:
Timothy Lin 2018-11-27 13:14:31 -05:00
parent f0bdb0a621
commit 89b665f4fa
8 changed files with 2533 additions and 1 deletions

View File

@ -1 +0,0 @@
../include

919
tests/include/VT100.js Normal file
View File

@ -0,0 +1,919 @@
// VT100.js -- a text terminal emulator in JavaScript with a ncurses-like
// interface and a POSIX-like interface. (The POSIX-like calls are
// implemented on top of the ncurses-like calls, not the other way round.)
//
// Released under the GNU LGPL v2.1, by Frank Bi <bi@zompower.tk>
//
// 2007-08-12 - refresh():
// - factor out colour code to html_colours_()
// - fix handling of A_REVERSE | A_DIM
// - simplify initial <br /> output code
// - fix underlining colour
// - fix attron() not to turn off attributes
// - decouple A_STANDOUT and A_BOLD
// 2007-08-11 - getch() now calls refresh()
// 2007-08-06 - Safari compat fix -- turn '\r' into '\n' for onkeypress
// 2007-08-05 - Opera compat fixes for onkeypress
// 2007-07-30 - IE compat fixes:
// - change key handling code
// - add <br />...<br />&nbsp; so that 1st and last lines align
// 2007-07-28 - change wrapping behaviour -- writing at the right edge no
// longer causes the cursor to immediately wrap around
// - add <b>...</b> to output to make A_STANDOUT stand out more
// - add handling of backspace, tab, return keys
// - fix doc. of VT100() constructor
// - change from GPL to LGPL
// 2007-07-09 - initial release
//
// class VT100
// A_NORMAL, A_UNDERLINE, A_REVERSE, A_BLINK, A_DIM, A_BOLD, A_STANDOUT
// =class constants=
// Attribute constants.
// VT100(wd, ht, scr_id) =constructor=
// Creates a virtual terminal with width `wd', and
// height `ht'. The terminal will be displayed between
// <pre>...</pre> tags which have element ID `scr_id'.
// addch(ch [, attr])
// Writes out the character `ch'. If `attr' is given,
// it specifies the attributes for the character,
// otherwise the current attributes are used.
// addstr(stuff) Writes out the string `stuff' using the current
// attributes.
// attroff(mode) Turns off any current options given in mode.
// attron(mode) Turns on any options given in mode.
// attrset(mode) Sets the current options to mode.
// bkgdset(attr) Sets the background attributes to attr.
// clear() Clears the terminal using the background attributes,
// and homes the cursor.
// clrtobol() Clears the portion of the terminal from the cursor
// to the bottom.
// clrtoeol() Clears the portion of the current line after the
// cursor.
// curs_set(vis [, grab])
// If `vis' is 0, makes the cursor invisible; otherwise
// make it visible. If `grab' is given and true, starts
// capturing keyboard events (for `getch()'); if given
// and false, stops capturing events.
// echo() Causes key strokes to be automatically echoed on the
// terminal.
// erase() Same as `clear()'.
// getch(isr) Arranges to call `isr' when a key stroke is
// received. The received character and the terminal
// object are passed as arguments to `isr'.
// getmaxyx() Returns an associative array with the maximum row
// (`y') and column (`x') numbers for the terminal.
// getyx() Returns an associative array with the current row
// (`y') and column (`x') of the cursor.
// move(r, c) Moves the cursor to row `r', column `c'.
// noecho() Stops automatically echoing key strokes.
// refresh() Updates the display.
// scroll() Scrolls the terminal up one line.
// standend() Same as `attrset(VT100.A_NORMAL)'.
// standout() Same as `attron(VT100.A_STANDOUT)'.
// write(stuff) Writes `stuff' to the terminal and immediately
// updates the display; (some) escape sequences are
// interpreted and acted on.
// constructor
function VT100(wd, ht, scr_id)
{
var r;
var c;
var scr = document.getElementById(scr_id);
this.wd_ = wd;
this.ht_ = ht;
this.scrolled_ = 0;
this.bkgd_ = {
mode: VT100.A_NORMAL,
fg: VT100.COLOR_WHITE,
bg: VT100.COLOR_BLACK
};
this.c_attr_ = {
mode: VT100.A_NORMAL,
fg: VT100.COLOR_WHITE,
bg: VT100.COLOR_BLACK
};
this.text_ = new Array(ht);
this.attr_ = new Array(ht);
for (r = 0; r < ht; ++r) {
this.text_[r] = new Array(wd);
this.attr_[r] = new Array(wd);
}
this.scr_ = scr;
this.cursor_vis_ = true;
this.grab_events_ = false;
this.getch_isr_ = undefined;
this.key_buf_ = [];
this.echo_ = true;
this.esc_state_ = 0;
// Internal debug setting.
this.debug_ = 0;
this.clear();
this.refresh();
}
// public constants -- colours and colour pairs
VT100.COLOR_BLACK = 0;
VT100.COLOR_BLUE = 1;
VT100.COLOR_GREEN = 2;
VT100.COLOR_CYAN = 3;
VT100.COLOR_RED = 4;
VT100.COLOR_MAGENTA = 5;
VT100.COLOR_YELLOW = 6;
VT100.COLOR_WHITE = 7;
VT100.COLOR_PAIRS = 256;
VT100.COLORS = 8;
// public constants -- attributes
VT100.A_NORMAL = 0;
VT100.A_UNDERLINE = 1;
VT100.A_REVERSE = 2;
VT100.A_BLINK = 4;
VT100.A_DIM = 8;
VT100.A_BOLD = 16;
VT100.A_STANDOUT = 32;
VT100.A_PROTECT = VT100.A_INVIS = 0; // ?
// other public constants
VT100.TABSIZE = 8;
// private constants
VT100.ATTR_FLAGS_ = VT100.A_UNDERLINE | VT100.A_REVERSE | VT100.A_BLINK |
VT100.A_DIM | VT100.A_BOLD | VT100.A_STANDOUT |
VT100.A_PROTECT | VT100.A_INVIS;
VT100.COLOR_SHIFT_ = 6;
VT100.browser_ie_ = (navigator.appName.indexOf("Microsoft") != -1);
VT100.browser_opera_ = (navigator.appName.indexOf("Opera") != -1);
// class variables
VT100.the_vt_ = undefined;
// class methods
// this is actually an event handler
VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event)
{
var vt = VT100.the_vt_, ch;
if (vt === undefined)
return true;
if (VT100.browser_ie_ || VT100.browser_opera_) {
ch = event.keyCode;
if (ch == 13)
ch = 10;
else if (ch > 255 || (ch < 32 && ch != 8))
return true;
ch = String.fromCharCode(ch);
} else {
ch = event.charCode;
//dump("ch: " + ch + "\n");
//dump("ctrl?: " + event.ctrlKey + "\n");
vt.debug("onkeypress:: keyCode: " + event.keyCode + ", ch: " + event.charCode);
if (ch) {
if (ch > 255)
return true;
if (event.ctrlKey && event.shiftKey) {
// Don't send the copy/paste commands.
var charStr = String.fromCharCode(ch);
if (charStr == 'C' || charStr == 'V') {
return false;
}
}
if (event.ctrlKey) {
ch = String.fromCharCode(ch - 96);
} else {
ch = String.fromCharCode(ch);
if (ch == '\r')
ch = '\n';
}
} else {
switch (event.keyCode) {
case event.DOM_VK_BACK_SPACE:
ch = '\b';
break;
case event.DOM_VK_TAB:
ch = '\t';
// Stop tab from moving to another element.
event.preventDefault();
break;
case event.DOM_VK_RETURN:
case event.DOM_VK_ENTER:
ch = '\n';
break;
case event.DOM_VK_UP:
ch = '\x1b[A';
break;
case event.DOM_VK_DOWN:
ch = '\x1b[B';
break;
case event.DOM_VK_RIGHT:
ch = '\x1b[C';
break;
case event.DOM_VK_LEFT:
ch = '\x1b[D';
break;
case event.DOM_VK_DELETE:
ch = '\x1b[3~';
break;
case event.DOM_VK_HOME:
ch = '\x1b[H';
break;
case event.DOM_VK_ESCAPE:
ch = '\x1bc';
break;
default:
return true;
}
}
}
vt.key_buf_.push(ch);
setTimeout(VT100.go_getch_, 0);
return false;
}
// this is actually an event handler
VT100.handle_onkeydown_ = function VT100_handle_onkeydown()
{
var vt = VT100.the_vt_, ch;
switch (event.keyCode) {
case 8:
ch = '\b'; break;
default:
return true;
}
vt.key_buf_.push(ch);
setTimeout(VT100.go_getch_, 0);
return false;
}
VT100.go_getch_ = function VT100_go_getch()
{
var vt = VT100.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;
}
if (vt.echo_)
vt.addch(ch);
isr(ch, vt);
}
// object methods
VT100.prototype.may_scroll_ = function()
{
var ht = this.ht_, cr = this.row_;
while (cr >= ht) {
this.scroll();
--cr;
}
this.row_ = cr;
}
VT100.prototype.html_colours_ = function(attr)
{
var fg, bg, co0, co1;
fg = attr.fg;
bg = attr.bg;
switch (attr.mode & (VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD)) {
case 0:
case VT100.A_DIM | VT100.A_BOLD:
co0 = '00'; co1 = 'c0';
break;
case VT100.A_BOLD:
co0 = '00'; co1 = 'ff';
break;
case VT100.A_DIM:
if (fg == VT100.COLOR_BLACK)
co0 = '40';
else
co0 = '00';
co1 = '40';
break;
case VT100.A_REVERSE:
case VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD:
co0 = 'c0'; co1 = '40';
break;
case VT100.A_REVERSE | VT100.A_BOLD:
co0 = 'c0'; co1 = '00';
break;
default:
if (fg == VT100.COLOR_BLACK)
co0 = '80';
else
co0 = 'c0';
co1 = 'c0';
}
return {
f: '#' + (fg & 4 ? co1 : co0) +
(fg & 2 ? co1 : co0) +
(fg & 1 ? co1 : co0),
b: '#' + (bg & 4 ? co1 : co0) +
(bg & 2 ? co1 : co0) +
(bg & 1 ? co1 : co0)
};
}
VT100.prototype.addch = function(ch, attr)
{
var cc = this.col_;
this.debug("addch:: ch: " + ch + ", attr: " + attr);
switch (ch) {
case '\b':
if (cc != 0)
--cc;
break;
case '\n':
++this.row_;
cc = 0;
this.clrtoeol();
this.may_scroll_();
break;
case '\r':
this.may_scroll_();
cc = 0;
break;
case '\t':
this.may_scroll_();
cc += VT100.TABSIZE - cc % VT100.TABSIZE;
if (cc >= this.wd_) {
++this.row_;
cc -= this.wd_;
}
break;
default:
if (attr === undefined)
attr = this._cloneAttr(this.c_attr_);
if (cc >= this.wd_) {
++this.row_;
cc = 0;
}
this.may_scroll_();
this.text_[this.row_][cc] = ch;
this.attr_[this.row_][cc] = attr;
++cc;
}
this.col_ = cc;
}
VT100.prototype.addstr = function(stuff)
{
for (var i = 0; i < stuff.length; ++i)
this.addch(stuff.charAt(i));
}
VT100.prototype._cloneAttr = function VT100_cloneAttr(a)
{
return {
mode: a.mode,
fg: a.fg,
bg: a.bg
};
}
VT100.prototype.attroff = function(a)
{
//dump("attroff: " + a + "\n");
a &= VT100.ATTR_FLAGS_;
this.c_attr_.mode &= ~a;
}
VT100.prototype.attron = function(a)
{
//dump("attron: " + a + "\n");
a &= VT100.ATTR_FLAGS_;
this.c_attr_.mode |= a;
}
VT100.prototype.attrset = function(a)
{
//dump("attrset: " + a + "\n");
this.c_attr_.mode = a;
}
VT100.prototype.fgset = function(fg)
{
//dump("fgset: " + fg + "\n");
this.c_attr_.fg = fg;
}
VT100.prototype.bgset = function(bg)
{
//dump("bgset: " + bg + "\n");
if (bg !== 0) {
this.warn("bgset: " + bg + "\n");
}
this.c_attr_.bg = bg;
}
VT100.prototype.bkgdset = function(a)
{
this.bkgd_ = a;
}
VT100.prototype.clear = function()
{
this.debug("clear");
this.row_ = this.col_ = 0;
this.scrolled_ = 0;
for (r = 0; r < this.ht_; ++r) {
for (c = 0; c < this.wd_; ++c) {
this.text_[r][c] = ' ';
this.attr_[r][c] = this._cloneAttr(this.bkgd_);
}
}
}
VT100.prototype.clrtobot = function()
{
this.debug("clrtobot, row: " + this.row_);
var ht = this.ht_;
var wd = this.wd_;
this.clrtoeol();
for (var r = this.row_ + 1; r < ht; ++r) {
for (var c = 0; c < wd; ++c) {
this.text_[r][c] = ' ';
this.attr_[r][c] = this.bkgd_;
}
}
}
VT100.prototype.clrtoeol = function()
{
this.debug("clrtoeol, col: " + this.col_);
var r = this.row_;
if (r >= this.ht_)
return;
for (var c = this.col_; c < this.wd_; ++c) {
this.text_[r][c] = ' ';
this.attr_[r][c] = this.bkgd_;
}
}
VT100.prototype.clearpos = function(row, col)
{
this.debug("clearpos (" + row + ", " + col + ")");
if (row < 0 || row >= this.ht_)
return;
if (col < 0 || col >= this.wd_)
return;
this.text_[row][col] = ' ';
this.attr_[row][col] = this.bkgd_;
}
VT100.prototype.curs_set = function(vis, grab, eventist)
{
this.debug("curs_set:: vis: " + vis + ", grab: " + grab);
if (vis !== undefined)
this.cursor_vis_ = (vis > 0);
if (eventist === undefined)
eventist = window;
if (grab === true || grab === false) {
if (grab === this.grab_events_)
return;
if (grab) {
this.grab_events_ = true;
VT100.the_vt_ = this;
eventist.addEventListener("keypress", VT100.handle_onkeypress_, false);
if (VT100.browser_ie_)
document.onkeydown = VT100.handle_onkeydown_;
} else {
eventist.removeEventListener("keypress", VT100.handle_onkeypress_, false);
if (VT100.browser_ie_)
document.onkeydown = VT100.handle_onkeydown_;
this.grab_events_ = false;
VT100.the_vt_ = undefined;
}
}
}
VT100.prototype.echo = function()
{
this.debug("echo on");
this.echo_ = true;
}
VT100.prototype.erase = VT100.prototype.clear;
VT100.prototype.getch = function(isr)
{
this.debug("getch");
this.refresh();
this.getch_isr_ = isr;
setTimeout(VT100.go_getch_, 0);
}
VT100.prototype.getmaxyx = function()
{
return { y: this.ht_ - 1, x: this.wd_ - 1 };
}
VT100.prototype.getyx = function()
{
return { y: this.row_, x: this.col_ };
}
VT100.prototype.move = function(r, c)
{
this.debug("move: (" + 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;
}
VT100.prototype.noecho = function()
{
this.debug("echo off");
this.echo_ = false;
}
VT100.prototype.refresh = function()
{
this.debug("refresh");
var r, c, stuff = "", start_tag = "", end_tag = "", at = -1, n_at, ch,
pair, cr, cc, ht, wd, cv, added_end_tag;
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';
}
for (c = 0; c < wd; ++c) {
added_end_tag = false;
n_at = this.attr_[r][c];
if (cv && r == cr && c == cc) {
// Draw the cursor here.
n_at = this._cloneAttr(n_at);
n_at.mode ^= VT100.A_REVERSE;
}
// If the attributes changed, make a new span.
if (n_at.mode != at.mode || n_at.fg != at.fg || n_at.bg != at.bg) {
if (c > 0) {
stuff += end_tag;
}
start_tag = "";
end_tag = "";
if (n_at.mode & VT100.A_BLINK) {
start_tag = "<blink>";
end_tag = "</blink>" + end_tag;
}
if (n_at.mode & VT100.A_STANDOUT)
n_at.mode |= VT100.A_BOLD;
pair = this.html_colours_(n_at);
start_tag += '<span style="color:' + pair.f +
';background-color:' + pair.b;
if (n_at.mode & VT100.A_UNDERLINE)
start_tag += ';text-decoration:underline';
start_tag += ';">';
stuff += start_tag;
end_tag = "</span>" + end_tag;
at = n_at;
added_end_tag = true;
} else if (c == 0) {
stuff += start_tag;
}
ch = this.text_[r][c];
switch (ch) {
case '&':
stuff += '&amp;'; break;
case '<':
stuff += '&lt;'; break;
case '>':
stuff += '&gt;'; break;
case ' ':
//stuff += '&nbsp;'; break;
stuff += ' '; break;
default:
stuff += ch;
}
}
if (!added_end_tag)
stuff += end_tag;
}
this.scr_.innerHTML = "<b>" + stuff + "</b>\n";
}
VT100.prototype.scroll = function()
{
this.scrolled_ += 1;
this.debug("scrolled: " + this.scrolled_);
var n_text = this.text_[0], n_attr = this.attr_[0],
ht = this.ht_, wd = this.wd_;
for (var r = 1; r < ht; ++r) {
this.text_[r - 1] = this.text_[r];
this.attr_[r - 1] = this.attr_[r];
}
this.text_[ht - 1] = n_text;
this.attr_[ht - 1] = n_attr;
for (var c = 0; c < wd; ++c) {
n_text[c] = ' ';
n_attr[c] = this.bkgd_;
}
}
VT100.prototype.standend = function()
{
//this.debug("standend");
this.attrset(0);
}
VT100.prototype.standout = function()
{
//this.debug("standout");
this.attron(VT100.A_STANDOUT);
}
VT100.prototype.write = function(stuff)
{
var ch, x, r, c, i, j, yx, myx;
for (i = 0; i < stuff.length; ++i) {
ch = stuff.charAt(i);
if (ch == '\x0D') {
this.debug("write:: ch: " + ch.charCodeAt(0) + ", '\\x0D'");
} else {
this.debug("write:: ch: " + ch.charCodeAt(0) + ", '" + (ch == '\x1b' ? "ESC" : ch) + "'");
}
//dump("ch: " + ch.charCodeAt(0) + ", '" + (ch == '\x1b' ? "ESC" : ch) + "'\n");
switch (ch) {
case '\x00':
case '\x7f':
case '\x07': /* bell, ignore it */
this.debug("write:: ignoring bell character: " + ch);
continue;
case '\a':
case '\b':
case '\t':
case '\r':
this.addch(ch);
continue;
case '\n':
case '\v':
case '\f': // what a mess
yx = this.getyx();
myx = this.getmaxyx();
if (yx.y >= myx.y) {
this.scroll();
this.move(myx.y, 0);
} else
this.move(yx.y + 1, 0);
continue;
case '\x18':
case '\x1a':
this.esc_state_ = 0;
this.debug("write:: set escape state: 0");
continue;
case '\x1b':
this.esc_state_ = 1;
this.debug("write:: set escape state: 1");
continue;
case '\x9b':
this.esc_state_ = 2;
this.debug("write:: set escape state: 2");
continue;
}
// not a recognized control character
switch (this.esc_state_) {
case 0: // not in escape sequence
this.addch(ch);
break;
case 1: // just saw ESC
switch (ch) {
case '[':
this.esc_state_ = 2;
this.debug("write:: set escape state: 2");
break;
case '=':
/* Set keypade mode (ignored) */
this.debug("write:: set keypade mode: ignored");
this.esc_state_ = 0;
break;
case '>':
/* Reset keypade mode (ignored) */
this.debug("write:: reset keypade mode: ignored");
this.esc_state_ = 0;
break;
case 'H':
/* Set tab at cursor column (ignored) */
this.debug("write:: set tab cursor column: ignored");
this.esc_state_ = 0;
break;
}
break;
case 2: // just saw CSI
switch (ch) {
case 'K':
/* Erase in Line */
this.esc_state_ = 0;
this.clrtoeol();
continue;
case 'H':
/* Move to (0,0). */
this.esc_state_ = 0;
this.move(0, 0);
continue;
case 'J':
/* Clear to the bottom. */
this.esc_state_ = 0;
this.clrtobot();
continue;
case '?':
/* Special VT100 mode handling. */
this.esc_state_ = 5;
this.debug("write:: special vt100 mode");
continue;
}
// Drop through to next case.
this.csi_parms_ = [0];
this.debug("write:: set escape state: 3");
this.esc_state_ = 3;
case 3: // saw CSI and parameters
switch (ch) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
x = this.csi_parms_.pop();
this.csi_parms_.push(x * 10 + ch * 1);
this.debug("csi_parms_: " + this.csi_parms_);
continue;
case ';':
if (this.csi_parms_.length < 17)
this.csi_parms_.push(0);
continue;
}
this.esc_state_ = 0;
switch (ch) {
case 'A':
// Cursor Up <ESC>[{COUNT}A
this.move(this.row_ - Math.max(1, this.csi_parms_[0]),
this.col_);
break;
case 'B':
// Cursor Down <ESC>[{COUNT}B
this.move(this.row_ + Math.max(1, this.csi_parms_[0]),
this.col_);
break;
case 'C':
// Cursor Forward <ESC>[{COUNT}C
this.move(this.row_,
this.col_ + Math.max(1, this.csi_parms_[0]));
break;
case 'c':
this.warn("write:: got TERM query");
break;
case 'D':
// Cursor Backward <ESC>[{COUNT}D
this.move(this.row_,
this.col_ - Math.max(1, this.csi_parms_[0]));
break;
case 'f':
case 'H':
// Cursor Home <ESC>[{ROW};{COLUMN}H
this.csi_parms_.push(0);
this.move(this.csi_parms_[0] - 1,
this.csi_parms_[1] - 1);
break;
case 'J':
switch (this.csi_parms_[0]) {
case 0:
this.clrtobot();
break;
case 2:
this.clear();
this.move(0, 0);
}
break;
case 'm':
for (j=0; j<this.csi_parms_.length; ++j) {
x = this.csi_parms_[j];
switch (x) {
case 0:
this.standend();
this.fgset(this.bkgd_.fg);
this.bgset(this.bkgd_.bg);
break;
case 1:
this.attron(VT100.A_BOLD);
break;
case 30:
this.fgset(VT100.COLOR_BLACK);
break;
case 31:
this.fgset(VT100.COLOR_RED);
break;
case 32:
this.fgset(VT100.COLOR_GREEN);
break;
case 33:
this.fgset(VT100.COLOR_YELLOW);
break;
case 34:
this.fgset(VT100.COLOR_BLUE);
break;
case 35:
this.fgset(VT100.COLOR_MAGENTA);
break;
case 36:
this.fgset(VT100.COLOR_CYAN);
break;
case 37:
this.fgset(VT100.COLOR_WHITE);
break;
case 40:
this.bgset(VT100.COLOR_BLACK);
break;
case 41:
this.bgset(VT100.COLOR_RED);
break;
case 42:
this.bgset(VT100.COLOR_GREEN);
break;
case 44:
this.bgset(VT100.COLOR_YELLOW);
break;
case 44:
this.bgset(VT100.COLOR_BLUE);
break;
case 45:
this.bgset(VT100.COLOR_MAGENTA);
break;
case 46:
this.bgset(VT100.COLOR_CYAN);
break;
case 47:
this.bgset(VT100.COLOR_WHITE);
break;
}
}
break;
case 'r':
// 1,24r - set scrolling region (ignored)
break;
case '[':
this.debug("write:: set escape state: 4");
this.esc_state_ = 4;
break;
case 'g':
// 0g: clear tab at cursor (ignored)
// 3g: clear all tabs (ignored)
break;
default:
this.warn("write:: unknown command: " + ch);
this.csi_parms_ = [];
break;
}
break;
case 4: // saw CSI [
this.esc_state_ = 0; // gobble char.
break;
case 5: // Special mode handling, saw <ESC>[?
// Expect a number - the reset type
this.csi_parms_ = [ch];
this.esc_state_ = 6;
break;
case 6: // Reset mode handling, saw <ESC>[?1
// Expect a letter - the mode target, example:
// <ESC>[?1l : cursor key mode = cursor
// <ESC>[?1h : save current screen, create new empty
// screen and position at 0,0
// <ESC>[?5l : White on blk
// XXX: Ignored for now.
//dump("Saw reset mode: <ESC>[?" + this.csi_parms_[0] + ch + "\n");
this.esc_state_ = 0;
this.debug("write:: set escape state: 0");
break;
}
}
this.refresh();
}
VT100.prototype.debug = function(message) {
if (this.debug_) {
dump(message + "\n");
}
}
VT100.prototype.warn = function(message) {
dump(message + "\n");
}

99
tests/include/keysym.js Normal file
View File

@ -0,0 +1,99 @@
/*
* from noVNC: HTML5 VNC client
* Copyright (C) 2010 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt)
*/
/* Translate DOM key down/up event to keysym value */
function getKeysym(e) {
var evt, keysym;
evt = (e ? e : window.event);
/* Remap modifier and special keys */
switch ( evt.keyCode ) {
case 8 : keysym = 0xFF08; break; // BACKSPACE
case 9 : keysym = 0xFF09; break; // TAB
case 13 : keysym = 0xFF0D; break; // ENTER
case 27 : keysym = 0xFF1B; break; // ESCAPE
case 45 : keysym = 0xFF63; break; // INSERT
case 46 : keysym = 0xFFFF; break; // DELETE
case 36 : keysym = 0xFF50; break; // HOME
case 35 : keysym = 0xFF57; break; // END
case 33 : keysym = 0xFF55; break; // PAGE_UP
case 34 : keysym = 0xFF56; break; // PAGE_DOWN
case 37 : keysym = 0xFF51; break; // LEFT
case 38 : keysym = 0xFF52; break; // UP
case 39 : keysym = 0xFF53; break; // RIGHT
case 40 : keysym = 0xFF54; break; // DOWN
case 112 : keysym = 0xFFBE; break; // F1
case 113 : keysym = 0xFFBF; break; // F2
case 114 : keysym = 0xFFC0; break; // F3
case 115 : keysym = 0xFFC1; break; // F4
case 116 : keysym = 0xFFC2; break; // F5
case 117 : keysym = 0xFFC3; break; // F6
case 118 : keysym = 0xFFC4; break; // F7
case 119 : keysym = 0xFFC5; break; // F8
case 120 : keysym = 0xFFC6; break; // F9
case 121 : keysym = 0xFFC7; break; // F10
case 122 : keysym = 0xFFC8; break; // F11
case 123 : keysym = 0xFFC9; break; // F12
case 16 : keysym = 0xFFE1; break; // SHIFT
case 17 : keysym = 0xFFE3; break; // CONTROL
//case 18 : keysym = 0xFFE7; break; // Left Meta (Mac Option)
case 18 : keysym = 0xFFE9; break; // Left ALT (Mac Command)
default : keysym = evt.keyCode; break;
}
/* Remap symbols */
switch (keysym) {
case 186 : keysym = 59; break; // ; (IE)
case 187 : keysym = 61; break; // = (IE)
case 188 : keysym = 44; break; // , (Mozilla, IE)
case 109 : // - (Mozilla)
if (Util.Engine.gecko) {
keysym = 45; }
break;
case 189 : keysym = 45; break; // - (IE)
case 190 : keysym = 46; break; // . (Mozilla, IE)
case 191 : keysym = 47; break; // / (Mozilla, IE)
case 192 : keysym = 96; break; // ` (Mozilla, IE)
case 219 : keysym = 91; break; // [ (Mozilla, IE)
case 220 : keysym = 92; break; // \ (Mozilla, IE)
case 221 : keysym = 93; break; // ] (Mozilla, IE)
case 222 : keysym = 39; break; // ' (Mozilla, IE)
}
/* Remap shifted and unshifted keys */
if (!!evt.shiftKey) {
switch (keysym) {
case 48 : keysym = 41 ; break; // ) (shifted 0)
case 49 : keysym = 33 ; break; // ! (shifted 1)
case 50 : keysym = 64 ; break; // @ (shifted 2)
case 51 : keysym = 35 ; break; // # (shifted 3)
case 52 : keysym = 36 ; break; // $ (shifted 4)
case 53 : keysym = 37 ; break; // % (shifted 5)
case 54 : keysym = 94 ; break; // ^ (shifted 6)
case 55 : keysym = 38 ; break; // & (shifted 7)
case 56 : keysym = 42 ; break; // * (shifted 8)
case 57 : keysym = 40 ; break; // ( (shifted 9)
case 59 : keysym = 58 ; break; // : (shifted `)
case 61 : keysym = 43 ; break; // + (shifted ;)
case 44 : keysym = 60 ; break; // < (shifted ,)
case 45 : keysym = 95 ; break; // _ (shifted -)
case 46 : keysym = 62 ; break; // > (shifted .)
case 47 : keysym = 63 ; break; // ? (shifted /)
case 96 : keysym = 126; break; // ~ (shifted `)
case 91 : keysym = 123; break; // { (shifted [)
case 92 : keysym = 124; break; // | (shifted \)
case 93 : keysym = 125; break; // } (shifted ])
case 39 : keysym = 34 ; break; // " (shifted ')
}
} else if ((keysym >= 65) && (keysym <=90)) {
/* Remap unshifted A-Z */
keysym += 32;
}
return keysym;
}

364
tests/include/util.js Normal file
View File

@ -0,0 +1,364 @@
/*
* from noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
"use strict";
/*jslint bitwise: false, white: false */
/*global window, console, document, navigator, ActiveXObject */
// Globals defined here
var Util = {};
/*
* Make arrays quack
*/
Array.prototype.push8 = function (num) {
this.push(num & 0xFF);
};
Array.prototype.push16 = function (num) {
this.push((num >> 8) & 0xFF,
(num ) & 0xFF );
};
Array.prototype.push32 = function (num) {
this.push((num >> 24) & 0xFF,
(num >> 16) & 0xFF,
(num >> 8) & 0xFF,
(num ) & 0xFF );
};
// IE does not support map (even in IE9)
//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
if (!Array.prototype.map)
{
Array.prototype.map = function(fun /*, thisp*/)
{
var len = this.length;
if (typeof fun != "function")
throw new TypeError();
var res = new Array(len);
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
res[i] = fun.call(thisp, this[i], i, this);
}
return res;
};
}
//
// requestAnimationFrame shim with setTimeout fallback
//
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
/*
* ------------------------------------------------------
* Namespaced in Util
* ------------------------------------------------------
*/
/*
* Logging/debug routines
*/
Util._log_level = 'warn';
Util.init_logging = function (level) {
if (typeof level === 'undefined') {
level = Util._log_level;
} else {
Util._log_level = level;
}
if (typeof window.console === "undefined") {
if (typeof window.opera !== "undefined") {
window.console = {
'log' : window.opera.postError,
'warn' : window.opera.postError,
'error': window.opera.postError };
} else {
window.console = {
'log' : function(m) {},
'warn' : function(m) {},
'error': function(m) {}};
}
}
Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
switch (level) {
case 'debug': Util.Debug = function (msg) { console.log(msg); };
case 'info': Util.Info = function (msg) { console.log(msg); };
case 'warn': Util.Warn = function (msg) { console.warn(msg); };
case 'error': Util.Error = function (msg) { console.error(msg); };
case 'none':
break;
default:
throw("invalid logging type '" + level + "'");
}
};
Util.get_logging = function () {
return Util._log_level;
};
// Initialize logging level
Util.init_logging();
// Set configuration default for Crockford style function namespaces
Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) {
var getter, setter;
// Default getter function
getter = function (idx) {
if ((type in {'arr':1, 'array':1}) &&
(typeof idx !== 'undefined')) {
return cfg[v][idx];
} else {
return cfg[v];
}
};
// Default setter function
setter = function (val, idx) {
if (type in {'boolean':1, 'bool':1}) {
if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
val = false;
} else {
val = true;
}
} else if (type in {'integer':1, 'int':1}) {
val = parseInt(val, 10);
} else if (type === 'str') {
val = String(val);
} else if (type === 'func') {
if (!val) {
val = function () {};
}
}
if (typeof idx !== 'undefined') {
cfg[v][idx] = val;
} else {
cfg[v] = val;
}
};
// Set the description
api[v + '_description'] = desc;
// Set the getter function
if (typeof api['get_' + v] === 'undefined') {
api['get_' + v] = getter;
}
// Set the setter function with extra sanity checks
if (typeof api['set_' + v] === 'undefined') {
api['set_' + v] = function (val, idx) {
if (mode in {'RO':1, 'ro':1}) {
throw(v + " is read-only");
} else if ((mode in {'WO':1, 'wo':1}) &&
(typeof cfg[v] !== 'undefined')) {
throw(v + " can only be set once");
}
setter(val, idx);
};
}
// Set the default value
if (typeof defaults[v] !== 'undefined') {
defval = defaults[v];
} else if ((type in {'arr':1, 'array':1}) &&
(! (defval instanceof Array))) {
defval = [];
}
// Coerce existing setting to the right type
//Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]);
setter(defval);
};
// Set group of configuration defaults
Util.conf_defaults = function(cfg, api, defaults, arr) {
var i;
for (i = 0; i < arr.length; i++) {
Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1],
arr[i][2], arr[i][3], arr[i][4]);
}
};
/*
* Cross-browser routines
*/
// Dynamically load scripts without using document.write()
// Reference: http://unixpapa.com/js/dyna.html
//
// Handles the case where load_scripts is invoked from a script that
// itself is loaded via load_scripts. Once all scripts are loaded the
// window.onscriptsloaded handler is called (if set).
Util.get_include_uri = function() {
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
}
Util._loading_scripts = [];
Util._pending_scripts = [];
Util.load_scripts = function(files) {
var head = document.getElementsByTagName('head')[0], script,
ls = Util._loading_scripts, ps = Util._pending_scripts;
for (var f=0; f<files.length; f++) {
script = document.createElement('script');
script.type = 'text/javascript';
script.src = Util.get_include_uri() + files[f];
//console.log("loading script: " + script.src);
script.onload = script.onreadystatechange = function (e) {
while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
ls[0].readyState === 'complete')) {
// For IE, append the script to trigger execution
var s = ls.shift();
//console.log("loaded script: " + s.src);
head.appendChild(s);
}
if (!this.readyState ||
(Util.Engine.presto && this.readyState === 'loaded') ||
this.readyState === 'complete') {
if (ps.indexOf(this) >= 0) {
this.onload = this.onreadystatechange = null;
//console.log("completed script: " + this.src);
ps.splice(ps.indexOf(this), 1);
// Call window.onscriptsload after last script loads
if (ps.length === 0 && window.onscriptsload) {
window.onscriptsload();
}
}
}
};
// In-order script execution tricks
if (Util.Engine.trident) {
// For IE wait until readyState is 'loaded' before
// appending it which will trigger execution
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
ls.push(script);
} else {
// For webkit and firefox set async=false and append now
// https://developer.mozilla.org/en-US/docs/HTML/Element/script
script.async = false;
head.appendChild(script);
}
ps.push(script);
}
}
// Get DOM element position on page
Util.getPosition = function (obj) {
var x = 0, y = 0;
if (obj.offsetParent) {
do {
x += obj.offsetLeft;
y += obj.offsetTop;
obj = obj.offsetParent;
} while (obj);
}
return {'x': x, 'y': y};
};
// Get mouse event position in DOM element
Util.getEventPosition = function (e, obj, scale) {
var evt, docX, docY, pos;
//if (!e) evt = window.event;
evt = (e ? e : window.event);
evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
if (evt.pageX || evt.pageY) {
docX = evt.pageX;
docY = evt.pageY;
} else if (evt.clientX || evt.clientY) {
docX = evt.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
docY = evt.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}
pos = Util.getPosition(obj);
if (typeof scale === "undefined") {
scale = 1;
}
return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale};
};
// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
Util.addEvent = function (obj, evType, fn){
if (obj.attachEvent){
var r = obj.attachEvent("on"+evType, fn);
return r;
} else if (obj.addEventListener){
obj.addEventListener(evType, fn, false);
return true;
} else {
throw("Handler could not be attached");
}
};
Util.removeEvent = function(obj, evType, fn){
if (obj.detachEvent){
var r = obj.detachEvent("on"+evType, fn);
return r;
} else if (obj.removeEventListener){
obj.removeEventListener(evType, fn, false);
return true;
} else {
throw("Handler could not be removed");
}
};
Util.stopEvent = function(e) {
if (e.stopPropagation) { e.stopPropagation(); }
else { e.cancelBubble = true; }
if (e.preventDefault) { e.preventDefault(); }
else { e.returnValue = false; }
};
// Set browser engine versions. Based on mootools.
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
Util.Engine = {
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
//'presto': (function() {
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
'presto': (function() { return (!window.opera) ? false : true; }()),
'trident': (function() {
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
'webkit': (function() {
try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
//'webkit': (function() {
// return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()),
'gecko': (function() {
return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }())
};
if (Util.Engine.webkit) {
// Extract actual webkit version if available
Util.Engine.webkit = (function(v) {
var re = new RegExp('WebKit/([0-9\.]*) ');
v = (navigator.userAgent.match(re) || ['', v])[1];
return parseFloat(v, 10);
})(Util.Engine.webkit);
}

363
tests/include/websock.js Normal file
View File

@ -0,0 +1,363 @@
/*
* Websock: high-performance binary WebSockets
* Copyright (C) 2012 Joel Martin
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* Websock is similar to the standard WebSocket object but with extra
* buffer handling.
*
* Websock has built-in receive queue buffering; the message event
* does not contain actual data but is simply a notification that
* there is new data available. Several rQ* methods are available to
* read binary data off of the receive queue.
*/
/* [module]
* import Util from "./util";
*/
/*jslint browser: true, bitwise: true */
/*global Util*/
/* [module] export default */ function Websock() {
"use strict";
this._websocket = null; // WebSocket object
this._rQi = 0; // Receive queue index
this._rQlen = 0; // Next write position in the receive queue
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
this._rQmax = this._rQbufferSize / 8;
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue
this._sQbufferSize = 1024 * 10; // 10 KiB
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
this._sQlen = 0;
this._sQ = null; // Send queue
this._eventHandlers = {
'message': function () {},
'open': function () {},
'close': function () {},
'error': function () {}
};
};
(function () {
"use strict";
// this has performance issues in some versions Chromium, and
// doesn't gain a tremendous amount of performance increase in Firefox
// at the moment. It may be valuable to turn it on in the future.
var ENABLE_COPYWITHIN = false;
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
var typedArrayToString = (function () {
// This is only for PhantomJS, which doesn't like apply-ing
// with Typed Arrays
try {
var arr = new Uint8Array([1, 2, 3]);
String.fromCharCode.apply(null, arr);
return function (a) { return String.fromCharCode.apply(null, a); };
} catch (ex) {
return function (a) {
return String.fromCharCode.apply(
null, Array.prototype.slice.call(a));
};
}
})();
Websock.prototype = {
// Getters and Setters
get_sQ: function () {
return this._sQ;
},
get_rQ: function () {
return this._rQ;
},
get_rQi: function () {
return this._rQi;
},
set_rQi: function (val) {
this._rQi = val;
},
// Receive Queue
rQlen: function () {
return this._rQlen - this._rQi;
},
rQpeek8: function () {
return this._rQ[this._rQi];
},
rQshift8: function () {
return this._rQ[this._rQi++];
},
rQskip8: function () {
this._rQi++;
},
rQskipBytes: function (num) {
this._rQi += num;
},
// TODO(directxman12): test performance with these vs a DataView
rQshift16: function () {
return (this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
rQshift32: function () {
return (this._rQ[this._rQi++] << 24) +
(this._rQ[this._rQi++] << 16) +
(this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
rQshiftStr: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
var arr = new Uint8Array(this._rQ.buffer, this._rQi, len);
this._rQi += len;
return typedArrayToString(arr);
},
rQshiftBytes: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
this._rQi += len;
return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
},
rQshiftTo: function (target, len) {
if (len === undefined) { len = this.rQlen(); }
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ
target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
this._rQi += len;
},
rQwhole: function () {
return new Uint8Array(this._rQ.buffer, 0, this._rQlen);
},
rQslice: function (start, end) {
if (end) {
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
} else {
return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start);
}
},
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
// to be available in the receive queue. Return true if we need to
// wait (and possibly print a debug message), otherwise false.
rQwait: function (msg, num, goback) {
var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call
if (rQlen < num) {
if (goback) {
if (this._rQi < goback) {
throw new Error("rQwait cannot backup " + goback + " bytes");
}
this._rQi -= goback;
}
return true; // true means need more data
}
return false;
},
// Send Queue
flush: function () {
if (this._websocket.bufferedAmount !== 0) {
Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
}
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
this._websocket.send(this._encode_message());
this._sQlen = 0;
}
},
send: function (arr) {
this._sQ.set(arr, this._sQlen);
this._sQlen += arr.length;
this.flush();
},
send_string: function (str) {
this.send(str.split('').map(function (chr) {
return chr.charCodeAt(0);
}));
},
// Event Handlers
off: function (evt) {
this._eventHandlers[evt] = function () {};
},
on: function (evt, handler) {
this._eventHandlers[evt] = handler;
},
_allocate_buffers: function () {
this._rQ = new Uint8Array(this._rQbufferSize);
this._sQ = new Uint8Array(this._sQbufferSize);
},
init: function () {
this._allocate_buffers();
this._rQi = 0;
this._websocket = null;
},
open: function (uri, protocols) {
var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
this.init();
// IE, Edge and Firefox misbehave when protocols is
// undefined, converting it to a string rather than
// treating it as if it wasn't specified
if (protocols) {
this._websocket = new WebSocket(uri, protocols);
} else {
this._websocket = new WebSocket(uri);
}
this._websocket.binaryType = 'arraybuffer';
this._websocket.onmessage = this._recv_message.bind(this);
this._websocket.onopen = (function () {
Util.Debug('>> WebSock.onopen');
if (this._websocket.protocol) {
Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
}
this._eventHandlers.open();
Util.Debug("<< WebSock.onopen");
}).bind(this);
this._websocket.onclose = (function (e) {
Util.Debug(">> WebSock.onclose");
this._eventHandlers.close(e);
Util.Debug("<< WebSock.onclose");
}).bind(this);
this._websocket.onerror = (function (e) {
Util.Debug(">> WebSock.onerror: " + e);
this._eventHandlers.error(e);
Util.Debug("<< WebSock.onerror: " + e);
}).bind(this);
},
close: function () {
if (this._websocket) {
if ((this._websocket.readyState === WebSocket.OPEN) ||
(this._websocket.readyState === WebSocket.CONNECTING)) {
Util.Info("Closing WebSocket connection");
this._websocket.close();
}
this._websocket.onmessage = function (e) { return; };
}
},
// private methods
_encode_message: function () {
// Put in a binary arraybuffer
// according to the spec, you can send ArrayBufferViews with the send method
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
},
_expand_compact_rQ: function (min_fit) {
var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2;
if (resizeNeeded) {
if (!min_fit) {
// just double the size if we need to do compaction
this._rQbufferSize *= 2;
} else {
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8;
}
}
// we don't want to grow unboundedly
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
this._rQbufferSize = MAX_RQ_GROW_SIZE;
if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) {
throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
}
}
if (resizeNeeded) {
var old_rQbuffer = this._rQ.buffer;
this._rQmax = this._rQbufferSize / 8;
this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
} else {
if (ENABLE_COPYWITHIN) {
this._rQ.copyWithin(0, this._rQi);
} else {
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
}
}
this._rQlen = this._rQlen - this._rQi;
this._rQi = 0;
},
_decode_message: function (data) {
// push arraybuffer values onto the end
var u8 = new Uint8Array(data);
if (u8.length > this._rQbufferSize - this._rQlen) {
this._expand_compact_rQ(u8.length);
}
this._rQ.set(u8, this._rQlen);
this._rQlen += u8.length;
},
_recv_message: function (e) {
try {
this._decode_message(e.data);
if (this.rQlen() > 0) {
this._eventHandlers.message();
// Compact the receive queue
if (this._rQlen == this._rQi) {
this._rQlen = 0;
this._rQi = 0;
} else if (this._rQlen > this._rQmax) {
this._expand_compact_rQ();
}
} else {
Util.Debug("Ignoring empty message");
}
} catch (exc) {
var exception_str = "";
if (exc.name) {
exception_str += "\n name: " + exc.name + "\n";
exception_str += " message: " + exc.message + "\n";
}
if (typeof exc.description !== 'undefined') {
exception_str += " description: " + exc.description + "\n";
}
if (typeof exc.stack !== 'undefined') {
exception_str += exc.stack;
}
if (exception_str.length > 0) {
Util.Error("recv_message, caught exception: " + exception_str);
} else {
Util.Error("recv_message, caught exception: " + exc);
}
if (typeof exc.name !== 'undefined') {
this._eventHandlers.error(exc.name + ": " + exc.message);
} else {
this._eventHandlers.error(exc);
}
}
}
};
})();

216
tests/include/webutil.js Normal file
View File

@ -0,0 +1,216 @@
/*
* from noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
"use strict";
/*jslint bitwise: false, white: false */
/*global Util, window, document */
// Globals defined here
var WebUtil = {}, $D;
/*
* Simple DOM selector by ID
*/
if (!window.$D) {
window.$D = function (id) {
if (document.getElementById) {
return document.getElementById(id);
} else if (document.all) {
return document.all[id];
} else if (document.layers) {
return document.layers[id];
}
return undefined;
};
}
/*
* ------------------------------------------------------
* Namespaced in WebUtil
* ------------------------------------------------------
*/
// init log level reading the logging HTTP param
WebUtil.init_logging = function(level) {
if (typeof level !== "undefined") {
Util._log_level = level;
} else {
Util._log_level = (document.location.href.match(
/logging=([A-Za-z0-9\._\-]*)/) ||
['', Util._log_level])[1];
}
Util.init_logging();
};
WebUtil.dirObj = function (obj, depth, parent) {
var i, msg = "", val = "";
if (! depth) { depth=2; }
if (! parent) { parent= ""; }
// Print the properties of the passed-in object
for (i in obj) {
if ((depth > 1) && (typeof obj[i] === "object")) {
// Recurse attributes that are objects
msg += WebUtil.dirObj(obj[i], depth-1, parent + "." + i);
} else {
//val = new String(obj[i]).replace("\n", " ");
if (typeof(obj[i]) === "undefined") {
val = "undefined";
} else {
val = obj[i].toString().replace("\n", " ");
}
if (val.length > 30) {
val = val.substr(0,30) + "...";
}
msg += parent + "." + i + ": " + val + "\n";
}
}
return msg;
};
// Read a query string variable
WebUtil.getQueryVar = function(name, defVal) {
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)'),
match = document.location.href.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
};
/*
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
*/
// No days means only for this browser session
WebUtil.createCookie = function(name,value,days) {
var date, expires;
if (days) {
date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
expires = "; expires="+date.toGMTString();
}
else {
expires = "";
}
document.cookie = name+"="+value+expires+"; path=/";
};
WebUtil.readCookie = function(name, defaultValue) {
var i, c, nameEQ = name + "=", ca = document.cookie.split(';');
for(i=0; i < ca.length; i += 1) {
c = ca[i];
while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); }
}
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
};
WebUtil.eraseCookie = function(name) {
WebUtil.createCookie(name,"",-1);
};
/*
* Setting handling.
*/
WebUtil.initSettings = function(callback) {
var callbackArgs = Array.prototype.slice.call(arguments, 1);
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.get(function (cfg) {
WebUtil.settings = cfg;
console.log(WebUtil.settings);
if (callback) {
callback.apply(this, callbackArgs);
}
});
} else {
// No-op
if (callback) {
callback.apply(this, callbackArgs);
}
}
};
// No days means only for this browser session
WebUtil.writeSetting = function(name, value) {
if (window.chrome && window.chrome.storage) {
//console.log("writeSetting:", name, value);
if (WebUtil.settings[name] !== value) {
WebUtil.settings[name] = value;
window.chrome.storage.sync.set(WebUtil.settings);
}
} else {
localStorage.setItem(name, value);
}
};
WebUtil.readSetting = function(name, defaultValue) {
var value;
if (window.chrome && window.chrome.storage) {
value = WebUtil.settings[name];
} else {
value = localStorage.getItem(name);
}
if (typeof value === "undefined") {
value = null;
}
if (value === null && typeof defaultValue !== undefined) {
return defaultValue;
} else {
return value;
}
};
WebUtil.eraseSetting = function(name) {
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.remove(name);
delete WebUtil.settings[name];
} else {
localStorage.removeItem(name);
}
};
/*
* Alternate stylesheet selection
*/
WebUtil.getStylesheets = function() { var i, links, sheets = [];
links = document.getElementsByTagName("link");
for (i = 0; i < links.length; i += 1) {
if (links[i].title &&
links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
sheets.push(links[i]);
}
}
return sheets;
};
// No sheet means try and use value from cookie, null sheet used to
// clear all alternates.
WebUtil.selectStylesheet = function(sheet) {
var i, link, sheets = WebUtil.getStylesheets();
if (typeof sheet === 'undefined') {
sheet = 'default';
}
for (i=0; i < sheets.length; i += 1) {
link = sheets[i];
if (link.title === sheet) {
Util.Debug("Using stylesheet " + sheet);
link.disabled = false;
} else {
//Util.Debug("Skipping stylesheet " + link.title);
link.disabled = true;
}
}
return sheet;
};

235
tests/include/wsirc.js Normal file
View File

@ -0,0 +1,235 @@
/*
* WebSockets IRC client
* Copyright (C) 2011 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt)
*
* Includes VT100.js from:
* http://code.google.com/p/sshconsole
* Which was modified from:
* http://fzort.org/bi/o.php#vt100_js
* IRC Client protocol:
* http://www.faqs.org/rfcs/rfc2812.html
*/
function IRC(target, connect_callback, disconnect_callback) {
var that = {}, // Public API interface
vt100, ws, sQ = [],
state = "unconnected",
irc_nick, irc_channel,
termType = "VT100";
Array.prototype.pushStr = function (str) {
var n = str.length;
for (var i=0; i < n; i++) {
this.push(str.charCodeAt(i));
}
}
function do_send() {
if (sQ.length > 0) {
Util.Debug("Sending " + sQ);
ws.send(sQ);
sQ = [];
}
}
function do_recv() {
console.log(">> do_recv");
var rQ, rQi, i;
while (ws.rQlen() > 1) {
rQ = ws.get_rQ();
rQi = ws.get_rQi();
for (i = rQi; i < rQ.length; i++) {
if (rQ[i] === 10) {
break;
}
}
if (i >= rQ.length) {
// No line break found
break;
}
recvMsg(ws.rQshiftStr((i-rQi) + 1));
}
//console.log("<< do_recv");
}
// Handle an IRC message
function recvMsg(msg) {
Util.Debug(">> recvMsg('" + msg + "')");
var tokens = msg.split(' '), in_params = true,
prefix, command, params = [], trailing = [];
Util.Info(" tokens: " + tokens);
if (tokens[0].charAt(0) === ":") {
prefix = tokens.shift();
}
command = tokens.shift();
while (tokens.length > 0) {
if (tokens[0].charAt(0) === ":") {
in_params = false;
}
if (in_params) {
params.push(tokens.shift());
} else {
trailing.push(tokens.shift());
}
}
Util.Info(" prefix: " + prefix);
Util.Info(" command: " + command);
Util.Info(" params: " + params);
Util.Info(" trailing: " + trailing);
// Show raw received
vt100.write(msg);
switch (command) {
case "004":
state = "registered";
vt100.write("Joining channel #" + irc_channel);
sendCmd("JOIN #" + irc_channel);
break;
case "JOIN":
state = "joined";
vt100.write("Joined channel #" + irc_channel);
break;
}
Util.Debug("<< recvMsg('" + msg + "')");
}
function sendCmd(msg) {
Util.Info("Sending: " + msg);
sQ.pushStr(msg + "\r\n");
do_send();
}
that.sendMsg = function(msg) {
// TODO parse into message
sendCmd("PRIVMSG #" + irc_channel + " :" + msg);
}
that.connect = function(host, port, encrypt, nick, channel) {
var host = host,
port = port,
scheme = "ws://", uri;
irc_nick = nick;
irc_channel = channel;
Util.Debug(">> connect");
if ((!host) || (!port)) {
alert("must set host and port");
return false;
}
if (ws) {
ws.close();
}
if (encrypt) {
scheme = "wss://";
}
uri = scheme + host + ":" + port;
Util.Info("connecting to " + uri);
ws.open(uri);
Util.Debug("<< connect");
return true;
}
that.disconnect = function() {
Util.Debug(">> disconnect");
if (ws) {
ws.close();
}
disconnect_callback();
Util.Debug("<< disconnect");
}
function constructor() {
/* Initialize Websock object */
ws = new Websock();
ws.on('message', do_recv);
ws.on('open', function(e) {
Util.Info(">> WebSockets.onopen");
// Send registration commands
state = "connected";
sendCmd("NICK " + irc_nick);
// TODO: how to determine this?
sendCmd("USER joelm 0 * :Joel Martin");
connect_callback();
Util.Info("<< WebSockets.onopen");
});
ws.on('close', function(e) {
Util.Info(">> WebSockets.onclose");
that.disconnect();
Util.Info("<< WebSockets.onclose");
});
ws.on('error', function(e) {
Util.Info(">> WebSockets.onerror");
that.disconnect();
Util.Info("<< WebSockets.onerror");
});
/* Initialize the terminal emulator/renderer */
vt100 = new VT100(80, 24, target);
// Show cursor
vt100.curs_set(true, false);
/*
* Override VT100 I/O routines
*/
// Set handler for sending characters
vt100.getch(
function send_chr(chr, vt) {
var i;
Util.Debug(">> send_chr: " + chr);
for (i = 0; i < chr.length; i++) {
sQ.push(chr.charCodeAt(i));
}
do_send();
vt100.getch(send_chr);
}
);
vt100.debug = function(message) {
Util.Debug(message + "\n");
}
vt100.warn = function(message) {
Util.Warn(message + "\n");
}
vt100.curs_set = function(vis, grab, eventist)
{
this.debug("curs_set:: vis: " + vis + ", grab: " + grab);
if (vis !== undefined)
this.cursor_vis_ = (vis > 0);
}
return that;
}
return constructor(); // Return the public API interface
} // End of Telnet()

337
tests/include/wstelnet.js Normal file
View File

@ -0,0 +1,337 @@
/*
* WebSockets telnet client
* Copyright (C) 2011 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt)
*
* Includes VT100.js from:
* http://code.google.com/p/sshconsole
* Which was modified from:
* http://fzort.org/bi/o.php#vt100_js
*
* Telnet protocol:
* http://www.networksorcery.com/enp/protocol/telnet.htm
* http://www.networksorcery.com/enp/rfc/rfc1091.txt
*
* ANSI escape sequeneces:
* http://en.wikipedia.org/wiki/ANSI_escape_code
* http://ascii-table.com/ansi-escape-sequences-vt-100.php
* http://www.termsys.demon.co.uk/vtansi.htm
* http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
*
* ASCII codes:
* http://en.wikipedia.org/wiki/ASCII
* http://www.hobbyprojects.com/ascii-table/ascii-table.html
*
* Other web consoles:
* http://stackoverflow.com/questions/244750/ajax-console-window-with-ansi-vt100-support
*/
function Telnet(target, connect_callback, disconnect_callback) {
var that = {}, // Public API interface
vt100, ws, sQ = [];
termType = "VT100";
Array.prototype.pushStr = function (str) {
var n = str.length;
for (var i=0; i < n; i++) {
this.push(str.charCodeAt(i));
}
}
function do_send() {
if (sQ.length > 0) {
Util.Debug("Sending " + sQ);
ws.send(sQ);
sQ = [];
}
}
function do_recv() {
//console.log(">> do_recv");
var arr = ws.rQshiftBytes(ws.rQlen()), str = "",
chr, cmd, code, value;
Util.Debug("Received array '" + arr + "'");
while (arr.length > 0) {
chr = arr.shift();
switch (chr) {
case 255: // IAC
cmd = chr;
code = arr.shift();
value = arr.shift();
switch (code) {
case 254: // DONT
Util.Debug("Got Cmd DONT '" + value + "', ignoring");
break;
case 253: // DO
Util.Debug("Got Cmd DO '" + value + "'");
if (value === 24) {
// Terminal type
Util.Info("Send WILL '" + value + "' (TERM-TYPE)");
sQ.push(255, 251, value);
} else {
// Refuse other DO requests with a WONT
Util.Debug("Send WONT '" + value + "'");
sQ.push(255, 252, value);
}
break;
case 252: // WONT
Util.Debug("Got Cmd WONT '" + value + "', ignoring");
break;
case 251: // WILL
Util.Debug("Got Cmd WILL '" + value + "'");
if (value === 1) {
// Server will echo, turn off local echo
vt100.noecho();
// Affirm echo with DO
Util.Info("Send Cmd DO '" + value + "' (echo)");
sQ.push(255, 253, value);
} else {
// Reject other WILL offers with a DONT
Util.Debug("Send Cmd DONT '" + value + "'");
sQ.push(255, 254, value);
}
break;
case 250: // SB (subnegotiation)
if (value === 24) {
Util.Info("Got IAC SB TERM-TYPE SEND(1) IAC SE");
// TERM-TYPE subnegotiation
if (arr[0] === 1 &&
arr[1] === 255 &&
arr[2] === 240) {
arr.shift(); arr.shift(); arr.shift();
Util.Info("Send IAC SB TERM-TYPE IS(0) '" +
termType + "' IAC SE");
sQ.push(255, 250, 24, 0);
sQ.pushStr(termType);
sQ.push(255, 240);
} else {
Util.Info("Invalid subnegotiation received" + arr);
}
} else {
Util.Info("Ignoring SB " + value);
}
break;
default:
Util.Info("Got Cmd " + cmd + " " + value + ", ignoring"); }
continue;
case 242: // Data Mark (Synch)
cmd = chr;
code = arr.shift();
value = arr.shift();
Util.Info("Ignoring Data Mark (Synch)");
break;
default: // everything else
str += String.fromCharCode(chr);
}
}
if (sQ) {
do_send();
}
if (str) {
vt100.write(str);
}
//console.log("<< do_recv");
}
that.connect = function(host, port, encrypt) {
var host = host,
port = port,
scheme = "ws://", uri;
Util.Debug(">> connect");
if ((!host) || (!port)) {
console.log("must set host and port");
return;
}
if (ws) {
ws.close();
}
if (encrypt) {
scheme = "wss://";
}
uri = scheme + host + ":" + port;
Util.Info("connecting to " + uri);
ws.open(uri);
Util.Debug("<< connect");
}
that.disconnect = function() {
Util.Debug(">> disconnect");
if (ws) {
ws.close();
}
vt100.curs_set(true, false);
disconnect_callback();
Util.Debug("<< disconnect");
}
function constructor() {
/* Initialize Websock object */
ws = new Websock();
ws.on('message', do_recv);
ws.on('open', function(e) {
Util.Info(">> WebSockets.onopen");
vt100.curs_set(true, true);
connect_callback();
Util.Info("<< WebSockets.onopen");
});
ws.on('close', function(e) {
Util.Info(">> WebSockets.onclose");
that.disconnect();
Util.Info("<< WebSockets.onclose");
});
ws.on('error', function(e) {
Util.Info(">> WebSockets.onerror");
that.disconnect();
Util.Info("<< WebSockets.onerror");
});
/* Initialize the terminal emulator/renderer */
vt100 = new VT100(80, 24, target);
/*
* Override VT100 I/O routines
*/
// Set handler for sending characters
vt100.getch(
function send_chr(chr, vt) {
var i;
Util.Debug(">> send_chr: " + chr);
for (i = 0; i < chr.length; i++) {
sQ.push(chr.charCodeAt(i));
}
do_send();
vt100.getch(send_chr);
}
);
vt100.debug = function(message) {
Util.Debug(message + "\n");
}
vt100.warn = function(message) {
Util.Warn(message + "\n");
}
vt100.curs_set = function(vis, grab, eventist)
{
this.debug("curs_set:: vis: " + vis + ", grab: " + grab);
if (vis !== undefined)
this.cursor_vis_ = (vis > 0);
if (eventist === undefined)
eventist = window;
if (grab === true || grab === false) {
if (grab === this.grab_events_)
return;
if (grab) {
this.grab_events_ = true;
VT100.the_vt_ = this;
Util.addEvent(eventist, 'keydown', vt100.key_down);
Util.addEvent(eventist, 'keyup', vt100.key_up);
} else {
Util.removeEvent(eventist, 'keydown', vt100.key_down);
Util.removeEvent(eventist, 'keyup', vt100.key_up);
this.grab_events_ = false;
VT100.the_vt_ = undefined;
}
}
}
vt100.key_down = function(e) {
var vt = VT100.the_vt_, keysym, ch, str = "";
if (vt === undefined)
return true;
keysym = getKeysym(e);
if (keysym < 128) {
if (e.ctrlKey) {
if (keysym == 64) {
// control 0
ch = 0;
} else if ((keysym >= 97) && (keysym <= 122)) {
// control codes 1-26
ch = keysym - 96;
} else if ((keysym >= 91) && (keysym <= 95)) {
// control codes 27-31
ch = keysym - 64;
} else {
Util.Info("Debug unknown control keysym: " + keysym);
}
} else {
ch = keysym;
}
str = String.fromCharCode(ch);
} else {
switch (keysym) {
case 65505: // Shift, do not send directly
break;
case 65507: // Ctrl, do not send directly
break;
case 65293: // Carriage return, line feed
str = '\n'; break;
case 65288: // Backspace
str = '\b'; break;
case 65289: // Tab
str = '\t'; break;
case 65307: // Escape
str = '\x1b'; break;
case 65361: // Left arrow
str = '\x1b[D'; break;
case 65362: // Up arrow
str = '\x1b[A'; break;
case 65363: // Right arrow
str = '\x1b[C'; break;
case 65364: // Down arrow
str = '\x1b[B'; break;
default:
Util.Info("Unrecoginized keysym " + keysym);
}
}
if (str) {
vt.key_buf_.push(str);
setTimeout(VT100.go_getch_, 0);
}
Util.stopEvent(e);
return false;
}
vt100.key_up = function(e) {
var vt = VT100.the_vt_;
if (vt === undefined)
return true;
Util.stopEvent(e);
return false;
}
return that;
}
return constructor(); // Return the public API interface
} // End of Telnet()