Files
eruda/src/lib/stringify.es6
2016-06-28 13:54:01 +08:00

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