Change format options

This commit is contained in:
redhoodsu
2018-06-16 18:07:12 +08:00
parent f88f8963b4
commit 12f82b6035
69 changed files with 5527 additions and 5627 deletions

View File

@@ -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 })
}
}