* Fixed double extra view redraws

* Redraw views when all of them are ready @ MCI init
* Moved a lot of properties of views to menu.json/prompts
* Allow scrolling editor for EditTextView
* New @config:path.to.property for menu.json view properties/etc.
This commit is contained in:
Bryan Ashby 2015-04-26 20:46:16 -06:00
parent 5288643189
commit 7a643150e7
10 changed files with 233 additions and 71 deletions

View File

@ -8,6 +8,7 @@ var assert = require('assert');
exports.parseAsset = parseAsset;
exports.getArtAsset = getArtAsset;
exports.resolveConfigAsset = resolveConfigAsset;
var ALL_ASSETS = [
'art',
@ -15,6 +16,7 @@ var ALL_ASSETS = [
'method',
'systemMethod',
'prompt',
'config',
];
var ASSET_RE = new RegExp('\\@(' + ALL_ASSETS.join('|') + ')\\:([\\w\\d\\.]*)(?:\\/([\\w\\d\\_]+))*');
@ -36,7 +38,7 @@ function parseAsset(s) {
}
}
function getArtAsset(art, cb) {
function getArtAsset(art) {
if(!_.isString(art)) {
return null;
}
@ -53,3 +55,22 @@ function getArtAsset(art, cb) {
};
}
}
function resolveConfigAsset(from) {
var asset = parseAsset(from);
if(asset) {
assert('config' === asset.type);
var path = asset.asset.split('.');
var conf = Config;
for(var i = 0; i < path.length; ++i) {
if(_.isUndefined(conf[path[i]])) {
return from;
}
conf = conf[path[i]];
}
return conf;
} else {
return from;
}
}

View File

@ -83,7 +83,7 @@ function getDefaultConfig() {
usernameMax : 22,
usernamePattern : '^[A-Za-z0-9~!@#$%^&*()\\-\\_+]+$',
passwordMin : 6,
passwordMax : 256,
passwordMax : 128,
requireActivation : true, // require SysOp activation?
invalidUsernames : [],
},

View File

