From ab989f13f157da5b44a791043eaaa3de6dd18468 Mon Sep 17 00:00:00 2001
From: chylex <contact@chylex.com>
Date: Mon, 24 Jul 2017 11:46:13 +0200
Subject: [PATCH] Add support for being included as an npm module

---
 README.md  |   2 +-
 objtree.js | 371 ++++++++++++++++++++++++++++-------------------------
 2 files changed, 197 insertions(+), 176 deletions(-)

diff --git a/README.md b/README.md
index 25288c2..e6dfc93 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-`objtree` is a script that generates a text tree representation of an object. The script can be executed in a browser console.
+`objtree` is a script that generates a text tree representation of an object. The script can be executed in a browser console or imported as an `npm` module.
 
 # How to Use
 
diff --git a/objtree.js b/objtree.js
index 175a424..276f1d9 100644
--- a/objtree.js
+++ b/objtree.js
@@ -1,194 +1,215 @@
-const OBJTREE_OBJECT = 0;
-const OBJTREE_UNKNOWN = 1;
-const OBJTREE_COMPLEX_FUNCTION = 2;
-const OBJTREE_SIMPLE_FUNCTION = 3;
-const OBJTREE_PRIMARRAY = 4;
-const OBJTREE_VARIABLE = 5;
-const OBJTREE_TOODEEP = 6;
-
-const OBJTREE_NAMES = [
-  "[obj]", "[???]", "[fun]", "[fun]", "[arr]", "[var]", "[!!!]"
-];
-
-var objtree = function(target, {
-  maxlevel = 10,
-  grandparent = "",
-  exclude = []
-} = {}){
-  var excludeRules = exclude.map(rule => new RegExp(rule));
+(function(){
+  const OBJTREE_OBJECT = 0;
+  const OBJTREE_UNKNOWN = 1;
+  const OBJTREE_COMPLEX_FUNCTION = 2;
+  const OBJTREE_SIMPLE_FUNCTION = 3;
+  const OBJTREE_PRIMARRAY = 4;
+  const OBJTREE_VARIABLE = 5;
+  const OBJTREE_TOODEEP = 6;
   
-  var getObjectDesc = function(obj){
-    switch(typeof obj){
-      case "object":
-        if (obj === null){
-          return [ OBJTREE_VARIABLE, "(null)" ];
-        }
-        else if (Array.isArray(obj)){
-          if (obj.length === 0){
-            return [ OBJTREE_PRIMARRAY, "[]" ];
-          }
-          else if (obj.every(ele => {
-            let type = typeof ele;
-            return type === "boolean" || type === "number" || (type === "string" && ele.indexOf('\n') === -1);
-          })){
-            return [ OBJTREE_PRIMARRAY, "[ "+obj.join(", ")+" ]" ];
-          }
-        }
-        
-        return [ OBJTREE_OBJECT ]; // special handling
-
-      case "function":
-        if (Object.keys(obj).length === 0 && (!obj.prototype || Object.keys(obj.prototype).length === 0)){
-          return [ OBJTREE_SIMPLE_FUNCTION, obj.toString().match(/\((.*?)\)/)[0] ];
-        }
-        else{
-          return [ OBJTREE_COMPLEX_FUNCTION, obj.toString().match(/\((.*?)\)/)[0] ];
-        }
-
-      case "boolean":
-      case "number":
-      case "string":
-        return [ OBJTREE_VARIABLE, obj ];
-
-      case "undefined":
-        return [ OBJTREE_VARIABLE, "(undefined)" ];
-
-      default:
-        return [ OBJTREE_UNKNOWN, obj.toString() ];
-    }
-  };
+  const OBJTREE_NAMES = [
+    "[obj]", "[???]", "[fun]", "[fun]", "[arr]", "[var]", "[!!!]"
+  ];
   
-  var generateTree = function(node, parents, level){
-    let res = {};
+  var objtree = function(target, {
+    maxlevel = 10,
+    grandparent = "",
+    exclude = []
+  } = {}){
+    var excludeRules = exclude.map(rule => new RegExp(rule));
     
-    for(let key in node){
-      if (excludeRules.length){
-        let fullKey = parents+key;
-        
-        if (excludeRules.some(rule => rule.test(fullKey))){
-          res[key] = {
-            type: OBJTREE_UNKNOWN,
-            value: "(excluded)"
-          };
-          
-          continue;
-        }
-      }
-      
-      let obj = node[key];
-      let [ type, value ] = getObjectDesc(obj);
-      
-      if (type === OBJTREE_OBJECT){
-        if (level > maxlevel){
-          type = OBJTREE_TOODEEP;
-          value = "(too deep)";
-        }
-        else{
-          value = generateTree(obj, parents+key+"/", level+1);
-        }
-      }
-      else if (type === OBJTREE_COMPLEX_FUNCTION){
-        let data = { __args: value };
-        
-        for(let fkey in obj){
-          data[fkey] = obj[fkey];
-        }
-        
-        if (obj.prototype){
-          data.prototype = {};
-          
-          for(let pkey in obj.prototype){
-            if (pkey !== "constructor"){
-              data.prototype[pkey] = obj.prototype[pkey];
+    var getObjectDesc = function(obj){
+      switch(typeof obj){
+        case "object":
+          if (obj === null){
+            return [ OBJTREE_VARIABLE, "(null)" ];
+          }
+          else if (Array.isArray(obj)){
+            if (obj.length === 0){
+              return [ OBJTREE_PRIMARRAY, "[]" ];
+            }
+            else if (obj.every(ele => {
+              let type = typeof ele;
+              return type === "boolean" || type === "number" || (type === "string" && ele.indexOf('\n') === -1);
+            })){
+              return [ OBJTREE_PRIMARRAY, "[ "+obj.join(", ")+" ]" ];
             }
           }
-        }
-        
-        value = generateTree(data, parents+key+"/", level+1);
-      }
-      
-      res[key] = { type, value };
-    }
-    
-    return res;
-  };
-  
-  var tree = generateTree(target, "", 1);
-  
-  var obj = {
-    asObj: function(){
-      return tree;
-    },
-    
-    asText: function(){
-      var lines = [ "OBJECT TREE", "===========" ];
-      var grandpa = " "+(grandparent ? grandparent+"." : "");
-      
-      var sorter = function(entry1, entry2){
-        let v = entry1[1].type - entry2[1].type;
-        return v === 0 ? +(entry1[0] > entry2[0]) : v;
-      };
-      
-      var keyRegex = /^[a-z_$][a-z0-9_$]+$/i;
-      
-      var getKeyAccess = function(key){
-        return keyRegex.test(key) ? "."+key : "['"+key+"']";
-      }
-      
-      var varTypes = [
-        OBJTREE_VARIABLE, OBJTREE_PRIMARRAY, OBJTREE_UNKNOWN, OBJTREE_TOODEEP
-      ];
-      
-      var addLines = function(node, parents, level){
-        let entries = Object.entries(node);
-        let prefix = "  ".repeat(level)+"|-- ";
-        
-        let longest = Math.max.apply(null, entries
-          .filter(entry => varTypes.includes(entry[1].type))
-          .map(entry => (level === 0 ? entry[0] : getKeyAccess(entry[0])).length)
-        );
-        
-        for(let [key, desc] of entries.sort(sorter)){
-          let keyText = level === 0 ? key : getKeyAccess(key);
           
-          if (desc.type === OBJTREE_OBJECT){
-            lines.push(prefix+grandpa+parents+keyText);
-            addLines(desc.value, parents+keyText, level+1);
+          return [ OBJTREE_OBJECT ]; // special handling
+  
+        case "function":
+          if (Object.keys(obj).length === 0 && (!obj.prototype || Object.keys(obj.prototype).length === 0)){
+            return [ OBJTREE_SIMPLE_FUNCTION, obj.toString().match(/\((.*?)\)/)[0] ];
           }
           else{
-            let commonPre = prefix+OBJTREE_NAMES[desc.type]+grandpa+parents+keyText;
+            return [ OBJTREE_COMPLEX_FUNCTION, obj.toString().match(/\((.*?)\)/)[0] ];
+          }
+  
+        case "boolean":
+        case "number":
+        case "string":
+          return [ OBJTREE_VARIABLE, obj ];
+  
+        case "undefined":
+          return [ OBJTREE_VARIABLE, "(undefined)" ];
+  
+        default:
+          return [ OBJTREE_UNKNOWN, obj.toString() ];
+      }
+    };
+    
+    var generateTree = function(node, parents, level){
+      let res = {};
+      
+      for(let key in node){
+        if (excludeRules.length){
+          let fullKey = parents+key;
+          
+          if (excludeRules.some(rule => rule.test(fullKey))){
+            res[key] = {
+              type: OBJTREE_UNKNOWN,
+              value: "(excluded)"
+            };
             
-            if (desc.type === OBJTREE_SIMPLE_FUNCTION){
-              lines.push(commonPre+desc.value);
+            continue;
+          }
+        }
+        
+        let obj = node[key];
+        let [ type, value ] = getObjectDesc(obj);
+        
+        if (type === OBJTREE_OBJECT){
+          if (level > maxlevel){
+            type = OBJTREE_TOODEEP;
+            value = "(too deep)";
+          }
+          else{
+            value = generateTree(obj, parents+key+"/", level+1);
+          }
+        }
+        else if (type === OBJTREE_COMPLEX_FUNCTION){
+          let data = { __args: value };
+          
+          for(let fkey in obj){
+            data[fkey] = obj[fkey];
+          }
+          
+          if (obj.prototype){
+            data.prototype = {};
+            
+            for(let pkey in obj.prototype){
+              if (pkey !== "constructor"){
+                data.prototype[pkey] = obj.prototype[pkey];
+              }
             }
-            else if (desc.type === OBJTREE_COMPLEX_FUNCTION){
-              lines.push(commonPre+desc.value.__args.value);
-              delete desc.value.__args;
+          }
+          
+          value = generateTree(data, parents+key+"/", level+1);
+        }
+        
+        res[key] = { type, value };
+      }
+      
+      return res;
+    };
+    
+    var tree = generateTree(target, "", 1);
+    
+    var obj = {
+      asObj: function(){
+        return tree;
+      },
+      
+      asText: function(){
+        var lines = [ "OBJECT TREE", "===========" ];
+        var grandpa = " "+(grandparent ? grandparent+"." : "");
+        
+        var sorter = function(entry1, entry2){
+          let v = entry1[1].type - entry2[1].type;
+          return v === 0 ? +(entry1[0] > entry2[0]) : v;
+        };
+        
+        var keyRegex = /^[a-z_$][a-z0-9_$]+$/i;
+        
+        var getKeyAccess = function(key){
+          return keyRegex.test(key) ? "."+key : "['"+key+"']";
+        }
+        
+        var varTypes = [
+          OBJTREE_VARIABLE, OBJTREE_PRIMARRAY, OBJTREE_UNKNOWN, OBJTREE_TOODEEP
+        ];
+        
+        var addLines = function(node, parents, level){
+          let entries = Object.entries(node);
+          let prefix = "  ".repeat(level)+"|-- ";
+          
+          let longest = Math.max.apply(null, entries
+            .filter(entry => varTypes.includes(entry[1].type))
+            .map(entry => (level === 0 ? entry[0] : getKeyAccess(entry[0])).length)
+          );
+          
+          for(let [key, desc] of entries.sort(sorter)){
+            let keyText = level === 0 ? key : getKeyAccess(key);
+            
+            if (desc.type === OBJTREE_OBJECT){
+              lines.push(prefix+grandpa+parents+keyText);
               addLines(desc.value, parents+keyText, level+1);
             }
             else{
-              lines.push(commonPre+(" ".repeat(longest-keyText.length))+" > "+desc.value);
+              let commonPre = prefix+OBJTREE_NAMES[desc.type]+grandpa+parents+keyText;
+              
+              if (desc.type === OBJTREE_SIMPLE_FUNCTION){
+                lines.push(commonPre+desc.value);
+              }
+              else if (desc.type === OBJTREE_COMPLEX_FUNCTION){
+                lines.push(commonPre+desc.value.__args.value);
+                delete desc.value.__args;
+                addLines(desc.value, parents+keyText, level+1);
+              }
+              else{
+                lines.push(commonPre+(" ".repeat(longest-keyText.length))+" > "+desc.value);
+              }
             }
           }
-        }
-      };
+        };
+        
+        addLines(tree, "", 0);
+        return lines.join("\n");
+      },
       
-      addLines(tree, "", 0);
-      return lines.join("\n");
-    },
+      downloadText: function(filename){
+        if (typeof window === "undefined"){
+          throw "objtree.downloadText is only supported in a browser";
+        }
+        
+        let url = window.URL.createObjectURL(new Blob([obj.asText()], { "type": "octet/stream" }));
+        let ele = document.createElement("a");
+        document.body.appendChild(ele);
+        ele.href = url;
+        ele.download = filename;
+        ele.style.display = "none";
+        ele.click();
+        document.body.removeChild(ele);
+        window.URL.revokeObjectURL(url);
+      }
+    };
     
-    downloadText: function(filename){
-      let url = window.URL.createObjectURL(new Blob([obj.asText()], { "type": "octet/stream" }));
-      let ele = document.createElement("a");
-      document.body.appendChild(ele);
-      ele.href = url;
-      ele.download = filename;
-      ele.style.display = "none";
-      ele.click();
-      document.body.removeChild(ele);
-      window.URL.revokeObjectURL(url);
-    }
+    return obj;
   };
   
-  return obj;
-};
+  objtree.TYPE_OBJECT = OBJTREE_OBJECT;
+  objtree.TYPE_UNKNOWN = OBJTREE_UNKNOWN;
+  objtree.TYPE_COMPLEX_FUNCTION = OBJTREE_COMPLEX_FUNCTION;
+  objtree.TYPE_SIMPLE_FUNCTION = OBJTREE_SIMPLE_FUNCTION;
+  objtree.TYPE_PRIMARRAY = OBJTREE_PRIMARRAY;
+  objtree.TYPE_VARIABLE = OBJTREE_VARIABLE;
+  objtree.TYPE_TOODEEP = OBJTREE_TOODEEP;
+  
+  if (typeof module !== "undefined" && typeof module.exports !== "undefined"){
+    module.exports = objtree;
+  }
+  else{
+    window.objtree = objtree;
+  }
+})();