diff --git a/canvas.html b/canvas.html index 50934d7..bb08280 100644 --- a/canvas.html +++ b/canvas.html @@ -70,7 +70,7 @@ canvas.style.backgroundColor = 'transparent'; - var xterm = new AnsiTerminal(120, 80, 0); + var xterm = new AnsiTerminal(80, 10, 0); xterm.debug = false; xterm.newline_mode = true; diff --git a/static/js/components/app-main/canvas-xterm.js b/static/js/components/app-main/canvas-xterm.js index 5ef8e74..432a276 100644 --- a/static/js/components/app-main/canvas-xterm.js +++ b/static/js/components/app-main/canvas-xterm.js @@ -17,8 +17,11 @@ function CanvasXTerm(font){ this.canvas = document.createElement('canvas'); this.canvas.style.backgroundColor = 'transparent'; this.brush = this.canvas.getContext('2d'); + this.brush.textAlign = 'start'; + this.brush.textBaseline = 'bottom'; this.brush.font = this.font + ' ' + this.fontSize; this.brush.fillStyle = this.font.color; + this.baseY = (this.font.lineHeight + this.font.size) / 2; } CanvasXTerm.prototype = { @@ -26,46 +29,45 @@ CanvasXTerm.prototype = { var text = ''; var rows = screen.length; var cols = rows ? screen[0].length : 0; - var node, styles, i, j, x, y, attrCache; - var baseY = (this.font.lineHeight + this.font.size) / 2; + var node, i, j, x, y, attrCache, stylesCache; if (!this.rows || !this.cols || this.rows !== rows || this.cols !== cols) { this.rows = rows; this.cols = cols; - this.canvas.width = this.measureWidth(textRepeat('A', cols), { - style: 'italic', - weight: 'bold', - family: this.font.family, - size: this.font.size - }); + this.canvas.width = this.measureWidth( + textRepeat('A', cols), + 'italic bold ' + ' ' + this.font.size + 'px' + this.font.family + ); this.canvas.height = rows * this.font.lineHeight; } for (i = 0; i < rows; i++) { x = 0; - y = i * 20 + baseY; + y = i * 20 + this.baseY; + text = ''; - for (j = 0; j < rows; j++) { + for (j = 0; j < cols; j++) { node = screen[i][j]; - if (j = 0) { - attrCache = node.attr; - - styles = {}; - } - - if (node.value) { - if (node.attr !== attrCache) { - - x = this.measureWidth(text); - attrCache = node.attr; - } - - text += node.value; - } + //if (j = 0) { + // attrCache = node.attr; + // stylesCache = this.getStyles(node); + //} + // + //if (node.value) { + // if (node.attr !== attrCache) { + // x = this.drawText(text, x, y, this.getStyles(stylesCache)).x; + // attrCache = node.attr; + // stylesCache = this.getStyles(node); + // } + // + // text += node.value; + //} } + + //this.drawText(text, x, y, this.getStyles(stylesCache)); } }, getStyles: function (node){ @@ -81,21 +83,54 @@ CanvasXTerm.prototype = { styles.foreground = this.font.color; } - ['bold', 'italic', 'underline', 'blink', 'conceal'].forEach(function (key){ + if (node.conceal) { + styles.foreground = styles.background = 'transparent'; + } + + ['bold', 'italic', 'underline', 'blink'].forEach(function (key){ styles[key] = node[key]; }); return styles; }, drawText: function (text, x, y, styles){ - }, - measureWidth: function (text, styles){ + var font = styles.italic ? 'italic' : 'normal' + + ' ' + styles.bold ? 'bold' : 'normal' + + ' ' + this.font.size + 'px' + + ' ' + this.font.family; + + var width = this.measureWidth(text, font); + + if (styles.background) { + this.brush.save(); + + this.fillStyle = styles.background; + + this.brush.fillRect(x, y - this.font.size, width, this.font.size); + this.brush.restore(); + } + this.brush.save(); - this.brush.font = styles.style - + ' ' + styles.weight - + ' ' + styles.family - + ' ' + styles.size; + this.font = font; + this.brush.fillStyle = styles.foreground; + + this.fillText(text, x, y); + this.brush.restore(); + + if (styles.underline) { + underline.call(this.brush, x, x + width, y, styles.foreground); + } + + return { + x: x + width, + y: y + }; + }, + measureWidth: function (text, font){ + this.brush.save(); + + this.brush.font = font; var width = this.brush.measureText(text).width; @@ -103,4 +138,16 @@ CanvasXTerm.prototype = { return width; } -}; \ No newline at end of file +}; + +function underline(fromX, toX, Y, foreground){ + this.save(); + this.translate(0, parseInt(Y) === Y ? 0 : 0.5); + this.lineWidth = 1; + this.strokeStyle = foreground; + this.beginPath(); + this.moveTo(fromX, Y); + this.lineTo(toX, Y); + this.stroke(); + this.restore(); +}; diff --git a/static/js/terminal/index.js b/static/js/terminal/index.js index 86c09af..07d42c8 100644 --- a/static/js/terminal/index.js +++ b/static/js/terminal/index.js @@ -1019,6 +1019,41 @@ AnsiTerminal.prototype.reset = function (){ this.row_wrap = false; }; +/** + * getStyles + * @returns {*|Array} + */ +AnsiTerminal.prototype.getStyles = function (){ + var i, j, cols, node, styleBuffer; + var rows = this.screen.buffer.length; + var stylesBuffer = this.stylesBuffer || []; + + stylesBuffer = stylesBuffer.slice(0, rows); + + for (i = 0; i < rows; ++i) { + stylesBuffer[i] = stylesBuffer[i] || []; + cols = this.screen.buffer[i].cells.length; + stylesBuffer[i] = stylesBuffer[i].slice(0, cols); + + for (j = 0; j < cols; ++j) { + styleBuffer = stylesBuffer[i][j]; + node = this.screen.buffer[i].cells[j]; + + if (!styleBuffer || styleBuffer.value !== node.value || styleBuffer.attr !== node.attr) { + styleBuffer = styles(node); + styleBuffer.attr = node.attr; + styleBuffer.value = node.value; + + stylesBuffer[i][j] = styleBuffer; + } + } + } + + this.stylesBuffer = stylesBuffer; + + return stylesBuffer; +}; + /** * toSting * @param [type] @@ -1034,25 +1069,16 @@ AnsiTerminal.prototype.toString = function (type){ var style = ''; var attrCache; var styleBuffer; - var stylesBuffer = this.stylesBuffer || []; - - stylesBuffer = stylesBuffer.slice(0, rows); + var stylesBuffer = this.getStyles(); for (i = 0; i < rows; ++i) { - stylesBuffer[i] = stylesBuffer[i] || []; cols = this.screen.buffer[i].cells.length; line = '