1 | module.exports = Table
|
2 |
|
3 | Table.string = function (val) {
|
4 | if (val === undefined) return ''
|
5 | return String(val)
|
6 | }
|
7 |
|
8 | Table.Number = function (digits) {
|
9 | return function (val, width) {
|
10 | if (val === undefined) return ''
|
11 | if (typeof val != 'number')
|
12 | throw new Error(String(val) + ' is not a number')
|
13 | var s = digits == null ? String(val) : val.toFixed(digits).toString()
|
14 | return Table.padLeft(s, width)
|
15 | }
|
16 | }
|
17 |
|
18 | Table.RightPadder = function (char) {
|
19 | char = char || ' '
|
20 | return function (val, length) {
|
21 | var s = String(val)
|
22 | var l = s.length
|
23 | for (var i = 0; i < length - l; i++) {
|
24 | s += char
|
25 | }
|
26 | return s
|
27 | }
|
28 | }
|
29 |
|
30 | Table.LeftPadder = function (char) {
|
31 | char = char || ' '
|
32 | return function (val, length) {
|
33 | var ret = ''
|
34 | var s = String(val)
|
35 | for (var i = 0; i < length - s.length; i++) {
|
36 | ret += char
|
37 | }
|
38 | ret += s
|
39 | return ret
|
40 | }
|
41 | }
|
42 |
|
43 | Table.padLeft = Table.LeftPadder()
|
44 |
|
45 | Table.printArray = function (arr, format, cb) {
|
46 | format = typeof format == 'function' ? format : Formatter(format)
|
47 | cb = cb || function (t) {
|
48 | return t.toString()
|
49 | }
|
50 |
|
51 | var t = new Table
|
52 | var cell = t.cell.bind(t)
|
53 |
|
54 | arr.forEach(function (obj) {
|
55 | format(obj, cell)
|
56 | t.newRow()
|
57 | })
|
58 | return cb(t)
|
59 | }
|
60 |
|
61 | Table.printObj = function (obj, format, cb) {
|
62 | format = typeof format == 'function' ? format : Formatter(format)
|
63 | cb = cb || function (t) {
|
64 | return t.printTransposed(' : ')
|
65 | }
|
66 |
|
67 | var t = new Table
|
68 | format(obj, t.cell.bind(t))
|
69 | t.newRow()
|
70 | return cb(t)
|
71 | }
|
72 |
|
73 | function Formatter (opts) {
|
74 | opts = opts || {}
|
75 | return function (obj, cell) {
|
76 | for (var key in obj) {
|
77 | if (!obj.hasOwnProperty(key)) continue
|
78 | var o = opts[key]
|
79 | cell(
|
80 | (o && o.name) || key,
|
81 | obj[key],
|
82 | o && o.printer,
|
83 | o && o.width
|
84 | )
|
85 | }
|
86 | }
|
87 | }
|
88 |
|
89 |
|
90 | Table.Row = Row
|
91 | function Row () {
|
92 | Object.defineProperties(this, {
|
93 | __printers: {
|
94 | value: {},
|
95 | enumerable: false
|
96 | },
|
97 | __cell: {
|
98 | value: function (col, val, printer) {
|
99 | this[col] = val
|
100 | this.__printers[col] = printer
|
101 | },
|
102 | enumerable: false
|
103 | }
|
104 | })
|
105 | }
|
106 |
|
107 |
|
108 | Table.print = print
|
109 | function print (rows, columns, shift) {
|
110 | var padSpaces = Table.RightPadder()
|
111 | var widths = {}
|
112 |
|
113 | function setWidth (col, width) {
|
114 | var isFixed = columns[col].width != null
|
115 | if (isFixed) {
|
116 | widths[col] = columns[col].width
|
117 | } else {
|
118 | if (widths[col] > width) return
|
119 | widths[col] = width
|
120 | }
|
121 | }
|
122 |
|
123 | function cellPrinter (row, col) {
|
124 | return (row.__printers && row.__printers[col]) || Table.string
|
125 | }
|
126 |
|
127 | function calcWidths () {
|
128 | rows.forEach(function (row) {
|
129 | for (var key in columns) {
|
130 | setWidth(key, cellPrinter(row, key).call(row, row[key]).length)
|
131 | }
|
132 | })
|
133 | }
|
134 |
|
135 | function printRow (cb) {
|
136 | var s = ''
|
137 | var firstColumn = true
|
138 | for (var key in columns) {
|
139 | if (!firstColumn) s += shift
|
140 | firstColumn = false
|
141 | var width = widths[key]
|
142 | s += printCell(cb(key, width), width)
|
143 | }
|
144 | s += '\n'
|
145 | return s
|
146 | }
|
147 |
|
148 | function printCell (s, width) {
|
149 | if (s.length <= width) return padSpaces(s, width)
|
150 | s = s.slice(0, width)
|
151 | if (width > 3) s = s.slice(0, -3).concat('...')
|
152 | return s
|
153 | }
|
154 |
|
155 | calcWidths()
|
156 |
|
157 | return rows.map(function (row) {
|
158 | return printRow(function (key, width) {
|
159 | return cellPrinter(row, key).call(row, row[key], width)
|
160 | })
|
161 | }).join('')
|
162 |
|
163 | }
|
164 |
|
165 |
|
166 | function Table () {
|
167 | this.columns = {}
|
168 | this.rows = []
|
169 | this._row = new Row
|
170 | }
|
171 |
|
172 |
|
173 | Table.prototype.cell = function (col, val, printer, width) {
|
174 | this._row.__cell(col, val, printer)
|
175 | var c = this.columns[col] || (this.columns[col] = {})
|
176 | if (width != null) c.width = width
|
177 | return this
|
178 | }
|
179 |
|
180 | Table.prototype.newRow = Table.prototype.newLine = function () {
|
181 | this.rows.push(this._row)
|
182 | this._row = new Row
|
183 | return this
|
184 | }
|
185 |
|
186 | Table.prototype.sort = require('./sort')
|
187 |
|
188 | Table.aggr = require('./aggregations')
|
189 |
|
190 | Table.prototype.totals = null
|
191 |
|
192 | Table.prototype.total = function (col, fn, printer) {
|
193 | fn = fn || Table.aggr.sum
|
194 | printer = printer || fn.printer
|
195 |
|
196 | this.totals = this.totals || new Row
|
197 |
|
198 | var val
|
199 | var rows = this.rows
|
200 |
|
201 | this.totals.__cell(col, null, function (_, width) {
|
202 | if (width != null) return printer(val, width)
|
203 | val = rows.reduce(function (val, row, index) {
|
204 | return fn(val, row[col], index, rows.length)
|
205 | }, null)
|
206 | return printer(val)
|
207 | })
|
208 | return this
|
209 | }
|
210 |
|
211 | Table.prototype.shift = ' '
|
212 |
|
213 | Table.prototype.print = function () {
|
214 | return print(this.rows, this.columns, this.shift)
|
215 | }
|
216 |
|
217 | Table.prototype.printTransposed = function (delimeter) {
|
218 | var t = new Table
|
219 | if (delimeter) t.shift = delimeter
|
220 |
|
221 | function Printer (row, key) {
|
222 | var p = row.__printers && row.__printers[key]
|
223 | if (p) return function (val) {
|
224 | return p(val)
|
225 | }
|
226 | }
|
227 |
|
228 | for (var key in this.columns) {
|
229 | t.cell('h', key)
|
230 | this.rows.forEach(function (row, index) {
|
231 | t.cell('f' + index, row[key], Printer(row, key))
|
232 | })
|
233 | t.newRow()
|
234 | }
|
235 | return t.print()
|
236 | }
|
237 |
|
238 | Table.prototype.toString = function () {
|
239 | var padWithDashs = Table.RightPadder('-')
|
240 | var delimeter = this.createRow(function () {
|
241 | return ['', padWithDashs]
|
242 | })
|
243 | var head = this.createRow(function (key) {
|
244 | return [key]
|
245 | })
|
246 | var rows = [head, delimeter].concat(this.rows)
|
247 | if (this.totals) {
|
248 | rows = rows.concat([delimeter, this.totals])
|
249 | }
|
250 | return print(rows, this.columns, this.shift)
|
251 | }
|
252 |
|
253 | Table.prototype.createRow = function (cb) {
|
254 | var row = new Row
|
255 | for (var key in this.columns) {
|
256 | var args = cb(key)
|
257 | row.__cell(key, args[0], args[1])
|
258 | }
|
259 | return row
|
260 | } |
\ | No newline at end of file |