| // 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; |
| |
| })); |