UNPKG

14.2 kBJavaScriptView Raw
1/**
2 * (c) 2013 Beau Sorensen
3 * MIT Licensed
4 * For all details and documentation:
5 * https://github.com/sorensen/ascii-table
6 */
7
8;(function() {
9'use strict';
10
11/*!
12 * Module dependencies
13 */
14
15var slice = Array.prototype.slice
16 , toString = Object.prototype.toString
17
18/**
19 * AsciiTable constructor
20 *
21 * @param {String|Object} title or JSON table
22 * @param {Object} table options
23 * - `prefix` - string prefix added to each line on render
24 * @constructor
25 * @api public
26 */
27
28function AsciiTable(name, options) {
29 this.options = options || {}
30 this.reset(name)
31}
32
33/*!
34 * Current library version, should match `package.json`
35 */
36
37AsciiTable.VERSION = '0.0.8'
38
39/*!
40 * Alignment constants
41 */
42
43AsciiTable.LEFT = 0
44AsciiTable.CENTER = 1
45AsciiTable.RIGHT = 2
46
47/*!
48 * Static methods
49 */
50
51/**
52 * Create a new table instance
53 *
54 * @param {String|Object} title or JSON table
55 * @param {Object} table options
56 * @api public
57 */
58
59AsciiTable.factory = function(name, options) {
60 return new AsciiTable(name, options)
61}
62
63/**
64 * Align the a string at the given length
65 *
66 * @param {Number} direction
67 * @param {String} string input
68 * @param {Number} string length
69 * @param {Number} padding character
70 * @api public
71 */
72
73AsciiTable.align = function(dir, str, len, pad) {
74 if (dir === AsciiTable.LEFT) return AsciiTable.alignLeft(str, len, pad)
75 if (dir === AsciiTable.RIGHT) return AsciiTable.alignRight(str, len, pad)
76 if (dir === AsciiTable.CENTER) return AsciiTable.alignCenter(str, len, pad)
77 return AsciiTable.alignAuto(str, len, pad)
78}
79
80/**
81 * Left align a string by padding it at a given length
82 *
83 * @param {String} str
84 * @param {Number} string length
85 * @param {String} padding character (optional, default '')
86 * @api public
87 */
88
89AsciiTable.alignLeft = function(str, len, pad) {
90 if (!len || len < 0) return ''
91 if (str === undefined || str === null) str = ''
92 if (typeof pad === 'undefined') pad = ' '
93 if (typeof str !== 'string') str = str.toString()
94 var alen = len + 1 - str.length
95 if (alen <= 0) return str
96 return str + Array(len + 1 - str.length).join(pad)
97}
98
99/**
100 * Center align a string by padding it at a given length
101 *
102 * @param {String} str
103 * @param {Number} string length
104 * @param {String} padding character (optional, default '')
105 * @api public
106 */
107
108AsciiTable.alignCenter = function(str, len, pad) {
109 if (!len || len < 0) return ''
110 if (str === undefined || str === null) str = ''
111 if (typeof pad === 'undefined') pad = ' '
112 if (typeof str !== 'string') str = str.toString()
113 var nLen = str.length
114 , half = Math.floor(len / 2 - nLen / 2)
115 , odds = Math.abs((nLen % 2) - (len % 2))
116 , len = str.length
117
118 return AsciiTable.alignRight('', half, pad)
119 + str
120 + AsciiTable.alignLeft('', half + odds, pad)
121}
122
123/**
124 * Right align a string by padding it at a given length
125 *
126 * @param {String} str
127 * @param {Number} string length
128 * @param {String} padding character (optional, default '')
129 * @api public
130 */
131
132AsciiTable.alignRight = function(str, len, pad) {
133 if (!len || len < 0) return ''
134 if (str === undefined || str === null) str = ''
135 if (typeof pad === 'undefined') pad = ' '
136 if (typeof str !== 'string') str = str.toString()
137 var alen = len + 1 - str.length
138 if (alen <= 0) return str
139 return Array(len + 1 - str.length).join(pad) + str
140}
141
142/**
143 * Auto align string value based on object type
144 *
145 * @param {Any} object to string
146 * @param {Number} string length
147 * @param {String} padding character (optional, default '')
148 * @api public
149 */
150
151AsciiTable.alignAuto = function(str, len, pad) {
152 if (str === undefined || str === null) str = ''
153 var type = toString.call(str)
154 pad || (pad = ' ')
155 len = +len
156 if (type !== '[object String]') {
157 str = str.toString()
158 }
159 if (str.length < len) {
160 switch(type) {
161 case '[object Number]': return AsciiTable.alignRight(str, len, pad)
162 default: return AsciiTable.alignLeft(str, len, pad)
163 }
164 }
165 return str
166}
167
168/**
169 * Fill an array at a given size with the given value
170 *
171 * @param {Number} array size
172 * @param {Any} fill value
173 * @return {Array} filled array
174 * @api public
175 */
176
177AsciiTable.arrayFill = function(len, fill) {
178 var arr = new Array(len)
179 for (var i = 0; i !== len; i++) {
180 arr[i] = fill;
181 }
182 return arr
183}
184
185/*!
186 * Instance methods
187 */
188
189/**
190 * Reset the table state back to defaults
191 *
192 * @param {String|Object} title or JSON table
193 * @api public
194 */
195
196AsciiTable.prototype.reset =
197AsciiTable.prototype.clear = function(name) {
198 this.__name = ''
199 this.__nameAlign = AsciiTable.CENTER
200 this.__rows = []
201 this.__maxCells = 0
202 this.__aligns = []
203 this.__colMaxes = []
204 this.__spacing = 1
205 this.__heading = null
206 this.__headingAlign = AsciiTable.CENTER
207 this.setBorder()
208
209 if (toString.call(name) === '[object String]') {
210 this.__name = name
211 } else if (toString.call(name) === '[object Object]') {
212 this.fromJSON(name)
213 }
214 return this
215}
216
217/**
218 * Set the table border
219 *
220 * @param {String} horizontal edges (optional, default `|`)
221 * @param {String} vertical edges (optional, default `-`)
222 * @param {String} top corners (optional, default `.`)
223 * @param {String} bottom corners (optional, default `'`)
224 * @api public
225 */
226
227AsciiTable.prototype.setBorder = function(edge, fill, top, bottom) {
228 this.__border = true
229 if (arguments.length === 1) {
230 fill = top = bottom = edge
231 }
232 this.__edge = edge || '|'
233 this.__fill = fill || '-'
234 this.__top = top || '.'
235 this.__bottom = bottom || "'"
236 return this
237}
238
239/**
240 * Remove all table borders
241 *
242 * @api public
243 */
244
245AsciiTable.prototype.removeBorder = function() {
246 this.__border = false
247 this.__edge = ' '
248 this.__fill = ' '
249 return this
250}
251
252/**
253 * Set the column alignment at a given index
254 *
255 * @param {Number} column index
256 * @param {Number} alignment direction
257 * @api public
258 */
259
260AsciiTable.prototype.setAlign = function(idx, dir) {
261 this.__aligns[idx] = dir
262 return this
263}
264
265/**
266 * Set the title of the table
267 *
268 * @param {String} title
269 * @api public
270 */
271
272AsciiTable.prototype.setTitle = function(name) {
273 this.__name = name
274 return this
275}
276
277/**
278 * Get the title of the table
279 *
280 * @return {String} title
281 * @api public
282 */
283
284AsciiTable.prototype.getTitle = function() {
285 return this.__name
286}
287
288/**
289 * Set table title alignment
290 *
291 * @param {Number} direction
292 * @api public
293 */
294
295AsciiTable.prototype.setTitleAlign = function(dir) {
296 this.__nameAlign = dir
297 return this
298}
299
300/**
301 * AsciiTable sorting shortcut to sort rows
302 *
303 * @param {Function} sorting method
304 * @api public
305 */
306
307AsciiTable.prototype.sort = function(method) {
308 this.__rows.sort(method)
309 return this
310}
311
312/**
313 * Sort rows based on sort method for given column
314 *
315 * @param {Number} column index
316 * @param {Function} sorting method
317 * @api public
318 */
319
320AsciiTable.prototype.sortColumn = function(idx, method) {
321 this.__rows.sort(function(a, b) {
322 return method(a[idx], b[idx])
323 })
324 return this
325}
326
327/**
328 * Set table heading for columns
329 *
330 * @api public
331 */
332
333AsciiTable.prototype.setHeading = function(row) {
334 if (arguments.length > 1 || toString.call(row) !== '[object Array]') {
335 row = slice.call(arguments)
336 }
337 this.__heading = row
338 return this
339}
340
341/**
342 * Get table heading for columns
343 *
344 * @return {Array} copy of headings
345 * @api public
346 */
347
348AsciiTable.prototype.getHeading = function() {
349 return this.__heading.slice()
350}
351
352/**
353 * Set heading alignment
354 *
355 * @param {Number} direction
356 * @api public
357 */
358
359AsciiTable.prototype.setHeadingAlign = function(dir) {
360 this.__headingAlign = dir
361 return this
362}
363
364/**
365 * Add a row of information to the table
366 *
367 * @param {...|Array} argument values in order of columns
368 * @api public
369 */
370
371AsciiTable.prototype.addRow = function(row) {
372 if (arguments.length > 1 || toString.call(row) !== '[object Array]') {
373 row = slice.call(arguments)
374 }
375 this.__maxCells = Math.max(this.__maxCells, row.length)
376 this.__rows.push(row)
377 return this
378}
379
380/**
381 * Get a copy of all rows of the table
382 *
383 * @return {Array} copy of rows
384 * @api public
385 */
386
387AsciiTable.prototype.getRows = function() {
388 return this.__rows.slice().map(function(row) {
389 return row.slice()
390 })
391}
392
393/**
394 * Add rows in the format of a row matrix
395 *
396 * @param {Array} row matrix
397 * @api public
398 */
399
400AsciiTable.prototype.addRowMatrix = function(rows) {
401 for (var i = 0; i < rows.length; i++) {
402 this.addRow(rows[i])
403 }
404 return this
405}
406
407/**
408 * Add rows from the given data array, processed by the callback function rowCallback.
409 *
410 * @param {Array} data
411 * @param (Function) rowCallback
412 * @param (Boolean) asMatrix - controls if the row created by rowCallback should be assigned as row matrix
413 * @api public
414 */
415
416AsciiTable.prototype.addData = function(data, rowCallback, asMatrix) {
417 if (toString.call(data) !== '[object Array]') {
418 return this;
419 }
420 for (var index = 0, limit = data.length; index < limit; index++) {
421 var row = rowCallback(data[index]);
422 if(asMatrix) {
423 this.addRowMatrix(row);
424 } else {
425 this.addRow(row);
426 }
427 }
428 return this
429}
430
431 /**
432 * Reset the current row state
433 *
434 * @api public
435 */
436
437AsciiTable.prototype.clearRows = function() {
438 this.__rows = []
439 this.__maxCells = 0
440 this.__colMaxes = []
441 return this
442}
443
444/**
445 * Apply an even spaced column justification
446 *
447 * @param {Boolean} on / off
448 * @api public
449 */
450
451AsciiTable.prototype.setJustify = function(val) {
452 arguments.length === 0 && (val = true)
453 this.__justify = !!val
454 return this
455}
456
457/**
458 * Convert the current instance to a JSON structure
459 *
460 * @return {Object} json representation
461 * @api public
462 */
463
464AsciiTable.prototype.toJSON = function() {
465 return {
466 title: this.getTitle()
467 , heading: this.getHeading()
468 , rows: this.getRows()
469 }
470}
471
472/**
473 * Populate the table from a JSON object
474 *
475 * @param {Object} json representation
476 * @api public
477 */
478
479AsciiTable.prototype.parse =
480AsciiTable.prototype.fromJSON = function(obj) {
481 return this
482 .clear()
483 .setTitle(obj.title)
484 .setHeading(obj.heading)
485 .addRowMatrix(obj.rows)
486}
487
488/**
489 * Render the table with the current information
490 *
491 * @return {String} formatted table
492 * @api public
493 */
494
495AsciiTable.prototype.render =
496AsciiTable.prototype.valueOf =
497AsciiTable.prototype.toString = function() {
498 var self = this
499 , body = []
500 , mLen = this.__maxCells
501 , max = AsciiTable.arrayFill(mLen, 0)
502 , total = mLen * 3
503 , rows = this.__rows
504 , justify
505 , border = this.__border
506 , all = this.__heading
507 ? [this.__heading].concat(rows)
508 : rows
509
510 // Calculate max table cell lengths across all rows
511 for (var i = 0; i < all.length; i++) {
512 var row = all[i]
513 for (var k = 0; k < mLen; k++) {
514 var cell = row[k]
515 max[k] = Math.max(max[k], cell ? cell.toString().length : 0)
516 }
517 }
518 this.__colMaxes = max
519 justify = this.__justify ? Math.max.apply(null, max) : 0
520
521 // Get
522 max.forEach(function(x) {
523 total += justify ? justify : x + self.__spacing
524 })
525 justify && (total += max.length)
526 total -= this.__spacing
527
528 // Heading
529 border && body.push(this._seperator(total - mLen + 1, this.__top))
530 if (this.__name) {
531 body.push(this._renderTitle(total - mLen + 1))
532 border && body.push(this._seperator(total - mLen + 1))
533 }
534 if (this.__heading) {
535 body.push(this._renderRow(this.__heading, ' ', this.__headingAlign))
536 body.push(this._rowSeperator(mLen, this.__fill))
537 }
538 for (var i = 0; i < this.__rows.length; i++) {
539 body.push(this._renderRow(this.__rows[i], ' '))
540 }
541 border && body.push(this._seperator(total - mLen + 1, this.__bottom))
542
543 var prefix = this.options.prefix || ''
544 return prefix + body.join('\n' + prefix)
545}
546
547/**
548 * Create a line seperator
549 *
550 * @param {Number} string size
551 * @param {String} side values (default '|')
552 * @api private
553 */
554
555AsciiTable.prototype._seperator = function(len, sep) {
556 sep || (sep = this.__edge)
557 return sep + AsciiTable.alignRight(sep, len, this.__fill)
558}
559
560/**
561 * Create a row seperator
562 *
563 * @return {String} seperator
564 * @api private
565 */
566
567AsciiTable.prototype._rowSeperator = function() {
568 var blanks = AsciiTable.arrayFill(this.__maxCells, this.__fill)
569 return this._renderRow(blanks, this.__fill)
570}
571
572/**
573 * Render the table title in a centered box
574 *
575 * @param {Number} string size
576 * @return {String} formatted title
577 * @api private
578 */
579
580AsciiTable.prototype._renderTitle = function(len) {
581 var name = ' ' + this.__name + ' '
582 , str = AsciiTable.align(this.__nameAlign, name, len - 1, ' ')
583 return this.__edge + str + this.__edge
584}
585
586/**
587 * Render an invdividual row
588 *
589 * @param {Array} row
590 * @param {String} column seperator
591 * @param {Number} total row alignment (optional, default `auto`)
592 * @return {String} formatted row
593 * @api private
594 */
595
596AsciiTable.prototype._renderRow = function(row, str, align) {
597 var tmp = ['']
598 , max = this.__colMaxes
599
600 for (var k = 0; k < this.__maxCells; k++) {
601 var cell = row[k]
602 , just = this.__justify ? Math.max.apply(null, max) : max[k]
603 // , pad = k === this.__maxCells - 1 ? just : just + this.__spacing
604 , pad = just
605 , cAlign = this.__aligns[k]
606 , use = align
607 , method = 'alignAuto'
608
609 if (typeof align === 'undefined') use = cAlign
610
611 if (use === AsciiTable.LEFT) method = 'alignLeft'
612 if (use === AsciiTable.CENTER) method = 'alignCenter'
613 if (use === AsciiTable.RIGHT) method = 'alignRight'
614
615 tmp.push(AsciiTable[method](cell, pad, str))
616 }
617 var front = tmp.join(str + this.__edge + str)
618 front = front.substr(1, front.length)
619 return front + str + this.__edge
620}
621
622/*!
623 * Aliases
624 */
625
626// Create method shortcuts to all alignment methods for each direction
627;['Left', 'Right', 'Center'].forEach(function(dir) {
628 var constant = AsciiTable[dir.toUpperCase()]
629
630 ;['setAlign', 'setTitleAlign', 'setHeadingAlign'].forEach(function(method) {
631 // Call the base method with the direction constant as the last argument
632 AsciiTable.prototype[method + dir] = function() {
633 var args = slice.call(arguments).concat(constant)
634 return this[method].apply(this, args)
635 }
636 })
637})
638
639/*!
640 * Module exports.
641 */
642
643if (typeof exports !== 'undefined') {
644 module.exports = AsciiTable
645} else {
646 this.AsciiTable = AsciiTable
647}
648
649}).call(this);