Utility Methods

This commit is contained in:
anseki 2015-04-15 01:05:24 +09:00
parent bdd72b0037
commit b8e194e743

View file

@ -25,6 +25,7 @@ var
mask: '*',
limit: [],
limitMessage: 'Input another, please.${( [)limit(])}',
defaultInput: '',
trueValue: [],
falseValue: [],
caseSensitive: false,
@ -32,7 +33,9 @@ var
encoding: 'utf8',
bufferSize: 1024,
print: void 0,
history: true
history: true,
cd: false,
phContent: void 0
},
fdR = 'none', fdW, ttyR, isRawMode = false,
@ -448,15 +451,16 @@ function margeOptions() {
if (!optionsPart.hasOwnProperty(optionName)) { return; }
value = optionsPart[optionName];
switch (optionName) {
// _readlineSync defaultOptions
// _readlineSync <- * * -> defaultOptions
// ================ string
case 'mask': // * *
case 'limitMessage': // *
case 'defaultInput': // *
case 'encoding': // * *
/* jshint eqnull:true */
value = value != null ? value + '' : '';
/* jshint eqnull:false */
if (value && optionName === 'mask' || optionName === 'encoding')
if (value && optionName !== 'limitMessage')
{ value = value.replace(/[\r\n]/g, ''); }
options[optionName] = value;
break;
@ -472,6 +476,7 @@ function margeOptions() {
case 'caseSensitive': // * *
case 'keepWhitespace': // * *
case 'history': // *
case 'cd': // *
options[optionName] = !!value;
break;
// ================ array
@ -488,6 +493,7 @@ function margeOptions() {
break;
// ================ function
case 'print': // * *
case 'phContent': // *
options[optionName] = typeof value === 'function' ? value : void 0;
break;
// ================ other
@ -539,7 +545,7 @@ function array2charlist(array, caseSensitive, collectSymbols) {
{ return chars.concat((value + '').split('')); }, [])
.reduce(function(groups, curChar) {
var curGroupClass, curCharCode;
if (!caseSensitive) { curChar = curChar.toUpperCase(); }
if (!caseSensitive) { curChar = curChar.toLowerCase(); }
curGroupClass = /^\d$/.test(curChar) ? 1 :
/^[A-Z]$/.test(curChar) ? 2 : /^[a-z]$/.test(curChar) ? 3 : 0;
if (collectSymbols && curGroupClass === 0) {
@ -567,54 +573,80 @@ function joinChunks(chunks, suppressed)
{ return chunks.join(chunks.length > 2 ? ', ' : suppressed ? ' / ' : '/'); }
function getPhContent(param, options) {
var text, values, resCharlist = {}, arg;
switch (param) {
case 'hideEchoBack':
case 'mask':
case 'caseSensitive':
case 'keepWhitespace':
case 'encoding':
case 'bufferSize':
case 'history':
text = options.hasOwnProperty(param) ? options[param] + '' : '';
break;
case 'prompt':
case 'query':
case 'display':
text = options.displaySrc + '';
break;
case 'limit':
case 'trueValue':
case 'falseValue':
values = options[options.hasOwnProperty(param + 'Src') ? param + 'Src' : param];
if (options.keyIn) { // suppress
resCharlist = array2charlist(values, options.caseSensitive);
values = resCharlist.values;
} else {
values = values.filter(function(value) {
var type = typeof value;
return type === 'string' || type === 'number';
});
}
text = joinChunks(values, resCharlist.suppressed);
break;
case 'limitCount':
case 'limitCountNotZero':
text = options[options.hasOwnProperty('limitSrc') ? 'limitSrc' : 'limit'].length;
text = text || param !== 'limitCountNotZero' ? text + '' : '';
break;
case 'lastInput':
text = lastInput;
break;
case 'cwd':
case 'CWD':
text = process.cwd();
if (param === 'CWD') { text = require('path').basename(text); }
break;
default: // with arg
if (typeof(arg = (param.match(/^history_m(\d+)$/) || [])[1]) === 'string') {
text = inputHistory[inputHistory.length - arg] || '';
}
var text, values, resCharlist = {}, path, arg;
if (options.phContent) {
text = options.phContent(param, options);
}
if (typeof text !== 'string') {
switch (param) {
case 'hideEchoBack':
case 'mask':
case 'defaultInput':
case 'caseSensitive':
case 'keepWhitespace':
case 'encoding':
case 'bufferSize':
case 'history':
case 'cd':
text = !options.hasOwnProperty(param) ? '' :
typeof options[param] === 'boolean' ? (options[param] ? 'on' : 'off') :
options[param] + '';
break;
// case 'prompt':
// case 'query':
// case 'display':
// text = options.hasOwnProperty('displaySrc') ? options.displaySrc + '' : '';
// break;
case 'limit':
case 'trueValue':
case 'falseValue':
values = options[options.hasOwnProperty(param + 'Src') ? param + 'Src' : param];
if (options.keyIn) { // suppress
resCharlist = array2charlist(values, options.caseSensitive);
values = resCharlist.values;
} else {
values = values.filter(function(value) {
var type = typeof value;
return type === 'string' || type === 'number';
});
}
text = joinChunks(values, resCharlist.suppressed);
break;
case 'limitCount':
case 'limitCountNotZero':
text = options[options.hasOwnProperty('limitSrc') ?
'limitSrc' : 'limit'].length;
text = text || param !== 'limitCountNotZero' ? text + '' : '';
break;
case 'lastInput':
text = lastInput;
break;
case 'cwd':
case 'CWD':
case 'cwdHome':
text = process.cwd();
if (param === 'CWD') { text = require('path').basename(text); }
else if (param === 'cwdHome') {
path = (IS_WIN ? ((process.env.HOMEDRIVE || '') +
(process.env.HOMEPATH || '')).toLowerCase() :
process.env.HOME || '').replace(/[\/\\]+$/, '');
if (path && (IS_WIN ? text.toLowerCase() : text).indexOf(path) === 0)
{ text = '~' + text.substr(path.length); }
}
break;
case 'date':
case 'time':
case 'localeDate':
case 'localeTime':
text = (new Date())['to' +
param.replace(/^./, function(str) { return str.toUpperCase(); }) +
'String']();
break;
default: // with arg
if (typeof(arg = (param.match(/^history_m(\d+)$/) || [])[1]) === 'string') {
text = inputHistory[inputHistory.length - arg] || '';
}
}
}
return text;
}
@ -630,7 +662,7 @@ function getPhCharlist(param) {
return text;
}
function readlineWithOptions(options, preCheck) {
function getValidLine(options, preCheck) {
var res, forceNext, resCheck, limitMessage,
matches, histInput;
function _getPhContent(param) { return getPhContent(param, options); }
@ -706,11 +738,11 @@ exports.setDefault = function(options) {
exports.prompt = function(options) {
var readOptions = margeOptions(true, options);
readOptions.display = readOptions.prompt;
return readlineWithOptions(readOptions);
return getValidLine(readOptions);
};
exports.question = function(query, options) {
return readlineWithOptions(margeOptions(margeOptions(true, options), {
return getValidLine(margeOptions(margeOptions(true, options), {
display: query
}));
};
@ -767,6 +799,7 @@ exports.questionEMail = function(query, options) {
}, options, {
// -------- forced
keepWhitespace: false,
cd: false
}));
};
@ -780,159 +813,183 @@ exports.questionNewPassword = function(query, options) {
trueValue: null,
falseValue: null,
caseSensitive: true
}, options),
// forced: limit
}, options, {
// -------- forced
history: false,
cd: false,
// limit (by charlist etc.),
phContent: function(param) {
return param === 'charlist' ? resCharlist.text :
param === 'length' ? min + '...' + max : null;
}
}),
// added: charlist, min, max, confirm
charlist, min, max, confirm, resCharlist, res1, res2, limit1, limitMessage1;
charlist, min, max, confirm, limit, resCharlist, limitMessage, res1, res2;
function phSpecial(param) {
return param === 'charlist' ? resCharlist.text :
param === 'length' ? min + '...' + max : null;
}
// bug? `eqnull:true` doesn't work
/* jshint ignore:start */
if (query == null) { query = 'Input new password :'; }
/* jshint ignore:end */
charlist = options && options.charlist ? options.charlist + '' : '${!-~}';
charlist = replacePlaceholder(charlist, getPhCharlist);
charlist = replacePlaceholder(
options && options.charlist ? options.charlist + '' : '${!-~}', getPhCharlist);
if (options) { min = options.min; max = options.max; }
if (isNaN(min = parseInt(min, 10)) || typeof min !== 'number') { min = 12; }
if (isNaN(max = parseInt(max, 10)) || typeof max !== 'number') { max = 24; }
limit = new RegExp('^[' + charlist.replace(/[^A-Za-z0-9_ ]/g, '\\$&') +
']{' + min + ',' + max + '}$');
resCharlist = array2charlist([charlist], readOptions.caseSensitive, true);
resCharlist.text = joinChunks(resCharlist.values, resCharlist.suppressed);
/* jshint eqnull:true */
confirm = options && options.confirm != null ? options.confirm :
'Reinput same one to confirm it :';
/* jshint eqnull:false */
limit1 = new RegExp('^[' + charlist.replace(/[^A-Za-z0-9_ ]/g, '\\$&') +
']{' + min + ',' + max + '}$');
if (readOptions.limitMessage) {
resCharlist = array2charlist([charlist], readOptions.caseSensitive, true);
resCharlist.text = joinChunks(resCharlist.values, resCharlist.suppressed);
// getPhContent is called by readlineWithOptions
limitMessage1 = replacePlaceholder(readOptions.limitMessage, phSpecial);
}
/* jshint eqnull:true */
if (query == null) { query = 'Input new password :'; }
/* jshint eqnull:false */
limitMessage = readOptions.limitMessage;
while (!res2) {
readOptions.limit = limit1;
readOptions.limitMessage = limitMessage1;
readOptions.limit = limit;
readOptions.limitMessage = limitMessage;
res1 = exports.question(query, readOptions);
readOptions.limit = [res1, ''];
readOptions.limitMessage = 'Two passwords don\'t match.' +
' Hit only Enter key if you want to retry from first password.';
// getPhContent is called by readlineWithOptions
res2 = exports.question(replacePlaceholder(confirm, phSpecial), readOptions);
res2 = exports.question(confirm, readOptions);
}
return res1;
};
function _questionParse(query, options, fncParse) {
function _questionNum(query, options, parser) {
var validValue;
function getValidValue(value) {
validValue = fncParse(value);
validValue = parser(value);
return !isNaN(validValue) && typeof validValue === 'number';
}
exports.question(query, margeOptions(options, {
exports.question(query, margeOptions({
// -------- default
limitMessage: 'Input valid number, please.'
}, options, {
// -------- forced
limit: getValidValue
limit: getValidValue,
cd: false
// trueValue, falseValue are don't work.
}));
return validValue;
}
exports.questionInt = function(query, options) {
return _questionParse(query, options, function(value) { return parseInt(value, 10); });
return _questionNum(query, options, function(value) { return parseInt(value, 10); });
};
exports.questionFloat = function(query, options) {
return _questionNum(query, options, parseFloat);
};
exports.questionFloat = function(query, options)
{ return _questionParse(query, options, parseFloat); };
exports.questionPath = function(query, options) {
var fs = require('fs'), validPath;
function getValidPath(value) {
var stat;
if (!fs.existsSync(value)) { return; }
validPath = fs.realpathSync(value);
if (options.minSize || options.maxSize || options.isFile || options.isDirectory) {
stat = fs.statSync(validPath);
if (options.minSize && stat.size < +options.minSize ||
options.maxSize && stat.size > +options.maxSize ||
options.isFile && !stat.isFile() ||
options.isDirectory && !stat.isDirectory()) { return false; }
}
if (typeof options.validate === 'function' && !options.validate(validPath))
{ return false; }
return true;
}
exports.question(query, margeOptions(options, {
var readOptions = margeOptions({
// -------- default
limitMessage: 'Input valid path, please.${( Min:)minSize}${( Max:)maxSize}',
history: true,
cd: true
}, options, {
// -------- forced
limit: getValidPath
keepWhitespace: false,
limit: function(value) {
var stat;
if (!fs.existsSync(value)) { return; }
validPath = fs.realpathSync(value);
if (options) {
if (options.minSize || options.maxSize ||
options.isFile || options.isDirectory) {
stat = fs.statSync(validPath);
if (options.minSize && stat.size < +options.minSize ||
options.maxSize && stat.size > +options.maxSize ||
options.isFile && !stat.isFile() ||
options.isDirectory && !stat.isDirectory()) { return; }
}
if (typeof options.validate === 'function' && !options.validate(validPath))
{ return; }
}
return true;
},
// trueValue, falseValue are don't work.
}));
phContent: function(param) {
return param !== 'minSize' && param !== 'maxSize' ? null :
options && options.hasOwnProperty(param) ? options[param] + '' : '';
}
}),
// added: minSize, maxSize, isFile, isDirectory, validate
validPath;
/* jshint eqnull:true */
if (query == null) { query = 'Input path (you can "cd" and "pwd") :'; }
/* jshint eqnull:false */
exports.question(query, readOptions);
return validPath;
};
exports.promptCWD = function(options) {
var readOptions = margeOptions({
exports.promptSimShell = function(options) {
return exports.prompt(margeOptions({
// -------- default
prompt: '[${cwd}]$ '
}, options);
return exports.prompt(readOptions);
history: true
}, options, {
// -------- forced
prompt: (function() {
return IS_WIN ?
'${cwd}>' :
// 'user@host:cwd$ '
(process.env.USER || '') +
(process.env.HOSTNAME ?
'@' + process.env.HOSTNAME.replace(/\..*$/, '') : '') +
':${cwdHome}$ ';
})()
}));
};
function _keyInYN(query, options, limit) {
var readOptions = margeOptions(options, {
// -------- forced
hideEchoBack: false,
limit: limit,
trueValue: 'y',
falseValue: 'n',
caseSensitive: false
}), res;
exports.keyInYN = function(query, options) {
var res;
/* jshint eqnull:true */
if (query == null) { query = 'Are you sure? :'; }
/* jshint eqnull:false */
if ((query += '') && options.guide !== false)
{ query = query.replace(/\s*:?\s*$/, '') + ' [Y/N] :'; }
res = exports.keyIn(query, readOptions);
if ((!options || options.guide !== false) && (query += ''))
{ query = query.replace(/\s*:?\s*$/, '') + ' [y/n] :'; }
res = exports.keyIn(query, margeOptions(options, {
// -------- forced
hideEchoBack: false,
limit: options && options.strict ? 'yn' : null,
trueValue: 'y',
falseValue: 'n',
caseSensitive: false
}));
return typeof res === 'boolean' ? res : '';
}
exports.keyInYN = function(query, options) { return _keyInYN(query, options); };
exports.keyInYNStrict = function(query, options)
{ return _keyInYN(query, options, 'yn'); };
};
exports.keyInPause = function(query, options) {
var readOptions = margeOptions(options, {
// -------- forced
hideEchoBack: true,
mask: ''
});
/* jshint eqnull:true */
if (query == null) { query = 'Continue...'; }
/* jshint eqnull:false */
if ((query += '') && options.guide !== false)
if ((!options || options.guide !== false) && (query += ''))
{ query = query.replace(/\s+$/, '') + ' (Hit any key)'; }
exports.keyIn(query, readOptions);
exports.keyIn(query, margeOptions(options, {
// -------- forced
hideEchoBack: true,
mask: ''
}));
return;
};
exports.keyInSelect = function(query, items, options) {
var readOptions = margeOptions({
// -------- default
hideEchoBack: false,
hideEchoBack: false
}, options, {
// -------- forced
trueValue: null,
falseValue: null,
caseSensitive: false
}), keylist = '', key2i = {}, charCode = 49 /* '1' */, display = '\n';
// limit (by items)
}),
keylist = '', key2i = {}, charCode = 49 /* '1' */, display = '\n';
if (!Array.isArray(items) || items.length > 35)
{ throw '`items` must be Array (max length: 35).'; }
@ -941,9 +998,9 @@ exports.keyInSelect = function(query, items, options) {
keylist += key;
key2i[key] = i;
display += '[' + key + '] ' + item.trim() + '\n';
charCode = charCode === 57 /* '9' */ ? 65 /* 'A' */ : charCode + 1;
charCode = charCode === 57 /* '9' */ ? 97 /* 'a' */ : charCode + 1;
});
if (options.cancel !== false) {
if (!options || options.cancel !== false) {
keylist += '0';
key2i['0'] = -1;
display += '[' + '0' + '] CANCEL\n';
@ -955,12 +1012,12 @@ exports.keyInSelect = function(query, items, options) {
if (query == null) { query = 'Choose one from list :'; }
/* jshint eqnull:false */
if ((query += '')) {
if (options.guide !== false)
if (!options || options.guide !== false)
{ query = query.replace(/\s*:?\s*$/, '') + ' [${limit}] :'; }
display += query;
}
return key2i[exports.keyIn(display, readOptions).toUpperCase()];
return key2i[exports.keyIn(display, readOptions).toLowerCase()];
};
// ======== DEPRECATED ========