(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; const OBJTREE_NAMES = [ "[obj]", "[???]", "[fun]", "[fun]", "[arr]", "[var]", "[!!!]" ]; var objtree = function(target, { maxlevel = 10, grandparent = "", exclude = [] } = {}){ var excludeRules = exclude.map(rule => new RegExp(rule)); 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() ]; } }; 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)" }; 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]; } } } 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{ 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"); }, 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); } }; 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; } })();