mirror of
https://github.com/liriliri/eruda.git
synced 2026-03-24 09:48:37 +08:00
160 lines
4.6 KiB
JavaScript
160 lines
4.6 KiB
JavaScript
import util from './util'
|
|
|
|
// Modified from: https://jsconsole.com/
|
|
export default function stringify(obj, visited, topObj, simple)
|
|
{
|
|
let json = '',
|
|
type = '',
|
|
parts = [],
|
|
names = [],
|
|
proto,
|
|
circular = false;
|
|
|
|
visited = visited || [];
|
|
topObj = topObj || obj;
|
|
|
|
try {
|
|
type = ({}).toString.call(obj);
|
|
} catch (e)
|
|
{
|
|
type = '[object Object]';
|
|
}
|
|
|
|
var isFn = (type == '[object Function]'),
|
|
isStr = (type == '[object String]'),
|
|
isArr = (type == '[object Array]'),
|
|
isObj = (type == '[object Object]'),
|
|
isNum = (type == '[object Number]'),
|
|
isSymbol = (type == '[object Symbol]'),
|
|
isBool = (type == '[object Boolean]');
|
|
|
|
for (let i = 0, len = visited.length; i < len; i++)
|
|
{
|
|
if (obj === visited[i])
|
|
{
|
|
circular = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (circular)
|
|
{
|
|
json = '"[circular]"';
|
|
} else if (isStr)
|
|
{
|
|
json = `"${escapeJsonStr(obj)}"`;
|
|
} else if (isArr)
|
|
{
|
|
visited.push(obj);
|
|
|
|
json = '[';
|
|
util.each(obj, val => parts.push(`${stringify(val, visited, null, simple)}`));
|
|
json += parts.join(', ') + ']';
|
|
} else if (isObj || isFn)
|
|
{
|
|
visited.push(obj);
|
|
|
|
names = Object.getOwnPropertyNames(obj);
|
|
proto = Object.getPrototypeOf(obj);
|
|
if (proto === Object.prototype || isFn || simple) proto = null;
|
|
if (proto) proto = `"erudaProto": ${stringify(proto, visited, topObj)}`;
|
|
names.sort(sortObjName);
|
|
if (isFn)
|
|
{
|
|
// We don't these properties to be display for functions.
|
|
names = names.filter(val => ['arguments', 'caller', 'name', 'length', 'prototype'].indexOf(val) < 0);
|
|
}
|
|
if (names.length === 0 && isFn)
|
|
{
|
|
json = `"${escapeJsonStr(obj.toString())}"`;
|
|
} else
|
|
{
|
|
json = '{';
|
|
if (isFn)
|
|
{
|
|
// Function length is restricted to 500 for performance reason.
|
|
var fnStr = obj.toString();
|
|
if (fnStr.length > 500) fnStr = fnStr.slice(0, 500) + '...';
|
|
parts.push(`"erudaObjAbstract": "${escapeJsonStr(fnStr)}"`);
|
|
}
|
|
util.each(names, name =>
|
|
{
|
|
parts.push(`"${escapeJsonStr(name)}": ${stringify(topObj[name], visited, null, simple)}`);
|
|
});
|
|
if (proto) parts.push(proto);
|
|
json += parts.join(', ') + '}';
|
|
}
|
|
} else if (isNum)
|
|
{
|
|
json = obj + '';
|
|
if (util.endWith(json, 'Infinity') || json === 'NaN') json = `"${json}"`;
|
|
} else if (isBool)
|
|
{
|
|
json = obj ? 'true' : 'false';
|
|
} else if (obj === null)
|
|
{
|
|
json = 'null';
|
|
} else if (isSymbol)
|
|
{
|
|
json = '"Symbol"';
|
|
} else if (obj === undefined)
|
|
{
|
|
json = '"undefined"';
|
|
} else if (type === '[object HTMLAllCollection]')
|
|
{
|
|
// https://docs.webplatform.org/wiki/dom/HTMLAllCollection
|
|
// Might cause a performance issue when stringify a dom element.
|
|
json = '"[object HTMLAllCollection]"';
|
|
} else
|
|
{
|
|
try
|
|
{
|
|
visited.push(obj);
|
|
|
|
json = '{\n';
|
|
if (!simple) parts.push(`"erudaObjAbstract": "${type.replace(/(\[object )|]/g, '')}"`);
|
|
names = Object.getOwnPropertyNames(obj);
|
|
proto = Object.getPrototypeOf(obj);
|
|
if (proto === Object.prototype || simple) proto = null;
|
|
if (proto)
|
|
{
|
|
try
|
|
{
|
|
proto = `"erudaProto": ${stringify(proto, visited, topObj)}`;
|
|
} catch(e)
|
|
{
|
|
proto = `"erudaProto": "${escapeJsonStr(e.message)}"`;
|
|
}
|
|
}
|
|
names.sort(sortObjName);
|
|
util.each(names, name =>
|
|
{
|
|
parts.push(`"${escapeJsonStr(name)}": ${stringify(topObj[name], visited, null, simple)}`);
|
|
});
|
|
if (proto) parts.push(proto);
|
|
json += parts.join(',\n') + '\n}';
|
|
} catch (e) {
|
|
json = `"${obj}"`;
|
|
}
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
var escapeJsonStr = str => str.replace(/\\/g, '\\\\')
|
|
.replace(/"/g, '\\"')
|
|
.replace(/\f|\n|\r|\t/g, '');
|
|
|
|
var sortObjName = (a, b) =>
|
|
{
|
|
let codeA = a.charCodeAt(0),
|
|
codeB = b.charCodeAt(0);
|
|
|
|
if (isLetter(codeA) && !isLetter(codeB)) return -1;
|
|
if (!isLetter(codeA) && isLetter(codeB)) return 1;
|
|
|
|
return a > b ? 1 : -1;
|
|
};
|
|
|
|
var isLetter = code => (code > 64 && code < 90) || (code > 96 && code < 123);
|