]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/node_modules/columnify/index.js
Adding integrated tile server
[simantics/district.git] / org.simantics.maps.server / node / node-v4.8.0-win-x64 / node_modules / npm / node_modules / columnify / index.js
1 "use strict"
2
3 const wcwidth = require('./width')
4 const {
5   padRight,
6   padCenter,
7   padLeft,
8   splitIntoLines,
9   splitLongWords,
10   truncateString
11 } = require('./utils')
12
13 const DEFAULT_HEADING_TRANSFORM = key => key.toUpperCase()
14
15 const DEFAULT_DATA_TRANSFORM = (cell, column, index) => cell
16
17 const DEFAULTS = Object.freeze({
18   maxWidth: Infinity,
19   minWidth: 0,
20   columnSplitter: ' ',
21   truncate: false,
22   truncateMarker: '…',
23   preserveNewLines: false,
24   paddingChr: ' ',
25   showHeaders: true,
26   headingTransform: DEFAULT_HEADING_TRANSFORM,
27   dataTransform: DEFAULT_DATA_TRANSFORM
28 })
29
30 module.exports = function(items, options = {}) {
31
32   let columnConfigs = options.config || {}
33   delete options.config // remove config so doesn't appear on every column.
34
35   let maxLineWidth = options.maxLineWidth || Infinity
36   if (maxLineWidth === 'auto') maxLineWidth = process.stdout.columns || Infinity
37   delete options.maxLineWidth // this is a line control option, don't pass it to column
38
39   // Option defaults inheritance:
40   // options.config[columnName] => options => DEFAULTS
41   options = mixin({}, DEFAULTS, options)
42
43   options.config = options.config || Object.create(null)
44
45   options.spacing = options.spacing || '\n' // probably useless
46   options.preserveNewLines = !!options.preserveNewLines
47   options.showHeaders = !!options.showHeaders;
48   options.columns = options.columns || options.include // alias include/columns, prefer columns if supplied
49   let columnNames = options.columns || [] // optional user-supplied columns to include
50
51   items = toArray(items, columnNames)
52
53   // if not suppled column names, automatically determine columns from data keys
54   if (!columnNames.length) {
55     items.forEach(function(item) {
56       for (let columnName in item) {
57         if (columnNames.indexOf(columnName) === -1) columnNames.push(columnName)
58       }
59     })
60   }
61
62   // initialize column defaults (each column inherits from options.config)
63   let columns = columnNames.reduce((columns, columnName) => {
64     let column = Object.create(options)
65     columns[columnName] = mixin(column, columnConfigs[columnName])
66     return columns
67   }, Object.create(null))
68
69   // sanitize column settings
70   columnNames.forEach(columnName => {
71     let column = columns[columnName]
72     column.name = columnName
73     column.maxWidth = Math.ceil(column.maxWidth)
74     column.minWidth = Math.ceil(column.minWidth)
75     column.truncate = !!column.truncate
76     column.align = column.align || 'left'
77   })
78
79   // sanitize data
80   items = items.map(item => {
81     let result = Object.create(null)
82     columnNames.forEach(columnName => {
83       // null/undefined -> ''
84       result[columnName] = item[columnName] != null ? item[columnName] : ''
85       // toString everything
86       result[columnName] = '' + result[columnName]
87       if (columns[columnName].preserveNewLines) {
88         // merge non-newline whitespace chars
89         result[columnName] = result[columnName].replace(/[^\S\n]/gmi, ' ')
90       } else {
91         // merge all whitespace chars
92         result[columnName] = result[columnName].replace(/\s/gmi, ' ')
93       }
94     })
95     return result
96   })
97
98   // transform data cells
99   columnNames.forEach(columnName => {
100     let column = columns[columnName]
101     items = items.map((item, index) => {
102       let col = Object.create(column)
103       item[columnName] = column.dataTransform(item[columnName], col, index)
104
105       let changedKeys = Object.keys(col)
106       // disable default heading transform if we wrote to column.name
107       if (changedKeys.indexOf('name') !== -1) {
108         if (column.headingTransform !== DEFAULT_HEADING_TRANSFORM) return
109         column.headingTransform = heading => heading
110       }
111       changedKeys.forEach(key => column[key] = col[key])
112       return item
113     })
114   })
115
116   // add headers
117   let headers = {}
118   if(options.showHeaders) {
119     columnNames.forEach(columnName => {
120       let column = columns[columnName]
121
122       if(!column.showHeaders){
123         headers[columnName] = '';
124         return;
125       }
126
127       headers[columnName] = column.headingTransform(column.name)
128     })
129     items.unshift(headers)
130   }
131   // get actual max-width between min & max
132   // based on length of data in columns
133   columnNames.forEach(columnName => {
134     let column = columns[columnName]
135     column.width = items
136     .map(item => item[columnName])
137     .reduce((min, cur) => {
138       // if already at maxWidth don't bother testing
139       if (min >= column.maxWidth) return min
140       return Math.max(min, Math.min(column.maxWidth, Math.max(column.minWidth, wcwidth(cur))))
141     }, 0)
142   })
143
144   // split long words so they can break onto multiple lines
145   columnNames.forEach(columnName => {
146     let column = columns[columnName]
147     items = items.map(item => {
148       item[columnName] = splitLongWords(item[columnName], column.width, column.truncateMarker)
149       return item
150     })
151   })
152
153   // wrap long lines. each item is now an array of lines.
154   columnNames.forEach(columnName => {
155     let column = columns[columnName]
156     items = items.map((item, index) => {
157       let cell = item[columnName]
158       item[columnName] = splitIntoLines(cell, column.width)
159
160       // if truncating required, only include first line + add truncation char
161       if (column.truncate && item[columnName].length > 1) {
162         item[columnName] = splitIntoLines(cell, column.width - wcwidth(column.truncateMarker))
163         let firstLine = item[columnName][0]
164         if (!endsWith(firstLine, column.truncateMarker)) item[columnName][0] += column.truncateMarker
165         item[columnName] = item[columnName].slice(0, 1)
166       }
167       return item
168     })
169   })
170
171   // recalculate column widths from truncated output/lines
172   columnNames.forEach(columnName => {
173     let column = columns[columnName]
174     column.width = items.map(item => {
175       return item[columnName].reduce((min, cur) => {
176         if (min >= column.maxWidth) return min
177         return Math.max(min, Math.min(column.maxWidth, Math.max(column.minWidth, wcwidth(cur))))
178       }, 0)
179     }).reduce((min, cur) => {
180       if (min >= column.maxWidth) return min
181       return Math.max(min, Math.min(column.maxWidth, Math.max(column.minWidth, cur)))
182     }, 0)
183   })
184
185
186   let rows = createRows(items, columns, columnNames, options.paddingChr) // merge lines into rows
187   // conceive output
188   return rows.reduce((output, row) => {
189     return output.concat(row.reduce((rowOut, line) => {
190       return rowOut.concat(line.join(options.columnSplitter))
191     }, []))
192   }, [])
193   .map(line => truncateString(line, maxLineWidth))
194   .join(options.spacing)
195 }
196
197 /**
198  * Convert wrapped lines into rows with padded values.
199  *
200  * @param Array items data to process
201  * @param Array columns column width settings for wrapping
202  * @param Array columnNames column ordering
203  * @return Array items wrapped in arrays, corresponding to lines
204  */
205
206 function createRows(items, columns, columnNames, paddingChr) {
207   return items.map(item => {
208     let row = []
209     let numLines = 0
210     columnNames.forEach(columnName => {
211       numLines = Math.max(numLines, item[columnName].length)
212     })
213     // combine matching lines of each rows
214     for (let i = 0; i < numLines; i++) {
215       row[i] = row[i] || []
216       columnNames.forEach(columnName => {
217         let column = columns[columnName]
218         let val = item[columnName][i] || '' // || '' ensures empty columns get padded
219         if (column.align === 'right') row[i].push(padLeft(val, column.width, paddingChr))
220         else if (column.align === 'center' || column.align === 'centre') row[i].push(padCenter(val, column.width, paddingChr))
221         else row[i].push(padRight(val, column.width, paddingChr))
222       })
223     }
224     return row
225   })
226 }
227
228 /**
229  * Object.assign
230  *
231  * @return Object Object with properties mixed in.
232  */
233
234 function mixin(...args) {
235   if (Object.assign) return Object.assign(...args)
236   return ObjectAssign(...args)
237 }
238
239 function ObjectAssign(target, firstSource) {
240   "use strict";
241   if (target === undefined || target === null)
242     throw new TypeError("Cannot convert first argument to object");
243
244   var to = Object(target);
245
246   var hasPendingException = false;
247   var pendingException;
248
249   for (var i = 1; i < arguments.length; i++) {
250     var nextSource = arguments[i];
251     if (nextSource === undefined || nextSource === null)
252       continue;
253
254     var keysArray = Object.keys(Object(nextSource));
255     for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
256       var nextKey = keysArray[nextIndex];
257       try {
258         var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
259         if (desc !== undefined && desc.enumerable)
260           to[nextKey] = nextSource[nextKey];
261       } catch (e) {
262         if (!hasPendingException) {
263           hasPendingException = true;
264           pendingException = e;
265         }
266       }
267     }
268
269     if (hasPendingException)
270       throw pendingException;
271   }
272   return to;
273 }
274
275 /**
276  * Adapted from String.prototype.endsWith polyfill.
277  */
278
279 function endsWith(target, searchString, position) {
280   position = position || target.length;
281   position = position - searchString.length;
282   let lastIndex = target.lastIndexOf(searchString);
283   return lastIndex !== -1 && lastIndex === position;
284 }
285
286
287 function toArray(items, columnNames) {
288   if (Array.isArray(items)) return items
289   let rows = []
290   for (let key in items) {
291     let item = {}
292     item[columnNames[0] || 'key'] = key
293     item[columnNames[1] || 'value'] = items[key]
294     rows.push(item)
295   }
296   return rows
297 }