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