"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/columnify/index.js" (11 Apr 2017, 9408 Bytes) of package /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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 }