| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 | 
							- 'use strict'
 
- var align = require('wide-align')
 
- var validate = require('aproba')
 
- var objectAssign = require('object-assign')
 
- var wideTruncate = require('./wide-truncate')
 
- var error = require('./error')
 
- var TemplateItem = require('./template-item')
 
- function renderValueWithValues (values) {
 
-   return function (item) {
 
-     return renderValue(item, values)
 
-   }
 
- }
 
- var renderTemplate = module.exports = function (width, template, values) {
 
-   var items = prepareItems(width, template, values)
 
-   var rendered = items.map(renderValueWithValues(values)).join('')
 
-   return align.left(wideTruncate(rendered, width), width)
 
- }
 
- function preType (item) {
 
-   var cappedTypeName = item.type[0].toUpperCase() + item.type.slice(1)
 
-   return 'pre' + cappedTypeName
 
- }
 
- function postType (item) {
 
-   var cappedTypeName = item.type[0].toUpperCase() + item.type.slice(1)
 
-   return 'post' + cappedTypeName
 
- }
 
- function hasPreOrPost (item, values) {
 
-   if (!item.type) return
 
-   return values[preType(item)] || values[postType(item)]
 
- }
 
- function generatePreAndPost (baseItem, parentValues) {
 
-   var item = objectAssign({}, baseItem)
 
-   var values = Object.create(parentValues)
 
-   var template = []
 
-   var pre = preType(item)
 
-   var post = postType(item)
 
-   if (values[pre]) {
 
-     template.push({value: values[pre]})
 
-     values[pre] = null
 
-   }
 
-   item.minLength = null
 
-   item.length = null
 
-   item.maxLength = null
 
-   template.push(item)
 
-   values[item.type] = values[item.type]
 
-   if (values[post]) {
 
-     template.push({value: values[post]})
 
-     values[post] = null
 
-   }
 
-   return function ($1, $2, length) {
 
-     return renderTemplate(length, template, values)
 
-   }
 
- }
 
- function prepareItems (width, template, values) {
 
-   function cloneAndObjectify (item, index, arr) {
 
-     var cloned = new TemplateItem(item, width)
 
-     var type = cloned.type
 
-     if (cloned.value == null) {
 
-       if (!(type in values)) {
 
-         if (cloned.default == null) {
 
-           throw new error.MissingTemplateValue(cloned, values)
 
-         } else {
 
-           cloned.value = cloned.default
 
-         }
 
-       } else {
 
-         cloned.value = values[type]
 
-       }
 
-     }
 
-     if (cloned.value == null || cloned.value === '') return null
 
-     cloned.index = index
 
-     cloned.first = index === 0
 
-     cloned.last = index === arr.length - 1
 
-     if (hasPreOrPost(cloned, values)) cloned.value = generatePreAndPost(cloned, values)
 
-     return cloned
 
-   }
 
-   var output = template.map(cloneAndObjectify).filter(function (item) { return item != null })
 
-   var outputLength = 0
 
-   var remainingSpace = width
 
-   var variableCount = output.length
 
-   function consumeSpace (length) {
 
-     if (length > remainingSpace) length = remainingSpace
 
-     outputLength += length
 
-     remainingSpace -= length
 
-   }
 
-   function finishSizing (item, length) {
 
-     if (item.finished) throw new error.Internal('Tried to finish template item that was already finished')
 
-     if (length === Infinity) throw new error.Internal('Length of template item cannot be infinity')
 
-     if (length != null) item.length = length
 
-     item.minLength = null
 
-     item.maxLength = null
 
-     --variableCount
 
-     item.finished = true
 
-     if (item.length == null) item.length = item.getBaseLength()
 
-     if (item.length == null) throw new error.Internal('Finished template items must have a length')
 
-     consumeSpace(item.getLength())
 
-   }
 
-   output.forEach(function (item) {
 
-     if (!item.kerning) return
 
-     var prevPadRight = item.first ? 0 : output[item.index - 1].padRight
 
-     if (!item.first && prevPadRight < item.kerning) item.padLeft = item.kerning - prevPadRight
 
-     if (!item.last) item.padRight = item.kerning
 
-   })
 
-   // Finish any that have a fixed (literal or intuited) length
 
-   output.forEach(function (item) {
 
-     if (item.getBaseLength() == null) return
 
-     finishSizing(item)
 
-   })
 
-   var resized = 0
 
-   var resizing
 
-   var hunkSize
 
-   do {
 
-     resizing = false
 
-     hunkSize = Math.round(remainingSpace / variableCount)
 
-     output.forEach(function (item) {
 
-       if (item.finished) return
 
-       if (!item.maxLength) return
 
-       if (item.getMaxLength() < hunkSize) {
 
-         finishSizing(item, item.maxLength)
 
-         resizing = true
 
-       }
 
-     })
 
-   } while (resizing && resized++ < output.length)
 
-   if (resizing) throw new error.Internal('Resize loop iterated too many times while determining maxLength')
 
-   resized = 0
 
-   do {
 
-     resizing = false
 
-     hunkSize = Math.round(remainingSpace / variableCount)
 
-     output.forEach(function (item) {
 
-       if (item.finished) return
 
-       if (!item.minLength) return
 
-       if (item.getMinLength() >= hunkSize) {
 
-         finishSizing(item, item.minLength)
 
-         resizing = true
 
-       }
 
-     })
 
-   } while (resizing && resized++ < output.length)
 
-   if (resizing) throw new error.Internal('Resize loop iterated too many times while determining minLength')
 
-   hunkSize = Math.round(remainingSpace / variableCount)
 
-   output.forEach(function (item) {
 
-     if (item.finished) return
 
-     finishSizing(item, hunkSize)
 
-   })
 
-   return output
 
- }
 
- function renderFunction (item, values, length) {
 
-   validate('OON', arguments)
 
-   if (item.type) {
 
-     return item.value(values, values[item.type + 'Theme'] || {}, length)
 
-   } else {
 
-     return item.value(values, {}, length)
 
-   }
 
- }
 
- function renderValue (item, values) {
 
-   var length = item.getBaseLength()
 
-   var value = typeof item.value === 'function' ? renderFunction(item, values, length) : item.value
 
-   if (value == null || value === '') return ''
 
-   var alignWith = align[item.align] || align.left
 
-   var leftPadding = item.padLeft ? align.left('', item.padLeft) : ''
 
-   var rightPadding = item.padRight ? align.right('', item.padRight) : ''
 
-   var truncated = wideTruncate(String(value), length)
 
-   var aligned = alignWith(truncated, length)
 
-   return leftPadding + aligned + rightPadding
 
- }
 
 
  |