* More View updates
This commit is contained in:
parent
c7b4714c3d
commit
eab6da2b0a
|
@ -4,6 +4,7 @@
|
||||||
var events = require('events');
|
var events = require('events');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var miscUtil = require('./misc_util.js');
|
var miscUtil = require('./misc_util.js');
|
||||||
|
var ansi = require('./ansi_term.js');
|
||||||
|
|
||||||
exports.ANSIEscapeParser = ANSIEscapeParser;
|
exports.ANSIEscapeParser = ANSIEscapeParser;
|
||||||
|
|
||||||
|
@ -18,8 +19,6 @@ function ANSIEscapeParser(options) {
|
||||||
this.flags = 0x00;
|
this.flags = 0x00;
|
||||||
this.scrollBack = 0;
|
this.scrollBack = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
options = miscUtil.valueWithDefault(options, {
|
options = miscUtil.valueWithDefault(options, {
|
||||||
mciReplaceChar : '',
|
mciReplaceChar : '',
|
||||||
termHeight : 25,
|
termHeight : 25,
|
||||||
|
@ -30,6 +29,12 @@ function ANSIEscapeParser(options) {
|
||||||
this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25);
|
this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25);
|
||||||
this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80);
|
this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80);
|
||||||
|
|
||||||
|
function saveLastColor() {
|
||||||
|
self.lastFlags = self.flags;
|
||||||
|
self.lastFgCololr = self.fgColor;
|
||||||
|
self.lastBgColor = self.bgColor;
|
||||||
|
}
|
||||||
|
|
||||||
function getArgArray(array) {
|
function getArgArray(array) {
|
||||||
var i = array.length;
|
var i = array.length;
|
||||||
while(i--) {
|
while(i--) {
|
||||||
|
@ -125,7 +130,8 @@ function ANSIEscapeParser(options) {
|
||||||
|
|
||||||
function getProcessedMCI(mci) {
|
function getProcessedMCI(mci) {
|
||||||
if(self.mciReplaceChar.length > 0) {
|
if(self.mciReplaceChar.length > 0) {
|
||||||
return new Array(mci.length + 1).join(self.mciReplaceChar);
|
var eraseColor = ansi.sgr(self.lastFlags, self.lastFgColor, self.lastBgColor);
|
||||||
|
return eraseColor + new Array(mci.length + 1).join(self.mciReplaceChar);
|
||||||
} else {
|
} else {
|
||||||
return mci;
|
return mci;
|
||||||
}
|
}
|
||||||
|
@ -254,6 +260,8 @@ function ANSIEscapeParser(options) {
|
||||||
|
|
||||||
// set graphic rendition
|
// set graphic rendition
|
||||||
case 'm' :
|
case 'm' :
|
||||||
|
saveLastColor();
|
||||||
|
|
||||||
for(i = 0, len = args.length; i < len; ++i) {
|
for(i = 0, len = args.length; i < len; ++i) {
|
||||||
arg = args[i];
|
arg = args[i];
|
||||||
if(0x00 === arg) {
|
if(0x00 === arg) {
|
||||||
|
@ -280,6 +288,9 @@ function ANSIEscapeParser(options) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.resetColor();
|
||||||
|
saveLastColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(ANSIEscapeParser, events.EventEmitter);
|
util.inherits(ANSIEscapeParser, events.EventEmitter);
|
|
@ -16,7 +16,7 @@ var miscUtil = require('./misc_util.js');
|
||||||
|
|
||||||
exports.sgr = sgr;
|
exports.sgr = sgr;
|
||||||
exports.clearScreen = clearScreen;
|
exports.clearScreen = clearScreen;
|
||||||
exports.clearScreenGoHome = clearScreenGoHome;
|
exports.resetScreen = resetScreen;
|
||||||
exports.normal = normal;
|
exports.normal = normal;
|
||||||
exports.goHome = goHome;
|
exports.goHome = goHome;
|
||||||
exports.disableVT100LineWrapping = disableVT100LineWrapping;
|
exports.disableVT100LineWrapping = disableVT100LineWrapping;
|
||||||
|
@ -40,13 +40,18 @@ var CONTROL = {
|
||||||
prevLine : 'F',
|
prevLine : 'F',
|
||||||
horizAbsolute : 'G',
|
horizAbsolute : 'G',
|
||||||
eraseData : 'J',
|
eraseData : 'J',
|
||||||
|
eraseLine : 'K',
|
||||||
|
insertLine : 'L',
|
||||||
|
deleteLine : 'M',
|
||||||
scrollUp : 'S',
|
scrollUp : 'S',
|
||||||
scrollDown : 'T',
|
scrollDown : 'T',
|
||||||
savePos : 's',
|
savePos : 's',
|
||||||
restorePos : 'u',
|
restorePos : 'u',
|
||||||
queryPos : '6n',
|
queryPos : '6n',
|
||||||
goto : 'H', // row Pr, column Pc -- same as f
|
goto : 'H', // row Pr, column Pc -- same as f
|
||||||
gotoAlt : 'f' // same as H
|
gotoAlt : 'f', // same as H
|
||||||
|
|
||||||
|
emulationSpeed : '*r' // Set output emulation speed. See cterm.txt
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -255,8 +260,8 @@ function clearScreen() {
|
||||||
return exports.eraseData(2);
|
return exports.eraseData(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearScreenGoHome() {
|
function resetScreen() {
|
||||||
return exports.goto(1,1) + exports.eraseData(2);
|
return exports.goHome() + exports.eraseData(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function normal() {
|
function normal() {
|
||||||
|
|
|
@ -418,7 +418,7 @@ function display(art, options, cb) {
|
||||||
|
|
||||||
parser.on('mci', function onMCI(mciCode, args) {
|
parser.on('mci', function onMCI(mciCode, args) {
|
||||||
if(mci[mciCode]) {
|
if(mci[mciCode]) {
|
||||||
mci[mciCode].altColor = {
|
mci[mciCode].focusColor = {
|
||||||
fg : parser.fgColor,
|
fg : parser.fgColor,
|
||||||
bg : parser.bgColor,
|
bg : parser.bgColor,
|
||||||
flags : parser.flags,
|
flags : parser.flags,
|
||||||
|
@ -430,7 +430,9 @@ function display(art, options, cb) {
|
||||||
fg : parser.fgColor,
|
fg : parser.fgColor,
|
||||||
bg : parser.bgColor,
|
bg : parser.bgColor,
|
||||||
flags : parser.flags,
|
flags : parser.flags,
|
||||||
}
|
},
|
||||||
|
code : mciCode.substr(0, 2),
|
||||||
|
id : mciCode.substr(2, 1), // :TODO: This NEEDs to read 01-99
|
||||||
};
|
};
|
||||||
|
|
||||||
mciPosQueue.push(mciCode);
|
mciPosQueue.push(mciCode);
|
||||||
|
|
126
core/client.js
126
core/client.js
|
@ -10,6 +10,57 @@ var logger = require('./logger.js');
|
||||||
|
|
||||||
exports.Client = Client;
|
exports.Client = Client;
|
||||||
|
|
||||||
|
//var ANSI_CONTROL_REGEX = /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/g;
|
||||||
|
|
||||||
|
// :TODO: Move all of the key stuff to it's own module
|
||||||
|
var ANSI_KEY_NAME_MAP = {
|
||||||
|
0x08 : 'backspace',
|
||||||
|
0x09 : 'tab',
|
||||||
|
0x7f : 'del',
|
||||||
|
0x1b : 'esc',
|
||||||
|
0x0d : 'enter',
|
||||||
|
};
|
||||||
|
|
||||||
|
var ANSI_KEY_CSI_NAME_MAP = {
|
||||||
|
0x40 : 'insert', // @
|
||||||
|
0x41 : 'up arrow', // A
|
||||||
|
0x42 : 'down arrow', // B
|
||||||
|
0x43 : 'right arrow', // C
|
||||||
|
0x44 : 'left arrow', // D
|
||||||
|
|
||||||
|
0x48 : 'home', // H
|
||||||
|
0x4b : 'end', // K
|
||||||
|
|
||||||
|
0x56 : 'page up', // V
|
||||||
|
0x55 : 'page down', // U
|
||||||
|
};
|
||||||
|
|
||||||
|
var ANSI_F_KEY_NAME_MAP_1 = {
|
||||||
|
0x50 : 'F1',
|
||||||
|
0x51 : 'F2',
|
||||||
|
0x52 : 'F3',
|
||||||
|
0x53 : 'F4',
|
||||||
|
0x74 : 'F5',
|
||||||
|
};
|
||||||
|
|
||||||
|
var ANSI_F_KEY_NAME_MAP_2 = {
|
||||||
|
// rxvt
|
||||||
|
11 : 'F1',
|
||||||
|
12 : 'F2',
|
||||||
|
13 : 'F3',
|
||||||
|
14 : 'F4',
|
||||||
|
15 : 'F5',
|
||||||
|
|
||||||
|
// SyncTERM
|
||||||
|
17 : 'F6',
|
||||||
|
18 : 'F7',
|
||||||
|
19 : 'F8',
|
||||||
|
20 : 'F9',
|
||||||
|
21 : 'F10',
|
||||||
|
23 : 'F11',
|
||||||
|
24 : 'F12',
|
||||||
|
};
|
||||||
|
|
||||||
function Client(input, output) {
|
function Client(input, output) {
|
||||||
stream.call(this);
|
stream.call(this);
|
||||||
|
|
||||||
|
@ -19,19 +70,80 @@ function Client(input, output) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
this.term = new term.ClientTerminal(this.output);
|
this.term = new term.ClientTerminal(this.output);
|
||||||
|
|
||||||
self.on('data', function onData(data) {
|
self.on('data', function onData1(data) {
|
||||||
console.log('data: ' + data.length);
|
console.log(data);
|
||||||
|
|
||||||
|
onData(data);
|
||||||
handleANSIControlResponse(data);
|
handleANSIControlResponse(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleANSIControlResponse(data) {
|
function handleANSIControlResponse(data) {
|
||||||
console.log(data);
|
//console.log(data);
|
||||||
ansi.forEachControlCode(data, function onControlResponse(name, params) {
|
ansi.forEachControlCode(data, function onControlResponse(name, params) {
|
||||||
var eventName = 'on' + name[0].toUpperCase() + name.substr(1);
|
var eventName = 'on' + name[0].toUpperCase() + name.substr(1);
|
||||||
console.log(eventName + ': ' + params);
|
console.log(eventName + ': ' + params);
|
||||||
self.emit(eventName, params);
|
self.emit(eventName, params);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Peek at |data| and emit for any specialized handling
|
||||||
|
// such as ANSI control codes or user/keyboard input
|
||||||
|
//
|
||||||
|
function onData(data) {
|
||||||
|
var len = data.length;
|
||||||
|
var c;
|
||||||
|
var name;
|
||||||
|
|
||||||
|
if(1 === len) {
|
||||||
|
c = data[0];
|
||||||
|
|
||||||
|
if(0x00 === c) {
|
||||||
|
// ignore single NUL
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = ANSI_KEY_NAME_MAP[c];
|
||||||
|
if(name) {
|
||||||
|
self.emit('special key', name);
|
||||||
|
self.emit('key press', data, true);
|
||||||
|
} else {
|
||||||
|
self.emit('key press', data, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0x1b !== data[0]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(3 === len) {
|
||||||
|
if(0x5b === data[1]) {
|
||||||
|
name = ANSI_KEY_CSI_NAME_MAP[data[2]];
|
||||||
|
if(name) {
|
||||||
|
self.emit('special key', name);
|
||||||
|
self.emit('key press', data, true);
|
||||||
|
}
|
||||||
|
} else if(0x4f === data[1]) {
|
||||||
|
name = ANSI_F_KEY_NAME_MAP_1[data[2]];
|
||||||
|
if(name) {
|
||||||
|
self.emit('special key', name);
|
||||||
|
self.emit('key press', data, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(5 === len && 0x5b === data[1] && 0x7e === data[4]) {
|
||||||
|
var code = parseInt(data.slice(2,4), 10);
|
||||||
|
|
||||||
|
if(!isNaN(code)) {
|
||||||
|
name = ANSI_F_KEY_NAME_MAP_2[code];
|
||||||
|
if(name) {
|
||||||
|
self.emit('special key', name);
|
||||||
|
self.emit('key press', data, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(len > 4 && len < 11 && 0x52 === data[len]) {
|
||||||
|
// :TODO: Look for various DSR responses such as cursor position
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
require('util').inherits(Client, stream);
|
require('util').inherits(Client, stream);
|
||||||
|
@ -48,11 +160,9 @@ Client.prototype.destroySoon = function () {
|
||||||
return this.output.destroySoon.apply(this.output, arguments);
|
return this.output.destroySoon.apply(this.output, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
Client.prototype.getch = function(cb) {
|
Client.prototype.waitForKeyPress = function(cb) {
|
||||||
this.input.once('data', function onData(data) {
|
this.once('key press', function onKeyPress(kp) {
|
||||||
// :TODO: needs work. What about F keys and the like?
|
cb(kp);
|
||||||
assert(data.length === 1);
|
|
||||||
cb(data);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var miscUtil = require('./misc_util.js');
|
||||||
|
|
||||||
|
|
||||||
exports.stylizeString = stylizeString;
|
exports.stylizeString = stylizeString;
|
||||||
|
exports.pad = pad;
|
||||||
|
|
||||||
// :TODO: create Unicode verison of this
|
// :TODO: create Unicode verison of this
|
||||||
var VOWELS = [ 'a', 'e', 'i', 'o', 'u' ];
|
var VOWELS = [ 'a', 'e', 'i', 'o', 'u' ];
|
||||||
|
@ -27,24 +31,29 @@ function stylizeString(s, style) {
|
||||||
|
|
||||||
switch(style) {
|
switch(style) {
|
||||||
// UPPERCASE
|
// UPPERCASE
|
||||||
|
case 'upper' :
|
||||||
case 'U' : return s.toUpperCase();
|
case 'U' : return s.toUpperCase();
|
||||||
|
|
||||||
// lowercase
|
// lowercase
|
||||||
|
case 'lower' :
|
||||||
case 'l' : return s.toLowerCase();
|
case 'l' : return s.toLowerCase();
|
||||||
|
|
||||||
// Proper Case
|
// Proper Case
|
||||||
|
case 'proper' :
|
||||||
case 'P' :
|
case 'P' :
|
||||||
return s.replace(/\w\S*/g, function onProperCaseChar(t) {
|
return s.replace(/\w\S*/g, function onProperCaseChar(t) {
|
||||||
return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase();
|
return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase();
|
||||||
});
|
});
|
||||||
|
|
||||||
// fIRST lOWER
|
// fIRST lOWER
|
||||||
|
case 'first lower' :
|
||||||
case 'f' :
|
case 'f' :
|
||||||
return s.replace(/\w\S*/g, function onFirstLowerChar(t) {
|
return s.replace(/\w\S*/g, function onFirstLowerChar(t) {
|
||||||
return t.charAt(0).toLowerCase() + t.substr(1).toUpperCase();
|
return t.charAt(0).toLowerCase() + t.substr(1).toUpperCase();
|
||||||
});
|
});
|
||||||
|
|
||||||
// SMaLL VoWeLS
|
// SMaLL VoWeLS
|
||||||
|
case 'small vowels' :
|
||||||
case 'v' :
|
case 'v' :
|
||||||
for(i = 0; i < len; ++i) {
|
for(i = 0; i < len; ++i) {
|
||||||
c = s[i];
|
c = s[i];
|
||||||
|
@ -57,6 +66,7 @@ function stylizeString(s, style) {
|
||||||
return stylized;
|
return stylized;
|
||||||
|
|
||||||
// bIg vOwELS
|
// bIg vOwELS
|
||||||
|
case 'big vowels' :
|
||||||
case 'V' :
|
case 'V' :
|
||||||
for(i = 0; i < len; ++i) {
|
for(i = 0; i < len; ++i) {
|
||||||
c = s[i];
|
c = s[i];
|
||||||
|
@ -69,9 +79,11 @@ function stylizeString(s, style) {
|
||||||
return stylized;
|
return stylized;
|
||||||
|
|
||||||
// Small i's: DEMENTiA
|
// Small i's: DEMENTiA
|
||||||
|
case 'small i' :
|
||||||
case 'i' : return s.toUpperCase().replace('I', 'i');
|
case 'i' : return s.toUpperCase().replace('I', 'i');
|
||||||
|
|
||||||
// mIxeD CaSE (random upper/lower)
|
// mIxeD CaSE (random upper/lower)
|
||||||
|
case 'mixed' :
|
||||||
case 'M' :
|
case 'M' :
|
||||||
for(i = 0; i < len; i++) {
|
for(i = 0; i < len; i++) {
|
||||||
if(Math.random() < 0.5) {
|
if(Math.random() < 0.5) {
|
||||||
|
@ -83,6 +95,7 @@ function stylizeString(s, style) {
|
||||||
return stylized;
|
return stylized;
|
||||||
|
|
||||||
// l337 5p34k
|
// l337 5p34k
|
||||||
|
case 'l33t' :
|
||||||
case '3' :
|
case '3' :
|
||||||
for(i = 0; i < len; ++i) {
|
for(i = 0; i < len; ++i) {
|
||||||
c = SIMPLE_ELITE_MAP[s[i].toLowerCase()];
|
c = SIMPLE_ELITE_MAP[s[i].toLowerCase()];
|
||||||
|
@ -93,3 +106,32 @@ function stylizeString(s, style) {
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Based on http://www.webtoolkit.info/
|
||||||
|
function pad(s, len, padChar, dir) {
|
||||||
|
len = miscUtil.valueWithDefault(len, 0);
|
||||||
|
padChar = miscUtil.valueWithDefault(padChar, ' ');
|
||||||
|
dir = miscUtil.valueWithDefault(dir, 'right');
|
||||||
|
|
||||||
|
var padlen = len - s.length;
|
||||||
|
|
||||||
|
switch(dir) {
|
||||||
|
case 'left' :
|
||||||
|
s = new Array(padlen).join(padChar) + s;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'both' :
|
||||||
|
var right = Math.ceil(padlen) / 2;
|
||||||
|
var left = padlen - right;
|
||||||
|
s = new Array(left + 1).join(padChar) + s + new Array(right + 1).join(padChar);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'right' :
|
||||||
|
s = s + new Array(padlen).join(padChar);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default : break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
392
core/view.js
392
core/view.js
|
@ -3,42 +3,241 @@
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var ansi = require('./ansi_term.js');
|
var ansi = require('./ansi_term.js');
|
||||||
|
var miscUtil = require('./misc_util.js');
|
||||||
|
var strUtil = require('./string_util.js');
|
||||||
|
var assert = require('assert');
|
||||||
|
var events = require('events');
|
||||||
|
var logger = require('./logger.js');
|
||||||
|
|
||||||
exports.View = View;
|
exports.View = View;
|
||||||
exports.LabelView = LabelView;
|
exports.LabelView = LabelView;
|
||||||
|
exports.TextEditView = TextEditView;
|
||||||
|
|
||||||
|
exports.ViewsController = ViewsController;
|
||||||
|
|
||||||
function View(client) {
|
function View(client) {
|
||||||
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
console.log('View ctor');
|
this.client = client;
|
||||||
|
this.acceptsFocus = false;
|
||||||
this.client = client;
|
this.acceptsKeys = false;
|
||||||
|
|
||||||
// this.width = width;
|
|
||||||
// this.height = height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// :TODO: allow pos[] or x, y
|
util.inherits(View, events.EventEmitter);
|
||||||
View.prototype.draw = function(x, y) {
|
|
||||||
|
View.prototype.place = function(pos) {
|
||||||
|
//
|
||||||
|
// We allow [x, y], { x : x, y : y }, or (x, y)
|
||||||
|
//
|
||||||
|
if(util.isArray(pos)) {
|
||||||
|
this.x = pos[0];
|
||||||
|
this.y = pos[1];
|
||||||
|
} else if(pos.x && pos.y) {
|
||||||
|
this.x = pos.x;
|
||||||
|
this.y = pos.y;
|
||||||
|
} else if(2 === arguments.length) {
|
||||||
|
var x = parseInt(arguments[0], 10);
|
||||||
|
var y = parseInt(arguments[1], 10);
|
||||||
|
if(!isNaN(x) && !isNaN(y)) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(this.x > 0 && this.x < this.client.term.termHeight);
|
||||||
|
assert(this.y > 0 && this.y < this.client.term.termWidth);
|
||||||
|
|
||||||
|
this.client.term.write(ansi.goto(this.x, this.y));
|
||||||
};
|
};
|
||||||
|
|
||||||
function LabelView(client, text, width) {
|
View.prototype.setFocus = function(focused) {
|
||||||
|
assert(this.x);
|
||||||
|
assert(this.y);
|
||||||
|
};
|
||||||
|
|
||||||
|
View.prototype.getColor = function() {
|
||||||
|
return this.options.color;
|
||||||
|
};
|
||||||
|
|
||||||
|
View.prototype.getFocusColor = function() {
|
||||||
|
return this.options.focusColor || this.getColor();
|
||||||
|
};
|
||||||
|
|
||||||
|
function LabelView(client, text, options) {
|
||||||
View.call(this, client);
|
View.call(this, client);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.text = text;
|
if(options) {
|
||||||
this.width = width || text.length;
|
if(options.maxWidth) {
|
||||||
|
text = text.substr(0, options.maxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
text = strUtil.stylizeString(text, options.style);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = text;
|
||||||
|
this.height = 1;
|
||||||
|
this.width = this.value.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(LabelView, View);
|
util.inherits(LabelView, View);
|
||||||
|
|
||||||
LabelView.prototype.draw = function(x, y) {
|
LabelView.prototype.place = function(pos) {
|
||||||
LabelView.super_.prototype.draw.call(this, x, y);
|
LabelView.super_.prototype.place.apply(this, arguments);
|
||||||
|
|
||||||
this.client.term.write(ansi.goto(x, y));
|
this.client.term.write(this.value);
|
||||||
this.client.term.write(this.text);
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var INTERACTIVE_VIEW_DEFAULT_SPECIAL_KEYSET = {
|
||||||
|
enter : [ 'enter' ],
|
||||||
|
exit : [ 'esc' ],
|
||||||
|
backspace : [ 'backspace', 'del' ],
|
||||||
|
next : [ 'tab' ],
|
||||||
|
};
|
||||||
|
|
||||||
|
function InteractiveView(client, options) {
|
||||||
|
View.call(this, client);
|
||||||
|
|
||||||
|
this.acceptsFocus = true;
|
||||||
|
this.acceptsKeys = true;
|
||||||
|
|
||||||
|
if(options) {
|
||||||
|
this.options = options;
|
||||||
|
} else {
|
||||||
|
this.options = {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.specialKeySet = miscUtil.valueWithDefault(
|
||||||
|
options.specialKeySet, INTERACTIVE_VIEW_DEFAULT_SPECIAL_KEYSET
|
||||||
|
);
|
||||||
|
|
||||||
|
this.isSpecialKeyFor = function(checkFor, specialKey) {
|
||||||
|
return this.options.specialKeySet[checkFor].indexOf(specialKey) > -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.backspace = function() {
|
||||||
|
this.client.term.write('\b \b');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(InteractiveView, View);
|
||||||
|
|
||||||
|
InteractiveView.prototype.setFocus = function(focused) {
|
||||||
|
InteractiveView.super_.prototype.setFocus.call(this, focused);
|
||||||
|
|
||||||
|
this.hasFocus = focused;
|
||||||
|
};
|
||||||
|
|
||||||
|
InteractiveView.prototype.setNextView = function(id) {
|
||||||
|
this.nextId = id;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var TEXT_EDIT_INPUT_TYPES = [
|
||||||
|
'none', // :TODO: TextEditView -> TextView (optional focus/editable)
|
||||||
|
'text',
|
||||||
|
'password',
|
||||||
|
'upper',
|
||||||
|
'lower',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
function TextEditView(client, options) {
|
||||||
|
InteractiveView.call(this, client, options);
|
||||||
|
|
||||||
|
if(!options) {
|
||||||
|
this.options.multiLine = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.inputType = miscUtil.valueWithDefault(this.options.inputType, 'text');
|
||||||
|
assert(TEXT_EDIT_INPUT_TYPES.indexOf(this.options.inputType) > -1);
|
||||||
|
|
||||||
|
if('password' === this.options.inputType) {
|
||||||
|
this.options.inputMaskChar = miscUtil.valueWithDefault(this.options.inputMaskChar, '*').substr(0,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = miscUtil.valueWithDefault(options.defaultValue, '');
|
||||||
|
// :TODO: hilight, text, etc., should come from options or default for theme if not provided
|
||||||
|
|
||||||
|
// focus=fg + bg
|
||||||
|
// standard=fg +bg
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(TextEditView, InteractiveView);
|
||||||
|
|
||||||
|
|
||||||
|
TextEditView.prototype.place = function(pos) {
|
||||||
|
TextEditView.super_.prototype.place.apply(this, arguments);
|
||||||
|
|
||||||
|
if(!this.options.maxWidth) {
|
||||||
|
this.options.maxWidth = this.client.term.termWidth - this.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.width = this.options.maxWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
TextEditView.prototype.setFocus = function(focused) {
|
||||||
|
TextEditView.super_.prototype.setFocus.call(this, focused);
|
||||||
|
|
||||||
|
this.client.term.write(ansi.goto(this.x, this.y));
|
||||||
|
this.redraw();
|
||||||
|
this.client.term.write(ansi.goto(this.x, this.y + this.value.length));
|
||||||
|
};
|
||||||
|
|
||||||
|
TextEditView.prototype.redraw = function() {
|
||||||
|
var color = this.hasFocus ? this.getFocusColor() : this.getColor();
|
||||||
|
|
||||||
|
this.client.term.write(ansi.sgr(color.flags, color.fg, color.bg));
|
||||||
|
this.client.term.write(strUtil.pad(this.value, this.width));
|
||||||
|
};
|
||||||
|
|
||||||
|
TextEditView.prototype.onKeyPressed = function(k, isSpecial) {
|
||||||
|
assert(this.hasFocus);
|
||||||
|
|
||||||
|
if(isSpecial) {
|
||||||
|
return; // handled via onSpecialKeyPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.value.length < this.options.maxWidth) {
|
||||||
|
|
||||||
|
k = strUtil.stylizeString(k, this.options.inputType);
|
||||||
|
|
||||||
|
this.value += k;
|
||||||
|
|
||||||
|
if('password' === this.options.inputType) {
|
||||||
|
this.client.term.write(this.options.inputMaskChar);
|
||||||
|
} else {
|
||||||
|
this.client.term.write(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TextEditView.prototype.onSpecialKeyPressed = function(keyName) {
|
||||||
|
assert(this.hasFocus);
|
||||||
|
|
||||||
|
console.log(keyName);
|
||||||
|
|
||||||
|
if(this.isSpecialKeyFor('backspace', keyName)) {
|
||||||
|
if(this.value.length > 0) {
|
||||||
|
this.value = this.value.substr(0, this.value.length - 1);
|
||||||
|
this.backspace();
|
||||||
|
}
|
||||||
|
} else if(this.isSpecialKeyFor('enter', keyName)) {
|
||||||
|
if(this.options.multiLine) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.emit('action', 'accepted');
|
||||||
|
}
|
||||||
|
} else if(this.isSpecialKeyFor('next', keyName)) {
|
||||||
|
this.emit('action', 'next');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,3 +248,166 @@ function MenuView(options) {
|
||||||
function VerticalMenuView(options) {
|
function VerticalMenuView(options) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
// :TODO: Move to view_controller.js
|
||||||
|
function ViewsController(client) {
|
||||||
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.views = {};
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
client.on('key press', function onKeyPress(k, isSpecial) {
|
||||||
|
if(self.focusedView && self.focusedView.acceptsKeys) {
|
||||||
|
self.focusedView.onKeyPressed(k, isSpecial);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('special key', function onSpecialKey(keyName) {
|
||||||
|
if(self.focusedView && self.focusedView.acceptsKeys) {
|
||||||
|
self.focusedView.onSpecialKeyPressed(keyName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.onViewAction = function(action) {
|
||||||
|
console.log(action + '@ ' + this.id);
|
||||||
|
|
||||||
|
self.emit('action', { id : this.id, action : action });
|
||||||
|
|
||||||
|
if('accepted' === action) {
|
||||||
|
self.nextFocus();
|
||||||
|
} else if('next' === action) {
|
||||||
|
self.nextFocus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(ViewsController, events.EventEmitter);
|
||||||
|
|
||||||
|
ViewsController.prototype.addView = function(viewInfo) {
|
||||||
|
viewInfo.view.id = viewInfo.id;
|
||||||
|
|
||||||
|
this.views[viewInfo.id] = {
|
||||||
|
view : viewInfo.view,
|
||||||
|
pos : viewInfo.pos
|
||||||
|
};
|
||||||
|
|
||||||
|
viewInfo.view.place(viewInfo.pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
ViewsController.prototype.viewExists = function(id) {
|
||||||
|
return id in this.views;
|
||||||
|
};
|
||||||
|
|
||||||
|
ViewsController.prototype.getView = function(id) {
|
||||||
|
return this.views[id].view;
|
||||||
|
};
|
||||||
|
|
||||||
|
ViewsController.prototype.switchFocus = function(id) {
|
||||||
|
var view = this.getView(id);
|
||||||
|
|
||||||
|
if(!view) {
|
||||||
|
logger.log.warn('Invalid view', { id : id });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!view.acceptsFocus) {
|
||||||
|
logger.log.warn('View does not accept focus', { id : id });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.focusedView = view;
|
||||||
|
view.setFocus(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
ViewsController.prototype.nextFocus = function() {
|
||||||
|
var nextId = this.focusedView.nextId;
|
||||||
|
|
||||||
|
this.focusedView.setFocus(false);
|
||||||
|
|
||||||
|
if(nextId > 0) {
|
||||||
|
this.switchFocus(nextId);
|
||||||
|
} else {
|
||||||
|
this.switchFocus(this.firstId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ViewsController.prototype.loadFromMCIMap = function(mciMap) {
|
||||||
|
var factory = new MCIViewFactory(this.client);
|
||||||
|
var view;
|
||||||
|
var mci;
|
||||||
|
|
||||||
|
for(var entry in mciMap) {
|
||||||
|
mci = mciMap[entry];
|
||||||
|
view = factory.createFromMCI(mci);
|
||||||
|
|
||||||
|
if(view) {
|
||||||
|
this.addView({
|
||||||
|
id : mci.id,
|
||||||
|
view : view,
|
||||||
|
pos : mci.position
|
||||||
|
});
|
||||||
|
|
||||||
|
view.on('action', this.onViewAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ViewsController.prototype.setViewOrder = function(order) {
|
||||||
|
var idOrder = [];
|
||||||
|
|
||||||
|
if(order) {
|
||||||
|
// :TODO:
|
||||||
|
} else {
|
||||||
|
for(var id in this.views) {
|
||||||
|
idOrder.push(id);
|
||||||
|
}
|
||||||
|
// :TODO: simply sort
|
||||||
|
console.log(idOrder);
|
||||||
|
this.firstId = idOrder[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var view;
|
||||||
|
for(var i = 0; i < idOrder.length - 1; ++i) {
|
||||||
|
view = this.getView(idOrder[i]);
|
||||||
|
if(view) {
|
||||||
|
view.setNextView(idOrder[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function MCIViewFactory(client, mci) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
MCIViewFactory.prototype.createFromMCI = function(mci) {
|
||||||
|
assert(mci.code);
|
||||||
|
assert(mci.id > 0);
|
||||||
|
|
||||||
|
var view;
|
||||||
|
var options = {};
|
||||||
|
|
||||||
|
switch(mci.code) {
|
||||||
|
case 'EV' :
|
||||||
|
if(mci.args.length > 0) {
|
||||||
|
options.maxWidth = mci.args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mci.args.length > 1) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
options.color = mci.color;
|
||||||
|
options.focusColor = mci.focusColor;
|
||||||
|
|
||||||
|
view = new TextEditView(this.client, options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return view;
|
||||||
|
};
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,6 +6,8 @@ var ansi = require('../core/ansi_term.js');
|
||||||
var lineEditor = require('../core/line_editor.js');
|
var lineEditor = require('../core/line_editor.js');
|
||||||
var art = require('../core/art.js');
|
var art = require('../core/art.js');
|
||||||
|
|
||||||
|
var view = require('../core/view.js');
|
||||||
|
|
||||||
exports.moduleInfo = {
|
exports.moduleInfo = {
|
||||||
name : 'Matrix',
|
name : 'Matrix',
|
||||||
desc : 'Standardish Matrix',
|
desc : 'Standardish Matrix',
|
||||||
|
@ -17,19 +19,39 @@ exports.entryPoint = entryPoint;
|
||||||
function entryPoint(client) {
|
function entryPoint(client) {
|
||||||
var term = client.term;
|
var term = client.term;
|
||||||
|
|
||||||
term.write(ansi.clearScreenGoHome());
|
term.write(ansi.resetScreen());
|
||||||
|
|
||||||
|
//-------------
|
||||||
|
/*
|
||||||
|
client.on('position', function onPos(pos) {
|
||||||
|
console.log(pos);
|
||||||
|
});
|
||||||
|
|
||||||
|
term.write('Hello, world!');
|
||||||
|
term.write(ansi.queryPos());
|
||||||
|
term.write(ansi.goto(5,5));
|
||||||
|
term.write('Yehawww a bunch of text incoming.... maybe that is what breaks it... hrm... who knows.\nHave to do more testing ;(\n');
|
||||||
|
term.write(ansi.queryPos());
|
||||||
|
return;
|
||||||
|
*/
|
||||||
|
|
||||||
|
//-------------
|
||||||
|
|
||||||
// :TODO: types, random, and others? could come from conf.mods.matrix or such
|
// :TODO: types, random, and others? could come from conf.mods.matrix or such
|
||||||
|
|
||||||
//art.getArt('SO-CC1.ANS'/* 'MATRIX'*/, { types: ['.ans'], random: true}, function onArt(err, theArt) {
|
//art.getArt('SO-CC1.ANS'/* 'MATRIX'*/, { types: ['.ans'], random: true}, function onArt(err, theArt) {
|
||||||
art.getArt('DM-ENIG2-MATRIX.ANS', {}, function onArt(err, theArt) {
|
art.getArt('MCI_TEST3.ANS', /*'MATRIX_TEST1.ANS'*/ {}, function onArt(err, theArt) {
|
||||||
if(!err) {
|
if(!err) {
|
||||||
|
|
||||||
art.display(theArt.data, { client : client, mciReplaceChar : ' ' }, function onArtDisplayed(err, mci) {
|
art.display(theArt.data, { client : client, mciReplaceChar : ' ' }, function onArtDisplayed(err, mci) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(mci);
|
var vc = new view.ViewsController(client);
|
||||||
|
vc.loadFromMCIMap(mci);
|
||||||
|
vc.setViewOrder();
|
||||||
|
vc.switchFocus(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue