mirror of
https://github.com/liriliri/eruda.git
synced 2026-03-24 09:48:37 +08:00
Change format options
This commit is contained in:
@@ -1,324 +1,314 @@
|
||||
import {
|
||||
escapeJsonStr,
|
||||
toStr,
|
||||
each,
|
||||
endWith,
|
||||
contain,
|
||||
filter,
|
||||
isEmpty,
|
||||
isArr,
|
||||
isFn,
|
||||
isRegExp,
|
||||
uniqId,
|
||||
last,
|
||||
extend
|
||||
} from './stringifyUtil';
|
||||
escapeJsonStr,
|
||||
toStr,
|
||||
each,
|
||||
endWith,
|
||||
contain,
|
||||
filter,
|
||||
isEmpty,
|
||||
isArr,
|
||||
isFn,
|
||||
isRegExp,
|
||||
uniqId,
|
||||
last,
|
||||
extend
|
||||
} from './stringifyUtil'
|
||||
|
||||
// Modified from: https://jsconsole.com/
|
||||
export default function stringify(
|
||||
obj,
|
||||
{
|
||||
visitor = new Visitor(),
|
||||
topObj,
|
||||
level = 0,
|
||||
circularMarker = false,
|
||||
getterVal = false,
|
||||
unenumerable = true
|
||||
} = {}
|
||||
obj,
|
||||
{
|
||||
visitor = new Visitor(),
|
||||
topObj,
|
||||
level = 0,
|
||||
circularMarker = false,
|
||||
getterVal = false,
|
||||
unenumerable = true
|
||||
} = {}
|
||||
) {
|
||||
let json = '',
|
||||
type,
|
||||
parts = [],
|
||||
names = [],
|
||||
proto,
|
||||
objAbstract,
|
||||
circularObj,
|
||||
allKeys,
|
||||
keys,
|
||||
id = '';
|
||||
let json = '',
|
||||
type,
|
||||
parts = [],
|
||||
names = [],
|
||||
proto,
|
||||
objAbstract,
|
||||
circularObj,
|
||||
allKeys,
|
||||
keys,
|
||||
id = ''
|
||||
|
||||
topObj = topObj || obj;
|
||||
topObj = topObj || obj
|
||||
|
||||
let passOpts = { visitor, getterVal, unenumerable, level: level + 1 },
|
||||
passProtoOpts = {
|
||||
visitor,
|
||||
getterVal,
|
||||
topObj,
|
||||
unenumerable,
|
||||
level: level + 1
|
||||
};
|
||||
let passOpts = { visitor, getterVal, unenumerable, level: level + 1 },
|
||||
passProtoOpts = {
|
||||
visitor,
|
||||
getterVal,
|
||||
topObj,
|
||||
unenumerable,
|
||||
level: level + 1
|
||||
}
|
||||
|
||||
let wrapKey = key => `"${escapeJsonStr(key)}"`,
|
||||
wrapStr = str => `"${escapeJsonStr(toStr(str))}"`;
|
||||
let wrapKey = key => `"${escapeJsonStr(key)}"`,
|
||||
wrapStr = str => `"${escapeJsonStr(toStr(str))}"`
|
||||
|
||||
type = getType(obj);
|
||||
type = getType(obj)
|
||||
|
||||
let 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]';
|
||||
let 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]'
|
||||
|
||||
circularObj = visitor.check(obj);
|
||||
circularObj = visitor.check(obj)
|
||||
|
||||
if (circularObj) {
|
||||
json = stringify(circularObj.abstract, { circularMarker: true });
|
||||
} else if (isStr) {
|
||||
json = wrapStr(obj);
|
||||
} else if (isArr || isObj || isFn) {
|
||||
id = visitor.visit(obj);
|
||||
if (circularObj) {
|
||||
json = stringify(circularObj.abstract, { circularMarker: true })
|
||||
} else if (isStr) {
|
||||
json = wrapStr(obj)
|
||||
} else if (isArr || isObj || isFn) {
|
||||
id = visitor.visit(obj)
|
||||
|
||||
if (canBeProto(obj)) {
|
||||
obj = Object.getPrototypeOf(obj);
|
||||
id = visitor.visit(obj);
|
||||
}
|
||||
if (canBeProto(obj)) {
|
||||
obj = Object.getPrototypeOf(obj)
|
||||
id = visitor.visit(obj)
|
||||
}
|
||||
|
||||
names = getKeys(obj);
|
||||
keys = names.keys;
|
||||
allKeys = names.allKeys;
|
||||
names = unenumerable ? allKeys : keys;
|
||||
names = getKeys(obj)
|
||||
keys = names.keys
|
||||
allKeys = names.allKeys
|
||||
names = unenumerable ? allKeys : keys
|
||||
|
||||
proto = Object.getPrototypeOf(obj);
|
||||
if (circularMarker && proto === Object.prototype) proto = null;
|
||||
if (proto) {
|
||||
proto = `${wrapKey('erudaProto')}: ${stringify(
|
||||
proto,
|
||||
passProtoOpts
|
||||
)}`;
|
||||
}
|
||||
if (isFn) {
|
||||
// We don't need these properties to display for functions.
|
||||
names = names.filter(
|
||||
val => ['arguments', 'caller'].indexOf(val) < 0
|
||||
);
|
||||
}
|
||||
json = '{ ';
|
||||
objAbstract = getObjAbstract(obj);
|
||||
visitor.updateAbstract(id, {
|
||||
erudaObjAbstract: objAbstract,
|
||||
erudaCircular: id
|
||||
});
|
||||
parts.push(`${wrapKey('erudaObjAbstract')}: ${wrapStr(objAbstract)}`);
|
||||
if (!circularMarker) parts.push(`"erudaId": "${id}"`);
|
||||
each(names, objIteratee);
|
||||
if (proto) parts.push(proto);
|
||||
json += parts.join(', ') + ' }';
|
||||
} else if (isNum) {
|
||||
json = obj + '';
|
||||
if (endWith(json, 'Infinity') || json === 'NaN') json = `"${json}"`;
|
||||
} else if (isBool) {
|
||||
json = obj ? 'true' : 'false';
|
||||
} else if (obj === null) {
|
||||
json = 'null';
|
||||
} else if (isSymbol) {
|
||||
json = wrapStr('Symbol');
|
||||
} else if (obj === undefined) {
|
||||
json = wrapStr('undefined');
|
||||
} else if (type === '[object HTMLAllCollection]') {
|
||||
// https://docs.webplatform.org/wiki/dom/HTMLAllCollection
|
||||
// Might cause a performance issue when stringify a dom element.
|
||||
json = wrapStr('[object HTMLAllCollection]');
|
||||
} else if (type === '[object HTMLDocument]' && level > 1) {
|
||||
// Same as reason above.
|
||||
json = wrapStr('[object HTMLDocument]');
|
||||
} else {
|
||||
proto = Object.getPrototypeOf(obj)
|
||||
if (circularMarker && proto === Object.prototype) proto = null
|
||||
if (proto) {
|
||||
proto = `${wrapKey('erudaProto')}: ${stringify(proto, passProtoOpts)}`
|
||||
}
|
||||
if (isFn) {
|
||||
// We don't need these properties to display for functions.
|
||||
names = names.filter(val => ['arguments', 'caller'].indexOf(val) < 0)
|
||||
}
|
||||
json = '{ '
|
||||
objAbstract = getObjAbstract(obj)
|
||||
visitor.updateAbstract(id, {
|
||||
erudaObjAbstract: objAbstract,
|
||||
erudaCircular: id
|
||||
})
|
||||
parts.push(`${wrapKey('erudaObjAbstract')}: ${wrapStr(objAbstract)}`)
|
||||
if (!circularMarker) parts.push(`"erudaId": "${id}"`)
|
||||
each(names, objIteratee)
|
||||
if (proto) parts.push(proto)
|
||||
json += parts.join(', ') + ' }'
|
||||
} else if (isNum) {
|
||||
json = obj + ''
|
||||
if (endWith(json, 'Infinity') || json === 'NaN') json = `"${json}"`
|
||||
} else if (isBool) {
|
||||
json = obj ? 'true' : 'false'
|
||||
} else if (obj === null) {
|
||||
json = 'null'
|
||||
} else if (isSymbol) {
|
||||
json = wrapStr('Symbol')
|
||||
} else if (obj === undefined) {
|
||||
json = wrapStr('undefined')
|
||||
} else if (type === '[object HTMLAllCollection]') {
|
||||
// https://docs.webplatform.org/wiki/dom/HTMLAllCollection
|
||||
// Might cause a performance issue when stringify a dom element.
|
||||
json = wrapStr('[object HTMLAllCollection]')
|
||||
} else if (type === '[object HTMLDocument]' && level > 1) {
|
||||
// Same as reason above.
|
||||
json = wrapStr('[object HTMLDocument]')
|
||||
} else {
|
||||
try {
|
||||
id = visitor.visit(obj)
|
||||
if (canBeProto(obj)) {
|
||||
obj = Object.getPrototypeOf(obj)
|
||||
id = visitor.visit(obj)
|
||||
}
|
||||
|
||||
json = '{ '
|
||||
objAbstract = getObjAbstract(obj)
|
||||
visitor.updateAbstract(id, {
|
||||
erudaObjAbstract: objAbstract,
|
||||
erudaCircular: id
|
||||
})
|
||||
parts.push(`${wrapKey('erudaObjAbstract')}: ${wrapStr(objAbstract)}`)
|
||||
if (!circularMarker) parts.push(`"erudaId": "${id}"`)
|
||||
|
||||
names = getKeys(obj)
|
||||
keys = names.keys
|
||||
allKeys = names.allKeys
|
||||
names = unenumerable ? allKeys : keys
|
||||
|
||||
proto = Object.getPrototypeOf(obj)
|
||||
if (circularMarker && proto === Object.prototype) proto = null
|
||||
if (proto) {
|
||||
try {
|
||||
id = visitor.visit(obj);
|
||||
if (canBeProto(obj)) {
|
||||
obj = Object.getPrototypeOf(obj);
|
||||
id = visitor.visit(obj);
|
||||
}
|
||||
|
||||
json = '{ ';
|
||||
objAbstract = getObjAbstract(obj);
|
||||
visitor.updateAbstract(id, {
|
||||
erudaObjAbstract: objAbstract,
|
||||
erudaCircular: id
|
||||
});
|
||||
parts.push(
|
||||
`${wrapKey('erudaObjAbstract')}: ${wrapStr(objAbstract)}`
|
||||
);
|
||||
if (!circularMarker) parts.push(`"erudaId": "${id}"`);
|
||||
|
||||
names = getKeys(obj);
|
||||
keys = names.keys;
|
||||
allKeys = names.allKeys;
|
||||
names = unenumerable ? allKeys : keys;
|
||||
|
||||
proto = Object.getPrototypeOf(obj);
|
||||
if (circularMarker && proto === Object.prototype) proto = null;
|
||||
if (proto) {
|
||||
try {
|
||||
proto = `${wrapKey('erudaProto')}: ${stringify(
|
||||
proto,
|
||||
passProtoOpts
|
||||
)}`;
|
||||
} catch (e) {
|
||||
proto = `${wrapKey('erudaProto')}: ${wrapStr(e.message)}`;
|
||||
}
|
||||
}
|
||||
each(names, objIteratee);
|
||||
if (proto) parts.push(proto);
|
||||
json += parts.join(', ') + ' }';
|
||||
proto = `${wrapKey('erudaProto')}: ${stringify(proto, passProtoOpts)}`
|
||||
} catch (e) {
|
||||
json = wrapStr(obj);
|
||||
proto = `${wrapKey('erudaProto')}: ${wrapStr(e.message)}`
|
||||
}
|
||||
}
|
||||
each(names, objIteratee)
|
||||
if (proto) parts.push(proto)
|
||||
json += parts.join(', ') + ' }'
|
||||
} catch (e) {
|
||||
json = wrapStr(obj)
|
||||
}
|
||||
}
|
||||
|
||||
function objIteratee(name) {
|
||||
let unenumerable = !contain(keys, name) ? 'erudaUnenumerable ' : '',
|
||||
key = wrapKey(unenumerable + name),
|
||||
getKey = wrapKey(unenumerable + 'get ' + name),
|
||||
setKey = wrapKey(unenumerable + 'set ' + name);
|
||||
function objIteratee(name) {
|
||||
let unenumerable = !contain(keys, name) ? 'erudaUnenumerable ' : '',
|
||||
key = wrapKey(unenumerable + name),
|
||||
getKey = wrapKey(unenumerable + 'get ' + name),
|
||||
setKey = wrapKey(unenumerable + 'set ' + name)
|
||||
|
||||
let descriptor = Object.getOwnPropertyDescriptor(obj, name),
|
||||
hasGetter = descriptor && descriptor.get,
|
||||
hasSetter = descriptor && descriptor.set;
|
||||
let descriptor = Object.getOwnPropertyDescriptor(obj, name),
|
||||
hasGetter = descriptor && descriptor.get,
|
||||
hasSetter = descriptor && descriptor.set
|
||||
|
||||
if (!getterVal && hasGetter) {
|
||||
parts.push(`${key}: "(...)"`);
|
||||
parts.push(`${getKey}: ${stringify(descriptor.get, passOpts)}`);
|
||||
} else {
|
||||
let val;
|
||||
try {
|
||||
val = topObj[name];
|
||||
} catch (e) {
|
||||
val = e.message;
|
||||
}
|
||||
parts.push(`${key}: ${stringify(val, passOpts)}`);
|
||||
}
|
||||
if (hasSetter) {
|
||||
parts.push(`${setKey}: ${stringify(descriptor.set, passOpts)}`);
|
||||
}
|
||||
if (!getterVal && hasGetter) {
|
||||
parts.push(`${key}: "(...)"`)
|
||||
parts.push(`${getKey}: ${stringify(descriptor.get, passOpts)}`)
|
||||
} else {
|
||||
let val
|
||||
try {
|
||||
val = topObj[name]
|
||||
} catch (e) {
|
||||
val = e.message
|
||||
}
|
||||
parts.push(`${key}: ${stringify(val, passOpts)}`)
|
||||
}
|
||||
if (hasSetter) {
|
||||
parts.push(`${setKey}: ${stringify(descriptor.set, passOpts)}`)
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
return json
|
||||
}
|
||||
|
||||
function getKeys(obj) {
|
||||
let allKeys = Object.getOwnPropertyNames(obj),
|
||||
keys = Object.keys(obj).sort(sortObjName);
|
||||
let allKeys = Object.getOwnPropertyNames(obj),
|
||||
keys = Object.keys(obj).sort(sortObjName)
|
||||
|
||||
allKeys = keys.concat(
|
||||
filter(allKeys, val => !contain(keys, val)).sort(sortObjName)
|
||||
);
|
||||
allKeys = keys.concat(
|
||||
filter(allKeys, val => !contain(keys, val)).sort(sortObjName)
|
||||
)
|
||||
|
||||
return { keys, allKeys };
|
||||
return { keys, allKeys }
|
||||
}
|
||||
|
||||
// $, upperCase, lowerCase, _
|
||||
function sortObjName(a, b) {
|
||||
let lenA = a.length,
|
||||
lenB = b.length,
|
||||
len = lenA > lenB ? lenB : lenA;
|
||||
let lenA = a.length,
|
||||
lenB = b.length,
|
||||
len = lenA > lenB ? lenB : lenA
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
let codeA = a.charCodeAt(i),
|
||||
codeB = b.charCodeAt(i),
|
||||
cmpResult = cmpCode(codeA, codeB);
|
||||
for (let i = 0; i < len; i++) {
|
||||
let codeA = a.charCodeAt(i),
|
||||
codeB = b.charCodeAt(i),
|
||||
cmpResult = cmpCode(codeA, codeB)
|
||||
|
||||
if (cmpResult !== 0) return cmpResult;
|
||||
}
|
||||
if (cmpResult !== 0) return cmpResult
|
||||
}
|
||||
|
||||
if (lenA > lenB) return 1;
|
||||
if (lenA < lenB) return -1;
|
||||
if (lenA > lenB) return 1
|
||||
if (lenA < lenB) return -1
|
||||
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
|
||||
function cmpCode(a, b) {
|
||||
a = transCode(a);
|
||||
b = transCode(b);
|
||||
a = transCode(a)
|
||||
b = transCode(b)
|
||||
|
||||
if (a > b) return 1;
|
||||
if (a < b) return -1;
|
||||
return 0;
|
||||
if (a > b) return 1
|
||||
if (a < b) return -1
|
||||
return 0
|
||||
}
|
||||
|
||||
function transCode(code) {
|
||||
// _ should be placed after lowercase chars.
|
||||
if (code === 95) return 123;
|
||||
return code;
|
||||
// _ should be placed after lowercase chars.
|
||||
if (code === 95) return 123
|
||||
return code
|
||||
}
|
||||
|
||||
let regFnHead = /function(.*?)\((.*?)\)/;
|
||||
let regFnHead = /function(.*?)\((.*?)\)/
|
||||
|
||||
function extractFnHead(fn) {
|
||||
let str = fn.toString(),
|
||||
fnHead = str.match(regFnHead);
|
||||
let str = fn.toString(),
|
||||
fnHead = str.match(regFnHead)
|
||||
|
||||
if (fnHead) return fnHead[0];
|
||||
if (fnHead) return fnHead[0]
|
||||
|
||||
return str;
|
||||
return str
|
||||
}
|
||||
|
||||
function getFnAbstract(fn) {
|
||||
let fnStr = fn.toString();
|
||||
if (fnStr.length > 500) fnStr = fnStr.slice(0, 500) + '...';
|
||||
let fnStr = fn.toString()
|
||||
if (fnStr.length > 500) fnStr = fnStr.slice(0, 500) + '...'
|
||||
|
||||
return extractFnHead(fnStr).replace('function', '');
|
||||
return extractFnHead(fnStr).replace('function', '')
|
||||
}
|
||||
|
||||
function canBeProto(obj) {
|
||||
let emptyObj = isEmpty(Object.getOwnPropertyNames(obj)),
|
||||
proto = Object.getPrototypeOf(obj);
|
||||
let emptyObj = isEmpty(Object.getOwnPropertyNames(obj)),
|
||||
proto = Object.getPrototypeOf(obj)
|
||||
|
||||
return emptyObj && proto && proto !== Object.prototype;
|
||||
return emptyObj && proto && proto !== Object.prototype
|
||||
}
|
||||
|
||||
function getObjAbstract(obj) {
|
||||
if (isArr(obj)) return `Array(${obj.length})`;
|
||||
if (isFn(obj)) return getFnAbstract(obj);
|
||||
if (isRegExp(obj)) return obj.toString();
|
||||
if (isArr(obj)) return `Array(${obj.length})`
|
||||
if (isFn(obj)) return getFnAbstract(obj)
|
||||
if (isRegExp(obj)) return obj.toString()
|
||||
|
||||
let type = getType(obj);
|
||||
let type = getType(obj)
|
||||
|
||||
return type.replace(/(\[object )|]/g, '');
|
||||
return type.replace(/(\[object )|]/g, '')
|
||||
}
|
||||
|
||||
function getType(obj) {
|
||||
let type;
|
||||
let type
|
||||
|
||||
try {
|
||||
type = {}.toString.call(obj);
|
||||
} catch (e) {
|
||||
type = '[object Object]';
|
||||
}
|
||||
try {
|
||||
type = {}.toString.call(obj)
|
||||
} catch (e) {
|
||||
type = '[object Object]'
|
||||
}
|
||||
|
||||
return type;
|
||||
return type
|
||||
}
|
||||
|
||||
class Visitor {
|
||||
constructor() {
|
||||
this._visited = [];
|
||||
this._map = {};
|
||||
}
|
||||
visit(val) {
|
||||
/* Add 0 to distinguish stringify generated id from JsonViewer id.
|
||||
constructor() {
|
||||
this._visited = []
|
||||
this._map = {}
|
||||
}
|
||||
visit(val) {
|
||||
/* Add 0 to distinguish stringify generated id from JsonViewer id.
|
||||
* When used in web worker, they are not calling the same uniqId method, thus result may be repeated.
|
||||
*/
|
||||
let id = uniqId('erudaJson0');
|
||||
let id = uniqId('erudaJson0')
|
||||
|
||||
this._visited.push({ id, val, abstract: {} });
|
||||
this._map[id] = last(this._visited);
|
||||
this._visited.push({ id, val, abstract: {} })
|
||||
this._map[id] = last(this._visited)
|
||||
|
||||
return id;
|
||||
}
|
||||
check(val) {
|
||||
let visited = this._visited;
|
||||
return id
|
||||
}
|
||||
check(val) {
|
||||
let visited = this._visited
|
||||
|
||||
for (let i = 0, len = visited.length; i < len; i++) {
|
||||
if (val === visited[i].val) return visited[i];
|
||||
}
|
||||
for (let i = 0, len = visited.length; i < len; i++) {
|
||||
if (val === visited[i].val) return visited[i]
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
update(id, data) {
|
||||
extend(this._map[id], data);
|
||||
}
|
||||
updateAbstract(id, abstract) {
|
||||
this.update(id, { abstract });
|
||||
}
|
||||
return false
|
||||
}
|
||||
update(id, data) {
|
||||
extend(this._map[id], data)
|
||||
}
|
||||
updateAbstract(id, abstract) {
|
||||
this.update(id, { abstract })
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user