major changes. better content formatting and parsing.

This commit is contained in:
Christopher Jeffrey 2013-06-20 06:43:56 -05:00
parent 5b8a16df23
commit f431e98c03
3 changed files with 241 additions and 17 deletions

View File

@ -6,8 +6,7 @@ Blessed was originally written to only support the xterm terminfo, but can
now parse and compile any terminfo to be completely portable accross all
terminals. See the `tput` example below.
I want this library to eventually become a high-level library for terminal
widgets.
Blessed also includes an extremely high-level widget library.
## Example Usage
@ -29,7 +28,7 @@ $ tput.js sgr0
$ echo "$(tput.js setaf 2)hello world$(tput.js sgr0)"
```
The higher level functionality is exposed in the main `blessed` module:
The main functionality is exposed in the main `blessed` module:
``` js
var blessed = require('blessed')
@ -135,6 +134,10 @@ The base node which everything inherits from.
- **adopt** - received when node is added to a parent.
- **remove** - received when node is removed from it's current parent.
- **reparent** - received when node gains a new parent.
- **attach** - received when node is attached to the screen directly or
somewhere in its ancestry.
- **detach** - received when node is detached from the screen directly or
somewhere in its ancestry.
##### Methods:

View File

@ -122,6 +122,10 @@ Program.prototype.listen = function() {
}
return;
}
if (key.name === 'undefined' && key.code === '[M') {
// A mouse sequence. The readline module doesn't understand these.
return;
}
self.emit('keypress', ch, key);
});
@ -1816,6 +1820,7 @@ Program.prototype.showCursor = function() {
Program.prototype.alternate =
Program.prototype.alternateBuffer = function() {
if (this.term('vt') || this.term('linux')) return;
//return this.setMode('?47');
//return this.setMode('?1047');
return this.setMode('?1049');
@ -1966,6 +1971,9 @@ Program.prototype.setMouse = function(opt) {
opt.allMotion = opt.normalMouse;
}
// Make sure we're not a vtNNN
if (this.term('vt')) return;
// Ps = 9 -> Send Mouse X & Y on button press. See the sec-
// tion Mouse Tracking.
// Ps = 9 -> Don't send Mouse X & Y on button press.
@ -1985,6 +1993,10 @@ Program.prototype.setMouse = function(opt) {
else this.resetMode('?1000');
}
// Linux Console actually *does* support mouse reporting.
// See: `$ man console_codes`.
if (this.term('linux')) return;
// Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
if (opt.hiliteTracking != null) {

View File

@ -33,6 +33,10 @@ function Node(options) {
this.parent.append(this);
}
if (!this.parent) {
this._detached = true;
}
(options.children || []).forEach(this.append.bind(this));
if (this._isScreen && !this.focused) {
@ -43,6 +47,8 @@ function Node(options) {
Node.prototype.__proto__ = EventEmitter.prototype;
Node.prototype.prepend = function(element) {
var old = element.parent;
element.parent = this;
if (this._isScreen && !this.focused) {
@ -55,9 +61,18 @@ Node.prototype.prepend = function(element) {
element.emit('reparent', this);
this.emit('adopt', element);
//if (!old) {
(function emit(el) {
el._detached = false;
el.emit('attach');
if (el.children) el.children.forEach(emit);
})(element);
};
Node.prototype.append = function(element) {
var old = element.parent;
element.parent = this;
if (this._isScreen && !this.focused) {
@ -70,6 +85,13 @@ Node.prototype.append = function(element) {
element.emit('reparent', this);
this.emit('adopt', element);
//if (!old) {
(function emit(el) {
el._detached = false;
el.emit('attach');
if (el.children) el.children.forEach(emit);
})(element);
};
Node.prototype.remove = function(element) {
@ -93,6 +115,12 @@ Node.prototype.remove = function(element) {
element.emit('reparent', null);
this.emit('remove', element);
(function emit(el) {
el._detached = true;
el.emit('detach');
if (el.children) el.children.forEach(emit);
})(element);
};
Node.prototype.detach = function(element) {
@ -736,6 +764,28 @@ function Element(options) {
self.screen._listenKeys(self);
}
});
this.screen.on('resize', function() {
self.parseContent();
});
this.on('resize', function() {
self.parseContent();
});
this.on('attach', function() {
self.parseContent();
});
this.parseContent();
//if (this.parent) {
// this.parseContent();
//} else {
// this.once('reparent', function() {
// self.parseContent();
// });
//}
}
Element.prototype.__proto__ = Node.prototype;
@ -757,6 +807,20 @@ Element.prototype.emit = function(type) {
};
*/
Element.prototype.parseContent = function() {
if (!this.content) return;
if (this.detached) return;
var w = this.width - (this.border ? 2 : 0);
if (this._clines == null
|| this._clines.width !== w
|| this._clines.content !== this.content) {
this._clines = wrapContent(this.content, w, this.align);
this._clines.width = w;
this._clines.content = this.content;
this._pcontent = this._clines.join('\n');
}
};
Element.prototype.hide = function() {
if (this.hidden) return;
this.hidden = true;
@ -800,6 +864,7 @@ Element.prototype.setContent = function(content) {
// TODO: Maybe simply set _pcontent with _parseTags result.
// text = text.replace(/\x1b(?!\[[\d;]*m)/g, '');
this.content = this._parseTags(content || '');
this.parseContent();
if (ret) {
//if (ret && !this.hidden) {
this.screen.clearRegion(ret.xi, ret.xl, ret.yi, ret.yl);
@ -833,6 +898,15 @@ Element.prototype.__defineGetter__('visible', function() {
return true;
});
Element.prototype.__defineGetter__('detached', function() {
var el = this;
do {
if (el._isScreen) return false;
if (!el.parent) return true;
} while (el = el.parent);
return false;
});
/**
* Positioning
*/
@ -1120,6 +1194,10 @@ Element.prototype.__defineSetter__('rbottom', function(val) {
return this.options.bottom = this.position.bottom = val;
});
Element.prototype.calcShrink = function(xi_, xl, yi_, yl) {
return [xi_, xl, yi_, yl];
};
/**
* Box
*/
@ -1135,6 +1213,17 @@ Box.prototype.__proto__ = Element.prototype;
// TODO: Optimize. Move elsewhere.
Box.prototype._getShrinkSize = function(content) {
if (this._clines) {
return {
height: this._clines.length,
width: this._clines.reduce(function(current, line) {
line = line.replace(/\x1b\[[\d;]*m/g, '');
return line.length > current
? line.length //+ (lines.length > 1 ? 1 : 0) // for newlines
: current;
}, 0)
};
}
var lines = content.replace(/\x1b\[[\d;]*m/g, '').split('\n');
return {
height: lines.length,
@ -1195,6 +1284,13 @@ Box.prototype.render = function(stop) {
// NOTE: Could simply change some offsets around and move this below the
// shrink block. Could also stop passing a string to _getShrinkSize.
/*
if (this.align === 'center' || this.align === 'right') {
content = '{' + this.align + '}' + content + '{/' + this.align + '}';
} else
*/
/*
if (this.align === 'center' || this.align === 'right') {
var ncontent = content.replace(/\x1b\[[\d;]*m/g, '')
, width = this.width - (this.border ? 2 : 0) - this.padding * 2;
@ -1216,7 +1312,58 @@ Box.prototype.render = function(stop) {
}
}
}
*/
//if (!this._pcontent && this.content) {
// this._clines = wrapContent(this.content, this.width - (this.border ? 2 : 0));
// this._pcontent = this._clines.join('\n');
// content = this._pcontent;
// //content = wrapContent(this.content, this.width - (this.border ? 2 : 0)).join('\n');
//}
/*
if (this.tags || this.align) {
var self = this;
content = content.replace(/(^|\n){(center|right)}([^\0]+?){\/\2}(?=\n|$)/g, function(_, start, align, text) {
var lines = text.trim().split('\n')
, line
, i = 0
, width = self.width - (self.border ? 2 : 0) - self.padding * 2
, out = ''
, len
, s;
for (; i < lines.length; i++) {
line = lines[i];
len = line.replace(/\x1b\[[\d;]*m/g, '').length;
s = width - len;
if (s < 0) {
out += '\n' + line;
continue;
}
if (align === 'center') {
s = Array(((s / 2 | 0) - (self.border ? 1 : 0)) + 1).join(' ');
out += '\n' + s + line + (self.shrink ? s : '');
} else {
s -= self.left; // this shouldn't be necessary
s = Array((s - (self.border ? 2 : 0)) + 1).join(' ');
out += '\n' + s + line;
}
}
if (out[0] === '\n' && !start) {
out = out.substring(1);
}
return out;
});
}
*/
// TODO: Check for 'center', recalculate yi, and xi. Better yet, simply
// move this check into this.left/width/etc.
if (this.shrink) {
var hw = this._getShrinkSize(content)
, h = hw.height
@ -1235,6 +1382,10 @@ Box.prototype.render = function(stop) {
}
}
//if (this.options.vshrink && this._clines) {
// yl = yi_ + this._clines.length + (this.border ? 2 : 0) + this.padding;
//}
var ret = this._lastPos = {
xi: xi_,
xl: xl,
@ -1785,6 +1936,7 @@ function ScrollableText(options) {
});
}
/*
this.screen.on('resize', function() {
self._recalculateIndex();
});
@ -1806,6 +1958,7 @@ function ScrollableText(options) {
self._recalculateIndex();
});
}
*/
}
ScrollableText.prototype.__proto__ = ScrollableBox.prototype;
@ -1827,11 +1980,13 @@ ScrollableText.prototype.scroll = function(offset) {
// feeds. This allows us to take preformatted text output from other programs
// and put it in a scrollable text box.
if (this.content != null) {
w = this.width - (this.border ? 2 : 0);
if (this._clines == null || this._clines.width !== w) {
this._clines = wrapContent(this.content, w);
this._pcontent = this._clines.join('\n');
}
this._parseContent();
//w = this.width - (this.border ? 2 : 0);
//if (this._clines == null || this._clines.width !== w) {
// this._clines = wrapContent(this.content, w);
// this._clines.content = this.content;
// this._pcontent = this._clines.join('\n');
//}
max = this._clines.length - 1 - (this.height - (this.border ? 2 : 0));
if (max < 0) max = 0;
@ -1854,14 +2009,14 @@ ScrollableText.prototype.scroll = function(offset) {
ScrollableText.prototype._setContent = ScrollableText.prototype.setContent;
ScrollableText.prototype.setContent = function(content) {
var ret = this._setContent(content);
this._recalculateIndex();
//this._recalculateIndex();
return ret;
};
ScrollableText.prototype._recalculateIndex = function() {
if (!this.parent) return;
this._clines = wrapContent(this.content, this.width - (this.border ? 2 : 0));
this._pcontent = this._clines.join('\n');
if (this.detached) return;
//this._clines = wrapContent(this.content, this.width - (this.border ? 2 : 0));
//this._pcontent = this._clines.join('\n');
var max = this._clines.length - 1 - (this.height - (this.border ? 2 : 0));
if (max < 0) max = 0;
@ -1876,6 +2031,14 @@ ScrollableText.prototype._recalculateIndex = function() {
this.contentIndex = t;
};
ScrollableText.prototype._parseContent = ScrollableText.prototype.parseContent;
ScrollableText.prototype.parseContent = function(content) {
var ret = this._parseContent();
this._recalculateIndex();
return ret;
};
/**
* Input
*/
@ -2264,11 +2427,44 @@ function readEditor(callback) {
});
}
function wrapContent(content, width) {
function sp(line, width, align) {
if (!align) return line;
var len = line.replace(/\x1b\[[\d;]*m/g, '').length
, s = width - len;
if (len === 0) return line;
if (s < 0) return line;
if (align === 'center') {
s = Array(((s / 2) | 0) + 1).join(' ');
return s + line + s;
} else if (align === 'right') {
s = Array(s + 1).join(' ');
return s + line;
}
return line;
}
function wrapContent(content, width, state) {
var lines = content.split('\n')
, out = [];
lines.forEach(function(line) {
var align = state
, cap;
if (cap = /^{(center|right)}/.exec(line)) {
line = line.substring(cap[0].length);
align = state = cap[1];
}
if (cap = /{\/(center|right)}$/.exec(line)) {
line = line.slice(0, -cap[0].length);
state = null;
}
var total
, i
, part
@ -2283,7 +2479,17 @@ function wrapContent(content, width) {
while (line[i] && line[i++] !== 'm');
}
if (!line[i]) break;
if (++total === width) break;
if (++total === width) {
// Try to find a space to break on:
if (line[i] !== ' ') {
var j = i;
while (j > i - 10 && j > 0 && line[j] !== ' ') j--;
if (line[j] === ' ') i = j + 1;
} else {
i++;
}
break;
}
}
part = line.substring(0, i - 1);
@ -2292,10 +2498,12 @@ function wrapContent(content, width) {
if (esc) {
part = part.slice(0, -esc[0].length);
line = line.substring(i - 1 - esc[0].length);
out.push(part);
//out.push(part);
out.push(sp(part, width, align));
} else {
line = line.substring(i - 1);
out.push(part);
//out.push(part);
out.push(sp(part, width, align));
}
}
@ -2304,7 +2512,8 @@ function wrapContent(content, width) {
out[out.length-1] += line;
return;
}
out.push(line);
//out.push(line);
out.push(sp(line, width, align));
});
out.width = width;