11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_civar align = require('wide-align')
31cb0ef41Sopenharmony_civar validate = require('aproba')
41cb0ef41Sopenharmony_civar wideTruncate = require('./wide-truncate')
51cb0ef41Sopenharmony_civar error = require('./error')
61cb0ef41Sopenharmony_civar TemplateItem = require('./template-item')
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_cifunction renderValueWithValues (values) {
91cb0ef41Sopenharmony_ci  return function (item) {
101cb0ef41Sopenharmony_ci    return renderValue(item, values)
111cb0ef41Sopenharmony_ci  }
121cb0ef41Sopenharmony_ci}
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_civar renderTemplate = module.exports = function (width, template, values) {
151cb0ef41Sopenharmony_ci  var items = prepareItems(width, template, values)
161cb0ef41Sopenharmony_ci  var rendered = items.map(renderValueWithValues(values)).join('')
171cb0ef41Sopenharmony_ci  return align.left(wideTruncate(rendered, width), width)
181cb0ef41Sopenharmony_ci}
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_cifunction preType (item) {
211cb0ef41Sopenharmony_ci  var cappedTypeName = item.type[0].toUpperCase() + item.type.slice(1)
221cb0ef41Sopenharmony_ci  return 'pre' + cappedTypeName
231cb0ef41Sopenharmony_ci}
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_cifunction postType (item) {
261cb0ef41Sopenharmony_ci  var cappedTypeName = item.type[0].toUpperCase() + item.type.slice(1)
271cb0ef41Sopenharmony_ci  return 'post' + cappedTypeName
281cb0ef41Sopenharmony_ci}
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_cifunction hasPreOrPost (item, values) {
311cb0ef41Sopenharmony_ci  if (!item.type) {
321cb0ef41Sopenharmony_ci    return
331cb0ef41Sopenharmony_ci  }
341cb0ef41Sopenharmony_ci  return values[preType(item)] || values[postType(item)]
351cb0ef41Sopenharmony_ci}
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_cifunction generatePreAndPost (baseItem, parentValues) {
381cb0ef41Sopenharmony_ci  var item = Object.assign({}, baseItem)
391cb0ef41Sopenharmony_ci  var values = Object.create(parentValues)
401cb0ef41Sopenharmony_ci  var template = []
411cb0ef41Sopenharmony_ci  var pre = preType(item)
421cb0ef41Sopenharmony_ci  var post = postType(item)
431cb0ef41Sopenharmony_ci  if (values[pre]) {
441cb0ef41Sopenharmony_ci    template.push({ value: values[pre] })
451cb0ef41Sopenharmony_ci    values[pre] = null
461cb0ef41Sopenharmony_ci  }
471cb0ef41Sopenharmony_ci  item.minLength = null
481cb0ef41Sopenharmony_ci  item.length = null
491cb0ef41Sopenharmony_ci  item.maxLength = null
501cb0ef41Sopenharmony_ci  template.push(item)
511cb0ef41Sopenharmony_ci  values[item.type] = values[item.type]
521cb0ef41Sopenharmony_ci  if (values[post]) {
531cb0ef41Sopenharmony_ci    template.push({ value: values[post] })
541cb0ef41Sopenharmony_ci    values[post] = null
551cb0ef41Sopenharmony_ci  }
561cb0ef41Sopenharmony_ci  return function ($1, $2, length) {
571cb0ef41Sopenharmony_ci    return renderTemplate(length, template, values)
581cb0ef41Sopenharmony_ci  }
591cb0ef41Sopenharmony_ci}
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_cifunction prepareItems (width, template, values) {
621cb0ef41Sopenharmony_ci  function cloneAndObjectify (item, index, arr) {
631cb0ef41Sopenharmony_ci    var cloned = new TemplateItem(item, width)
641cb0ef41Sopenharmony_ci    var type = cloned.type
651cb0ef41Sopenharmony_ci    if (cloned.value == null) {
661cb0ef41Sopenharmony_ci      if (!(type in values)) {
671cb0ef41Sopenharmony_ci        if (cloned.default == null) {
681cb0ef41Sopenharmony_ci          throw new error.MissingTemplateValue(cloned, values)
691cb0ef41Sopenharmony_ci        } else {
701cb0ef41Sopenharmony_ci          cloned.value = cloned.default
711cb0ef41Sopenharmony_ci        }
721cb0ef41Sopenharmony_ci      } else {
731cb0ef41Sopenharmony_ci        cloned.value = values[type]
741cb0ef41Sopenharmony_ci      }
751cb0ef41Sopenharmony_ci    }
761cb0ef41Sopenharmony_ci    if (cloned.value == null || cloned.value === '') {
771cb0ef41Sopenharmony_ci      return null
781cb0ef41Sopenharmony_ci    }
791cb0ef41Sopenharmony_ci    cloned.index = index
801cb0ef41Sopenharmony_ci    cloned.first = index === 0
811cb0ef41Sopenharmony_ci    cloned.last = index === arr.length - 1
821cb0ef41Sopenharmony_ci    if (hasPreOrPost(cloned, values)) {
831cb0ef41Sopenharmony_ci      cloned.value = generatePreAndPost(cloned, values)
841cb0ef41Sopenharmony_ci    }
851cb0ef41Sopenharmony_ci    return cloned
861cb0ef41Sopenharmony_ci  }
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci  var output = template.map(cloneAndObjectify).filter(function (item) {
891cb0ef41Sopenharmony_ci    return item != null
901cb0ef41Sopenharmony_ci  })
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci  var remainingSpace = width
931cb0ef41Sopenharmony_ci  var variableCount = output.length
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci  function consumeSpace (length) {
961cb0ef41Sopenharmony_ci    if (length > remainingSpace) {
971cb0ef41Sopenharmony_ci      length = remainingSpace
981cb0ef41Sopenharmony_ci    }
991cb0ef41Sopenharmony_ci    remainingSpace -= length
1001cb0ef41Sopenharmony_ci  }
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci  function finishSizing (item, length) {
1031cb0ef41Sopenharmony_ci    if (item.finished) {
1041cb0ef41Sopenharmony_ci      throw new error.Internal('Tried to finish template item that was already finished')
1051cb0ef41Sopenharmony_ci    }
1061cb0ef41Sopenharmony_ci    if (length === Infinity) {
1071cb0ef41Sopenharmony_ci      throw new error.Internal('Length of template item cannot be infinity')
1081cb0ef41Sopenharmony_ci    }
1091cb0ef41Sopenharmony_ci    if (length != null) {
1101cb0ef41Sopenharmony_ci      item.length = length
1111cb0ef41Sopenharmony_ci    }
1121cb0ef41Sopenharmony_ci    item.minLength = null
1131cb0ef41Sopenharmony_ci    item.maxLength = null
1141cb0ef41Sopenharmony_ci    --variableCount
1151cb0ef41Sopenharmony_ci    item.finished = true
1161cb0ef41Sopenharmony_ci    if (item.length == null) {
1171cb0ef41Sopenharmony_ci      item.length = item.getBaseLength()
1181cb0ef41Sopenharmony_ci    }
1191cb0ef41Sopenharmony_ci    if (item.length == null) {
1201cb0ef41Sopenharmony_ci      throw new error.Internal('Finished template items must have a length')
1211cb0ef41Sopenharmony_ci    }
1221cb0ef41Sopenharmony_ci    consumeSpace(item.getLength())
1231cb0ef41Sopenharmony_ci  }
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_ci  output.forEach(function (item) {
1261cb0ef41Sopenharmony_ci    if (!item.kerning) {
1271cb0ef41Sopenharmony_ci      return
1281cb0ef41Sopenharmony_ci    }
1291cb0ef41Sopenharmony_ci    var prevPadRight = item.first ? 0 : output[item.index - 1].padRight
1301cb0ef41Sopenharmony_ci    if (!item.first && prevPadRight < item.kerning) {
1311cb0ef41Sopenharmony_ci      item.padLeft = item.kerning - prevPadRight
1321cb0ef41Sopenharmony_ci    }
1331cb0ef41Sopenharmony_ci    if (!item.last) {
1341cb0ef41Sopenharmony_ci      item.padRight = item.kerning
1351cb0ef41Sopenharmony_ci    }
1361cb0ef41Sopenharmony_ci  })
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ci  // Finish any that have a fixed (literal or intuited) length
1391cb0ef41Sopenharmony_ci  output.forEach(function (item) {
1401cb0ef41Sopenharmony_ci    if (item.getBaseLength() == null) {
1411cb0ef41Sopenharmony_ci      return
1421cb0ef41Sopenharmony_ci    }
1431cb0ef41Sopenharmony_ci    finishSizing(item)
1441cb0ef41Sopenharmony_ci  })
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci  var resized = 0
1471cb0ef41Sopenharmony_ci  var resizing
1481cb0ef41Sopenharmony_ci  var hunkSize
1491cb0ef41Sopenharmony_ci  do {
1501cb0ef41Sopenharmony_ci    resizing = false
1511cb0ef41Sopenharmony_ci    hunkSize = Math.round(remainingSpace / variableCount)
1521cb0ef41Sopenharmony_ci    output.forEach(function (item) {
1531cb0ef41Sopenharmony_ci      if (item.finished) {
1541cb0ef41Sopenharmony_ci        return
1551cb0ef41Sopenharmony_ci      }
1561cb0ef41Sopenharmony_ci      if (!item.maxLength) {
1571cb0ef41Sopenharmony_ci        return
1581cb0ef41Sopenharmony_ci      }
1591cb0ef41Sopenharmony_ci      if (item.getMaxLength() < hunkSize) {
1601cb0ef41Sopenharmony_ci        finishSizing(item, item.maxLength)
1611cb0ef41Sopenharmony_ci        resizing = true
1621cb0ef41Sopenharmony_ci      }
1631cb0ef41Sopenharmony_ci    })
1641cb0ef41Sopenharmony_ci  } while (resizing && resized++ < output.length)
1651cb0ef41Sopenharmony_ci  if (resizing) {
1661cb0ef41Sopenharmony_ci    throw new error.Internal('Resize loop iterated too many times while determining maxLength')
1671cb0ef41Sopenharmony_ci  }
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_ci  resized = 0
1701cb0ef41Sopenharmony_ci  do {
1711cb0ef41Sopenharmony_ci    resizing = false
1721cb0ef41Sopenharmony_ci    hunkSize = Math.round(remainingSpace / variableCount)
1731cb0ef41Sopenharmony_ci    output.forEach(function (item) {
1741cb0ef41Sopenharmony_ci      if (item.finished) {
1751cb0ef41Sopenharmony_ci        return
1761cb0ef41Sopenharmony_ci      }
1771cb0ef41Sopenharmony_ci      if (!item.minLength) {
1781cb0ef41Sopenharmony_ci        return
1791cb0ef41Sopenharmony_ci      }
1801cb0ef41Sopenharmony_ci      if (item.getMinLength() >= hunkSize) {
1811cb0ef41Sopenharmony_ci        finishSizing(item, item.minLength)
1821cb0ef41Sopenharmony_ci        resizing = true
1831cb0ef41Sopenharmony_ci      }
1841cb0ef41Sopenharmony_ci    })
1851cb0ef41Sopenharmony_ci  } while (resizing && resized++ < output.length)
1861cb0ef41Sopenharmony_ci  if (resizing) {
1871cb0ef41Sopenharmony_ci    throw new error.Internal('Resize loop iterated too many times while determining minLength')
1881cb0ef41Sopenharmony_ci  }
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci  hunkSize = Math.round(remainingSpace / variableCount)
1911cb0ef41Sopenharmony_ci  output.forEach(function (item) {
1921cb0ef41Sopenharmony_ci    if (item.finished) {
1931cb0ef41Sopenharmony_ci      return
1941cb0ef41Sopenharmony_ci    }
1951cb0ef41Sopenharmony_ci    finishSizing(item, hunkSize)
1961cb0ef41Sopenharmony_ci  })
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci  return output
1991cb0ef41Sopenharmony_ci}
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_cifunction renderFunction (item, values, length) {
2021cb0ef41Sopenharmony_ci  validate('OON', arguments)
2031cb0ef41Sopenharmony_ci  if (item.type) {
2041cb0ef41Sopenharmony_ci    return item.value(values, values[item.type + 'Theme'] || {}, length)
2051cb0ef41Sopenharmony_ci  } else {
2061cb0ef41Sopenharmony_ci    return item.value(values, {}, length)
2071cb0ef41Sopenharmony_ci  }
2081cb0ef41Sopenharmony_ci}
2091cb0ef41Sopenharmony_ci
2101cb0ef41Sopenharmony_cifunction renderValue (item, values) {
2111cb0ef41Sopenharmony_ci  var length = item.getBaseLength()
2121cb0ef41Sopenharmony_ci  var value = typeof item.value === 'function' ? renderFunction(item, values, length) : item.value
2131cb0ef41Sopenharmony_ci  if (value == null || value === '') {
2141cb0ef41Sopenharmony_ci    return ''
2151cb0ef41Sopenharmony_ci  }
2161cb0ef41Sopenharmony_ci  var alignWith = align[item.align] || align.left
2171cb0ef41Sopenharmony_ci  var leftPadding = item.padLeft ? align.left('', item.padLeft) : ''
2181cb0ef41Sopenharmony_ci  var rightPadding = item.padRight ? align.right('', item.padRight) : ''
2191cb0ef41Sopenharmony_ci  var truncated = wideTruncate(String(value), length)
2201cb0ef41Sopenharmony_ci  var aligned = alignWith(truncated, length)
2211cb0ef41Sopenharmony_ci  return leftPadding + aligned + rightPadding
2221cb0ef41Sopenharmony_ci}
223