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:
parent
f0bdb0a621
commit
89b665f4fa
|
|
@ -1 +0,0 @@
|
|||
../include
|
||||
|
|
@ -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 /> 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 += '&'; break;
|
||||
case '<':
|
||||
stuff += '<'; break;
|
||||
case '>':
|
||||
stuff += '>'; break;
|
||||
case ' ':
|
||||
//stuff += ' '; 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");
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
Loading…
Reference in New Issue