Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / gauge / render-template.js
diff --git a/legacy-libs/gauge/render-template.js b/legacy-libs/gauge/render-template.js
new file mode 100644 (file)
index 0000000..3261bfb
--- /dev/null
@@ -0,0 +1,181 @@
+'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
+}