content attribute and tag handling.
- handle content attributes with better respect to element's style. - add a more sophisticated tag parser.
This commit is contained in:
parent
927c40d2f3
commit
084f28fcff
|
@ -1843,16 +1843,32 @@ Program.prototype.text = function(text, attr) {
|
|||
// NOTE: sun-color may not allow multiple params for SGR.
|
||||
Program.prototype._attr = function(param, val) {
|
||||
var self = this
|
||||
, param = param || 'normal'
|
||||
, parts = param.split(/\s*[,;]\s*/)
|
||||
, param
|
||||
, parts
|
||||
, color
|
||||
, m;
|
||||
|
||||
if (Array.isArray(param)) {
|
||||
parts = param;
|
||||
param = parts[0] || 'normal';
|
||||
} else {
|
||||
param = param || 'normal';
|
||||
parts = param.split(/\s*[,;]\s*/);
|
||||
}
|
||||
|
||||
if (parts.length > 1) {
|
||||
parts = parts.map(function(part) {
|
||||
return self._attr(part, val).slice(2, -1);
|
||||
var used = {}
|
||||
, out = [];
|
||||
|
||||
parts.forEach(function(part) {
|
||||
part = self._attr(part, val).slice(2, -1);
|
||||
if (part === '') return;
|
||||
if (used[part]) return;
|
||||
used[part] = true;
|
||||
out.push(part);
|
||||
});
|
||||
return '\x1b[' + parts.join(';') + 'm';
|
||||
|
||||
return '\x1b[' + out.join(';') + 'm';
|
||||
}
|
||||
|
||||
if (param.indexOf('no ') === 0) {
|
||||
|
@ -1867,6 +1883,7 @@ Program.prototype._attr = function(param, val) {
|
|||
// attributes
|
||||
case 'normal':
|
||||
case 'default':
|
||||
if (val === false) return '';
|
||||
return '\x1b[m';
|
||||
case 'bold':
|
||||
return val === false
|
||||
|
@ -1891,10 +1908,6 @@ Program.prototype._attr = function(param, val) {
|
|||
return val === false
|
||||
? '\x1b[28m'
|
||||
: '\x1b[8m';
|
||||
case 'invisible':
|
||||
return val === false
|
||||
? '\x1b[28m'
|
||||
: '\x1b[8m';
|
||||
|
||||
// 8-color foreground
|
||||
case 'black fg':
|
||||
|
@ -1930,6 +1943,7 @@ Program.prototype._attr = function(param, val) {
|
|||
? '\x1b[39m'
|
||||
: '\x1b[37m';
|
||||
case 'default fg':
|
||||
if (val === false) return '';
|
||||
return '\x1b[39m';
|
||||
|
||||
// 8-color background
|
||||
|
@ -1966,6 +1980,7 @@ Program.prototype._attr = function(param, val) {
|
|||
? '\x1b[49m'
|
||||
: '\x1b[47m';
|
||||
case 'default bg':
|
||||
if (val === false) return '';
|
||||
return '\x1b[49m';
|
||||
|
||||
// 16-color foreground
|
||||
|
@ -2038,6 +2053,7 @@ Program.prototype._attr = function(param, val) {
|
|||
|
||||
// non-16-color rxvt default fg and bg
|
||||
case 'default fg bg':
|
||||
if (val === false) return '';
|
||||
return this.term('rxvt')
|
||||
? '\x1b[100m'
|
||||
: '\x1b[39;49m';
|
||||
|
@ -2081,7 +2097,12 @@ Program.prototype._attr = function(param, val) {
|
|||
return '\x1b[48;5;' + color + 'm';
|
||||
}
|
||||
}
|
||||
return '\x1b[' + param + 'm';
|
||||
|
||||
if (/^[\d;]*$/.test(param)) {
|
||||
return '\x1b[' + param + 'm';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
185
lib/widget.js
185
lib/widget.js
|
@ -1143,60 +1143,75 @@ Screen.prototype._reduceColor = function(col) {
|
|||
};
|
||||
|
||||
// Convert an SGR string to our own attribute format.
|
||||
Screen.prototype.attrCode = function(code, cur) {
|
||||
Screen.prototype.attrCode = function(code, cur, def) {
|
||||
var flags = (cur >> 18) & 0x1ff
|
||||
, fg = (cur >> 9) & 0x1ff
|
||||
, bg = cur & 0x1ff
|
||||
, c
|
||||
, i;
|
||||
|
||||
code = /^\x1b\[([\d;]*)m$/.exec(code);
|
||||
if (!code) return cur;
|
||||
|
||||
code = code[1].split(';');
|
||||
code = code.slice(2, -1).split(';');
|
||||
if (!code[0]) code[0] = '0';
|
||||
|
||||
for (i = 0; i < code.length; i++) {
|
||||
c = +code[i] || 0;
|
||||
switch (c) {
|
||||
case 0: // normal
|
||||
bg = 0x1ff;
|
||||
fg = 0x1ff;
|
||||
flags = 0;
|
||||
//bg = 0x1ff;
|
||||
//fg = 0x1ff;
|
||||
//flags = 0;
|
||||
bg = def & 0x1ff;
|
||||
fg = (def >> 9) & 0x1ff;
|
||||
flags = (def >> 18) & 0x1ff;
|
||||
break;
|
||||
case 1: // bold
|
||||
flags |= 1;
|
||||
break;
|
||||
case 22:
|
||||
flags &= ~1;
|
||||
//flags &= ~1;
|
||||
flags = (def >> 18) & 0x1ff;
|
||||
break;
|
||||
case 4: // underline
|
||||
flags |= 2;
|
||||
break;
|
||||
case 24:
|
||||
flags &= ~2;
|
||||
//flags &= ~2;
|
||||
flags = (def >> 18) & 0x1ff;
|
||||
break;
|
||||
case 5: // blink
|
||||
flags |= 4;
|
||||
break;
|
||||
case 25:
|
||||
flags &= ~4;
|
||||
//flags &= ~4;
|
||||
flags = (def >> 18) & 0x1ff;
|
||||
break;
|
||||
case 7: // inverse
|
||||
flags |= 8;
|
||||
break;
|
||||
case 27:
|
||||
flags &= ~8;
|
||||
//flags &= ~8;
|
||||
flags = (def >> 18) & 0x1ff;
|
||||
break;
|
||||
case 8: // invisible
|
||||
flags |= 16;
|
||||
break;
|
||||
case 28:
|
||||
flags &= ~16;
|
||||
//flags &= ~16;
|
||||
flags = (def >> 18) & 0x1ff;
|
||||
break;
|
||||
case 39: // default fg
|
||||
//fg = 0x1ff;
|
||||
fg = (def >> 9) & 0x1ff;
|
||||
break;
|
||||
case 49: // default bg
|
||||
//bg = 0x1ff;
|
||||
bg = def & 0x1ff;
|
||||
break;
|
||||
case 100: // default fg/bg
|
||||
bg = 0x1ff;
|
||||
fg = 0x1ff;
|
||||
//fg = 0x1ff;
|
||||
//bg = 0x1ff;
|
||||
fg = (def >> 9) & 0x1ff;
|
||||
bg = def & 0x1ff;
|
||||
break;
|
||||
default: // color
|
||||
if (c === 48 && +code[i+1] === 5) {
|
||||
|
@ -1226,14 +1241,21 @@ Screen.prototype.attrCode = function(code, cur) {
|
|||
bg = c - 100;
|
||||
bg += 8;
|
||||
} else if (c === 49) {
|
||||
bg = 0x1ff;
|
||||
//bg = 0x1ff;
|
||||
bg = def & 0x1ff;
|
||||
} else if (c >= 30 && c <= 37) {
|
||||
fg = c - 30;
|
||||
} else if (c >= 90 && c <= 97) {
|
||||
fg = c - 90;
|
||||
fg += 8;
|
||||
} else if (c === 39) {
|
||||
fg = 0x1ff;
|
||||
//fg = 0x1ff;
|
||||
fg = (def >> 9) & 0x1ff;
|
||||
} else if (c === 100) {
|
||||
//fg = 0x1ff;
|
||||
//bg = 0x1ff;
|
||||
fg = (def >> 9) & 0x1ff;
|
||||
bg = def & 0x1ff;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1915,21 +1937,128 @@ Element.prototype._parseTags = function(text) {
|
|||
var program = this.screen.program;
|
||||
return text.replace(/{(\/?)([\w\-,;!#]*)}/g, function(tag, slash, color) {
|
||||
if (!color) return slash ? '\x1b[m' : tag;
|
||||
|
||||
color = color.replace(/-/g, ' ');
|
||||
var result = program._attr(color, !slash);
|
||||
|
||||
// Parse error. Just return the original text.
|
||||
if (!/^\x1b\[[\d;]*m$/.test(result)) {
|
||||
return tag;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result != null ? result : tag;
|
||||
});
|
||||
};
|
||||
|
||||
// Convert `{red-fg}foo{/red-fg}` to `\x1b[31mfoo\x1b[39m`.
|
||||
Element.prototype._parseTags_ = function(text) {
|
||||
if (!this.parseTags) return text;
|
||||
if (!/{\/?[\w\-,;!#]*}/.test(text)) return text;
|
||||
|
||||
var out = ''
|
||||
, state
|
||||
, bg = []
|
||||
, fg = []
|
||||
, flag = []
|
||||
, cap
|
||||
, slash
|
||||
, param
|
||||
, attr;
|
||||
|
||||
for (;;) {
|
||||
if (cap = /^{(\/?)([\w\-,;!#]*)}/.exec(text)) {
|
||||
text = text.substring(cap[0].length);
|
||||
slash = cap[1] === '/';
|
||||
param = cap[2].replace(/-/g, ' ');
|
||||
|
||||
if (param.slice(-3) === ' bg') state = bg;
|
||||
else if (param.slice(-3) === ' fg') state = fg;
|
||||
else state = flag;
|
||||
|
||||
if (slash) {
|
||||
if (!param) {
|
||||
out += this._attr('normal');
|
||||
bg.length = 0;
|
||||
fg.length = 0;
|
||||
flag.length = 0;
|
||||
} else {
|
||||
attr = this._attr(param, false);
|
||||
if (attr == null) {
|
||||
out += cap[0];
|
||||
} else {
|
||||
if (param !== state[state.length-1]) {
|
||||
throw new Error('Misnested tags.');
|
||||
}
|
||||
state.pop();
|
||||
if (state.length) {
|
||||
out += this._attr(state[state.length-1]);
|
||||
} else {
|
||||
out += attr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!param) {
|
||||
out += cap[0];
|
||||
} else {
|
||||
attr = this._attr(param);
|
||||
if (attr == null) {
|
||||
out += cap[0];
|
||||
} else {
|
||||
state.push(param);
|
||||
out += attr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cap = /^[\s\S]+?(?={\/?[\w\-,;!#]*})/.exec(text)) {
|
||||
text = text.substring(cap[0].length);
|
||||
out += cap[0];
|
||||
continue;
|
||||
}
|
||||
|
||||
out += text;
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
Element.prototype._attr = function(param, val) {
|
||||
return this.screen.program._attr(param, val);
|
||||
};
|
||||
|
||||
// If we want the resetting of attributes to the element's current style to
|
||||
// *only* be for tags, we can do this and set the codes above to start at 9000:
|
||||
Element.prototype._attr_ = function(param, val) {
|
||||
var param = this.screen.program._attr(param, val)
|
||||
, i
|
||||
, c;
|
||||
|
||||
if (!param) return param;
|
||||
|
||||
param = param.slice(2, -1).split(';');
|
||||
if (!param[0]) param[0] = '0';
|
||||
|
||||
for (i = 0; i < param.length; i++) {
|
||||
c = +param[i] || 0;
|
||||
switch (c) {
|
||||
case 0: // reset
|
||||
case 22: // reset bold
|
||||
case 24: // reset underline
|
||||
case 25: // reset blink
|
||||
case 27: // reset inverse
|
||||
case 28: // reset invisible
|
||||
case 39: // reset fg
|
||||
case 49: // reset bg
|
||||
case 100: // reset fg/bg
|
||||
param[i] = 9000 + c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return '\x1b[' + out.join(';') + 'm';
|
||||
};
|
||||
|
||||
Element.prototype._parseAttr = function(lines) {
|
||||
var attr = this.sattr(this.style, this.style.fg, this.style.bg)
|
||||
var dattr = this.sattr(this.style, this.style.fg, this.style.bg)
|
||||
, attr = dattr
|
||||
, attrs = []
|
||||
, line
|
||||
, i
|
||||
|
@ -1946,7 +2075,7 @@ Element.prototype._parseAttr = function(lines) {
|
|||
for (i = 0; i < line.length; i++) {
|
||||
if (line[i] === '\x1b') {
|
||||
if (c = /^\x1b\[[\d;]*m/.exec(line.substring(i))) {
|
||||
attr = this.screen.attrCode(c[0], attr);
|
||||
attr = this.screen.attrCode(c[0], attr, dattr);
|
||||
i += c[0].length - 1;
|
||||
}
|
||||
}
|
||||
|
@ -2781,7 +2910,7 @@ Element.prototype.render = function() {
|
|||
while (ch === '\x1b') {
|
||||
if (c = /^\x1b\[[\d;]*m/.exec(content.substring(ci - 1))) {
|
||||
ci += c[0].length - 1;
|
||||
attr = this.screen.attrCode(c[0], attr);
|
||||
attr = this.screen.attrCode(c[0], attr, dattr);
|
||||
ch = content[ci] || ' ';
|
||||
ci++;
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
var blessed = require('../')
|
||||
, screen;
|
||||
|
||||
screen = blessed.screen({
|
||||
dump: __dirname + '/nested-attr.log',
|
||||
smartCSR: true
|
||||
});
|
||||
|
||||
blessed.box({
|
||||
parent: screen,
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
width: '80%',
|
||||
height: '80%',
|
||||
bg: 'black',
|
||||
fg: 'yellow',
|
||||
tags: true,
|
||||
border: {
|
||||
type: 'ascii'
|
||||
},
|
||||
content: '{red-fg}hello {blue-fg}how{/blue-fg} are you?{/red-fg}'
|
||||
});
|
||||
|
||||
screen.key('q', function() {
|
||||
return process.exit(0);
|
||||
});
|
||||
|
||||
screen.render();
|
|
@ -145,7 +145,7 @@ var stext = blessed.scrollabletext({
|
|||
mouse: true,
|
||||
content: lorem,
|
||||
fg: 'blue',
|
||||
bg: 'default',
|
||||
bg: 'black',
|
||||
border: {
|
||||
type: 'ascii',
|
||||
fg: 'default',
|
||||
|
|
Loading…
Reference in New Issue