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.
|
// NOTE: sun-color may not allow multiple params for SGR.
|
||||||
Program.prototype._attr = function(param, val) {
|
Program.prototype._attr = function(param, val) {
|
||||||
var self = this
|
var self = this
|
||||||
, param = param || 'normal'
|
, param
|
||||||
, parts = param.split(/\s*[,;]\s*/)
|
, parts
|
||||||
, color
|
, color
|
||||||
, m;
|
, m;
|
||||||
|
|
||||||
|
if (Array.isArray(param)) {
|
||||||
|
parts = param;
|
||||||
|
param = parts[0] || 'normal';
|
||||||
|
} else {
|
||||||
|
param = param || 'normal';
|
||||||
|
parts = param.split(/\s*[,;]\s*/);
|
||||||
|
}
|
||||||
|
|
||||||
if (parts.length > 1) {
|
if (parts.length > 1) {
|
||||||
parts = parts.map(function(part) {
|
var used = {}
|
||||||
return self._attr(part, val).slice(2, -1);
|
, 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) {
|
if (param.indexOf('no ') === 0) {
|
||||||
|
@ -1867,6 +1883,7 @@ Program.prototype._attr = function(param, val) {
|
||||||
// attributes
|
// attributes
|
||||||
case 'normal':
|
case 'normal':
|
||||||
case 'default':
|
case 'default':
|
||||||
|
if (val === false) return '';
|
||||||
return '\x1b[m';
|
return '\x1b[m';
|
||||||
case 'bold':
|
case 'bold':
|
||||||
return val === false
|
return val === false
|
||||||
|
@ -1891,10 +1908,6 @@ Program.prototype._attr = function(param, val) {
|
||||||
return val === false
|
return val === false
|
||||||
? '\x1b[28m'
|
? '\x1b[28m'
|
||||||
: '\x1b[8m';
|
: '\x1b[8m';
|
||||||
case 'invisible':
|
|
||||||
return val === false
|
|
||||||
? '\x1b[28m'
|
|
||||||
: '\x1b[8m';
|
|
||||||
|
|
||||||
// 8-color foreground
|
// 8-color foreground
|
||||||
case 'black fg':
|
case 'black fg':
|
||||||
|
@ -1930,6 +1943,7 @@ Program.prototype._attr = function(param, val) {
|
||||||
? '\x1b[39m'
|
? '\x1b[39m'
|
||||||
: '\x1b[37m';
|
: '\x1b[37m';
|
||||||
case 'default fg':
|
case 'default fg':
|
||||||
|
if (val === false) return '';
|
||||||
return '\x1b[39m';
|
return '\x1b[39m';
|
||||||
|
|
||||||
// 8-color background
|
// 8-color background
|
||||||
|
@ -1966,6 +1980,7 @@ Program.prototype._attr = function(param, val) {
|
||||||
? '\x1b[49m'
|
? '\x1b[49m'
|
||||||
: '\x1b[47m';
|
: '\x1b[47m';
|
||||||
case 'default bg':
|
case 'default bg':
|
||||||
|
if (val === false) return '';
|
||||||
return '\x1b[49m';
|
return '\x1b[49m';
|
||||||
|
|
||||||
// 16-color foreground
|
// 16-color foreground
|
||||||
|
@ -2038,6 +2053,7 @@ Program.prototype._attr = function(param, val) {
|
||||||
|
|
||||||
// non-16-color rxvt default fg and bg
|
// non-16-color rxvt default fg and bg
|
||||||
case 'default fg bg':
|
case 'default fg bg':
|
||||||
|
if (val === false) return '';
|
||||||
return this.term('rxvt')
|
return this.term('rxvt')
|
||||||
? '\x1b[100m'
|
? '\x1b[100m'
|
||||||
: '\x1b[39;49m';
|
: '\x1b[39;49m';
|
||||||
|
@ -2081,7 +2097,12 @@ Program.prototype._attr = function(param, val) {
|
||||||
return '\x1b[48;5;' + color + 'm';
|
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.
|
// 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
|
var flags = (cur >> 18) & 0x1ff
|
||||||
, fg = (cur >> 9) & 0x1ff
|
, fg = (cur >> 9) & 0x1ff
|
||||||
, bg = cur & 0x1ff
|
, bg = cur & 0x1ff
|
||||||
, c
|
, c
|
||||||
, i;
|
, i;
|
||||||
|
|
||||||
code = /^\x1b\[([\d;]*)m$/.exec(code);
|
code = code.slice(2, -1).split(';');
|
||||||
if (!code) return cur;
|
|
||||||
|
|
||||||
code = code[1].split(';');
|
|
||||||
if (!code[0]) code[0] = '0';
|
if (!code[0]) code[0] = '0';
|
||||||
|
|
||||||
for (i = 0; i < code.length; i++) {
|
for (i = 0; i < code.length; i++) {
|
||||||
c = +code[i] || 0;
|
c = +code[i] || 0;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 0: // normal
|
case 0: // normal
|
||||||
bg = 0x1ff;
|
//bg = 0x1ff;
|
||||||
fg = 0x1ff;
|
//fg = 0x1ff;
|
||||||
flags = 0;
|
//flags = 0;
|
||||||
|
bg = def & 0x1ff;
|
||||||
|
fg = (def >> 9) & 0x1ff;
|
||||||
|
flags = (def >> 18) & 0x1ff;
|
||||||
break;
|
break;
|
||||||
case 1: // bold
|
case 1: // bold
|
||||||
flags |= 1;
|
flags |= 1;
|
||||||
break;
|
break;
|
||||||
case 22:
|
case 22:
|
||||||
flags &= ~1;
|
//flags &= ~1;
|
||||||
|
flags = (def >> 18) & 0x1ff;
|
||||||
break;
|
break;
|
||||||
case 4: // underline
|
case 4: // underline
|
||||||
flags |= 2;
|
flags |= 2;
|
||||||
break;
|
break;
|
||||||
case 24:
|
case 24:
|
||||||
flags &= ~2;
|
//flags &= ~2;
|
||||||
|
flags = (def >> 18) & 0x1ff;
|
||||||
break;
|
break;
|
||||||
case 5: // blink
|
case 5: // blink
|
||||||
flags |= 4;
|
flags |= 4;
|
||||||
break;
|
break;
|
||||||
case 25:
|
case 25:
|
||||||
flags &= ~4;
|
//flags &= ~4;
|
||||||
|
flags = (def >> 18) & 0x1ff;
|
||||||
break;
|
break;
|
||||||
case 7: // inverse
|
case 7: // inverse
|
||||||
flags |= 8;
|
flags |= 8;
|
||||||
break;
|
break;
|
||||||
case 27:
|
case 27:
|
||||||
flags &= ~8;
|
//flags &= ~8;
|
||||||
|
flags = (def >> 18) & 0x1ff;
|
||||||
break;
|
break;
|
||||||
case 8: // invisible
|
case 8: // invisible
|
||||||
flags |= 16;
|
flags |= 16;
|
||||||
break;
|
break;
|
||||||
case 28:
|
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;
|
break;
|
||||||
case 100: // default fg/bg
|
case 100: // default fg/bg
|
||||||
bg = 0x1ff;
|
//fg = 0x1ff;
|
||||||
fg = 0x1ff;
|
//bg = 0x1ff;
|
||||||
|
fg = (def >> 9) & 0x1ff;
|
||||||
|
bg = def & 0x1ff;
|
||||||
break;
|
break;
|
||||||
default: // color
|
default: // color
|
||||||
if (c === 48 && +code[i+1] === 5) {
|
if (c === 48 && +code[i+1] === 5) {
|
||||||
|
@ -1226,14 +1241,21 @@ Screen.prototype.attrCode = function(code, cur) {
|
||||||
bg = c - 100;
|
bg = c - 100;
|
||||||
bg += 8;
|
bg += 8;
|
||||||
} else if (c === 49) {
|
} else if (c === 49) {
|
||||||
bg = 0x1ff;
|
//bg = 0x1ff;
|
||||||
|
bg = def & 0x1ff;
|
||||||
} else if (c >= 30 && c <= 37) {
|
} else if (c >= 30 && c <= 37) {
|
||||||
fg = c - 30;
|
fg = c - 30;
|
||||||
} else if (c >= 90 && c <= 97) {
|
} else if (c >= 90 && c <= 97) {
|
||||||
fg = c - 90;
|
fg = c - 90;
|
||||||
fg += 8;
|
fg += 8;
|
||||||
} else if (c === 39) {
|
} 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;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1915,21 +1937,128 @@ Element.prototype._parseTags = function(text) {
|
||||||
var program = this.screen.program;
|
var program = this.screen.program;
|
||||||
return text.replace(/{(\/?)([\w\-,;!#]*)}/g, function(tag, slash, color) {
|
return text.replace(/{(\/?)([\w\-,;!#]*)}/g, function(tag, slash, color) {
|
||||||
if (!color) return slash ? '\x1b[m' : tag;
|
if (!color) return slash ? '\x1b[m' : tag;
|
||||||
|
|
||||||
color = color.replace(/-/g, ' ');
|
color = color.replace(/-/g, ' ');
|
||||||
var result = program._attr(color, !slash);
|
var result = program._attr(color, !slash);
|
||||||
|
return result != null ? result : tag;
|
||||||
// Parse error. Just return the original text.
|
|
||||||
if (!/^\x1b\[[\d;]*m$/.test(result)) {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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) {
|
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 = []
|
, attrs = []
|
||||||
, line
|
, line
|
||||||
, i
|
, i
|
||||||
|
@ -1946,7 +2075,7 @@ Element.prototype._parseAttr = function(lines) {
|
||||||
for (i = 0; i < line.length; i++) {
|
for (i = 0; i < line.length; i++) {
|
||||||
if (line[i] === '\x1b') {
|
if (line[i] === '\x1b') {
|
||||||
if (c = /^\x1b\[[\d;]*m/.exec(line.substring(i))) {
|
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;
|
i += c[0].length - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2781,7 +2910,7 @@ Element.prototype.render = function() {
|
||||||
while (ch === '\x1b') {
|
while (ch === '\x1b') {
|
||||||
if (c = /^\x1b\[[\d;]*m/.exec(content.substring(ci - 1))) {
|
if (c = /^\x1b\[[\d;]*m/.exec(content.substring(ci - 1))) {
|
||||||
ci += c[0].length - 1;
|
ci += c[0].length - 1;
|
||||||
attr = this.screen.attrCode(c[0], attr);
|
attr = this.screen.attrCode(c[0], attr, dattr);
|
||||||
ch = content[ci] || ' ';
|
ch = content[ci] || ' ';
|
||||||
ci++;
|
ci++;
|
||||||
} else {
|
} 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,
|
mouse: true,
|
||||||
content: lorem,
|
content: lorem,
|
||||||
fg: 'blue',
|
fg: 'blue',
|
||||||
bg: 'default',
|
bg: 'black',
|
||||||
border: {
|
border: {
|
||||||
type: 'ascii',
|
type: 'ascii',
|
||||||
fg: 'default',
|
fg: 'default',
|
||||||
|
|
Loading…
Reference in New Issue