From 6a87253d94639caabe0b22043115ce4b463d08de Mon Sep 17 00:00:00 2001 From: Alexander Olsson Date: Mon, 11 Apr 2016 15:06:40 +0200 Subject: [PATCH 01/10] nodejs module definition Allow importing xterm into a nodejs application (for instance an electron application, or perhaps a test environment using phantomjs which also has a DOM), --- src/xterm.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/xterm.js b/src/xterm.js index 42c8b81c..5642bfe7 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -32,7 +32,12 @@ */ (function (xterm) { - if (typeof define == 'function') { + if (typeof exports === 'object' && typeof module === 'object') { + /* + * npm/nodejs project + */ + module.exports = xterm.call(this); + } else if (typeof define == 'function') { /* * Require.js is available */ From 685bc54bf297b094ec9f02b911f4396a56593a40 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 14 Apr 2016 06:35:44 -0500 Subject: [PATCH 02/10] Use shift to reduce lines to preserve lines up to cursor --- src/xterm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xterm.js b/src/xterm.js index 5642bfe7..0d297362 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -2520,10 +2520,10 @@ } else if (j > y) { while (j-- > y) { if (this.lines.length > y + this.ybase) { - this.lines.pop(); + this.lines.shift(); } if (this.children.length > y) { - el = this.children.pop(); + el = this.children.shift(); if (!el) continue; el.parentNode.removeChild(el); } From a5e34e22887599a8449adb85650963eb5989b265 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 20 Apr 2016 15:41:33 -0500 Subject: [PATCH 03/10] Fix cursor blink --- src/xterm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xterm.js b/src/xterm.js index 0d297362..5821a2fc 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -398,7 +398,7 @@ this.showCursor(); this.textarea.focus(); - + Terminal.focus = this; }; Terminal.prototype.blur = function() { From 7988f634f3dd7ad9719c87054a3a2d9025900f58 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 21 Apr 2016 07:57:13 -0500 Subject: [PATCH 04/10] Handle focus clearing --- src/xterm.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/xterm.js b/src/xterm.js index 5821a2fc..69b64511 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -379,6 +379,8 @@ Terminal.options = {}; + Terminal.focus = null; + each(keys(Terminal.defaults), function(key) { Terminal[key] = Terminal.defaults[key]; Terminal.options[key] = Terminal.defaults[key]; @@ -413,6 +415,7 @@ if (this.sendFocus) { this.send('\x1b[O'); } + Terminal.focus = null; }; /** From 537fbad70539ab896a33ab49a956e5df5af30565 Mon Sep 17 00:00:00 2001 From: Paris Date: Tue, 26 Apr 2016 12:17:41 +0300 Subject: [PATCH 05/10] Bump version to 0.33 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 449c61df..33fbf7d3 100644 --- a/bower.json +++ b/bower.json @@ -1,5 +1,5 @@ { "name": "xterm.js", - "version": "0.32", + "version": "0.33", "ignore": ["demo", "test", ".gitignore"] } diff --git a/package.json b/package.json index a3f6bc6a..0e0d8bd1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xterm", - "version": "0.32.0", + "version": "0.33.0", "ignore": ["demo", "test", ".gitignore"], "main": "src/xterm.js", "repository": "https://github.com/sourcelair/xterm.js", From bd2158613ddfe2da930f691f04d5aea674c78e5f Mon Sep 17 00:00:00 2001 From: Paris Date: Tue, 3 May 2016 12:05:13 +0300 Subject: [PATCH 06/10] Make all add-ons CommonJS importable - Fix #52 - Rename `npm/nodejs project` documentation to `CommonJS environment` --- addons/attach/attach.js | 7 ++++++- addons/fit/fit.js | 7 ++++++- addons/fullscreen/fullscreen.js | 11 ++++++++--- addons/linkify/linkify.js | 7 ++++++- src/xterm.js | 2 +- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/addons/attach/attach.js b/addons/attach/attach.js index e2ce145f..2788f8e7 100644 --- a/addons/attach/attach.js +++ b/addons/attach/attach.js @@ -7,7 +7,12 @@ */ (function (attach) { - if (typeof define == 'function') { + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = attach.call(this); + } else if (typeof define == 'function') { /* * Require.js is available */ diff --git a/addons/fit/fit.js b/addons/fit/fit.js index f913d4ea..c39b9cf1 100644 --- a/addons/fit/fit.js +++ b/addons/fit/fit.js @@ -12,7 +12,12 @@ * of columns) */ (function (fit) { - if (typeof define == 'function') { + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = fit.call(this); + } else if (typeof define == 'function') { /* * Require.js is available */ diff --git a/addons/fullscreen/fullscreen.js b/addons/fullscreen/fullscreen.js index 4816e951..4e3d0028 100644 --- a/addons/fullscreen/fullscreen.js +++ b/addons/fullscreen/fullscreen.js @@ -11,7 +11,12 @@ * fullscreen mode is being toggled. */ (function (fullscreen) { - if (typeof define == 'function') { + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = fullscreen.call(this); + } else if (typeof define == 'function') { /* * Require.js is available */ @@ -19,7 +24,7 @@ } else { /* * Plain browser environment - */ + */ fullscreen(this.Xterm); } })(function (Xterm) { @@ -36,4 +41,4 @@ this.element.classList[fn]('fullscreen'); }; -}); \ No newline at end of file +}); diff --git a/addons/linkify/linkify.js b/addons/linkify/linkify.js index 9834183b..ab3c041a 100644 --- a/addons/linkify/linkify.js +++ b/addons/linkify/linkify.js @@ -1,5 +1,10 @@ (function (linkify) { - if (typeof define == 'function') { + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = linkify.call(this); + } else if (typeof define == 'function') { /* * Require.js is available */ diff --git a/src/xterm.js b/src/xterm.js index 69b64511..6bec2030 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -34,7 +34,7 @@ (function (xterm) { if (typeof exports === 'object' && typeof module === 'object') { /* - * npm/nodejs project + * CommonJS environment */ module.exports = xterm.call(this); } else if (typeof define == 'function') { From fff56eea1b5bf00961587d6af9d148bb41992c27 Mon Sep 17 00:00:00 2001 From: Paris Date: Tue, 3 May 2016 12:36:21 +0300 Subject: [PATCH 07/10] Extend Xterm prototype only when `Xterm` is a function Add-ons attempted to extend the `Xterm` prototype no matter what. This was an issue when importing add-ons through CommonJS, where `Xterm` is not available in the global scope. --- addons/attach/attach.js | 203 +++++++++++---------- addons/attach/package.json | 5 + addons/fit/fit.js | 128 ++++++++----- addons/fit/package.json | 5 + addons/fullscreen/fullscreen.js | 77 +++++--- addons/fullscreen/package.json | 5 + addons/linkify/linkify.js | 313 +++++++++++++++++--------------- addons/linkify/package.json | 5 + 8 files changed, 423 insertions(+), 318 deletions(-) create mode 100644 addons/attach/package.json create mode 100644 addons/fit/package.json create mode 100644 addons/fullscreen/package.json create mode 100644 addons/linkify/package.json diff --git a/addons/attach/attach.js b/addons/attach/attach.js index 2788f8e7..775535cf 100644 --- a/addons/attach/attach.js +++ b/addons/attach/attach.js @@ -7,105 +7,111 @@ */ (function (attach) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = attach.call(this); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../src/xterm'], attach); - } else { - /* - * Plain browser environment - */ - attach(this.Xterm); - } + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = attach.call(this); + } else if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], attach); + } else { + /* + * Plain browser environment + */ + attach(this.Xterm); + } })(function (Xterm) { - 'use strict'; + 'use strict'; - /** - * This module provides methods for attaching a terminal to a WebSocket - * stream. - * - * @module xterm/addons/attach/attach - */ - var exports = {}; + /** + * This module provides methods for attaching a terminal to a WebSocket + * stream. + * + * @module xterm/addons/attach/attach + */ + var exports = {}; - /** - * Attaches the given terminal to the given socket. - * - * @param {Xterm} term - The terminal to be attached to the given socket. - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - exports.attach = function (term, socket, bidirectional, buffered) { - bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; - term.socket = socket; + /** + * Attaches the given terminal to the given socket. + * + * @param {Xterm} term - The terminal to be attached to the given socket. + * @param {WebSocket} socket - The socket to attach the current terminal. + * @param {boolean} bidirectional - Whether the terminal should send data + * to the socket as well. + * @param {boolean} buffered - Whether the rendering of incoming data + * should happen instantly or at a maximum + * frequency of 1 rendering per 10ms. + */ + exports.attach = function (term, socket, bidirectional, buffered) { + bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional; + term.socket = socket; - term._flushBuffer = function () { - term.write(term._attachSocketBuffer); - term._attachSocketBuffer = null; - clearTimeout(term._attachSocketBufferTimer); - term._attachSocketBufferTimer = null; - }; - - term._pushToBuffer = function (data) { - if (term._attachSocketBuffer) { - term._attachSocketBuffer += data; - } else { - term._attachSocketBuffer = data; - setTimeout(term._flushBuffer, 10); - } - }; - - term._getMessage = function (ev) { - if (buffered) { - term._pushToBuffer(ev.data); - } else { - term.write(ev.data); - } - }; - - term._sendData = function (data) { - socket.send(data); - }; - - socket.addEventListener('message', term._getMessage); - - if (bidirectional) { - term.on('data', term._sendData); - } - - socket.addEventListener('close', term.detach.bind(term, socket)); - socket.addEventListener('error', term.detach.bind(term, socket)); + term._flushBuffer = function () { + term.write(term._attachSocketBuffer); + term._attachSocketBuffer = null; + clearTimeout(term._attachSocketBufferTimer); + term._attachSocketBufferTimer = null; }; - /** - * Detaches the given terminal from the given socket - * - * @param {Xterm} term - The terminal to be detached from the given socket. - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - exports.detach = function (term, socket) { - term.off('data', term._sendData); - - socket = (typeof socket == 'undefined') ? term.socket : socket; - - if (socket) { - socket.removeEventListener('message', term._getMessage); - } - - delete term.socket; + term._pushToBuffer = function (data) { + if (term._attachSocketBuffer) { + term._attachSocketBuffer += data; + } else { + term._attachSocketBuffer = data; + setTimeout(term._flushBuffer, 10); + } }; + term._getMessage = function (ev) { + if (buffered) { + term._pushToBuffer(ev.data); + } else { + term.write(ev.data); + } + }; + + term._sendData = function (data) { + socket.send(data); + }; + + socket.addEventListener('message', term._getMessage); + + if (bidirectional) { + term.on('data', term._sendData); + } + + socket.addEventListener('close', term.detach.bind(term, socket)); + socket.addEventListener('error', term.detach.bind(term, socket)); + }; + + /** + * Detaches the given terminal from the given socket + * + * @param {Xterm} term - The terminal to be detached from the given socket. + * @param {WebSocket} socket - The socket from which to detach the current + * terminal. + */ + exports.detach = function (term, socket) { + term.off('data', term._sendData); + + socket = (typeof socket == 'undefined') ? term.socket : socket; + + if (socket) { + socket.removeEventListener('message', term._getMessage); + } + + delete term.socket; + }; + + /** + * Extends the given terminal prototype with the public methods of this add-on. + * + * @param {function} Xterm - The prototype to be extended. + */ + exports.extendXtermPrototype = function (Xterm) { /** * Attaches the current terminal to the given socket * @@ -117,7 +123,7 @@ * frequency of 1 rendering per 10ms. */ Xterm.prototype.attach = function (socket, bidirectional, buffered) { - return exports.attach(this, socket, bidirectional, buffered); + return exports.attach(this, socket, bidirectional, buffered); }; /** @@ -127,8 +133,17 @@ * terminal. */ Xterm.prototype.detach = function (socket) { - return exports.detach(this, socket); + return exports.detach(this, socket); }; + }; - return exports; + /** + * If the Xterm parameter is a function, then extend it with the methods declared in this + * add-on. + */ + if (typeof Xterm == 'function') { + exports.extendXtermPrototype(Xterm); + } + + return exports; }); diff --git a/addons/attach/package.json b/addons/attach/package.json new file mode 100644 index 00000000..9e45068b --- /dev/null +++ b/addons/attach/package.json @@ -0,0 +1,5 @@ +{ + "name": "xterm.attach", + "main": "attach.js", + "private": true +} diff --git a/addons/fit/fit.js b/addons/fit/fit.js index c39b9cf1..3bcf0907 100644 --- a/addons/fit/fit.js +++ b/addons/fit/fit.js @@ -12,58 +12,90 @@ * of columns) */ (function (fit) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = fit.call(this); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../src/xterm'], fit); - } else { - /* - * Plain browser environment - */ - fit(this.Xterm); - } + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = fit.call(this); + } else if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], fit); + } else { + /* + * Plain browser environment + */ + fit(this.Xterm); + } })(function (Xterm) { + /** + * This module provides methods for fitting a terminal's size to a parent container. + * + * @module xterm/addons/fit/fit + */ + var exports = {}; + + exports.proposeGeometry = function (term) { + var parentElementStyle = window.getComputedStyle(term.element.parentElement), + parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')), + parentElementWidth = parseInt(parentElementStyle.getPropertyValue('width')), + elementStyle = window.getComputedStyle(term.element), + elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')), + elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')), + availableHeight = parentElementHeight - elementPaddingVer, + availableWidth = parentElementWidth - elementPaddingHor, + container = term.rowContainer, + subjectRow = term.rowContainer.firstElementChild, + contentBuffer = subjectRow.innerHTML, + characterHeight, + rows, + characterWidth, + cols, + geometry; + + subjectRow.style.display = 'inline'; + subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace + characterWidth = subjectRow.getBoundingClientRect().width; + subjectRow.style.display = ''; // Revert style before calculating height, since they differ. + characterHeight = parseInt(subjectRow.offsetHeight); + subjectRow.innerHTML = contentBuffer; + + rows = parseInt(availableHeight / characterHeight); + cols = parseInt(availableWidth / characterWidth) - 1; + + geometry = {cols: cols, rows: rows}; + return geometry; + }; + + exports.fit = function (term) { + var geometry = exports.proposeGeometry(term); + + term.resize(geometry.cols, geometry.rows); + }; + + /** + * Extends the given terminal prototype with the public methods of this add-on. + * + * @param {function} Xterm - The prototype to be extended. + */ + exports.extendXtermPrototype = function (Xterm) { Xterm.prototype.proposeGeometry = function () { - var parentElementStyle = window.getComputedStyle(this.element.parentElement), - parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')), - parentElementWidth = parseInt(parentElementStyle.getPropertyValue('width')), - elementStyle = window.getComputedStyle(this.element), - elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')), - elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')), - availableHeight = parentElementHeight - elementPaddingVer, - availableWidth = parentElementWidth - elementPaddingHor, - container = this.rowContainer, - subjectRow = this.rowContainer.firstElementChild, - contentBuffer = subjectRow.innerHTML, - characterHeight, - rows, - characterWidth, - cols, - geometry; - - subjectRow.style.display = 'inline'; - subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace - characterWidth = subjectRow.getBoundingClientRect().width; - subjectRow.style.display = ''; // Revert style before calculating height, since they differ. - characterHeight = parseInt(subjectRow.offsetHeight); - subjectRow.innerHTML = contentBuffer; - - rows = parseInt(availableHeight / characterHeight); - cols = parseInt(availableWidth / characterWidth) - 1; - - geometry = {cols: cols, rows: rows}; - return geometry; + return exports.proposeGeometry(this); }; Xterm.prototype.fit = function () { - var geometry = this.proposeGeometry(); - - this.resize(geometry.cols, geometry.rows); + return exports.fit(this); }; + }; + + /** + * If the Xterm parameter is a function, then extend it with the methods declared in this + * add-on. + */ + if (typeof Xterm == 'function') { + exports.extendXtermPrototype(Xterm); + } + + return exports; }); diff --git a/addons/fit/package.json b/addons/fit/package.json new file mode 100644 index 00000000..f7cb5bc8 --- /dev/null +++ b/addons/fit/package.json @@ -0,0 +1,5 @@ +{ + "name": "xterm.fit", + "main": "fit.js", + "private": true +} diff --git a/addons/fullscreen/fullscreen.js b/addons/fullscreen/fullscreen.js index 4e3d0028..1d5a568b 100644 --- a/addons/fullscreen/fullscreen.js +++ b/addons/fullscreen/fullscreen.js @@ -11,34 +11,57 @@ * fullscreen mode is being toggled. */ (function (fullscreen) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = fullscreen.call(this); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../src/xterm'], fullscreen); - } else { - /* - * Plain browser environment - */ - fullscreen(this.Xterm); - } + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = fullscreen.call(this); + } else if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], fullscreen); + } else { + /* + * Plain browser environment + */ + fullscreen(this.Xterm); + } })(function (Xterm) { + var exports = {}; + + exports.toggleFullScreen = function (term, fullscreen) { + var fn; + + if (typeof fullscreen == 'undefined') { + fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add'; + } else if (!fullscreen) { + fn = 'remove'; + } else { + fn = 'add'; + } + + term.element.classList[fn]('fullscreen'); + }; + + /** + * Extends the given terminal prototype with the public methods of this add-on. + * + * @param {function} Xterm - The prototype to be extended. + */ + exports.extendXtermPrototype = function (Xterm) { Xterm.prototype.toggleFullscreen = function (fullscreen) { - var fn; - - if (typeof fullscreen == 'undefined') { - fn = (this.element.classList.contains('fullscreen')) ? 'remove' : 'add'; - } else if (!fullscreen) { - fn = 'remove'; - } else { - fn = 'add'; - } - - this.element.classList[fn]('fullscreen'); + exports.toggleFullScreen(this, fullscreen); }; + }; + + /** + * If the Xterm parameter is a function, then extend it with the methods declared in this + * add-on. + */ + if (typeof Xterm == 'function') { + exports.extendXtermPrototype(Xterm); + } + + return exports; }); diff --git a/addons/fullscreen/package.json b/addons/fullscreen/package.json new file mode 100644 index 00000000..fdaf6880 --- /dev/null +++ b/addons/fullscreen/package.json @@ -0,0 +1,5 @@ +{ + "name": "xterm.fullscreen", + "main": "fullscreen.js", + "private": true +} diff --git a/addons/linkify/linkify.js b/addons/linkify/linkify.js index ab3c041a..86849409 100644 --- a/addons/linkify/linkify.js +++ b/addons/linkify/linkify.js @@ -1,164 +1,170 @@ (function (linkify) { - if (typeof exports === 'object' && typeof module === 'object') { - /* - * CommonJS environment - */ - module.exports = linkify.call(this); - } else if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../src/xterm'], linkify); - } else { - /* + if (typeof exports === 'object' && typeof module === 'object') { + /* + * CommonJS environment + */ + module.exports = linkify.call(this); + } else if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], linkify); + } else { + /* * Plain browser environment */ - linkify(this.Xterm); - } + linkify(this.Xterm); + } })(function (Xterm) { - 'use strict'; + 'use strict'; + + /** + * This module provides methods for convertings valid URL substrings + * into HTML anchor elements (links), inside a terminal view. + * + * @module xterm/addons/linkify/linkify + */ + var exports = {}, + protocolClause = '(https?:\\/\\/)', + domainCharacterSet = '[\\da-z\\.-]+', + negatedDomainCharacterSet = '[^\\da-z\\.-]+', + domainBodyClause = '(' + domainCharacterSet + ')', + tldClause = '([a-z\\.]{2,6})', + hostClause = domainBodyClause + '\\.' + tldClause, + pathClause = '([\\/\\w\\.-]*)*\\/?', + negatedPathCharacterSet = '[^\\/\\w\\.-]+', + bodyClause = hostClause + pathClause, + start = '(?:^|' + negatedDomainCharacterSet + ')(', + end = ')($|' + negatedPathCharacterSet + ')', + lenientUrlClause = start + protocolClause + '?' + bodyClause + end, + strictUrlClause = start + protocolClause + bodyClause + end, + lenientUrlRegex = new RegExp(lenientUrlClause), + strictUrlRegex = new RegExp(strictUrlClause); + + + /** + * Converts all valid URLs found in the given terminal line into + * hyperlinks. The terminal line can be either the HTML element itself + * or the index of the termina line in the children of the terminal + * rows container. + * + * @param {Xterm} terminal - The terminal that owns the given line. + * @param {number|HTMLDivElement} line - The terminal line that should get + * "linkified". + * @param {boolean} lenient - The regex type that will be used to identify links. If lenient is + * false, the regex requires a protocol clause. Defaults to true. + * @emits linkify + * @emits linkify:line + */ + exports.linkifyTerminalLine = function (terminal, line, lenient) { + if (typeof line == 'number') { + line = terminal.rowContainer.children[line]; + } else if (! (line instanceof HTMLDivElement)) { + var message = 'The "line" argument should be either a number'; + message += ' or an HTMLDivElement'; + + throw new TypeError(message); + } + + var buffer = document.createElement('span'), + nodes = line.childNodes; + + for (var j=0; j' + url + '', + newHTML = nodeHTML.replace(url, link); + + line.innerHTML = line.innerHTML.replace(nodeHTML, newHTML); + } /** - * This module provides methods for convertings valid URL substrings - * into HTML anchor elements (links), inside a terminal view. + * This event gets emitted when conversion of all URL susbtrings + * to HTML anchor elements (links) has finished, for a specific + * line of the current Xterm instance. * - * @module xterm/addons/linkify/linkify + * @event linkify:line */ - var exports = {}, - protocolClause = '(https?:\\/\\/)', - domainCharacterSet = '[\\da-z\\.-]+', - negatedDomainCharacterSet = '[^\\da-z\\.-]+', - domainBodyClause = '(' + domainCharacterSet + ')', - tldClause = '([a-z\\.]{2,6})', - hostClause = domainBodyClause + '\\.' + tldClause, - pathClause = '([\\/\\w\\.-]*)*\\/?', - negatedPathCharacterSet = '[^\\/\\w\\.-]+', - bodyClause = hostClause + pathClause, - start = '(?:^|' + negatedDomainCharacterSet + ')(', - end = ')($|' + negatedPathCharacterSet + ')', - lenientUrlClause = start + protocolClause + '?' + bodyClause + end, - strictUrlClause = start + protocolClause + bodyClause + end, - lenientUrlRegex = new RegExp(lenientUrlClause), - strictUrlRegex = new RegExp(strictUrlClause); + terminal.emit('linkify:line', line); + }; + /** + * Converts all valid URLs found in the terminal view into hyperlinks. + * + * @param {Xterm} terminal - The terminal that should get "linkified". + * @param {boolean} lenient - The regex type that will be used to identify links. If lenient is + * false, the regex requires a protocol clause. Defaults to true. + * @emits linkify + * @emits linkify:line + */ + exports.linkify = function (terminal, lenient) { + var rows = terminal.rowContainer.children; + + lenient = (typeof lenient == "boolean") ? lenient : true; + for (var i=0; i' + url + '', - newHTML = nodeHTML.replace(url, link); - - line.innerHTML = line.innerHTML.replace(nodeHTML, newHTML); - } - - /** - * This event gets emitted when conversion of all URL susbtrings - * to HTML anchor elements (links) has finished, for a specific - * line of the current Xterm instance. - * - * @event linkify:line - */ - terminal.emit('linkify:line', line); - }; + terminal.emit('linkify'); + }; + /** + * Extend Xterm prototype. + */ + /** + * Extends the given terminal prototype with the public methods of this add-on. + * + * @param {function} Xterm - The prototype to be extended. + */ + exports.extendXtermPrototype = function (Xterm) { /** - * Converts all valid URLs found in the terminal view into hyperlinks. - * - * @param {Xterm} terminal - The terminal that should get "linkified". - * @param {boolean} lenient - The regex type that will be used to identify links. If lenient is - * false, the regex requires a protocol clause. Defaults to true. - * @emits linkify - * @emits linkify:line - */ - exports.linkify = function (terminal, lenient) { - var rows = terminal.rowContainer.children; - - lenient = (typeof lenient == "boolean") ? lenient : true; - for (var i=0; i Date: Tue, 3 May 2016 12:48:16 +0300 Subject: [PATCH 08/10] Change CommonJS package structure Instead of making Xterm prototype extending optional, just require Xterm and pass it as parameter to the function just like it is being done with RequireJS --- addons/attach/attach.js | 49 ++++++++++------------------- addons/fit/fit.js | 27 ++++------------ addons/fullscreen/fullscreen.js | 21 ++----------- addons/linkify/linkify.js | 55 ++++++++++++--------------------- 4 files changed, 46 insertions(+), 106 deletions(-) diff --git a/addons/attach/attach.js b/addons/attach/attach.js index 775535cf..24a45eae 100644 --- a/addons/attach/attach.js +++ b/addons/attach/attach.js @@ -11,7 +11,7 @@ /* * CommonJS environment */ - module.exports = attach.call(this); + module.exports = attach(require('../../src/xterm')); } else if (typeof define == 'function') { /* * Require.js is available @@ -107,43 +107,28 @@ }; /** - * Extends the given terminal prototype with the public methods of this add-on. + * Attaches the current terminal to the given socket * - * @param {function} Xterm - The prototype to be extended. + * @param {WebSocket} socket - The socket to attach the current terminal. + * @param {boolean} bidirectional - Whether the terminal should send data + * to the socket as well. + * @param {boolean} buffered - Whether the rendering of incoming data + * should happen instantly or at a maximum + * frequency of 1 rendering per 10ms. */ - exports.extendXtermPrototype = function (Xterm) { - /** - * Attaches the current terminal to the given socket - * - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. - */ - Xterm.prototype.attach = function (socket, bidirectional, buffered) { - return exports.attach(this, socket, bidirectional, buffered); - }; - - /** - * Detaches the current terminal from the given socket. - * - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. - */ - Xterm.prototype.detach = function (socket) { - return exports.detach(this, socket); - }; + Xterm.prototype.attach = function (socket, bidirectional, buffered) { + return exports.attach(this, socket, bidirectional, buffered); }; /** - * If the Xterm parameter is a function, then extend it with the methods declared in this - * add-on. + * Detaches the current terminal from the given socket. + * + * @param {WebSocket} socket - The socket from which to detach the current + * terminal. */ - if (typeof Xterm == 'function') { - exports.extendXtermPrototype(Xterm); - } + Xterm.prototype.detach = function (socket) { + return exports.detach(this, socket); + }; return exports; }); diff --git a/addons/fit/fit.js b/addons/fit/fit.js index 3bcf0907..deefd1fd 100644 --- a/addons/fit/fit.js +++ b/addons/fit/fit.js @@ -16,7 +16,7 @@ /* * CommonJS environment */ - module.exports = fit.call(this); + module.exports = fit(require('../../src/xterm')); } else if (typeof define == 'function') { /* * Require.js is available @@ -74,28 +74,13 @@ term.resize(geometry.cols, geometry.rows); }; - /** - * Extends the given terminal prototype with the public methods of this add-on. - * - * @param {function} Xterm - The prototype to be extended. - */ - exports.extendXtermPrototype = function (Xterm) { - Xterm.prototype.proposeGeometry = function () { - return exports.proposeGeometry(this); - }; - - Xterm.prototype.fit = function () { - return exports.fit(this); - }; + Xterm.prototype.proposeGeometry = function () { + return exports.proposeGeometry(this); }; - /** - * If the Xterm parameter is a function, then extend it with the methods declared in this - * add-on. - */ - if (typeof Xterm == 'function') { - exports.extendXtermPrototype(Xterm); - } + Xterm.prototype.fit = function () { + return exports.fit(this); + }; return exports; }); diff --git a/addons/fullscreen/fullscreen.js b/addons/fullscreen/fullscreen.js index 1d5a568b..2689e616 100644 --- a/addons/fullscreen/fullscreen.js +++ b/addons/fullscreen/fullscreen.js @@ -15,7 +15,7 @@ /* * CommonJS environment */ - module.exports = fullscreen.call(this); + module.exports = fullscreen(require('../../src/xterm')); } else if (typeof define == 'function') { /* * Require.js is available @@ -44,24 +44,9 @@ term.element.classList[fn]('fullscreen'); }; - /** - * Extends the given terminal prototype with the public methods of this add-on. - * - * @param {function} Xterm - The prototype to be extended. - */ - exports.extendXtermPrototype = function (Xterm) { - Xterm.prototype.toggleFullscreen = function (fullscreen) { - exports.toggleFullScreen(this, fullscreen); - }; + Xterm.prototype.toggleFullscreen = function (fullscreen) { + exports.toggleFullScreen(this, fullscreen); }; - /** - * If the Xterm parameter is a function, then extend it with the methods declared in this - * add-on. - */ - if (typeof Xterm == 'function') { - exports.extendXtermPrototype(Xterm); - } - return exports; }); diff --git a/addons/linkify/linkify.js b/addons/linkify/linkify.js index 86849409..152404d4 100644 --- a/addons/linkify/linkify.js +++ b/addons/linkify/linkify.js @@ -3,7 +3,7 @@ /* * CommonJS environment */ - module.exports = linkify.call(this); + module.exports = linkify(require('../../src/xterm')); } else if (typeof define == 'function') { /* * Require.js is available @@ -11,8 +11,8 @@ define(['../../src/xterm'], linkify); } else { /* - * Plain browser environment - */ + * Plain browser environment + */ linkify(this.Xterm); } })(function (Xterm) { @@ -159,44 +159,29 @@ */ /** - * Extends the given terminal prototype with the public methods of this add-on. + * Converts all valid URLs found in the current terminal linte into + * hyperlinks. * - * @param {function} Xterm - The prototype to be extended. + * @memberof Xterm + * @param {number|HTMLDivElement} line - The terminal line that should get + * "linkified". + * @param {boolean} lenient - The regex type that will be used to identify links. If lenient is + * false, the regex requires a protocol clause. Defaults to true. */ - exports.extendXtermPrototype = function (Xterm) { - /** - * Converts all valid URLs found in the current terminal linte into - * hyperlinks. - * - * @memberof Xterm - * @param {number|HTMLDivElement} line - The terminal line that should get - * "linkified". - * @param {boolean} lenient - The regex type that will be used to identify links. If lenient is - * false, the regex requires a protocol clause. Defaults to true. - */ - Xterm.prototype.linkifyTerminalLine = function (line, lenient) { - return exports.linkifyTerminalLine(this, line, lenient); - }; - - /** - * Converts all valid URLs found in the current terminal into hyperlinks. - * - * @memberof Xterm - * @param {boolean} lenient - The regex type that will be used to identify links. If lenient is - * false, the regex requires a protocol clause. Defaults to true. - */ - Xterm.prototype.linkify = function (lenient) { - return exports.linkify(this, lenient); - }; + Xterm.prototype.linkifyTerminalLine = function (line, lenient) { + return exports.linkifyTerminalLine(this, line, lenient); }; /** - * If the Xterm parameter is a function, then extend it with the methods declared in this - * add-on. + * Converts all valid URLs found in the current terminal into hyperlinks. + * + * @memberof Xterm + * @param {boolean} lenient - The regex type that will be used to identify links. If lenient is + * false, the regex requires a protocol clause. Defaults to true. */ - if (typeof Xterm == 'function') { - exports.extendXtermPrototype(Xterm); - } + Xterm.prototype.linkify = function (lenient) { + return exports.linkify(this, lenient); + }; return exports; }); From 3d64f91a5cb95a17fced556f7e65473f47c84f9b Mon Sep 17 00:00:00 2001 From: Paris Date: Thu, 26 May 2016 11:41:42 +0300 Subject: [PATCH 09/10] Properly adjust height of terminal rows and child elements Fix #54 --- src/xterm.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/xterm.css b/src/xterm.css index b0876f2d..7a4246a9 100644 --- a/src/xterm.css +++ b/src/xterm.css @@ -2134,3 +2134,19 @@ .terminal .xterm-bg-color-255 { background-color: #eeeeee; } + +/** + * All terminal rows should have explicitly declared height, + * in order to allow child elements to adjust. + */ +.terminal .xterm-rows > div { + line-height: 1.2; +} + +/** + * All styled spans inside terminal lines should be inline-blocks, + * in orde to achieve better height adjustment. + */ +.terminal .xterm-rows span { + display: inline-block; +} From 1a3846161a6fc58f45cb5b13aa93bc73d6112341 Mon Sep 17 00:00:00 2001 From: Paris Date: Mon, 30 May 2016 17:11:31 +0300 Subject: [PATCH 10/10] Proper rendering of inverse data in default colors Fix #57 --- src/xterm.js | 59 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/src/xterm.js b/src/xterm.js index 6bec2030..608f88b9 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -968,6 +968,17 @@ }; + /** + * Flags used to render terminal text properly + */ + Terminal.flags = { + BOLD: 0b00001, + UNDERLINE: 0b00010, + BLINK: 0b00100, + INVERSE: 0b01000, + INVISIBLE: 0b10000 + } + /* * Rendering Engine * @@ -1035,8 +1046,7 @@ fg = (data >> 9) & 0x1ff; flags = data >> 18; - // bold - if (flags & 1) { + if (flags & Terminal.flags.BOLD) { if (!Terminal.brokenBold) { out += ' xterm-bold '; } @@ -1044,35 +1054,50 @@ if (fg < 8) fg += 8; } - // underline - if (flags & 2) { + if (flags & Terminal.flags.UNDERLINE) { out += ' xterm-underline '; } - // blink - if (flags & 4) { + if (flags & Terminal.flags.BLINK) { out += ' xterm-blink '; } - // inverse - if (flags & 8) { - bg = (data >> 9) & 0x1ff; - fg = data & 0x1ff; - // Should inverse just be before the - // above boldColors effect instead? - if ((flags & 1) && fg < 8) fg += 8; + /** + * If inverse flag is on, then swap the foreground and background variables. + */ + if (flags & Terminal.flags.INVERSE) { + /* One-line variable swap in JavaScript: http://stackoverflow.com/a/16201730 */ + bg = [fg, fg = bg][0]; + // Should inverse just be before the + // above boldColors effect instead? + if ((flags & 1) && fg < 8) fg += 8; } - // invisible - if (flags & 16) { + if (flags & Terminal.flags.INVISIBLE) { out += ' xterm-hidden '; } - if (bg !== 256) { + /** + * Weird situation: Invert flag used black foreground and white background results + * in invalid background color, positioned at the 256 index of the 256 terminal + * color map. Pin the colors manually in such a case. + * + * Source: https://github.com/sourcelair/xterm.js/issues/57 + */ + if (flags & Terminal.flags.INVERSE) { + if (bg == 257) { + bg = 15; + } + if (fg == 256) { + fg = 0; + } + } + + if (bg < 256) { out += ' xterm-bg-color-' + bg + ' '; } - if (fg !== 257) { + if (fg < 256) { out += ' xterm-color-' + fg + ' '; }