Update term.js to 0.0.7

This commit is contained in:
Rajat Vig 2016-01-05 14:20:27 -08:00
parent a4ac0b809a
commit a25027aceb
2 changed files with 321 additions and 105 deletions

View File

@ -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.
}

View File

@ -114,6 +114,54 @@ EventEmitter.prototype.listeners = function(type) {
return this._events[type] = this._events[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;
// 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;
}
// 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);
}
// Listen for mouse events and translate
// them into terminal mouse protocols.
this.bindMouse();
if (!('useMouse' in this.options) || this.options.useMouse) {
// Listen for mouse events and translate
// them into terminal mouse protocols.
this.bindMouse();
}
// this.emit('open');
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;