| 'use strict'; | 
|   | 
| const colors = require('ansi-colors'); | 
| const ArrayPrompt = require('../types/array'); | 
| const utils = require('../utils'); | 
|   | 
| class LikertScale extends ArrayPrompt { | 
|   constructor(options = {}) { | 
|     super(options); | 
|     this.widths = [].concat(options.messageWidth || 50); | 
|     this.align = [].concat(options.align || 'left'); | 
|     this.linebreak = options.linebreak || false; | 
|     this.edgeLength = options.edgeLength || 3; | 
|     this.newline = options.newline || '\n   '; | 
|     let start = options.startNumber || 1; | 
|     if (typeof this.scale === 'number') { | 
|       this.scaleKey = false; | 
|       this.scale = Array(this.scale).fill(0).map((v, i) => ({ name: i + start })); | 
|     } | 
|   } | 
|   | 
|   async reset() { | 
|     this.tableized = false; | 
|     await super.reset(); | 
|     return this.render(); | 
|   } | 
|   | 
|   tableize() { | 
|     if (this.tableized === true) return; | 
|     this.tableized = true; | 
|     let longest = 0; | 
|   | 
|     for (let ch of this.choices) { | 
|       longest = Math.max(longest, ch.message.length); | 
|       ch.scaleIndex = ch.initial || 2; | 
|       ch.scale = []; | 
|   | 
|       for (let i = 0; i < this.scale.length; i++) { | 
|         ch.scale.push({ index: i }); | 
|       } | 
|     } | 
|     this.widths[0] = Math.min(this.widths[0], longest + 3); | 
|   } | 
|   | 
|   async dispatch(s, key) { | 
|     if (this.multiple) { | 
|       return this[key.name] ? await this[key.name](s, key) : await super.dispatch(s, key); | 
|     } | 
|     this.alert(); | 
|   } | 
|   | 
|   heading(msg, item, i) { | 
|     return this.styles.strong(msg); | 
|   } | 
|   | 
|   separator() { | 
|     return this.styles.muted(this.symbols.ellipsis); | 
|   } | 
|   | 
|   right() { | 
|     let choice = this.focused; | 
|     if (choice.scaleIndex >= this.scale.length - 1) return this.alert(); | 
|     choice.scaleIndex++; | 
|     return this.render(); | 
|   } | 
|   | 
|   left() { | 
|     let choice = this.focused; | 
|     if (choice.scaleIndex <= 0) return this.alert(); | 
|     choice.scaleIndex--; | 
|     return this.render(); | 
|   } | 
|   | 
|   indent() { | 
|     return ''; | 
|   } | 
|   | 
|   format() { | 
|     if (this.state.submitted) { | 
|       let values = this.choices.map(ch => this.styles.info(ch.index)); | 
|       return values.join(', '); | 
|     } | 
|     return ''; | 
|   } | 
|   | 
|   pointer() { | 
|     return ''; | 
|   } | 
|   | 
|   /** | 
|    * Render the scale "Key". Something like: | 
|    * @return {String} | 
|    */ | 
|   | 
|   renderScaleKey() { | 
|     if (this.scaleKey === false) return ''; | 
|     if (this.state.submitted) return ''; | 
|     let scale = this.scale.map(item => `   ${item.name} - ${item.message}`); | 
|     let key = ['', ...scale].map(item => this.styles.muted(item)); | 
|     return key.join('\n'); | 
|   } | 
|   | 
|   /** | 
|    * Render the heading row for the scale. | 
|    * @return {String} | 
|    */ | 
|   | 
|   renderScaleHeading(max) { | 
|     let keys = this.scale.map(ele => ele.name); | 
|     if (typeof this.options.renderScaleHeading === 'function') { | 
|       keys = this.options.renderScaleHeading.call(this, max); | 
|     } | 
|     let diff = this.scaleLength - keys.join('').length; | 
|     let spacing = Math.round(diff / (keys.length - 1)); | 
|     let names = keys.map(key => this.styles.strong(key)); | 
|     let headings = names.join(' '.repeat(spacing)); | 
|     let padding = ' '.repeat(this.widths[0]); | 
|     return this.margin[3] + padding + this.margin[1] + headings; | 
|   } | 
|   | 
|   /** | 
|    * Render a scale indicator => ◯ or ◉ by default | 
|    */ | 
|   | 
|   scaleIndicator(choice, item, i) { | 
|     if (typeof this.options.scaleIndicator === 'function') { | 
|       return this.options.scaleIndicator.call(this, choice, item, i); | 
|     } | 
|     let enabled = choice.scaleIndex === item.index; | 
|     if (item.disabled) return this.styles.hint(this.symbols.radio.disabled); | 
|     if (enabled) return this.styles.success(this.symbols.radio.on); | 
|     return this.symbols.radio.off; | 
|   } | 
|   | 
|   /** | 
|    * Render the actual scale => ◯────◯────◉────◯────◯ | 
|    */ | 
|   | 
|   renderScale(choice, i) { | 
|     let scale = choice.scale.map(item => this.scaleIndicator(choice, item, i)); | 
|     let padding = this.term === 'Hyper' ? '' : ' '; | 
|     return scale.join(padding + this.symbols.line.repeat(this.edgeLength)); | 
|   } | 
|   | 
|   /** | 
|    * Render a choice, including scale => | 
|    *   "The website is easy to navigate. ◯───◯───◉───◯───◯" | 
|    */ | 
|   | 
|   async renderChoice(choice, i) { | 
|     await this.onChoice(choice, i); | 
|   | 
|     let focused = this.index === i; | 
|     let pointer = await this.pointer(choice, i); | 
|     let hint = await choice.hint; | 
|   | 
|     if (hint && !utils.hasColor(hint)) { | 
|       hint = this.styles.muted(hint); | 
|     } | 
|   | 
|     let pad = str => this.margin[3] + str.replace(/\s+$/, '').padEnd(this.widths[0], ' '); | 
|     let newline = this.newline; | 
|     let ind = this.indent(choice); | 
|     let message = await this.resolve(choice.message, this.state, choice, i); | 
|     let scale = await this.renderScale(choice, i); | 
|     let margin = this.margin[1] + this.margin[3]; | 
|     this.scaleLength = colors.unstyle(scale).length; | 
|     this.widths[0] = Math.min(this.widths[0], this.width - this.scaleLength - margin.length); | 
|     let msg = utils.wordWrap(message, { width: this.widths[0], newline }); | 
|     let lines = msg.split('\n').map(line => pad(line) + this.margin[1]); | 
|   | 
|     if (focused) { | 
|       scale = this.styles.info(scale); | 
|       lines = lines.map(line => this.styles.info(line)); | 
|     } | 
|   | 
|     lines[0] += scale; | 
|   | 
|     if (this.linebreak) lines.push(''); | 
|     return [ind + pointer, lines.join('\n')].filter(Boolean); | 
|   } | 
|   | 
|   async renderChoices() { | 
|     if (this.state.submitted) return ''; | 
|     this.tableize(); | 
|     let choices = this.visible.map(async(ch, i) => await this.renderChoice(ch, i)); | 
|     let visible = await Promise.all(choices); | 
|     let heading = await this.renderScaleHeading(); | 
|     return this.margin[0] + [heading, ...visible.map(v => v.join(' '))].join('\n'); | 
|   } | 
|   | 
|   async render() { | 
|     let { submitted, size } = this.state; | 
|   | 
|     let prefix = await this.prefix(); | 
|     let separator = await this.separator(); | 
|     let message = await this.message(); | 
|   | 
|     let prompt = ''; | 
|     if (this.options.promptLine !== false) { | 
|       prompt = [prefix, message, separator, ''].join(' '); | 
|       this.state.prompt = prompt; | 
|     } | 
|   | 
|     let header = await this.header(); | 
|     let output = await this.format(); | 
|     let key = await this.renderScaleKey(); | 
|     let help = await this.error() || await this.hint(); | 
|     let body = await this.renderChoices(); | 
|     let footer = await this.footer(); | 
|     let err = this.emptyError; | 
|   | 
|     if (output) prompt += output; | 
|     if (help && !prompt.includes(help)) prompt += ' ' + help; | 
|   | 
|     if (submitted && !output && !body.trim() && this.multiple && err != null) { | 
|       prompt += this.styles.danger(err); | 
|     } | 
|   | 
|     this.clear(size); | 
|     this.write([header, prompt, key, body, footer].filter(Boolean).join('\n')); | 
|     if (!this.state.submitted) { | 
|       this.write(this.margin[2]); | 
|     } | 
|     this.restore(); | 
|   } | 
|   | 
|   submit() { | 
|     this.value = {}; | 
|     for (let choice of this.choices) { | 
|       this.value[choice.name] = choice.scaleIndex; | 
|     } | 
|     return this.base.submit.call(this); | 
|   } | 
| } | 
|   | 
| module.exports = LikertScale; |