293 lines
8.7 KiB
JavaScript
293 lines
8.7 KiB
JavaScript
var TeleportJS = (function (Primitive, primitive) { // eslint-disable-line no-unused-vars
|
|
var REF_KEY_PREFIX = '_'
|
|
var SINGLE_REF = REF_KEY_PREFIX + '0'
|
|
var REF_PREFIX = {
|
|
undefined: 'u',
|
|
number: 'n',
|
|
bigint: 'b',
|
|
symbol: 's',
|
|
Map: 'M',
|
|
Set: 'S',
|
|
Date: 'D',
|
|
RegExp: 'R',
|
|
Buffer: 'B',
|
|
Int8Array: 'H',
|
|
Uint8Array: 'I',
|
|
Uint8ClampedArray: 'J',
|
|
Int16Array: 'P',
|
|
Uint16Array: 'Q',
|
|
Int32Array: 'F',
|
|
Uint32Array: 'G',
|
|
Float32Array: 'K',
|
|
Float64Array: 'L'
|
|
}
|
|
|
|
/*!
|
|
* ISC License
|
|
*
|
|
* Copyright (c) 2018, Andrea Giammarchi, @WebReflection
|
|
*/
|
|
|
|
var TeleportJS = {
|
|
|
|
parse: function parse (text, reviver) {
|
|
var input = JSON.parse(text, Primitives).map(primitives)
|
|
var len = input.length
|
|
var refs = len > 1 ? input[len - 1] : []
|
|
var value = input[0]
|
|
var $ = reviver || noop
|
|
var tmp = typeof value === 'object' && value
|
|
? revive(input, refs, new Set(), value, $)
|
|
: (value === SINGLE_REF && refs.length ? reviveRefs(refs, 0) : value)
|
|
return $.call({ '': tmp }, '', tmp)
|
|
},
|
|
|
|
stringify: function stringify (value, replacer, space) {
|
|
for (var
|
|
firstRun,
|
|
known = new Map(),
|
|
knownRefs = new Map(),
|
|
refs = [],
|
|
input = [],
|
|
output = [],
|
|
$ = replacer && typeof replacer === typeof input
|
|
? function (k, v) {
|
|
if (k === '' || replacer.indexOf(k) > -1) return v
|
|
}
|
|
: (replacer || noop),
|
|
i = +set(known, input, $.call({ '': value }, '', value)),
|
|
replace = function (key, value) {
|
|
var after = $.call(this, key, value)
|
|
var refIndex = setRefs(knownRefs, refs, key, after, this)
|
|
|
|
// bail out if value set via ref
|
|
if (refIndex) {
|
|
return refIndex
|
|
}
|
|
|
|
if (firstRun) {
|
|
firstRun = !firstRun
|
|
return value
|
|
// this was invoking twice each root object
|
|
// return i < 1 ? value : $.call(this, key, value);
|
|
}
|
|
|
|
switch (typeof after) {
|
|
case 'object':
|
|
if (after === null) return after
|
|
// eslint-disable-next-line no-fallthrough
|
|
case primitive:
|
|
return known.get(after) || set(known, input, after)
|
|
}
|
|
return after
|
|
};
|
|
i < input.length; i++
|
|
) {
|
|
firstRun = true
|
|
output[i] = JSON.stringify(input[i], replace, space)
|
|
}
|
|
refs.length && output.push(JSON.stringify(refs))
|
|
return '[' + output.join(',') + ']'
|
|
}
|
|
|
|
}
|
|
|
|
return TeleportJS
|
|
|
|
function noop (key, value) {
|
|
return value
|
|
}
|
|
|
|
function reviveRefs (refs, index) {
|
|
var value = refs[index].substring(1)
|
|
|
|
switch (refs[index].charAt(0)) {
|
|
case REF_PREFIX.undefined:
|
|
refs[index] = undefined
|
|
break
|
|
case REF_PREFIX.number:
|
|
refs[index] = Number(value)
|
|
break
|
|
case REF_PREFIX.bigint:
|
|
refs[index] = BigInt(value)
|
|
break
|
|
case REF_PREFIX.symbol:
|
|
refs[index] = Symbol.for(value)
|
|
break
|
|
case REF_PREFIX.RegExp:
|
|
var parts = /\/(.*)\/(.*)/.exec(value)
|
|
refs[index] = new RegExp(parts[1], parts[2])
|
|
break
|
|
case REF_PREFIX.Buffer:
|
|
refs[index] = Buffer.from(JSON.parse(value))
|
|
break
|
|
case REF_PREFIX.Date:
|
|
refs[index] = new Date(value)
|
|
break
|
|
case REF_PREFIX.Map:
|
|
refs[index] = new Map(TeleportJS.parse(value))
|
|
break
|
|
case REF_PREFIX.Set:
|
|
refs[index] = new Set(TeleportJS.parse(value))
|
|
break
|
|
case REF_PREFIX.Int8Array:
|
|
refs[index] = new Int8Array(JSON.parse(value))
|
|
break
|
|
case REF_PREFIX.Uint8Array:
|
|
refs[index] = new Uint8Array(JSON.parse(value))
|
|
break
|
|
case REF_PREFIX.Uint8ClampedArray:
|
|
refs[index] = new Uint8ClampedArray(JSON.parse(value))
|
|
break
|
|
case REF_PREFIX.Int16Array:
|
|
refs[index] = new Int16Array(JSON.parse(value))
|
|
break
|
|
case REF_PREFIX.Uint16Array:
|
|
refs[index] = new Uint16Array(JSON.parse(value))
|
|
break
|
|
case REF_PREFIX.Int32Array:
|
|
refs[index] = new Int32Array(JSON.parse(value))
|
|
break
|
|
case REF_PREFIX.Uint32Array:
|
|
refs[index] = new Uint32Array(JSON.parse(value))
|
|
break
|
|
case REF_PREFIX.Float32Array:
|
|
refs[index] = new Float32Array(JSON.parse(value))
|
|
break
|
|
case REF_PREFIX.Float64Array:
|
|
refs[index] = new Float64Array(JSON.parse(value))
|
|
break
|
|
}
|
|
|
|
return refs[index]
|
|
}
|
|
|
|
function revive (input, refs, parsed, output, $) {
|
|
return Object.keys(output).reduce(
|
|
function (output, key) {
|
|
var value = output[key]
|
|
if (value instanceof Primitive) {
|
|
if (value.startsWith(REF_KEY_PREFIX)) {
|
|
var index = value.substring(1)
|
|
|
|
if (refs[index] instanceof Primitive) {
|
|
reviveRefs(refs, index)
|
|
}
|
|
|
|
output[key] = refs[index]
|
|
return output
|
|
}
|
|
|
|
var tmp = input[value]
|
|
if (typeof tmp === 'object' && !parsed.has(tmp)) {
|
|
parsed.add(tmp)
|
|
output[key] = $.call(output, key, revive(input, refs, parsed, tmp, $))
|
|
} else {
|
|
output[key] = $.call(output, key, tmp)
|
|
}
|
|
} else { output[key] = $.call(output, key, value) }
|
|
return output
|
|
},
|
|
output
|
|
)
|
|
}
|
|
|
|
function set (known, input, value) {
|
|
var index = Primitive(input.push(value) - 1)
|
|
known.set(value, index)
|
|
return index
|
|
}
|
|
|
|
function setRefs (known, refs, key, value, obj) {
|
|
var after
|
|
var i
|
|
|
|
switch (typeof value) {
|
|
// case 'boolean': break
|
|
// case 'function': break
|
|
case 'string':
|
|
if (obj[key] instanceof Date) {
|
|
after = REF_PREFIX.Date + value
|
|
}
|
|
break
|
|
case 'undefined':
|
|
after = REF_PREFIX.undefined
|
|
break
|
|
case 'number':
|
|
if (!Number.isFinite(value)) {
|
|
after = REF_PREFIX.number + Primitive(value)
|
|
}
|
|
break
|
|
case 'bigint':
|
|
after = REF_PREFIX.bigint + Primitive(value)
|
|
break
|
|
case 'symbol':
|
|
var description = Primitive(value)
|
|
after = REF_PREFIX.symbol + description.substring(7, description.length - 1)
|
|
break
|
|
case 'object':
|
|
if (value === null) {
|
|
break
|
|
} else if (value.type === 'Buffer' && value.data && Buffer.isBuffer(obj[key])) {
|
|
after = REF_PREFIX.Buffer + JSON.stringify(value.data)
|
|
} else if (value instanceof RegExp) {
|
|
after = REF_PREFIX.RegExp + Primitive(value)
|
|
} else if (value instanceof Map) {
|
|
var m = []
|
|
for (i of value.entries()) m.push(i)
|
|
after = REF_PREFIX.Map + TeleportJS.stringify(m)
|
|
} else if (value instanceof Set) {
|
|
var s = []
|
|
for (i of value.values()) s.push(i)
|
|
after = REF_PREFIX.Set + TeleportJS.stringify(s)
|
|
} else if (value instanceof Int8Array) {
|
|
after = REF_PREFIX.Int8Array + JSON.stringify(Array.apply([], value))
|
|
} else if (value instanceof Uint8Array) {
|
|
after = REF_PREFIX.Uint8Array + JSON.stringify(Array.apply([], value))
|
|
} else if (value instanceof Uint8ClampedArray) {
|
|
after = REF_PREFIX.Uint8ClampedArray + JSON.stringify(Array.apply([], value))
|
|
} else if (value instanceof Int16Array) {
|
|
after = REF_PREFIX.Int16Array + JSON.stringify(Array.apply([], value))
|
|
} else if (value instanceof Uint16Array) {
|
|
after = REF_PREFIX.Uint16Array + JSON.stringify(Array.apply([], value))
|
|
} else if (value instanceof Int32Array) {
|
|
after = REF_PREFIX.Int32Array + JSON.stringify(Array.apply([], value))
|
|
} else if (value instanceof Uint32Array) {
|
|
after = REF_PREFIX.Uint32Array + JSON.stringify(Array.apply([], value))
|
|
} else if (value instanceof Float32Array) {
|
|
after = REF_PREFIX.Float32Array + JSON.stringify(Array.apply([], value))
|
|
} else if (value instanceof Float64Array) {
|
|
after = REF_PREFIX.Float64Array + JSON.stringify(Array.apply([], value))
|
|
}
|
|
break
|
|
}
|
|
|
|
if (!after) {
|
|
return
|
|
}
|
|
|
|
var index = known.get(after)
|
|
|
|
if (index) {
|
|
return index
|
|
}
|
|
|
|
index = REF_KEY_PREFIX + Primitive(refs.push(after) - 1)
|
|
known.set(after, index)
|
|
return index
|
|
}
|
|
|
|
// the two kinds of primitives
|
|
// 1. the real one
|
|
// 2. the wrapped one
|
|
|
|
function primitives (value) {
|
|
return value instanceof Primitive ? Primitive(value) : value
|
|
}
|
|
|
|
function Primitives (key, value) {
|
|
// eslint-disable-next-line valid-typeof
|
|
return typeof value === primitive ? new Primitive(value) : value
|
|
}
|
|
}(String, 'string'))
|