Comparing version 0.8.5 to 0.9.0
@@ -15,7 +15,7 @@ /** | ||
inquirer.Separator = require("./objects/separator"); | ||
inquirer.Separator = require('./objects/separator'); | ||
inquirer.ui = { | ||
BottomBar: require("./ui/bottom-bar"), | ||
Prompt: require("./ui/prompt") | ||
BottomBar: require('./ui/bottom-bar'), | ||
Prompt: require('./ui/prompt') | ||
}; | ||
@@ -27,5 +27,5 @@ | ||
inquirer.createPromptModule = function () { | ||
var promptModule = function( questions, allDone ) { | ||
var ui = new inquirer.ui.Prompt( promptModule.prompts ); | ||
ui.run( questions, allDone ); | ||
var promptModule = function (questions, allDone) { | ||
var ui = new inquirer.ui.Prompt(promptModule.prompts); | ||
ui.run(questions, allDone); | ||
return ui; | ||
@@ -42,4 +42,4 @@ }; | ||
promptModule.registerPrompt = function( name, prompt ) { | ||
promptModule.prompts[ name ] = prompt; | ||
promptModule.registerPrompt = function (name, prompt) { | ||
promptModule.prompts[name] = prompt; | ||
return this; | ||
@@ -53,9 +53,9 @@ }; | ||
promptModule.restoreDefaultPrompts = function () { | ||
this.registerPrompt( "list", require("./prompts/list")); | ||
this.registerPrompt( "input", require("./prompts/input")); | ||
this.registerPrompt( "confirm", require("./prompts/confirm")); | ||
this.registerPrompt( "rawlist", require("./prompts/rawlist")); | ||
this.registerPrompt( "expand", require("./prompts/expand")); | ||
this.registerPrompt( "checkbox", require("./prompts/checkbox")); | ||
this.registerPrompt( "password", require("./prompts/password")); | ||
this.registerPrompt('list', require('./prompts/list')); | ||
this.registerPrompt('input', require('./prompts/input')); | ||
this.registerPrompt('confirm', require('./prompts/confirm')); | ||
this.registerPrompt('rawlist', require('./prompts/rawlist')); | ||
this.registerPrompt('expand', require('./prompts/expand')); | ||
this.registerPrompt('checkbox', require('./prompts/checkbox')); | ||
this.registerPrompt('password', require('./prompts/password')); | ||
}; | ||
@@ -78,7 +78,7 @@ | ||
// Expose helper functions on the top level for easiest usage by common users | ||
inquirer.registerPrompt = function( name, prompt ) { | ||
inquirer.prompt.registerPrompt( name, prompt ); | ||
inquirer.registerPrompt = function (name, prompt) { | ||
inquirer.prompt.registerPrompt(name, prompt); | ||
}; | ||
inquirer.restoreDefaultPrompts = function() { | ||
inquirer.restoreDefaultPrompts = function () { | ||
inquirer.prompt.restoreDefaultPrompts(); | ||
}; |
@@ -1,17 +0,8 @@ | ||
/** | ||
* Choice object | ||
* Normalize input as choice object | ||
*/ | ||
'use strict'; | ||
var _ = require('lodash'); | ||
var _ = require("lodash"); | ||
/** | ||
* Module exports | ||
*/ | ||
module.exports = Choice; | ||
/** | ||
* Choice object | ||
* Normalize input as choice object | ||
* @constructor | ||
@@ -22,24 +13,23 @@ * @param {String|Object} val Choice value. If an object is passed, it should contains | ||
function Choice( val, answers ) { | ||
var Choice = module.exports = function (val, answers) { | ||
// Don't process Choice and Separator object | ||
if ( val instanceof Choice || val.type === "separator" ) { | ||
if (val instanceof Choice || val.type === 'separator') { | ||
return val; | ||
} | ||
if ( _.isString(val) ) { | ||
if (_.isString(val)) { | ||
this.name = val; | ||
this.value = val; | ||
} else { | ||
_.extend( this, val, { | ||
_.extend(this, val, { | ||
name: val.name || val.value, | ||
value: val.hasOwnProperty("value") ? val.value : val.name | ||
value: val.hasOwnProperty('value') ? val.value : val.name | ||
}); | ||
} | ||
if ( _.isFunction(val.disabled) ) { | ||
this.disabled = val.disabled( answers ); | ||
if (_.isFunction(val.disabled)) { | ||
this.disabled = val.disabled(answers); | ||
} else { | ||
this.disabled = val.disabled; | ||
} | ||
} | ||
}; |
@@ -1,21 +0,12 @@ | ||
/** | ||
* Choices object | ||
* Collection of multiple `choice` object | ||
*/ | ||
'use strict'; | ||
var assert = require('assert'); | ||
var _ = require('lodash'); | ||
var chalk = require('chalk'); | ||
var Separator = require('./separator'); | ||
var Choice = require('./choice'); | ||
var _ = require("lodash"); | ||
var chalk = require("chalk"); | ||
var Separator = require("./separator"); | ||
var Choice = require("./choice"); | ||
/** | ||
* Module exports | ||
*/ | ||
module.exports = Choices; | ||
/** | ||
* Choices collection | ||
* Collection of multiple `choice` object | ||
* @constructor | ||
@@ -25,6 +16,6 @@ * @param {Array} choices All `choice` to keep in the collection | ||
function Choices( choices, answers ) { | ||
this.choices = _.map( choices, function( val ) { | ||
if ( val.type === "separator" ) { | ||
if(!(val instanceof Separator)){ | ||
var Choices = module.exports = function (choices, answers) { | ||
this.choices = choices.map(function (val) { | ||
if (val.type === 'separator') { | ||
if (!(val instanceof Separator)) { | ||
val = new Separator(val.line); | ||
@@ -34,3 +25,3 @@ } | ||
} | ||
return new Choice( val, answers ); | ||
return new Choice(val, answers); | ||
}); | ||
@@ -40,11 +31,11 @@ | ||
.filter(Separator.exclude) | ||
.filter(function( item ) { | ||
.filter(function (item) { | ||
return !item.disabled; | ||
}); | ||
Object.defineProperty( this, "length", { | ||
get: function() { | ||
Object.defineProperty(this, 'length', { | ||
get: function () { | ||
return this.choices.length; | ||
}, | ||
set: function( val ) { | ||
set: function (val) { | ||
this.choices.length = val; | ||
@@ -54,17 +45,13 @@ } | ||
Object.defineProperty( this, "realLength", { | ||
get: function() { | ||
Object.defineProperty(this, 'realLength', { | ||
get: function () { | ||
return this.realChoices.length; | ||
}, | ||
set: function() { | ||
throw new Error("Cannot set `realLength` of a Choices collection"); | ||
set: function () { | ||
throw new Error('Cannot set `realLength` of a Choices collection'); | ||
} | ||
}); | ||
}; | ||
// Set pagination state | ||
this.pointer = 0; | ||
this.lastIndex = 0; | ||
} | ||
/** | ||
@@ -76,7 +63,5 @@ * Get a valid choice from the collection | ||
Choices.prototype.getChoice = function( selector ) { | ||
if ( _.isNumber(selector) ) { | ||
return this.realChoices[ selector ]; | ||
} | ||
return undefined; | ||
Choices.prototype.getChoice = function (selector) { | ||
assert(_.isNumber(selector)); | ||
return this.realChoices[selector]; | ||
}; | ||
@@ -91,7 +76,5 @@ | ||
Choices.prototype.get = function( selector ) { | ||
if ( _.isNumber(selector) ) { | ||
return this.choices[ selector ]; | ||
} | ||
return undefined; | ||
Choices.prototype.get = function (selector) { | ||
assert(_.isNumber(selector)); | ||
return this.choices[selector]; | ||
}; | ||
@@ -106,4 +89,4 @@ | ||
Choices.prototype.where = function( whereClause ) { | ||
return _.where( this.realChoices, whereClause ); | ||
Choices.prototype.where = function (whereClause) { | ||
return _.where(this.realChoices, whereClause); | ||
}; | ||
@@ -118,71 +101,19 @@ | ||
Choices.prototype.pluck = function( propertyName ) { | ||
return _.pluck( this.realChoices, propertyName ); | ||
Choices.prototype.pluck = function (propertyName) { | ||
return _.pluck(this.realChoices, propertyName); | ||
}; | ||
// Propagate usual Array methods | ||
Choices.prototype.forEach = function() { | ||
return this.choices.forEach.apply( this.choices, arguments ); | ||
// Expose usual Array methods | ||
Choices.prototype.forEach = function () { | ||
return this.choices.forEach.apply(this.choices, arguments); | ||
}; | ||
Choices.prototype.filter = function() { | ||
return this.choices.filter.apply( this.choices, arguments ); | ||
Choices.prototype.filter = function () { | ||
return this.choices.filter.apply(this.choices, arguments); | ||
}; | ||
Choices.prototype.push = function() { | ||
var objs = _.map( arguments, function( val ) { return new Choice( val ); }); | ||
this.choices.push.apply( this.choices, objs ); | ||
Choices.prototype.push = function () { | ||
var objs = _.map(arguments, function (val) { return new Choice(val); }); | ||
this.choices.push.apply(this.choices, objs); | ||
this.realChoices = this.choices.filter(Separator.exclude); | ||
return this.choices; | ||
}; | ||
/** | ||
* Render the choices as formatted string | ||
* @return {String} formatted content | ||
*/ | ||
Choices.prototype.render = function() { | ||
return this.renderingMethod.apply( this, arguments ); | ||
}; | ||
/** | ||
* Set the rendering method | ||
* @param {Function} render Function to be use when rendering | ||
*/ | ||
Choices.prototype.setRender = function( render ) { | ||
this.renderingMethod = (this.choices.length > 9) ? this.paginateOutput(render) : render; | ||
}; | ||
/** | ||
* Paginate the output of a render function | ||
* @param {Function} render Render function whose content must be paginated | ||
* @return {Function} Wrapped render function | ||
*/ | ||
Choices.prototype.paginateOutput = function( render ) { | ||
var pageSize = 7; | ||
return function( active ) { | ||
var output = render.apply( this, arguments ); | ||
var lines = output.split("\n"); | ||
// Make sure there's enough line to paginate | ||
if ( lines.length <= pageSize ) return output; | ||
// Move the pointer only when the user go down and limit it to 3 | ||
if ( this.pointer < 3 && this.lastIndex < active && active - this.lastIndex < 9 ) { | ||
this.pointer = Math.min( 3, this.pointer + active - this.lastIndex); | ||
} | ||
this.lastIndex = active; | ||
// Duplicate the lines so it give an infinite list look | ||
var infinite = _.flatten([ lines, lines, lines ]); | ||
var topIndex = Math.max( 0, active + lines.length - this.pointer ); | ||
var section = infinite.splice( topIndex, pageSize ).join("\n"); | ||
return section + "\n" + chalk.dim("(Move up and down to reveal more choices)"); | ||
}.bind(this); | ||
}; |
@@ -1,19 +0,9 @@ | ||
/** | ||
* Separator object | ||
* Used to space/separate choices group | ||
*/ | ||
'use strict'; | ||
var chalk = require('chalk'); | ||
var figures = require('figures'); | ||
var chalk = require("chalk"); | ||
var figures = require("figures"); | ||
/** | ||
* Module exports | ||
*/ | ||
module.exports = Separator; | ||
/** | ||
* Separator object | ||
* Used to space/separate choices group | ||
* @constructor | ||
@@ -23,8 +13,7 @@ * @param {String} line Separation line content (facultative) | ||
function Separator( line ) { | ||
this.type = "separator"; | ||
var Separator = module.exports = function (line) { | ||
this.type = 'separator'; | ||
this.line = chalk.dim(line || new Array(15).join(figures.line)); | ||
} | ||
}; | ||
/** | ||
@@ -36,7 +25,6 @@ * Helper function returning false if object is a separator | ||
Separator.exclude = function( obj ) { | ||
return obj.type !== "separator"; | ||
Separator.exclude = function (obj) { | ||
return obj.type !== 'separator'; | ||
}; | ||
/** | ||
@@ -47,4 +35,4 @@ * Stringify separator | ||
Separator.prototype.toString = function() { | ||
Separator.prototype.toString = function () { | ||
return this.line; | ||
}; |
@@ -6,36 +6,26 @@ /** | ||
var rx = require("rx"); | ||
var _ = require("lodash"); | ||
var chalk = require("chalk"); | ||
var ansiRegex = require("ansi-regex"); | ||
var readline = require("readline"); | ||
var cliWidth = require("cli-width"); | ||
var utils = require("../utils/utils"); | ||
var Choices = require("../objects/choices"); | ||
var tty = require("../utils/tty"); | ||
var rx = require('rx-lite'); | ||
var _ = require('lodash'); | ||
var chalk = require('chalk'); | ||
var ansiRegex = require('ansi-regex'); | ||
var readline = require('readline'); | ||
var cliWidth = require('cli-width'); | ||
var runAsync = require('run-async'); | ||
var Choices = require('../objects/choices'); | ||
var ScreenManager = require('../utils/screen-manager'); | ||
/** | ||
* Module exports | ||
*/ | ||
module.exports = Prompt; | ||
var Prompt = module.exports = function (question, rl, answers) { | ||
/** | ||
* Prompt constructor | ||
*/ | ||
function Prompt( question, rl, answers ) { | ||
// Setup instance defaults property | ||
_.assign( this, { | ||
height : 0, | ||
status : "pending" | ||
_.assign(this, { | ||
answers: answers, | ||
status : 'pending' | ||
}); | ||
// Set defaults prompt options | ||
this.opt = _.defaults( _.clone(question), { | ||
validate: function() { return true; }, | ||
filter: function( val ) { return val; }, | ||
when: function() { return true; } | ||
this.opt = _.defaults(_.clone(question), { | ||
validate: function () { return true; }, | ||
filter: function (val) { return val; }, | ||
when: function () { return true; } | ||
}); | ||
@@ -45,21 +35,18 @@ | ||
if (!this.opt.message) { | ||
this.throwParamError("message"); | ||
this.throwParamError('message'); | ||
} | ||
if (!this.opt.name) { | ||
this.throwParamError("name"); | ||
this.throwParamError('name'); | ||
} | ||
// Normalize choices | ||
if ( _.isArray(this.opt.choices) ) { | ||
this.opt.choices = new Choices( this.opt.choices, answers ); | ||
if (Array.isArray(this.opt.choices)) { | ||
this.opt.choices = new Choices(this.opt.choices, answers); | ||
} | ||
this.rl = rl; | ||
this.screen = new ScreenManager(this.rl); | ||
}; | ||
return this; | ||
} | ||
_.extend( Prompt.prototype, tty ); | ||
/** | ||
@@ -72,11 +59,9 @@ * Start the Inquiry session and manage output value filtering | ||
Prompt.prototype.run = function( cb ) { | ||
var self = this; | ||
this._run(function( value ) { | ||
self.filter( value, cb ); | ||
}); | ||
return this; | ||
this._run(function (value) { | ||
this.filter(value, cb); | ||
}.bind(this)); | ||
}; | ||
// default noop (this one should be overwritten in prompts) | ||
Prompt.prototype._run = function( cb ) { cb(); }; | ||
Prompt.prototype._run = function (cb) { cb(); }; | ||
@@ -90,45 +75,7 @@ | ||
Prompt.prototype.throwParamError = function( name ) { | ||
throw new Error("You must provide a `" + name + "` parameter"); | ||
Prompt.prototype.throwParamError = function (name) { | ||
throw new Error('You must provide a `' + name + '` parameter'); | ||
}; | ||
/** | ||
* Write error message | ||
* @param {String} Error Error message | ||
* @return {Prompt} Self | ||
*/ | ||
Prompt.prototype.error = function( error ) { | ||
readline.moveCursor( this.rl.output, -cliWidth(), 0 ); | ||
readline.clearLine( this.rl.output, 0 ); | ||
var errMsg = chalk.red(">> ") + | ||
(error || "Please enter a valid value"); | ||
this.write( errMsg ); | ||
return this.up(); | ||
}; | ||
/** | ||
* Write hint message | ||
* @param {String} Hint Hint message | ||
* @return {Prompt} Self | ||
*/ | ||
Prompt.prototype.hint = function( hint ) { | ||
readline.moveCursor( this.rl.output, -cliWidth(), 0 ); | ||
readline.clearLine( this.rl.output, 0 ); | ||
if ( hint.length ) { | ||
var hintMsg = chalk.cyan(">> ") + hint; | ||
this.write( hintMsg ); | ||
} | ||
return this.up(); | ||
}; | ||
/** | ||
* Validate a given input | ||
@@ -141,4 +88,4 @@ * @param {String} value Input string | ||
Prompt.prototype.validate = function( input, cb ) { | ||
utils.runAsync( this.opt.validate, cb, input ); | ||
Prompt.prototype.validate = function (input, cb) { | ||
runAsync(this.opt.validate, cb, input); | ||
}; | ||
@@ -151,11 +98,10 @@ | ||
*/ | ||
Prompt.prototype.handleSubmitEvents = function( submit ) { | ||
Prompt.prototype.handleSubmitEvents = function (submit) { | ||
var self = this; | ||
var opt = this.opt; | ||
var validation = submit.flatMap(function( value ) { | ||
return rx.Observable.create(function( observer ) { | ||
utils.runAsync( opt.validate, function( isValid ) { | ||
var validation = submit.flatMap(function (value) { | ||
return rx.Observable.create(function (observer) { | ||
runAsync(self.opt.validate, function (isValid) { | ||
observer.onNext({ isValid: isValid, value: self.getCurrentValue(value) }); | ||
observer.onCompleted(); | ||
}, self.getCurrentValue(value) ); | ||
}, self.getCurrentValue(value), self.answers); | ||
}); | ||
@@ -165,7 +111,7 @@ }).share(); | ||
var success = validation | ||
.filter(function( state ) { return state.isValid === true; }) | ||
.filter(function (state) { return state.isValid === true; }) | ||
.take(1); | ||
var error = validation | ||
.filter(function( state ) { return state.isValid !== true; }) | ||
.filter(function (state) { return state.isValid !== true; }) | ||
.takeUntil(success); | ||
@@ -190,7 +136,6 @@ | ||
Prompt.prototype.filter = function( input, cb ) { | ||
utils.runAsync( this.opt.filter, cb, input ); | ||
Prompt.prototype.filter = function (input, cb) { | ||
runAsync(this.opt.filter, cb, input); | ||
}; | ||
/** | ||
@@ -202,8 +147,7 @@ * Return the prompt line prefix | ||
Prompt.prototype.prefix = function( str ) { | ||
str || (str = ""); | ||
return chalk.green("?") + " " + str; | ||
Prompt.prototype.prefix = function (str) { | ||
str || (str = ''); | ||
return chalk.green('?') + ' ' + str; | ||
}; | ||
/** | ||
@@ -215,12 +159,15 @@ * Return the prompt line suffix | ||
var reStrEnd = new RegExp("(?:" + ansiRegex().source + ")$|$"); | ||
var reStrEnd = new RegExp('(?:' + ansiRegex().source + ')$|$'); | ||
Prompt.prototype.suffix = function( str ) { | ||
str || (str = ""); | ||
return (str.length < 1 || /[a-z1-9]$/i.test(chalk.stripColor(str)) ? | ||
// make sure we get the `:` inside the styles | ||
str.replace(reStrEnd, ":$&") : str).trim() + " "; | ||
Prompt.prototype.suffix = function (str) { | ||
str || (str = ''); | ||
// make sure we get the `:` inside the styles | ||
if (str.length < 1 || /[a-z1-9]$/i.test(chalk.stripColor(str))) { | ||
str = str.replace(reStrEnd, ':$&'); | ||
} | ||
return str.trim() + ' '; | ||
}; | ||
/** | ||
@@ -231,9 +178,8 @@ * Generate the prompt question string | ||
Prompt.prototype.getQuestion = function() { | ||
Prompt.prototype.getQuestion = function () { | ||
var message = chalk.green('?') + ' ' + chalk.bold(this.opt.message) + ' '; | ||
var message = _.compose(this.prefix, this.suffix)(chalk.bold(this.opt.message)); | ||
// Append the default if available, and if question isn't answered | ||
if ( this.opt.default != null && this.status !== "answered" ) { | ||
message += chalk.dim("("+ this.opt.default + ") "); | ||
if ( this.opt.default != null && this.status !== 'answered' ) { | ||
message += chalk.dim('('+ this.opt.default + ') '); | ||
} | ||
@@ -240,0 +186,0 @@ |
@@ -8,5 +8,7 @@ /** | ||
var chalk = require("chalk"); | ||
var figures = require("figures"); | ||
var Base = require("./base"); | ||
var utils = require("../utils/utils"); | ||
var observe = require("../utils/events"); | ||
var utils = require("../utils/readline"); | ||
var Paginator = require("../utils/paginator"); | ||
@@ -43,8 +45,6 @@ | ||
this.opt.choices.setRender( renderChoices ); | ||
// Make sure no default is set (so it won't be printed) | ||
this.opt.default = null; | ||
return this; | ||
this.paginator = new Paginator(); | ||
} | ||
@@ -75,8 +75,5 @@ util.inherits( Prompt, Base ); | ||
// Init the prompt | ||
utils.hideCursor(this.rl); | ||
this.render(); | ||
this.hideCursor(); | ||
// Prevent user from writing | ||
this.rl.output.mute(); | ||
return this; | ||
@@ -91,6 +88,6 @@ }; | ||
Prompt.prototype.render = function() { | ||
Prompt.prototype.render = function (error) { | ||
// Render question | ||
var message = this.getQuestion(); | ||
var choicesStr = "\n" + this.opt.choices.render( this.pointer ); | ||
var cursor = 0; | ||
var message = this.getQuestion(); | ||
@@ -103,12 +100,16 @@ if ( this.firstRender ) { | ||
if ( this.status === "answered" ) { | ||
message += chalk.cyan( this.selection.join(", ") ) + "\n"; | ||
message += chalk.cyan( this.selection.join(", ") ); | ||
} else { | ||
message += choicesStr; | ||
var choicesStr = renderChoices(this.opt.choices, this.pointer); | ||
message += "\n" + this.paginator.paginate(choicesStr, this.pointer); | ||
} | ||
if (error) { | ||
message += '\n' + chalk.red('>> ') + error; | ||
cursor++; | ||
} | ||
this.firstRender = false; | ||
utils.writeMessage( this, message ); | ||
return this; | ||
this.screen.render(message, { cursor: cursor }); | ||
}; | ||
@@ -122,4 +123,2 @@ | ||
Prompt.prototype.onEnd = function( state ) { | ||
this.rl.output.unmute(); | ||
this.showCursor(); | ||
@@ -129,4 +128,6 @@ this.status = "answered"; | ||
// Rerender prompt (and clean subline error) | ||
this.down().clean(1).render(); | ||
this.render(); | ||
this.screen.done(); | ||
utils.showCursor(this.rl); | ||
this.done( state.value ); | ||
@@ -136,8 +137,3 @@ }; | ||
Prompt.prototype.onError = function ( state ) { | ||
this.rl.output.unmute(); | ||
this.showCursor(); | ||
this.down().error( state.isValid ).clean().render(); | ||
this.hideCursor(); | ||
this.rl.output.mute(); | ||
this.render(state.isValid); | ||
}; | ||
@@ -154,75 +150,25 @@ | ||
/** | ||
* When user press a key | ||
*/ | ||
Prompt.prototype.onKeypress = function( s, key ) { | ||
// Only process up, down, space, j, k and 1-9 keys | ||
var keyWhitelist = [ "up", "down", "space", "j", "k" ]; | ||
if ( key && !_.contains(keyWhitelist, key.name) ) return; | ||
if ( key && (key.name === "space" || key.name === "j" || key.name === "k") ) s = undefined; | ||
if ( s && "123456789".indexOf(s) < 0 ) return; | ||
Prompt.prototype.onUpKey = function() { | ||
var len = this.opt.choices.realLength; | ||
this.rl.output.unmute(); | ||
var shortcut = Number(s); | ||
if ( shortcut <= len && shortcut > 0 ) { | ||
this.pointer = shortcut - 1; | ||
key = { name: "space" }; | ||
} | ||
if ( key && key.name === "space" ) { | ||
var checked = this.opt.choices.getChoice(this.pointer).checked; | ||
this.opt.choices.getChoice(this.pointer).checked = !checked; | ||
} else if ( key && (key.name === "up" || key.name === "k") ) { | ||
(this.pointer > 0) ? this.pointer-- : (this.pointer = len - 1); | ||
} else if ( key && (key.name === "down" || key.name === "j") ) { | ||
(this.pointer < len - 1) ? this.pointer++ : (this.pointer = 0); | ||
} | ||
// Rerender | ||
this.clean().render(); | ||
this.rl.output.mute(); | ||
this.pointer = (this.pointer > 0) ? this.pointer - 1 : len - 1; | ||
this.render(); | ||
}; | ||
Prompt.prototype.handleKeypress = function(action) { | ||
this.rl.output.unmute(); | ||
action(); | ||
// Rerender | ||
this.clean().render(); | ||
this.rl.output.mute(); | ||
}; | ||
Prompt.prototype.onUpKey = function() { | ||
this.handleKeypress(function() { | ||
var len = this.opt.choices.realLength; | ||
this.pointer = (this.pointer > 0) ? this.pointer - 1 : len - 1; | ||
}.bind(this)); | ||
}; | ||
Prompt.prototype.onDownKey = function() { | ||
this.handleKeypress(function() { | ||
var len = this.opt.choices.realLength; | ||
this.pointer = (this.pointer < len - 1) ? this.pointer + 1 : 0; | ||
}.bind(this)); | ||
var len = this.opt.choices.realLength; | ||
this.pointer = (this.pointer < len - 1) ? this.pointer + 1 : 0; | ||
this.render(); | ||
}; | ||
Prompt.prototype.onNumberKey = function( input ) { | ||
this.handleKeypress(function() { | ||
if ( input <= this.opt.choices.realLength ) { | ||
this.pointer = input - 1; | ||
this.toggleChoice( this.pointer ); | ||
} | ||
}.bind(this)); | ||
if ( input <= this.opt.choices.realLength ) { | ||
this.pointer = input - 1; | ||
this.toggleChoice( this.pointer ); | ||
} | ||
this.render(); | ||
}; | ||
Prompt.prototype.onSpaceKey = function( input ) { | ||
this.handleKeypress(function() { | ||
this.toggleChoice(this.pointer); | ||
}.bind(this)); | ||
this.toggleChoice(this.pointer); | ||
this.render(); | ||
}; | ||
@@ -242,27 +188,37 @@ | ||
function renderChoices( pointer ) { | ||
var output = ""; | ||
function renderChoices(choices, pointer) { | ||
var output = ''; | ||
var separatorOffset = 0; | ||
this.choices.forEach(function( choice, i ) { | ||
if ( choice.type === "separator" ) { | ||
choices.forEach(function (choice, i) { | ||
if (choice.type === 'separator') { | ||
separatorOffset++; | ||
output += " " + choice + "\n"; | ||
output += ' ' + choice + '\n'; | ||
return; | ||
} | ||
if ( choice.disabled ) { | ||
if (choice.disabled) { | ||
separatorOffset++; | ||
output += " - " + choice.name; | ||
output += " (" + (_.isString(choice.disabled) ? choice.disabled : "Disabled") + ")"; | ||
output += ' - ' + choice.name; | ||
output += ' (' + (_.isString(choice.disabled) ? choice.disabled : 'Disabled') + ')'; | ||
} else { | ||
var isSelected = (i - separatorOffset === pointer); | ||
output += isSelected ? chalk.cyan(utils.getPointer()) : " "; | ||
output += utils.getCheckbox( choice.checked, choice.name ); | ||
output += isSelected ? chalk.cyan(figures.pointer) : ' '; | ||
output += getCheckbox(choice.checked) + ' ' + choice.name; | ||
} | ||
output += "\n"; | ||
}.bind(this)); | ||
output += '\n'; | ||
}); | ||
return output.replace(/\n$/, ""); | ||
return output.replace(/\n$/, ''); | ||
} | ||
/** | ||
* Get the checkbox | ||
* @param {Boolean} checked - add a X or not to the checkbox | ||
* @return {String} Composited checkbox string | ||
*/ | ||
function getCheckbox(checked) { | ||
return checked ? chalk.green(figures.radioOn) : figures.radioOff; | ||
} |
@@ -9,3 +9,2 @@ /** | ||
var Base = require("./base"); | ||
var utils = require("../utils/utils"); | ||
var observe = require("../utils/events"); | ||
@@ -78,6 +77,13 @@ | ||
Prompt.prototype.render = function() { | ||
Prompt.prototype.render = function (answer) { | ||
var message = this.getQuestion(); | ||
utils.writeMessage( this, message ); | ||
if (typeof answer === "boolean") { | ||
message += chalk.cyan(answer ? "Yes" : "No"); | ||
} else { | ||
message += this.rl.line; | ||
} | ||
this.screen.render(message); | ||
return this; | ||
@@ -94,6 +100,5 @@ }; | ||
var output = this.opt.filter( input ); | ||
this.render( output ); | ||
this.clean(1).render(); | ||
this.write( chalk.cyan(output ? "Yes" : "No") + "\n" ); | ||
this.screen.done(); | ||
this.done( input ); // send "input" because the master class will refilter | ||
@@ -107,3 +112,3 @@ }; | ||
Prompt.prototype.onKeypress = function() { | ||
this.clean().render().write( this.rl.line ); | ||
this.render(); | ||
}; |
@@ -9,5 +9,5 @@ /** | ||
var Base = require("./base"); | ||
var utils = require("../utils/utils"); | ||
var Separator = require("../objects/separator"); | ||
var observe = require("../utils/events"); | ||
var Paginator = require("../utils/paginator"); | ||
@@ -42,8 +42,6 @@ | ||
this.opt.choices.setRender( renderChoice ); | ||
// Setup the default string (capitalize the default key) | ||
this.opt.default = this.generateChoicesString( this.opt.choices, this.opt.default ); | ||
return this; | ||
this.paginator = new Paginator(); | ||
} | ||
@@ -79,17 +77,27 @@ util.inherits( Prompt, Base ); | ||
Prompt.prototype.render = function() { | ||
// Render question | ||
Prompt.prototype.render = function (error, hint) { | ||
var cursor = 0; | ||
var message = this.getQuestion(); | ||
if ( this.status === "answered" ) { | ||
message += chalk.cyan( this.selected.name ) + "\n"; | ||
message += chalk.cyan( this.selected.name ); | ||
} else if ( this.status === "expanded" ) { | ||
message += this.opt.choices.render( this.selectedKey ); | ||
var choicesStr = renderChoices(this.opt.choices, this.selectedKey); | ||
message += this.paginator.paginate(choicesStr, this.selectedKey); | ||
message += "\n Answer: "; | ||
} | ||
utils.writeMessage( this, message ); | ||
message += this.rl.line; | ||
return this; | ||
if (error) { | ||
message += '\n' + chalk.red('>> ') + error; | ||
cursor++; | ||
} | ||
if (hint) { | ||
message += '\n' + chalk.cyan('>> ') + hint; | ||
cursor++; | ||
} | ||
this.screen.render(message, { cursor: cursor }); | ||
}; | ||
@@ -139,3 +147,3 @@ | ||
this.status = "expanded"; | ||
this.down().clean(2).render(); | ||
this.render(); | ||
return; | ||
@@ -149,6 +157,7 @@ } | ||
// Re-render prompt | ||
this.down().clean(2).render(); | ||
this.render(); | ||
this.lineObs.dispose(); | ||
this.keypressObs.dispose(); | ||
this.screen.done(); | ||
this.done( this.selected.value ); | ||
@@ -159,6 +168,3 @@ return; | ||
// Input is invalid | ||
this | ||
.error("Please enter a valid command") | ||
.clean() | ||
.render(); | ||
this.render("Please enter a valid command"); | ||
}; | ||
@@ -174,14 +180,7 @@ | ||
var selected = this.opt.choices.where({ key : this.selectedKey })[0]; | ||
this.cacheCursorPos(); | ||
if ( this.status === "expanded" ) { | ||
this.clean().render(); | ||
this.render(); | ||
} else { | ||
this | ||
.down() | ||
.hint( selected ? selected.name : "" ) | ||
.clean() | ||
.render(); | ||
this.render(null, selected ? selected.name : null); | ||
} | ||
this.write( this.rl.line ).restoreCursorPos(); | ||
}; | ||
@@ -246,21 +245,21 @@ | ||
function renderChoice( pointer ) { | ||
var output = ""; | ||
function renderChoices (choices, pointer) { | ||
var output = ''; | ||
this.choices.forEach(function( choice, i ) { | ||
output += "\n "; | ||
choices.forEach(function (choice, i) { | ||
output += '\n '; | ||
if ( choice.type === "separator" ) { | ||
output += " " + choice; | ||
if (choice.type === 'separator') { | ||
output += ' ' + choice; | ||
return; | ||
} | ||
var choiceStr = choice.key + ") " + choice.name; | ||
if ( pointer === choice.key ) { | ||
choiceStr = chalk.cyan( choiceStr ); | ||
var choiceStr = choice.key + ') ' + choice.name; | ||
if (pointer === choice.key) { | ||
choiceStr = chalk.cyan(choiceStr); | ||
} | ||
output += choiceStr; | ||
}.bind(this)); | ||
}); | ||
return output; | ||
} |
@@ -9,3 +9,2 @@ /** | ||
var Base = require("./base"); | ||
var utils = require("../utils/utils"); | ||
var observe = require("../utils/events"); | ||
@@ -62,7 +61,18 @@ | ||
Prompt.prototype.render = function() { | ||
Prompt.prototype.render = function (error) { | ||
var cursor = 0; | ||
var message = this.getQuestion(); | ||
utils.writeMessage( this, message ); | ||
return this; | ||
if (this.status === 'answered') { | ||
message += chalk.cyan(this.answer); | ||
} else { | ||
message += this.rl.line; | ||
} | ||
if (error) { | ||
message += '\n' + chalk.red('>> ') + error; | ||
cursor++; | ||
} | ||
this.screen.render(message, { cursor: cursor }); | ||
}; | ||
@@ -84,10 +94,9 @@ | ||
this.filter( state.value, function( filteredValue ) { | ||
this.answer = filteredValue; | ||
this.status = "answered"; | ||
// Re-render prompt | ||
this.clean(1).render(); | ||
this.render(); | ||
// Render answer | ||
this.write( chalk.cyan(filteredValue) + "\n" ); | ||
this.screen.done(); | ||
this.done( state.value ); | ||
@@ -98,3 +107,3 @@ }.bind(this)); | ||
Prompt.prototype.onError = function( state ) { | ||
this.error( state.isValid ).clean().render(); | ||
this.render(state.isValid); | ||
}; | ||
@@ -107,5 +116,3 @@ | ||
Prompt.prototype.onKeypress = function() { | ||
this.cacheCursorPos(); | ||
this.clean().render().write( this.rl.line ); | ||
this.restoreCursorPos(); | ||
this.render(); | ||
}; |
@@ -8,5 +8,7 @@ /** | ||
var chalk = require("chalk"); | ||
var figures = require("figures"); | ||
var Base = require("./base"); | ||
var utils = require("../utils/utils"); | ||
var observe = require("../utils/events"); | ||
var utils = require("../utils/readline"); | ||
var Paginator = require("../utils/paginator"); | ||
@@ -47,8 +49,6 @@ | ||
this.opt.choices.setRender( listRender ); | ||
// Make sure no default is set (so it won't be printed) | ||
this.opt.default = null; | ||
return this; | ||
this.paginator = new Paginator(); | ||
} | ||
@@ -74,8 +74,5 @@ util.inherits( Prompt, Base ); | ||
// Init the prompt | ||
utils.hideCursor(this.rl); | ||
this.render(); | ||
this.hideCursor(); | ||
// Prevent user from writing | ||
this.rl.output.mute(); | ||
return this; | ||
@@ -91,6 +88,4 @@ }; | ||
Prompt.prototype.render = function() { | ||
// Render question | ||
var message = this.getQuestion(); | ||
var choicesStr = "\n" + this.opt.choices.render( this.selected ); | ||
var message = this.getQuestion(); | ||
@@ -103,5 +98,6 @@ if ( this.firstRender ) { | ||
if ( this.status === "answered" ) { | ||
message += chalk.cyan( this.opt.choices.getChoice(this.selected).name ) + "\n"; | ||
message += chalk.cyan( this.opt.choices.getChoice(this.selected).name ); | ||
} else { | ||
message += choicesStr; | ||
var choicesStr = listRender(this.opt.choices, this.selected ); | ||
message += "\n" + this.paginator.paginate(choicesStr, this.selected); | ||
} | ||
@@ -111,5 +107,3 @@ | ||
utils.writeMessage( this, message ); | ||
return this; | ||
this.screen.render(message); | ||
}; | ||
@@ -127,7 +121,6 @@ | ||
// Rerender prompt | ||
this.rl.output.unmute(); | ||
this.clean().render(); | ||
this.render(); | ||
this.showCursor(); | ||
this.screen.done(); | ||
utils.showCursor(this.rl); | ||
this.done( choice.value ); | ||
@@ -140,34 +133,19 @@ }; | ||
*/ | ||
Prompt.prototype.handleKeypress = function(action) { | ||
this.rl.output.unmute(); | ||
action(); | ||
// Rerender | ||
this.clean().render(); | ||
this.rl.output.mute(); | ||
}; | ||
Prompt.prototype.onUpKey = function() { | ||
this.handleKeypress(function() { | ||
var len = this.opt.choices.realLength; | ||
this.selected = (this.selected > 0) ? this.selected - 1 : len - 1; | ||
}.bind(this)); | ||
var len = this.opt.choices.realLength; | ||
this.selected = (this.selected > 0) ? this.selected - 1 : len - 1; | ||
this.render(); | ||
}; | ||
Prompt.prototype.onDownKey = function() { | ||
this.handleKeypress(function() { | ||
var len = this.opt.choices.realLength; | ||
this.selected = (this.selected < len - 1) ? this.selected + 1 : 0; | ||
}.bind(this)); | ||
var len = this.opt.choices.realLength; | ||
this.selected = (this.selected < len - 1) ? this.selected + 1 : 0; | ||
this.render(); | ||
}; | ||
Prompt.prototype.onNumberKey = function( input ) { | ||
this.handleKeypress(function() { | ||
if ( input <= this.opt.choices.realLength ) { | ||
this.selected = input - 1; | ||
} | ||
}.bind(this)); | ||
if ( input <= this.opt.choices.realLength ) { | ||
this.selected = input - 1; | ||
} | ||
this.render(); | ||
}; | ||
@@ -181,23 +159,22 @@ | ||
*/ | ||
function listRender(choices, pointer) { | ||
var output = ''; | ||
var separatorOffset = 0; | ||
function listRender( pointer ) { | ||
var output = ""; | ||
var separatorOffset = 0; | ||
choices.forEach(function (choice, i) { | ||
if (choice.type === 'separator') { | ||
separatorOffset++; | ||
output += ' ' + choice + '\n'; | ||
return; | ||
} | ||
this.choices.forEach(function( choice, i ) { | ||
if ( choice.type === "separator" ) { | ||
separatorOffset++; | ||
output += " " + choice + "\n"; | ||
return; | ||
} | ||
var isSelected = (i - separatorOffset === pointer); | ||
var line = (isSelected ? figures.pointer + ' ' : ' ') + choice.name; | ||
if (isSelected) { | ||
line = chalk.cyan(line); | ||
} | ||
output += line + ' \n'; | ||
}); | ||
var isSelected = (i - separatorOffset === pointer); | ||
var line = (isSelected ? utils.getPointer() + " " : " ") + choice.name; | ||
if ( isSelected ) { | ||
line = chalk.cyan( line ); | ||
} | ||
output += line + " \n"; | ||
}.bind(this)); | ||
return output.replace(/\n$/, ""); | ||
return output.replace(/\n$/, ''); | ||
} |
@@ -9,5 +9,13 @@ /** | ||
var Base = require("./base"); | ||
var utils = require("../utils/utils"); | ||
var observe = require("../utils/events"); | ||
function mask(input) { | ||
input = String(input); | ||
if (input.length === 0) { | ||
return ''; | ||
} | ||
return new Array(input.length + 1).join('*'); | ||
} | ||
/** | ||
@@ -52,3 +60,2 @@ * Module exports | ||
this.render(); | ||
this.rl.output.mute(); | ||
@@ -64,44 +71,18 @@ return this; | ||
Prompt.prototype.render = function() { | ||
Prompt.prototype.render = function (error) { | ||
var cursor = 0; | ||
var message = this.getQuestion(); | ||
utils.writeMessage( this, message ); | ||
return this; | ||
}; | ||
if (this.status === 'answered') { | ||
message += chalk.cyan(mask(this.answer)); | ||
} else { | ||
message += mask(this.rl.line || ''); | ||
} | ||
/** | ||
* When user press `enter` key | ||
*/ | ||
Prompt.prototype.onSubmit = function( input ) { | ||
var value = input; | ||
if ( !value ) { | ||
var value = this.opt.default != null ? this.opt.default : ""; | ||
if (error) { | ||
message += '\n' + chalk.red('>> ') + error; | ||
cursor++; | ||
} | ||
this.rl.output.unmute(); | ||
this.write("\n"); // manually output the line return as the readline was muted | ||
this.validate( value, function( isValid ) { | ||
if ( isValid === true ) { | ||
this.status = "answered"; | ||
// Re-render prompt | ||
this.clean(1).render(); | ||
// Mask answer | ||
var mask = new Array( value.toString().length + 1 ).join("*"); | ||
// Render answer | ||
this.write( chalk.cyan(mask) + "\n" ); | ||
this.lineObs.dispose(); | ||
this.keypressObs.dispose(); | ||
this.done( value ); | ||
} else { | ||
this.error( isValid ).clean().render(); | ||
this.rl.output.mute(); | ||
} | ||
}.bind(this)); | ||
this.screen.render(message, { cursor: cursor }); | ||
}; | ||
@@ -121,16 +102,9 @@ | ||
Prompt.prototype.onEnd = function( state ) { | ||
this.rl.output.unmute(); | ||
this.write("\n"); // manually output the line return as the readline was muted | ||
this.status = "answered"; | ||
this.answer = state.value; | ||
// Re-render prompt | ||
this.clean(1).render(); | ||
this.render(); | ||
// Mask answer | ||
var mask = new Array( state.value.toString().length + 1 ).join("*"); | ||
// Render answer | ||
this.write( chalk.cyan(mask) + "\n" ); | ||
this.screen.done(); | ||
this.done( state.value ); | ||
@@ -140,7 +114,4 @@ }; | ||
Prompt.prototype.onError = function( state ) { | ||
this.render(state.isValid); | ||
this.rl.output.unmute(); | ||
this.write("\n"); // manually output the line return as the readline was muted | ||
this.error( state.isValid ).clean().render(); | ||
this.rl.output.mute(); | ||
}; | ||
@@ -153,7 +124,3 @@ | ||
Prompt.prototype.onKeypress = function() { | ||
this.rl.output.unmute(); | ||
this.cacheCursorPos().clean().render(); | ||
var mask = new Array( this.rl.line.length + 1 ).join("*"); | ||
this.write(mask).restoreCursorPos(); | ||
this.rl.output.mute(); | ||
this.render(); | ||
}; |
@@ -9,5 +9,5 @@ /** | ||
var Base = require("./base"); | ||
var utils = require("../utils/utils"); | ||
var Separator = require("../objects/separator"); | ||
var observe = require("../utils/events"); | ||
var Paginator = require("../utils/paginator"); | ||
@@ -38,4 +38,2 @@ | ||
this.opt.choices.setRender( renderChoices ); | ||
_.extend(this.opt, { | ||
@@ -55,3 +53,3 @@ validate: function( index ) { | ||
return this; | ||
this.paginator = new Paginator(); | ||
} | ||
@@ -92,17 +90,23 @@ util.inherits( Prompt, Base ); | ||
Prompt.prototype.render = function() { | ||
Prompt.prototype.render = function (error) { | ||
// Render question | ||
var message = this.getQuestion(); | ||
var choicesStr = this.opt.choices.render( this.selected ); | ||
var cursor = 0; | ||
var message = this.getQuestion(); | ||
if ( this.status === "answered" ) { | ||
message += chalk.cyan(this.opt.choices.getChoice(this.selected).name) + "\n"; | ||
message += chalk.cyan(this.opt.choices.getChoice(this.selected).name); | ||
} else { | ||
message += choicesStr; | ||
var choicesStr = renderChoices(this.opt.choices, this.selected); | ||
message += this.paginator.paginate(choicesStr, this.selected); | ||
message += "\n Answer: "; | ||
} | ||
utils.writeMessage( this, message ); | ||
message += this.rl.line; | ||
return this; | ||
if (error) { | ||
message += '\n' + chalk.red('>> ') + error; | ||
cursor++; | ||
} | ||
this.screen.render(message, { cursor: cursor }); | ||
}; | ||
@@ -129,4 +133,5 @@ | ||
// Re-render prompt | ||
this.down().clean(2).render(); | ||
this.render(); | ||
this.screen.done(); | ||
this.done( selectedChoice.value ); | ||
@@ -136,7 +141,3 @@ }; | ||
Prompt.prototype.onError = function() { | ||
this.hasError = true; | ||
this | ||
.error("Please enter a valid index") | ||
.clean() | ||
.render(); | ||
this.render("Please enter a valid index"); | ||
}; | ||
@@ -157,11 +158,3 @@ | ||
this.cacheCursorPos(); | ||
if ( this.hasError ) { | ||
this.down().clean(1); | ||
} else { | ||
this.clean(); | ||
} | ||
this.render().write( this.rl.line ).restoreCursorPos(); | ||
this.render(); | ||
}; | ||
@@ -176,12 +169,12 @@ | ||
function renderChoices( pointer ) { | ||
var output = ""; | ||
function renderChoices(choices, pointer) { | ||
var output = ''; | ||
var separatorOffset = 0; | ||
this.choices.forEach(function( choice, i ) { | ||
output += "\n "; | ||
choices.forEach(function (choice, i) { | ||
output += '\n '; | ||
if ( choice.type === "separator" ) { | ||
if (choice.type === 'separator') { | ||
separatorOffset++; | ||
output += " " + choice; | ||
output += ' ' + choice; | ||
return; | ||
@@ -191,10 +184,10 @@ } | ||
var index = i - separatorOffset; | ||
var display = (index + 1) + ") " + choice.name; | ||
if ( index === pointer ) { | ||
var display = (index + 1) + ') ' + choice.name; | ||
if (index === pointer) { | ||
display = chalk.cyan( display ); | ||
} | ||
output += display; | ||
}.bind(this)); | ||
}); | ||
return output; | ||
} |
@@ -1,21 +0,11 @@ | ||
/** | ||
* Base interface class other can inherits from | ||
*/ | ||
'use strict'; | ||
var _ = require('lodash'); | ||
var readlineFacade = require('readline2'); | ||
var _ = require("lodash"); | ||
var tty = require("../utils/tty"); | ||
var readlineFacade = require("readline2"); | ||
/** | ||
* Module exports | ||
* Base interface class other can inherits from | ||
*/ | ||
module.exports = UI; | ||
/** | ||
* Constructor | ||
*/ | ||
function UI( opt ) { | ||
var UI = module.exports = function (opt) { | ||
// Instantiate the Readline interface | ||
@@ -30,9 +20,8 @@ // @Note: Don't reassign if already present (allow test to override the Stream) | ||
// Make sure new prompt start on a newline when closing | ||
this.rl.on( "SIGINT", this.onForceClose ); | ||
process.on( "exit", this.onForceClose ); | ||
this.rl.on('SIGINT', this.onForceClose); | ||
process.on('exit', this.onForceClose); | ||
// Propagate keypress events directly on the readline | ||
process.stdin.addListener( "keypress", this.onKeypress ); | ||
} | ||
_.extend( UI.prototype, tty ); | ||
process.stdin.addListener('keypress', this.onKeypress); | ||
}; | ||
@@ -45,5 +34,5 @@ | ||
UI.prototype.onForceClose = function() { | ||
UI.prototype.onForceClose = function () { | ||
this.close(); | ||
console.log("\n"); // Line return | ||
console.log('\n'); // Line return | ||
}; | ||
@@ -56,11 +45,11 @@ | ||
UI.prototype.close = function() { | ||
UI.prototype.close = function () { | ||
// Remove events listeners | ||
this.rl.removeListener( "SIGINT", this.onForceClose ); | ||
process.stdin.removeListener( "keypress", this.onKeypress ); | ||
process.removeListener( "exit", this.onForceClose ); | ||
this.rl.removeListener('SIGINT', this.onForceClose); | ||
process.stdin.removeListener('keypress', this.onKeypress); | ||
process.removeListener('exit', this.onForceClose); | ||
// Restore prompt functionnalities | ||
this.rl.output.unmute(); | ||
process.stdout.write("\x1B[?25h"); // show cursor | ||
process.stdout.write('\x1B[?25h'); // show cursor | ||
@@ -79,9 +68,11 @@ // Close the readline | ||
UI.prototype.onKeypress = function( s, key ) { | ||
UI.prototype.onKeypress = function (s, key) { | ||
// Ignore `enter` key (readline `line` event is the only one we care for) | ||
if ( key && (key.name === "enter" || key.name === "return") ) return; | ||
if (key && (key.name === 'enter' || key.name === 'return')) { | ||
return; | ||
} | ||
if ( this.rl ) { | ||
this.rl.emit( "keypress", s, key ); | ||
if (this.rl) { | ||
this.rl.emit( 'keypress', s, key ); | ||
} | ||
}; |
@@ -8,3 +8,4 @@ /** | ||
var Base = require("./baseUI"); | ||
var utils = require("../utils/utils"); | ||
var rlUtils = require("../utils/readline"); | ||
var _ = require("lodash"); | ||
@@ -40,4 +41,3 @@ | ||
Prompt.prototype.render = function() { | ||
utils.writeMessage ( this, this.bottomBar ); | ||
this.write(this.bottomBar); | ||
return this; | ||
@@ -55,3 +55,4 @@ }; | ||
this.bottomBar = bottomBar; | ||
return this.clean().render(); | ||
rlUtils.clearLine(this.rl); | ||
return this.render(); | ||
}; | ||
@@ -66,4 +67,4 @@ | ||
Prompt.prototype.writeLog = function( data ) { | ||
this.clean(); | ||
this.write.call( this, this.enforceLF(data.toString()) ); | ||
rlUtils.clearLine(this.rl); | ||
this.rl.output.write(this.enforceLF(data.toString())); | ||
return this.render(); | ||
@@ -82,1 +83,21 @@ }; | ||
}; | ||
/** | ||
* Helper for writing message in Prompt | ||
* @param {Prompt} prompt - The Prompt object that extends tty | ||
* @param {String} message - The message to be output | ||
*/ | ||
Prompt.prototype.write = function (message) { | ||
var msgLines = message.split(/\n/); | ||
this.height = msgLines.length; | ||
// Write message to screen and setPrompt to control backspace | ||
this.rl.setPrompt( _.last(msgLines) ); | ||
if ( process.stdout.rows === 0 && process.stdout.columns === 0 ) { | ||
/* When it's a tty through serial port there's no terminal info and the render will malfunction, | ||
so we need enforce the cursor to locate to the leftmost position for rendering. */ | ||
rlUtils.left( this.rl, message.length + this.rl.line.length ); | ||
} | ||
this.rl.output.write( message ); | ||
}; |
@@ -1,32 +0,21 @@ | ||
/** | ||
* Base interface class other can inherits from | ||
*/ | ||
'use strict'; | ||
var _ = require('lodash'); | ||
var rx = require('rx-lite'); | ||
var util = require('util'); | ||
var runAsync = require('run-async'); | ||
var utils = require('../utils/utils'); | ||
var Base = require('./baseUI'); | ||
var _ = require("lodash"); | ||
var rx = require("rx"); | ||
var util = require("util"); | ||
var utils = require("../utils/utils"); | ||
var Base = require("./baseUI"); | ||
var inquirer = require("../inquirer"); | ||
/** | ||
* Module exports | ||
* Base interface class other can inherits from | ||
*/ | ||
module.exports = PromptUI; | ||
/** | ||
* Constructor | ||
*/ | ||
function PromptUI( prompts ) { | ||
var PromptUI = module.exports = function (prompts) { | ||
Base.call(this); | ||
this.prompts = prompts; | ||
} | ||
util.inherits( PromptUI, Base ); | ||
}; | ||
util.inherits(PromptUI, Base); | ||
PromptUI.prototype.run = function( questions, allDone ) { | ||
PromptUI.prototype.run = function (questions, allDone) { | ||
// Keep global reference to the answers | ||
@@ -37,3 +26,3 @@ this.answers = {}; | ||
// Make sure questions is an array. | ||
if ( _.isPlainObject(questions) ) { | ||
if (_.isPlainObject(questions)) { | ||
questions = [questions]; | ||
@@ -45,12 +34,10 @@ } | ||
// be using the exact same object in memory. | ||
var obs = _.isArray( questions ) ? rx.Observable.fromArray( questions ) : questions; | ||
var obs = _.isArray(questions) ? rx.Observable.fromArray(questions) : questions; | ||
// Start running the questions | ||
this.process = obs.concatMap( this.processQuestion.bind(this) ); | ||
this.process = obs.concatMap(this.processQuestion.bind(this)); | ||
this.process.forEach( | ||
function() {}, | ||
function( err ) { | ||
throw err; | ||
}, | ||
function (err) { throw err; }, | ||
this.onCompletion.bind(this) | ||
@@ -67,14 +54,14 @@ ); | ||
PromptUI.prototype.onCompletion = function() { | ||
PromptUI.prototype.onCompletion = function () { | ||
this.close(); | ||
if ( _.isFunction(this.completed) ) { | ||
this.completed( this.answers ); | ||
if (_.isFunction(this.completed)) { | ||
this.completed(this.answers); | ||
} | ||
}; | ||
PromptUI.prototype.processQuestion = function( question ) { | ||
return rx.Observable.defer(function() { | ||
var obs = rx.Observable.create(function(obs) { | ||
obs.onNext( question ); | ||
PromptUI.prototype.processQuestion = function (question) { | ||
return rx.Observable.defer(function () { | ||
var obs = rx.Observable.create(function (obs) { | ||
obs.onNext(question); | ||
obs.onCompleted(); | ||
@@ -84,18 +71,18 @@ }); | ||
return obs | ||
.concatMap( this.setDefaultType.bind(this) ) | ||
.concatMap( this.filterIfRunnable.bind(this) ) | ||
.concatMap( utils.fetchAsyncQuestionProperty.bind( null, question, "message", this.answers ) ) | ||
.concatMap( utils.fetchAsyncQuestionProperty.bind( null, question, "default", this.answers ) ) | ||
.concatMap( utils.fetchAsyncQuestionProperty.bind( null, question, "choices", this.answers ) ) | ||
.concatMap( this.fetchAnswer.bind(this) ); | ||
.concatMap(this.setDefaultType.bind(this)) | ||
.concatMap(this.filterIfRunnable.bind(this)) | ||
.concatMap(utils.fetchAsyncQuestionProperty.bind(null, question, 'message', this.answers)) | ||
.concatMap(utils.fetchAsyncQuestionProperty.bind(null, question, 'default', this.answers)) | ||
.concatMap(utils.fetchAsyncQuestionProperty.bind(null, question, 'choices', this.answers)) | ||
.concatMap(this.fetchAnswer.bind(this)); | ||
}.bind(this)); | ||
}; | ||
PromptUI.prototype.fetchAnswer = function( question ) { | ||
PromptUI.prototype.fetchAnswer = function (question) { | ||
var Prompt = this.prompts[question.type]; | ||
var prompt = new Prompt( question, this.rl, this.answers ); | ||
var prompt = new Prompt(question, this.rl, this.answers); | ||
var answers = this.answers; | ||
return utils.createObservableFromAsync(function() { | ||
return utils.createObservableFromAsync(function () { | ||
var done = this.async(); | ||
prompt.run(function( answer ) { | ||
prompt.run(function (answer) { | ||
answers[question.name] = answer; | ||
@@ -107,18 +94,20 @@ done({ name: question.name, answer: answer }); | ||
PromptUI.prototype.setDefaultType = function( question ) { | ||
PromptUI.prototype.setDefaultType = function (question) { | ||
// Default type to input | ||
if ( !this.prompts[question.type] ) { | ||
question.type = "input"; | ||
if (!this.prompts[question.type]) { | ||
question.type = 'input'; | ||
} | ||
return rx.Observable.defer(function() { | ||
return rx.Observable.return( question ); | ||
return rx.Observable.defer(function () { | ||
return rx.Observable.return(question); | ||
}); | ||
}; | ||
PromptUI.prototype.filterIfRunnable = function( question ) { | ||
if ( question.when == null ) return rx.Observable.return(question); | ||
PromptUI.prototype.filterIfRunnable = function (question) { | ||
if (question.when == null) { | ||
return rx.Observable.return(question); | ||
} | ||
var handleResult = function( obs, shouldRun ) { | ||
if ( shouldRun ) { | ||
obs.onNext( question ); | ||
var handleResult = function (obs, shouldRun) { | ||
if (shouldRun) { | ||
obs.onNext(question); | ||
} | ||
@@ -129,14 +118,14 @@ obs.onCompleted(); | ||
var answers = this.answers; | ||
return rx.Observable.defer(function() { | ||
return rx.Observable.create(function( obs ) { | ||
if ( _.isBoolean(question.when) ) { | ||
handleResult( obs, question.when ); | ||
return rx.Observable.defer(function () { | ||
return rx.Observable.create(function (obs) { | ||
if (_.isBoolean(question.when)) { | ||
handleResult(obs, question.when); | ||
return; | ||
} | ||
utils.runAsync( question.when, function( shouldRun ) { | ||
handleResult( obs, shouldRun ); | ||
}, answers ); | ||
runAsync(question.when, function (shouldRun) { | ||
handleResult(obs, shouldRun); | ||
}, answers); | ||
}); | ||
}); | ||
}; |
@@ -1,2 +0,3 @@ | ||
var rx = require("rx"); | ||
'use strict'; | ||
var rx = require('rx-lite'); | ||
@@ -7,24 +8,24 @@ function normalizeKeypressEvents(args) { | ||
module.exports = function(rl) { | ||
module.exports = function (rl) { | ||
return { | ||
line: rx.Observable.fromEvent(rl, "line"), | ||
line: rx.Observable.fromEvent(rl, 'line'), | ||
keypress: rx.Observable.fromEvent(rl, "keypress", normalizeKeypressEvents), | ||
keypress: rx.Observable.fromEvent(rl, 'keypress', normalizeKeypressEvents), | ||
normalizedUpKey: rx.Observable.fromEvent(rl, "keypress", normalizeKeypressEvents).filter(function (e) { | ||
return e.key && (e.key.name === "up" || e.key.name === "k"); | ||
normalizedUpKey: rx.Observable.fromEvent(rl, 'keypress', normalizeKeypressEvents).filter(function (e) { | ||
return e.key && (e.key.name === 'up' || e.key.name === 'k'); | ||
}).share(), | ||
normalizedDownKey: rx.Observable.fromEvent(rl, "keypress", normalizeKeypressEvents).filter(function (e) { | ||
return e.key && (e.key.name === "down" || e.key.name === "j"); | ||
normalizedDownKey: rx.Observable.fromEvent(rl, 'keypress', normalizeKeypressEvents).filter(function (e) { | ||
return e.key && (e.key.name === 'down' || e.key.name === 'j'); | ||
}).share(), | ||
numberKey: rx.Observable.fromEvent(rl, "keypress", normalizeKeypressEvents).filter(function (e) { | ||
return e.value && "123456789".indexOf(e.value) >= 0; | ||
}).map(function(e) { | ||
numberKey: rx.Observable.fromEvent(rl, 'keypress', normalizeKeypressEvents).filter(function (e) { | ||
return e.value && '123456789'.indexOf(e.value) >= 0; | ||
}).map(function (e) { | ||
return Number(e.value); | ||
}).share(), | ||
spaceKey: rx.Observable.fromEvent(rl, "keypress", normalizeKeypressEvents).filter(function (e) { | ||
return e.key && e.key.name === "space"; | ||
spaceKey: rx.Observable.fromEvent(rl, 'keypress', normalizeKeypressEvents).filter(function (e) { | ||
return e.key && e.key.name === 'space'; | ||
}).share(), | ||
@@ -31,0 +32,0 @@ |
@@ -1,43 +0,8 @@ | ||
/** | ||
* Utility functions | ||
*/ | ||
'use strict'; | ||
var _ = require('lodash'); | ||
var rx = require('rx-lite'); | ||
var runAsync = require('run-async'); | ||
"use strict"; | ||
var _ = require("lodash"); | ||
var chalk = require("chalk"); | ||
var rx = require("rx"); | ||
var figures = require("figures"); | ||
/** | ||
* Module exports | ||
*/ | ||
var utils = module.exports; | ||
/** | ||
* Run a function asynchronously or synchronously | ||
* @param {Function} func Function to run | ||
* @param {Function} cb Callback function passed the `func` returned value | ||
* @...rest {Mixed} rest Arguments to pass to `func` | ||
* @return {Null} | ||
*/ | ||
utils.runAsync = function( func, cb ) { | ||
var async = false; | ||
var isValid = func.apply({ | ||
async: function() { | ||
async = true; | ||
return _.once(cb); | ||
} | ||
}, Array.prototype.slice.call(arguments, 2) ); | ||
if ( !async ) { | ||
cb(isValid); | ||
} | ||
}; | ||
/** | ||
* Create an oversable returning the result of a function runned in sync or async mode. | ||
@@ -48,7 +13,7 @@ * @param {Function} func Function to run | ||
utils.createObservableFromAsync = function( func ) { | ||
return rx.Observable.defer(function() { | ||
return rx.Observable.create(function( obs ) { | ||
utils.runAsync( func, function( value ) { | ||
obs.onNext( value ); | ||
exports.createObservableFromAsync = function (func) { | ||
return rx.Observable.defer(function () { | ||
return rx.Observable.create(function (obs) { | ||
runAsync(func, function (value) { | ||
obs.onNext(value); | ||
obs.onCompleted(); | ||
@@ -71,55 +36,14 @@ }); | ||
utils.fetchAsyncQuestionProperty = function( question, prop, answers ) { | ||
if ( !_.isFunction(question[prop]) ) return rx.Observable.return(question); | ||
exports.fetchAsyncQuestionProperty = function (question, prop, answers) { | ||
if (!_.isFunction(question[prop])) { | ||
return rx.Observable.return(question); | ||
} | ||
return utils.createObservableFromAsync(function() { | ||
return exports.createObservableFromAsync(function () { | ||
var done = this.async(); | ||
utils.runAsync( question[prop], function( value ) { | ||
runAsync(question[prop], function (value) { | ||
question[prop] = value; | ||
done( question ); | ||
}, answers ); | ||
done(question); | ||
}, answers); | ||
}); | ||
}; | ||
/** | ||
* Get the pointer char | ||
* @return {String} the pointer char | ||
*/ | ||
utils.getPointer = function() { | ||
return figures.pointer; | ||
}; | ||
/** | ||
* Get the checkbox | ||
* @param {Boolean} checked - add a X or not to the checkbox | ||
* @param {String} after - Text to append after the check char | ||
* @return {String} - Composited checkbox string | ||
*/ | ||
utils.getCheckbox = function( checked, after ) { | ||
var checked = checked ? chalk.green( figures.radioOn ) : figures.radioOff; | ||
return checked + " " + ( after || "" ); | ||
}; | ||
/** | ||
* Helper for writing message in Prompt | ||
* @param {Prompt} prompt - The Prompt object that extends tty | ||
* @param {String} message - The message to be output | ||
*/ | ||
utils.writeMessage = function ( prompt, message ) { | ||
var msgLines = message.split(/\n/); | ||
prompt.height = msgLines.length; | ||
// Write message to screen and setPrompt to control backspace | ||
prompt.rl.setPrompt( _.last(msgLines) ); | ||
if ( process.stdout.rows === 0 && process.stdout.columns === 0 ) { | ||
/* When it's a tty through serial port there's no terminal info and the render will malfunction, | ||
so we need enforce the cursor to locate to the leftmost position for rendering. */ | ||
prompt.left( message.length + prompt.rl.line.length ); | ||
} | ||
prompt.write( message ); | ||
}; |
{ | ||
"name": "inquirer", | ||
"version": "0.8.5", | ||
"version": "0.9.0", | ||
"description": "A collection of common interactive command line user interfaces.", | ||
@@ -24,3 +24,3 @@ "main": "lib/inquirer.js", | ||
"dependencies": { | ||
"ansi-regex": "^1.1.1", | ||
"ansi-regex": "^2.0.0", | ||
"chalk": "^1.0.0", | ||
@@ -31,7 +31,9 @@ "cli-width": "^1.0.1", | ||
"readline2": "^0.1.1", | ||
"rx": "^2.4.3", | ||
"run-async": "^0.1.0", | ||
"rx-lite": "^2.5.2", | ||
"strip-ansi": "^3.0.0", | ||
"through": "^2.3.6" | ||
}, | ||
"devDependencies": { | ||
"chai": "^2.1.2", | ||
"chai": "^3.0.0", | ||
"cmdify": "^0.0.4", | ||
@@ -41,3 +43,3 @@ "grunt": "^0.4.1", | ||
"grunt-contrib-jshint": "^0.11.1", | ||
"grunt-mocha-test": "^0.10.2", | ||
"grunt-mocha-test": "^0.12.7", | ||
"mocha": "^2.2.1", | ||
@@ -44,0 +46,0 @@ "mockery": "^1.4.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
22
62953
10
1716
+ Addedrun-async@^0.1.0
+ Addedrx-lite@^2.5.2
+ Addedstrip-ansi@^3.0.0
+ Addedonce@1.4.0(transitive)
+ Addedrun-async@0.1.0(transitive)
+ Addedrx-lite@2.5.2(transitive)
+ Addedwrappy@1.0.2(transitive)
- Removedrx@^2.4.3
- Removedrx@2.5.3(transitive)
Updatedansi-regex@^2.0.0