| module.exports = function (rows_, opts) { | 
|     if (!opts) opts = {}; | 
|     var hsep = opts.hsep === undefined ? '  ' : opts.hsep; | 
|     var align = opts.align || []; | 
|     var stringLength = opts.stringLength | 
|         || function (s) { return String(s).length; } | 
|     ; | 
|      | 
|     var dotsizes = reduce(rows_, function (acc, row) { | 
|         forEach(row, function (c, ix) { | 
|             var n = dotindex(c); | 
|             if (!acc[ix] || n > acc[ix]) acc[ix] = n; | 
|         }); | 
|         return acc; | 
|     }, []); | 
|      | 
|     var rows = map(rows_, function (row) { | 
|         return map(row, function (c_, ix) { | 
|             var c = String(c_); | 
|             if (align[ix] === '.') { | 
|                 var index = dotindex(c); | 
|                 var size = dotsizes[ix] + (/\./.test(c) ? 1 : 2) | 
|                     - (stringLength(c) - index) | 
|                 ; | 
|                 return c + Array(size).join(' '); | 
|             } | 
|             else return c; | 
|         }); | 
|     }); | 
|      | 
|     var sizes = reduce(rows, function (acc, row) { | 
|         forEach(row, function (c, ix) { | 
|             var n = stringLength(c); | 
|             if (!acc[ix] || n > acc[ix]) acc[ix] = n; | 
|         }); | 
|         return acc; | 
|     }, []); | 
|      | 
|     return map(rows, function (row) { | 
|         return map(row, function (c, ix) { | 
|             var n = (sizes[ix] - stringLength(c)) || 0; | 
|             var s = Array(Math.max(n + 1, 1)).join(' '); | 
|             if (align[ix] === 'r' || align[ix] === '.') { | 
|                 return s + c; | 
|             } | 
|             if (align[ix] === 'c') { | 
|                 return Array(Math.ceil(n / 2 + 1)).join(' ') | 
|                     + c + Array(Math.floor(n / 2 + 1)).join(' ') | 
|                 ; | 
|             } | 
|              | 
|             return c + s; | 
|         }).join(hsep).replace(/\s+$/, ''); | 
|     }).join('\n'); | 
| }; | 
|   | 
| function dotindex (c) { | 
|     var m = /\.[^.]*$/.exec(c); | 
|     return m ? m.index + 1 : c.length; | 
| } | 
|   | 
| function reduce (xs, f, init) { | 
|     if (xs.reduce) return xs.reduce(f, init); | 
|     var i = 0; | 
|     var acc = arguments.length >= 3 ? init : xs[i++]; | 
|     for (; i < xs.length; i++) { | 
|         f(acc, xs[i], i); | 
|     } | 
|     return acc; | 
| } | 
|   | 
| function forEach (xs, f) { | 
|     if (xs.forEach) return xs.forEach(f); | 
|     for (var i = 0; i < xs.length; i++) { | 
|         f.call(xs, xs[i], i); | 
|     } | 
| } | 
|   | 
| function map (xs, f) { | 
|     if (xs.map) return xs.map(f); | 
|     var res = []; | 
|     for (var i = 0; i < xs.length; i++) { | 
|         res.push(f.call(xs, xs[i], i)); | 
|     } | 
|     return res; | 
| } |