summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Kluyver <takowl@gmail.com>2016-01-05 22:53:33 +0000
committerThomas Kluyver <takowl@gmail.com>2016-01-05 22:53:33 +0000
commit7fef70f6e07504d1b003d6d5f6cb51c7d9dfcdd7 (patch)
tree07ee9c37532bfe42dba378a0d002c767e0f4d3fd
parenta4ac0b809ab0d84c636a760215839bb6d68d0113 (diff)
parenta25027aceb19a0ab8270bebd30438ecb5eaa45a3 (diff)
downloadxstatic-termjs-master.tar.gz
Merge pull request #1 from rajatvig/masterHEADmaster
Update term.js to 0.0.7
-rw-r--r--xstatic/pkg/termjs/__init__.py5
-rw-r--r--xstatic/pkg/termjs/data/term.js423
2 files changed, 322 insertions, 106 deletions
diff --git a/xstatic/pkg/termjs/__init__.py b/xstatic/pkg/termjs/__init__.py
index 85ac28f..c1d3437 100644
--- a/xstatic/pkg/termjs/__init__.py
+++ b/xstatic/pkg/termjs/__init__.py
@@ -11,9 +11,9 @@ NAME = __name__.split('.')[-1] # package name (e.g. 'foo' or 'foo_bar')
# please use a all-lowercase valid python
# package name
-VERSION = '0.0.4' # version of the packaged files, please use the upstream
+VERSION = '0.0.7' # version of the packaged files, please use the upstream
# version number
-BUILD = '2' # our package build number, so we can release new builds
+BUILD = '0' # our package build number, so we can release new builds
# with fixes for xstatic stuff.
PACKAGE_VERSION = VERSION + '.' + BUILD # version used for PyPi
@@ -50,4 +50,3 @@ LOCATIONS = {
# information, because either the base dir/url is exactly for this
# version or the mapping will care for accessing this version.
}
-
diff --git a/xstatic/pkg/termjs/data/term.js b/xstatic/pkg/termjs/data/term.js
index d041e37..f542dd0 100644
--- a/xstatic/pkg/termjs/data/term.js
+++ b/xstatic/pkg/termjs/data/term.js
@@ -115,6 +115,54 @@ EventEmitter.prototype.listeners = function(type) {
};
/**
+ * Stream
+ */
+
+function Stream() {
+ EventEmitter.call(this);
+}
+
+inherits(Stream, EventEmitter);
+
+Stream.prototype.pipe = function(dest, options) {
+ var src = this
+ , ondata
+ , onerror
+ , onend;
+
+ function unbind() {
+ src.removeListener('data', ondata);
+ src.removeListener('error', onerror);
+ src.removeListener('end', onend);
+ dest.removeListener('error', onerror);
+ dest.removeListener('close', unbind);
+ }
+
+ src.on('data', ondata = function(data) {
+ dest.write(data);
+ });
+
+ src.on('error', onerror = function(err) {
+ unbind();
+ if (!this.listeners('error').length) {
+ throw err;
+ }
+ });
+
+ src.on('end', onend = function() {
+ dest.end();
+ unbind();
+ });
+
+ dest.on('error', onerror);
+ dest.on('close', unbind);
+
+ dest.emit('pipe', src);
+
+ return dest;
+};
+
+/**
* States
*/
@@ -124,7 +172,8 @@ var normal = 0
, osc = 3
, charset = 4
, dcs = 5
- , ignore = 6;
+ , ignore = 6
+ , UDK = { type: 'udk' };
/**
* Terminal
@@ -137,7 +186,7 @@ function Terminal(options) {
return new Terminal(arguments[0], arguments[1], arguments[2]);
}
- EventEmitter.call(this);
+ Stream.call(this);
if (typeof options === 'number') {
options = {
@@ -168,7 +217,7 @@ function Terminal(options) {
options.colors = options.colors.slice(0, -2).concat(
Terminal._colors.slice(8, -2), options.colors.slice(-2));
} else if (options.colors.length === 18) {
- options.colors = options.colors.concat(
+ options.colors = options.colors.slice(0, -2).concat(
Terminal._colors.slice(16, -2), options.colors.slice(-2));
}
this.colors = options.colors;
@@ -183,6 +232,13 @@ function Terminal(options) {
this.cols = options.cols || options.geometry[0];
this.rows = options.rows || options.geometry[1];
+ // Act as though we are a node TTY stream:
+ this.setRawMode;
+ this.isTTY = true;
+ this.isRaw = true;
+ this.columns = this.cols;
+ this.rows = this.rows;
+
if (options.handler) {
this.on('data', options.handler);
}
@@ -268,13 +324,7 @@ function Terminal(options) {
this.setupStops();
}
-inherits(Terminal, EventEmitter);
-
-// back_color_erase feature for xterm.
-Terminal.prototype.eraseAttr = function() {
- // if (this.is('screen')) return this.defAttr;
- return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff);
-};
+inherits(Terminal, Stream);
/**
* Colors
@@ -470,8 +520,8 @@ Terminal.prototype.initGlobal = function() {
Terminal.bindCopy(document);
- if (this.isIpad || this.isIphone) {
- Terminal.fixIpad(document);
+ if (this.isMobile) {
+ this.fixMobile(document);
}
if (this.useStyle) {
@@ -598,10 +648,12 @@ Terminal.bindCopy = function(document) {
};
/**
- * Fix iPad - no idea if this works
+ * Fix Mobile
*/
-Terminal.fixIpad = function(document) {
+Terminal.prototype.fixMobile = function(document) {
+ var self = this;
+
var textarea = document.createElement('textarea');
textarea.style.position = 'absolute';
textarea.style.left = '-32000px';
@@ -622,6 +674,15 @@ Terminal.fixIpad = function(document) {
setTimeout(function() {
textarea.focus();
}, 1000);
+
+ if (this.isAndroid) {
+ on(textarea, 'change', function() {
+ var value = textarea.textContent || textarea.value;
+ textarea.value = '';
+ textarea.textContent = '';
+ self.send(value + '\r');
+ });
+ }
};
/**
@@ -695,6 +756,8 @@ Terminal.prototype.open = function(parent) {
this.isMac = !!~this.context.navigator.userAgent.indexOf('Mac');
this.isIpad = !!~this.context.navigator.userAgent.indexOf('iPad');
this.isIphone = !!~this.context.navigator.userAgent.indexOf('iPhone');
+ this.isAndroid = !!~this.context.navigator.userAgent.indexOf('Android');
+ this.isMobile = this.isIpad || this.isIphone || this.isAndroid;
this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE');
}
@@ -703,6 +766,7 @@ Terminal.prototype.open = function(parent) {
this.element.className = 'terminal';
this.element.style.outline = 'none';
this.element.setAttribute('tabindex', 0);
+ this.element.setAttribute('spellcheck', 'false');
this.element.style.backgroundColor = this.colors[256];
this.element.style.color = this.colors[257];
@@ -718,61 +782,77 @@ Terminal.prototype.open = function(parent) {
// Draw the screen.
this.refresh(0, this.rows - 1);
- // Initialize global actions that
- // need to be taken on the document.
- this.initGlobal();
+ if (!('useEvents' in this.options) || this.options.useEvents) {
+ // Initialize global actions that
+ // need to be taken on the document.
+ this.initGlobal();
+ }
- // Ensure there is a Terminal.focus.
- this.focus();
+ if (!('useFocus' in this.options) || this.options.useFocus) {
+ // Ensure there is a Terminal.focus.
+ this.focus();
- // Start blinking the cursor.
- this.startBlink();
+ // Start blinking the cursor.
+ this.startBlink();
- // Bind to DOM events related
- // to focus and paste behavior.
- on(this.element, 'focus', function() {
- self.focus();
- if (self.isIpad || self.isIphone) {
- Terminal._textarea.focus();
- }
- });
+ // Bind to DOM events related
+ // to focus and paste behavior.
+ on(this.element, 'focus', function() {
+ self.focus();
+ if (self.isMobile) {
+ Terminal._textarea.focus();
+ }
+ });
- // This causes slightly funky behavior.
- // on(this.element, 'blur', function() {
- // self.blur();
- // });
+ // This causes slightly funky behavior.
+ // on(this.element, 'blur', function() {
+ // self.blur();
+ // });
- on(this.element, 'mousedown', function() {
- self.focus();
- });
+ on(this.element, 'mousedown', function() {
+ self.focus();
+ });
+
+ // Clickable paste workaround, using contentEditable.
+ // This probably shouldn't work,
+ // ... but it does. Firefox's paste
+ // event seems to only work for textareas?
+ on(this.element, 'mousedown', function(ev) {
+ var button = ev.button != null
+ ? +ev.button
+ : ev.which != null
+ ? ev.which - 1
+ : null;
+
+ // Does IE9 do this?
+ if (self.isMSIE) {
+ button = button === 1 ? 0 : button === 4 ? 1 : button;
+ }
- // Clickable paste workaround, using contentEditable.
- // This probably shouldn't work,
- // ... but it does. Firefox's paste
- // event seems to only work for textareas?
- on(this.element, 'mousedown', function(ev) {
- var button = ev.button != null
- ? +ev.button
- : ev.which != null
- ? ev.which - 1
- : null;
-
- // Does IE9 do this?
- if (self.isMSIE) {
- button = button === 1 ? 0 : button === 4 ? 1 : button;
- }
+ if (button !== 2) return;
- if (button !== 2) return;
+ self.element.contentEditable = 'true';
+ setTimeout(function() {
+ self.element.contentEditable = 'inherit'; // 'false';
+ }, 1);
+ }, true);
+ }
- self.element.contentEditable = 'true';
- setTimeout(function() {
- self.element.contentEditable = 'inherit'; // 'false';
- }, 1);
- }, true);
+ if (!('useMouse' in this.options) || this.options.useMouse) {
+ // Listen for mouse events and translate
+ // them into terminal mouse protocols.
+ this.bindMouse();
+ }
+
+ // this.emit('open');
- // Listen for mouse events and translate
- // them into terminal mouse protocols.
- this.bindMouse();
+ if (!('useFocus' in this.options) || this.options.useFocus) {
+ // This can be useful for pasting,
+ // as well as the iPad fix.
+ setTimeout(function() {
+ self.element.focus();
+ }, 100);
+ }
// Figure out whether boldness affects
// the character width of monospace fonts.
@@ -780,13 +860,11 @@ Terminal.prototype.open = function(parent) {
Terminal.brokenBold = isBoldBroken(this.document);
}
- // this.emit('open');
+ this.emit('open');
+};
- // This can be useful for pasting,
- // as well as the iPad fix.
- setTimeout(function() {
- self.element.focus();
- }, 100);
+Terminal.prototype.setRawMode = function(value) {
+ this.isRaw = !!value;
};
// XTerm mouse events
@@ -1079,10 +1157,11 @@ Terminal.prototype.bindMouse = function() {
// fix for odd bug
//if (self.vt200Mouse && !self.normalMouse) {
- if (self.vt200Mouse) {
- sendButton({ __proto__: ev, type: 'mouseup' });
- return cancel(ev);
- }
+ // XXX This seems to break certain programs.
+ // if (self.vt200Mouse) {
+ // sendButton({ __proto__: ev, type: 'mouseup' });
+ // return cancel(ev);
+ // }
// bind events
if (self.normalMouse) on(self.document, 'mousemove', sendMove);
@@ -1131,16 +1210,35 @@ Terminal.prototype.bindMouse = function() {
* Destroy Terminal
*/
+Terminal.prototype.close =
+Terminal.prototype.destroySoon =
Terminal.prototype.destroy = function() {
+ if (this.destroyed) {
+ return;
+ }
+
+ if (this._blink) {
+ clearInterval(this._blink);
+ delete this._blink;
+ }
+
this.readable = false;
this.writable = false;
+ this.destroyed = true;
this._events = {};
+
this.handler = function() {};
this.write = function() {};
+ this.end = function() {};
+
if (this.element.parentNode) {
this.element.parentNode.removeChild(this.element);
}
- //this.emit('close');
+
+ this.emit('end');
+ this.emit('close');
+ this.emit('finish');
+ this.emit('destroy');
};
/**
@@ -1344,7 +1442,7 @@ Terminal.prototype.startBlink = function() {
};
Terminal.prototype.refreshBlink = function() {
- if (!this.cursorBlink) return;
+ if (!this.cursorBlink || !this._blink) return;
clearInterval(this._blink);
this._blink = setInterval(this._blinker, 500);
};
@@ -1418,7 +1516,7 @@ Terminal.prototype.write = function(data) {
// this.log(JSON.stringify(data.replace(/\x1b/g, '^[')));
- for (; i < l; i++) {
+ for (; i < l; i++, this.lch = ch) {
ch = data[i];
switch (this.state) {
case normal:
@@ -1534,7 +1632,8 @@ Terminal.prototype.write = function(data) {
// ESC P Device Control String ( DCS is 0x90).
case 'P':
this.params = [];
- this.currentParam = 0;
+ this.prefix = '';
+ this.currentParam = '';
this.state = dcs;
break;
@@ -1757,8 +1856,14 @@ Terminal.prototype.write = function(data) {
// OSC Ps ; Pt ST
// OSC Ps ; Pt BEL
// Set Text Parameters.
- if (ch === '\x1b' || ch === '\x07') {
- if (ch === '\x1b') i++;
+ if ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') {
+ if (this.lch === '\x1b') {
+ if (typeof this.currentParam === 'string') {
+ this.currentParam = this.currentParam.slice(0, -1);
+ } else if (typeof this.currentParam == 'number') {
+ this.currentParam = (this.currentParam - ('\x1b'.charCodeAt(0) - 48)) / 10;
+ }
+ }
this.params.push(this.currentParam);
@@ -2290,94 +2395,158 @@ Terminal.prototype.write = function(data) {
break;
case dcs:
- if (ch === '\x1b' || ch === '\x07') {
- if (ch === '\x1b') i++;
+ if ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') {
+ // Workarounds:
+ if (this.prefix === 'tmux;\x1b') {
+ // `DCS tmux; Pt ST` may contain a Pt with an ST
+ // XXX Does tmux work this way?
+ // if (this.lch === '\x1b' & data[i + 1] === '\x1b' && data[i + 2] === '\\') {
+ // this.currentParam += ch;
+ // continue;
+ // }
+ // Tmux only accepts ST, not BEL:
+ if (ch === '\x07') {
+ this.currentParam += ch;
+ continue;
+ }
+ }
+
+ if (this.lch === '\x1b') {
+ if (typeof this.currentParam === 'string') {
+ this.currentParam = this.currentParam.slice(0, -1);
+ } else if (typeof this.currentParam == 'number') {
+ this.currentParam = (this.currentParam - ('\x1b'.charCodeAt(0) - 48)) / 10;
+ }
+ }
+
+ this.params.push(this.currentParam);
+
+ var pt = this.params[this.params.length - 1];
switch (this.prefix) {
// User-Defined Keys (DECUDK).
- case '':
+ // DCS Ps; Ps| Pt ST
+ case UDK:
+ this.emit('udk', {
+ clearAll: this.params[0] === 0,
+ eraseBelow: this.params[0] === 1,
+ lockKeys: this.params[1] === 0,
+ dontLockKeys: this.params[1] === 1,
+ keyList: (this.params[2] + '').split(';').map(function(part) {
+ part = part.split('/');
+ return {
+ keyCode: part[0],
+ hexKeyValue: part[1]
+ };
+ })
+ });
break;
// Request Status String (DECRQSS).
+ // DCS $ q Pt ST
// test: echo -e '\eP$q"p\e\\'
case '$q':
- var pt = this.currentParam
- , valid = false;
+ var valid = 0;
switch (pt) {
// DECSCA
+ // CSI Ps " q
case '"q':
pt = '0"q';
+ valid = 1;
break;
// DECSCL
+ // CSI Ps ; Ps " p
case '"p':
- pt = '61"p';
+ pt = '61;0"p';
+ valid = 1;
break;
// DECSTBM
+ // CSI Ps ; Ps r
case 'r':
pt = ''
+ (this.scrollTop + 1)
+ ';'
+ (this.scrollBottom + 1)
+ 'r';
+ valid = 1;
break;
// SGR
+ // CSI Pm m
case 'm':
- pt = '0m';
+ // TODO: Parse this.curAttr here.
+ // pt = '0m';
+ // valid = 1;
+ valid = 0; // Not implemented.
break;
default:
this.error('Unknown DCS Pt: %s.', pt);
- pt = '';
+ valid = 0; // unimplemented
break;
}
- this.send('\x1bP' + +valid + '$r' + pt + '\x1b\\');
+ this.send('\x1bP' + valid + '$r' + pt + '\x1b\\');
break;
// Set Termcap/Terminfo Data (xterm, experimental).
+ // DCS + p Pt ST
case '+p':
+ this.emit('set terminfo', {
+ name: this.params[0]
+ });
break;
// Request Termcap/Terminfo String (xterm, experimental)
// Regular xterm does not even respond to this sequence.
// This can cause a small glitch in vim.
+ // DCS + q Pt ST
// test: echo -ne '\eP+q6b64\e\\'
case '+q':
- var pt = this.currentParam
- , valid = false;
-
+ var valid = false;
this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\');
break;
+ // Implement tmux sequence forwarding is
+ // someone uses term.js for a multiplexer.
+ // DCS tmux; ESC Pt ST
+ case 'tmux;\x1b':
+ this.emit('passthrough', pt);
+ break;
+
default:
- this.error('Unknown DCS prefix: %s.', this.prefix);
+ this.error('Unknown DCS prefix: %s.', pt);
break;
}
this.currentParam = 0;
this.prefix = '';
this.state = normal;
- } else if (!this.currentParam) {
- if (!this.prefix && ch !== '$' && ch !== '+') {
- this.currentParam = ch;
- } else if (this.prefix.length === 2) {
- this.currentParam = ch;
- } else {
- this.prefix += ch;
- }
} else {
this.currentParam += ch;
+ if (!this.prefix) {
+ if (/^\d*;\d*\|/.test(this.currentParam)) {
+ this.prefix = UDK;
+ this.params = this.currentParam.split(/[;|]/).map(function(n) {
+ if (!n.length) return 0;
+ return +n;
+ }).slice(0, -1);
+ this.currentParam = '';
+ } else if (/^[$+][a-zA-Z]/.test(this.currentParam)
+ || /^\w+;\x1b/.test(this.currentParam)) {
+ this.prefix = this.currentParam;
+ this.currentParam = '';
+ }
+ }
}
break;
case ignore:
// For PM and APC.
- if (ch === '\x1b' || ch === '\x07') {
- if (ch === '\x1b') i++;
+ if ((this.lch === '\x1b' && ch === '\\') || ch === '\x07') {
this.state = normal;
}
break;
@@ -2386,10 +2555,29 @@ Terminal.prototype.write = function(data) {
this.updateRange(this.y);
this.refresh(this.refreshStart, this.refreshEnd);
+
+ return true;
};
Terminal.prototype.writeln = function(data) {
- this.write(data + '\r\n');
+ return this.write(data + '\r\n');
+};
+
+Terminal.prototype.end = function(data) {
+ var ret = true;
+ if (data) {
+ ret = this.write(data);
+ }
+ this.destroySoon();
+ return ret;
+};
+
+Terminal.prototype.resume = function() {
+ ;
+};
+
+Terminal.prototype.pause = function() {
+ ;
};
// Key Resources:
@@ -2401,6 +2589,10 @@ Terminal.prototype.keyDown = function(ev) {
switch (ev.keyCode) {
// backspace
case 8:
+ if (ev.altKey) {
+ key = '\x17';
+ break;
+ }
if (ev.shiftKey) {
key = '\x08'; // ^H
break;
@@ -2430,6 +2622,10 @@ Terminal.prototype.keyDown = function(ev) {
//key = '\x8fD'; // SS3 as 0x8f for 8-bit
break;
}
+ if (ev.ctrlKey) {
+ key = '\x1b[5D';
+ break;
+ }
key = '\x1b[D';
break;
// right-arrow
@@ -2438,6 +2634,10 @@ Terminal.prototype.keyDown = function(ev) {
key = '\x1bOC';
break;
}
+ if (ev.ctrlKey) {
+ key = '\x1b[5C';
+ break;
+ }
key = '\x1b[C';
break;
// up-arrow
@@ -2598,7 +2798,7 @@ Terminal.prototype.keyDown = function(ev) {
// ^] - group sep
key = String.fromCharCode(29);
}
- } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) {
+ } else if (ev.altKey) {
if (ev.keyCode >= 65 && ev.keyCode <= 90) {
key = '\x1b' + String.fromCharCode(ev.keyCode + 32);
} else if (ev.keyCode === 192) {
@@ -2696,6 +2896,7 @@ Terminal.prototype.send = function(data) {
};
Terminal.prototype.bell = function() {
+ this.emit('bell');
if (!this.visualBell) return;
var self = this;
this.element.style.borderColor = 'white';
@@ -2749,6 +2950,7 @@ Terminal.prototype.resize = function(x, y) {
}
this.setupStops(j);
this.cols = x;
+ this.columns = x;
// resize rows
j = this.rows;
@@ -2792,6 +2994,9 @@ Terminal.prototype.resize = function(x, y) {
// screen buffer. just set it
// to null for now.
this.normal = null;
+
+ // Act as though we are a node TTY stream:
+ this.emit('resize');
};
Terminal.prototype.updateRange = function(y) {
@@ -2841,6 +3046,12 @@ Terminal.prototype.nextStop = function(x) {
: x < 0 ? 0 : x;
};
+// back_color_erase feature for xterm.
+Terminal.prototype.eraseAttr = function() {
+ // if (this.is('screen')) return this.defAttr;
+ return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff);
+};
+
Terminal.prototype.eraseRight = function(x, y) {
var line = this.lines[this.ybase + y]
, ch = [this.eraseAttr(), ' ']; // xterm
@@ -5637,13 +5848,18 @@ function inherits(child, parent) {
// use it in the terminal.
function isBoldBroken(document) {
var body = document.getElementsByTagName('body')[0];
+ var terminal = document.createElement('div');
+ terminal.className = 'terminal';
+ var line = document.createElement('div');
var el = document.createElement('span');
el.innerHTML = 'hello world';
- body.appendChild(el);
+ line.appendChild(el);
+ terminal.appendChild(line);
+ body.appendChild(terminal);
var w1 = el.scrollWidth;
el.style.fontWeight = 'bold';
var w2 = el.scrollWidth;
- body.removeChild(el);
+ body.removeChild(terminal);
return w1 !== w2;
}
@@ -5740,6 +5956,7 @@ function keys(obj) {
*/
Terminal.EventEmitter = EventEmitter;
+Terminal.Stream = Stream;
Terminal.inherits = inherits;
Terminal.on = on;
Terminal.off = off;