Security update for permissions_by_term
[yaffs-website] / node_modules / gauge / render-template.js
1 'use strict'
2 var align = require('wide-align')
3 var validate = require('aproba')
4 var objectAssign = require('object-assign')
5 var wideTruncate = require('./wide-truncate')
6 var error = require('./error')
7 var TemplateItem = require('./template-item')
8
9 function renderValueWithValues (values) {
10   return function (item) {
11     return renderValue(item, values)
12   }
13 }
14
15 var renderTemplate = module.exports = function (width, template, values) {
16   var items = prepareItems(width, template, values)
17   var rendered = items.map(renderValueWithValues(values)).join('')
18   return align.left(wideTruncate(rendered, width), width)
19 }
20
21 function preType (item) {
22   var cappedTypeName = item.type[0].toUpperCase() + item.type.slice(1)
23   return 'pre' + cappedTypeName
24 }
25
26 function postType (item) {
27   var cappedTypeName = item.type[0].toUpperCase() + item.type.slice(1)
28   return 'post' + cappedTypeName
29 }
30
31 function hasPreOrPost (item, values) {
32   if (!item.type) return
33   return values[preType(item)] || values[postType(item)]
34 }
35
36 function generatePreAndPost (baseItem, parentValues) {
37   var item = objectAssign({}, baseItem)
38   var values = Object.create(parentValues)
39   var template = []
40   var pre = preType(item)
41   var post = postType(item)
42   if (values[pre]) {
43     template.push({value: values[pre]})
44     values[pre] = null
45   }
46   item.minLength = null
47   item.length = null
48   item.maxLength = null
49   template.push(item)
50   values[item.type] = values[item.type]
51   if (values[post]) {
52     template.push({value: values[post]})
53     values[post] = null
54   }
55   return function ($1, $2, length) {
56     return renderTemplate(length, template, values)
57   }
58 }
59
60 function prepareItems (width, template, values) {
61   function cloneAndObjectify (item, index, arr) {
62     var cloned = new TemplateItem(item, width)
63     var type = cloned.type
64     if (cloned.value == null) {
65       if (!(type in values)) {
66         if (cloned.default == null) {
67           throw new error.MissingTemplateValue(cloned, values)
68         } else {
69           cloned.value = cloned.default
70         }
71       } else {
72         cloned.value = values[type]
73       }
74     }
75     if (cloned.value == null || cloned.value === '') return null
76     cloned.index = index
77     cloned.first = index === 0
78     cloned.last = index === arr.length - 1
79     if (hasPreOrPost(cloned, values)) cloned.value = generatePreAndPost(cloned, values)
80     return cloned
81   }
82
83   var output = template.map(cloneAndObjectify).filter(function (item) { return item != null })
84
85   var outputLength = 0
86   var remainingSpace = width
87   var variableCount = output.length
88
89   function consumeSpace (length) {
90     if (length > remainingSpace) length = remainingSpace
91     outputLength += length
92     remainingSpace -= length
93   }
94
95   function finishSizing (item, length) {
96     if (item.finished) throw new error.Internal('Tried to finish template item that was already finished')
97     if (length === Infinity) throw new error.Internal('Length of template item cannot be infinity')
98     if (length != null) item.length = length
99     item.minLength = null
100     item.maxLength = null
101     --variableCount
102     item.finished = true
103     if (item.length == null) item.length = item.getBaseLength()
104     if (item.length == null) throw new error.Internal('Finished template items must have a length')
105     consumeSpace(item.getLength())
106   }
107
108   output.forEach(function (item) {
109     if (!item.kerning) return
110     var prevPadRight = item.first ? 0 : output[item.index - 1].padRight
111     if (!item.first && prevPadRight < item.kerning) item.padLeft = item.kerning - prevPadRight
112     if (!item.last) item.padRight = item.kerning
113   })
114
115   // Finish any that have a fixed (literal or intuited) length
116   output.forEach(function (item) {
117     if (item.getBaseLength() == null) return
118     finishSizing(item)
119   })
120
121   var resized = 0
122   var resizing
123   var hunkSize
124   do {
125     resizing = false
126     hunkSize = Math.round(remainingSpace / variableCount)
127     output.forEach(function (item) {
128       if (item.finished) return
129       if (!item.maxLength) return
130       if (item.getMaxLength() < hunkSize) {
131         finishSizing(item, item.maxLength)
132         resizing = true
133       }
134     })
135   } while (resizing && resized++ < output.length)
136   if (resizing) throw new error.Internal('Resize loop iterated too many times while determining maxLength')
137
138   resized = 0
139   do {
140     resizing = false
141     hunkSize = Math.round(remainingSpace / variableCount)
142     output.forEach(function (item) {
143       if (item.finished) return
144       if (!item.minLength) return
145       if (item.getMinLength() >= hunkSize) {
146         finishSizing(item, item.minLength)
147         resizing = true
148       }
149     })
150   } while (resizing && resized++ < output.length)
151   if (resizing) throw new error.Internal('Resize loop iterated too many times while determining minLength')
152
153   hunkSize = Math.round(remainingSpace / variableCount)
154   output.forEach(function (item) {
155     if (item.finished) return
156     finishSizing(item, hunkSize)
157   })
158
159   return output
160 }
161
162 function renderFunction (item, values, length) {
163   validate('OON', arguments)
164   if (item.type) {
165     return item.value(values, values[item.type + 'Theme'] || {}, length)
166   } else {
167     return item.value(values, {}, length)
168   }
169 }
170
171 function renderValue (item, values) {
172   var length = item.getBaseLength()
173   var value = typeof item.value === 'function' ? renderFunction(item, values, length) : item.value
174   if (value == null || value === '') return ''
175   var alignWith = align[item.align] || align.left
176   var leftPadding = item.padLeft ? align.left('', item.padLeft) : ''
177   var rightPadding = item.padRight ? align.right('', item.padRight) : ''
178   var truncated = wideTruncate(String(value), length)
179   var aligned = alignWith(truncated, length)
180   return leftPadding + aligned + rightPadding
181 }