var self = this;
function on() {
var args = Array.prototype.slice.call(arguments);
- self.removeListener(type, on);
- return listener.apply(self, args);
+ this.removeListener(type, on);
+ return listener.apply(this, args);
}
on.listener = listener;
return this.on(type, on);
* Focus the terminal. Delegates focus handling to the terminal's DOM element.
*/
Terminal.prototype.focus = function() {
- return this.element.focus();
+ if (document.activeElement === this.textarea) {
+ return;
+ }
+
+ if (this.sendFocus) {
+ this.send('\x1b[I');
+ }
+
+ this.element.classList.add('focus');
+ this.showCursor();
+ this.textarea.focus();
+ Terminal.focus = this;
};
/**
* Blur the terminal. Delegates blur handling to the terminal's DOM element.
*/
Terminal.prototype.blur = function() {
- return this.element.blur();
+ if (Terminal.focus !== this) {
+ return;
+ }
+
+ this.element.classList.remove('focus');
+ this.cursorState = 0;
+ this.refresh(this.y, this.y);
+ this.textarea.blur();
+ if (this.sendFocus) {
+ this.send('\x1b[0]');
+ }
+ Terminal.focus = null;
};
/**
* Initialize default behavior
*/
Terminal.prototype.initGlobal = function() {
- Terminal.bindKeys(this);
Terminal.bindPaste(this);
+ Terminal.bindKeys(this);
Terminal.bindCopy(this);
- Terminal.bindCut(this);
- Terminal.bindDrop(this);
- Terminal.bindFocus(this);
- Terminal.bindBlur(this);
- };
-
- /**
- * Clears all selected text, inside the terminal.
- */
- Terminal.prototype.clearSelection = function() {
- var selectionBaseNode = window.getSelection().baseNode;
-
- if (selectionBaseNode && (this.element.contains(selectionBaseNode.parentElement))) {
- window.getSelection().removeAllRanges();
- }
- };
-
- /**
- * This function temporarily enables (leases) the contentEditable value of the terminal, which
- * should be set back to false within 5 seconds at most.
- */
- Terminal.prototype.leaseContentEditable = function (ms, callback) {
- var term = this;
-
- term.element.contentEditable = true;
-
- /**
- * Blur and re-focus instantly. This is due to a weird focus state on Chrome, when setting
- * contentEditable to true on a focused element.
- */
- term.blur();
- term.focus();
-
- setTimeout(function () {
- term.element.contentEditable = false;
- if (typeof callback == 'function') {
- callback.call(term);
- }
- }, ms || 5000);
};
/**
* contentEditable value set to true.
*/
Terminal.bindPaste = function(term) {
- on(term.element, 'paste', function(ev) {
+ on([term.textarea, term.element], 'paste', function(ev) {
+ ev.stopPropagation();
if (ev.clipboardData) {
var text = ev.clipboardData.getData('text/plain');
- term.emit('paste', text, ev);
term.handler(text);
- /**
- * Cancel the paste event, or else things will be pasted twice:
- * 1. by the terminal handler
- * 2. by the browser, because of the contentEditable value being true
- */
- term.cancel(ev, true);
-
- /**
- * After the paste event is completed, always set the contentEditable value to false.
- */
- term.element.contentEditable = false;
+ term.textarea.value = '';
+ return term.cancel(ev);
}
});
-
- /**
- * Hack pasting with keyboard, in order to make it work without contentEditable.
- * When a user types Ctrl + Shift + V or Shift + Insert on a non Mac or Cmd + V on a Mac,
- * lease the contentEditable value as true.
- */
- on(term.element, 'keydown', function (ev) {
- var isEditable = term.element.contentEditable === "true";
-
- /**
- * If on a Mac, lease the contentEditable value temporarily, when the user presses
- * the Cmd button, in a keydown event order to paste frictionlessly.
- */
- if (term.isMac && ev.metaKey && !isEditable) {
- term.leaseContentEditable(5000);
- }
-
- if (!term.isMac && !isEditable) {
- if ((ev.keyCode == 45 && ev.shiftKey && !ev.ctrlKey) || // Shift + Insert
- (ev.keyCode == 86 && ev.shiftKey && ev.ctrlKey)) { // Ctrl + Shict + V
- term.leaseContentEditable();
- }
- }
- });
-
- /**
- * Hack pasting with right-click in order to allow right-click paste, by leasing the
- * contentEditable value as true.
- */
- on(term.element, 'contextmenu', function (ev) {
- term.leaseContentEditable();
- });
- };
-
-
- /**
- * Apply key handling to the terminal
- *
- * @param {Xterm} term The terminal on which to bind key handling
- * @static
- */
- Terminal.bindKeys = function(term) {
- on(term.element, 'keydown', function(ev) {
- term.keyDown(ev);
- }, true);
-
- on(term.element, 'keypress', function(ev) {
- term.keyPress(ev);
- }, true);
};
/**
};
/**
- * Binds copy functionality to the given terminal.
- * @static
+ * Apply key handling to the terminal
*/
- Terminal.bindCopy = function(term) {
- on(term.element, 'copy', function(ev) {
- var copiedText = window.getSelection().toString(),
- text = Terminal.prepareCopiedTextForClipboard(copiedText);
+ Terminal.bindKeys = function(term) {
+ on(term.element, 'keydown', function(ev) {
+ if (document.activeElement != this) {
+ return;
+ }
+ term.keyDown(ev);
+ }, true);
- ev.clipboardData.setData('text/plain', text);
- ev.preventDefault();
- });
- };
+ on(term.element, 'keypress', function(ev) {
+ if (document.activeElement != this) {
+ return;
+ }
+ term.keyPress(ev);
+ }, true);
- /**
- * Cancel the cut event completely.
- * @param {Xterm} term The terminal on which to bind the cut event handling functionality.
- * @static
- */
- Terminal.bindCut = function(term) {
- on(term.element, 'cut', function (ev) {
- ev.preventDefault();
- });
- };
+ on(term.element, 'keyup', term.focus.bind(term));
+ on(term.textarea, 'keydown', function(ev) {
+ term.keyDown(ev);
+ }, true);
- /**
- * Do not perform the "drop" event. Altering the contents of the
- * terminal with drag n drop is unwanted behavior.
- * @param {Xterm} term The terminal on which to bind the drop event handling functionality.
- * @static
- */
- Terminal.bindDrop = function (term) {
- on(term.element, 'drop', function (ev) {
- term.cancel(ev, true);
+ on(term.textarea, 'keypress', function(ev) {
+ term.keyPress(ev);
+ // Truncate the textarea's value, since it is not needed
+ this.value = '';
+ }, true);
+
+ on(term.textarea, 'compositionstart', function(ev) {
+ console.log('compositionstart', ev);
+ // TODO: Set a composing flag
+ });
+ on(term.textarea, 'compositionupdate', function(ev) {
+ console.log('compositionupdate', ev);
+ // TODO: Display text being composed in the UI (needs to be handled in keydown)
+ });
+ on(term.textarea, 'compositionend', function(ev) {
+ console.log('compositionend', ev);
+ // TODO: Send composed text to the pty
+ // TODO: Remove composing flag
});
};
/**
- * Cancel click handling on the given terminal
- * @param {Xterm} term The terminal on which to bind the click event handling functionality.
- * @static
- */
- Terminal.click = function (term) {
- on(term.element, 'click', function (ev) {
- term.cancel(ev, true);
+ * Binds copy functionality to the given terminal.
+ * @static
+ */
+ Terminal.bindCopy = function(term) {
+ on(term.element, 'copy', function(ev) {
+ return; // temporary
});
};
this.element.classList.add('xterm');
this.element.classList.add('xterm-theme-' + this.theme);
this.element.setAttribute('tabindex', 0);
- this.element.spellcheck = false;
/*
* Create the container that will hold the lines of the terminal and then
this.element.appendChild(this.rowContainer);
this.children = [];
+ /*
+ * Create the container that will hold helpers like the textarea for
+ * capturing DOM Events. Then produce the helpers.
+ */
+ this.helperContainer = document.createElement('div');
+ this.helperContainer.classList.add('xterm-helpers');
+ this.element.appendChild(this.helperContainer);
+ this.textarea = document.createElement('textarea');
+ this.textarea.classList.add('xterm-helper-textarea');
+ this.textarea.setAttribute('autocorrect', 'off');
+ this.textarea.setAttribute('autocapitalize', 'off');
+ this.textarea.setAttribute('spellcheck', 'false');
+ this.textarea.tabIndex = 0;
+ this.textarea.onfocus = function() {
+ self.emit('focus', {terminal: self});
+ }
+ this.textarea.onblur = function() {
+ self.emit('blur', {terminal: self});
+ }
+ this.helperContainer.appendChild(this.textarea);
+
for (; i < this.rows; i++) {
this.insertRow();
}
* @param {KeyboardEvent} ev The keydown event to be handled.
*/
Terminal.prototype.keyDown = function(ev) {
+ // TODO: Ignore event if currently composing text
+
var self = this;
var result = this.evaluateKeyEscapeSequence(ev);
Terminal.prototype.keyPress = function(ev) {
var key;
+ this.cancel(ev);
+
if (ev.charCode) {
key = ev.charCode;
} else if (ev.which == null) {
key = String.fromCharCode(key);
- /**
- * When a key is pressed and a character is sent to the terminal, then clear any text
- * selected in the terminal.
- */
- if (key) {
- this.clearSelection();
- }
-
this.emit('keypress', key, ev);
this.emit('key', key, ev);
this.showCursor();
this.handler(key);
- this.cancel(ev, true);
-
return false;
};