| // treeify.js | |
| // Luke Plaster <notatestuser@gmail.com> | |
| // https://github.com/notatestuser/treeify.js | |
| // do the universal module definition dance | |
| (function (root, factory) { | |
| if (typeof exports === 'object') { | |
| module.exports = factory(); | |
| } else if (typeof define === 'function' && define.amd) { | |
| define(factory); | |
| } else { | |
| root.treeify = factory(); | |
| } | |
| }(this, function() { | |
| function makePrefix(key, last) { | |
| var str = (last ? '└' : '├'); | |
| if (key) { | |
| str += '─ '; | |
| } else { | |
| str += '──┐'; | |
| } | |
| return str; | |
| } | |
| function filterKeys(obj, hideFunctions) { | |
| var keys = []; | |
| for (var branch in obj) { | |
| // always exclude anything in the object's prototype | |
| if (!obj.hasOwnProperty(branch)) { | |
| continue; | |
| } | |
| // ... and hide any keys mapped to functions if we've been told to | |
| if (hideFunctions && ((typeof obj[branch])==="function")) { | |
| continue; | |
| } | |
| keys.push(branch); | |
| } | |
| return keys; | |
| } | |
| function growBranch(key, root, last, lastStates, showValues, hideFunctions, callback) { | |
| var line = '', index = 0, lastKey, circular, lastStatesCopy = lastStates.slice(0); | |
| if (lastStatesCopy.push([ root, last ]) && lastStates.length > 0) { | |
| // based on the "was last element" states of whatever we're nested within, | |
| // we need to append either blankness or a branch to our line | |
| lastStates.forEach(function(lastState, idx) { | |
| if (idx > 0) { | |
| line += (lastState[1] ? ' ' : '│') + ' '; | |
| } | |
| if ( ! circular && lastState[0] === root) { | |
| circular = true; | |
| } | |
| }); | |
| // the prefix varies based on whether the key contains something to show and | |
| // whether we're dealing with the last element in this collection | |
| line += makePrefix(key, last) + key; | |
| // append values and the circular reference indicator | |
| showValues && (typeof root !== 'object' || root instanceof Date) && (line += ': ' + root); | |
| circular && (line += ' (circular ref.)'); | |
| callback(line); | |
| } | |
| // can we descend into the next item? | |
| if ( ! circular && typeof root === 'object') { | |
| var keys = filterKeys(root, hideFunctions); | |
| keys.forEach(function(branch){ | |
| // the last key is always printed with a different prefix, so we'll need to know if we have it | |
| lastKey = ++index === keys.length; | |
| // hold your breath for recursive action | |
| growBranch(branch, root[branch], lastKey, lastStatesCopy, showValues, hideFunctions, callback); | |
| }); | |
| } | |
| }; | |
| // -------------------- | |
| var Treeify = {}; | |
| // Treeify.asLines | |
| // -------------------- | |
| // Outputs the tree line-by-line, calling the lineCallback when each one is available. | |
| Treeify.asLines = function(obj, showValues, hideFunctions, lineCallback) { | |
| /* hideFunctions and lineCallback are curried, which means we don't break apps using the older form */ | |
| var hideFunctionsArg = typeof hideFunctions !== 'function' ? hideFunctions : false; | |
| growBranch('.', obj, false, [], showValues, hideFunctionsArg, lineCallback || hideFunctions); | |
| }; | |
| // Treeify.asTree | |
| // -------------------- | |
| // Outputs the entire tree, returning it as a string with line breaks. | |
| Treeify.asTree = function(obj, showValues, hideFunctions) { | |
| var tree = ''; | |
| growBranch('.', obj, false, [], showValues, hideFunctions, function(line) { | |
| tree += line + '\n'; | |
| }); | |
| return tree; | |
| }; | |
| // -------------------- | |
| return Treeify; | |
| })); |