* Rewrite of Views. Cleaner. WIP
This commit is contained in:
parent
c6e6c1562d
commit
51d15659d5
|
@ -0,0 +1,68 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var View = require('./view.js').View;
|
||||||
|
var miscUtil = require('./misc_util.js');
|
||||||
|
var strUtil = require('./string_util.js');
|
||||||
|
var ansi = require('./ansi_term.js');
|
||||||
|
var util = require('util');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
exports.TextView = TextView;
|
||||||
|
|
||||||
|
function TextView(client, options) {
|
||||||
|
View.call(this, client, options);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if(this.options.maxLength) {
|
||||||
|
this.maxLength = this.options.maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.textStyle = this.options.textStyle || 'normal';
|
||||||
|
this.multiLine = this.options.multiLine || false;
|
||||||
|
|
||||||
|
assert(!this.multiLine); // :TODO: not yet supported
|
||||||
|
|
||||||
|
if(!this.multiLine) {
|
||||||
|
this.dimens.height = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setText(this.options.text || '');
|
||||||
|
|
||||||
|
this.isPasswordTextStyle = 'P' === self.textStyle || 'password' === self.textStyle;
|
||||||
|
|
||||||
|
if(this.isPasswordTextStyle) {
|
||||||
|
this.passwordMaskChar = miscUtil.valueWithDefault(this.options.passwordMaskChar, '*').substr(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(TextView, View);
|
||||||
|
|
||||||
|
TextView.prototype.redraw = function() {
|
||||||
|
TextView.super_.prototype.redraw.call(this);
|
||||||
|
|
||||||
|
var color = this.hasFocus ? this.getFocusColor() : this.getColor();
|
||||||
|
|
||||||
|
this.client.term.write(ansi.sgr(color.flags, color.fg, color.bg));
|
||||||
|
|
||||||
|
if(this.isPasswordTextStyle) {
|
||||||
|
this.client.term.write(strUtil.pad(new Array(this.text.length).join(this.passwordMaskChar), this.dimens.width));
|
||||||
|
} else {
|
||||||
|
this.client.term.write(strUtil.pad(this.text, this.dimens.width));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TextView.prototype.setText = function(text) {
|
||||||
|
this.text = text;
|
||||||
|
|
||||||
|
if(this.maxLength > 0) {
|
||||||
|
this.text = this.text.substr(0, this.maxLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.text = strUtil.stylizeString(this.text, this.textStyle);
|
||||||
|
|
||||||
|
if(!this.multiLine) {
|
||||||
|
this.dimens.width = this.text.length;
|
||||||
|
}
|
||||||
|
};
|
435
core/view.js
435
core/view.js
|
@ -1,420 +1,97 @@
|
||||||
/* jslint node: true */
|
/* jslint node: true */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var util = require('util');
|
|
||||||
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 events = require('events');
|
||||||
var logger = require('./logger.js');
|
var util = require('util');
|
||||||
|
var assert = require('assert');
|
||||||
|
var ansi = require('./ansi_term.js');
|
||||||
|
|
||||||
exports.View = View;
|
exports.View = View;
|
||||||
exports.LabelView = LabelView;
|
|
||||||
exports.TextEditView = TextEditView;
|
|
||||||
|
|
||||||
exports.ViewsController = ViewsController;
|
var VIEW_SPECIAL_KEY_MAP_DEFAULT = {
|
||||||
|
enter : [ 'enter' ],
|
||||||
|
exit : [ 'esc' ],
|
||||||
|
backspace : [ 'backspace' ],
|
||||||
|
del : [ 'del' ],
|
||||||
|
next : [ 'tab' ],
|
||||||
|
};
|
||||||
|
|
||||||
function View(client) {
|
function View(client, options) {
|
||||||
events.EventEmitter.call(this);
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
var self = this;
|
assert(client);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.client = client;
|
||||||
|
this.options = options || {};
|
||||||
|
|
||||||
this.client = client;
|
|
||||||
this.acceptsFocus = false;
|
this.acceptsFocus = false;
|
||||||
this.acceptsKeys = false;
|
this.acceptsInput = false;
|
||||||
|
|
||||||
|
this.position = { x : 0, y : 0 };
|
||||||
|
this.dimens = { height : 1, width : 0 };
|
||||||
|
|
||||||
|
if(this.options.position) {
|
||||||
|
this.setPosition(this.options.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.options.dimens && this.options.dimens.height) {
|
||||||
|
this.dimens.height = this.options.dimens.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.options.dimens && this.options.dimens.width) {
|
||||||
|
this.dimens.width = this.options.dimens.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.color = this.options.color || { flags : 0, fg : 7, bg : 0 };
|
||||||
|
this.focusColor = this.options.focusColor || this.color;
|
||||||
|
|
||||||
|
if(this.acceptsInput) {
|
||||||
|
this.specialKeyMap = this.options.specialKeyMap || VIEW_SPECIAL_KEY_MAP_DEFAULT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(View, events.EventEmitter);
|
util.inherits(View, events.EventEmitter);
|
||||||
|
|
||||||
View.prototype.place = function(pos) {
|
|
||||||
|
View.prototype.setPosition = function(pos) {
|
||||||
//
|
//
|
||||||
// We allow [x, y], { x : x, y : y }, or (x, y)
|
// We allow [x, y], { x : x, y : y }, or (x, y)
|
||||||
//
|
//
|
||||||
if(util.isArray(pos)) {
|
if(util.isArray(pos)) {
|
||||||
this.x = pos[0];
|
this.position.x = pos[0];
|
||||||
this.y = pos[1];
|
this.position.y = pos[1];
|
||||||
} else if(pos.x && pos.y) {
|
} else if(pos.x && pos.y) {
|
||||||
this.x = pos.x;
|
this.position.x = pos.x;
|
||||||
this.y = pos.y;
|
this.position.y = pos.y;
|
||||||
} else if(2 === arguments.length) {
|
} else if(2 === arguments.length) {
|
||||||
var x = parseInt(arguments[0], 10);
|
var x = parseInt(arguments[0], 10);
|
||||||
var y = parseInt(arguments[1], 10);
|
var y = parseInt(arguments[1], 10);
|
||||||
if(!isNaN(x) && !isNaN(y)) {
|
if(!isNaN(x) && !isNaN(y)) {
|
||||||
this.x = x;
|
this.position.x = x;
|
||||||
this.y = y;
|
this.position.y = y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(this.x > 0 && this.x < this.client.term.termHeight);
|
assert(this.position.x > 0 && this.position.x < this.client.term.termHeight);
|
||||||
assert(this.y > 0 && this.y < this.client.term.termWidth);
|
assert(this.position.y > 0 && this.position.y < this.client.term.termWidth);
|
||||||
|
|
||||||
this.client.term.write(ansi.goto(this.x, this.y));
|
|
||||||
};
|
|
||||||
|
|
||||||
View.prototype.setFocus = function(focused) {
|
|
||||||
assert(this.x);
|
|
||||||
assert(this.y);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
View.prototype.getColor = function() {
|
View.prototype.getColor = function() {
|
||||||
return this.options.color;
|
return this.color;
|
||||||
};
|
};
|
||||||
|
|
||||||
View.prototype.getFocusColor = function() {
|
View.prototype.getFocusColor = function() {
|
||||||
return this.options.focusColor || this.getColor();
|
return this.focusColor;
|
||||||
};
|
};
|
||||||
|
|
||||||
function LabelView(client, text, options) {
|
View.prototype.redraw = function() {
|
||||||
View.call(this, client);
|
this.client.term.write(ansi.goto(this.position.x, this.position.y));
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
if(options) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
LabelView.prototype.place = function(pos) {
|
|
||||||
LabelView.super_.prototype.place.apply(this, arguments);
|
|
||||||
|
|
||||||
this.client.term.write(this.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
View.prototype.setFocus = function(focused) {
|
||||||
|
assert(this.acceptsFocus, 'View does not accept focus');
|
||||||
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;
|
this.hasFocus = focused;
|
||||||
};
|
};
|
||||||
|
|
||||||
InteractiveView.prototype.setNextView = function(id) {
|
|
||||||
this.nextId = id;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var TEXT_EDIT_INPUT_TYPES = [
|
|
||||||
'normal', 'N',
|
|
||||||
'password', 'P',
|
|
||||||
'upper', 'U',
|
|
||||||
'lower', 'l',
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
function TextEditView(client, options) {
|
|
||||||
InteractiveView.call(this, client, options);
|
|
||||||
|
|
||||||
if(!options) {
|
|
||||||
this.options.multiLine = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.options.inputType = miscUtil.valueWithDefault(this.options.inputType, 'normal');
|
|
||||||
assert(TEXT_EDIT_INPUT_TYPES.indexOf(this.options.inputType) > -1);
|
|
||||||
|
|
||||||
if('password' === this.options.inputType || 'P' === 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('P' === this.options.inputType.charAt(0).toUpperCase()) {
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function MenuView(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);
|
|
||||||
|
|
||||||
if(self.submitViewId == this.id) {
|
|
||||||
self.emit('action', { view : this, action : 'submit' });
|
|
||||||
} else {
|
|
||||||
self.emit('action', { view : this, action : action });
|
|
||||||
|
|
||||||
if('accepted' === action || '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.setSubmitView = function(id) {
|
|
||||||
this.submitViewId = id;
|
|
||||||
};
|
|
||||||
|
|
||||||
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.inputType = mci.args[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
options.color = mci.color;
|
|
||||||
options.focusColor = mci.focusColor;
|
|
||||||
|
|
||||||
view = new TextEditView(this.client, options);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return view;
|
|
||||||
};
|
|
|
@ -0,0 +1,432 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var util = require('util');
|
||||||
|
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.LabelView = LabelView;
|
||||||
|
exports.TextEditView = TextEditView;
|
||||||
|
|
||||||
|
exports.ViewsController = ViewsController;
|
||||||
|
|
||||||
|
function View(client, options) {
|
||||||
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.client = client;
|
||||||
|
this.options = options;
|
||||||
|
this.acceptsFocus = false;
|
||||||
|
this.acceptsKeys = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(View, events.EventEmitter);
|
||||||
|
|
||||||
|
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));
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if(options) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
LabelView.prototype.place = function(pos) {
|
||||||
|
LabelView.super_.prototype.place.apply(this, arguments);
|
||||||
|
|
||||||
|
this.client.term.write(this.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
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 = [
|
||||||
|
'normal', 'N',
|
||||||
|
'password', 'P',
|
||||||
|
'upper', 'U',
|
||||||
|
'lower', 'l',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
function TextEditView(client, options) {
|
||||||
|
InteractiveView.call(this, client, options);
|
||||||
|
|
||||||
|
if(!options) {
|
||||||
|
this.options.multiLine = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options.inputType = miscUtil.valueWithDefault(this.options.inputType, 'normal');
|
||||||
|
assert(TEXT_EDIT_INPUT_TYPES.indexOf(this.options.inputType) > -1);
|
||||||
|
|
||||||
|
if('password' === this.options.inputType || 'P' === 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('P' === this.options.inputType.charAt(0).toUpperCase()) {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function MenuView(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);
|
||||||
|
|
||||||
|
if('next' === action) {
|
||||||
|
self.emit('action', { view : this, action : action });
|
||||||
|
self.nextFocus();
|
||||||
|
} else if('accepted' === action) {
|
||||||
|
if(self.submitViewId == this.id) {
|
||||||
|
self.emit('action', { view : this, action : 'submit' });
|
||||||
|
} else {
|
||||||
|
self.nextFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if(self.submitViewId == this.id) {
|
||||||
|
self.emit('action', { view : this, action : 'submit' });
|
||||||
|
} else {
|
||||||
|
self.emit('action', { view : this, action : action });
|
||||||
|
|
||||||
|
if('accepted' === action || '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.setSubmitView = function(id) {
|
||||||
|
this.submitViewId = id;
|
||||||
|
};
|
||||||
|
|
||||||
|
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.inputType = mci.args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
options.color = mci.color;
|
||||||
|
options.focusColor = mci.focusColor;
|
||||||
|
|
||||||
|
view = new TextEditView(this.client, options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return view;
|
||||||
|
};
|
|
@ -7,7 +7,8 @@ var lineEditor = require('../core/line_editor.js');
|
||||||
var art = require('../core/art.js');
|
var art = require('../core/art.js');
|
||||||
var user = require('../core/user.js');
|
var user = require('../core/user.js');
|
||||||
|
|
||||||
var view = require('../core/view.js');
|
//var view = require('../core/view.js');
|
||||||
|
var textView = require('../core/text_view.js');
|
||||||
|
|
||||||
exports.moduleInfo = {
|
exports.moduleInfo = {
|
||||||
name : 'Matrix',
|
name : 'Matrix',
|
||||||
|
@ -33,6 +34,16 @@ function entryPoint(client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tv = new textView.TextView(client, {
|
||||||
|
position : [5, 5],
|
||||||
|
text : 'Hello, World!',
|
||||||
|
textStyle : 'password',
|
||||||
|
maxLength : 10
|
||||||
|
});
|
||||||
|
|
||||||
|
tv.redraw();
|
||||||
|
|
||||||
|
/*
|
||||||
var vc = new view.ViewsController(client);
|
var vc = new view.ViewsController(client);
|
||||||
vc.loadFromMCIMap(mci);
|
vc.loadFromMCIMap(mci);
|
||||||
vc.setViewOrder();
|
vc.setViewOrder();
|
||||||
|
@ -46,25 +57,6 @@ function entryPoint(client) {
|
||||||
console.log('userName: ' + un);
|
console.log('userName: ' + un);
|
||||||
console.log('password: ' + pw);
|
console.log('password: ' + pw);
|
||||||
|
|
||||||
/*
|
|
||||||
var user1 = new user.User();
|
|
||||||
user.User.generatePasswordDerivedKey('password', function onDk(err, dkInfo) {
|
|
||||||
user1.userName = 'NuSkooler';
|
|
||||||
user1.properties = {
|
|
||||||
pw_pbkdf2_salt : dkInfo.salt,
|
|
||||||
pw_pbkdf2_dk : dkInfo.dk,
|
|
||||||
};
|
|
||||||
user.User.addNew(user1, function onNewUser(err, id) {
|
|
||||||
if(err) {
|
|
||||||
console.log(err);
|
|
||||||
} else {
|
|
||||||
console.log('new user id: ' + id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
user.User.loadWithCredentials(un, pw, function onUser(err, user) {
|
user.User.loadWithCredentials(un, pw, function onUser(err, user) {
|
||||||
if(err) {
|
if(err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
@ -75,6 +67,7 @@ function entryPoint(client) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue