initial commit
commit
55ebab1701
@ -0,0 +1,25 @@
|
|||||||
|
`script` and `scriptreplay` are part of the `bsdutils` package in Debian and
|
||||||
|
Ubuntu, `util-linux-ng` in Fedora and `util-linux` in SUSE. They allow to
|
||||||
|
capture a terminal or script output and replay it, respectively.
|
||||||
|
|
||||||
|
This page uses the vt100 emulator by Frank Bi and adds code of my own to read
|
||||||
|
typescript and timing files to make it possible to replay captured scripts a
|
||||||
|
web browser.
|
||||||
|
|
||||||
|
By adding an upload facility to this it would be possible to have a youtube or
|
||||||
|
pastebin for terminal sessions. Due to laziness this remains a proof of concept
|
||||||
|
for now though.
|
||||||
|
|
||||||
|
- This javascript terminal window is 80x24 characters, so it might be
|
||||||
|
best to adjust your terminal window to that size as well using the
|
||||||
|
following to check:
|
||||||
|
$ watch "tput cols; tput lines"
|
||||||
|
- Start recording:
|
||||||
|
$ SHELL=/bin/sh TERM=vt100 script -t typescript 2> timingfile
|
||||||
|
- Do your stuff and when done exit script with `exit`, `logout` or
|
||||||
|
ctrl-d.
|
||||||
|
- To test how your recorded session looks like, use:
|
||||||
|
$ scriptreplay timingfile typescript
|
||||||
|
- Enter `timingfile` and `typescript` into form above and hit the play
|
||||||
|
button.
|
||||||
|
|
@ -0,0 +1,649 @@
|
|||||||
|
// 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(a) Turns off any current attributes given in `a'.
|
||||||
|
// attron(a) Turns on any attributes given in `a'.
|
||||||
|
// attrset(a) Sets the current attributes to `a'.
|
||||||
|
// bkgdset(a) Sets the background attributes to `a'.
|
||||||
|
// 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.
|
||||||
|
// COLOR_PAIR(pn) Converts a colour pair number `pn' into an
|
||||||
|
// attribute.
|
||||||
|
// 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.
|
||||||
|
// pair_content(pn)
|
||||||
|
// Returns an associative array with the foreground
|
||||||
|
// (`f') and background (`b') colour numbers for the
|
||||||
|
// colour pair `pn'.
|
||||||
|
// PAIR_NUMBER(a) Returns the colour pair number in the attributes
|
||||||
|
// `a'.
|
||||||
|
// 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.c_attr_ = this.bkgd_ = this.COLOR_PAIR(0) | VT100.A_NORMAL;
|
||||||
|
this.color_pair_ = new Array(VT100.COLOR_PAIRS);
|
||||||
|
this.color_pair_[0] = { f: VT100.COLOR_WHITE, b: 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;
|
||||||
|
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(e)
|
||||||
|
{
|
||||||
|
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 = e.charCode;
|
||||||
|
if (ch) {
|
||||||
|
if (ch > 255)
|
||||||
|
return true;
|
||||||
|
ch = String.fromCharCode(ch);
|
||||||
|
if (ch == '\r')
|
||||||
|
ch = '\n';
|
||||||
|
} else
|
||||||
|
switch (e.keyCode) {
|
||||||
|
case e.DOM_VK_BACK_SPACE:
|
||||||
|
ch = '\b'; break;
|
||||||
|
case e.DOM_VK_TAB:
|
||||||
|
ch = '\t'; break;
|
||||||
|
case e.DOM_VK_RETURN:
|
||||||
|
case e.DOM_VK_ENTER:
|
||||||
|
ch = '\n'; 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()
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
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 pair, fg, bg, co0, co1;
|
||||||
|
pair = this.pair_content(this.PAIR_NUMBER(attr));
|
||||||
|
fg = pair.f;
|
||||||
|
bg = pair.b;
|
||||||
|
switch (attr & (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_;
|
||||||
|
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.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.attroff = function(a)
|
||||||
|
{
|
||||||
|
a &= VT100.ATTR_FLAGS_;
|
||||||
|
this.c_attr_ &= ~a;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.attron = function(a)
|
||||||
|
{
|
||||||
|
a &= VT100.ATTR_FLAGS_;
|
||||||
|
this.c_attr_ |= a;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.attrset = function(a)
|
||||||
|
{
|
||||||
|
this.c_attr_ = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.bkgdset = function(a)
|
||||||
|
{
|
||||||
|
this.bkgd_ = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.clear = function()
|
||||||
|
{
|
||||||
|
this.row_ = this.col_ = 0;
|
||||||
|
for (r = 0; r < this.ht_; ++r) {
|
||||||
|
for (c = 0; c < this.wd_; ++c) {
|
||||||
|
this.text_[r][c] = ' ';
|
||||||
|
this.attr_[r][c] = this.bkgd_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.clrtobot = function()
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
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.COLOR_PAIR = function(pn)
|
||||||
|
{
|
||||||
|
return pn << VT100.COLOR_SHIFT_;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.curs_set = function(vis, grab)
|
||||||
|
{
|
||||||
|
if (vis !== undefined)
|
||||||
|
this.cursor_vis_ = (vis > 0);
|
||||||
|
if (grab === true || grab === false) {
|
||||||
|
if (grab === this.grab_events_)
|
||||||
|
return;
|
||||||
|
if (grab) {
|
||||||
|
this.grab_events_ = true;
|
||||||
|
VT100.the_vt_ = this;
|
||||||
|
document.onkeypress = VT100.handle_onkeypress_;
|
||||||
|
if (VT100.browser_ie_)
|
||||||
|
document.onkeydown = VT100.handle_onkeydown_;
|
||||||
|
} else {
|
||||||
|
document.onkeypress = undefined;
|
||||||
|
if (VT100.browser_ie_)
|
||||||
|
document.onkeydown = VT100.handle_onkeydown_;
|
||||||
|
this.grab_events_ = false;
|
||||||
|
VT100.the_vt_ = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.echo = function()
|
||||||
|
{
|
||||||
|
this.echo_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.erase = VT100.prototype.clear;
|
||||||
|
|
||||||
|
VT100.prototype.getch = function(isr)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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.echo_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.pair_content = function(pn)
|
||||||
|
{
|
||||||
|
return this.color_pair_[pn];
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.PAIR_NUMBER = function(at)
|
||||||
|
{
|
||||||
|
return at >> VT100.COLOR_SHIFT_;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.refresh = function()
|
||||||
|
{
|
||||||
|
var r, c, stuff = "", end_tag = "", at = -1, n_at, ch,
|
||||||
|
pair, cr, cc, ht, wd, cv;
|
||||||
|
ht = this.ht_;
|
||||||
|
wd = this.wd_;
|
||||||
|
cr = this.row_;
|
||||||
|
cc = this.col_;
|
||||||
|
cv = this.cursor_vis_;
|
||||||
|
if (cc >= wd)
|
||||||
|
cc = wd - 1;
|
||||||
|
for (r = 0; r < ht; ++r) {
|
||||||
|
stuff += '<br>';
|
||||||
|
for (c = 0; c < wd; ++c) {
|
||||||
|
n_at = this.attr_[r][c];
|
||||||
|
if (cv && r == cr && c == cc)
|
||||||
|
n_at ^= VT100.A_REVERSE;
|
||||||
|
if (n_at != at) {
|
||||||
|
stuff += end_tag;
|
||||||
|
end_tag = "";
|
||||||
|
if (n_at & VT100.A_BLINK) {
|
||||||
|
stuff += "<blink>";
|
||||||
|
end_tag = "</blink>" + end_tag;
|
||||||
|
}
|
||||||
|
if (n_at & VT100.A_STANDOUT)
|
||||||
|
n_at |= VT100.A_BOLD;
|
||||||
|
pair = this.html_colours_(n_at);
|
||||||
|
stuff += '<span style="color:' + pair.f +
|
||||||
|
';background-color:' + pair.b;
|
||||||
|
if (n_at & VT100.A_UNDERLINE)
|
||||||
|
stuff += ';text-decoration:underline';
|
||||||
|
stuff += ';">';
|
||||||
|
end_tag = "</span>" + end_tag;
|
||||||
|
at = n_at;
|
||||||
|
}
|
||||||
|
ch = this.text_[r][c];
|
||||||
|
switch (ch) {
|
||||||
|
case '&':
|
||||||
|
stuff += '&'; break;
|
||||||
|
case '<':
|
||||||
|
stuff += '<'; break;
|
||||||
|
case '>':
|
||||||
|
stuff += '>'; break;
|
||||||
|
case ' ':
|
||||||
|
stuff += ' '; break;
|
||||||
|
default:
|
||||||
|
stuff += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.scr_.innerHTML = " <b>" + stuff + end_tag + "</b><br> ";
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.scroll = function()
|
||||||
|
{
|
||||||
|
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.attrset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VT100.prototype.standout = function()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
switch (ch) {
|
||||||
|
case '\x00':
|
||||||
|
case '\x7f':
|
||||||
|
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;
|
||||||
|
continue;
|
||||||
|
case '\x1b':
|
||||||
|
this.esc_state_ = 1;
|
||||||
|
continue;
|
||||||
|
case '\x9b':
|
||||||
|
this.esc_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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // just saw CSI
|
||||||
|
this.csi_parms_ = [0];
|
||||||
|
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);
|
||||||
|
continue;
|
||||||
|
case ';':
|
||||||
|
if (this.csi_parms_.length < 17)
|
||||||
|
this.csi_parms_.push(0);
|
||||||
|
case '?': // ?!?
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
switch (ch) {
|
||||||
|
case 'H':
|
||||||
|
this.esc_state_ = 0;
|
||||||
|
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();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
this.attron(VT100.A_BOLD);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case '[':
|
||||||
|
this.esc_state_ = 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4: // saw CSI [
|
||||||
|
this.esc_state_ = 0; // gobble char.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.refresh();
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>scriptreplay in javascript</title>
|
||||||
|
<script type="text/javascript" src="./VT100.js" ></script>
|
||||||
|
<script type="text/javascript" src="./scriptreplay.js" ></script>
|
||||||
|
<script type="text/javascript"><!--
|
||||||
|
window.addEventListener("load", function(evt) {
|
||||||
|
vt = new VT100(80, 24, "term");
|
||||||
|
vt.clear();
|
||||||
|
vt.refresh();
|
||||||
|
vt.write(
|
||||||
|
"\n\n\n" +
|
||||||
|
" * This javascript terminal window is 80x24 characters, so it might be\n" +
|
||||||
|
" best to adjust your terminal window to that size as well using the\n" +
|
||||||
|
" following to check:\n" +
|
||||||
|
" $ watch \"tput cols; tput lines\"\n" +
|
||||||
|
" * Start recording:\n" +
|
||||||
|
" $ SHELL=/bin/sh TERM=vt100 script -t typescript 2> timingfile\n" +
|
||||||
|
" * Do your stuff and when done exit script with `exit`, `logout` or\n" +
|
||||||
|
" ctrl-d.\n" +
|
||||||
|
" * To test how your recorded session looks like, use:\n" +
|
||||||
|
" $ scriptreplay timingfile typescript\n" +
|
||||||
|
" * Enter `timingfile` and `typescript` into form above and hit the play\n" +
|
||||||
|
" button.\n");
|
||||||
|
document.getElementById("stop").addEventListener("click", stop, false);
|
||||||
|
document.getElementById("speed").addEventListener('change', set_speed, false);
|
||||||
|
document.getElementById("fontsize").addEventListener('change', set_fontsize, false);
|
||||||
|
document.getElementById("play").addEventListener("click", play, false);
|
||||||
|
|
||||||
|
var samples = document.querySelectorAll("ul#sample>li>a[id^=\"sample_\"]");
|
||||||
|
for (var i = 0; i < samples.length; i++) {
|
||||||
|
samples[i].addEventListener('click', function(evt) {
|
||||||
|
play_file(evt.target.id.substr(7));
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
--></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h2>scriptreplay in javascript</h2>
|
||||||
|
|
||||||
|
<p><tt>script</tt> and <tt>scriptreplay</tt> are part of the <tt>bsdutils</tt>
|
||||||
|
package in debian and ubuntu, <tt>util-linux-ng</tt> in fedora and
|
||||||
|
<tt>util-linux</tt> in suse. They allow to capture a terminal or script output
|
||||||
|
and replay it.</p>
|
||||||
|
|
||||||
|
<p>This page uses the <a href="./VT100.js">vt100 emulator</a> by
|
||||||
|
<a href="http://fzort.org/bi/o.php#vt100_js">frank bi</a> and adds
|
||||||
|
<a href="./scriptreplay.js">code of my own</a> to read typescript and timing
|
||||||
|
files to make it possible to replay captured scripts a web browser.</p>
|
||||||
|
|
||||||
|
<p>By adding an upload facility to this it would be possible to have a youtube
|
||||||
|
or pastebin for terminal sessions. Due to laziness this remains a proof of
|
||||||
|
concept for now though.</p>
|
||||||
|
|
||||||
|
<p>(C) 2011 Johannes 'josch' Schauer <j [dot] schauer [at] email [dot] de></p>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>file input</legend>
|
||||||
|
typescript: <input type="file" id="typescript" name="typescript" /><br />
|
||||||
|
timingfile: <input type="file" id="timingfile" name="typescript" /><br />
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>samples</legend>
|
||||||
|
<ul id="sample">
|
||||||
|
<li><a href="javascript:void()" id="sample_qemu">qemu boot</a></li>
|
||||||
|
<li><a href="javascript:void()" id="sample_starwars">star wars</a></li>
|
||||||
|
</ul>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<tt><pre id=term></pre></tt>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>play control</legend>
|
||||||
|
<button id="play">play</button>
|
||||||
|
<button id="stop">stop</button>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>output properties</legend>
|
||||||
|
Font size
|
||||||
|
<select id="fontsize">
|
||||||
|
<option value="8">8</option>
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="12" selected="selected">12</option>
|
||||||
|
<option value="14">14</option>
|
||||||
|
<option value="16">16</option>
|
||||||
|
<option value="18">18</option>
|
||||||
|
</select>
|
||||||
|
Speed
|
||||||
|
<select id="speed">
|
||||||
|
<option value="0.25">slowest</option>
|
||||||
|
<option value="0.5">slower</option>
|
||||||
|
<option value="0.75">slow</option>
|
||||||
|
<option value="1.0" selected="selected">normal</option>
|
||||||
|
<option value="1.5">fast</option>
|
||||||
|
<option value="2.0">faster</option>
|
||||||
|
<option value="4.0">fastest</option>
|
||||||
|
</select>
|
||||||
|
</fieldset>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,198 @@
|
|||||||
|
0.596415 2
|
||||||
|
0.015173 125
|
||||||
|
5.780041 2
|
||||||
|
1.039932 793
|
||||||
|
0.049982 358
|
||||||
|
0.010029 651
|
||||||
|
0.009955 289
|
||||||
|
0.010026 94
|
||||||
|
2.939978 41
|
||||||
|
0.009958 32
|
||||||
|
0.070038 401
|
||||||
|
0.010057 39
|
||||||
|
0.019968 97
|
||||||
|
0.009996 47
|
||||||
|
0.010002 252
|
||||||
|
0.010000 226
|
||||||
|
0.009973 399
|
||||||
|
0.030011 236
|
||||||
|
0.010025 127
|
||||||
|
0.009969 122
|
||||||
|
0.009994 44
|
||||||
|
0.010029 506
|
||||||
|
0.009990 72
|
||||||
|
0.219995 271
|
||||||
|
0.019997 151
|
||||||
|
0.010009 330
|
||||||
|
0.009992 359
|
||||||
|
0.010024 23
|
||||||
|
0.000017 5
|
||||||
|
0.000012 393
|
||||||
|
0.009949 155
|
||||||
|
0.010019 93
|
||||||
|
0.090023 197
|
||||||
|
0.049982 98
|
||||||
|
0.009962 437
|
||||||
|
0.010017 37
|
||||||
|
0.009987 59
|
||||||
|
0.010005 133
|
||||||
|
0.010040 126
|
||||||
|
0.009964 58
|
||||||
|
0.009994 27
|
||||||
|
0.180012 4
|
||||||
|
0.010035 51
|
||||||
|
0.129993 3
|
||||||
|
0.045599 45
|
||||||
|
0.424369 27
|
||||||
|
0.140006 45
|
||||||
|
0.270003 4
|
||||||
|
0.290000 42
|
||||||
|
0.130012 8
|
||||||
|
0.479987 41
|
||||||
|
0.380031 126
|
||||||
|
2.399973 87
|
||||||
|
0.049983 59
|
||||||
|
0.010009 113
|
||||||
|
0.190004 76
|
||||||
|
0.120004 69
|
||||||
|
0.080005 287
|
||||||
|
0.067802 67
|
||||||
|
0.000037 349
|
||||||
|
0.032161 156
|
||||||
|
0.059996 103
|
||||||
|
0.019999 124
|
||||||
|
0.070005 67
|
||||||
|
0.669997 61
|
||||||
|
0.079992 32
|
||||||
|
0.019996 14
|
||||||
|
0.000019 5
|
||||||
|
0.000013 4
|
||||||
|
0.000016 2
|
||||||
|
0.000012 234
|
||||||
|
0.009944 64
|
||||||
|
0.010001 8
|
||||||
|
0.370023 18
|
||||||
|
0.779984 90
|
||||||
|
0.059988 8
|
||||||
|
0.010016 28
|
||||||
|
0.129994 33
|
||||||
|
0.200000 58
|
||||||
|
0.119996 3
|
||||||
|
0.010008 8
|
||||||
|
0.039999 51
|
||||||
|
0.170013 23
|
||||||
|
0.370015 4
|
||||||
|
0.159999 24
|
||||||
|
0.069972 4
|
||||||
|
0.159990 25
|
||||||
|
0.100006 37
|
||||||
|
0.130004 8
|
||||||
|
0.300000 29
|
||||||
|
1.310011 8
|
||||||
|
0.039991 24
|
||||||
|
0.039991 30
|
||||||
|
0.130006 3
|
||||||
|
0.000021 8
|
||||||
|
0.039982 29
|
||||||
|
0.089997 8
|
||||||
|
0.059996 27
|
||||||
|
0.019991 8
|
||||||
|
0.040015 30
|
||||||
|
0.099998 4
|
||||||
|
0.459992 33
|
||||||
|
0.180006 8
|
||||||
|
0.240004 26
|
||||||
|
0.049996 4
|
||||||
|
0.210005 36
|
||||||
|
0.219998 4
|
||||||
|
0.620003 30
|
||||||
|
0.350055 28
|
||||||
|
0.339933 3
|
||||||
|
0.000033 4
|
||||||
|
0.300007 28
|
||||||
|
0.269964 8
|
||||||
|
0.060055 7
|
||||||
|
0.879942 24
|
||||||
|
0.010045 54
|
||||||
|
0.109966 40
|
||||||
|
1.599991 46
|
||||||
|
0.030000 39
|
||||||
|
1.170010 46
|
||||||
|
1.279981 41
|
||||||
|
0.020009 4
|
||||||
|
0.460008 37
|
||||||
|
1.040004 13
|
||||||
|
0.019993 6
|
||||||
|
6.320004 4
|
||||||
|
0.279992 52
|
||||||
|
0.299999 3
|
||||||
|
1.230074 39
|
||||||
|
0.009964 14
|
||||||
|
0.009961 1
|
||||||
|
5.940003 1
|
||||||
|
0.129993 1
|
||||||
|
0.160008 1
|
||||||
|
0.110012 3
|
||||||
|
0.240002 10
|
||||||
|
0.189996 3
|
||||||
|
1.370004 424
|
||||||
|
0.359983 15
|
||||||
|
8.180054 1
|
||||||
|
1.659965 1
|
||||||
|
0.119988 1
|
||||||
|
0.160018 1
|
||||||
|
0.059981 1
|
||||||
|
0.400005 1
|
||||||
|
0.190007 1
|
||||||
|
0.149992 1
|
||||||
|
0.179992 3
|
||||||
|
0.180015 81
|
||||||
|
0.050017 15
|
||||||
|
0.010008 1
|
||||||
|
1.520035 1
|
||||||
|
0.099976 1
|
||||||
|
0.039946 1
|
||||||
|
0.100034 1
|
||||||
|
0.220008 1
|
||||||
|
0.189971 1
|
||||||
|
0.110026 1
|
||||||
|
0.100004 2
|
||||||
|
0.389977 1
|
||||||
|
0.020018 1
|
||||||
|
0.470004 1
|
||||||
|
0.099966 5
|
||||||
|
0.440017 3
|
||||||
|
0.620020 409
|
||||||
|
0.039985 15
|
||||||
|
0.009990 1
|
||||||
|
6.040066 1
|
||||||
|
0.049927 1
|
||||||
|
0.130006 1
|
||||||
|
0.120011 3
|
||||||
|
0.310013 133
|
||||||
|
0.119961 50
|
||||||
|
0.600002 44
|
||||||
|
0.009997 54
|
||||||
|
1.170033 46
|
||||||
|
0.899968 28
|
||||||
|
0.010002 4
|
||||||
|
0.100023 46
|
||||||
|
0.579972 8
|
||||||
|
0.159989 48
|
||||||
|
1.310007 35
|
||||||
|
0.039996 4
|
||||||
|
0.220008 30
|
||||||
|
0.619996 40
|
||||||
|
0.050000 18
|
||||||
|
0.940034 388
|
||||||
|
0.130012 27
|
||||||
|
0.449955 27
|
||||||
|
0.020000 20
|
||||||
|
0.389991 8
|
||||||
|
0.090015 17
|
||||||
|
0.840000 2
|
||||||
|
0.970052 1
|
||||||
|
1.709937 1
|
||||||
|
0.080006 1
|
||||||
|
0.089997 1
|
||||||
|
0.160021 2
|
@ -0,0 +1,133 @@
|
|||||||
|
var vt, timer;
|
||||||
|
var speed = 1.0;
|
||||||
|
|
||||||
|
function Timer(callback, delay) {
|
||||||
|
var timerId, start, remaining = delay;
|
||||||
|
|
||||||
|
this.pause = function() {
|
||||||
|
window.clearTimeout(timerId);
|
||||||
|
remaining -= new Date() - start;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.resume = function() {
|
||||||
|
start = new Date();
|
||||||
|
timerId = window.setTimeout(callback, remaining);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_file_contents(filename, callback) {
|
||||||
|
if (window.XMLHttpRequest) {
|
||||||
|
req = new XMLHttpRequest();
|
||||||
|
} else {
|
||||||
|
req = new ActiveXObject("Microsoft.XMLHTTP");
|
||||||
|
}
|
||||||
|
req.open("GET", filename, false);
|
||||||
|
req.onreadystatechange = function() {
|
||||||
|
// status is 0 for local files
|
||||||
|
if (req.readyState==4 && ( req.status==200 || req.status==0)) {
|
||||||
|
callback(req.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.send(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function play_file(name) {
|
||||||
|
get_file_contents(name+".script", function(typescript_data) {
|
||||||
|
get_file_contents(name+".time", function(timing_data) {
|
||||||
|
run_typescript(typescript_data, timing_data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_speed(evt) {
|
||||||
|
var value = evt.target.options[evt.target.selectedIndex].value;
|
||||||
|
speed = parseFloat(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_fontsize(evt) {
|
||||||
|
var value = evt.target.options[evt.target.selectedIndex].value;
|
||||||
|
document.getElementById("term").style.fontSize=value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function play(evt) {
|
||||||
|
if (evt.target.textContent == "play") {
|
||||||
|
readBlob('typescript', reader_onloadend);
|
||||||
|
} else if (evt.target.textContent == "resume") {
|
||||||
|
evt.target.textContent = "pause";
|
||||||
|
timer.resume();
|
||||||
|
} else if (evt.target.textContent == "pause") {
|
||||||
|
evt.target.textContent = "resume";
|
||||||
|
timer.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop(evt) {
|
||||||
|
document.getElementById("play").textContent = "play";
|
||||||
|
timer.pause();
|
||||||
|
vt.clear();
|
||||||
|
vt.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_typescript(typescript_data, timing_data) {
|
||||||
|
if (timer) timer.pause();
|
||||||
|
document.getElementById("play").textContent = "pause";
|
||||||
|
vt.clear();
|
||||||
|
vt.refresh();
|
||||||
|
|
||||||
|
var where = 0;
|
||||||
|
var linenum = 0;
|
||||||
|
var timings = timing_data.split("\n");
|
||||||
|
var firstlinelen = typescript_data.indexOf("\n") + 1;
|
||||||
|
var text = typescript_data.substr(0, firstlinelen);
|
||||||
|
var newtext = "";
|
||||||
|
where += firstlinelen;
|
||||||
|
|
||||||
|
timer = new Timer(
|
||||||
|
function() {
|
||||||
|
vt.write(text);
|
||||||
|
text = newtext;
|
||||||
|
var me = arguments.callee;
|
||||||
|
var line = timings[linenum].split(" ");
|
||||||
|
var time = parseFloat(line[0]);
|
||||||
|
var bytes = parseInt(line[1]);
|
||||||
|
if (isFinite(time) && isFinite(bytes)) {
|
||||||
|
newtext = typescript_data.substr(where, bytes);
|
||||||
|
where += bytes;
|
||||||
|
linenum += 1;
|
||||||
|
timer = new Timer(me, time*1000*1/speed);
|
||||||
|
} else {
|
||||||
|
vt.write(typescript_data.substr(where, typescript_data.length-where));
|
||||||
|
document.getElementById("play").textContent = "play";
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reader_onloadend(evt) {
|
||||||
|
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
|
||||||
|
typescript_data = evt.target.result;
|
||||||
|
readBlob('timingfile',
|
||||||
|
function(evt) {
|
||||||
|
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
|
||||||
|
timing_data = evt.target.result;
|
||||||
|
run_typescript(typescript_data, timing_data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBlob(id, onload_handler) {
|
||||||
|
var files = document.getElementById(id).files;
|
||||||
|
if (!files.length) {
|
||||||
|
alert('Please select a file!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = files[0];
|
||||||
|
var blob = file.slice(0, file.size);
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onloadend = onload_handler;
|
||||||
|
reader.onerror = function(evt) {alert(evt);};
|
||||||
|
reader.readAsBinaryString(blob);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue