* 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:
parent
5288643189
commit
7a643150e7
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 : [],
|
||||
},
|
||||
|
|
|
@ -14,9 +14,11 @@ function EditTextView(options) {
|
|||
options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true);
|
||||
options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true);
|
||||
options.resizable = false;
|
||||
|
||||
|
||||
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);
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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" : "#"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
},
|
||||
"ET2" : {
|
||||
"submit" : true,
|
||||
"argName" : "password"
|
||||
"argName" : "password",
|
||||
"password" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue