treeify.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // treeify.js
  2. // Luke Plaster <notatestuser@gmail.com>
  3. // https://github.com/notatestuser/treeify.js
  4. // do the universal module definition dance
  5. (function (root, factory) {
  6. if (typeof exports === 'object') {
  7. module.exports = factory();
  8. } else if (typeof define === 'function' && define.amd) {
  9. define(factory);
  10. } else {
  11. root.treeify = factory();
  12. }
  13. }(this, function() {
  14. function makePrefix(key, last) {
  15. var str = (last ? '└' : '├');
  16. if (key) {
  17. str += '─ ';
  18. } else {
  19. str += '──┐';
  20. }
  21. return str;
  22. }
  23. function filterKeys(obj, hideFunctions) {
  24. var keys = [];
  25. for (var branch in obj) {
  26. // always exclude anything in the object's prototype
  27. if (!obj.hasOwnProperty(branch)) {
  28. continue;
  29. }
  30. // ... and hide any keys mapped to functions if we've been told to
  31. if (hideFunctions && ((typeof obj[branch])==="function")) {
  32. continue;
  33. }
  34. keys.push(branch);
  35. }
  36. return keys;
  37. }
  38. function growBranch(key, root, last, lastStates, showValues, hideFunctions, callback) {
  39. var line = '', index = 0, lastKey, circular, lastStatesCopy = lastStates.slice(0);
  40. if (lastStatesCopy.push([ root, last ]) && lastStates.length > 0) {
  41. // based on the "was last element" states of whatever we're nested within,
  42. // we need to append either blankness or a branch to our line
  43. lastStates.forEach(function(lastState, idx) {
  44. if (idx > 0) {
  45. line += (lastState[1] ? ' ' : '│') + ' ';
  46. }
  47. if ( ! circular && lastState[0] === root) {
  48. circular = true;
  49. }
  50. });
  51. // the prefix varies based on whether the key contains something to show and
  52. // whether we're dealing with the last element in this collection
  53. line += makePrefix(key, last) + key;
  54. // append values and the circular reference indicator
  55. showValues && (typeof root !== 'object' || root instanceof Date) && (line += ': ' + root);
  56. circular && (line += ' (circular ref.)');
  57. callback(line);
  58. }
  59. // can we descend into the next item?
  60. if ( ! circular && typeof root === 'object') {
  61. var keys = filterKeys(root, hideFunctions);
  62. keys.forEach(function(branch){
  63. // the last key is always printed with a different prefix, so we'll need to know if we have it
  64. lastKey = ++index === keys.length;
  65. // hold your breath for recursive action
  66. growBranch(branch, root[branch], lastKey, lastStatesCopy, showValues, hideFunctions, callback);
  67. });
  68. }
  69. };
  70. // --------------------
  71. var Treeify = {};
  72. // Treeify.asLines
  73. // --------------------
  74. // Outputs the tree line-by-line, calling the lineCallback when each one is available.
  75. Treeify.asLines = function(obj, showValues, hideFunctions, lineCallback) {
  76. /* hideFunctions and lineCallback are curried, which means we don't break apps using the older form */
  77. var hideFunctionsArg = typeof hideFunctions !== 'function' ? hideFunctions : false;
  78. growBranch('.', obj, false, [], showValues, hideFunctionsArg, lineCallback || hideFunctions);
  79. };
  80. // Treeify.asTree
  81. // --------------------
  82. // Outputs the entire tree, returning it as a string with line breaks.
  83. Treeify.asTree = function(obj, showValues, hideFunctions) {
  84. var tree = '';
  85. growBranch('.', obj, false, [], showValues, hideFunctions, function(line) {
  86. tree += line + '\n';
  87. });
  88. return tree;
  89. };
  90. // --------------------
  91. return Treeify;
  92. }));