1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | 'use strict';
|
10 |
|
11 | var
|
12 | IS_WIN = process.platform === 'win32',
|
13 |
|
14 | ALGORITHM_CIPHER = 'aes-256-cbc',
|
15 | ALGORITHM_HASH = 'sha256',
|
16 | DEFAULT_ERR_MSG = 'The current environment doesn\'t support interactive reading from TTY.',
|
17 |
|
18 | fs = require('fs'),
|
19 | TTY = process.binding('tty_wrap').TTY,
|
20 | childProc = require('child_process'),
|
21 | pathUtil = require('path'),
|
22 |
|
23 | defaultOptions = {
|
24 |
|
25 | prompt: '> ',
|
26 | hideEchoBack: false,
|
27 | mask: '*',
|
28 | limit: [],
|
29 | limitMessage: 'Input another, please.$<( [)limit(])>',
|
30 | defaultInput: '',
|
31 | trueValue: [],
|
32 | falseValue: [],
|
33 | caseSensitive: false,
|
34 | keepWhitespace: false,
|
35 | encoding: 'utf8',
|
36 | bufferSize: 1024,
|
37 | print: void 0,
|
38 | history: true,
|
39 | cd: false,
|
40 | phContent: void 0,
|
41 | preCheck: void 0
|
42 |
|
43 | },
|
44 |
|
45 | fdR = 'none', fdW, ttyR, isRawMode = false,
|
46 | extHostPath, extHostArgs, tempdir, salt = 0,
|
47 | lastInput = '', inputHistory = [],
|
48 | _DBG_useExt = false, _DBG_checkOptions = false, _DBG_checkMethod = false;
|
49 |
|
50 | function getHostArgs(options) {
|
51 |
|
52 | function encodeArg(arg) {
|
53 | return arg.replace(/[^\w\u0080-\uFFFF]/g, function(chr) {
|
54 | return '#' + chr.charCodeAt(0) + ';';
|
55 | });
|
56 | }
|
57 |
|
58 | return extHostArgs.concat((function(conf) {
|
59 | var args = [];
|
60 | Object.keys(conf).forEach(function(optionName) {
|
61 | if (conf[optionName] === 'boolean') {
|
62 | if (options[optionName]) { args.push('--' + optionName); }
|
63 | } else if (conf[optionName] === 'string') {
|
64 | if (options[optionName]) {
|
65 | args.push('--' + optionName, encodeArg(options[optionName]));
|
66 | }
|
67 | }
|
68 | });
|
69 | return args;
|
70 | })({
|
71 |
|
72 | display: 'string',
|
73 | displayOnly: 'boolean',
|
74 | keyIn: 'boolean',
|
75 | hideEchoBack: 'boolean',
|
76 | mask: 'string',
|
77 | limit: 'string',
|
78 | caseSensitive: 'boolean'
|
79 |
|
80 | }));
|
81 | }
|
82 |
|
83 |
|
84 | function _execFileSync(options, execOptions) {
|
85 |
|
86 | function getTempfile(name) {
|
87 | var filepath, suffix = '', fd;
|
88 | tempdir = tempdir || require('os').tmpdir();
|
89 |
|
90 | while (true) {
|
91 | filepath = pathUtil.join(tempdir, name + suffix);
|
92 | try {
|
93 | fd = fs.openSync(filepath, 'wx');
|
94 | } catch (e) {
|
95 | if (e.code === 'EEXIST') {
|
96 | suffix++;
|
97 | continue;
|
98 | } else {
|
99 | throw e;
|
100 | }
|
101 | }
|
102 | fs.closeSync(fd);
|
103 | break;
|
104 | }
|
105 | return filepath;
|
106 | }
|
107 |
|
108 | var hostArgs, shellPath, shellArgs, res = {}, exitCode, extMessage,
|
109 | pathStdout = getTempfile('readline-sync.stdout'),
|
110 | pathStderr = getTempfile('readline-sync.stderr'),
|
111 | pathExit = getTempfile('readline-sync.exit'),
|
112 | pathDone = getTempfile('readline-sync.done'),
|
113 | crypto = require('crypto'), shasum, decipher, password;
|
114 |
|
115 | shasum = crypto.createHash(ALGORITHM_HASH);
|
116 | shasum.update('' + process.pid + (salt++) + Math.random());
|
117 | password = shasum.digest('hex');
|
118 | decipher = crypto.createDecipher(ALGORITHM_CIPHER, password);
|
119 |
|
120 | hostArgs = getHostArgs(options);
|
121 | if (IS_WIN) {
|
122 | shellPath = process.env.ComSpec || 'cmd.exe';
|
123 | process.env.Q = '"';
|
124 |
|
125 | shellArgs = ['/V:ON', '/S', '/C',
|
126 | '(%Q%' + shellPath + '%Q% /V:ON /S /C %Q%' +
|
127 | '%Q%' + extHostPath + '%Q%' +
|
128 | hostArgs.map(function(arg) { return ' %Q%' + arg + '%Q%'; }).join('') +
|
129 | ' & (echo !ERRORLEVEL!)>%Q%' + pathExit + '%Q%%Q%) 2>%Q%' + pathStderr + '%Q%' +
|
130 | ' |%Q%' + process.execPath + '%Q% %Q%' + __dirname + '\\encrypt.js%Q%' +
|
131 | ' %Q%' + ALGORITHM_CIPHER + '%Q% %Q%' + password + '%Q%' +
|
132 | ' >%Q%' + pathStdout + '%Q%' +
|
133 | ' & (echo 1)>%Q%' + pathDone + '%Q%'];
|
134 | } else {
|
135 | shellPath = '/bin/sh';
|
136 | shellArgs = ['-c',
|
137 |
|
138 | '("' + extHostPath + '"' +
|
139 | hostArgs.map(function(arg) { return " '" + arg.replace(/'/g, "'\\''") + "'"; }).join('') +
|
140 | '; echo $?>"' + pathExit + '") 2>"' + pathStderr + '"' +
|
141 | ' |"' + process.execPath + '" "' + __dirname + '/encrypt.js"' +
|
142 | ' "' + ALGORITHM_CIPHER + '" "' + password + '"' +
|
143 | ' >"' + pathStdout + '"' +
|
144 | '; echo 1 >"' + pathDone + '"'];
|
145 | }
|
146 | if (_DBG_checkMethod) { _DBG_checkMethod('_execFileSync', hostArgs); }
|
147 | try {
|
148 | childProc.spawn(shellPath, shellArgs, execOptions);
|
149 | } catch (e) {
|
150 | res.error = new Error(e.message);
|
151 | res.error.method = '_execFileSync - spawn';
|
152 | res.error.program = shellPath;
|
153 | res.error.args = shellArgs;
|
154 | }
|
155 |
|
156 | while (fs.readFileSync(pathDone, {encoding: options.encoding}).trim() !== '1') {}
|
157 | if ((exitCode =
|
158 | fs.readFileSync(pathExit, {encoding: options.encoding}).trim()) === '0') {
|
159 | res.input =
|
160 | decipher.update(fs.readFileSync(pathStdout, {encoding: 'binary'}),
|
161 | 'hex', options.encoding) +
|
162 | decipher.final(options.encoding);
|
163 | } else {
|
164 | extMessage = fs.readFileSync(pathStderr, {encoding: options.encoding}).trim();
|
165 | res.error = new Error(DEFAULT_ERR_MSG + (extMessage ? '\n' + extMessage : ''));
|
166 | res.error.method = '_execFileSync';
|
167 | res.error.program = shellPath;
|
168 | res.error.args = shellArgs;
|
169 | res.error.extMessage = extMessage;
|
170 | res.error.exitCode = +exitCode;
|
171 | }
|
172 |
|
173 | fs.unlinkSync(pathStdout);
|
174 | fs.unlinkSync(pathStderr);
|
175 | fs.unlinkSync(pathExit);
|
176 | fs.unlinkSync(pathDone);
|
177 |
|
178 | return res;
|
179 | }
|
180 |
|
181 | function readlineExt(options) {
|
182 | var hostArgs, res = {}, extMessage,
|
183 | execOptions = {env: process.env, encoding: options.encoding};
|
184 |
|
185 | if (!extHostPath) {
|
186 | if (IS_WIN) {
|
187 | if (process.env.PSModulePath) {
|
188 | extHostPath = 'powershell.exe';
|
189 | extHostArgs = ['-ExecutionPolicy', 'Bypass', '-File', __dirname + '\\read.ps1'];
|
190 | } else {
|
191 | extHostPath = 'cscript.exe';
|
192 | extHostArgs = ['//nologo', __dirname + '\\read.cs.js'];
|
193 | }
|
194 | } else {
|
195 | extHostPath = '/bin/sh';
|
196 | extHostArgs = [__dirname + '/read.sh'];
|
197 | }
|
198 | }
|
199 | if (IS_WIN && !process.env.PSModulePath) {
|
200 |
|
201 |
|
202 | execOptions.stdio = [process.stdin];
|
203 | }
|
204 |
|
205 | if (childProc.execFileSync) {
|
206 | hostArgs = getHostArgs(options);
|
207 | if (_DBG_checkMethod) { _DBG_checkMethod('execFileSync', hostArgs); }
|
208 | try {
|
209 | res.input = childProc.execFileSync(extHostPath, hostArgs, execOptions);
|
210 | } catch (e) {
|
211 | extMessage = e.stderr ? (e.stderr + '').trim() : '';
|
212 | res.error = new Error(DEFAULT_ERR_MSG + (extMessage ? '\n' + extMessage : ''));
|
213 | res.error.method = 'execFileSync';
|
214 | res.error.program = extHostPath;
|
215 | res.error.args = hostArgs;
|
216 | res.error.extMessage = extMessage;
|
217 | res.error.exitCode = e.status;
|
218 | res.error.code = e.code;
|
219 | res.error.signal = e.signal;
|
220 | }
|
221 | } else {
|
222 | res = _execFileSync(options, execOptions);
|
223 | }
|
224 | if (!res.error) {
|
225 | res.input = res.input.replace(/^\s*'|'\s*$/g, '');
|
226 | options.display = '';
|
227 | }
|
228 |
|
229 | return res;
|
230 | }
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 | function _readlineSync(options) {
|
244 | var input = '', displaySave = options.display,
|
245 | silent = !options.display &&
|
246 | options.keyIn && options.hideEchoBack && !options.mask;
|
247 |
|
248 | function tryExt() {
|
249 | var res = readlineExt(options);
|
250 | if (res.error) { throw res.error; }
|
251 | return res.input;
|
252 | }
|
253 |
|
254 | if (_DBG_checkOptions) { _DBG_checkOptions(options); }
|
255 |
|
256 | (function() {
|
257 | var fsB, constants, verNum;
|
258 |
|
259 | function getFsB() {
|
260 | if (!fsB) {
|
261 | fsB = process.binding('fs');
|
262 | constants = process.binding('constants');
|
263 | }
|
264 | return fsB;
|
265 | }
|
266 |
|
267 | if (typeof fdR !== 'string') { return; }
|
268 | fdR = null;
|
269 |
|
270 | if (IS_WIN) {
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 | verNum = (function(ver) {
|
277 | var nums = ver.replace(/^\D+/, '').split('.');
|
278 | var verNum = 0;
|
279 | if ((nums[0] = +nums[0])) { verNum += nums[0] * 10000; }
|
280 | if ((nums[1] = +nums[1])) { verNum += nums[1] * 100; }
|
281 | if ((nums[2] = +nums[2])) { verNum += nums[2]; }
|
282 | return verNum;
|
283 | })(process.version);
|
284 | if (!(verNum >= 20302 && verNum < 40204 || verNum >= 50000 && verNum < 50100 || verNum >= 50600 && verNum < 60200) &&
|
285 | process.stdin.isTTY) {
|
286 | process.stdin.pause();
|
287 | fdR = process.stdin.fd;
|
288 | ttyR = process.stdin._handle;
|
289 | } else {
|
290 | try {
|
291 |
|
292 |
|
293 | fdR = getFsB().open('CONIN$', constants.O_RDWR, parseInt('0666', 8));
|
294 | ttyR = new TTY(fdR, true);
|
295 | } catch (e) { }
|
296 | }
|
297 |
|
298 | if (process.stdout.isTTY) {
|
299 | fdW = process.stdout.fd;
|
300 | } else {
|
301 | try {
|
302 | fdW = fs.openSync('\\\\.\\CON', 'w');
|
303 | } catch (e) { }
|
304 | if (typeof fdW !== 'number') {
|
305 | try {
|
306 | fdW = getFsB().open('CONOUT$', constants.O_RDWR, parseInt('0666', 8));
|
307 | } catch (e) { }
|
308 | }
|
309 | }
|
310 |
|
311 | } else {
|
312 | if (process.stdin.isTTY) {
|
313 | process.stdin.pause();
|
314 | try {
|
315 | fdR = fs.openSync('/dev/tty', 'r');
|
316 | ttyR = process.stdin._handle;
|
317 | } catch (e) { }
|
318 | } else {
|
319 |
|
320 | try {
|
321 | fdR = fs.openSync('/dev/tty', 'r');
|
322 | ttyR = new TTY(fdR, false);
|
323 | } catch (e) { }
|
324 | }
|
325 |
|
326 | if (process.stdout.isTTY) {
|
327 | fdW = process.stdout.fd;
|
328 | } else {
|
329 | try {
|
330 | fdW = fs.openSync('/dev/tty', 'w');
|
331 | } catch (e) { }
|
332 | }
|
333 | }
|
334 | })();
|
335 |
|
336 | (function() {
|
337 | var atEol, limit,
|
338 | isCooked = !options.hideEchoBack && !options.keyIn,
|
339 | buffer, reqSize, readSize, chunk, line;
|
340 |
|
341 |
|
342 | function setRawMode(mode) {
|
343 | if (mode === isRawMode) { return true; }
|
344 | if (ttyR.setRawMode(mode) !== 0) { return false; }
|
345 | isRawMode = mode;
|
346 | return true;
|
347 | }
|
348 |
|
349 | if (_DBG_useExt || !ttyR ||
|
350 | typeof fdW !== 'number' && (options.display || !isCooked)) {
|
351 | input = tryExt();
|
352 | return;
|
353 | }
|
354 |
|
355 | if (options.display) {
|
356 | fs.writeSync(fdW, options.display);
|
357 | options.display = '';
|
358 | }
|
359 | if (options.displayOnly) { return; }
|
360 |
|
361 | if (!setRawMode(!isCooked)) {
|
362 | input = tryExt();
|
363 | return;
|
364 | }
|
365 |
|
366 |
|
367 |
|
368 | if (Buffer.alloc) {
|
369 | buffer = Buffer.alloc((reqSize = options.keyIn ? 1 : options.bufferSize));
|
370 | } else {
|
371 | buffer = new Buffer((reqSize = options.keyIn ? 1 : options.bufferSize));
|
372 | }
|
373 |
|
374 | if (options.keyIn && options.limit) {
|
375 | limit = new RegExp('[^' + options.limit + ']',
|
376 | 'g' + (options.caseSensitive ? '' : 'i'));
|
377 | }
|
378 |
|
379 | while (true) {
|
380 | readSize = 0;
|
381 | try {
|
382 | readSize = fs.readSync(fdR, buffer, 0, reqSize);
|
383 | } catch (e) {
|
384 | if (e.code !== 'EOF') {
|
385 | setRawMode(false);
|
386 | input += tryExt();
|
387 | return;
|
388 | }
|
389 | }
|
390 | chunk = readSize > 0 ? buffer.toString(options.encoding, 0, readSize) : '\n';
|
391 |
|
392 | if (chunk && typeof (line = (chunk.match(/^(.*?)[\r\n]/) || [])[1]) === 'string') {
|
393 | chunk = line;
|
394 | atEol = true;
|
395 | }
|
396 |
|
397 |
|
398 | if (chunk) { chunk = chunk.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ''); }
|
399 | if (chunk && limit) { chunk = chunk.replace(limit, ''); }
|
400 |
|
401 | if (chunk) {
|
402 | if (!isCooked) {
|
403 | if (!options.hideEchoBack) {
|
404 | fs.writeSync(fdW, chunk);
|
405 | } else if (options.mask) {
|
406 | fs.writeSync(fdW, (new Array(chunk.length + 1)).join(options.mask));
|
407 | }
|
408 | }
|
409 | input += chunk;
|
410 | }
|
411 |
|
412 | if (!options.keyIn && atEol ||
|
413 | options.keyIn && input.length >= reqSize) { break; }
|
414 | }
|
415 |
|
416 | if (!isCooked && !silent) { fs.writeSync(fdW, '\n'); }
|
417 | setRawMode(false);
|
418 | })();
|
419 |
|
420 | if (options.print && !silent) {
|
421 | options.print(displaySave + (options.displayOnly ? '' :
|
422 | (options.hideEchoBack ? (new Array(input.length + 1)).join(options.mask)
|
423 | : input) + '\n'),
|
424 | options.encoding);
|
425 | }
|
426 |
|
427 | return options.displayOnly ? '' :
|
428 | (lastInput = options.keepWhitespace || options.keyIn ? input : input.trim());
|
429 | }
|
430 |
|
431 | function flattenArray(array, validator) {
|
432 | var flatArray = [];
|
433 | function _flattenArray(array) {
|
434 | if (array == null) {
|
435 | return;
|
436 | } else if (Array.isArray(array)) {
|
437 | array.forEach(_flattenArray);
|
438 | } else if (!validator || validator(array)) {
|
439 | flatArray.push(array);
|
440 | }
|
441 | }
|
442 | _flattenArray(array);
|
443 | return flatArray;
|
444 | }
|
445 |
|
446 | function escapePattern(pattern) {
|
447 | return pattern.replace(/[\x00-\x7f]/g,
|
448 | function(s) { return '\\x' + ('00' + s.charCodeAt().toString(16)).substr(-2); });
|
449 | }
|
450 |
|
451 |
|
452 |
|
453 |
|
454 | function margeOptions() {
|
455 | var optionsList = Array.prototype.slice.call(arguments),
|
456 | optionNames, fromDefault;
|
457 |
|
458 | if (optionsList.length && typeof optionsList[0] === 'boolean') {
|
459 | fromDefault = optionsList.shift();
|
460 | if (fromDefault) {
|
461 | optionNames = Object.keys(defaultOptions);
|
462 | optionsList.unshift(defaultOptions);
|
463 | }
|
464 | }
|
465 |
|
466 | return optionsList.reduce(function(options, optionsPart) {
|
467 | if (optionsPart == null) { return options; }
|
468 |
|
469 |
|
470 | if (optionsPart.hasOwnProperty('noEchoBack') &&
|
471 | !optionsPart.hasOwnProperty('hideEchoBack')) {
|
472 | optionsPart.hideEchoBack = optionsPart.noEchoBack;
|
473 | delete optionsPart.noEchoBack;
|
474 | }
|
475 | if (optionsPart.hasOwnProperty('noTrim') &&
|
476 | !optionsPart.hasOwnProperty('keepWhitespace')) {
|
477 | optionsPart.keepWhitespace = optionsPart.noTrim;
|
478 | delete optionsPart.noTrim;
|
479 | }
|
480 |
|
481 |
|
482 | if (!fromDefault) { optionNames = Object.keys(optionsPart); }
|
483 | optionNames.forEach(function(optionName) {
|
484 | var value;
|
485 | if (!optionsPart.hasOwnProperty(optionName)) { return; }
|
486 | value = optionsPart[optionName];
|
487 | switch (optionName) {
|
488 |
|
489 |
|
490 | case 'mask':
|
491 | case 'limitMessage':
|
492 | case 'defaultInput':
|
493 | case 'encoding':
|
494 | value = value != null ? value + '' : '';
|
495 | if (value && optionName !== 'limitMessage') { value = value.replace(/[\r\n]/g, ''); }
|
496 | options[optionName] = value;
|
497 | break;
|
498 |
|
499 | case 'bufferSize':
|
500 | if (!isNaN(value = parseInt(value, 10)) && typeof value === 'number') {
|
501 | options[optionName] = value;
|
502 | }
|
503 | break;
|
504 |
|
505 | case 'displayOnly':
|
506 | case 'keyIn':
|
507 | case 'hideEchoBack':
|
508 | case 'caseSensitive':
|
509 | case 'keepWhitespace':
|
510 | case 'history':
|
511 | case 'cd':
|
512 | options[optionName] = !!value;
|
513 | break;
|
514 |
|
515 | case 'limit':
|
516 | case 'trueValue':
|
517 | case 'falseValue':
|
518 | options[optionName] = flattenArray(value, function(value) {
|
519 | var type = typeof value;
|
520 | return type === 'string' || type === 'number' ||
|
521 | type === 'function' || value instanceof RegExp;
|
522 | }).map(function(value) {
|
523 | return typeof value === 'string' ? value.replace(/[\r\n]/g, '') : value;
|
524 | });
|
525 | break;
|
526 |
|
527 | case 'print':
|
528 | case 'phContent':
|
529 | case 'preCheck':
|
530 | options[optionName] = typeof value === 'function' ? value : void 0;
|
531 | break;
|
532 |
|
533 | case 'prompt':
|
534 | case 'display':
|
535 | options[optionName] = value != null ? value : '';
|
536 | break;
|
537 |
|
538 | }
|
539 | });
|
540 | return options;
|
541 | }, {});
|
542 | }
|
543 |
|
544 | function isMatched(res, comps, caseSensitive) {
|
545 | return comps.some(function(comp) {
|
546 | var type = typeof comp;
|
547 | return type === 'string' ?
|
548 | (caseSensitive ? res === comp : res.toLowerCase() === comp.toLowerCase()) :
|
549 | type === 'number' ? parseFloat(res) === comp :
|
550 | type === 'function' ? comp(res) :
|
551 | comp instanceof RegExp ? comp.test(res) : false;
|
552 | });
|
553 | }
|
554 |
|
555 | function replaceHomePath(path, expand) {
|
556 | var homePath = pathUtil.normalize(
|
557 | IS_WIN ? (process.env.HOMEDRIVE || '') + (process.env.HOMEPATH || '') :
|
558 | process.env.HOME || '').replace(/[\/\\]+$/, '');
|
559 | path = pathUtil.normalize(path);
|
560 | return expand ? path.replace(/^~(?=\/|\\|$)/, homePath) :
|
561 | path.replace(new RegExp('^' + escapePattern(homePath) +
|
562 | '(?=\\/|\\\\|$)', IS_WIN ? 'i' : ''), '~');
|
563 | }
|
564 |
|
565 | function replacePlaceholder(text, generator) {
|
566 | var PTN_INNER = '(?:\\(([\\s\\S]*?)\\))?(\\w+|.-.)(?:\\(([\\s\\S]*?)\\))?',
|
567 | rePlaceholder = new RegExp('(\\$)?(\\$<' + PTN_INNER + '>)', 'g'),
|
568 | rePlaceholderCompat = new RegExp('(\\$)?(\\$\\{' + PTN_INNER + '\\})', 'g');
|
569 |
|
570 | function getPlaceholderText(s, escape, placeholder, pre, param, post) {
|
571 | var text;
|
572 | return escape || typeof (text = generator(param)) !== 'string' ? placeholder :
|
573 | text ? (pre || '') + text + (post || '') : '';
|
574 | }
|
575 |
|
576 | return text.replace(rePlaceholder, getPlaceholderText)
|
577 | .replace(rePlaceholderCompat, getPlaceholderText);
|
578 | }
|
579 |
|
580 | function array2charlist(array, caseSensitive, collectSymbols) {
|
581 | var values, group = [], groupClass = -1, charCode = 0, symbols = '', suppressed;
|
582 | function addGroup(groups, group) {
|
583 | if (group.length > 3) {
|
584 | groups.push(group[0] + '...' + group[group.length - 1]);
|
585 | suppressed = true;
|
586 | } else if (group.length) {
|
587 | groups = groups.concat(group);
|
588 | }
|
589 | return groups;
|
590 | }
|
591 |
|
592 | values = array.reduce(
|
593 | function(chars, value) { return chars.concat((value + '').split('')); }, [])
|
594 | .reduce(function(groups, curChar) {
|
595 | var curGroupClass, curCharCode;
|
596 | if (!caseSensitive) { curChar = curChar.toLowerCase(); }
|
597 | curGroupClass = /^\d$/.test(curChar) ? 1 :
|
598 | /^[A-Z]$/.test(curChar) ? 2 : /^[a-z]$/.test(curChar) ? 3 : 0;
|
599 | if (collectSymbols && curGroupClass === 0) {
|
600 | symbols += curChar;
|
601 | } else {
|
602 | curCharCode = curChar.charCodeAt(0);
|
603 | if (curGroupClass && curGroupClass === groupClass &&
|
604 | curCharCode === charCode + 1) {
|
605 | group.push(curChar);
|
606 | } else {
|
607 | groups = addGroup(groups, group);
|
608 | group = [curChar];
|
609 | groupClass = curGroupClass;
|
610 | }
|
611 | charCode = curCharCode;
|
612 | }
|
613 | return groups;
|
614 | }, []);
|
615 | values = addGroup(values, group);
|
616 | if (symbols) { values.push(symbols); suppressed = true; }
|
617 | return {values: values, suppressed: suppressed};
|
618 | }
|
619 |
|
620 | function joinChunks(chunks, suppressed) {
|
621 | return chunks.join(chunks.length > 2 ? ', ' : suppressed ? ' / ' : '/');
|
622 | }
|
623 |
|
624 | function getPhContent(param, options) {
|
625 | var text, values, resCharlist = {}, arg;
|
626 | if (options.phContent) {
|
627 | text = options.phContent(param, options);
|
628 | }
|
629 | if (typeof text !== 'string') {
|
630 | switch (param) {
|
631 | case 'hideEchoBack':
|
632 | case 'mask':
|
633 | case 'defaultInput':
|
634 | case 'caseSensitive':
|
635 | case 'keepWhitespace':
|
636 | case 'encoding':
|
637 | case 'bufferSize':
|
638 | case 'history':
|
639 | case 'cd':
|
640 | text = !options.hasOwnProperty(param) ? '' :
|
641 | typeof options[param] === 'boolean' ? (options[param] ? 'on' : 'off') :
|
642 | options[param] + '';
|
643 | break;
|
644 |
|
645 |
|
646 |
|
647 |
|
648 |
|
649 | case 'limit':
|
650 | case 'trueValue':
|
651 | case 'falseValue':
|
652 | values = options[options.hasOwnProperty(param + 'Src') ? param + 'Src' : param];
|
653 | if (options.keyIn) {
|
654 | resCharlist = array2charlist(values, options.caseSensitive);
|
655 | values = resCharlist.values;
|
656 | } else {
|
657 | values = values.filter(function(value) {
|
658 | var type = typeof value;
|
659 | return type === 'string' || type === 'number';
|
660 | });
|
661 | }
|
662 | text = joinChunks(values, resCharlist.suppressed);
|
663 | break;
|
664 | case 'limitCount':
|
665 | case 'limitCountNotZero':
|
666 | text = options[options.hasOwnProperty('limitSrc') ?
|
667 | 'limitSrc' : 'limit'].length;
|
668 | text = text || param !== 'limitCountNotZero' ? text + '' : '';
|
669 | break;
|
670 | case 'lastInput':
|
671 | text = lastInput;
|
672 | break;
|
673 | case 'cwd':
|
674 | case 'CWD':
|
675 | case 'cwdHome':
|
676 | text = process.cwd();
|
677 | if (param === 'CWD') {
|
678 | text = pathUtil.basename(text);
|
679 | } else if (param === 'cwdHome') {
|
680 | text = replaceHomePath(text);
|
681 | }
|
682 | break;
|
683 | case 'date':
|
684 | case 'time':
|
685 | case 'localeDate':
|
686 | case 'localeTime':
|
687 | text = (new Date())['to' +
|
688 | param.replace(/^./, function(str) { return str.toUpperCase(); }) +
|
689 | 'String']();
|
690 | break;
|
691 | default:
|
692 | if (typeof (arg = (param.match(/^history_m(\d+)$/) || [])[1]) === 'string') {
|
693 | text = inputHistory[inputHistory.length - arg] || '';
|
694 | }
|
695 | }
|
696 | }
|
697 | return text;
|
698 | }
|
699 |
|
700 | function getPhCharlist(param) {
|
701 | var matches = /^(.)-(.)$/.exec(param), text = '', from, to, code, step;
|
702 | if (!matches) { return null; }
|
703 | from = matches[1].charCodeAt(0);
|
704 | to = matches[2].charCodeAt(0);
|
705 | step = from < to ? 1 : -1;
|
706 | for (code = from; code !== to + step; code += step) { text += String.fromCharCode(code); }
|
707 | return text;
|
708 | }
|
709 |
|
710 |
|
711 | function parseCl(cl) {
|
712 | var reToken = new RegExp(/(\s*)(?:("|')(.*?)(?:\2|$)|(\S+))/g), matches,
|
713 | taken = '', args = [], part;
|
714 | cl = cl.trim();
|
715 | while ((matches = reToken.exec(cl))) {
|
716 | part = matches[3] || matches[4] || '';
|
717 | if (matches[1]) {
|
718 | args.push(taken);
|
719 | taken = '';
|
720 | }
|
721 | taken += part;
|
722 | }
|
723 | if (taken) { args.push(taken); }
|
724 | return args;
|
725 | }
|
726 |
|
727 | function toBool(res, options) {
|
728 | return (
|
729 | (options.trueValue.length &&
|
730 | isMatched(res, options.trueValue, options.caseSensitive)) ? true :
|
731 | (options.falseValue.length &&
|
732 | isMatched(res, options.falseValue, options.caseSensitive)) ? false : res);
|
733 | }
|
734 |
|
735 | function getValidLine(options) {
|
736 | var res, forceNext, limitMessage,
|
737 | matches, histInput, args, resCheck;
|
738 |
|
739 | function _getPhContent(param) { return getPhContent(param, options); }
|
740 | function addDisplay(text) { options.display += (/[^\r\n]$/.test(options.display) ? '\n' : '') + text; }
|
741 |
|
742 | options.limitSrc = options.limit;
|
743 | options.displaySrc = options.display;
|
744 | options.limit = '';
|
745 | options.display = replacePlaceholder(options.display + '', _getPhContent);
|
746 |
|
747 | while (true) {
|
748 | res = _readlineSync(options);
|
749 | forceNext = false;
|
750 | limitMessage = '';
|
751 |
|
752 | if (options.defaultInput && !res) { res = options.defaultInput; }
|
753 |
|
754 | if (options.history) {
|
755 | if ((matches = /^\s*\!(?:\!|-1)(:p)?\s*$/.exec(res))) {
|
756 | histInput = inputHistory[0] || '';
|
757 | if (matches[1]) {
|
758 | forceNext = true;
|
759 | } else {
|
760 | res = histInput;
|
761 | }
|
762 |
|
763 | addDisplay(histInput + '\n');
|
764 | if (!forceNext) {
|
765 | options.displayOnly = true;
|
766 | _readlineSync(options);
|
767 | options.displayOnly = false;
|
768 | }
|
769 | } else if (res && res !== inputHistory[inputHistory.length - 1]) {
|
770 | inputHistory = [res];
|
771 | }
|
772 | }
|
773 |
|
774 | if (!forceNext && options.cd && res) {
|
775 | args = parseCl(res);
|
776 | switch (args[0].toLowerCase()) {
|
777 | case 'cd':
|
778 | if (args[1]) {
|
779 | try {
|
780 | process.chdir(replaceHomePath(args[1], true));
|
781 | } catch (e) {
|
782 | addDisplay(e + '');
|
783 | }
|
784 | }
|
785 | forceNext = true;
|
786 | break;
|
787 | case 'pwd':
|
788 | addDisplay(process.cwd());
|
789 | forceNext = true;
|
790 | break;
|
791 |
|
792 | }
|
793 | }
|
794 |
|
795 | if (!forceNext && options.preCheck) {
|
796 | resCheck = options.preCheck(res, options);
|
797 | res = resCheck.res;
|
798 | if (resCheck.forceNext) { forceNext = true; }
|
799 | }
|
800 |
|
801 | if (!forceNext) {
|
802 | if (!options.limitSrc.length ||
|
803 | isMatched(res, options.limitSrc, options.caseSensitive)) { break; }
|
804 | if (options.limitMessage) {
|
805 | limitMessage = replacePlaceholder(options.limitMessage, _getPhContent);
|
806 | }
|
807 | }
|
808 |
|
809 | addDisplay((limitMessage ? limitMessage + '\n' : '') +
|
810 | replacePlaceholder(options.displaySrc + '', _getPhContent));
|
811 | }
|
812 | return toBool(res, options);
|
813 | }
|
814 |
|
815 |
|
816 | exports._DBG_set_useExt = function(val) { _DBG_useExt = val; };
|
817 | exports._DBG_set_checkOptions = function(val) { _DBG_checkOptions = val; };
|
818 | exports._DBG_set_checkMethod = function(val) { _DBG_checkMethod = val; };
|
819 | exports._DBG_clearHistory = function() { lastInput = ''; inputHistory = []; };
|
820 |
|
821 |
|
822 |
|
823 | exports.setDefaultOptions = function(options) {
|
824 | defaultOptions = margeOptions(true, options);
|
825 | return margeOptions(true);
|
826 | };
|
827 |
|
828 | exports.question = function(query, options) {
|
829 |
|
830 | return getValidLine(margeOptions(margeOptions(true, options), {
|
831 | display: query
|
832 | }));
|
833 |
|
834 | };
|
835 |
|
836 | exports.prompt = function(options) {
|
837 | var readOptions = margeOptions(true, options);
|
838 | readOptions.display = readOptions.prompt;
|
839 | return getValidLine(readOptions);
|
840 | };
|
841 |
|
842 | exports.keyIn = function(query, options) {
|
843 |
|
844 | var readOptions = margeOptions(margeOptions(true, options), {
|
845 | display: query,
|
846 | keyIn: true,
|
847 | keepWhitespace: true
|
848 | });
|
849 |
|
850 |
|
851 |
|
852 | readOptions.limitSrc = readOptions.limit.filter(function(value) {
|
853 | var type = typeof value;
|
854 | return type === 'string' || type === 'number';
|
855 | })
|
856 | .map(function(text) { return replacePlaceholder(text + '', getPhCharlist); });
|
857 |
|
858 | readOptions.limit = escapePattern(readOptions.limitSrc.join(''));
|
859 |
|
860 | ['trueValue', 'falseValue'].forEach(function(optionName) {
|
861 | readOptions[optionName] = readOptions[optionName].reduce(function(comps, comp) {
|
862 | var type = typeof comp;
|
863 | if (type === 'string' || type === 'number') {
|
864 | comps = comps.concat((comp + '').split(''));
|
865 | } else { comps.push(comp); }
|
866 | return comps;
|
867 | }, []);
|
868 | });
|
869 |
|
870 | readOptions.display = replacePlaceholder(readOptions.display + '',
|
871 | function(param) { return getPhContent(param, readOptions); });
|
872 |
|
873 | return toBool(_readlineSync(readOptions), readOptions);
|
874 | };
|
875 |
|
876 |
|
877 |
|
878 | exports.questionEMail = function(query, options) {
|
879 | if (query == null) { query = 'Input e-mail address :'; }
|
880 |
|
881 | return exports.question(query, margeOptions({
|
882 |
|
883 | hideEchoBack: false,
|
884 |
|
885 | limit: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
|
886 | limitMessage: 'Input valid e-mail address, please.',
|
887 | trueValue: null,
|
888 | falseValue: null
|
889 | }, options, {
|
890 |
|
891 | keepWhitespace: false,
|
892 | cd: false
|
893 | }));
|
894 |
|
895 | };
|
896 |
|
897 | exports.questionNewPassword = function(query, options) {
|
898 |
|
899 | var resCharlist, min, max,
|
900 | readOptions = margeOptions({
|
901 |
|
902 | hideEchoBack: true,
|
903 | mask: '*',
|
904 | limitMessage: 'It can include: $<charlist>\n' +
|
905 | 'And the length must be: $<length>',
|
906 | trueValue: null,
|
907 | falseValue: null,
|
908 | caseSensitive: true
|
909 | }, options, {
|
910 |
|
911 | history: false,
|
912 | cd: false,
|
913 |
|
914 | phContent: function(param) {
|
915 | return param === 'charlist' ? resCharlist.text :
|
916 | param === 'length' ? min + '...' + max : null;
|
917 | }
|
918 | }),
|
919 |
|
920 | charlist, confirmMessage, unmatchMessage,
|
921 | limit, limitMessage, res1, res2;
|
922 |
|
923 | options = options || {};
|
924 |
|
925 | charlist = replacePlaceholder(
|
926 | options.charlist ? options.charlist + '' : '$<!-~>', getPhCharlist);
|
927 | if (isNaN(min = parseInt(options.min, 10)) || typeof min !== 'number') { min = 12; }
|
928 | if (isNaN(max = parseInt(options.max, 10)) || typeof max !== 'number') { max = 24; }
|
929 | limit = new RegExp('^[' + escapePattern(charlist) +
|
930 | ']{' + min + ',' + max + '}$');
|
931 | resCharlist = array2charlist([charlist], readOptions.caseSensitive, true);
|
932 | resCharlist.text = joinChunks(resCharlist.values, resCharlist.suppressed);
|
933 |
|
934 | confirmMessage = options.confirmMessage != null ? options.confirmMessage :
|
935 | 'Reinput a same one to confirm it :';
|
936 | unmatchMessage = options.unmatchMessage != null ? options.unmatchMessage :
|
937 | 'It differs from first one.' +
|
938 | ' Hit only the Enter key if you want to retry from first one.';
|
939 |
|
940 | if (query == null) { query = 'Input new password :'; }
|
941 |
|
942 | limitMessage = readOptions.limitMessage;
|
943 | while (!res2) {
|
944 | readOptions.limit = limit;
|
945 | readOptions.limitMessage = limitMessage;
|
946 | res1 = exports.question(query, readOptions);
|
947 |
|
948 | readOptions.limit = [res1, ''];
|
949 | readOptions.limitMessage = unmatchMessage;
|
950 | res2 = exports.question(confirmMessage, readOptions);
|
951 | }
|
952 |
|
953 | return res1;
|
954 | };
|
955 |
|
956 | function _questionNum(query, options, parser) {
|
957 | var validValue;
|
958 | function getValidValue(value) {
|
959 | validValue = parser(value);
|
960 | return !isNaN(validValue) && typeof validValue === 'number';
|
961 | }
|
962 |
|
963 | exports.question(query, margeOptions({
|
964 |
|
965 | limitMessage: 'Input valid number, please.'
|
966 | }, options, {
|
967 |
|
968 | limit: getValidValue,
|
969 | cd: false
|
970 |
|
971 | }));
|
972 |
|
973 | return validValue;
|
974 | }
|
975 | exports.questionInt = function(query, options) {
|
976 | return _questionNum(query, options, function(value) { return parseInt(value, 10); });
|
977 | };
|
978 | exports.questionFloat = function(query, options) {
|
979 | return _questionNum(query, options, parseFloat);
|
980 | };
|
981 |
|
982 | exports.questionPath = function(query, options) {
|
983 |
|
984 | var validPath, error = '',
|
985 | readOptions = margeOptions({
|
986 |
|
987 | hideEchoBack: false,
|
988 | limitMessage: '$<error(\n)>Input valid path, please.' +
|
989 | '$<( Min:)min>$<( Max:)max>',
|
990 | history: true,
|
991 | cd: true
|
992 | }, options, {
|
993 |
|
994 | keepWhitespace: false,
|
995 | limit: function(value) {
|
996 | var exists, stat, res;
|
997 | value = replaceHomePath(value, true);
|
998 | error = '';
|
999 |
|
1000 | function mkdirParents(dirPath) {
|
1001 | dirPath.split(/\/|\\/).reduce(function(parents, dir) {
|
1002 | var path = pathUtil.resolve((parents += dir + pathUtil.sep));
|
1003 | if (!fs.existsSync(path)) {
|
1004 | fs.mkdirSync(path);
|
1005 | } else if (!fs.statSync(path).isDirectory()) {
|
1006 | throw new Error('Non directory already exists: ' + path);
|
1007 | }
|
1008 | return parents;
|
1009 | }, '');
|
1010 | }
|
1011 |
|
1012 | try {
|
1013 | exists = fs.existsSync(value);
|
1014 | validPath = exists ? fs.realpathSync(value) : pathUtil.resolve(value);
|
1015 |
|
1016 | if (!options.hasOwnProperty('exists') && !exists ||
|
1017 | typeof options.exists === 'boolean' && options.exists !== exists) {
|
1018 | error = (exists ? 'Already exists' : 'No such file or directory') +
|
1019 | ': ' + validPath;
|
1020 | return false;
|
1021 | }
|
1022 | if (!exists && options.create) {
|
1023 | if (options.isDirectory) {
|
1024 | mkdirParents(validPath);
|
1025 | } else {
|
1026 | mkdirParents(pathUtil.dirname(validPath));
|
1027 | fs.closeSync(fs.openSync(validPath, 'w'));
|
1028 | }
|
1029 | validPath = fs.realpathSync(validPath);
|
1030 | }
|
1031 | if (exists && (options.min || options.max ||
|
1032 | options.isFile || options.isDirectory)) {
|
1033 | stat = fs.statSync(validPath);
|
1034 |
|
1035 | if (options.isFile && !stat.isFile()) {
|
1036 | error = 'Not file: ' + validPath;
|
1037 | return false;
|
1038 | } else if (options.isDirectory && !stat.isDirectory()) {
|
1039 | error = 'Not directory: ' + validPath;
|
1040 | return false;
|
1041 | } else if (options.min && stat.size < +options.min ||
|
1042 | options.max && stat.size > +options.max) {
|
1043 | error = 'Size ' + stat.size + ' is out of range: ' + validPath;
|
1044 | return false;
|
1045 | }
|
1046 | }
|
1047 | if (typeof options.validate === 'function' &&
|
1048 | (res = options.validate(validPath)) !== true) {
|
1049 | if (typeof res === 'string') { error = res; }
|
1050 | return false;
|
1051 | }
|
1052 | } catch (e) {
|
1053 | error = e + '';
|
1054 | return false;
|
1055 | }
|
1056 | return true;
|
1057 | },
|
1058 |
|
1059 | phContent: function(param) {
|
1060 | return param === 'error' ? error :
|
1061 | param !== 'min' && param !== 'max' ? null :
|
1062 | options.hasOwnProperty(param) ? options[param] + '' : '';
|
1063 | }
|
1064 | });
|
1065 |
|
1066 |
|
1067 | options = options || {};
|
1068 |
|
1069 | if (query == null) { query = 'Input path (you can "cd" and "pwd") :'; }
|
1070 |
|
1071 | exports.question(query, readOptions);
|
1072 | return validPath;
|
1073 | };
|
1074 |
|
1075 |
|
1076 | function getClHandler(commandHandler, options) {
|
1077 | var clHandler = {}, hIndex = {};
|
1078 | if (typeof commandHandler === 'object') {
|
1079 | Object.keys(commandHandler).forEach(function(cmd) {
|
1080 | if (typeof commandHandler[cmd] === 'function') {
|
1081 | hIndex[options.caseSensitive ? cmd : cmd.toLowerCase()] = commandHandler[cmd];
|
1082 | }
|
1083 | });
|
1084 | clHandler.preCheck = function(res) {
|
1085 | var cmdKey;
|
1086 | clHandler.args = parseCl(res);
|
1087 | cmdKey = clHandler.args[0] || '';
|
1088 | if (!options.caseSensitive) { cmdKey = cmdKey.toLowerCase(); }
|
1089 | clHandler.hRes =
|
1090 | cmdKey !== '_' && hIndex.hasOwnProperty(cmdKey) ?
|
1091 | hIndex[cmdKey].apply(res, clHandler.args.slice(1)) :
|
1092 | hIndex.hasOwnProperty('_') ? hIndex._.apply(res, clHandler.args) : null;
|
1093 | return {res: res, forceNext: false};
|
1094 | };
|
1095 | if (!hIndex.hasOwnProperty('_')) {
|
1096 | clHandler.limit = function() {
|
1097 | var cmdKey = clHandler.args[0] || '';
|
1098 | if (!options.caseSensitive) { cmdKey = cmdKey.toLowerCase(); }
|
1099 | return hIndex.hasOwnProperty(cmdKey);
|
1100 | };
|
1101 | }
|
1102 | } else {
|
1103 | clHandler.preCheck = function(res) {
|
1104 | clHandler.args = parseCl(res);
|
1105 | clHandler.hRes = typeof commandHandler === 'function' ?
|
1106 | commandHandler.apply(res, clHandler.args) : true;
|
1107 | return {res: res, forceNext: false};
|
1108 | };
|
1109 | }
|
1110 | return clHandler;
|
1111 | }
|
1112 |
|
1113 | exports.promptCL = function(commandHandler, options) {
|
1114 |
|
1115 | var readOptions = margeOptions({
|
1116 |
|
1117 | hideEchoBack: false,
|
1118 | limitMessage: 'Requested command is not available.',
|
1119 | caseSensitive: false,
|
1120 | history: true
|
1121 | }, options),
|
1122 |
|
1123 |
|
1124 |
|
1125 | clHandler = getClHandler(commandHandler, readOptions);
|
1126 |
|
1127 | readOptions.limit = clHandler.limit;
|
1128 | readOptions.preCheck = clHandler.preCheck;
|
1129 | exports.prompt(readOptions);
|
1130 | return clHandler.args;
|
1131 | };
|
1132 |
|
1133 | exports.promptLoop = function(inputHandler, options) {
|
1134 |
|
1135 | var readOptions = margeOptions({
|
1136 |
|
1137 | hideEchoBack: false,
|
1138 | trueValue: null,
|
1139 | falseValue: null,
|
1140 | caseSensitive: false,
|
1141 | history: true
|
1142 | }, options);
|
1143 |
|
1144 | while (true) { if (inputHandler(exports.prompt(readOptions))) { break; } }
|
1145 | return;
|
1146 | };
|
1147 |
|
1148 | exports.promptCLLoop = function(commandHandler, options) {
|
1149 |
|
1150 | var readOptions = margeOptions({
|
1151 |
|
1152 | hideEchoBack: false,
|
1153 | limitMessage: 'Requested command is not available.',
|
1154 | caseSensitive: false,
|
1155 | history: true
|
1156 | }, options),
|
1157 |
|
1158 |
|
1159 |
|
1160 | clHandler = getClHandler(commandHandler, readOptions);
|
1161 |
|
1162 | readOptions.limit = clHandler.limit;
|
1163 | readOptions.preCheck = clHandler.preCheck;
|
1164 | while (true) {
|
1165 | exports.prompt(readOptions);
|
1166 | if (clHandler.hRes) { break; }
|
1167 | }
|
1168 | return;
|
1169 | };
|
1170 |
|
1171 | exports.promptSimShell = function(options) {
|
1172 |
|
1173 | return exports.prompt(margeOptions({
|
1174 |
|
1175 | hideEchoBack: false,
|
1176 | history: true
|
1177 | }, options, {
|
1178 |
|
1179 | prompt: (function() {
|
1180 | return IS_WIN ?
|
1181 | '$<cwd>>' :
|
1182 |
|
1183 | (process.env.USER || '') +
|
1184 | (process.env.HOSTNAME ?
|
1185 | '@' + process.env.HOSTNAME.replace(/\..*$/, '') : '') +
|
1186 | ':$<cwdHome>$ ';
|
1187 | })()
|
1188 | }));
|
1189 |
|
1190 | };
|
1191 |
|
1192 | function _keyInYN(query, options, limit) {
|
1193 | var res;
|
1194 | if (query == null) { query = 'Are you sure? :'; }
|
1195 | if ((!options || options.guide !== false) && (query += '')) {
|
1196 | query = query.replace(/\s*:?\s*$/, '') + ' [y/n] :';
|
1197 | }
|
1198 |
|
1199 | res = exports.keyIn(query, margeOptions(options, {
|
1200 |
|
1201 | hideEchoBack: false,
|
1202 | limit: limit,
|
1203 | trueValue: 'y',
|
1204 | falseValue: 'n',
|
1205 | caseSensitive: false
|
1206 |
|
1207 | }));
|
1208 |
|
1209 |
|
1210 | return typeof res === 'boolean' ? res : '';
|
1211 | }
|
1212 | exports.keyInYN = function(query, options) { return _keyInYN(query, options); };
|
1213 | exports.keyInYNStrict = function(query, options) { return _keyInYN(query, options, 'yn'); };
|
1214 |
|
1215 | exports.keyInPause = function(query, options) {
|
1216 | if (query == null) { query = 'Continue...'; }
|
1217 | if ((!options || options.guide !== false) && (query += '')) {
|
1218 | query = query.replace(/\s+$/, '') + ' (Hit any key)';
|
1219 | }
|
1220 |
|
1221 | exports.keyIn(query, margeOptions({
|
1222 |
|
1223 | limit: null
|
1224 | }, options, {
|
1225 |
|
1226 | hideEchoBack: true,
|
1227 | mask: ''
|
1228 | }));
|
1229 |
|
1230 |
|
1231 | return;
|
1232 | };
|
1233 |
|
1234 | exports.keyInSelect = function(items, query, options) {
|
1235 |
|
1236 | var readOptions = margeOptions({
|
1237 |
|
1238 | hideEchoBack: false
|
1239 | }, options, {
|
1240 |
|
1241 | trueValue: null,
|
1242 | falseValue: null,
|
1243 | caseSensitive: false,
|
1244 |
|
1245 | phContent: function(param) {
|
1246 | return param === 'itemsCount' ? items.length + '' :
|
1247 | param === 'firstItem' ? (items[0] + '').trim() :
|
1248 | param === 'lastItem' ? (items[items.length - 1] + '').trim() : null;
|
1249 | }
|
1250 | }),
|
1251 |
|
1252 | keylist = '', key2i = {}, charCode = 49 , display = '\n';
|
1253 |
|
1254 | if (!Array.isArray(items) || !items.length || items.length > 35) {
|
1255 | throw '`items` must be Array (max length: 35).';
|
1256 | }
|
1257 |
|
1258 | items.forEach(function(item, i) {
|
1259 | var key = String.fromCharCode(charCode);
|
1260 | keylist += key;
|
1261 | key2i[key] = i;
|
1262 | display += '[' + key + '] ' + (item + '').trim() + '\n';
|
1263 | charCode = charCode === 57 ? 97 : charCode + 1;
|
1264 | });
|
1265 | if (!options || options.cancel !== false) {
|
1266 | keylist += '0';
|
1267 | key2i['0'] = -1;
|
1268 | display += '[0] ' +
|
1269 | (options && options.cancel != null && typeof options.cancel !== 'boolean' ?
|
1270 | (options.cancel + '').trim() : 'CANCEL') + '\n';
|
1271 | }
|
1272 | readOptions.limit = keylist;
|
1273 | display += '\n';
|
1274 |
|
1275 | if (query == null) { query = 'Choose one from list :'; }
|
1276 | if ((query += '')) {
|
1277 | if (!options || options.guide !== false) {
|
1278 | query = query.replace(/\s*:?\s*$/, '') + ' [$<limit>] :';
|
1279 | }
|
1280 | display += query;
|
1281 | }
|
1282 |
|
1283 | return key2i[exports.keyIn(display, readOptions).toLowerCase()];
|
1284 | };
|
1285 |
|
1286 |
|
1287 | function _setOption(optionName, args) {
|
1288 | var options;
|
1289 | if (args.length) { options = {}; options[optionName] = args[0]; }
|
1290 | return exports.setDefaultOptions(options)[optionName];
|
1291 | }
|
1292 | exports.setPrint = function() { return _setOption('print', arguments); };
|
1293 | exports.setPrompt = function() { return _setOption('prompt', arguments); };
|
1294 | exports.setEncoding = function() { return _setOption('encoding', arguments); };
|
1295 | exports.setMask = function() { return _setOption('mask', arguments); };
|
1296 | exports.setBufferSize = function() { return _setOption('bufferSize', arguments); };
|