mirror of
https://github.com/nuintun/command-manager.git
synced 2025-06-15 09:17:02 +08:00
3308 lines
88 KiB
JavaScript
3308 lines
88 KiB
JavaScript
/*
|
|
termlib.js - JS-WebTerminal Object v1.66
|
|
|
|
(c) Norbert Landsteiner 2003-2015
|
|
mass:werk - media environments
|
|
<http://www.masswerk.at/termlib/>
|
|
|
|
Creates [multiple] Terminal instances.
|
|
|
|
Synopsis:
|
|
|
|
myTerminal = new Terminal(<config object>);
|
|
myTerminal.open();
|
|
|
|
<config object> overrides any values of object `TerminalDefaults'.
|
|
individual values of `id' must be supplied for multiple terminals.
|
|
`handler' specifies a function to be called for input handling.
|
|
(see `Terminal.prototype.defaultHandler()' and documentation.)
|
|
|
|
globals defined in this library:
|
|
Terminal (Terminal object)
|
|
TerminalDefaults (default configuration)
|
|
termDefaultHandler (default command line handler)
|
|
TermGlobals (common vars and code for all instances)
|
|
termKey (named mappings for special keys)
|
|
termDomKeyRef (special key mapping for DOM constants)
|
|
|
|
(please see the v. 1.4 history entry on these elements)
|
|
|
|
required CSS classes for font definitions: ".term", ".termReverse".
|
|
|
|
Compatibilty:
|
|
Standard web browsers with a JavaScript implementation compliant to
|
|
ECMA-262 2nd edition and support for the anonymous array and object
|
|
constructs and the anonymous function construct in the form of
|
|
"myfunc=function(x) {}" (c.f. ECMA-262 3rd edion for details).
|
|
This comprises almost all current browsers but Konquerer (khtml) and
|
|
versions of Apple Safari for Mac OS 10.0-10.28 (Safari 1.0) which
|
|
lack support for keyboard events.
|
|
v1.5: Dropped support of Netscape 4 (layers)
|
|
|
|
License:
|
|
This JavaScript-library is free.
|
|
Include a visible backlink to <http://www.masswerk.at/termlib/> in the
|
|
embedding web page or application.
|
|
The library should always be accompanied by the 'readme.txt' and the
|
|
sample HTML-documents.
|
|
|
|
Any changes should be commented and must be reflected in `Terminal.version'
|
|
in the format: "Version.Subversion (compatibility)".
|
|
|
|
Donations:
|
|
Donations are welcome: You may support and/or honor the development of
|
|
"termlib.js" via PayPal at: <http://www.masswerk.at/termlib/donate/>
|
|
|
|
Disclaimer:
|
|
This software is distributed AS IS and in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The entire risk as to
|
|
the quality and performance of the product is borne by the user. No use of
|
|
the product is authorized hereunder except under this disclaimer.
|
|
|
|
### The sections above must not be removed. ###
|
|
|
|
version 1.01: added Terminal.prototype.resizeTo(x,y)
|
|
added Terminal.conf.fontClass (=> configureable class name)
|
|
Terminal.prototype.open() now checks for element conf.termDiv
|
|
in advance and returns success.
|
|
|
|
version 1.02: added support for <TAB> and Euro sign
|
|
(Terminal.conf.printTab, Terminal.conf.printEuro)
|
|
and a method to evaluate printable chars:
|
|
Terminal.prototype.isPrintable(keycode)
|
|
|
|
version 1.03: added global keyboard locking (TermGlobals.keylock)
|
|
modified Terminal.prototype.redraw for speed (use of locals)
|
|
|
|
version 1.04: modified the key handler to fix a bug with MSIE5/Mac
|
|
fixed a bug in TermGlobals.setVisible with older MSIE-alike
|
|
browsers without DOM support.
|
|
|
|
version 1.05: added config flag historyUnique.
|
|
|
|
version 1.06: fixed CTRl+ALT (Windows alt gr) isn't CTRL any more
|
|
fixed double backspace bug for Safari;
|
|
added TermGlobals.setDisplay for setting style.display props
|
|
termlib.js now outputs lower case html (xhtml compatibility)
|
|
|
|
version 1.07: added method rebuild() to rebuild with new color settings.
|
|
|
|
version 1.1: fixed a bug in 'more' output mode (cursor could be hidden after
|
|
quit)
|
|
added socket-extension for server-client talk in a separate file
|
|
-> "temlib_socket.js" (to be loaded after termlib.js)
|
|
(this is a separate file because we break our compatibility
|
|
guide lines with this IO/AJAX library.)
|
|
|
|
version 1.2 added color support ("%[+-]c(<color>)" markup)
|
|
moved paste support from sample file to lib
|
|
* TermGlobals.insertText( <text>)
|
|
* TermGlobals.importEachLine( <text> )
|
|
* TermGlobals.importMultiLine( <text> )
|
|
|
|
version 1.3 added word wrapping to write()
|
|
* activate with myTerm.wrapOn()
|
|
* deactivate with myTerm.wrapOff()
|
|
use conf.wrapping (boolean) for a global setting
|
|
|
|
version 1.4 Terminal is now an entirely self-contained object
|
|
Global references to inner objects for backward compatipility:
|
|
* TerminalDefaults => Terminal.prototype.Defaults
|
|
* termDefaultHandler => Terminal.prototype.defaultHandler
|
|
* termKey => Terminal.prototype.globals.termKey
|
|
see also: Terminal.prototype.termKey
|
|
* TermGlobals => Terminal.prototype.globals
|
|
* termDomKeyRef => Terminal.prototype.globals.termDomKeyRef
|
|
|
|
So in effect to outside scripts everything remains the same;
|
|
no need to rewrite any existing scripts.
|
|
You may now use "this.globals" inside any handlers
|
|
to refer to the static global object (TermGlobals).
|
|
You may also refer to key definitions as "this.termKey.*".
|
|
(Please mind that "this.termKey" is a reference to the static object
|
|
and not specific to the instance. A change to "this.termKey" will be
|
|
by any other instances of Terminal too.)
|
|
|
|
Added method TermGlobals.assignStyle() for custom styles & mark up.
|
|
|
|
Unified the color mark up: You may now use color codes (decimal or hex)
|
|
inside brackets. e.g.: %c(10)DARKRED%c() or %c(a)DARKRED%c()
|
|
|
|
Added key repeat for remapped keys (cursor movements etc).
|
|
|
|
version 1.41 fixed a bug in the word wrapping regarding write() output, when
|
|
the cursor was set with cursorSet() before.
|
|
|
|
version 1.42 fixed a bug which caused Opera to delete 2 chars at once.
|
|
introduced property Terminal.isOpera (Boolean)
|
|
|
|
version 1.43 enhanced the control handler so it also catches ESC if flag closeOnESC
|
|
is set to false. fixed a bug with Safari which fired repeated events
|
|
for the control handler for TAB if flag printTab was set to false.
|
|
|
|
version 1.5 Changed the license.
|
|
Dropped support for Netscape 4 (layers).
|
|
HTML-elements are now created by document.createElement, if applicable.
|
|
Included the formerly separate socket extension in the main library.
|
|
Added methods 'backupScreen()' and 'restoreScreen()' to save a screen
|
|
and restore it's content from backup. (see the globbing sample).
|
|
|
|
version 1.51 Added basic support of ANSI-SGR-sequences.
|
|
|
|
version 1.52 Added method swapBackup(), reorganized some of the accompanying files.
|
|
|
|
version 1.54 Fixed BACK_SPACE for Chrome, DELETE for Safari/WebKit.
|
|
|
|
version 1.55 Fixed dead keys issue for Mac OS (Leapard & later), vowels only.
|
|
version 1.56 Fixed new ESC issue for Safari.
|
|
version 1.57 Fixed dead keys fix: now only for Safari/Mac, German (de-de).
|
|
version 1.59 Dropped dead keys fix, fixed backspace for Safari.
|
|
version 1.6 Saved some bytes by discarding traces of ancient condition syntax
|
|
Added input mode "fieldMode"
|
|
version 1.61 Changes to defaults implementation of the constructor.
|
|
version 1.62 Fixed a bug related to AltGr-sequences with IE8+.
|
|
version 1.65 Added options for textColor and textBlur.
|
|
version 1.66 textBlur accepts also an array of values for multiple text-shadows.
|
|
|
|
*/
|
|
|
|
var Terminal = function (conf){
|
|
if (typeof conf != 'object') conf = {};
|
|
|
|
for (var i in this.Defaults) {
|
|
if (this.Defaults.hasOwnProperty(i)) {
|
|
if (typeof conf[i] == 'undefined') conf[i] = this.Defaults[i];
|
|
}
|
|
}
|
|
|
|
if (typeof conf.handler != 'function') conf.handler = Terminal.prototype.defaultHandler;
|
|
|
|
this.conf = conf;
|
|
this.setInitValues();
|
|
};
|
|
|
|
Terminal.prototype = {
|
|
// prototype definitions (save some 2k on indentation)
|
|
version: '1.66 (original)',
|
|
Defaults: {
|
|
// dimensions
|
|
cols: 80,
|
|
rows: 24,
|
|
// appearance
|
|
x: 100,
|
|
y: 100,
|
|
termDiv: 'termDiv',
|
|
bgColor: '#181818',
|
|
frameColor: '#555555',
|
|
frameWidth: 1,
|
|
rowHeight: 15,
|
|
blinkDelay: 500,
|
|
// css class
|
|
fontClass: 'term',
|
|
// initial cursor mode
|
|
crsrBlinkMode: false,
|
|
crsrBlockMode: true,
|
|
// key mapping
|
|
DELisBS: false,
|
|
printTab: true,
|
|
printEuro: true,
|
|
catchCtrlH: true,
|
|
closeOnESC: true,
|
|
// prevent consecutive history doublets
|
|
historyUnique: false,
|
|
// optional id
|
|
id: 0,
|
|
// strings
|
|
ps: '>',
|
|
greeting: '%+r Terminal ready. %-r',
|
|
// handlers
|
|
handler: null,
|
|
ctrlHandler: null,
|
|
initHandler: null,
|
|
exitHandler: null,
|
|
wrapping: false,
|
|
mapANSI: false,
|
|
ANSItrueBlack: false,
|
|
textBlur: 0,
|
|
textColor: ''
|
|
},
|
|
setInitValues: function (){
|
|
this.isSafari = !!(navigator.userAgent.indexOf('Safari') >= 0 || navigator.userAgent.indexOf('WebKit') >= 0);
|
|
this.isOpera = !!(window.opera && navigator.userAgent.indexOf('Opera') >= 0);
|
|
this.isChrome = !!(navigator.userAgent.indexOf('Chrome/') >= 0 && navigator.userAgent.indexOf('WebKit') >= 0);
|
|
this.domAPI = (document && document.createElement) ? true : false;
|
|
this.isMac = navigator.userAgent.indexOf('Mac') >= 0;
|
|
this.id = this.conf.id;
|
|
this.maxLines = this.conf.rows;
|
|
this.maxCols = this.conf.cols;
|
|
this.termDiv = this.conf.termDiv;
|
|
this.crsrBlinkMode = this.conf.crsrBlinkMode;
|
|
this.crsrBlockMode = this.conf.crsrBlockMode;
|
|
this.blinkDelay = this.conf.blinkDelay;
|
|
this.DELisBS = this.conf.DELisBS;
|
|
this.printTab = this.conf.printTab;
|
|
this.printEuro = this.conf.printEuro;
|
|
this.catchCtrlH = this.conf.catchCtrlH;
|
|
this.closeOnESC = this.conf.closeOnESC;
|
|
this.historyUnique = this.conf.historyUnique;
|
|
this.ps = this.conf.ps;
|
|
this.closed = false;
|
|
this.r = 0;
|
|
this.c = 0;
|
|
this.charBuf = [];
|
|
this.styleBuf = [];
|
|
this.scrollBuf = null;
|
|
this.blinkBuffer = 0;
|
|
this.blinkTimer = null;
|
|
this.cursoractive = false;
|
|
this.lock = true;
|
|
this.insert = false;
|
|
this.charMode = false;
|
|
this.rawMode = false;
|
|
this.lineBuffer = '';
|
|
this.inputChar = 0;
|
|
this.lastLine = '';
|
|
this.guiCounter = 0;
|
|
this.history = [];
|
|
this.histPtr = 0;
|
|
this.env = [];
|
|
this.buckupBuffer = null;
|
|
this.handler = this.conf.handler;
|
|
this.wrapping = this.conf.wrapping;
|
|
this.mapANSI = this.conf.mapANSI;
|
|
this.ANSItrueBlack = this.conf.ANSItrueBlack;
|
|
this.ctrlHandler = this.conf.ctrlHandler;
|
|
this.initHandler = this.conf.initHandler;
|
|
this.exitHandler = this.conf.exitHandler;
|
|
this.fieldMode = false;
|
|
this.fieldStart = this.fieldEnd = this.fieldC = 0;
|
|
|
|
if (typeof this.conf.textBlur === 'object' && this.conf.textBlur.length) {
|
|
var a = [];
|
|
|
|
for (var i = 0; i < this.conf.textBlur.length; i++) {
|
|
var b = Number(this.conf.textBlur[i]);
|
|
if (!isNaN(b) && b > 0) a.push(b);
|
|
}
|
|
|
|
this.textBlur = (a.length) ? a : 0;
|
|
} else {
|
|
this.textBlur = Number(this.conf.textBlur);
|
|
if (isNaN(this.textBlur) || this.textBlur < 0 || this.textBlur > 40) this.textBlur = 0;
|
|
}
|
|
this.textColor = this.conf.textColor || '';
|
|
},
|
|
defaultHandler: function (){
|
|
this.newLine();
|
|
|
|
if (this.lineBuffer != '') {
|
|
this.type('You typed: ' + this.lineBuffer);
|
|
this.newLine();
|
|
}
|
|
|
|
this.prompt();
|
|
},
|
|
open: function (){
|
|
if (this.termDivReady()) {
|
|
if (!this.closed) this._makeTerm();
|
|
|
|
this.init();
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
close: function (){
|
|
this.lock = true;
|
|
this.cursorOff();
|
|
if (this.exitHandler) this.exitHandler();
|
|
this.globals.setVisible(this.termDiv, 0);
|
|
this.closed = true;
|
|
},
|
|
init: function (){
|
|
// wait for gui
|
|
if (this.guiReady()) {
|
|
this.guiCounter = 0;
|
|
// clean up at re-entry
|
|
if (this.closed) {
|
|
this.setInitValues();
|
|
}
|
|
|
|
this.clear();
|
|
this.globals.setVisible(this.termDiv, 1);
|
|
this.globals.enableKeyboard(this);
|
|
|
|
if (this.initHandler) {
|
|
this.initHandler();
|
|
} else {
|
|
this.write(this.conf.greeting);
|
|
this.newLine();
|
|
this.prompt();
|
|
}
|
|
} else {
|
|
this.guiCounter++;
|
|
|
|
if (this.guiCounter > 18000) {
|
|
if (confirm('Terminal:\nYour browser hasn\'t responded for more than 2 minutes.\nRetry?')) {
|
|
this.guiCounter = 0;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.globals.termToInitialze = this;
|
|
|
|
window.setTimeout('Terminal.prototype.globals.termToInitialze.init()', 200);
|
|
}
|
|
},
|
|
getRowArray: function (l, v){
|
|
// returns a fresh array of l length initialized with value v
|
|
var a = [];
|
|
|
|
for (var i = 0; i < l; i++) a[i] = v;
|
|
|
|
return a;
|
|
},
|
|
wrapOn: function (){
|
|
// activate word wrap, wrapping workes with write() only!
|
|
this.wrapping = true;
|
|
},
|
|
wrapOff: function (){
|
|
this.wrapping = false;
|
|
},
|
|
setTextBlur: function (v){
|
|
var rerender = false;
|
|
|
|
if (typeof v === 'object' && v.length) {
|
|
var a = [];
|
|
|
|
for (var i = 0; i < v.length; i++) {
|
|
var b = Number(v[i]);
|
|
if (!isNaN(b) && b > 0) a.push(b);
|
|
}
|
|
|
|
this.textBlur = (a.length) ? a : 0;
|
|
|
|
rerender = true;
|
|
} else {
|
|
v = Number(v);
|
|
|
|
if (isNaN(v) || v < 0 || v > 40) v = 0;
|
|
|
|
if (v != this.textBlur) {
|
|
this.textBlur = v;
|
|
rerender = true;
|
|
}
|
|
}
|
|
|
|
if (rerender) {
|
|
for (var r = 0, l = this.conf.rows; r < l; r++) this.redraw(r);
|
|
}
|
|
},
|
|
setTextColor: function (v){
|
|
if (!v) v = '';
|
|
|
|
if (v != this.textColor) {
|
|
this.textColor = v;
|
|
|
|
for (var r = 0, l = this.conf.rows; r < l; r++) {
|
|
this.redraw(r);
|
|
}
|
|
}
|
|
},
|
|
// main output methods
|
|
type: function (text, style){
|
|
for (var i = 0; i < text.length; i++) {
|
|
var ch = text.charCodeAt(i);
|
|
|
|
if (!this.isPrintable(ch)) ch = 94;
|
|
|
|
this.charBuf[this.r][this.c] = ch;
|
|
this.styleBuf[this.r][this.c] = (style) ? style : 0;
|
|
|
|
var last_r = this.r;
|
|
|
|
this._incCol();
|
|
|
|
if (this.r != last_r) this.redraw(last_r);
|
|
}
|
|
|
|
this.redraw(this.r)
|
|
},
|
|
write: function (text, usemore){
|
|
var ta;
|
|
var opt;
|
|
|
|
// write to scroll buffer with markup
|
|
// new line = '%n' prepare any strings or arrys first
|
|
if (typeof text != 'object') {
|
|
if (typeof text != 'string') text = '' + text;
|
|
|
|
if (text.indexOf('\n') >= 0) {
|
|
ta = text.split('\n');
|
|
text = ta.join('%n');
|
|
}
|
|
}
|
|
else {
|
|
if (text.join) {
|
|
text = text.join('%n');
|
|
} else {
|
|
text = '' + text;
|
|
}
|
|
|
|
if (text.indexOf('\n') >= 0) {
|
|
ta = text.split('\n');
|
|
text = ta.join('%n');
|
|
}
|
|
}
|
|
|
|
if (this.mapANSI) text = this.globals.ANSI_map(text, this.ANSItrueBlack);
|
|
|
|
this._sbInit(usemore);
|
|
|
|
var chunks = text.split('%');
|
|
var esc = (text.charAt(0) != '%');
|
|
var style = 0;
|
|
var styleMarkUp = this.globals.termStyleMarkup;
|
|
|
|
for (var i = 0; i < chunks.length; i++) {
|
|
if (esc) {
|
|
if (chunks[i].length > 0) {
|
|
this._sbType(chunks[i], style);
|
|
} else if (i > 0) {
|
|
this._sbType('%', style);
|
|
}
|
|
|
|
esc = false;
|
|
} else {
|
|
var func = chunks[i].charAt(0);
|
|
|
|
if (chunks[i].length == 0 && i > 0) {
|
|
this._sbType('%', style);
|
|
|
|
esc = true;
|
|
} else if (func == 'n') {
|
|
this._sbNewLine(true);
|
|
|
|
if (chunks[i].length > 1) this._sbType(chunks[i].substring(1), style);
|
|
}
|
|
else if (func == '+') {
|
|
opt = chunks[i].charAt(1);
|
|
|
|
opt = opt.toLowerCase();
|
|
|
|
if (opt == 'p') {
|
|
style = 0;
|
|
} else if (styleMarkUp[opt]) {
|
|
style |= styleMarkUp[opt];
|
|
}
|
|
|
|
if (chunks[i].length > 2) this._sbType(chunks[i].substring(2), style);
|
|
}
|
|
else if (func == '-') {
|
|
opt = chunks[i].charAt(1);
|
|
opt = opt.toLowerCase();
|
|
|
|
if (opt == 'p') {
|
|
style = 0;
|
|
} else if (styleMarkUp[opt]) {
|
|
style &= ~styleMarkUp[opt];
|
|
}
|
|
|
|
if (chunks[i].length > 2) this._sbType(chunks[i].substring(2), style);
|
|
} else if (chunks[i].length > 1 && func == 'c') {
|
|
var cinfo = this._parseColor(chunks[i].substring(1));
|
|
|
|
style = (style & (~0xfffff0)) | cinfo.style;
|
|
|
|
if (cinfo.rest) this._sbType(cinfo.rest, style);
|
|
} else if (chunks[i].length > 1 && chunks[i].charAt(0) == 'C' && chunks[i].charAt(1) == 'S') {
|
|
this.clear();
|
|
this._sbInit();
|
|
|
|
if (chunks[i].length > 2) this._sbType(chunks[i].substring(2), style);
|
|
} else {
|
|
if (chunks[i].length > 0) this._sbType(chunks[i], style);
|
|
}
|
|
}
|
|
}
|
|
|
|
this._sbOut();
|
|
},
|
|
// parse a color markup
|
|
_parseColor: function (chunk){
|
|
var i;
|
|
var c;
|
|
var sc;
|
|
var cl;
|
|
var rest = '';
|
|
var style = 0;
|
|
|
|
if (chunk.length) {
|
|
if (chunk.charAt(0) == '(') {
|
|
var clabel = '';
|
|
|
|
for (i = 1; i < chunk.length; i++) {
|
|
c = chunk.charAt(i);
|
|
|
|
if (c == ')') {
|
|
if (chunk.length > i) rest = chunk.substring(i + 1);
|
|
break;
|
|
}
|
|
|
|
clabel += c;
|
|
}
|
|
|
|
if (clabel) {
|
|
if (clabel.charAt(0) == '@') {
|
|
sc = this.globals.nsColors[clabel.substring(1).toLowerCase()];
|
|
|
|
if (sc) style = (16 + sc) * 0x100;
|
|
} else if (clabel.charAt(0) == '#') {
|
|
cl = clabel.substring(1).toLowerCase();
|
|
sc = this.globals.webColors[cl];
|
|
|
|
if (sc) {
|
|
style = sc * 0x10000;
|
|
} else {
|
|
cl = this.globals.webifyColor(cl);
|
|
|
|
if (cl) style = this.globals.webColors[cl] * 0x10000;
|
|
}
|
|
} else if (clabel.length && clabel.length <= 2) {
|
|
var isHex = false;
|
|
|
|
for (i = 0; i < clabel.length; i++) {
|
|
if (this.globals.isHexOnlyChar(clabel.charAt(i))) {
|
|
isHex = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
cl = (isHex) ? parseInt(clabel, 16) : parseInt(clabel, 10);
|
|
|
|
if (!isNaN(cl) || cl <= 15) {
|
|
style = cl * 0x100;
|
|
}
|
|
} else {
|
|
style = this.globals.getColorCode(clabel) * 0x100;
|
|
}
|
|
}
|
|
} else {
|
|
c = chunk.charAt(0);
|
|
|
|
if (this.globals.isHexChar(c)) {
|
|
style = this.globals.hexToNum[c] * 0x100;
|
|
rest = chunk.substring(1);
|
|
} else {
|
|
rest = chunk;
|
|
}
|
|
}
|
|
}
|
|
|
|
return { rest: rest, style: style };
|
|
},
|
|
// internal scroll buffer output methods
|
|
_sbInit: function (usemore){
|
|
var sb = this.scrollBuf = {};
|
|
var sbl = sb.lines = [];
|
|
var sbs = sb.styles = [];
|
|
|
|
sb.more = usemore;
|
|
sb.line = 0;
|
|
sb.status = 0;
|
|
sb.r = 0;
|
|
sb.c = this.c;
|
|
sbl[0] = this.getRowArray(this.conf.cols, 0);
|
|
sbs[0] = this.getRowArray(this.conf.cols, 0);
|
|
|
|
for (var i = 0; i < this.c; i++) {
|
|
sbl[0][i] = this.charBuf[this.r][i];
|
|
sbs[0][i] = this.styleBuf[this.r][i];
|
|
}
|
|
},
|
|
_sbType: function (text, style){
|
|
// type to scroll buffer
|
|
var sb = this.scrollBuf;
|
|
|
|
for (var i = 0; i < text.length; i++) {
|
|
var ch = text.charCodeAt(i);
|
|
|
|
if (!this.isPrintable(ch)) ch = 94;
|
|
|
|
sb.lines[sb.r][sb.c] = ch;
|
|
sb.styles[sb.r][sb.c++] = (style) ? style : 0;
|
|
|
|
if (sb.c >= this.maxCols) this._sbNewLine();
|
|
}
|
|
},
|
|
_sbNewLine: function (forced){
|
|
var sb = this.scrollBuf;
|
|
|
|
if (this.wrapping && forced) {
|
|
sb.lines[sb.r][sb.c] = 10;
|
|
sb.lines[sb.r].length = sb.c + 1;
|
|
}
|
|
|
|
sb.r++;
|
|
sb.c = 0;
|
|
sb.lines[sb.r] = this.getRowArray(this.conf.cols, 0);
|
|
sb.styles[sb.r] = this.getRowArray(this.conf.cols, 0);
|
|
},
|
|
_sbWrap: function (){
|
|
// create a temp wrap buffer wb and scan for words/wrap-chars
|
|
// then re-asign lines & styles to scrollBuf
|
|
var wb = {};
|
|
|
|
wb.lines = [];
|
|
wb.styles = [];
|
|
wb.lines[0] = this.getRowArray(this.conf.cols, 0);
|
|
wb.styles[0] = this.getRowArray(this.conf.cols, 0);
|
|
wb.r = 0;
|
|
wb.c = 0;
|
|
|
|
var sb = this.scrollBuf;
|
|
var sbl = sb.lines;
|
|
var sbs = sb.styles;
|
|
var ch;
|
|
var st;
|
|
var wrap;
|
|
var lc;
|
|
var ls;
|
|
var l = this.c;
|
|
var lastR = 0;
|
|
var lastC = 0;
|
|
|
|
wb.cBreak = false;
|
|
|
|
for (var r = 0; r < sbl.length; r++) {
|
|
lc = sbl[r];
|
|
ls = sbs[r];
|
|
|
|
for (var c = 0; c < lc.length; c++) {
|
|
ch = lc[c];
|
|
st = ls[c];
|
|
|
|
if (ch) {
|
|
wrap = this.globals.wrapChars[ch];
|
|
|
|
if (ch == 10) wrap = 1;
|
|
|
|
if (wrap) {
|
|
if (wrap == 2) {
|
|
l++;
|
|
} else if (wrap == 4) {
|
|
l++;
|
|
lc[c] = 45;
|
|
}
|
|
|
|
this._wbOut(wb, lastR, lastC, l);
|
|
|
|
if (ch == 10) {
|
|
this._wbIncLine(wb);
|
|
} else if (wrap == 1 && wb.c < this.maxCols) {
|
|
wb.lines[wb.r][wb.c] = ch;
|
|
wb.styles[wb.r][wb.c++] = st;
|
|
|
|
if (wb.c >= this.maxCols) this._wbIncLine(wb);
|
|
}
|
|
|
|
if (wrap == 3) {
|
|
lastR = r;
|
|
lastC = c;
|
|
l = 1;
|
|
} else {
|
|
l = 0;
|
|
lastR = r;
|
|
lastC = c + 1;
|
|
if (lastC == lc.length) {
|
|
lastR++;
|
|
lastC = 0;
|
|
}
|
|
|
|
if (wrap == 4) wb.cBreak = true;
|
|
}
|
|
} else {
|
|
l++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (l) {
|
|
if (wb.cBreak && wb.c != 0) wb.c--;
|
|
|
|
this._wbOut(wb, lastR, lastC, l);
|
|
}
|
|
|
|
sb.lines = wb.lines;
|
|
sb.styles = wb.styles;
|
|
sb.r = wb.r;
|
|
sb.c = wb.c;
|
|
},
|
|
_wbOut: function (wb, br, bc, l){
|
|
// copy a word (of l length from br/bc) to wrap buffer wb
|
|
var sb = this.scrollBuf;
|
|
var sbl = sb.lines;
|
|
var sbs = sb.styles;
|
|
var ofs = 0;
|
|
var lc;
|
|
var ls;
|
|
var i;
|
|
|
|
if (l + wb.c > this.maxCols) {
|
|
if (l < this.maxCols) {
|
|
this._wbIncLine(wb);
|
|
} else {
|
|
var i0 = 0;
|
|
|
|
ofs = this.maxCols - wb.c;
|
|
lc = sbl[br];
|
|
ls = sbs[br];
|
|
|
|
while (true) {
|
|
for (i = i0; i < ofs; i++) {
|
|
wb.lines[wb.r][wb.c] = lc[bc];
|
|
wb.styles[wb.r][wb.c++] = ls[bc++];
|
|
|
|
if (bc == sbl[br].length) {
|
|
bc = 0;
|
|
br++;
|
|
lc = sbl[br];
|
|
ls = sbs[br];
|
|
}
|
|
}
|
|
|
|
this._wbIncLine(wb);
|
|
|
|
if (l - ofs < this.maxCols) break;
|
|
i0 = ofs;
|
|
ofs += this.maxCols;
|
|
}
|
|
}
|
|
} else if (wb.cBreak) {
|
|
wb.c--;
|
|
}
|
|
|
|
lc = sbl[br];
|
|
ls = sbs[br];
|
|
|
|
for (i = ofs; i < l; i++) {
|
|
wb.lines[wb.r][wb.c] = lc[bc];
|
|
wb.styles[wb.r][wb.c++] = ls[bc++];
|
|
|
|
if (bc == sbl[br].length) {
|
|
bc = 0;
|
|
br++;
|
|
lc = sbl[br];
|
|
ls = sbs[br];
|
|
}
|
|
}
|
|
|
|
wb.cBreak = false;
|
|
},
|
|
_wbIncLine: function (wb){
|
|
// create a new line in temp buffer
|
|
wb.r++;
|
|
wb.c = 0;
|
|
wb.lines[wb.r] = this.getRowArray(this.conf.cols, 0);
|
|
wb.styles[wb.r] = this.getRowArray(this.conf.cols, 0);
|
|
},
|
|
_sbOut: function (){
|
|
var sb = this.scrollBuf;
|
|
|
|
if (this.wrapping && !sb.status) this._sbWrap();
|
|
|
|
var i;
|
|
var r;
|
|
var ofs;
|
|
var sbl = sb.lines;
|
|
var sbs = sb.styles;
|
|
var tcb = this.charBuf;
|
|
var tsb = this.styleBuf;
|
|
var ml = this.maxLines;
|
|
var buflen = sbl.length;
|
|
|
|
if (sb.more) {
|
|
if (sb.status) {
|
|
if (this.inputChar == this.globals.lcMoreKeyAbort) {
|
|
this.r = ml - 1;
|
|
this.c = 0;
|
|
tcb[this.r] = this.getRowArray(this.conf.cols, 0);
|
|
tsb[this.r] = this.getRowArray(this.conf.cols, 0);
|
|
|
|
this.redraw(this.r);
|
|
|
|
this.handler = sb.handler;
|
|
this.charMode = false;
|
|
this.inputChar = 0;
|
|
this.scrollBuf = null;
|
|
|
|
this.prompt();
|
|
|
|
return;
|
|
} else if (this.inputChar == this.globals.lcMoreKeyContinue) {
|
|
this.clear();
|
|
} else {
|
|
return;
|
|
}
|
|
} else {
|
|
if (this.r >= ml - 1) this.clear();
|
|
}
|
|
}
|
|
|
|
if (this.r + buflen - sb.line <= ml) {
|
|
for (i = sb.line; i < buflen; i++) {
|
|
r = this.r + i - sb.line;
|
|
|
|
tcb[r] = sbl[i];
|
|
tsb[r] = sbs[i];
|
|
|
|
this.redraw(r);
|
|
}
|
|
|
|
this.r += sb.r - sb.line;
|
|
this.c = sb.c;
|
|
|
|
if (sb.more) {
|
|
if (sb.status) this.handler = sb.handler;
|
|
|
|
this.charMode = false;
|
|
this.inputChar = 0;
|
|
this.scrollBuf = null;
|
|
|
|
this.prompt();
|
|
|
|
return;
|
|
}
|
|
}
|
|
else if (sb.more) {
|
|
ml--;
|
|
|
|
if (sb.status == 0) {
|
|
sb.handler = this.handler;
|
|
this.handler = this._sbOut;
|
|
this.charMode = true;
|
|
sb.status = 1;
|
|
}
|
|
|
|
if (this.r) {
|
|
ofs = ml - this.r;
|
|
|
|
for (i = sb.line; i < ofs; i++) {
|
|
r = this.r + i - sb.line;
|
|
tcb[r] = sbl[i];
|
|
tsb[r] = sbs[i];
|
|
|
|
this.redraw(r);
|
|
}
|
|
}
|
|
else {
|
|
ofs = sb.line + ml;
|
|
|
|
for (i = sb.line; i < ofs; i++) {
|
|
r = this.r + i - sb.line;
|
|
tcb[r] = sbl[i];
|
|
tsb[r] = sbs[i];
|
|
|
|
this.redraw(r);
|
|
}
|
|
}
|
|
|
|
sb.line = ofs;
|
|
this.r = ml;
|
|
this.c = 0;
|
|
this.type(this.globals.lcMorePrompt1, this.globals.lcMorePromtp1Style);
|
|
this.type(this.globals.lcMorePrompt2, this.globals.lcMorePrompt2Style);
|
|
this.lock = false;
|
|
|
|
return;
|
|
}
|
|
else if (buflen >= ml) {
|
|
ofs = buflen - ml;
|
|
|
|
for (i = 0; i < ml; i++) {
|
|
r = ofs + i;
|
|
tcb[i] = sbl[r];
|
|
tsb[i] = sbs[r];
|
|
|
|
this.redraw(i);
|
|
}
|
|
|
|
this.r = ml - 1;
|
|
this.c = sb.c;
|
|
}
|
|
else {
|
|
var dr = ml - buflen;
|
|
|
|
ofs = this.r - dr;
|
|
|
|
for (i = 0; i < dr; i++) {
|
|
r = ofs + i;
|
|
|
|
for (var c = 0; c < this.maxCols; c++) {
|
|
tcb[i][c] = tcb[r][c];
|
|
tsb[i][c] = tsb[r][c];
|
|
}
|
|
|
|
this.redraw(i);
|
|
}
|
|
|
|
for (i = 0; i < buflen; i++) {
|
|
r = dr + i;
|
|
tcb[r] = sbl[i];
|
|
tsb[r] = sbs[i];
|
|
|
|
this.redraw(r);
|
|
}
|
|
|
|
this.r = ml - 1;
|
|
this.c = sb.c;
|
|
}
|
|
|
|
this.scrollBuf = null;
|
|
},
|
|
|
|
// basic console output
|
|
typeAt: function (r, c, text, style){
|
|
var tr1 = this.r;
|
|
var tc1 = this.c;
|
|
|
|
this.cursorSet(r, c);
|
|
|
|
for (var i = 0; i < text.length; i++) {
|
|
var ch = text.charCodeAt(i);
|
|
|
|
if (!this.isPrintable(ch)) ch = 94;
|
|
|
|
this.charBuf[this.r][this.c] = ch;
|
|
this.styleBuf[this.r][this.c] = (style) ? style : 0;
|
|
|
|
var last_r = this.r;
|
|
|
|
this._incCol();
|
|
|
|
if (this.r != last_r) this.redraw(last_r);
|
|
}
|
|
|
|
this.redraw(this.r);
|
|
|
|
this.r = tr1;
|
|
this.c = tc1;
|
|
},
|
|
statusLine: function (text, style, offset){
|
|
var ch, r;
|
|
|
|
style = (style && !isNaN(style)) ? parseInt(style) & 15 : 0;
|
|
|
|
if (offset && offset > 0) {
|
|
r = this.conf.rows - offset;
|
|
} else {
|
|
r = this.conf.rows - 1;
|
|
}
|
|
|
|
for (var i = 0; i < this.conf.cols; i++) {
|
|
if (i < text.length) {
|
|
ch = text.charCodeAt(i);
|
|
if (!this.isPrintable(ch)) ch = 94;
|
|
} else {
|
|
ch = 0;
|
|
}
|
|
|
|
this.charBuf[r][i] = ch;
|
|
this.styleBuf[r][i] = style;
|
|
}
|
|
|
|
this.redraw(r);
|
|
},
|
|
printRowFromString: function (r, text, style){
|
|
var ch;
|
|
|
|
style = (style && !isNaN(style)) ? parseInt(style) & 15 : 0;
|
|
|
|
if (r >= 0 && r < this.maxLines) {
|
|
if (typeof text != 'string') text = '' + text;
|
|
|
|
for (var i = 0; i < this.conf.cols; i++) {
|
|
if (i < text.length) {
|
|
ch = text.charCodeAt(i);
|
|
if (!this.isPrintable(ch)) ch = 94;
|
|
} else {
|
|
ch = 0;
|
|
}
|
|
|
|
this.charBuf[r][i] = ch;
|
|
this.styleBuf[r][i] = style;
|
|
}
|
|
|
|
this.redraw(r);
|
|
}
|
|
},
|
|
setChar: function (ch, r, c, style){
|
|
this.charBuf[r][c] = ch;
|
|
this.styleBuf[r][c] = (style) ? style : 0;
|
|
|
|
this.redraw(r);
|
|
},
|
|
newLine: function (){
|
|
this.c = 0;
|
|
|
|
this._incRow();
|
|
},
|
|
// internal methods for output
|
|
_charOut: function (ch, style){
|
|
this.charBuf[this.r][this.c] = ch;
|
|
this.styleBuf[this.r][this.c] = (style) ? style : 0;
|
|
|
|
this.redraw(this.r);
|
|
this._incCol();
|
|
},
|
|
_incCol: function (){
|
|
this.c++;
|
|
|
|
if (this.c >= this.maxCols) {
|
|
this.c = 0;
|
|
this._incRow();
|
|
}
|
|
},
|
|
_incRow: function (){
|
|
this.r++;
|
|
|
|
if (this.r >= this.maxLines) {
|
|
this._scrollLines(0, this.maxLines);
|
|
this.r = this.maxLines - 1;
|
|
}
|
|
},
|
|
_scrollLines: function (start, end){
|
|
start++;
|
|
|
|
var rt;
|
|
|
|
for (var ri = start; ri < end; ri++) {
|
|
rt = ri - 1;
|
|
this.charBuf[rt] = this.charBuf[ri];
|
|
this.styleBuf[rt] = this.styleBuf[ri];
|
|
}
|
|
|
|
// clear last line
|
|
rt = end - 1;
|
|
this.charBuf[rt] = this.getRowArray(this.conf.cols, 0);
|
|
this.styleBuf[rt] = this.getRowArray(this.conf.cols, 0);
|
|
|
|
this.redraw(rt);
|
|
|
|
for (var r = end - 1; r >= start; r--) this.redraw(r - 1);
|
|
},
|
|
// control methods
|
|
clear: function (){
|
|
this.cursorOff();
|
|
|
|
this.insert = false;
|
|
|
|
for (var ri = 0; ri < this.maxLines; ri++) {
|
|
this.charBuf[ri] = this.getRowArray(this.conf.cols, 0);
|
|
this.styleBuf[ri] = this.getRowArray(this.conf.cols, 0);
|
|
this.redraw(ri);
|
|
}
|
|
|
|
this.r = 0;
|
|
this.c = 0;
|
|
},
|
|
reset: function (){
|
|
if (this.lock) return;
|
|
|
|
this.lock = true;
|
|
this.rawMode = false;
|
|
this.charMode = false;
|
|
this.maxLines = this.conf.rows;
|
|
this.maxCols = this.conf.cols;
|
|
this.lastLine = '';
|
|
this.lineBuffer = '';
|
|
this.inputChar = 0;
|
|
|
|
this.clear();
|
|
},
|
|
prompt: function (){
|
|
this.lock = true;
|
|
|
|
if (this.c > 0) this.newLine();
|
|
|
|
this.type(this.ps);
|
|
this._charOut(1);
|
|
|
|
this.lock = false;
|
|
|
|
this.cursorOn();
|
|
},
|
|
isPrintable: function (ch, unicodePage1only){
|
|
if (this.wrapping && this.globals.wrapChars[ch] == 4) return true;
|
|
|
|
if (unicodePage1only && ch > 255) {
|
|
return (ch == this.termKey.EURO && this.printEuro) ? true : false;
|
|
}
|
|
|
|
return (
|
|
(ch >= 32 && ch != this.termKey.DEL) ||
|
|
(this.printTab && ch == this.termKey.TAB)
|
|
);
|
|
},
|
|
// cursor methods
|
|
cursorSet: function (r, c){
|
|
var crsron = this.cursoractive;
|
|
|
|
if (crsron) this.cursorOff();
|
|
|
|
this.r = r % this.maxLines;
|
|
this.c = c % this.maxCols;
|
|
|
|
this._cursorReset(crsron);
|
|
},
|
|
cursorOn: function (){
|
|
if (this.blinkTimer) clearTimeout(this.blinkTimer);
|
|
|
|
this.blinkBuffer = this.styleBuf[this.r][this.c];
|
|
|
|
this._cursorBlink();
|
|
|
|
this.cursoractive = true;
|
|
},
|
|
cursorOff: function (){
|
|
if (this.blinkTimer) clearTimeout(this.blinkTimer);
|
|
|
|
if (this.cursoractive) {
|
|
this.styleBuf[this.r][this.c] = this.blinkBuffer;
|
|
|
|
this.redraw(this.r);
|
|
|
|
this.cursoractive = false;
|
|
}
|
|
},
|
|
cursorLeft: function (){
|
|
var crsron = this.cursoractive;
|
|
|
|
if (crsron) this.cursorOff();
|
|
|
|
var r = this.r;
|
|
var c = this.c;
|
|
|
|
if (c > 0) {
|
|
c--;
|
|
} else if (r > 0) {
|
|
c = this.maxCols - 1;
|
|
r--;
|
|
}
|
|
|
|
if (this.isPrintable(this.charBuf[r][c])) {
|
|
this.r = r;
|
|
this.c = c;
|
|
}
|
|
|
|
this.insert = true;
|
|
|
|
this._cursorReset(crsron);
|
|
},
|
|
cursorRight: function (){
|
|
var crsron = this.cursoractive;
|
|
|
|
if (crsron) this.cursorOff();
|
|
|
|
var r = this.r;
|
|
var c = this.c;
|
|
|
|
if (c < this.maxCols - 1) {
|
|
c++;
|
|
} else if (r < this.maxLines - 1) {
|
|
c = 0;
|
|
r++;
|
|
}
|
|
|
|
if (!this.isPrintable(this.charBuf[r][c])) {
|
|
this.insert = false;
|
|
}
|
|
|
|
if (this.isPrintable(this.charBuf[this.r][this.c])) {
|
|
this.r = r;
|
|
this.c = c;
|
|
}
|
|
|
|
this._cursorReset(crsron);
|
|
},
|
|
backspace: function (){
|
|
var crsron = this.cursoractive;
|
|
|
|
if (crsron) this.cursorOff();
|
|
|
|
var r = this.r;
|
|
var c = this.c;
|
|
|
|
if (c > 0) {
|
|
c--;
|
|
} else if (r > 0) {
|
|
c = this.maxCols - 1;
|
|
r--;
|
|
}
|
|
|
|
if (this.isPrintable(this.charBuf[r][c])) {
|
|
this._scrollLeft(r, c);
|
|
this.r = r;
|
|
this.c = c;
|
|
}
|
|
|
|
this._cursorReset(crsron);
|
|
},
|
|
fwdDelete: function (){
|
|
var crsron = this.cursoractive;
|
|
|
|
if (crsron) this.cursorOff();
|
|
|
|
if (this.isPrintable(this.charBuf[this.r][this.c])) {
|
|
this._scrollLeft(this.r, this.c);
|
|
|
|
if (!this.isPrintable(this.charBuf[this.r][this.c])) this.insert = false;
|
|
}
|
|
|
|
this._cursorReset(crsron);
|
|
},
|
|
_cursorReset: function (crsron){
|
|
if (crsron) {
|
|
this.cursorOn();
|
|
} else {
|
|
this.blinkBuffer = this.styleBuf[this.r][this.c];
|
|
}
|
|
},
|
|
_cursorBlink: function (){
|
|
if (this.blinkTimer) clearTimeout(this.blinkTimer);
|
|
|
|
if (this == this.globals.activeTerm) {
|
|
if (this.crsrBlockMode) {
|
|
this.styleBuf[this.r][this.c] = (this.styleBuf[this.r][this.c] & 1) ?
|
|
this.styleBuf[this.r][this.c] & 0xfffffe : this.styleBuf[this.r][this.c] | 1;
|
|
} else {
|
|
this.styleBuf[this.r][this.c] = (this.styleBuf[this.r][this.c] & 2) ?
|
|
this.styleBuf[this.r][this.c] & 0xffffd : this.styleBuf[this.r][this.c] | 2;
|
|
}
|
|
|
|
this.redraw(this.r);
|
|
}
|
|
|
|
if (this.crsrBlinkMode) {
|
|
this.blinkTimer = setTimeout('Terminal.prototype.globals.activeTerm._cursorBlink()', this.blinkDelay);
|
|
}
|
|
},
|
|
_scrollLeft: function (r, c){
|
|
var rows = [];
|
|
|
|
rows[0] = r;
|
|
|
|
while (this.isPrintable(this.charBuf[r][c])) {
|
|
var ri = r;
|
|
var ci = c + 1;
|
|
|
|
if (ci == this.maxCols) {
|
|
if (ri < this.maxLines - 1) {
|
|
ci = 0;
|
|
ri++;
|
|
rows[rows.length] = ri;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
this.charBuf[r][c] = this.charBuf[ri][ci];
|
|
this.styleBuf[r][c] = this.styleBuf[ri][ci];
|
|
c = ci;
|
|
r = ri;
|
|
}
|
|
|
|
if (this.charBuf[r][c] != 0) this.charBuf[r][c] = 0;
|
|
|
|
for (var i = 0; i < rows.length; i++) this.redraw(rows[i]);
|
|
},
|
|
_scrollRight: function (r, c){
|
|
var rows = [];
|
|
var end = this._getLineEnd(r, c);
|
|
var ri = end[0];
|
|
var ci = end[1];
|
|
|
|
if (ci == this.maxCols - 1 && ri == this.maxLines - 1) {
|
|
if (r == 0) return;
|
|
|
|
this._scrollLines(0, this.maxLines);
|
|
|
|
this.r--;
|
|
r--;
|
|
ri--;
|
|
}
|
|
|
|
rows[r] = 1;
|
|
|
|
while (this.isPrintable(this.charBuf[ri][ci])) {
|
|
var rt = ri;
|
|
var ct = ci + 1;
|
|
|
|
if (ct == this.maxCols) {
|
|
ct = 0;
|
|
rt++;
|
|
rows[rt] = 1;
|
|
}
|
|
|
|
this.charBuf[rt][ct] = this.charBuf[ri][ci];
|
|
this.styleBuf[rt][ct] = this.styleBuf[ri][ci];
|
|
|
|
if (ri == r && ci == c) break;
|
|
|
|
ci--;
|
|
|
|
if (ci < 0) {
|
|
ci = this.maxCols - 1;
|
|
ri--;
|
|
rows[ri] = 1;
|
|
}
|
|
}
|
|
|
|
for (var i = r; i < this.maxLines; i++) {
|
|
if (rows[i]) this.redraw(i);
|
|
}
|
|
},
|
|
_getLineEnd: function (r, c){
|
|
if (!this.isPrintable(this.charBuf[r][c])) {
|
|
c--;
|
|
|
|
if (c < 0) {
|
|
if (r > 0) {
|
|
r--;
|
|
c = this.maxCols - 1;
|
|
} else {
|
|
c = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.isPrintable(this.charBuf[r][c])) {
|
|
while (true) {
|
|
var ri = r;
|
|
var ci = c + 1;
|
|
|
|
if (ci == this.maxCols) {
|
|
if (ri < this.maxLines - 1) {
|
|
ri++;
|
|
ci = 0;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!this.isPrintable(this.charBuf[ri][ci])) break;
|
|
|
|
c = ci;
|
|
r = ri;
|
|
}
|
|
}
|
|
|
|
return [r, c];
|
|
},
|
|
_getLineStart: function (r, c){
|
|
// not used by now, just in case anyone needs this ...
|
|
var ci, ri;
|
|
|
|
if (!this.isPrintable(this.charBuf[r][c])) {
|
|
ci = c - 1;
|
|
ri = r;
|
|
|
|
if (ci < 0) {
|
|
if (ri == 0) return [0, 0];
|
|
|
|
ci = this.maxCols - 1;
|
|
ri--;
|
|
}
|
|
|
|
if (!this.isPrintable(this.charBuf[ri][ci])) {
|
|
return [r, c];
|
|
} else {
|
|
r = ri;
|
|
c = ci;
|
|
}
|
|
}
|
|
|
|
while (true) {
|
|
ri = r;
|
|
ci = c - 1;
|
|
|
|
if (ci < 0) {
|
|
if (ri == 0) break;
|
|
|
|
ci = this.maxCols - 1;
|
|
ri--;
|
|
}
|
|
|
|
if (!this.isPrintable(this.charBuf[ri][ci])) break;
|
|
|
|
r = ri;
|
|
c = ci;
|
|
}
|
|
|
|
return [r, c];
|
|
},
|
|
_getLine: function (adjustCrsrPos){
|
|
var end = this._getLineEnd(this.r, this.c);
|
|
var r = end[0];
|
|
var c = end[1];
|
|
|
|
if (adjustCrsrPos && (this.r != r || this.c != c + 1)) {
|
|
this.r = r;
|
|
this.c = c + 1;
|
|
|
|
if (this.c >= this.maxCols) this.c = this.maxCols - 1;
|
|
}
|
|
|
|
var line = [];
|
|
|
|
while (this.isPrintable(this.charBuf[r][c])) {
|
|
line[line.length] = String.fromCharCode(this.charBuf[r][c]);
|
|
|
|
if (c > 0) {
|
|
c--;
|
|
} else if (r > 0) {
|
|
c = this.maxCols - 1;
|
|
r--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
line.reverse();
|
|
|
|
return line.join('');
|
|
},
|
|
_clearLine: function (){
|
|
var end = this._getLineEnd(this.r, this.c);
|
|
var r = end[0];
|
|
var c = end[1];
|
|
|
|
while (this.isPrintable(this.charBuf[r][c])) {
|
|
this.charBuf[r][c] = 0;
|
|
|
|
if (c > 0) {
|
|
c--;
|
|
} else if (r > 0) {
|
|
this.redraw(r);
|
|
|
|
c = this.maxCols - 1;
|
|
r--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (r != end[0]) this.redraw(r);
|
|
|
|
c++;
|
|
|
|
this.cursorSet(r, c);
|
|
|
|
this.insert = false;
|
|
},
|
|
// backup/restore screen & state
|
|
backupScreen: function (){
|
|
var backup = this.backupBuffer = {};
|
|
var rl = this.conf.rows;
|
|
var cl = this.conf.cols;
|
|
|
|
backup.cbuf = new Array(rl);
|
|
backup.sbuf = new Array(rl);
|
|
backup.maxCols = this.maxCols;
|
|
backup.maxLines = this.maxLines;
|
|
backup.r = this.r;
|
|
backup.c = this.c;
|
|
backup.charMode = this.charMode;
|
|
backup.rawMode = this.rawMode;
|
|
backup.handler = this.handler;
|
|
backup.ctrlHandler = this.ctrlHandler;
|
|
backup.cursoractive = this.cursoractive;
|
|
backup.crsrBlinkMode = this.crsrBlinkMode;
|
|
backup.crsrBlockMode = this.crsrBlockMode;
|
|
backup.blinkDelay = this.blinkDelay;
|
|
backup.DELisBS = this.DELisBS;
|
|
backup.printTab = this.printTab;
|
|
backup.printEuro = this.printEuro;
|
|
backup.catchCtrlH = this.catchCtrlH;
|
|
backup.closeOnESC = this.closeOnESC;
|
|
backup.historyUnique = this.historyUnique;
|
|
backup.ps = this.ps;
|
|
backup.lineBuffer = this.lineBuffer;
|
|
backup.inputChar = this.inputChar;
|
|
backup.lastLine = this.lastLine;
|
|
backup.historyLength = this.history.length;
|
|
backup.histPtr = this.histPtr;
|
|
backup.wrapping = this.wrapping;
|
|
backup.mapANSI = this.mapANSI;
|
|
backup.ANSItrueBlack = this.ANSItrueBlack;
|
|
|
|
if (this.cursoractive) this.cursorOff();
|
|
|
|
for (var r = 0; r < rl; r++) {
|
|
var cbr = this.charBuf[r];
|
|
var sbr = this.styleBuf[r];
|
|
var tcbr = backup.cbuf[r] = new Array(cl);
|
|
var tsbr = backup.sbuf[r] = new Array(cl);
|
|
|
|
for (var c = 0; c < cl; c++) {
|
|
tcbr[c] = cbr[c];
|
|
tsbr[c] = sbr[c];
|
|
}
|
|
}
|
|
},
|
|
restoreScreen: function (){
|
|
var backup = this.backupBuffer;
|
|
|
|
if (!backup) return;
|
|
|
|
var rl = this.conf.rows;
|
|
|
|
for (var r = 0; r < rl; r++) {
|
|
this.charBuf[r] = backup.cbuf[r];
|
|
this.styleBuf[r] = backup.sbuf[r];
|
|
|
|
this.redraw(r);
|
|
}
|
|
|
|
this.maxCols = backup.maxCols;
|
|
this.maxLines = backup.maxLines;
|
|
this.r = backup.r;
|
|
this.c = backup.c;
|
|
this.charMode = backup.charMode;
|
|
this.rawMode = backup.rawMode;
|
|
this.handler = backup.handler;
|
|
this.ctrlHandler = backup.ctrlHandler;
|
|
this.cursoractive = backup.cursoractive;
|
|
this.crsrBlinkMode = backup.crsrBlinkMode;
|
|
this.crsrBlockMode = backup.crsrBlockMode;
|
|
this.blinkDelay = backup.blinkDelay;
|
|
this.DELisBS = backup.DELisBS;
|
|
this.printTab = backup.printTab;
|
|
this.printEuro = backup.printEuro;
|
|
this.catchCtrlH = backup.catchCtrlH;
|
|
this.closeOnESC = backup.closeOnESC;
|
|
this.historyUnique = backup.historyUnique;
|
|
this.ps = backup.ps;
|
|
this.lineBuffer = backup.lineBuffer;
|
|
this.inputChar = backup.inputChar;
|
|
this.lastLine = backup.lastLine;
|
|
|
|
if (this.history.length > backup.historyLength) {
|
|
this.history.length = backup.historyLength;
|
|
this.histPtr = backup.histPtr;
|
|
}
|
|
|
|
this.wrapping = backup.wrapping;
|
|
this.mapANSI = backup.mapANSI;
|
|
this.ANSItrueBlack = backup.ANSItrueBlack;
|
|
|
|
if (this.cursoractive) this.cursorOn();
|
|
|
|
this.backupBuffer = null;
|
|
},
|
|
swapBackup: function (){
|
|
// swap current state and backup buffer (e.g.: toggle do/undo)
|
|
var backup = this.backupBuffer;
|
|
|
|
this.backupScreen();
|
|
|
|
if (backup) {
|
|
var backup2 = this.backupBuffer;
|
|
|
|
this.backupBuffer = backup;
|
|
|
|
this.restoreScreen();
|
|
|
|
this.backupBuffer = backup2;
|
|
}
|
|
},
|
|
// simple markup escaping
|
|
escapeMarkup: function (t){
|
|
return t.replace(/%/g, '%%');
|
|
},
|
|
// field mode
|
|
enterFieldMode: function (start, end, style){
|
|
this.cursorOff();
|
|
|
|
if (start === undefined || start < 0) start = this.c;
|
|
|
|
if (end === undefined || end < start || end > this.maxCols) end = this.maxCols;
|
|
|
|
if (!style) style = 0;
|
|
|
|
this.fieldStart = start;
|
|
this.fieldEnd = end;
|
|
this.fieldStyle = style;
|
|
this.fieldC = 0;
|
|
this.lastLine = '';
|
|
this.fieldMode = true;
|
|
this.rawMode = this.charMode = false;
|
|
|
|
if (style & 1) {
|
|
this._crsrWasBlockMode = this.crsrBlockMode;
|
|
this._crsrWasBlinkMode = this.crsrBlinkMode;
|
|
this.crsrBlockMode = false;
|
|
this.crsrBlinkMode = true;
|
|
}
|
|
|
|
this.drawField();
|
|
this.lock = false;
|
|
},
|
|
exitFieldMode: function (){
|
|
this.drawField(true);
|
|
|
|
this.fieldMode = false;
|
|
this.c = this.fieldEnd;
|
|
|
|
if (this.c == this.maxLine) this.newLine();
|
|
|
|
this.lock = true;
|
|
},
|
|
drawField: function (isfinal){
|
|
this.cursorOff();
|
|
|
|
if (isfinal) this.fieldC = 0;
|
|
|
|
var fl = this.fieldEnd - this.fieldStart;
|
|
|
|
if (this.fieldC == this.lastLine.length) fl--;
|
|
|
|
var ofs = this.fieldC - fl;
|
|
|
|
if (ofs < 0) ofs = 0;
|
|
|
|
var line = (ofs) ? this.lastLine.substring(ofs) : this.lastLine;
|
|
var sb = this.styleBuf[this.r];
|
|
var cb = this.charBuf[this.r];
|
|
var max = line.length;
|
|
|
|
for (var i = this.fieldStart, k = 0; i < this.fieldEnd; i++, k++) {
|
|
sb[i] = this.fieldStyle;
|
|
cb[i] = (k < max) ? line.charCodeAt(k) : 0;
|
|
}
|
|
|
|
this.redraw(this.r);
|
|
|
|
if (isfinal) {
|
|
if (this.fieldStyle & 1) {
|
|
this.crsrBlockMode = this._crsrWasBlockMode;
|
|
this.crsrBlinkMode = this._crsrWasBlinkMode;
|
|
|
|
delete this._crsrWasBlockMode;
|
|
delete this._crsrWasBlinkMode;
|
|
}
|
|
} else {
|
|
this.c = this.fieldStart + this.fieldC - ofs;
|
|
|
|
this.cursorOn();
|
|
}
|
|
},
|
|
// keyboard focus
|
|
focus: function (){
|
|
this.globals.setFocus(this);
|
|
},
|
|
// a inner reference (just for comfort) to be mapped to Terminal.prototype.globals.termKey
|
|
termKey: null,
|
|
// GUI related methods
|
|
_makeTerm: function (rebuild){
|
|
var c;
|
|
var r;
|
|
var rstr = '';
|
|
var divPrefix = this.termDiv + '_r';
|
|
|
|
if (this.domAPI) {
|
|
// if applicable we're using createElement
|
|
this.globals.hasSubDivs = false;
|
|
var row, table, tbody, ptd, table2, tbody2, tr, td, node;
|
|
|
|
table = document.createElement('table');
|
|
|
|
table.setAttribute('border', 0);
|
|
table.setAttribute('cellSpacing', 0);
|
|
table.setAttribute('cellPadding', this.conf.frameWidth);
|
|
|
|
tbody = document.createElement('tbody');
|
|
|
|
table.appendChild(tbody);
|
|
|
|
row = document.createElement('tr');
|
|
|
|
tbody.appendChild(row);
|
|
|
|
ptd = document.createElement('td');
|
|
ptd.style.backgroundColor = this.conf.frameColor;
|
|
|
|
row.appendChild(ptd);
|
|
|
|
table2 = document.createElement('table');
|
|
|
|
table2.setAttribute('border', 0);
|
|
table2.setAttribute('cellSpacing', 0);
|
|
table2.setAttribute('cellPadding', 2);
|
|
|
|
tbody2 = document.createElement('tbody');
|
|
|
|
table2.appendChild(tbody2);
|
|
|
|
tr = document.createElement('tr');
|
|
|
|
tbody2.appendChild(tr);
|
|
|
|
td = document.createElement('td');
|
|
td.style.backgroundColor = this.conf.bgColor;
|
|
|
|
tr.appendChild(td);
|
|
ptd.appendChild(table2);
|
|
|
|
ptd = td;
|
|
table2 = document.createElement('table');
|
|
|
|
table2.setAttribute('border', 0);
|
|
table2.setAttribute('cellSpacing', 0);
|
|
table2.setAttribute('cellPadding', 0);
|
|
|
|
tbody2 = document.createElement('tbody');
|
|
|
|
table2.appendChild(tbody2);
|
|
|
|
for (c = 0; c < this.conf.cols; c++) rstr += ' ';
|
|
|
|
for (r = 0; r < this.conf.rows; r++) {
|
|
tr = document.createElement('tr');
|
|
td = document.createElement('td');
|
|
td.id = divPrefix + r;
|
|
td.style.height = td.style.minHeight = td.style.maxHeight = this.conf.rowHeight;
|
|
td.style.whiteSpace = 'nowrap';
|
|
td.className = this.conf.fontClass;
|
|
td.innerHTML = rstr;
|
|
|
|
tr.appendChild(td);
|
|
tbody2.appendChild(tr);
|
|
}
|
|
|
|
ptd.appendChild(table2);
|
|
|
|
node = document.getElementById(this.termDiv);
|
|
|
|
while (node.hasChildNodes()) node.removeChild(node.firstChild);
|
|
|
|
node.appendChild(table);
|
|
} else {
|
|
// legacy code
|
|
this.globals.hasSubDivs = (navigator.userAgent.indexOf('Gecko') < 0);
|
|
|
|
var s = '';
|
|
var bgColorAttribute = (this.conf.bgColor && (this.conf.bgColor !== 'none'
|
|
|| this.conf.bgColor != 'transparent')) ? ' bgcolor="' + this.conf.bgColor + '"' : '';
|
|
var frameColorAttribute = (this.conf.frameColor && (this.conf.frameColor !== 'none'
|
|
|| this.conf.frameColor != 'transparent')) ? ' bgcolor="' + this.conf.frameColor + '"' : '';
|
|
|
|
s += '<table border="0" cellspacing="0" cellpadding="' + this.conf.frameWidth + '">\n';
|
|
s += '<tr><td' + frameColorAttribute + '><table border="0" cellspacing="0" cellpadding="2"><tr><td'
|
|
+ bgColorAttribute + '><table border="0" cellspacing="0" cellpadding="0">\n';
|
|
|
|
for (c = 0; c < this.conf.cols; c++) rstr += ' ';
|
|
|
|
for (r = 0; r < this.conf.rows; r++) {
|
|
var termid = (this.globals.hasSubDivs) ? '' : ' id="' + divPrefix + r + '"';
|
|
|
|
s += '<tr><td nowrap height="' + this.conf.rowHeight + '"' + termid + ' class="'
|
|
+ this.conf.fontClass + '">' + rstr + '<\/td><\/tr>\n';
|
|
}
|
|
|
|
s += '<\/table><\/td><\/tr>\n';
|
|
s += '<\/table><\/td><\/tr>\n';
|
|
s += '<\/table>\n';
|
|
|
|
var termOffset = 2 + this.conf.frameWidth;
|
|
|
|
if (this.globals.hasSubDivs) {
|
|
for (var r = 0; r < this.conf.rows; r++) {
|
|
s += '<div id="' + divPrefix + r + '" style="position:absolute; top:'
|
|
+ (termOffset + r * this.conf.rowHeight) + 'px; left: ' + termOffset + 'px;" class="'
|
|
+ this.conf.fontClass + '"><\/div>\n';
|
|
}
|
|
|
|
this.globals.termStringStart = '<table border="0" cellspacing="0" cellpadding="0"><tr><td nowrap height="'
|
|
+ this.conf.rowHeight + '" class="' + this.conf.fontClass + '">';
|
|
this.globals.termStringEnd = '<\/td><\/tr><\/table>';
|
|
}
|
|
|
|
this.globals.writeElement(this.termDiv, s);
|
|
}
|
|
|
|
if (!rebuild) {
|
|
this.globals.setElementXY(this.termDiv, this.conf.x, this.conf.y);
|
|
this.globals.setVisible(this.termDiv, 1);
|
|
}
|
|
},
|
|
rebuild: function (){
|
|
// check for bounds and array lengths
|
|
var c;
|
|
var r;
|
|
var rl = this.conf.rows;
|
|
var cl = this.conf.cols;
|
|
|
|
for (r = 0; r < rl; r++) {
|
|
var cbr = this.charBuf[r];
|
|
|
|
if (!cbr) {
|
|
this.charBuf[r] = this.getRowArray(cl, 0);
|
|
this.styleBuf[r] = this.getRowArray(cl, 0);
|
|
} else if (cbr.length < cl) {
|
|
for (c = cbr.length; c < cl; c++) {
|
|
this.charBuf[r][c] = 0;
|
|
this.styleBuf[r][c] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
var resetcrsr = false;
|
|
|
|
if (this.r >= rl) {
|
|
r = rl - 1;
|
|
resetcrsr = true;
|
|
}
|
|
|
|
if (this.c >= cl) {
|
|
c = cl - 1;
|
|
resetcrsr = true;
|
|
}
|
|
|
|
if (resetcrsr && this.cursoractive) this.cursorOn();
|
|
|
|
// and actually rebuild
|
|
this._makeTerm(true);
|
|
|
|
for (r = 0; r < rl; r++) {
|
|
this.redraw(r);
|
|
}
|
|
|
|
// clear backup buffer to prevent errors
|
|
this.backupBuffer = null;
|
|
},
|
|
moveTo: function (x, y){
|
|
this.globals.setElementXY(this.termDiv, x, y);
|
|
},
|
|
resizeTo: function (x, y){
|
|
if (this.termDivReady()) {
|
|
x = parseInt(x, 10);
|
|
y = parseInt(y, 10);
|
|
|
|
if (isNaN(x) || isNaN(y) || x < 4 || y < 2) return false;
|
|
|
|
this.maxCols = this.conf.cols = x;
|
|
this.maxLines = this.conf.rows = y;
|
|
|
|
this._makeTerm();
|
|
this.clear();
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
redraw: function (r){
|
|
var k;
|
|
var st;
|
|
var s = this.globals.termStringStart;
|
|
var curStyle = 0;
|
|
var tstls = this.globals.termStyles;
|
|
var tscls = this.globals.termStyleClose;
|
|
var tsopn = this.globals.termStyleOpen;
|
|
var tspcl = this.globals.termSpecials;
|
|
var tclrs = this.globals.colorCodes;
|
|
var tnclrs = this.globals.nsColorCodes;
|
|
var twclrs = this.globals.webColorCodes;
|
|
var t_cb = this.charBuf;
|
|
var t_sb = this.styleBuf;
|
|
var blur = this.textBlur;
|
|
var clr = '';
|
|
var textColor = this.textColor || '';
|
|
|
|
for (var i = 0; i < this.conf.cols; i++) {
|
|
var c = t_cb[r][i];
|
|
var cs = t_sb[r][i];
|
|
|
|
if (cs != curStyle || (i == 0 && textColor)) {
|
|
if (curStyle) {
|
|
if (curStyle & 0xffff00) s += '</span>';
|
|
|
|
for (k = tstls.length - 1; k >= 0; k--) {
|
|
st = tstls[k];
|
|
|
|
if (curStyle & st) s += tscls[st];
|
|
}
|
|
}
|
|
|
|
curStyle = cs;
|
|
|
|
for (k = 0; k < tstls.length; k++) {
|
|
st = tstls[k];
|
|
|
|
if (curStyle & st) s += tsopn[st];
|
|
}
|
|
|
|
clr = textColor;
|
|
|
|
if (curStyle & 0xff00) {
|
|
var cc = (curStyle & 0xff00) >>> 8;
|
|
|
|
clr = (cc < 16) ? tclrs[cc] : '#' + tnclrs[cc - 16];
|
|
} else if (curStyle & 0xff0000) {
|
|
clr = '#' + twclrs[(curStyle & 0xff0000) >>> 16];
|
|
}
|
|
|
|
if (clr) {
|
|
if (curStyle & 1) {
|
|
s += '<span style="background-color:' + clr + ' !important;">';
|
|
} else if (typeof blur === 'object') {
|
|
s += '<span style="color:' + clr + ' !important; text-shadow: 0 0 '
|
|
+ blur.join('px ' + clr + ', 0 0 ') + 'px ' + clr + ';">';
|
|
} else if (blur) {
|
|
s += '<span style="color:' + clr + ' !important; text-shadow: 0 0 ' + blur + 'px ' + clr + ';">';
|
|
} else {
|
|
s += '<span style="color:' + clr + ' !important;">';
|
|
}
|
|
}
|
|
}
|
|
|
|
s += (tspcl[c]) ? tspcl[c] : String.fromCharCode(c);
|
|
}
|
|
|
|
if (curStyle > 0) {
|
|
if (curStyle & 0xffff00) s += '</span>';
|
|
|
|
for (k = tstls.length - 1; k >= 0; k--) {
|
|
st = tstls[k];
|
|
|
|
if (curStyle & st) s += tscls[st];
|
|
}
|
|
}
|
|
|
|
s += this.globals.termStringEnd;
|
|
|
|
this.globals.writeElement(this.termDiv + '_r' + r, s);
|
|
},
|
|
guiReady: function (){
|
|
var ready = true;
|
|
|
|
if (this.globals.guiElementsReady(this.termDiv)) {
|
|
for (var r = 0; r < this.conf.rows; r++) {
|
|
if (this.globals.guiElementsReady(this.termDiv + '_r' + r) == false) {
|
|
ready = false;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
ready = false;
|
|
}
|
|
|
|
return ready;
|
|
},
|
|
termDivReady: function (){
|
|
if (document.getElementById) {
|
|
return (document.getElementById(this.termDiv)) ? true : false;
|
|
} else if (document.all) {
|
|
return (document.all[this.termDiv]) ? true : false;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
getDimensions: function (){
|
|
var object;
|
|
var w = 0;
|
|
var h = 0;
|
|
var d = this.termDiv;
|
|
|
|
if (document.getElementById) {
|
|
object = document.getElementById(d);
|
|
|
|
if (object && object.firstChild) {
|
|
w = parseInt(object.firstChild.offsetWidth, 10);
|
|
h = parseInt(object.firstChild.offsetHeight, 10);
|
|
} else if (object && object.children && object.children[0]) {
|
|
w = parseInt(object.children[0].offsetWidth, 10);
|
|
h = parseInt(object.children[0].offsetHeight, 10);
|
|
}
|
|
} else if (document.all) {
|
|
object = document.all[d];
|
|
|
|
if (object && object.children && object.children[0]) {
|
|
w = parseInt(object.children[0].offsetWidth, 10);
|
|
h = parseInt(object.children[0].offsetHeight, 10);
|
|
}
|
|
}
|
|
|
|
return { width: w, height: h };
|
|
},
|
|
// global store for static data and methods (former "TermGlobals")
|
|
globals: {
|
|
termToInitialze: null,
|
|
activeTerm: null,
|
|
kbdEnabled: false,
|
|
keylock: false,
|
|
keyRepeatDelay1: 450, // initial delay
|
|
keyRepeatDelay2: 100, // consecutive delays
|
|
keyRepeatTimer: null,
|
|
lcMorePrompt1: ' -- MORE -- ',
|
|
lcMorePromtp1Style: 1,
|
|
lcMorePrompt2: ' (Type: space to continue, \'q\' to quit)',
|
|
lcMorePrompt2Style: 0,
|
|
lcMoreKeyAbort: 113,
|
|
lcMoreKeyContinue: 32,
|
|
// initialize global data structs
|
|
_initGlobals: function (){
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
tg._extendMissingStringMethods();
|
|
tg._initWebColors();
|
|
tg._initDomKeyRef();
|
|
|
|
Terminal.prototype.termKey = tg.termKey;
|
|
},
|
|
// hex support (don't rely on generic support like Number.toString(16))
|
|
getHexChar: function (c){
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
if (tg.isHexChar(c)) return tg.hexToNum[c];
|
|
|
|
return -1;
|
|
},
|
|
isHexChar: function (c){
|
|
return !!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
|
|
},
|
|
isHexOnlyChar: function (c){
|
|
return !!((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
|
|
},
|
|
hexToNum: {
|
|
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7,
|
|
'8': 8, '9': 9, 'a': 10, 'b': 11, 'c': 12, 'd': 13, 'e': 14, 'f': 15,
|
|
'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15
|
|
},
|
|
// data for color support
|
|
webColors: [],
|
|
webColorCodes: [''],
|
|
colors: {
|
|
// ANSI bright (bold) color set
|
|
black: 1,
|
|
red: 2,
|
|
green: 3,
|
|
yellow: 4,
|
|
blue: 5,
|
|
magenta: 6,
|
|
cyan: 7,
|
|
white: 8,
|
|
// dark color set
|
|
grey: 9,
|
|
red2: 10,
|
|
green2: 11,
|
|
yellow2: 12,
|
|
blue2: 13,
|
|
magenta2: 14,
|
|
cyan2: 15,
|
|
// synonyms
|
|
red1: 2,
|
|
green1: 3,
|
|
yellow1: 4,
|
|
blue1: 5,
|
|
magenta1: 6,
|
|
cyan1: 7,
|
|
gray: 9,
|
|
darkred: 10,
|
|
darkgreen: 11,
|
|
darkyellow: 12,
|
|
darkblue: 13,
|
|
darkmagenta: 14,
|
|
darkcyan: 15,
|
|
// default color
|
|
'default': 0,
|
|
clear: 0
|
|
},
|
|
colorCodes: [
|
|
'', '#000000', '#ff0000', '#00ff00', '#ffff00', '#0066ff', '#ff00ff', '#00ffff', '#ffffff',
|
|
'#808080', '#990000', '#009900', '#999900', '#003399', '#990099', '#009999'
|
|
],
|
|
nsColors: {
|
|
'aliceblue': 1, 'antiquewhite': 2, 'aqua': 3, 'aquamarine': 4,
|
|
'azure': 5, 'beige': 6, 'black': 7, 'blue': 8,
|
|
'blueviolet': 9, 'brown': 10, 'burlywood': 11, 'cadetblue': 12,
|
|
'chartreuse': 13, 'chocolate': 14, 'coral': 15, 'cornflowerblue': 16,
|
|
'cornsilk': 17, 'crimson': 18, 'darkblue': 19, 'darkcyan': 20,
|
|
'darkgoldenrod': 21, 'darkgray': 22, 'darkgreen': 23, 'darkkhaki': 24,
|
|
'darkmagenta': 25, 'darkolivegreen': 26, 'darkorange': 27, 'darkorchid': 28,
|
|
'darkred': 29, 'darksalmon': 30, 'darkseagreen': 31, 'darkslateblue': 32,
|
|
'darkslategray': 33, 'darkturquoise': 34, 'darkviolet': 35, 'deeppink': 36,
|
|
'deepskyblue': 37, 'dimgray': 38, 'dodgerblue': 39, 'firebrick': 40,
|
|
'floralwhite': 41, 'forestgreen': 42, 'fuchsia': 43, 'gainsboro': 44,
|
|
'ghostwhite': 45, 'gold': 46, 'goldenrod': 47, 'gray': 48,
|
|
'green': 49, 'greenyellow': 50, 'honeydew': 51, 'hotpink': 52,
|
|
'indianred': 53, 'indigo': 54, 'ivory': 55, 'khaki': 56,
|
|
'lavender': 57, 'lavenderblush': 58, 'lawngreen': 59, 'lemonchiffon': 60,
|
|
'lightblue': 61, 'lightcoral': 62, 'lightcyan': 63, 'lightgoldenrodyellow': 64,
|
|
'lightgreen': 65, 'lightgrey': 66, 'lightpink': 67, 'lightsalmon': 68,
|
|
'lightseagreen': 69, 'lightskyblue': 70, 'lightslategray': 71, 'lightsteelblue': 72,
|
|
'lightyellow': 73, 'lime': 74, 'limegreen': 75, 'linen': 76,
|
|
'maroon': 77, 'mediumaquamarine': 78, 'mediumblue': 79, 'mediumorchid': 80,
|
|
'mediumpurple': 81, 'mediumseagreen': 82, 'mediumslateblue': 83, 'mediumspringgreen': 84,
|
|
'mediumturquoise': 85, 'mediumvioletred': 86, 'midnightblue': 87, 'mintcream': 88,
|
|
'mistyrose': 89, 'moccasin': 90, 'navajowhite': 91, 'navy': 92,
|
|
'oldlace': 93, 'olive': 94, 'olivedrab': 95, 'orange': 96,
|
|
'orangered': 97, 'orchid': 98, 'palegoldenrod': 99, 'palegreen': 100,
|
|
'paleturquoise': 101, 'palevioletred': 102, 'papayawhip': 103, 'peachpuff': 104,
|
|
'peru': 105, 'pink': 106, 'plum': 107, 'powderblue': 108,
|
|
'purple': 109, 'red': 110, 'rosybrown': 111, 'royalblue': 112,
|
|
'saddlebrown': 113, 'salmon': 114, 'sandybrown': 115, 'seagreen': 116,
|
|
'seashell': 117, 'sienna': 118, 'silver': 119, 'skyblue': 120,
|
|
'slateblue': 121, 'slategray': 122, 'snow': 123, 'springgreen': 124,
|
|
'steelblue': 125, 'tan': 126, 'teal': 127, 'thistle': 128,
|
|
'tomato': 129, 'turquoise': 130, 'violet': 131, 'wheat': 132,
|
|
'white': 133, 'whitesmoke': 134, 'yellow': 135, 'yellowgreen': 136
|
|
},
|
|
nsColorCodes: [
|
|
'',
|
|
'f0f8ff', 'faebd7', '00ffff', '7fffd4',
|
|
'f0ffff', 'f5f5dc', '000000', '0000ff',
|
|
'8a2be2', 'a52a2a', 'deb887', '5f9ea0',
|
|
'7fff00', 'd2691e', 'ff7f50', '6495ed',
|
|
'fff8dc', 'dc143c', '00008b', '008b8b',
|
|
'b8860b', 'a9a9a9', '006400', 'bdb76b',
|
|
'8b008b', '556b2f', 'ff8c00', '9932cc',
|
|
'8b0000', 'e9967a', '8fbc8f', '483d8b',
|
|
'2f4f4f', '00ced1', '9400d3', 'ff1493',
|
|
'00bfff', '696969', '1e90ff', 'b22222',
|
|
'fffaf0', '228b22', 'ff00ff', 'dcdcdc',
|
|
'f8f8ff', 'ffd700', 'daa520', '808080',
|
|
'008000', 'adff2f', 'f0fff0', 'ff69b4',
|
|
'cd5c5c', '4b0082', 'fffff0', 'f0e68c',
|
|
'e6e6fa', 'fff0f5', '7cfc00', 'fffacd',
|
|
'add8e6', 'f08080', 'e0ffff', 'fafad2',
|
|
'90ee90', 'd3d3d3', 'ffb6c1', 'ffa07a',
|
|
'20b2aa', '87cefa', '778899', 'b0c4de',
|
|
'ffffe0', '00ff00', '32cd32', 'faf0e6',
|
|
'800000', '66cdaa', '0000cd', 'ba55d3',
|
|
'9370db', '3cb371', '7b68ee', '00fa9a',
|
|
'48d1cc', 'c71585', '191970', 'f5fffa',
|
|
'ffe4e1', 'ffe4b5', 'ffdead', '000080',
|
|
'fdf5e6', '808000', '6b8e23', 'ffa500',
|
|
'ff4500', 'da70d6', 'eee8aa', '98fb98',
|
|
'afeeee', 'db7093', 'ffefd5', 'ffdab9',
|
|
'cd853f', 'ffc0cb', 'dda0dd', 'b0e0e6',
|
|
'800080', 'ff0000', 'bc8f8f', '4169e1',
|
|
'8b4513', 'fa8072', 'f4a460', '2e8b57',
|
|
'fff5ee', 'a0522d', 'c0c0c0', '87ceeb',
|
|
'6a5acd', '708090', 'fffafa', '00ff7f',
|
|
'4682b4', 'd2b48c', '008080', 'd8bfd8',
|
|
'ff6347', '40e0d0', 'ee82ee', 'f5deb3',
|
|
'ffffff', 'f5f5f5', 'ffff00', '9acd32'
|
|
],
|
|
_webSwatchChars: ['0', '3', '6', '9', 'c', 'f'],
|
|
_initWebColors: function (){
|
|
// generate long and short web color ref
|
|
var tg = Terminal.prototype.globals;
|
|
var wn = tg.webColors;
|
|
var cc = tg.webColorCodes;
|
|
var n = 1;
|
|
var a, b, c, al, bl, bs, cl;
|
|
|
|
for (var i = 0; i < 6; i++) {
|
|
a = tg._webSwatchChars[i];
|
|
al = a + a;
|
|
|
|
for (var j = 0; j < 6; j++) {
|
|
b = tg._webSwatchChars[j];
|
|
bl = al + b + b;
|
|
bs = a + b;
|
|
|
|
for (var k = 0; k < 6; k++) {
|
|
c = tg._webSwatchChars[k];
|
|
cl = bl + c + c;
|
|
wn[bs + c] = wn[cl] = n;
|
|
cc[n] = cl;
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
webifyColor: function (s){
|
|
var i;
|
|
var a;
|
|
var c = '';
|
|
// return nearest web color in 3 digit format
|
|
// (do without RegExp for compatibility)
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
if (s.length == 6) {
|
|
for (i = 0; i < 6; i += 2) {
|
|
a = s.charAt(i);
|
|
|
|
var b = s.charAt(i + 1);
|
|
|
|
if (tg.isHexChar(a) && tg.isHexChar(b)) {
|
|
c += tg._webSwatchChars[Math.round(parseInt(a + b, 16) / 255 * 5)];
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
return c;
|
|
} else if (s.length == 3) {
|
|
for (i = 0; i < 3; i++) {
|
|
a = s.charAt(i);
|
|
|
|
if (tg.isHexChar(a)) {
|
|
c += tg._webSwatchChars[Math.round(parseInt(a, 16) / 15 * 5)];
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
return c;
|
|
} else {
|
|
return '';
|
|
}
|
|
},
|
|
// public methods for color support
|
|
setColor: function (label, value){
|
|
var n;
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
if (typeof label == 'number' && label >= 1 && label <= 15) {
|
|
tg.colorCodes[label] = value;
|
|
} else if (typeof label == 'string') {
|
|
label = label.toLowerCase();
|
|
|
|
if (label.length == 1 && tg.isHexChar(label)) {
|
|
n = tg.hexToNum[label];
|
|
|
|
if (n) tg.colorCodes[n] = value;
|
|
}
|
|
else if (typeof tg.colors[label] != 'undefined') {
|
|
n = tg.colors[label];
|
|
|
|
if (n) tg.colorCodes[n] = value;
|
|
}
|
|
}
|
|
},
|
|
getColorString: function (label){
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
if (typeof label == 'number' && label >= 0 && label <= 15) {
|
|
return tg.colorCodes[label];
|
|
} else if (typeof label == 'string') {
|
|
label = label.toLowerCase();
|
|
|
|
if (label.length == 1 && tg.isHexChar(label)) {
|
|
return tg.colorCodes[tg.hexToNum[label]];
|
|
} else if (typeof tg.colors[label] != 'undefined') {
|
|
return tg.colorCodes[tg.colors[label]];
|
|
}
|
|
}
|
|
|
|
return '';
|
|
},
|
|
getColorCode: function (label){
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
if (typeof label == 'number' && label >= 0 && label <= 15) {
|
|
return label;
|
|
} else if (typeof label == 'string') {
|
|
label = label.toLowerCase();
|
|
|
|
if (label.length == 1 && tg.isHexChar(label)) {
|
|
return parseInt(label, 16);
|
|
} else if (typeof tg.colors[label] != 'undefined') {
|
|
return tg.colors[label];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
},
|
|
// import/paste methods (methods return success)
|
|
insertText: function (text){
|
|
// auto-types a given string to the active terminal
|
|
// returns success (false indicates a lock or no active terminal)
|
|
var tg = Terminal.prototype.globals;
|
|
var termRef = tg.activeTerm;
|
|
|
|
if (!termRef || termRef.closed || tg.keylock
|
|
|| termRef.lock || termRef.charMode || termRef.fieldMode) return false;
|
|
|
|
// terminal open and unlocked, so type the text
|
|
for (var i = 0; i < text.length; i++) {
|
|
tg.keyHandler({ which: text.charCodeAt(i), _remapped: true });
|
|
}
|
|
|
|
return true;
|
|
},
|
|
importEachLine: function (text){
|
|
// import multiple lines of text per line each and execs
|
|
// returns success (false indicates a lock or no active terminal)
|
|
var tg = Terminal.prototype.globals;
|
|
var termRef = tg.activeTerm;
|
|
|
|
if (!termRef || termRef.closed || tg.keylock
|
|
|| termRef.lock || termRef.charMode || termRef.fieldMode) return false;
|
|
|
|
// clear the current command line
|
|
termRef.cursorOff();
|
|
termRef._clearLine();
|
|
|
|
// normalize line breaks
|
|
text = text.replace(/\r\n?/g, '\n');
|
|
// split lines and auto-type the text
|
|
var t = text.split('\n');
|
|
|
|
for (var i = 0; i < t.length; i++) {
|
|
for (var k = 0; k < t[i].length; k++) {
|
|
tg.keyHandler({ which: t[i].charCodeAt(k), _remapped: true });
|
|
}
|
|
|
|
tg.keyHandler({ which: term.termKey.CR, _remapped: true });
|
|
}
|
|
|
|
return true;
|
|
},
|
|
importMultiLine: function (text){
|
|
// importing multi-line text as single input with "\n" in lineBuffer
|
|
var tg = Terminal.prototype.globals;
|
|
var termRef = tg.activeTerm;
|
|
|
|
if (!termRef || termRef.closed || tg.keylock
|
|
|| termRef.lock || termRef.charMode || termRef.fieldMode) return false;
|
|
|
|
// lock and clear the line
|
|
termRef.lock = true;
|
|
|
|
termRef.cursorOff();
|
|
termRef._clearLine();
|
|
// normalize linebreaks and echo the text linewise
|
|
text = text.replace(/\r\n?/g, '\n');
|
|
|
|
var lines = text.split('\n');
|
|
|
|
for (var i = 0; i < lines.length; i++) {
|
|
termRef.type(lines[i]);
|
|
|
|
if (i < lines.length - 1) termRef.newLine();
|
|
}
|
|
|
|
// fake <ENTER>;
|
|
// (no history entry for this)
|
|
termRef.lineBuffer = text;
|
|
termRef.lastLine = '';
|
|
termRef.inputChar = 0;
|
|
termRef.handler();
|
|
|
|
return true;
|
|
},
|
|
// text related service functions
|
|
normalize: function (n, m){
|
|
var s = '' + n;
|
|
|
|
while (s.length < m) s = '0' + s;
|
|
|
|
return s;
|
|
},
|
|
fillLeft: function (t, n){
|
|
if (typeof t != 'string') t = '' + t;
|
|
|
|
while (t.length < n) t = ' ' + t;
|
|
|
|
return t;
|
|
},
|
|
center: function (t, l){
|
|
var s = '';
|
|
|
|
for (var i = t.length; i < l; i += 2) s += ' ';
|
|
|
|
return s + t;
|
|
},
|
|
// simple substitute for String.replace()
|
|
stringReplace: function (s1, s2, t){
|
|
var l1 = s1.length;
|
|
var l2 = s2.length;
|
|
var ofs = t.indexOf(s1);
|
|
|
|
while (ofs >= 0) {
|
|
t = t.substring(0, ofs) + s2 + t.substring(ofs + l1);
|
|
ofs = t.indexOf(s1, ofs + l2);
|
|
}
|
|
|
|
return t;
|
|
},
|
|
// config data for text wrap
|
|
wrapChars: {
|
|
// keys: charCode
|
|
// values: 1 = white space, 2 = wrap after, 3 = wrap before, 4 = conditional word break
|
|
9: 1, // tab
|
|
10: 1, // new line - don't change this (used internally)!!!
|
|
12: 4, // form feed (use this for conditional word breaks)
|
|
13: 1, // cr
|
|
32: 1, // blank
|
|
40: 3, // (
|
|
45: 2, // dash/hyphen
|
|
61: 2, // =
|
|
91: 3, // [
|
|
94: 3, // caret (non-printing chars)
|
|
123: 3 // {
|
|
},
|
|
// keyboard methods & controls
|
|
setFocus: function (termref){
|
|
Terminal.prototype.globals.activeTerm = termref;
|
|
Terminal.prototype.globals.clearRepeatTimer();
|
|
},
|
|
termKey: {
|
|
// codes of special keys
|
|
'NUL': 0x00,
|
|
'SOH': 0x01,
|
|
'STX': 0x02,
|
|
'ETX': 0x03,
|
|
'EOT': 0x04,
|
|
'ENQ': 0x05,
|
|
'ACK': 0x06,
|
|
'BEL': 0x07,
|
|
'BS': 0x08,
|
|
'BACKSPACE': 0x08,
|
|
'HT': 0x09,
|
|
'TAB': 0x09,
|
|
'LF': 0x0A,
|
|
'VT': 0x0B,
|
|
'FF': 0x0C,
|
|
'CR': 0x0D,
|
|
'SO': 0x0E,
|
|
'SI': 0x0F,
|
|
'DLE': 0x10,
|
|
'DC1': 0x11,
|
|
'DC2': 0x12,
|
|
'DC3': 0x13,
|
|
'DC4': 0x14,
|
|
'NAK': 0x15,
|
|
'SYN': 0x16,
|
|
'ETB': 0x17,
|
|
'CAN': 0x18,
|
|
'EM': 0x19,
|
|
'SUB': 0x1A,
|
|
'ESC': 0x1B,
|
|
'IS4': 0x1C,
|
|
'IS3': 0x1D,
|
|
'IS2': 0x1E,
|
|
'IS1': 0x1F,
|
|
'DEL': 0x7F,
|
|
// other specials
|
|
'EURO': 0x20AC,
|
|
// cursor mapping
|
|
'LEFT': 0x1C,
|
|
'RIGHT': 0x1D,
|
|
'UP': 0x1E,
|
|
'DOWN': 0x1F
|
|
},
|
|
// map some DOM_VK_* properties to values defined in termKey
|
|
termDomKeyRef: {},
|
|
_domKeyMappingData: {
|
|
'LEFT': 'LEFT',
|
|
'RIGHT': 'RIGHT',
|
|
'UP': 'UP',
|
|
'DOWN': 'DOWN',
|
|
'BACK_SPACE': 'BS',
|
|
'RETURN': 'CR',
|
|
'ENTER': 'CR',
|
|
'ESCAPE': 'ESC',
|
|
'DELETE': 'DEL',
|
|
'TAB': 'TAB'
|
|
},
|
|
_initDomKeyRef: function (){
|
|
var tg = Terminal.prototype.globals;
|
|
var m = tg._domKeyMappingData;
|
|
var r = tg.termDomKeyRef;
|
|
var k = tg.termKey;
|
|
|
|
for (var i in m) {
|
|
if (m.hasOwnProperty(i)) {
|
|
r['DOM_VK_' + i] = k[m[i]];
|
|
}
|
|
}
|
|
},
|
|
registerEvent: function (element, eventType, handler, capture){
|
|
if (element.addEventListener) {
|
|
element.addEventListener(eventType.toLowerCase(), handler, capture);
|
|
} else {
|
|
var et = eventType.toUpperCase();
|
|
|
|
if (window.Event && window.Event[et] && element.captureEvents) element.captureEvents(Event[et]);
|
|
|
|
element['on' + eventType.toLowerCase()] = handler;
|
|
}
|
|
},
|
|
releaseEvent: function (element, eventType, handler, capture){
|
|
if (element.removeEventListener) {
|
|
element.removeEventListener(eventType.toLowerCase(), handler, capture);
|
|
} else {
|
|
var et = eventType.toUpperCase();
|
|
|
|
if (window.Event && window.Event[et] && element.releaseEvents) element.releaseEvents(Event[et]);
|
|
|
|
et = 'on' + eventType.toLowerCase();
|
|
|
|
if (element[et] && element[et] == handler) element.et = null;
|
|
}
|
|
},
|
|
|
|
enableKeyboard: function (term){
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
if (!tg.kbdEnabled) {
|
|
tg.registerEvent(document, 'keypress', tg.keyHandler, true);
|
|
tg.registerEvent(document, 'keydown', tg.keyFix, true);
|
|
tg.registerEvent(document, 'keyup', tg.clearRepeatTimer, true);
|
|
|
|
tg.kbdEnabled = true;
|
|
}
|
|
|
|
tg.activeTerm = term;
|
|
},
|
|
disableKeyboard: function (term){
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
if (tg.kbdEnabled) {
|
|
tg.releaseEvent(document, 'keypress', tg.keyHandler, true);
|
|
tg.releaseEvent(document, 'keydown', tg.keyFix, true);
|
|
tg.releaseEvent(document, 'keyup', tg.clearRepeatTimer, true);
|
|
|
|
tg.kbdEnabled = false;
|
|
}
|
|
|
|
tg.activeTerm = null;
|
|
},
|
|
// remap some special key mappings on keydown
|
|
keyFix: function (e){
|
|
var tg = Terminal.prototype.globals;
|
|
var term = tg.activeTerm;
|
|
var ch;
|
|
|
|
if (tg.keylock || term.lock) return true;
|
|
|
|
if (window.event) {
|
|
if (!e) e = window.event;
|
|
|
|
ch = e.keyCode;
|
|
|
|
if (e.DOM_VK_UP) {
|
|
for (var i in tg.termDomKeyRef) {
|
|
if (tg.termDomKeyRef.hasOwnProperty(i)) {
|
|
if (e[i] && ch == e[i]) {
|
|
tg.keyHandler({ which: tg.termDomKeyRef[i], _remapped: true, _repeat: ch == 0x1B });
|
|
|
|
if (e.preventDefault) e.preventDefault();
|
|
|
|
if (e.stopPropagation) e.stopPropagation();
|
|
|
|
e.cancelBubble = true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
e.cancelBubble = false;
|
|
|
|
return true;
|
|
} else {
|
|
// no DOM support
|
|
var termKey = term.termKey;
|
|
var keyHandler = tg.keyHandler;
|
|
|
|
if (ch == 8 && !term.isOpera) {
|
|
keyHandler({ which: termKey.BS, _remapped: true, _repeat: true });
|
|
} else if (ch == 9) {
|
|
keyHandler({
|
|
which: termKey.TAB,
|
|
_remapped: true,
|
|
_repeat: (term.printTab) ? false : true
|
|
});
|
|
} else if (ch == 27) {
|
|
keyHandler({
|
|
which: termKey.ESC,
|
|
_remapped: true,
|
|
_repeat: (term.printTab) ? false : true
|
|
});
|
|
} else if (ch == 37) {
|
|
keyHandler({ which: termKey.LEFT, _remapped: true, _repeat: true });
|
|
} else if (ch == 39) {
|
|
keyHandler({ which: termKey.RIGHT, _remapped: true, _repeat: true });
|
|
} else if (ch == 38) {
|
|
keyHandler({ which: termKey.UP, _remapped: true, _repeat: true });
|
|
} else if (ch == 40) {
|
|
keyHandler({ which: termKey.DOWN, _remapped: true, _repeat: true });
|
|
} else if (ch == 127 || ch == 46) {
|
|
keyHandler({ which: termKey.DEL, _remapped: true, _repeat: true });
|
|
} else if (ch >= 57373 && ch <= 57376) {
|
|
if (ch == 57373) {
|
|
keyHandler({ which: termKey.UP, _remapped: true, _repeat: true });
|
|
} else if (ch == 57374) {
|
|
keyHandler({ which: termKey.DOWN, _remapped: true, _repeat: true });
|
|
} else if (ch == 57375) {
|
|
keyHandler({ which: termKey.LEFT, _remapped: true, _repeat: true });
|
|
} else if (ch == 57376) {
|
|
keyHandler({ which: termKey.RIGHT, _remapped: true, _repeat: true });
|
|
}
|
|
} else {
|
|
e.cancelBubble = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (e.preventDefault) e.preventDefault();
|
|
|
|
if (e.stopPropagation) e.stopPropagation();
|
|
|
|
e.cancelBubble = true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
},
|
|
clearRepeatTimer: function (e){
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
if (tg.keyRepeatTimer) {
|
|
clearTimeout(tg.keyRepeatTimer);
|
|
|
|
tg.keyRepeatTimer = null;
|
|
}
|
|
},
|
|
doKeyRepeat: function (ch){
|
|
Terminal.prototype.globals.keyHandler({ which: ch, _remapped: true, _repeated: true })
|
|
},
|
|
keyHandler: function (e){
|
|
var tg = Terminal.prototype.globals;
|
|
var term = tg.activeTerm;
|
|
|
|
if (tg.keylock || term.lock || term.isMac && e && e.metaKey) return true;
|
|
|
|
if (window.event) {
|
|
if (window.event.preventDefault) window.event.preventDefault();
|
|
|
|
if (window.event.stopPropagation) window.event.stopPropagation();
|
|
} else if (e) {
|
|
if (e.preventDefault) e.preventDefault();
|
|
|
|
if (e.stopPropagation) e.stopPropagation();
|
|
}
|
|
|
|
var ch;
|
|
var ctrl = false;
|
|
var shft = false;
|
|
var remapped = false;
|
|
var termKey = term.termKey;
|
|
var keyRepeat = 0;
|
|
|
|
if (e) {
|
|
ch = e.which;
|
|
ctrl = ((e.ctrlKey && !e.altKey) || e.modifiers == 2);
|
|
shft = (e.shiftKey || e.modifiers == 4);
|
|
|
|
if (e._remapped) {
|
|
remapped = true;
|
|
|
|
if (window.event) {
|
|
//ctrl=(ctrl || window.event.ctrlKey);
|
|
ctrl = (ctrl || (window.event.ctrlKey && !window.event.altKey));
|
|
shft = (shft || window.event.shiftKey);
|
|
}
|
|
}
|
|
|
|
if (e._repeated) {
|
|
keyRepeat = 2;
|
|
} else if (e._repeat) {
|
|
keyRepeat = 1;
|
|
}
|
|
} else if (window.event) {
|
|
ch = window.event.keyCode;
|
|
//ctrl=(window.event.ctrlKey);
|
|
ctrl = (window.event.ctrlKey && !window.event.altKey); // allow alt gr == ctrl alt
|
|
shft = (window.event.shiftKey);
|
|
|
|
if (window.event._repeated) {
|
|
keyRepeat = 2;
|
|
} else if (window.event._repeat) {
|
|
keyRepeat = 1;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
|
|
if (ch == '' && remapped == false) {
|
|
// map specials
|
|
if (e == null) e = window.event;
|
|
|
|
if (e.charCode == 0 && e.keyCode) {
|
|
if (e.DOM_VK_UP) {
|
|
var dkr = tg.termDomKeyRef;
|
|
|
|
for (var i in dkr) {
|
|
if (dkr.hasOwnProperty(i)) {
|
|
if (e[i] && e.keyCode == e[i]) {
|
|
ch = dkr[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// NS4
|
|
if (e.keyCode == 28) { ch = termKey.LEFT; }
|
|
else if (e.keyCode == 29) { ch = termKey.RIGHT; }
|
|
else if (e.keyCode == 30) { ch = termKey.UP; }
|
|
else if (e.keyCode == 31) { ch = termKey.DOWN; }
|
|
// Mozilla alike but no DOM support
|
|
else if (e.keyCode == 37) { ch = termKey.LEFT; }
|
|
else if (e.keyCode == 39) { ch = termKey.RIGHT; }
|
|
else if (e.keyCode == 38) { ch = termKey.UP; }
|
|
else if (e.keyCode == 40) { ch = termKey.DOWN; }
|
|
// just to have the TAB mapping here too
|
|
else if (e.keyCode == 9) { ch = termKey.TAB; }
|
|
}
|
|
}
|
|
}
|
|
|
|
// leave on unicode private use area (might be function key etc)
|
|
if ((ch >= 0xE000) && (ch <= 0xF8FF)) return;
|
|
|
|
if (keyRepeat) {
|
|
tg.clearRepeatTimer();
|
|
|
|
tg.keyRepeatTimer = window.setTimeout(
|
|
'Terminal.prototype.globals.doKeyRepeat(' + ch + ')',
|
|
(keyRepeat == 1) ? tg.keyRepeatDelay1 : tg.keyRepeatDelay2
|
|
);
|
|
}
|
|
|
|
// key actions
|
|
if (term.charMode) {
|
|
term.insert = false;
|
|
term.inputChar = ch;
|
|
term.lineBuffer = '';
|
|
term.handler();
|
|
|
|
if (ch <= 32 && window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!ctrl) {
|
|
// special keys
|
|
if (ch == termKey.CR) {
|
|
term.lock = true;
|
|
|
|
term.cursorOff();
|
|
|
|
term.insert = false;
|
|
|
|
if (term.rawMode) {
|
|
term.lineBuffer = term.lastLine;
|
|
} else if (term.fieldMode) {
|
|
term.lineBuffer = term.lastLine;
|
|
term.exitFieldMode();
|
|
} else {
|
|
term.lineBuffer = term._getLine(true);
|
|
|
|
if (
|
|
term.lineBuffer != '' &&
|
|
(!term.historyUnique || term.history.length == 0 ||
|
|
term.lineBuffer != term.history[term.history.length - 1])
|
|
) {
|
|
term.history[term.history.length] = term.lineBuffer;
|
|
}
|
|
|
|
term.histPtr = term.history.length;
|
|
}
|
|
|
|
term.lastLine = '';
|
|
term.inputChar = 0;
|
|
term.handler();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (term.fieldMode) {
|
|
if (ch == termKey.ESC) {
|
|
term.lineBuffer = term.lastLine = '';
|
|
term.exitFieldMode();
|
|
term.lastLine = '';
|
|
term.inputChar = 0;
|
|
term.handler();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (ch == termKey.LEFT) {
|
|
if (term.fieldC > 0) term.fieldC--;
|
|
} else if (ch == termKey.RIGHT) {
|
|
if (term.fieldC < term.lastLine.length) term.fieldC++;
|
|
} else if (ch == termKey.BS) {
|
|
if (term.fieldC > 0) {
|
|
term.lastLine = term.lastLine.substring(0, term.fieldC - 1) + term.lastLine.substring(term.fieldC);
|
|
term.fieldC--;
|
|
}
|
|
} else if (ch == termKey.DEL) {
|
|
if (term.fieldC < term.lastLine.length) {
|
|
term.lastLine = term.lastLine.substring(0, term.fieldC) + term.lastLine.substring(term.fieldC + 1);
|
|
}
|
|
} else if (ch >= 32) {
|
|
term.lastLine = term.lastLine.substring(0, term.fieldC) + String.fromCharCode(ch)
|
|
+ term.lastLine.substring(term.fieldC);
|
|
term.fieldC++;
|
|
}
|
|
|
|
term.drawField();
|
|
|
|
return false;
|
|
} else if (ch == termKey.ESC && term.conf.closeOnESC) {
|
|
term.close();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
}
|
|
|
|
if (ch < 32 && term.rawMode) {
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else {
|
|
if (ch == termKey.LEFT) {
|
|
term.cursorLeft();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (ch == termKey.RIGHT) {
|
|
term.cursorRight();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (ch == termKey.UP) {
|
|
term.cursorOff();
|
|
|
|
if (term.histPtr == term.history.length) term.lastLine = term._getLine();
|
|
|
|
term._clearLine();
|
|
|
|
if (term.history.length && term.histPtr >= 0) {
|
|
if (term.histPtr > 0) term.histPtr--;
|
|
|
|
term.type(term.history[term.histPtr]);
|
|
} else if (term.lastLine) {
|
|
term.type(term.lastLine);
|
|
}
|
|
|
|
term.cursorOn();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (ch == termKey.DOWN) {
|
|
term.cursorOff();
|
|
|
|
if (term.histPtr == term.history.length) term.lastLine = term._getLine();
|
|
term._clearLine();
|
|
|
|
if (term.history.length && term.histPtr <= term.history.length) {
|
|
if (term.histPtr < term.history.length) term.histPtr++;
|
|
|
|
if (term.histPtr < term.history.length) {
|
|
term.type(term.history[term.histPtr]);
|
|
} else if (term.lastLine) {
|
|
term.type(term.lastLine);
|
|
}
|
|
} else if (term.lastLine) {
|
|
term.type(term.lastLine);
|
|
}
|
|
|
|
term.cursorOn();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (ch == termKey.BS) {
|
|
term.backspace();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (ch == termKey.DEL) {
|
|
if (term.DELisBS) {
|
|
term.backspace();
|
|
} else {
|
|
term.fwdDelete();
|
|
}
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (term.rawMode) {
|
|
if (term.isPrintable(ch)) {
|
|
term.lastLine += String.fromCharCode(ch);
|
|
}
|
|
|
|
if (ch == 32 && window.event) {
|
|
window.event.cancelBubble = true;
|
|
} else if (window.opera && window.event) {
|
|
window.event.cancelBubble = true;
|
|
}
|
|
|
|
return false;
|
|
} else {
|
|
if (term.conf.catchCtrlH && (ch == termKey.BS || (ctrl && ch == 72))) {
|
|
// catch ^H
|
|
term.backspace();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (term.ctrlHandler && (ch < 32 || (ctrl && term.isPrintable(ch, true)))) {
|
|
if ((ch >= 65 && ch <= 96) || ch == 63) {
|
|
// remap canonical
|
|
if (ch == 63) {
|
|
ch = 31;
|
|
} else {
|
|
ch -= 64;
|
|
}
|
|
}
|
|
|
|
term.inputChar = ch;
|
|
term.ctrlHandler();
|
|
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (ctrl || !term.isPrintable(ch, true)) {
|
|
if (window.event) window.event.cancelBubble = true;
|
|
|
|
return false;
|
|
} else if (term.isPrintable(ch, true)) {
|
|
if (term.blinkTimer) clearTimeout(term.blinkTimer);
|
|
|
|
if (term.insert) {
|
|
term.cursorOff();
|
|
term._scrollRight(term.r, term.c);
|
|
}
|
|
|
|
term._charOut(ch);
|
|
term.cursorOn();
|
|
|
|
if (ch == 32 && window.event) {
|
|
window.event.cancelBubble = true;
|
|
} else if (window.opera && window.event) {
|
|
window.event.cancelBubble = true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
},
|
|
// gui mappings
|
|
hasSubDivs: false,
|
|
termStringStart: '',
|
|
termStringEnd: '',
|
|
termSpecials: {
|
|
// special HTML escapes
|
|
0: ' ',
|
|
1: ' ',
|
|
9: ' ',
|
|
32: ' ',
|
|
34: '"',
|
|
38: '&',
|
|
60: '<',
|
|
62: '>',
|
|
127: '◊',
|
|
0x20AC: '€'
|
|
},
|
|
// extensive list of max 8 styles (2^n, n<16)
|
|
termStyles: [1, 2, 4, 8, 16],
|
|
// style markup: one letter keys, reserved keys: "p" (plain), "c" (color)
|
|
termStyleMarkup: {
|
|
'r': 1,
|
|
'u': 2,
|
|
'i': 4,
|
|
's': 8,
|
|
'b': 16 // map "b" to 16 (italics) for ANSI mapping
|
|
},
|
|
// mappings for styles (heading HTML)
|
|
termStyleOpen: {
|
|
1: '<span class="termReverse">',
|
|
2: '<u>',
|
|
4: '<i>',
|
|
8: '<strike>',
|
|
16: '<i>'
|
|
},
|
|
// mapping for styles (trailing HTML)
|
|
termStyleClose: {
|
|
1: '<\/span>',
|
|
2: '<\/u>',
|
|
4: '<\/i>',
|
|
8: '<\/strike>',
|
|
16: '</i>'
|
|
},
|
|
// method to install custom styles
|
|
assignStyle: function (styleCode, markup, htmlOpen, htmlClose){
|
|
var i;
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
// check params
|
|
if (!styleCode || isNaN(styleCode)) {
|
|
if (styleCode >= 256) {
|
|
alert('termlib.js:\nCould not assign style.\n' + s + ' is not a valid power of 2 between 0 and 256.');
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
var s = styleCode & 0xff;
|
|
var matched = false;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
if ((s >>> i) & 1) {
|
|
if (matched) {
|
|
alert('termlib.js:\nCould not assign style code.\n' + s + ' is not a power of 2!');
|
|
return;
|
|
}
|
|
matched = true;
|
|
}
|
|
}
|
|
|
|
if (!matched) {
|
|
alert('termlib.js:\nCould not assign style code.\n' + s + ' is not a valid power of 2 between 0 and 256.');
|
|
|
|
return;
|
|
}
|
|
|
|
markup = String(markup).toLowerCase();
|
|
|
|
if (markup == 'c' || markup == 'p') {
|
|
alert('termlib.js:\nCould not assign mark up.\n"' + markup + '" is a reserved code.');
|
|
|
|
return;
|
|
}
|
|
|
|
if (markup.length > 1) {
|
|
alert('termlib.js:\nCould not assign mark up.\n"' + markup + '" is not a single letter code.');
|
|
|
|
return;
|
|
}
|
|
|
|
var exists = false;
|
|
|
|
for (i = 0; i < tg.termStyles.length; i++) {
|
|
if (tg.termStyles[i] == s) {
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (exists) {
|
|
var m = tg.termStyleMarkup[markup];
|
|
|
|
if (m && m != s) {
|
|
alert('termlib.js:\nCould not assign mark up.\n"' + markup + '" is already in use.');
|
|
|
|
return;
|
|
}
|
|
} else {
|
|
if (tg.termStyleMarkup[markup]) {
|
|
alert('termlib.js:\nCould not assign mark up.\n"' + markup + '" is already in use.');
|
|
|
|
return;
|
|
}
|
|
|
|
tg.termStyles[tg.termStyles.length] = s;
|
|
}
|
|
|
|
// install properties
|
|
tg.termStyleMarkup[markup] = s;
|
|
tg.termStyleOpen[s] = htmlOpen;
|
|
tg.termStyleClose[s] = htmlClose;
|
|
},
|
|
// ANSI output mapping (styles & fg colors only)
|
|
ANSI_regexp: /(\x1b\[|x9b)([0-9;]+?)([a-zA-Z])/g, // CSI ( = 0x1b+"[" or 0x9b ) + params + letter
|
|
ANIS_SGR_codes: {
|
|
'0': '%+p',
|
|
'1': '%+b',
|
|
'3': '%+i',
|
|
'4': '%+u',
|
|
'7': '%+r',
|
|
'9': '%+s',
|
|
'21': '%+u',
|
|
'22': '%-b',
|
|
'23': '%-i',
|
|
'24': '%-u',
|
|
'27': '%-r',
|
|
'29': '%-s',
|
|
'30': '%c(0)', // using default fg color for black (black: "%c(1)")
|
|
'31': '%c(a)',
|
|
'32': '%c(b)',
|
|
'33': '%c(c)',
|
|
'34': '%c(d)',
|
|
'35': '%c(e)',
|
|
'36': '%c(f)',
|
|
'37': '%c(#999)',
|
|
'39': '%c(0)',
|
|
'90': '%c(9)',
|
|
'91': '%c(2)',
|
|
'92': '%c(3)',
|
|
'93': '%c(4)',
|
|
'94': '%c(5)',
|
|
'95': '%c(6)',
|
|
'96': '%c(7)',
|
|
'97': '%c(8)',
|
|
'99': '%c(0)',
|
|
'trueBlack': '%c(1)'
|
|
},
|
|
ANSI_map: function (t, trueBlack){
|
|
// transform simple ANSI SGR codes to internal markup
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
tg.ANSI_regexp.lastIndex = 0;
|
|
|
|
return t.replace(
|
|
tg.ANSI_regexp,
|
|
function (str, p1, p2, p3, offset, s){
|
|
return tg.ANSI_replace(p2, p3, trueBlack);
|
|
}
|
|
);
|
|
},
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
ANSI_replace: function (p, cmd, trueBlack){
|
|
var tg = Terminal.prototype.globals;
|
|
|
|
if (cmd == 'm') {
|
|
if (p == '') {
|
|
return tg.ANIS_SGR_codes[0];
|
|
} else if (trueBlack && p == '30') {
|
|
return tg.ANIS_SGR_codes.trueBlack;
|
|
} else if (tg.ANIS_SGR_codes[p]) {
|
|
return tg.ANIS_SGR_codes[p];
|
|
}
|
|
}
|
|
|
|
return '';
|
|
},
|
|
// basic DHTML dynamics and browser abstraction
|
|
writeElement: function (e, t){
|
|
if (document.getElementById) {
|
|
var element = document.getElementById(e);
|
|
|
|
element.innerHTML = t;
|
|
} else if (document.all) {
|
|
document.all[e].innerHTML = t;
|
|
}
|
|
},
|
|
setElementXY: function (d, x, y){
|
|
if (document.getElementById) {
|
|
var element = document.getElementById(d);
|
|
|
|
element.style.left = x + 'px';
|
|
element.style.top = y + 'px';
|
|
} else if (document.all) {
|
|
document.all[d].style.left = x + 'px';
|
|
document.all[d].style.top = y + 'px';
|
|
}
|
|
},
|
|
setVisible: function (d, v){
|
|
if (document.getElementById) {
|
|
var element = document.getElementById(d);
|
|
|
|
element.style.visibility = (v) ? 'visible' : 'hidden';
|
|
} else if (document.all) {
|
|
document.all[d].style.visibility = (v) ? 'visible' : 'hidden';
|
|
}
|
|
},
|
|
setDisplay: function (d, v){
|
|
if (document.getElementById) {
|
|
var element = document.getElementById(d);
|
|
|
|
element.style.display = v;
|
|
}
|
|
else if (document.all) {
|
|
document.all[d].style.display = v;
|
|
}
|
|
},
|
|
guiElementsReady: function (e){
|
|
if (document.getElementById) {
|
|
return (document.getElementById(e)) ? true : false;
|
|
} else if (document.all) {
|
|
return (document.all[e]) ? true : false;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
// constructor mods (MSIE fixes)
|
|
_termString_makeKeyref: function (){
|
|
var tg = Terminal.prototype.globals;
|
|
var termString_keyref = tg.termString_keyref = [];
|
|
var termString_keycoderef = tg.termString_keycoderef = [];
|
|
var hex = ['A', 'B', 'C', 'D', 'E', 'F'];
|
|
|
|
for (var i = 0; i <= 15; i++) {
|
|
var high = (i < 10) ? i : hex[i - 10];
|
|
|
|
for (var k = 0; k <= 15; k++) {
|
|
var low = (k < 10) ? k : hex[k - 10];
|
|
var cc = i * 16 + k;
|
|
|
|
if (cc >= 32) {
|
|
var cs = unescape("%" + high + low);
|
|
|
|
termString_keyref[cc] = cs;
|
|
termString_keycoderef[cs] = cc;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_extendMissingStringMethods: function (){
|
|
if (!String.fromCharCode || !String.prototype.charCodeAt) {
|
|
Terminal.prototype.globals._termString_makeKeyref();
|
|
}
|
|
|
|
if (!String.fromCharCode) {
|
|
String.fromCharCode = function (cc){
|
|
return (cc != null) ? Terminal.prototype.globals.termString_keyref[cc] : '';
|
|
};
|
|
}
|
|
|
|
if (!String.prototype.charCodeAt) {
|
|
String.prototype.charCodeAt = function (n){
|
|
var cs = this.charAt(n);
|
|
|
|
return (Terminal.prototype.globals.termString_keycoderef[cs]) ?
|
|
Terminal.prototype.globals.termString_keycoderef[cs] : 0;
|
|
};
|
|
}
|
|
}
|
|
// end of Terminal.prototype.globals
|
|
}
|
|
// end of Terminal.prototype
|
|
};
|
|
|
|
// initialize global data
|
|
Terminal.prototype.globals._initGlobals();
|
|
// global entities for backward compatibility with termlib 1.x applications
|
|
var TerminalDefaults = Terminal.prototype.Defaults;
|
|
var termDefaultHandler = Terminal.prototype.defaultHandler;
|
|
var TermGlobals = Terminal.prototype.globals;
|
|
var termKey = Terminal.prototype.globals.termKey;
|
|
var termDomKeyRef = Terminal.prototype.globals.termDomKeyRef;
|
|
// eof
|