2017-02-15 11:22:09 -09:00
|
|
|
|
import {clipboard} from 'electron';
|
2016-09-21 06:27:11 -08:00
|
|
|
|
import {hterm, lib} from 'hterm-umdjs';
|
2016-11-17 16:12:23 -09:00
|
|
|
|
import runes from 'runes';
|
|
|
|
|
|
import fromCharCode from './utils/key-code';
|
|
|
|
|
|
import selection from './utils/selection';
|
2017-06-02 16:03:47 -08:00
|
|
|
|
import returnKey from './utils/keymaps';
|
|
|
|
|
|
import CommandRegistry from './command-registry';
|
2016-07-08 10:48:24 -08:00
|
|
|
|
|
|
|
|
|
|
hterm.defaultStorage = new lib.Storage.Memory();
|
|
|
|
|
|
|
2016-08-18 19:09:40 -08:00
|
|
|
|
// Provide selectAll to terminal viewport
|
|
|
|
|
|
hterm.Terminal.prototype.selectAll = function () {
|
2016-12-08 14:09:02 -09:00
|
|
|
|
// If the cursorNode_ having hyperCaret we need to remove it
|
|
|
|
|
|
if (this.cursorNode_.contains(this.hyperCaret)) {
|
|
|
|
|
|
this.cursorNode_.removeChild(this.hyperCaret);
|
|
|
|
|
|
// We need to clear the DOM range to reset anchorNode
|
|
|
|
|
|
selection.clear(this);
|
|
|
|
|
|
selection.all(this);
|
|
|
|
|
|
}
|
2016-08-18 19:09:40 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
2016-07-08 10:48:24 -08:00
|
|
|
|
// override double click behavior to copy
|
|
|
|
|
|
const oldMouse = hterm.Terminal.prototype.onMouse_;
|
|
|
|
|
|
hterm.Terminal.prototype.onMouse_ = function (e) {
|
2016-09-21 06:27:11 -08:00
|
|
|
|
if (e.type === 'dblclick') {
|
2016-08-18 19:09:40 -08:00
|
|
|
|
selection.extend(this);
|
2016-10-08 08:26:07 -08:00
|
|
|
|
console.log('[hyper+hterm] ignore double click');
|
2016-07-08 10:48:24 -08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
return oldMouse.call(this, e);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-11-17 16:12:23 -09:00
|
|
|
|
function containsNonLatinCodepoints(s) {
|
|
|
|
|
|
return /[^\u0000-\u00ff]/.test(s);
|
|
|
|
|
|
}
|
2016-12-07 07:17:22 -09:00
|
|
|
|
|
|
|
|
|
|
// hterm Unicode patch
|
|
|
|
|
|
hterm.TextAttributes.splitWidecharString = function (str) {
|
|
|
|
|
|
const context = runes(str).reduce((ctx, rune) => {
|
|
|
|
|
|
const code = rune.codePointAt(0);
|
|
|
|
|
|
if (code < 128 || lib.wc.charWidth(code) === 1) {
|
|
|
|
|
|
ctx.acc += rune;
|
|
|
|
|
|
return ctx;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ctx.acc) {
|
2016-12-14 10:18:15 -09:00
|
|
|
|
ctx.items.push({str: ctx.acc});
|
2016-12-07 07:17:22 -09:00
|
|
|
|
ctx.acc = '';
|
|
|
|
|
|
}
|
2016-12-14 10:18:15 -09:00
|
|
|
|
ctx.items.push({str: rune, wcNode: true});
|
2016-12-07 07:17:22 -09:00
|
|
|
|
return ctx;
|
|
|
|
|
|
}, {items: [], acc: ''});
|
|
|
|
|
|
if (context.acc) {
|
|
|
|
|
|
context.items.push({str: context.acc});
|
|
|
|
|
|
}
|
|
|
|
|
|
return context.items;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// hterm Unicode patch
|
2017-02-23 02:10:05 -09:00
|
|
|
|
const cache = [];
|
2016-12-07 07:17:22 -09:00
|
|
|
|
lib.wc.strWidth = function (str) {
|
2017-02-23 02:10:05 -09:00
|
|
|
|
const shouldCache = str.length === 1;
|
|
|
|
|
|
if (shouldCache && cache[str] !== undefined) {
|
|
|
|
|
|
return cache[str];
|
|
|
|
|
|
}
|
2016-12-07 07:17:22 -09:00
|
|
|
|
const chars = runes(str);
|
|
|
|
|
|
let width = 0;
|
|
|
|
|
|
let rv = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < chars.length; i++) {
|
|
|
|
|
|
const codePoint = chars[i].codePointAt(0);
|
|
|
|
|
|
width = lib.wc.charWidth(codePoint);
|
|
|
|
|
|
if (width < 0) {
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
2017-08-31 05:20:39 -08:00
|
|
|
|
rv += width * ((codePoint <= 0xFFFF) ? 1 : 2);
|
2016-12-07 07:17:22 -09:00
|
|
|
|
}
|
2017-02-23 02:10:05 -09:00
|
|
|
|
if (shouldCache) {
|
|
|
|
|
|
cache[str] = rv;
|
|
|
|
|
|
}
|
2016-12-07 07:17:22 -09:00
|
|
|
|
return rv;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// hterm Unicode patch
|
|
|
|
|
|
lib.wc.substr = function (str, start, optWidth) {
|
|
|
|
|
|
const chars = runes(str);
|
|
|
|
|
|
let startIndex;
|
|
|
|
|
|
let endIndex;
|
2017-05-19 15:52:36 -08:00
|
|
|
|
let width = 0;
|
2016-12-07 07:17:22 -09:00
|
|
|
|
|
2017-05-19 15:52:36 -08:00
|
|
|
|
for (let i = 0; i < chars.length; i++) {
|
|
|
|
|
|
const codePoint = chars[i].codePointAt(0);
|
|
|
|
|
|
const charWidth = lib.wc.charWidth(codePoint);
|
|
|
|
|
|
if ((width + charWidth) > start) {
|
|
|
|
|
|
startIndex = i;
|
2016-12-07 07:17:22 -09:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2017-05-19 15:52:36 -08:00
|
|
|
|
width += charWidth;
|
2016-12-07 07:17:22 -09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (optWidth) {
|
2017-05-19 15:52:36 -08:00
|
|
|
|
width = 0;
|
|
|
|
|
|
for (endIndex = startIndex; endIndex < chars.length && width < optWidth; endIndex++) {
|
2016-12-07 07:17:22 -09:00
|
|
|
|
width += lib.wc.charWidth(chars[endIndex].charCodeAt(0));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (width > optWidth) {
|
|
|
|
|
|
endIndex--;
|
|
|
|
|
|
}
|
|
|
|
|
|
return chars.slice(startIndex, endIndex).join('');
|
|
|
|
|
|
}
|
|
|
|
|
|
return chars.slice(startIndex).join('');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// MacOS emoji bar support
|
|
|
|
|
|
hterm.Keyboard.prototype.onTextInput_ = function (e) {
|
|
|
|
|
|
if (!e.data) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
runes(e.data).forEach(this.terminal.onVTKeystroke.bind(this.terminal));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-05-19 15:52:36 -08:00
|
|
|
|
hterm.Terminal.IO.prototype.writeUTF8 = function (string) {
|
|
|
|
|
|
if (this.terminal_.io !== this) {
|
|
|
|
|
|
throw new Error('Attempt to print from inactive IO object.');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!containsNonLatinCodepoints(string)) {
|
|
|
|
|
|
this.terminal_.interpret(string);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
runes(string).forEach(rune => {
|
|
|
|
|
|
this.terminal_.getTextAttributes().unicodeNode = containsNonLatinCodepoints(rune);
|
|
|
|
|
|
this.terminal_.interpret(rune);
|
|
|
|
|
|
this.terminal_.getTextAttributes().unicodeNode = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const oldIsDefault = hterm.TextAttributes.prototype.isDefault;
|
|
|
|
|
|
hterm.TextAttributes.prototype.isDefault = function () {
|
|
|
|
|
|
return !this.unicodeNode && oldIsDefault.call(this);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-12-14 10:18:15 -09:00
|
|
|
|
const oldSetFontSize = hterm.Terminal.prototype.setFontSize;
|
|
|
|
|
|
hterm.Terminal.prototype.setFontSize = function (px) {
|
|
|
|
|
|
oldSetFontSize.call(this, px);
|
|
|
|
|
|
const doc = this.getDocument();
|
|
|
|
|
|
let unicodeNodeStyle = doc.getElementById('hyper-unicode-styles');
|
|
|
|
|
|
if (!unicodeNodeStyle) {
|
|
|
|
|
|
unicodeNodeStyle = doc.createElement('style');
|
|
|
|
|
|
unicodeNodeStyle.setAttribute('id', 'hyper-unicode-styles');
|
|
|
|
|
|
doc.head.appendChild(unicodeNodeStyle);
|
2016-11-17 16:12:23 -09:00
|
|
|
|
}
|
2016-12-14 10:18:15 -09:00
|
|
|
|
unicodeNodeStyle.innerHTML = `
|
2017-05-19 15:52:36 -08:00
|
|
|
|
.unicode-node {
|
2016-12-14 10:18:15 -09:00
|
|
|
|
display: inline-block;
|
2017-02-17 11:21:55 -09:00
|
|
|
|
vertical-align: top;
|
2017-02-15 20:16:59 -09:00
|
|
|
|
width: ${this.scrollPort_.characterSize.width}px;
|
2016-12-14 10:18:15 -09:00
|
|
|
|
}
|
|
|
|
|
|
`;
|
2016-11-17 16:12:23 -09:00
|
|
|
|
};
|
|
|
|
|
|
|
2017-05-19 15:52:36 -08:00
|
|
|
|
const oldCreateContainer = hterm.TextAttributes.prototype.createContainer;
|
|
|
|
|
|
hterm.TextAttributes.prototype.createContainer = function (text) {
|
|
|
|
|
|
const container = oldCreateContainer.call(this, text);
|
|
|
|
|
|
if (container.style && runes(text).length === 1 && containsNonLatinCodepoints(text)) {
|
|
|
|
|
|
container.className += ' unicode-node';
|
|
|
|
|
|
}
|
|
|
|
|
|
return container;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Do not match containers when one of them has unicode text (unicode chars need to be alone in their containers)
|
|
|
|
|
|
const oldMatchesContainer = hterm.TextAttributes.prototype.matchesContainer;
|
|
|
|
|
|
hterm.TextAttributes.prototype.matchesContainer = function (obj) {
|
|
|
|
|
|
return oldMatchesContainer.call(this, obj) &&
|
|
|
|
|
|
!this.unicodeNode &&
|
|
|
|
|
|
!containsNonLatinCodepoints(obj.textContent);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-07-08 10:48:24 -08:00
|
|
|
|
// there's no option to turn off the size overlay
|
|
|
|
|
|
hterm.Terminal.prototype.overlaySize = function () {};
|
|
|
|
|
|
|
|
|
|
|
|
// fixing a bug in hterm where a double click triggers
|
|
|
|
|
|
// a non-collapsed selection whose text is '', and results
|
|
|
|
|
|
// in an infinite copy loop
|
|
|
|
|
|
hterm.Terminal.prototype.copySelectionToClipboard = function () {
|
2016-09-21 06:27:11 -08:00
|
|
|
|
const text = this.getSelectionText();
|
2016-10-04 10:16:34 -08:00
|
|
|
|
if (text) {
|
2016-07-08 10:48:24 -08:00
|
|
|
|
this.copyStringToClipboard(text);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-01-06 16:46:54 -09:00
|
|
|
|
let lastEventTimeStamp;
|
|
|
|
|
|
let lastEventKey;
|
2016-07-08 10:48:24 -08:00
|
|
|
|
// passthrough all the commands that are meant to control
|
2016-10-08 08:26:07 -08:00
|
|
|
|
// hyper and not the terminal itself
|
2016-07-08 10:48:24 -08:00
|
|
|
|
const oldKeyDown = hterm.Keyboard.prototype.onKeyDown_;
|
|
|
|
|
|
hterm.Keyboard.prototype.onKeyDown_ = function (e) {
|
2016-10-03 07:29:33 -08:00
|
|
|
|
const modifierKeysConf = this.terminal.modifierKeys;
|
2017-01-06 16:46:54 -09:00
|
|
|
|
if (e.timeStamp === lastEventTimeStamp && e.key === lastEventKey) {
|
|
|
|
|
|
// Event was already processed.
|
|
|
|
|
|
// It seems to occur after a char composition ended by Tab and cause a blur.
|
|
|
|
|
|
// See https://github.com/zeit/hyper/issues/1341
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
lastEventTimeStamp = e.timeStamp;
|
|
|
|
|
|
lastEventKey = e.key;
|
2016-10-03 07:29:33 -08:00
|
|
|
|
|
2016-09-30 04:30:11 -08:00
|
|
|
|
if (e.altKey &&
|
2016-10-03 07:29:33 -08:00
|
|
|
|
e.which !== 16 && // Ignore other modifer keys
|
|
|
|
|
|
e.which !== 17 &&
|
|
|
|
|
|
e.which !== 18 &&
|
|
|
|
|
|
e.which !== 91 &&
|
2016-09-30 04:30:11 -08:00
|
|
|
|
modifierKeysConf.altIsMeta) {
|
2016-10-03 07:29:33 -08:00
|
|
|
|
const char = fromCharCode(e);
|
|
|
|
|
|
this.terminal.onVTKeystroke('\x1b' + char);
|
2016-09-30 04:30:11 -08:00
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (e.metaKey &&
|
|
|
|
|
|
e.code !== 'MetaLeft' &&
|
|
|
|
|
|
e.code !== 'MetaRight' &&
|
2016-10-03 07:29:33 -08:00
|
|
|
|
e.which !== 16 &&
|
|
|
|
|
|
e.which !== 17 &&
|
|
|
|
|
|
e.which !== 18 &&
|
|
|
|
|
|
e.which !== 91 &&
|
2016-09-30 04:30:11 -08:00
|
|
|
|
modifierKeysConf.cmdIsMeta) {
|
2016-10-03 07:29:33 -08:00
|
|
|
|
const char = fromCharCode(e);
|
|
|
|
|
|
this.terminal.onVTKeystroke('\x1b' + char);
|
2016-09-30 04:30:11 -08:00
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-02 16:03:47 -08:00
|
|
|
|
// test key from keymaps before moving forward with actions
|
|
|
|
|
|
const key = returnKey(e);
|
|
|
|
|
|
if (key) {
|
|
|
|
|
|
if (CommandRegistry.getCommand(key)) {
|
|
|
|
|
|
CommandRegistry.exec(key, e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (e.altKey || e.metaKey || key) {
|
2017-01-18 06:19:11 -09:00
|
|
|
|
// If the `hyperCaret` was removed on `selectAll`, we need to insert it back
|
|
|
|
|
|
if (e.key === 'v' && this.terminal.hyperCaret.parentNode !== this.terminal.cursorNode_) {
|
2016-12-15 07:08:36 -09:00
|
|
|
|
this.terminal.focusHyperCaret();
|
|
|
|
|
|
}
|
2016-07-08 10:48:24 -08:00
|
|
|
|
return;
|
2016-09-21 06:27:11 -08:00
|
|
|
|
}
|
2016-12-09 10:03:03 -09:00
|
|
|
|
|
2016-12-15 07:08:36 -09:00
|
|
|
|
// Test for valid keys in order to accept clear status
|
2017-01-18 06:19:11 -09:00
|
|
|
|
const clearBlacklist = [
|
|
|
|
|
|
'control',
|
|
|
|
|
|
'shift',
|
|
|
|
|
|
'capslock',
|
|
|
|
|
|
'dead'
|
|
|
|
|
|
];
|
|
|
|
|
|
if (!clearBlacklist.includes(e.code.toLowerCase()) &&
|
|
|
|
|
|
!clearBlacklist.includes(e.key.toLowerCase())) {
|
2017-09-03 08:06:12 -08:00
|
|
|
|
// Since Electron 1.6.X, there is a race condition with character composition
|
|
|
|
|
|
// if this selection clearing is made synchronously. See #2140.
|
|
|
|
|
|
setTimeout(() => selection.clear(this.terminal), 0);
|
2016-12-15 07:08:36 -09:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-18 06:19:11 -09:00
|
|
|
|
// If the `hyperCaret` was removed on `selectAll`, we need to insert it back
|
|
|
|
|
|
if (this.terminal.hyperCaret.parentNode !== this.terminal.cursorNode_) {
|
2016-12-09 10:03:03 -09:00
|
|
|
|
this.terminal.focusHyperCaret();
|
|
|
|
|
|
}
|
2016-07-08 10:48:24 -08:00
|
|
|
|
return oldKeyDown.call(this, e);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-10-03 18:00:50 -08:00
|
|
|
|
const oldOnMouse = hterm.Terminal.prototype.onMouse_;
|
|
|
|
|
|
hterm.Terminal.prototype.onMouse_ = function (e) {
|
|
|
|
|
|
// override `preventDefault` to not actually
|
|
|
|
|
|
// prevent default when the type of event is
|
|
|
|
|
|
// mousedown, so that we can still trigger
|
|
|
|
|
|
// focus on the terminal when the underlying
|
|
|
|
|
|
// VT is interested in mouse events, as is the
|
|
|
|
|
|
// case of programs like `vtop` that allow for
|
|
|
|
|
|
// the user to click on rows
|
|
|
|
|
|
if (e.type === 'mousedown') {
|
|
|
|
|
|
e.preventDefault = function () { };
|
2017-02-15 11:22:09 -09:00
|
|
|
|
return;
|
2016-10-03 18:00:50 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return oldOnMouse.call(this, e);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-02-15 11:22:09 -09:00
|
|
|
|
hterm.Terminal.prototype.onMouseDown_ = function (e) {
|
|
|
|
|
|
// copy/paste on right click
|
|
|
|
|
|
if (e.button === 2) {
|
|
|
|
|
|
const text = this.getSelectionText();
|
|
|
|
|
|
if (text) {
|
|
|
|
|
|
this.copyStringToClipboard(text);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.onVTKeystroke(clipboard.readText());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-02-17 18:27:29 -09:00
|
|
|
|
// override `ScrollPort.resize` to avoid an expensive calculation
|
|
|
|
|
|
// just to get the size of the scrollbar, which for Hyper is always
|
|
|
|
|
|
// set to overlay (hence with `0`)
|
|
|
|
|
|
hterm.ScrollPort.prototype.resize = function () {
|
|
|
|
|
|
this.currentScrollbarWidthPx = 0;
|
|
|
|
|
|
|
|
|
|
|
|
this.syncScrollHeight();
|
|
|
|
|
|
this.syncRowNodesDimensions_();
|
|
|
|
|
|
|
|
|
|
|
|
this.publish(
|
|
|
|
|
|
'resize',
|
|
|
|
|
|
{scrollPort: this},
|
|
|
|
|
|
() => {
|
|
|
|
|
|
this.scrollRowToBottom(this.rowProvider_.getRowCount());
|
|
|
|
|
|
this.scheduleRedraw();
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-11-09 15:11:51 -09:00
|
|
|
|
// make background transparent to avoid transparency issues
|
|
|
|
|
|
hterm.ScrollPort.prototype.setBackgroundColor = function () {
|
|
|
|
|
|
this.screen_.style.backgroundColor = 'transparent';
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-11-16 09:44:04 -09:00
|
|
|
|
// will be called by the <Term/> right after the `hterm.Terminal` is instantiated
|
|
|
|
|
|
hterm.Terminal.prototype.onHyperCaret = function (caret) {
|
|
|
|
|
|
this.hyperCaret = caret;
|
|
|
|
|
|
let ongoingComposition = false;
|
|
|
|
|
|
|
|
|
|
|
|
caret.addEventListener('compositionstart', () => {
|
|
|
|
|
|
ongoingComposition = true;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// we can ignore `compositionstart` since chromium always fire it with ''
|
|
|
|
|
|
caret.addEventListener('compositionupdate', () => {
|
|
|
|
|
|
this.cursorNode_.style.backgroundColor = 'yellow';
|
|
|
|
|
|
this.cursorNode_.style.borderColor = 'yellow';
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// at this point the char(s) is ready
|
|
|
|
|
|
caret.addEventListener('compositionend', () => {
|
|
|
|
|
|
ongoingComposition = false;
|
|
|
|
|
|
this.cursorNode_.style.backgroundColor = '';
|
|
|
|
|
|
this.setCursorShape(this.getCursorShape());
|
|
|
|
|
|
this.cursorNode_.style.borderColor = this.getCursorColor();
|
|
|
|
|
|
caret.innerText = '';
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// if you open the `Emoji & Symbols` (ctrl+cmd+space)
|
|
|
|
|
|
// and select an emoji, it'll be inserted into our caret
|
|
|
|
|
|
// and stay there until you star a compositon event.
|
|
|
|
|
|
// to avoid that, we'll just check if there's an ongoing
|
|
|
|
|
|
// compostion event. if there's one, we do nothing.
|
|
|
|
|
|
// otherwise, we just remove the emoji and stop the event
|
|
|
|
|
|
// propagation.
|
|
|
|
|
|
// PS: this event will *not* be fired when a standard char
|
|
|
|
|
|
// (a, b, c, 1, 2, 3, etc) is typed – only for composed
|
|
|
|
|
|
// ones and `Emoji & Symbols`
|
|
|
|
|
|
caret.addEventListener('input', e => {
|
|
|
|
|
|
if (!ongoingComposition) {
|
|
|
|
|
|
caret.innerText = '';
|
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// we need to capture pastes, prevent them and send its contents to the terminal
|
|
|
|
|
|
caret.addEventListener('paste', e => {
|
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
const text = e.clipboardData.getData('text');
|
|
|
|
|
|
this.onVTKeystroke(text);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// here we replicate the focus/blur state of our caret on the `hterm` caret
|
|
|
|
|
|
caret.addEventListener('focus', () => {
|
|
|
|
|
|
this.cursorNode_.setAttribute('focus', true);
|
|
|
|
|
|
this.restyleCursor_();
|
|
|
|
|
|
});
|
|
|
|
|
|
caret.addEventListener('blur', () => {
|
|
|
|
|
|
this.cursorNode_.setAttribute('focus', false);
|
|
|
|
|
|
this.restyleCursor_();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// this is necessary because we need to access the `document_` and the hyperCaret
|
|
|
|
|
|
// on `hterm.Screen.prototype.syncSelectionCaret`
|
|
|
|
|
|
this.primaryScreen_.terminal = this;
|
|
|
|
|
|
this.alternateScreen_.terminal = this;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ensure that our contenteditable caret is injected
|
|
|
|
|
|
// inside the term's cursor node and that it's focused
|
|
|
|
|
|
hterm.Terminal.prototype.focusHyperCaret = function () {
|
|
|
|
|
|
if (!this.hyperCaret.parentNode !== this.cursorNode_) {
|
|
|
|
|
|
this.cursorNode_.appendChild(this.hyperCaret);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.hyperCaret.focus();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
hterm.Screen.prototype.syncSelectionCaret = function () {
|
|
|
|
|
|
const p = this.terminal.hyperCaret;
|
|
|
|
|
|
const doc = this.terminal.document_;
|
|
|
|
|
|
const win = doc.defaultView;
|
|
|
|
|
|
const s = win.getSelection();
|
|
|
|
|
|
const r = doc.createRange();
|
|
|
|
|
|
r.selectNodeContents(p);
|
|
|
|
|
|
s.removeAllRanges();
|
|
|
|
|
|
s.addRange(r);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-01-14 11:12:08 -09:00
|
|
|
|
// For some reason, when the original version of this function was called right
|
|
|
|
|
|
// after a new tab was created, it was breaking the focus of the other tab.
|
|
|
|
|
|
// After some investigation, I (matheuss) found that `this.iframe_.focus();` (from
|
|
|
|
|
|
// the original function) was causing the issue. So right now we're overriding
|
|
|
|
|
|
// the function to prevent the `iframe_` from being focused.
|
|
|
|
|
|
// This shouldn't create any side effects – we're _stealing_ the focus from `htem` anyways.
|
2017-01-14 10:56:18 -09:00
|
|
|
|
hterm.ScrollPort.prototype.focus = function () {
|
2017-01-14 11:12:08 -09:00
|
|
|
|
this.screen_.focus();
|
2017-01-14 10:56:18 -09:00
|
|
|
|
};
|
|
|
|
|
|
|
2016-11-16 09:44:04 -09:00
|
|
|
|
// fixes a bug in hterm, where the cursor goes back to `BLOCK`
|
|
|
|
|
|
// after the bell rings
|
|
|
|
|
|
const oldRingBell = hterm.Terminal.prototype.ringBell;
|
|
|
|
|
|
hterm.Terminal.prototype.ringBell = function () {
|
|
|
|
|
|
oldRingBell.call(this);
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.restyleCursor_();
|
|
|
|
|
|
}, 200);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-07-19 09:46:26 -08:00
|
|
|
|
// fixes a bug in hterm, where the shorthand hex
|
|
|
|
|
|
// is not properly converted to rgb
|
2016-07-20 14:21:29 -08:00
|
|
|
|
lib.colors.hexToRGB = function (arg) {
|
2016-09-21 06:27:11 -08:00
|
|
|
|
const hex16 = lib.colors.re_.hex16;
|
|
|
|
|
|
const hex24 = lib.colors.re_.hex24;
|
2016-07-19 09:46:26 -08:00
|
|
|
|
|
2016-09-21 06:27:11 -08:00
|
|
|
|
function convert(hex) {
|
2016-07-20 14:21:29 -08:00
|
|
|
|
if (hex.length === 4) {
|
2016-09-21 06:27:11 -08:00
|
|
|
|
hex = hex.replace(hex16, (h, r, g, b) => {
|
2016-07-20 14:21:29 -08:00
|
|
|
|
return '#' + r + r + g + g + b + b;
|
2016-07-19 09:46:26 -08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2016-09-21 06:27:11 -08:00
|
|
|
|
const ary = hex.match(hex24);
|
|
|
|
|
|
if (!ary) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2016-07-19 09:46:26 -08:00
|
|
|
|
|
2016-07-20 14:21:29 -08:00
|
|
|
|
return 'rgb(' +
|
|
|
|
|
|
parseInt(ary[1], 16) + ', ' +
|
|
|
|
|
|
parseInt(ary[2], 16) + ', ' +
|
|
|
|
|
|
parseInt(ary[3], 16) +
|
|
|
|
|
|
')';
|
2016-07-19 09:46:26 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-31 05:20:39 -08:00
|
|
|
|
if (Array.isArray(arg)) {
|
2016-09-21 06:27:11 -08:00
|
|
|
|
for (let i = 0; i < arg.length; i++) {
|
2016-07-19 09:46:26 -08:00
|
|
|
|
arg[i] = convert(arg[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
arg = convert(arg);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return arg;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-12-24 09:29:52 -09:00
|
|
|
|
// add support for cursor styles 5 and 6, fixes #270
|
|
|
|
|
|
hterm.VT.CSI[' q'] = function (parseState) {
|
|
|
|
|
|
const arg = parseState.args[0];
|
|
|
|
|
|
if (arg === '0' || arg === '1') {
|
|
|
|
|
|
this.terminal.setCursorShape(hterm.Terminal.cursorShape.BLOCK);
|
|
|
|
|
|
this.terminal.setCursorBlink(true);
|
|
|
|
|
|
} else if (arg === '2') {
|
|
|
|
|
|
this.terminal.setCursorShape(hterm.Terminal.cursorShape.BLOCK);
|
|
|
|
|
|
this.terminal.setCursorBlink(false);
|
|
|
|
|
|
} else if (arg === '3') {
|
|
|
|
|
|
this.terminal.setCursorShape(hterm.Terminal.cursorShape.UNDERLINE);
|
|
|
|
|
|
this.terminal.setCursorBlink(true);
|
|
|
|
|
|
} else if (arg === '4') {
|
|
|
|
|
|
this.terminal.setCursorShape(hterm.Terminal.cursorShape.UNDERLINE);
|
|
|
|
|
|
this.terminal.setCursorBlink(false);
|
|
|
|
|
|
} else if (arg === '5') {
|
|
|
|
|
|
this.terminal.setCursorShape(hterm.Terminal.cursorShape.BEAM);
|
|
|
|
|
|
this.terminal.setCursorBlink(true);
|
|
|
|
|
|
} else if (arg === '6') {
|
|
|
|
|
|
this.terminal.setCursorShape(hterm.Terminal.cursorShape.BEAM);
|
|
|
|
|
|
this.terminal.setCursorBlink(false);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn('Unknown cursor style: ' + arg);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2016-07-13 12:44:24 -08:00
|
|
|
|
export default hterm;
|
2016-09-21 06:27:11 -08:00
|
|
|
|
export {lib};
|