@ -17,6 +17,8 @@ function EditTextView(options) {
TextView.call(this, options);
this.cursorPos = { x : 0 };
this.clientBackspace = function() {
this.client.term.write(
'\b' + this.getANSIColor(this.getColor()) + this.fillChar + '\b' + this.getANSIColor(this.getFocusColor()));
@ -33,15 +35,22 @@ EditTextView.prototype.onKeyPress = function(key, isSpecial) {
assert(1 === key.length);
// :TODO: how to handle justify left/center?
if(!_.isNumber(this.maxLength) || this.text.length < this.maxLength) {
if(this.text.length < this.maxLength) {
key = strUtil.stylizeString(key, this.textStyle);
this.text += key;
if(this.textMaskChar) {
this.client.term.write(this.textMaskChar);
if(this.text.length > this.dimens.width) {
// no shortcuts - redraw the view
this.redraw();
} else {
this.client.term.write(key);
this.cursorPos.x += 1;
if(this.textMaskChar) {
this.client.term.write(this.textMaskChar);
} else {
this.client.term.write(key);
}
}
}
@ -54,9 +63,18 @@ EditTextView.prototype.onSpecialKeyPress = function(keyName) {
if(this.isSpecialKeyMapped('backspace', keyName)) {
if(this.text.length > 0) {
this.text = this.text.substr(0, this.text.length - 1);
this.clientBackspace();
if(this.text.length >= this.dimens.width) {
this.redraw();
} else {
this.cursorPos.x -= 1;
if(this.cursorPos.x >= 0) {
this.clientBackspace();
}
}
}
}
EditTextView.super_.prototype.onSpecialKeyPress.call(this, keyName);
};

View File

@ -70,6 +70,17 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
return false;
}
function setWidth(pos) {
if(mci.args.length > pos && mci.args[pos].length > 0) {
if(!_.isObject(options.dimens)) {
options.dimens = {};
}
options.dimens.width = parseInt(mci.args[pos], 10);
return true;
}
return false;
}
function setFocusOption(pos, name) {
if(mci.focusArgs && mci.focusArgs.length > pos && mci.focusArgs[pos].length > 0) {
options[name] = mci.focusArgs[pos];
@ -82,30 +93,31 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
case 'TL' :
setOption(0, 'textStyle');
setOption(1, 'justify');
setWidth(2);
/*
if(setOption(2, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10);
options.dimens = { width : options.maxLength };
}
*/
view = new TextView(options);
break;
// Edit Text
case 'ET' :
setWidth(0);
/*
if(setOption(0, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10); // ensure number
options.dimens = { width : options.maxLength };
}
*/
setOption(1, 'textStyle');
if(options.textStyle === 'P') {
// Assign the proper password character / text mask
assert(_.isObject(this.client.currentThemeInfo));
options.textMaskChar = this.client.currentThemeInfo.getPasswordChar();
}
setFocusOption(0, 'focusTextStyle');
setOption(1, 'textStyle');
setFocusOption(0, 'focusTextStyle');
view = new EditTextView(options);
break;
@ -117,11 +129,14 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
if(options.text) {
setOption(1, 'textStyle');
setOption(2, 'justify');
setWidth(3);
/*
if(setOption(3, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10);
options.dimens = { width : options.maxLength };
}
*/
view = new TextView(options);
}

View File

@ -148,6 +148,7 @@ function getFormConfigByIDAndMap(menuConfig, formId, mciMap, cb) {
var formForId = menuConfig.form[formId];
var mciReqKey = _.sortBy(Object.keys(mciMap), String).join('');
Log.trace( { mciKey : mciReqKey }, 'Looking for MCI configuration key');
if(_.isObject(formForId[mciReqKey])) {
cb(null, formForId[mciReqKey]);
return;

View File

@ -5,8 +5,10 @@ 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');
var _ = require('lodash');
exports.TextView = TextView;
@ -17,27 +19,52 @@ function TextView(options) {
if(options.maxLength) {
this.maxLength = options.maxLength;
} else {
this.maxLength = this.client.term.termWidth - this.position.x;
}
this.multiLine = options.multiLine || false;
this.fillChar = miscUtil.valueWithDefault(options.fillChar, ' ').substr(0, 1);
this.justify = options.justify || 'right';
this.resizable = miscUtil.valueWithDefault(options.resizable, true);
//this.inputType = options.inputType || 'normal';
this.fillChar = miscUtil.valueWithDefault(options.fillChar, ' ').substr(0, 1);
this.justify = options.justify || 'right';
this.resizable = miscUtil.valueWithDefault(options.resizable, true);
this.horizScroll = miscUtil.valueWithDefault(options.horizScroll, true);
this.isPasswordTextStyle = 'P' === this.textStyle || 'password' === this.textStyle;
assert(!this.multiLine); // :TODO: not yet supported
if(!this.multiLine) {
this.dimens.height = 1;
if(_.isString(options.textMaskChar) && 1 === options.textMaskChar.length) {
this.textMaskChar = options.textMaskChar;
}
this.dimens.height = 1;
this.drawText = function(s) {
var ansiColor = this.getANSIColor(this.hasFocus ? this.getFocusColor() : this.getColor());
if(this.isPasswordTextStyle) {
//
// ABCDEFGHIJK
// |ABCDEFG| ^_ length
// ^-- dimens.width
//
var textToDraw = _.isString(this.textMaskChar) ?
new Array(s.length + 1).join(this.textMaskChar) :
strUtil.stylizeString(s, this.hasFocus ? this.focusTextStyle : this.textStyle);
if(textToDraw.length > this.dimens.width) {
// XXXXXXXXXXXXXXXXX
// this is the text but too long
// text but too long
if(this.horizScroll) {
textToDraw = textToDraw.substr(textToDraw.length - this.dimens.width, textToDraw.length);//0, this.dimens.width);//textToDraw.length - (this.dimens.width + 1));
}
}
var textAnsiColor = this.getANSIColor(this.hasFocus ? this.getFocusColor() : this.getColor());
var fillAnsiColor = this.getANSIColor(this.getColor());
//console.log(textToDraw)
///console.log(this.dimens.width + 1)
this.client.term.write(strUtil.pad(textToDraw, this.dimens.width + 1, this.fillChar, this.justify, textAnsiColor, fillAnsiColor));
/*
if(_.isString(this.textMaskChar)) {
this.client.term.write(strUtil.pad(
new Array(s.length + 1).join(this.textMaskChar),
this.dimens.width + 1,
@ -55,13 +82,10 @@ function TextView(options) {
ansiColor,
this.getANSIColor(this.getColor())));
}
*/
};
this.setText(options.text || '');
if(this.isPasswordTextStyle) {
this.textMaskChar = miscUtil.valueWithDefault(options.textMaskChar, '*').substr(0, 1);
}
}
util.inherits(TextView, View);
@ -99,14 +123,8 @@ TextView.prototype.setText = function(text) {
this.text = strUtil.stylizeString(this.text, this.hasFocus ? this.focusTextStyle : this.textStyle);
/*if(!this.multiLine && !this.dimens.width) {
this.dimens.width = this.text.length;
}*/
if(!this.multiLine) {
if(this.resizable) {
this.dimens.width = this.text.length + widthDelta;
}
if(this.resizable) {
this.dimens.width = this.text.length + widthDelta;
}
this.redraw();

View File

@ -49,9 +49,6 @@ function View(options) {
this.setPosition(options.position);
}
// this.isPasswordTextStyle = 'P' === this.textStyle || 'password' === this.textStyle;
// :TODO: Don't allow width/height > client.term
if(options.dimens && options.dimens.height) {
this.dimens.height = options.dimens.height;

View File

@ -120,6 +120,7 @@ function ViewController(options) {
};
this.getLogFriendlyFormData = function(formData) {
// :TODO: these fields should be part of menu.json sensitiveMembers[]
var safeFormData = _.cloneDeep(formData);
if(safeFormData.value.password) {
safeFormData.value.password = '*****';
@ -149,8 +150,6 @@ function ViewController(options) {
view.on('action', self.viewActionListener);
self.addView(view);
view.redraw(); // :TODO: fix double-redraw if this is the item we set focus to!
}
nextItem(null);
@ -162,21 +161,54 @@ function ViewController(options) {
};
this.setViewPropertiesFromMCIConf = function(view, conf) {
if(_.isBoolean(conf.submit)) {
view.submit = conf.submit;
} else if(_.isArray(conf.submit) && conf.submit.length > 0) {
view.submit = true;
function getViewProp(propName) {
if(conf[propName]) {
return asset.resolveConfigAsset(conf[propName]);
}
}
function setSimpleViewProp(propName) {
var propValue = getViewProp(propName);
if(propValue) {
view[propName] = propValue;
}
}
var value;
value = getViewProp('items');
if(value) {
view.setItems(value);
}
value = getViewProp('text');
if(value) {
view.setText(value);
}
setSimpleViewProp('textStyle');
setSimpleViewProp('focusTextStyle');
setSimpleViewProp('fillChar');
setSimpleViewProp('maxLength');
value = getViewProp('textMaskChar');
if(_.isString(value)) {
view.textMaskChar = value.substr(0, 1);
} else if(value && true === value) {
//
// Option that should normally be used in order to
// get the password character from Config/theme
//
view.textMaskChar = self.client.currentThemeInfo.getPasswordChar();
}
value = getViewProp('submit');
if(_.isBoolean(value)) {
view.submit = value;
} else {
view.submit = false;
}
//view.submit = conf.submit || false;
if(_.isArray(conf.items)) {
view.setItems(conf.items);
}
if(_.isString(conf.text)) {
view.setText(conf.text);
view.submit = _.isArray(value) && value.length > 0;
}
if(_.isString(conf.argName)) {
@ -196,6 +228,13 @@ function ViewController(options) {
assert(!isNaN(viewId));
var view = self.getView(viewId);
if(!view) {
Log.warn( { viewId : viewId }, 'Cannot find view');
nextItem(null);
return;
}
var mciConf = config.mci[mci];
self.setViewPropertiesFromMCIConf(view, mciConf);
@ -328,6 +367,7 @@ ViewController.prototype.setViewOrder = function(order) {
}
};
/*
ViewController.prototype.loadFromMCIMap = function(mciMap) {
var factory = new MCIViewFactory(this.client);
var self = this;
@ -343,6 +383,7 @@ ViewController.prototype.loadFromMCIMap = function(mciMap) {
}
});
};
*/
ViewController.prototype.loadFromPromptConfig = function(options, cb) {
assert(_.isObject(options));
@ -375,6 +416,15 @@ ViewController.prototype.loadFromPromptConfig = function(options, cb) {
callback(null);
},
function drawAllViews(callback) {
for(var id in self.views) {
if(initialFocusId === id) {
continue; // will draw @ focus
}
self.views[id].redraw();
}
callback(null);
},
function setInitialViewFocus(callback) {
if(initialFocusId) {
self.switchFocus(initialFocusId);
@ -501,6 +551,15 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) {
callback(null);
},
function drawAllViews(callback) {
for(var id in self.views) {
if(initialFocusId === id) {
continue; // will draw @ focus
}
self.views[id].redraw();
}
callback(null);
},
function setInitialViewFocus(callback) {
if(initialFocusId) {
self.switchFocus(initialFocusId);

View File

@ -99,20 +99,41 @@
"form" : {
"0" : {
"BT12BT13ET1ET10ET2ET3ET4ET5ET6ET7ET8ET9TL11" : {
//
// "beforeViewsDraw" : "@method:location.js/myBeforeViewsDraw" -> myBeforeViewsDraw(views)
"mci" : {
"ET1" : {
"focus" : true,
"argName" : "username"
},
"ET2" : { "argName" : "realName" },
"ET2" : {
"argName" : "realName",
"maxLength" : "@config:users.usernameMax"
},
"ET3" : { "argName" : "age" },
"ET4" : { "argName" : "sex" },
"ET5" : { "argName" : "location" },
"ET6" : { "argName" : "affils" },
"ET7" : { "argName" : "email" },
"ET8" : { "argName" : "web" },
"ET9" : { "argName" : "password" },
"ET10" : { "argName" : "passwordConfirm" },
"ET6" : {
"argName" : "affils",
"maxLength" : 30
},
"ET7" : {
"argName" : "email",
"maxLength" : 255
},
"ET8" : {
"argName" : "web",
"maxLength" : 255
},
"ET9" : {
"argName" : "password",
"password" : true
},
"ET10" : {
"argName" : "passwordConfirm",
"password" : true,
"maxLength" : "@config:users.passwordMax"
},
"BT12" : {
"submit" : true,
"text" : "Apply"
@ -162,7 +183,8 @@
"VM1" : {
"mci" : {
"VM1" : {
"items" : [ "Edit Text View", "Other" ]
"items" : [ "Edit Text View", "Other" ],
"focusTextStyle" : "U"
}
},
"submit" : {
@ -182,7 +204,17 @@
"options" : { "cls" : true },
"form" : {
"0" : {
"CB4ET1ET2ET3ET4ET5" : {
"CB4ET1ET2ET3ET5" : {
"mci" : {
"ET1" : { "maxLength" : 1 },
"ET2" : { "maxLength" : 1 },
"ET3" : { "maxLength" : 1 },
"ET5" : {
"password" : true,
"submit" : [ "esc" ],
"fillChar" : "#"
}
}
}
}
}

View File

@ -7,7 +7,8 @@
},
"ET2" : {
"submit" : true,
"argName" : "password"
"argName" : "password",
"password" : true
}
}
}