diff --git a/Procfile.dev b/Procfile.dev new file mode 100644 index 00000000..c7a89ee1 --- /dev/null +++ b/Procfile.dev @@ -0,0 +1 @@ +web: ./bin/server diff --git a/addons/attach/attach.js b/addons/attach/attach.js index e2ce145f..24a45eae 100644 --- a/addons/attach/attach.js +++ b/addons/attach/attach.js @@ -7,123 +7,128 @@ */ (function (attach) { - 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(require('../../src/xterm')); + } 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); + } }; - /** - * 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); + term._getMessage = function (ev) { + if (buffered) { + term._pushToBuffer(ev.data); + } else { + term.write(ev.data); + } }; - /** - * 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); + term._sendData = function (data) { + socket.send(data); }; - return exports; + 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; + }; + + /** + * 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); + }; + + 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 f913d4ea..deefd1fd 100644 --- a/addons/fit/fit.js +++ b/addons/fit/fit.js @@ -12,53 +12,75 @@ * of columns) */ (function (fit) { - 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(require('../../src/xterm')); + } else if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], fit); + } else { + /* + * Plain browser environment + */ + fit(this.Xterm); + } })(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; + /** + * This module provides methods for fitting a terminal's size to a parent container. + * + * @module xterm/addons/fit/fit + */ + var exports = {}; - 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; + 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; - rows = parseInt(availableHeight / characterHeight); - cols = parseInt(availableWidth / characterWidth) - 1; + 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; - geometry = {cols: cols, rows: rows}; - return geometry; - }; + rows = parseInt(availableHeight / characterHeight); + cols = parseInt(availableWidth / characterWidth) - 1; - Xterm.prototype.fit = function () { - var geometry = this.proposeGeometry(); + geometry = {cols: cols, rows: rows}; + return geometry; + }; - this.resize(geometry.cols, geometry.rows); - }; + exports.fit = function (term) { + var geometry = exports.proposeGeometry(term); + + term.resize(geometry.cols, geometry.rows); + }; + + Xterm.prototype.proposeGeometry = function () { + return exports.proposeGeometry(this); + }; + + Xterm.prototype.fit = function () { + return exports.fit(this); + }; + + 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 4816e951..2689e616 100644 --- a/addons/fullscreen/fullscreen.js +++ b/addons/fullscreen/fullscreen.js @@ -11,29 +11,42 @@ * fullscreen mode is being toggled. */ (function (fullscreen) { - 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(require('../../src/xterm')); + } else if (typeof define == 'function') { + /* + * Require.js is available + */ + define(['../../src/xterm'], fullscreen); + } else { + /* + * Plain browser environment + */ + fullscreen(this.Xterm); + } })(function (Xterm) { - Xterm.prototype.toggleFullscreen = function (fullscreen) { - var fn; + var exports = {}; - if (typeof fullscreen == 'undefined') { - fn = (this.element.classList.contains('fullscreen')) ? 'remove' : 'add'; - } else if (!fullscreen) { - fn = 'remove'; - } else { - fn = 'add'; - } + exports.toggleFullScreen = function (term, fullscreen) { + var fn; - this.element.classList[fn]('fullscreen'); - }; -}); \ No newline at end of file + 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'); + }; + + Xterm.prototype.toggleFullscreen = function (fullscreen) { + exports.toggleFullScreen(this, fullscreen); + }; + + 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 9834183b..152404d4 100644 --- a/addons/linkify/linkify.js +++ b/addons/linkify/linkify.js @@ -1,182 +1,187 @@ (function (linkify) { - if (typeof define == 'function') { - /* - * Require.js is available - */ - define(['../../src/xterm'], linkify); - } else { - /* - * Plain browser environment - */ - linkify(this.Xterm); - } -})(function (Xterm) { - '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 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); - }; - - - /** - * 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. * - * @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. + * @event linkify:line */ - Xterm.prototype.linkifyTerminalLine = function (line, lenient) { - return exports.linkifyTerminalLine(this, line, lenient); - }; + terminal.emit('linkify:line', line); + }; - /** - * Converts all valid URLs found in the current terminal into hyperlinks. + + /** + * 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 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; +} diff --git a/src/xterm.js b/src/xterm.js index fd9d03a4..e3410f20 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') { + /* + * CommonJS environment + */ + module.exports = xterm.call(this); + } else if (typeof define == 'function') { /* * Require.js is available */ @@ -375,6 +380,8 @@ Terminal.options = {}; + Terminal.focus = null; + each(keys(Terminal.defaults), function(key) { Terminal[key] = Terminal.defaults[key]; Terminal.options[key] = Terminal.defaults[key]; @@ -394,7 +401,6 @@ this.showCursor(); this.element.focus(); - }; Terminal.prototype.blur = function() { @@ -409,6 +415,7 @@ if (this.sendFocus) { this.send('\x1b[O'); } + Terminal.focus = null; }; /** @@ -950,6 +957,17 @@ }; + /** + * Flags used to render terminal text properly + */ + Terminal.flags = { + BOLD: 0b00001, + UNDERLINE: 0b00010, + BLINK: 0b00100, + INVERSE: 0b01000, + INVISIBLE: 0b10000 + } + /* * Rendering Engine * @@ -1017,8 +1035,7 @@ fg = (data >> 9) & 0x1ff; flags = data >> 18; - // bold - if (flags & 1) { + if (flags & Terminal.flags.BOLD) { if (!Terminal.brokenBold) { out += ' xterm-bold '; } @@ -1026,35 +1043,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 + ' '; } @@ -2505,10 +2537,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); }