text: /**
   * Base prompt implementation
   * Should be extended by prompt types.
   */
  
  var _ = require('lodash');
  var chalk = require('chalk');
  var runAsync = require('run-async');
  var Choices = require('../objects/choices');
  var ScreenManager = require('../utils/screen-manager');
  
  var Prompt = module.exports = function (question, rl, answers) {
    // Setup instance defaults property
    _.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;
      },
      suffix: '',
      prefix: chalk.green('?')
    });
  
    // Check to make sure prompt requirements are there
    if (!this.opt.message) {
      this.throwParamError('message');
    }
    if (!this.opt.name) {
      this.throwParamError('name');
    }
  
    // Normalize choices
    if (Array.isArray(this.opt.choices)) {
      this.opt.choices = new Choices(this.opt.choices, answers);
    }
  
    this.rl = rl;
    this.screen = new ScreenManager(this.rl);
  };
  
  /**
   * Start the Inquiry session and manage output value filtering
   * @return {Promise}
   */
  
  Prompt.prototype.run = function () {
    return new Promise(function (resolve) {
      this._run(function (value) {
        resolve(value);
      });
    }.bind(this));
  };
  
  // default noop (this one should be overwritten in prompts)
  Prompt.prototype._run = function (cb) {
    cb();
  };
  
  /**
   * Throw an error telling a required parameter is missing
   * @param  {String} name Name of the missing param
   * @return {Throw Error}
   */
  
  Prompt.prototype.throwParamError = function (name) {
    throw new Error('You must provide a `' + name + '` parameter');
  };
  
  /**
   * Called when the UI closes. Override to do any specific cleanup necessary
   */
  Prompt.prototype.close = function () {
    this.screen.releaseCursor();
  };
  
  /**
   * Run the provided validation method each time a submit event occur.
   * @param  {Rx.Observable} submit - submit event flow
   * @return {Object}        Object containing two observables: `success` and `error`
   */
  Prompt.prototype.handleSubmitEvents = function (submit) {
    var self = this;
    var validate = runAsync(this.opt.validate);
    var filter = runAsync(this.opt.filter);
    var validation = submit.flatMap(function (value) {
      return filter(value, self.answers).then(function (filteredValue) {
        return validate(filteredValue, self.answers).then(function (isValid) {
          return {isValid: isValid, value: filteredValue};
        }, function (err) {
          return {isValid: err};
        });
      }, function (err) {
        return {isValid: err};
      });
    }).share();
  
    var success = validation
      .filter(function (state) {
        return state.isValid === true;
      })
      .take(1);
  
    var error = validation
      .filter(function (state) {
        return state.isValid !== true;
      })
      .takeUntil(success);
  
    return {
      success: success,
      error: error
    };
  };
  
  /**
   * Generate the prompt question string
   * @return {String} prompt question string
   */
  
  Prompt.prototype.getQuestion = function () {
    var message = this.opt.prefix + ' ' + chalk.bold(this.opt.message) + this.opt.suffix + chalk.reset(' ');
  
    // 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 + ') ');
    }
  
    return message;
  };
  