You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							181 lines
						
					
					
						
							5.6 KiB
						
					
					
				
			
		
		
	
	
							181 lines
						
					
					
						
							5.6 KiB
						
					
					
				'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 | 
						|
}
 | 
						|
 |