mirror of
https://github.com/nuintun/command-manager.git
synced 2025-06-06 10:54:07 +08:00
update files
This commit is contained in:
parent
40d80cf2ea
commit
39de864aee
@ -4,7 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<script type="text/javascript" src="./static/js/terminal/index.js"></script>
|
||||
<script type="text/javascript" src="./static/js/components/app-main/canvas-xterm.js"></script>
|
||||
<script type="text/javascript" src="./static/js/terminal/lru.js"></script>
|
||||
<script type="text/javascript" src="./static/js/terminal/canvas-xterm.js"></script>
|
||||
<style type="text/css">
|
||||
#term {
|
||||
color: #fff;
|
||||
@ -85,7 +86,7 @@
|
||||
|
||||
xterm.write('\033[4;41;32m红底下划线绿字\033[0m\r\n' + new Date().toISOString() + '\r\n');
|
||||
|
||||
// document.getElementById('term').innerHTML = xterm.toString('html');
|
||||
document.getElementById('term').innerHTML = xterm.toString('html');
|
||||
|
||||
canvasXTerm.draw(xterm.styles());
|
||||
|
||||
|
@ -41,20 +41,23 @@ CanvasXTerm.prototype = {
|
||||
draw: function (screen){
|
||||
var text = '';
|
||||
var width, height;
|
||||
var rows = screen.length;
|
||||
var cols = rows ? screen[0].length : 0;
|
||||
var rows = screen.rows;
|
||||
var cols = screen.cols;
|
||||
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;
|
||||
|
||||
width = this.measureWidth(
|
||||
width = measureWidth(
|
||||
this.brush,
|
||||
textRepeat('A', cols),
|
||||
'italic bold ' + this.font.size + 'px ' + this.font.family
|
||||
);
|
||||
|
||||
height = rows * this.font.lineHeight;
|
||||
|
||||
this.lru = new LRUCache(rows);
|
||||
} else {
|
||||
width = this.canvas.width;
|
||||
height = this.canvas.height;
|
||||
@ -64,13 +67,29 @@ CanvasXTerm.prototype = {
|
||||
this.canvas.width = width;
|
||||
this.canvas.height = height;
|
||||
|
||||
var line;
|
||||
var canvas;
|
||||
var brush;
|
||||
|
||||
for (i = 0; i < rows; i++) {
|
||||
x = 0;
|
||||
y = (i + 0.5) * this.font.lineHeight;
|
||||
text = '';
|
||||
x = 0;
|
||||
y = i * this.font.lineHeight;
|
||||
line = this.lru.get(screen.buffer[i].id);
|
||||
|
||||
if (line && line.version === screen.buffer[i].version) {
|
||||
this.brush.drawImage(line.canvas, 0, y, line.canvas.width, line.canvas.height);
|
||||
continue;
|
||||
}
|
||||
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = this.font.lineHeight;
|
||||
canvas.style.backgroundColor = 'transparent';
|
||||
brush = canvas.getContext('2d');
|
||||
|
||||
for (j = 0; j < cols; j++) {
|
||||
node = screen[i][j];
|
||||
node = screen.buffer[i].cells[j];
|
||||
|
||||
if (j === 0) {
|
||||
attrCache = node.attr;
|
||||
@ -79,7 +98,7 @@ CanvasXTerm.prototype = {
|
||||
|
||||
if (node.value) {
|
||||
if (node.attr !== attrCache) {
|
||||
x = this.drawText(text, x, y, this.getStyles(stylesCache)).x;
|
||||
x = drawLine(brush, text, x, this.getStyles(stylesCache));
|
||||
text = '';
|
||||
attrCache = node.attr;
|
||||
stylesCache = this.getStyles(node);
|
||||
@ -89,7 +108,15 @@ CanvasXTerm.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
this.drawText(text, x, y, this.getStyles(stylesCache));
|
||||
if (text) {
|
||||
drawLine(brush, text, x, this.getStyles(stylesCache));
|
||||
}
|
||||
|
||||
this.brush.drawImage(canvas, 0, y, canvas.width, canvas.height);
|
||||
this.lru.set(screen.buffer[i].id, {
|
||||
canvas: canvas,
|
||||
version: screen.buffer[i].version
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
@ -100,6 +127,8 @@ CanvasXTerm.prototype = {
|
||||
getStyles: function (node){
|
||||
var styles = {};
|
||||
|
||||
styles.font = this.font;
|
||||
|
||||
if (node.background) {
|
||||
styles.background = node.background;
|
||||
}
|
||||
@ -119,70 +148,28 @@ CanvasXTerm.prototype = {
|
||||
});
|
||||
|
||||
return styles;
|
||||
},
|
||||
/**
|
||||
* drawText
|
||||
* @param text
|
||||
* @param x
|
||||
* @param y
|
||||
* @param styles
|
||||
* @returns {{x: *, y: *}}
|
||||
*/
|
||||
drawText: function (text, x, y, 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.brush.fillStyle = styles.background;
|
||||
|
||||
this.brush.fillRect(x, y - this.font.size / 2, width, this.font.size);
|
||||
this.brush.restore();
|
||||
}
|
||||
|
||||
this.brush.save();
|
||||
|
||||
this.brush.font = font;
|
||||
this.brush.fillStyle = styles.foreground;
|
||||
this.brush.textAlign = 'start';
|
||||
this.brush.textBaseline = 'middle';
|
||||
|
||||
this.brush.fillText(text, x, y);
|
||||
this.brush.restore();
|
||||
|
||||
if (styles.underline) {
|
||||
underline(this.brush, x, x + width, y + this.font.size / 2, styles.foreground);
|
||||
}
|
||||
|
||||
return {
|
||||
x: x + width,
|
||||
y: y
|
||||
};
|
||||
},
|
||||
/**
|
||||
* measureWidth
|
||||
* @param text
|
||||
* @param font
|
||||
* @returns {Number}
|
||||
*/
|
||||
measureWidth: function (text, font){
|
||||
this.brush.save();
|
||||
|
||||
this.brush.font = font;
|
||||
|
||||
var width = this.brush.measureText(text).width;
|
||||
|
||||
this.brush.restore();
|
||||
|
||||
return width;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* measureWidth
|
||||
* @param brush
|
||||
* @param text
|
||||
* @param font
|
||||
* @returns {Number}
|
||||
*/
|
||||
function measureWidth(brush, text, font){
|
||||
brush.save();
|
||||
|
||||
brush.font = font;
|
||||
|
||||
var width = brush.measureText(text).width;
|
||||
|
||||
brush.restore();
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* draw underline
|
||||
* @param brush
|
||||
@ -202,3 +189,49 @@ function underline(brush, fromX, toX, Y, foreground){
|
||||
brush.stroke();
|
||||
brush.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* drawLine
|
||||
* @param brush
|
||||
* @param text
|
||||
* @param x
|
||||
* @param styles
|
||||
*/
|
||||
function drawLine(brush, text, x, styles){
|
||||
var y;
|
||||
var font = (styles.italic ? 'italic ' : 'normal ')
|
||||
+ (styles.bold ? 'bold ' : 'normal ')
|
||||
+ styles.font.size + 'px '
|
||||
+ styles.font.family;
|
||||
|
||||
var width = measureWidth(brush, text, font);
|
||||
|
||||
if (styles.background) {
|
||||
brush.save();
|
||||
|
||||
brush.fillStyle = styles.background;
|
||||
y = (styles.font.lineHeight - styles.font.size) / 2;
|
||||
|
||||
brush.fillRect(x, y, width, styles.font.size);
|
||||
brush.restore();
|
||||
}
|
||||
|
||||
brush.save();
|
||||
|
||||
brush.font = font;
|
||||
brush.fillStyle = styles.foreground;
|
||||
brush.textAlign = 'start';
|
||||
brush.textBaseline = 'middle';
|
||||
y = styles.font.lineHeight / 2;
|
||||
|
||||
brush.fillText(text, x, y);
|
||||
brush.restore();
|
||||
|
||||
if (styles.underline) {
|
||||
y = (styles.font.lineHeight + styles.font.size) / 2;
|
||||
|
||||
underline(brush, x, x + width, y, styles.foreground);
|
||||
}
|
||||
|
||||
return x + width;
|
||||
}
|
@ -1043,13 +1043,21 @@ AnsiTerminal.prototype.reset = function (){
|
||||
* @returns {*|Array}
|
||||
*/
|
||||
AnsiTerminal.prototype.styles = function (){
|
||||
var stylesBuffer = [];
|
||||
var i, j, cols, node, styleBuffer;
|
||||
var rows = this.screen.buffer.length;
|
||||
var i, j, node, styleBuffer;
|
||||
var rows = this.rows;
|
||||
var cols = this.cols;
|
||||
var stylesBuffer = {
|
||||
buffer: [],
|
||||
rows: rows,
|
||||
cols: cols
|
||||
};
|
||||
|
||||
for (i = 0; i < rows; ++i) {
|
||||
stylesBuffer[i] = [];
|
||||
cols = this.screen.buffer[i].cells.length;
|
||||
stylesBuffer.buffer[i] = {
|
||||
id: this.screen.buffer[i].uniqueId,
|
||||
version: this.screen.buffer[i].version,
|
||||
cells: []
|
||||
};
|
||||
|
||||
for (j = 0; j < cols; ++j) {
|
||||
styleBuffer = [];
|
||||
@ -1057,7 +1065,7 @@ AnsiTerminal.prototype.styles = function (){
|
||||
styleBuffer = styles(node);
|
||||
styleBuffer.attr = node.attr;
|
||||
styleBuffer.value = node.value;
|
||||
stylesBuffer[i][j] = styleBuffer;
|
||||
stylesBuffer.buffer[i].cells[j] = styleBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1071,8 +1079,9 @@ AnsiTerminal.prototype.styles = function (){
|
||||
*/
|
||||
AnsiTerminal.prototype.toString = function (type){
|
||||
var s = '';
|
||||
var i, j, cols, node;
|
||||
var rows = this.screen.buffer.length;
|
||||
var i, j, node;
|
||||
var rows = this.rows;
|
||||
var cols = this.cols;
|
||||
|
||||
if (type === 'html') {
|
||||
var text = '';
|
||||
@ -1083,13 +1092,11 @@ AnsiTerminal.prototype.toString = function (type){
|
||||
var stylesBuffer = this.styles();
|
||||
|
||||
for (i = 0; i < rows; ++i) {
|
||||
cols = this.screen.buffer[i].cells.length;
|
||||
|
||||
line = '<div>';
|
||||
|
||||
for (j = 0; j < cols; ++j) {
|
||||
styleBuffer = stylesBuffer[i][j];
|
||||
node = this.screen.buffer[i].cells[j];
|
||||
styleBuffer = stylesBuffer.buffer[i].cells[j];
|
||||
|
||||
if (j === 0) {
|
||||
style = htmlStyle(styleBuffer);
|
||||
@ -1123,8 +1130,6 @@ AnsiTerminal.prototype.toString = function (type){
|
||||
// FIXME: quick and dirty fill up from left
|
||||
var last_nonspace = 0;
|
||||
|
||||
cols = this.screen.buffer[i].cells.length;
|
||||
|
||||
for (j = 0; j < cols; ++j) {
|
||||
node = this.screen.buffer[i].cells[j];
|
||||
|
||||
|
313
static/js/terminal/lru.js
Normal file
313
static/js/terminal/lru.js
Normal file
@ -0,0 +1,313 @@
|
||||
/**
|
||||
* A doubly linked list-based Least Recently Used (LRU) cache. Will keep most
|
||||
* recently used items while discarding least recently used items when its limit
|
||||
* is reached.
|
||||
*
|
||||
* Licensed under MIT. Copyright (c) 2010 Rasmus Andersson <http://hunch.se/>
|
||||
* See README.md for details.
|
||||
*
|
||||
* Illustration of the design:
|
||||
*
|
||||
* entry entry entry entry
|
||||
* ______ ______ ______ ______
|
||||
* | head |.newer => | |.newer => | |.newer => | tail |
|
||||
* | A | | B | | C | | D |
|
||||
* |______| <= older.|______| <= older.|______| <= older.|______|
|
||||
*
|
||||
* removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added
|
||||
*/
|
||||
function LRUCache(limit){
|
||||
// Current size of the cache. (Read-only).
|
||||
this.size = 0;
|
||||
// Maximum number of items this cache can hold.
|
||||
this.limit = limit;
|
||||
this._keymap = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Put <value> into the cache associated with <key>. Returns the entry which was
|
||||
* removed to make room for the new entry. Otherwise undefined is returned
|
||||
* (i.e. if there was enough room already).
|
||||
*/
|
||||
LRUCache.prototype.put = function (key, value){
|
||||
var entry = { key: key, value: value };
|
||||
|
||||
// Note: No protection agains replacing, and thus orphan entries. By design.
|
||||
this._keymap[key] = entry;
|
||||
|
||||
if (this.tail) {
|
||||
// link previous tail to the new tail (entry)
|
||||
this.tail.newer = entry;
|
||||
entry.older = this.tail;
|
||||
} else {
|
||||
// we're first in -- yay
|
||||
this.head = entry;
|
||||
}
|
||||
|
||||
// add new entry to the end of the linked list -- it's now the freshest entry.
|
||||
this.tail = entry;
|
||||
|
||||
if (this.size === this.limit) {
|
||||
// we hit the limit -- remove the head
|
||||
return this.shift();
|
||||
} else {
|
||||
// increase the size counter
|
||||
this.size++;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Purge the least recently used (oldest) entry from the cache. Returns the
|
||||
* removed entry or undefined if the cache was empty.
|
||||
*
|
||||
* If you need to perform any form of finalization of purged items, this is a
|
||||
* good place to do it. Simply override/replace this function:
|
||||
*
|
||||
* var c = new LRUCache(123);
|
||||
* c.shift = function() {
|
||||
* var entry = LRUCache.prototype.shift.call(this);
|
||||
* doSomethingWith(entry);
|
||||
* return entry;
|
||||
* }
|
||||
*/
|
||||
LRUCache.prototype.shift = function (){
|
||||
// todo: handle special case when limit == 1
|
||||
var entry = this.head;
|
||||
|
||||
if (entry) {
|
||||
if (this.head.newer) {
|
||||
this.head = this.head.newer;
|
||||
this.head.older = undefined;
|
||||
} else {
|
||||
this.head = undefined;
|
||||
}
|
||||
|
||||
// Remove last strong reference to <entry> and remove links from the purged
|
||||
// entry being returned:
|
||||
entry.newer = entry.older = undefined;
|
||||
// delete is slow, but we need to do this to avoid uncontrollable growth:
|
||||
delete this._keymap[entry.key];
|
||||
}
|
||||
|
||||
return entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get and register recent use of <key>. Returns the value associated with <key>
|
||||
* or undefined if not in cache.
|
||||
*/
|
||||
LRUCache.prototype.get = function (key, returnEntry){
|
||||
// First, find our cache entry
|
||||
var entry = this._keymap[key];
|
||||
|
||||
// Not cached. Sorry.
|
||||
if (entry === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// As <key> was found in the cache, register it as being requested recently
|
||||
if (entry === this.tail) {
|
||||
// Already the most recenlty used entry, so no need to update the list
|
||||
return returnEntry ? entry : entry.value;
|
||||
}
|
||||
|
||||
// HEAD--------------TAIL
|
||||
// <.older .newer>
|
||||
// <--- add direction --
|
||||
// A B C <D> E
|
||||
if (entry.newer) {
|
||||
if (entry === this.head) {
|
||||
this.head = entry.newer;
|
||||
}
|
||||
|
||||
// C <-- E.
|
||||
entry.newer.older = entry.older;
|
||||
}
|
||||
|
||||
// C. --> E
|
||||
if (entry.older) {
|
||||
entry.older.newer = entry.newer;
|
||||
}
|
||||
|
||||
// D --x
|
||||
entry.newer = undefined;
|
||||
// D. --> E
|
||||
entry.older = this.tail;
|
||||
|
||||
// E. <-- D
|
||||
if (this.tail) {
|
||||
this.tail.newer = entry;
|
||||
}
|
||||
|
||||
this.tail = entry;
|
||||
|
||||
return returnEntry ? entry : entry.value;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Following code is optional and can be removed without breaking the core
|
||||
// functionality.
|
||||
|
||||
/**
|
||||
* Check if <key> is in the cache without registering recent use. Feasible if
|
||||
* you do not want to chage the state of the cache, but only "peek" at it.
|
||||
* Returns the entry associated with <key> if found, or undefined if not found.
|
||||
*/
|
||||
LRUCache.prototype.find = function (key){
|
||||
return this._keymap[key];
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the value of entry with <key>. Returns the old value, or undefined if
|
||||
* entry was not in the cache.
|
||||
*/
|
||||
LRUCache.prototype.set = function (key, value){
|
||||
var oldvalue, entry = this.get(key, true);
|
||||
|
||||
if (entry) {
|
||||
oldvalue = entry.value;
|
||||
entry.value = value;
|
||||
} else {
|
||||
oldvalue = this.put(key, value);
|
||||
|
||||
if (oldvalue) {
|
||||
oldvalue = oldvalue.value;
|
||||
}
|
||||
}
|
||||
|
||||
return oldvalue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove entry <key> from cache and return its value. Returns undefined if not
|
||||
* found.
|
||||
*/
|
||||
LRUCache.prototype.remove = function (key){
|
||||
var entry = this._keymap[key];
|
||||
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete this._keymap[entry.key]; // need to do delete unfortunately
|
||||
|
||||
if (entry.newer && entry.older) {
|
||||
// relink the older entry with the newer entry
|
||||
entry.older.newer = entry.newer;
|
||||
entry.newer.older = entry.older;
|
||||
} else if (entry.newer) {
|
||||
// remove the link to us
|
||||
entry.newer.older = undefined;
|
||||
// link the newer entry to head
|
||||
this.head = entry.newer;
|
||||
} else if (entry.older) {
|
||||
// remove the link to us
|
||||
entry.older.newer = undefined;
|
||||
// link the newer entry to head
|
||||
this.tail = entry.older;
|
||||
} else {// if(entry.older === undefined && entry.newer === undefined) {
|
||||
this.head = this.tail = undefined;
|
||||
}
|
||||
|
||||
this.size--;
|
||||
|
||||
return entry.value;
|
||||
};
|
||||
|
||||
/** Removes all entries */
|
||||
LRUCache.prototype.removeAll = function (){
|
||||
// This should be safe, as we never expose strong refrences to the outside
|
||||
this.head = this.tail = undefined;
|
||||
this.size = 0;
|
||||
this._keymap = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an array containing all keys of entries stored in the cache object, in
|
||||
* arbitrary order.
|
||||
*/
|
||||
if (typeof Object.keys === 'function') {
|
||||
LRUCache.prototype.keys = function (){
|
||||
return Object.keys(this._keymap);
|
||||
};
|
||||
} else {
|
||||
LRUCache.prototype.keys = function (){
|
||||
var keys = [];
|
||||
|
||||
for (var k in this._keymap) {
|
||||
if (this._keymap.hasOwnProperty(k)) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Call `fun` for each entry. Starting with the newest entry if `desc` is a true
|
||||
* value, otherwise starts with the oldest (head) enrty and moves towards the
|
||||
* tail.
|
||||
*
|
||||
* `fun` is called with 3 arguments in the context `context`:
|
||||
* `fun.call(context, Object key, Object value, LRUCache self)`
|
||||
*/
|
||||
LRUCache.prototype.forEach = function (fun, context, desc){
|
||||
var entry;
|
||||
|
||||
if (context === true) {
|
||||
desc = true;
|
||||
context = undefined;
|
||||
} else if (typeof context !== 'object') {
|
||||
context = this;
|
||||
}
|
||||
|
||||
if (desc) {
|
||||
entry = this.tail;
|
||||
|
||||
while (entry) {
|
||||
fun.call(context, entry.key, entry.value, this);
|
||||
|
||||
entry = entry.older;
|
||||
}
|
||||
} else {
|
||||
entry = this.head;
|
||||
|
||||
while (entry) {
|
||||
fun.call(context, entry.key, entry.value, this);
|
||||
|
||||
entry = entry.newer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Returns a JSON (array) representation */
|
||||
LRUCache.prototype.toJSON = function (){
|
||||
var s = [];
|
||||
var entry = this.head;
|
||||
|
||||
while (entry) {
|
||||
s.push({ key: entry.key.toJSON(), value: entry.value.toJSON() });
|
||||
|
||||
entry = entry.newer;
|
||||
}
|
||||
|
||||
return s;
|
||||
};
|
||||
|
||||
/** Returns a String representation */
|
||||
LRUCache.prototype.toString = function (){
|
||||
var s = '';
|
||||
var entry = this.head;
|
||||
|
||||
while (entry) {
|
||||
s += String(entry.key) + ':' + entry.value;
|
||||
entry = entry.newer;
|
||||
|
||||
if (entry) {
|
||||
s += ' < ';
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user