ig-serialize 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/package.json +1 -1
- package/serialize.js +57 -24
- package/test.js +42 -5
- package/serialize2.js +0 -645
package/README.md
CHANGED
|
@@ -11,5 +11,44 @@ This extends the default JSON serialization adding the following:
|
|
|
11
11
|
|
|
12
12
|
## Motivation
|
|
13
13
|
|
|
14
|
+
This was originally built as a companion to a testing module for a
|
|
15
|
+
programming class, illustrating several concepts, including: guaranteed
|
|
16
|
+
clean isolation of data structures via serialization, instrumenting code
|
|
17
|
+
and tooling design, basic parsing, among others.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Introduction
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Serializing functions
|
|
28
|
+
|
|
29
|
+
Due to how JavaScript is designed it is not possible to trivially and
|
|
30
|
+
fully clone a function with all of it's references, `.serilaize(..)` will
|
|
31
|
+
not attempt to clone any state a function may have, this will lead to
|
|
32
|
+
loosing:
|
|
33
|
+
|
|
34
|
+
- Function closure
|
|
35
|
+
- Attributes set on the function or any of it's prototypes, including the
|
|
36
|
+
`.__proto__` value if it was changed.
|
|
37
|
+
|
|
38
|
+
Thus, care must be taken when serializing structures containing function.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
### `serialize(..)` / `eJSON.stringify(..)`
|
|
44
|
+
|
|
45
|
+
### `deserialize(..)` / 'eJSON.parse(..)'
|
|
46
|
+
|
|
47
|
+
### `deepCopy(..)`
|
|
48
|
+
|
|
49
|
+
### `partialDeepCopy(..)`
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
14
53
|
|
|
15
54
|
|
package/package.json
CHANGED
package/serialize.js
CHANGED
|
@@ -41,8 +41,11 @@ var debug = {
|
|
|
41
41
|
|
|
42
42
|
//---------------------------------------------------------------------
|
|
43
43
|
|
|
44
|
+
module.STRING_LENGTH_REF = 64
|
|
45
|
+
|
|
44
46
|
//
|
|
45
|
-
// serialize(obj[,
|
|
47
|
+
// serialize(obj[, options])
|
|
48
|
+
// serialize(obj[, indent[, depth[, options]]])
|
|
46
49
|
// -> str
|
|
47
50
|
//
|
|
48
51
|
// indent can be:
|
|
@@ -50,7 +53,7 @@ var debug = {
|
|
|
50
53
|
// string - string to use for indenting
|
|
51
54
|
//
|
|
52
55
|
//
|
|
53
|
-
// _serialize(obj, base_path, seen, indent, depth,
|
|
56
|
+
// _serialize(obj, base_path, seen, indent, depth, options)
|
|
54
57
|
// -> str
|
|
55
58
|
//
|
|
56
59
|
//
|
|
@@ -103,7 +106,11 @@ var debug = {
|
|
|
103
106
|
// ...breaks .trim*() in Map/Set/Object...
|
|
104
107
|
var _serialize =
|
|
105
108
|
module._serialize =
|
|
106
|
-
function(obj, path=[], seen=new Map(), indent, depth=0,
|
|
109
|
+
function(obj, path=[], seen=new Map(), indent, depth=0, options={}){
|
|
110
|
+
var string_length_ref =
|
|
111
|
+
options.string_length_ref
|
|
112
|
+
?? module.STRING_LENGTH_REF
|
|
113
|
+
|
|
107
114
|
// recursive...
|
|
108
115
|
var p = seen.get(obj)
|
|
109
116
|
if(p != null){
|
|
@@ -117,14 +124,24 @@ function(obj, path=[], seen=new Map(), indent, depth=0, functions){
|
|
|
117
124
|
seen.set(obj, path)
|
|
118
125
|
// if functions array is given add function to it and store its
|
|
119
126
|
// index in the serialized data...
|
|
120
|
-
if(functions instanceof Array){
|
|
121
|
-
functions.push(obj)
|
|
122
|
-
obj = functions.length-1 }
|
|
127
|
+
if(options.functions instanceof Array){
|
|
128
|
+
options.functions.push(obj)
|
|
129
|
+
obj = options.functions.length-1 }
|
|
123
130
|
var s = '('+ obj.toString() +')'
|
|
124
131
|
return FUNCTION
|
|
125
132
|
.replace('%', s.length +','+ s) }
|
|
126
133
|
|
|
134
|
+
// special case: long strings...
|
|
135
|
+
if(typeof(obj) == 'string'
|
|
136
|
+
&& obj.length > string_length_ref){
|
|
137
|
+
seen.set(obj, path) }
|
|
138
|
+
// BigInt...
|
|
139
|
+
if(typeof(obj) == 'bigint'){
|
|
140
|
+
seen.set(obj, path)
|
|
141
|
+
return obj.toString() +'n' }
|
|
142
|
+
|
|
127
143
|
// atomics...
|
|
144
|
+
// NOTE: these are not stored in seen thus are not re-referenced...
|
|
128
145
|
if(obj === null){
|
|
129
146
|
return NULL }
|
|
130
147
|
if(typeof(obj) != 'object'){
|
|
@@ -137,6 +154,8 @@ function(obj, path=[], seen=new Map(), indent, depth=0, functions){
|
|
|
137
154
|
INFINITY
|
|
138
155
|
: obj === -Infinity ?
|
|
139
156
|
NEG_INFINITY
|
|
157
|
+
// XXX might be a good idea to reference really long strings instead
|
|
158
|
+
// of storing each...
|
|
140
159
|
: JSON.stringify(obj, null, indent) }
|
|
141
160
|
|
|
142
161
|
// objects...
|
|
@@ -152,20 +171,20 @@ function(obj, path=[], seen=new Map(), indent, depth=0, functions){
|
|
|
152
171
|
for(var i=0; i < obj.length; i++){
|
|
153
172
|
elems.push(
|
|
154
173
|
i in obj ?
|
|
155
|
-
_serialize(obj[i], [...path, i], seen, indent, depth+1,
|
|
174
|
+
_serialize(obj[i], [...path, i], seen, indent, depth+1, options)
|
|
156
175
|
: EMPTY) }
|
|
157
176
|
} else if(obj instanceof Map){
|
|
158
177
|
pre = 'Map(['
|
|
159
178
|
post = '])'
|
|
160
179
|
elems = [
|
|
161
|
-
_serialize([...obj], path, seen, indent, depth,
|
|
180
|
+
_serialize([...obj], path, seen, indent, depth, options)
|
|
162
181
|
.slice(1, -1)
|
|
163
182
|
.trim() ]
|
|
164
183
|
} else if(obj instanceof Set){
|
|
165
184
|
pre = 'Set(['
|
|
166
185
|
post = '])'
|
|
167
186
|
elems = [
|
|
168
|
-
_serialize([...obj], path, seen, indent, depth,
|
|
187
|
+
_serialize([...obj], path, seen, indent, depth, options)
|
|
169
188
|
.slice(1, -1)
|
|
170
189
|
.trim() ]
|
|
171
190
|
} else {
|
|
@@ -175,7 +194,7 @@ function(obj, path=[], seen=new Map(), indent, depth=0, functions){
|
|
|
175
194
|
elems.push(`${
|
|
176
195
|
JSON.stringify(k)
|
|
177
196
|
}:${ indent != null ? ' ' : '' }${
|
|
178
|
-
_serialize(v, [...path, k], seen, indent, depth+1,
|
|
197
|
+
_serialize(v, [...path, k], seen, indent, depth+1, options)
|
|
179
198
|
// relevant for pretty-printing only...
|
|
180
199
|
.trimLeft()
|
|
181
200
|
}`) } }
|
|
@@ -196,8 +215,8 @@ function(obj, path=[], seen=new Map(), indent, depth=0, functions){
|
|
|
196
215
|
// user interface...
|
|
197
216
|
var serialize =
|
|
198
217
|
module.serialize =
|
|
199
|
-
function(obj, indent, depth=0,
|
|
200
|
-
return _serialize(obj, [], new Map(), indent, depth,
|
|
218
|
+
function(obj, indent, depth=0, options){
|
|
219
|
+
return _serialize(obj, [], new Map(), indent, depth, options) }
|
|
201
220
|
|
|
202
221
|
|
|
203
222
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
@@ -357,6 +376,9 @@ module.eJSON = {
|
|
|
357
376
|
|| (str[j] >= '0'
|
|
358
377
|
&& str[j] <= '9'))){
|
|
359
378
|
j++ }
|
|
379
|
+
// BigInt...
|
|
380
|
+
if(str[j] == 'n'){
|
|
381
|
+
return [ BigInt(str.slice(i, j)), j+1, line ] }
|
|
360
382
|
return [ str.slice(i, j)*1, j, line ] },
|
|
361
383
|
// XXX TEST count \\n
|
|
362
384
|
string: function(state, path, match, str, i, line){
|
|
@@ -381,7 +403,10 @@ module.eJSON = {
|
|
|
381
403
|
if(j == str.length
|
|
382
404
|
&& str[j-1] != match){
|
|
383
405
|
this.error('Unexpected end of input wile looking fot "'+ match +'".', str, i, line) }
|
|
384
|
-
|
|
406
|
+
// NOTE: this is cheating -- instead of dancing around and
|
|
407
|
+
// replicating the full functionality of JSON.parse(..) we
|
|
408
|
+
// are simply falling back on it, as we did when serializing.
|
|
409
|
+
return [ JSON.parse(`"${str.slice(i+1, j)}"`), j+1, line ] },
|
|
385
410
|
identifier: function(state, path, match, str, i, line){
|
|
386
411
|
debug.lex('identifier', str, i, line)
|
|
387
412
|
if(!/[a-zA-Z_]/.test(str[i])){
|
|
@@ -543,7 +568,6 @@ module.eJSON = {
|
|
|
543
568
|
// NOTE: this uses eval(..) so care must be taken when enabling this...
|
|
544
569
|
func: function(state, path, match, str, i, line){
|
|
545
570
|
if(state.functions == null){
|
|
546
|
-
console.log('---', state)
|
|
547
571
|
this.error('Deserializing functions disabled.', str, i, line) }
|
|
548
572
|
|
|
549
573
|
debug.lex('function', str, i, line)
|
|
@@ -555,7 +579,7 @@ module.eJSON = {
|
|
|
555
579
|
this.error('Expected "," got "'+ str[i] +'"', str, i, line) }
|
|
556
580
|
i++
|
|
557
581
|
|
|
558
|
-
// func
|
|
582
|
+
// func index...
|
|
559
583
|
if(state.functions instanceof Array){
|
|
560
584
|
var [n, i, line] = this.number(state, path, str[i+1], str, i+1, line)
|
|
561
585
|
res = state.functions[n]
|
|
@@ -604,10 +628,10 @@ module.eJSON = {
|
|
|
604
628
|
return this[handler](state, path, match, str, i, line) },
|
|
605
629
|
|
|
606
630
|
|
|
607
|
-
parse: function(str,
|
|
631
|
+
parse: function(str, options={}){
|
|
608
632
|
|
|
609
633
|
// stage 1: build the object...
|
|
610
|
-
var state = {functions}
|
|
634
|
+
var state = {functions: options.functions}
|
|
611
635
|
var res = this.value(state, [], str)[0]
|
|
612
636
|
|
|
613
637
|
// stage 2: link the recursive structures...
|
|
@@ -615,13 +639,20 @@ module.eJSON = {
|
|
|
615
639
|
this.setItem(res, a, this.getItem(res, b)) }
|
|
616
640
|
|
|
617
641
|
return res },
|
|
642
|
+
|
|
643
|
+
// to comply with POLS...
|
|
644
|
+
stringify: serialize,
|
|
618
645
|
}
|
|
619
646
|
|
|
620
647
|
|
|
621
648
|
var deserialize =
|
|
622
649
|
module.deserialize =
|
|
623
|
-
function(str,
|
|
624
|
-
|
|
650
|
+
function(str, options){
|
|
651
|
+
options =
|
|
652
|
+
options === true ?
|
|
653
|
+
{functions: true}
|
|
654
|
+
: options
|
|
655
|
+
return eJSON.parse(str, options) }
|
|
625
656
|
|
|
626
657
|
|
|
627
658
|
|
|
@@ -630,18 +661,20 @@ function(str, functions){
|
|
|
630
661
|
|
|
631
662
|
var deepCopy =
|
|
632
663
|
module.deepCopy =
|
|
633
|
-
function(obj,
|
|
664
|
+
function(obj, funcs){
|
|
665
|
+
var options = {functions: funcs}
|
|
634
666
|
return deserialize(
|
|
635
|
-
serialize(obj, null, 0,
|
|
636
|
-
|
|
667
|
+
serialize(obj, null, 0, options),
|
|
668
|
+
options) }
|
|
637
669
|
|
|
638
670
|
|
|
639
671
|
var partialDeepCopy =
|
|
640
672
|
module.partialDeepCopy =
|
|
641
673
|
function(obj, funcs=[]){
|
|
674
|
+
var options = {functions: funcs}
|
|
642
675
|
return deserialize(
|
|
643
|
-
serialize(obj, null, 0,
|
|
644
|
-
|
|
676
|
+
serialize(obj, null, 0, options),
|
|
677
|
+
options) }
|
|
645
678
|
|
|
646
679
|
|
|
647
680
|
|
package/test.js
CHANGED
|
@@ -26,6 +26,8 @@ var eJSON = require('./serialize')
|
|
|
26
26
|
var json = true
|
|
27
27
|
var ejson = false
|
|
28
28
|
|
|
29
|
+
var pre_cycle = true
|
|
30
|
+
|
|
29
31
|
// XXX test whitespace handling...
|
|
30
32
|
var setups = test.Setups({
|
|
31
33
|
'true': function(assert){
|
|
@@ -49,11 +51,13 @@ var setups = test.Setups({
|
|
|
49
51
|
return ['0.123', json] },
|
|
50
52
|
'float-b': function(assert){
|
|
51
53
|
return ['1.23', json] },
|
|
54
|
+
'bigint': function(assert){
|
|
55
|
+
return ['999999999999999999999n'] },
|
|
52
56
|
// XXX need a way to test this...
|
|
53
57
|
//'float-a': function(assert){
|
|
54
|
-
// return '.123' },
|
|
58
|
+
// return ['.123'] },
|
|
55
59
|
//'float-c': function(assert){
|
|
56
|
-
// return '123.' },
|
|
60
|
+
// return ['123.'] },
|
|
57
61
|
// XXX also test:
|
|
58
62
|
// hex/bin/orc/...
|
|
59
63
|
Infinity: function(assert){
|
|
@@ -64,6 +68,9 @@ var setups = test.Setups({
|
|
|
64
68
|
// XXX also test diffrerent quotations...
|
|
65
69
|
string: function(assert){
|
|
66
70
|
return ['"string"', json] },
|
|
71
|
+
// XXX ERR this breaks...
|
|
72
|
+
'string-multiline': function(assert){
|
|
73
|
+
return ['"string\\nstring\\nstring"', json] },
|
|
67
74
|
|
|
68
75
|
'array-empty': function(assert){
|
|
69
76
|
return ['[]', json] },
|
|
@@ -177,17 +184,47 @@ test.Tests({
|
|
|
177
184
|
//*/
|
|
178
185
|
})
|
|
179
186
|
|
|
180
|
-
/* XXX these break things...
|
|
181
187
|
test.Cases({
|
|
188
|
+
/* XXX for some magical reason hese break as soon as we add [setup] to arguments...
|
|
182
189
|
'deep-copy-function': function(assert, [setup]){
|
|
183
190
|
// XXX check function isolation...
|
|
184
191
|
},
|
|
192
|
+
//*/
|
|
185
193
|
|
|
186
|
-
|
|
194
|
+
// NOTE: these syntax variants are not output by .serialize(..) this it
|
|
195
|
+
// is less critical to test them in the main loop.
|
|
196
|
+
// XXX though it would be nice to do so...
|
|
197
|
+
tests: [
|
|
198
|
+
// numbers/floats...
|
|
199
|
+
['.123', '0.123'],
|
|
200
|
+
['123.', '123'],
|
|
201
|
+
|
|
202
|
+
// string quotes...
|
|
203
|
+
['"abc"', "'abc'"],
|
|
204
|
+
['"abc"', '`abc`'],
|
|
205
|
+
|
|
206
|
+
// arrays...
|
|
207
|
+
['[1,2,]', '[1,2]'],
|
|
208
|
+
|
|
209
|
+
// sparse arrays...
|
|
210
|
+
['[<empty>]', '[,]'],
|
|
211
|
+
['[1,2,<empty>]', '[1,2,,]'],
|
|
212
|
+
['[1,2,<empty>]', '[1,2,<empty>,]'],
|
|
213
|
+
],
|
|
214
|
+
'syntax-simplifications': function(assert){
|
|
215
|
+
var aa, bb
|
|
216
|
+
for(var [a, b] of this.tests){
|
|
217
|
+
assert(eJSON.serialize(aa = eJSON.deserialize(a)) == eJSON.serialize(bb = eJSON.deserialize(b)),
|
|
218
|
+
`"${ a }" and "${ b }" should deserialize to the samve value.`,
|
|
219
|
+
'got:', aa, 'and', bb, 'resp.') } },
|
|
220
|
+
|
|
221
|
+
'deep-copy-function': function(assert){
|
|
222
|
+
// XXX check function isolation...
|
|
223
|
+
},
|
|
224
|
+
'partial-deep-copy-function': function(assert){
|
|
187
225
|
// XXX check function isolation...
|
|
188
226
|
},
|
|
189
227
|
})
|
|
190
|
-
//*/
|
|
191
228
|
|
|
192
229
|
|
|
193
230
|
//---------------------------------------------------------------------
|
package/serialize2.js
DELETED
|
@@ -1,645 +0,0 @@
|
|
|
1
|
-
/**********************************************************************
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
**********************************************************************/
|
|
6
|
-
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
|
7
|
-
(function(require){ var module={} // make module AMD/node compatible...
|
|
8
|
-
/*********************************************************************/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
var EMPTY = '<empty>'
|
|
12
|
-
var NULL = 'null'
|
|
13
|
-
var UNDEFINED = 'undefined'
|
|
14
|
-
var NAN = 'NaN'
|
|
15
|
-
var INFINITY = 'Infinity'
|
|
16
|
-
var NEG_INFINITY = '-Infinity'
|
|
17
|
-
|
|
18
|
-
var RECURSIVE = '<RECURSIVE%>'
|
|
19
|
-
|
|
20
|
-
var FUNCTION = '<FUNCTION[%]>'
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
25
|
-
|
|
26
|
-
module.DEBUG = false
|
|
27
|
-
|
|
28
|
-
var DEBUG_PREFIX = '---'
|
|
29
|
-
|
|
30
|
-
var debug = {
|
|
31
|
-
context: 16,
|
|
32
|
-
|
|
33
|
-
log: function(...args){
|
|
34
|
-
if(!module.DEBUG){
|
|
35
|
-
return }
|
|
36
|
-
return console.log(DEBUG_PREFIX, ...arguments) },
|
|
37
|
-
lex: function(name, str, i, line){
|
|
38
|
-
return this.log(name +':', str.slice(i, i+this.context) +'...') },
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
//---------------------------------------------------------------------
|
|
43
|
-
|
|
44
|
-
//
|
|
45
|
-
// serialize(obj[, indent[, depth]])
|
|
46
|
-
// -> str
|
|
47
|
-
//
|
|
48
|
-
// indent can be:
|
|
49
|
-
// number - number of spaces to use for indent
|
|
50
|
-
// string - string to use for indenting
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
// _serialize(obj, base_path, seen, indent, depth, functions)
|
|
54
|
-
// -> str
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// Paths
|
|
58
|
-
// A path is a chain of indexes/keys leading to a specific object in
|
|
59
|
-
// tree.
|
|
60
|
-
//
|
|
61
|
-
// the root object is referenced by an empty path array.
|
|
62
|
-
//
|
|
63
|
-
// For sets, positional indexes are used, i.e. a set is treated like
|
|
64
|
-
// an array ov values.
|
|
65
|
-
//
|
|
66
|
-
// For maps a two number index is used with the first being the position
|
|
67
|
-
// of the item and the second indicated if the target is the key or the
|
|
68
|
-
// value. i.e. a map is treated like an array of key value pairs.
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
// Examples:
|
|
72
|
-
// Object paths
|
|
73
|
-
// ---------------------------------------------------------------
|
|
74
|
-
// obj = [ <- []
|
|
75
|
-
// null, <- [0]
|
|
76
|
-
// [
|
|
77
|
-
// 'a',
|
|
78
|
-
// 'b', <- [1, 1]
|
|
79
|
-
// 'c',
|
|
80
|
-
// ],
|
|
81
|
-
// Set([
|
|
82
|
-
// 1,
|
|
83
|
-
// 2,
|
|
84
|
-
// 3, <- [2, 2]
|
|
85
|
-
// ]),
|
|
86
|
-
// Map([i <- [3] +----- index of elemnt in map
|
|
87
|
-
// [ / +--- 0 means key
|
|
88
|
-
// 'key', <- [3, 0, 0]
|
|
89
|
-
// 'value' <- [3, 0, 1]
|
|
90
|
-
// ], +--- 1 means value
|
|
91
|
-
// [
|
|
92
|
-
// [
|
|
93
|
-
// 123 <- [3, 1, 0, 0]
|
|
94
|
-
// ],
|
|
95
|
-
// 'got tired of thinking up names',
|
|
96
|
-
// ],
|
|
97
|
-
// ]),
|
|
98
|
-
// ]
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
// XXX BUG: using non-whitespace as indent breaks the depth of the first
|
|
102
|
-
// or last elements in sequences
|
|
103
|
-
// ...breaks .trim*() in Map/Set/Object...
|
|
104
|
-
var _serialize =
|
|
105
|
-
module._serialize =
|
|
106
|
-
function(obj, path=[], seen=new Map(), indent, depth=0, functions){
|
|
107
|
-
// recursive...
|
|
108
|
-
var p = seen.get(obj)
|
|
109
|
-
if(p != null){
|
|
110
|
-
// NOTE: _serialize(..) is always printed flat here, regardless of indent/depth...
|
|
111
|
-
return RECURSIVE.replace('%', _serialize(p)) }
|
|
112
|
-
|
|
113
|
-
// functions...
|
|
114
|
-
// NOTE: we are storing function length to avoid parsing the function...
|
|
115
|
-
// NOTE: storing length is a potential attack vector...
|
|
116
|
-
if(typeof(obj) == 'function'){
|
|
117
|
-
seen.set(obj, path)
|
|
118
|
-
// if functions array is given add function to it and store its
|
|
119
|
-
// index in the serialized data...
|
|
120
|
-
if(functions != null){
|
|
121
|
-
functions.push(obj)
|
|
122
|
-
obj = functions.length-1 }
|
|
123
|
-
var s = '('+ obj.toString() +')'
|
|
124
|
-
return FUNCTION
|
|
125
|
-
.replace('%', s.length +','+ s) }
|
|
126
|
-
|
|
127
|
-
// atomics...
|
|
128
|
-
if(obj === null){
|
|
129
|
-
return NULL }
|
|
130
|
-
if(typeof(obj) != 'object'){
|
|
131
|
-
return typeof(obj) == 'number'
|
|
132
|
-
&& isNaN(obj) ?
|
|
133
|
-
NAN
|
|
134
|
-
: obj === undefined ?
|
|
135
|
-
UNDEFINED
|
|
136
|
-
: obj === Infinity ?
|
|
137
|
-
INFINITY
|
|
138
|
-
: obj === -Infinity ?
|
|
139
|
-
NEG_INFINITY
|
|
140
|
-
: JSON.stringify(obj, null, indent) }
|
|
141
|
-
|
|
142
|
-
// objects...
|
|
143
|
-
seen.set(obj, path)
|
|
144
|
-
|
|
145
|
-
var elems = []
|
|
146
|
-
var pre = ''
|
|
147
|
-
var join = ','
|
|
148
|
-
var post = ''
|
|
149
|
-
if(obj instanceof Array){
|
|
150
|
-
pre = '['
|
|
151
|
-
post = ']'
|
|
152
|
-
for(var i=0; i < obj.length; i++){
|
|
153
|
-
elems.push(
|
|
154
|
-
i in obj ?
|
|
155
|
-
_serialize(obj[i], [...path, i], seen, indent, depth+1, functions)
|
|
156
|
-
: EMPTY) }
|
|
157
|
-
} else if(obj instanceof Map){
|
|
158
|
-
pre = 'Map(['
|
|
159
|
-
post = '])'
|
|
160
|
-
elems = [
|
|
161
|
-
_serialize([...obj], path, seen, indent, depth, functions)
|
|
162
|
-
.slice(1, -1)
|
|
163
|
-
.trim() ]
|
|
164
|
-
} else if(obj instanceof Set){
|
|
165
|
-
pre = 'Set(['
|
|
166
|
-
post = '])'
|
|
167
|
-
elems = [
|
|
168
|
-
_serialize([...obj], path, seen, indent, depth, functions)
|
|
169
|
-
.slice(1, -1)
|
|
170
|
-
.trim() ]
|
|
171
|
-
} else {
|
|
172
|
-
pre = '{'
|
|
173
|
-
post = '}'
|
|
174
|
-
for(var [k, v] of Object.entries(obj)){
|
|
175
|
-
elems.push(`${
|
|
176
|
-
JSON.stringify(k)
|
|
177
|
-
}:${ indent != null ? ' ' : '' }${
|
|
178
|
-
_serialize(v, [...path, k], seen, indent, depth+1, functions)
|
|
179
|
-
// relevant for pretty-printing only...
|
|
180
|
-
.trimLeft()
|
|
181
|
-
}`) } }
|
|
182
|
-
|
|
183
|
-
// handle indent...
|
|
184
|
-
if(indent != null){
|
|
185
|
-
i = indent.repeat(depth)
|
|
186
|
-
s = i + indent
|
|
187
|
-
if(elems.length > 0){
|
|
188
|
-
pre = pre + '\n' + s
|
|
189
|
-
post = '\n' + i + post
|
|
190
|
-
// XXX set limit for number of elements to keep horizontal...
|
|
191
|
-
// ...also account for element length...
|
|
192
|
-
join = join + '\n' + s } }
|
|
193
|
-
|
|
194
|
-
return pre+ elems.join(join) +post }
|
|
195
|
-
|
|
196
|
-
// user interface...
|
|
197
|
-
var serialize =
|
|
198
|
-
module.serialize =
|
|
199
|
-
function(obj, indent, depth=0, functions){
|
|
200
|
-
return _serialize(obj, [], new Map(), indent, depth, functions) }
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
204
|
-
|
|
205
|
-
// XXX better error handling...
|
|
206
|
-
// XXX try and make this single stage (see notes for : .recursive(..))
|
|
207
|
-
var eJSON =
|
|
208
|
-
module.eJSON = {
|
|
209
|
-
chars: {
|
|
210
|
-
'"': 'string', "'": 'string', '`': 'string',
|
|
211
|
-
|
|
212
|
-
0: 'number', 1: 'number', 2: 'number', 3: 'number', 4: 'number',
|
|
213
|
-
5: 'number', 6: 'number', 7: 'number', 8: 'number', 9: 'number',
|
|
214
|
-
'.': 'number', '-': 'number',
|
|
215
|
-
|
|
216
|
-
'[': 'array',
|
|
217
|
-
'{': 'object',
|
|
218
|
-
},
|
|
219
|
-
words: {
|
|
220
|
-
true: true,
|
|
221
|
-
false: false,
|
|
222
|
-
|
|
223
|
-
Infinity: 'number',
|
|
224
|
-
|
|
225
|
-
'Set(': 'set',
|
|
226
|
-
'Map(': 'map',
|
|
227
|
-
|
|
228
|
-
null: null,
|
|
229
|
-
undefined: undefined,
|
|
230
|
-
NaN: NaN,
|
|
231
|
-
|
|
232
|
-
'<empty>': 'empty',
|
|
233
|
-
'<RECURSIVE': 'recursive',
|
|
234
|
-
|
|
235
|
-
'<FUNCTION[': 'func',
|
|
236
|
-
},
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// generic helpers...
|
|
240
|
-
//
|
|
241
|
-
// XXX need to keep the context constrained to one line...
|
|
242
|
-
context_size: 16,
|
|
243
|
-
error: function(msg, str, i, line){
|
|
244
|
-
var pre = i
|
|
245
|
-
var post = i
|
|
246
|
-
while(pre > 0
|
|
247
|
-
&& i - pre <= this.context_size
|
|
248
|
-
&& str[pre] != '\n'){
|
|
249
|
-
pre-- }
|
|
250
|
-
while(post < str.length
|
|
251
|
-
&& post - i <= this.context_size
|
|
252
|
-
&& str[post] != '\n'){
|
|
253
|
-
post++ }
|
|
254
|
-
throw new SyntaxError(`${ msg }\n`
|
|
255
|
-
+` ${line}: ${ str.slice(pre, post) }\n`
|
|
256
|
-
+` ${ ' '.repeat( i - pre + ((line + ': ').length) ) }^`) },
|
|
257
|
-
|
|
258
|
-
//
|
|
259
|
-
// ._getItem(obj, path)
|
|
260
|
-
// -> [path, obj]
|
|
261
|
-
//
|
|
262
|
-
// NOTE: we are returning path here because we need to distinguish
|
|
263
|
-
// incomplete paths that we can infer the container from and
|
|
264
|
-
// complete paths, e.g:
|
|
265
|
-
// For
|
|
266
|
-
// m = [new Map([[1,2], [2,3]])]
|
|
267
|
-
// Both of the below will return the map and the correct path ([0]):
|
|
268
|
-
// eJSON._getItem(m, [0, 1])
|
|
269
|
-
// eJSON._getItem(m, [0])
|
|
270
|
-
// But we need to destinguish between the two cases when
|
|
271
|
-
// writing to a map (see: .setItem(..))
|
|
272
|
-
_getItem: function(obj, path){
|
|
273
|
-
for(var i=0; i<path.length; i++){
|
|
274
|
-
var k = path[i]
|
|
275
|
-
// speacial case: incomplete map index...
|
|
276
|
-
if( obj instanceof Map
|
|
277
|
-
&& i == path.length-1){
|
|
278
|
-
return [path.slice(0, -1), obj] }
|
|
279
|
-
|
|
280
|
-
obj = obj instanceof Set ?
|
|
281
|
-
[...obj][k]
|
|
282
|
-
: obj instanceof Map ?
|
|
283
|
-
[...obj][k][path[++i]]
|
|
284
|
-
: obj[k] }
|
|
285
|
-
return [path, obj] },
|
|
286
|
-
//
|
|
287
|
-
// .getItem(obj, path)
|
|
288
|
-
// -> obj
|
|
289
|
-
//
|
|
290
|
-
// NOTE: this is a POLS wrapper of ._getItem(..)
|
|
291
|
-
getItem: function(obj, path){
|
|
292
|
-
return this._getItem(...arguments)[1] },
|
|
293
|
-
// NOTE: this behaves in a similar way to normal key value assignment,
|
|
294
|
-
// i.e. replacing items if they exist, this is not a problem here
|
|
295
|
-
// as in the general case we are assigning over a placeholder
|
|
296
|
-
// object...
|
|
297
|
-
// NOTE: as a side-effect this maintains element oreder even in object
|
|
298
|
-
// with no direct concept of element order, like objects, sets and
|
|
299
|
-
// maps.
|
|
300
|
-
// NOTE: some operations envolve rewriting the container elements so
|
|
301
|
-
// are not as fast, namely writing set elements and map keys.
|
|
302
|
-
setItem: function(obj, path, value){
|
|
303
|
-
var [p, parent] = this._getItem(obj, path.slice(0, -1))
|
|
304
|
-
var k = path.at(-1)
|
|
305
|
-
if(parent instanceof Set){
|
|
306
|
-
var elems = [...parent]
|
|
307
|
-
elems[k] = value
|
|
308
|
-
// we cant to keep the order...
|
|
309
|
-
parent.clear()
|
|
310
|
-
for(var e of elems){
|
|
311
|
-
parent.add(e) }
|
|
312
|
-
} else if(parent instanceof Map){
|
|
313
|
-
if(path.length-2 !== p.length){
|
|
314
|
-
throw new Error('.setItem(..): incomplete path.') }
|
|
315
|
-
var i = path.at(-2)
|
|
316
|
-
// replace the index...
|
|
317
|
-
if(k == 0){
|
|
318
|
-
var elems = [...parent]
|
|
319
|
-
elems[i][0] = value
|
|
320
|
-
parent.clear()
|
|
321
|
-
for(var e of elems){
|
|
322
|
-
parent.set(...e) }
|
|
323
|
-
// set the value...
|
|
324
|
-
} else {
|
|
325
|
-
parent.set([...parent][i][0], value) }
|
|
326
|
-
} else {
|
|
327
|
-
parent[k] = value }
|
|
328
|
-
return value },
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
WHITESPACE: ' \t\n',
|
|
333
|
-
skipWhitespace: function(str, i, line){
|
|
334
|
-
while(i < str.length
|
|
335
|
-
&& this.WHITESPACE.includes(str[i])){
|
|
336
|
-
if(str[i] == '\n'){
|
|
337
|
-
line++ }
|
|
338
|
-
i++ }
|
|
339
|
-
return [i, line] },
|
|
340
|
-
|
|
341
|
-
//
|
|
342
|
-
// .handler(match, str, i, line)
|
|
343
|
-
// -> [value, i, line]
|
|
344
|
-
//
|
|
345
|
-
number: function(state, path, match, str, i, line){
|
|
346
|
-
debug.lex('number', str, i, line)
|
|
347
|
-
// special cases..,
|
|
348
|
-
if(match == 'Infinity'){
|
|
349
|
-
return [Infinity, i+'Infinity'.length, line] }
|
|
350
|
-
if(match == '-'
|
|
351
|
-
&& str.slice(i, i+'-Infinity'.length) == '-Infinity'){
|
|
352
|
-
return [-Infinity, i+'-Infinity'.length, line] }
|
|
353
|
-
// numbers...
|
|
354
|
-
var j = i+1
|
|
355
|
-
while(j < str.length
|
|
356
|
-
&& (str[j] == '.'
|
|
357
|
-
|| (str[j] >= '0'
|
|
358
|
-
&& str[j] <= '9'))){
|
|
359
|
-
j++ }
|
|
360
|
-
return [ str.slice(i, j)*1, j, line ] },
|
|
361
|
-
// XXX TEST count \\n
|
|
362
|
-
string: function(state, path, match, str, i, line){
|
|
363
|
-
debug.lex('string', str, i, line)
|
|
364
|
-
var j = i+1
|
|
365
|
-
while(j < str.length
|
|
366
|
-
&& str[j] != match){
|
|
367
|
-
// newlines...
|
|
368
|
-
if(str[j] == '\n'){
|
|
369
|
-
line++ }
|
|
370
|
-
// escaped newlines...
|
|
371
|
-
if(str[j] == '\\'
|
|
372
|
-
&& j+1 < str.length
|
|
373
|
-
&& str[j+1] == 'n'){
|
|
374
|
-
line++ }
|
|
375
|
-
// skip escaped quotes...
|
|
376
|
-
if(str[j] == '\\'
|
|
377
|
-
&& j+1 < str.length
|
|
378
|
-
&& str[j+1] == match){
|
|
379
|
-
j++ }
|
|
380
|
-
j++ }
|
|
381
|
-
if(j == str.length
|
|
382
|
-
&& str[j-1] != match){
|
|
383
|
-
this.error('Unexpected end of input wile looking fot "'+ match +'".', str, i, line) }
|
|
384
|
-
return [ str.slice(i+1, j), j+1, line ] },
|
|
385
|
-
identifier: function(state, path, match, str, i, line){
|
|
386
|
-
debug.lex('identifier', str, i, line)
|
|
387
|
-
if(!/[a-zA-Z_]/.test(str[i])){
|
|
388
|
-
this.error('Not an identifier: "'+ str[i] +'"', str, i, line) }
|
|
389
|
-
var j = i+1
|
|
390
|
-
while(j < str.length
|
|
391
|
-
&& /[a-zA-Z0-9_]/.test(str[j])){
|
|
392
|
-
j++ }
|
|
393
|
-
return [ str.slice(i, j), j, line ] },
|
|
394
|
-
|
|
395
|
-
//
|
|
396
|
-
// handler(res, index, str, i, line)
|
|
397
|
-
// -> [res, i, line]
|
|
398
|
-
//
|
|
399
|
-
sequence: function(state, path, str, i, line, end, handler, initial=[]){
|
|
400
|
-
var index = 0
|
|
401
|
-
while(i < str.length){
|
|
402
|
-
;[i, line] = this.skipWhitespace(str, i, line)
|
|
403
|
-
|
|
404
|
-
// done...
|
|
405
|
-
if(str.slice(i, i+end.length) == end){
|
|
406
|
-
return [initial, i+end.length, line] }
|
|
407
|
-
|
|
408
|
-
// empty...
|
|
409
|
-
if(str[i] == ','){
|
|
410
|
-
index++
|
|
411
|
-
i++
|
|
412
|
-
// XXX this feels hackish -- can this be deligated to the handler???
|
|
413
|
-
initial instanceof Array
|
|
414
|
-
&& initial.length++
|
|
415
|
-
continue }
|
|
416
|
-
if(str.slice(i, i+EMPTY.length) == EMPTY){
|
|
417
|
-
index++
|
|
418
|
-
i += EMPTY.length
|
|
419
|
-
if(str[i] == ','){
|
|
420
|
-
i++ }
|
|
421
|
-
// XXX this feels hackish -- can this be deligated to the handler???
|
|
422
|
-
initial instanceof Array
|
|
423
|
-
&& initial.length++
|
|
424
|
-
continue }
|
|
425
|
-
|
|
426
|
-
// end of input...
|
|
427
|
-
if(i >= str.length-1){
|
|
428
|
-
break }
|
|
429
|
-
|
|
430
|
-
// value...
|
|
431
|
-
;[initial, i, line] = handler.call(this, initial, index, str, i, line)
|
|
432
|
-
|
|
433
|
-
;[i, line] = this.skipWhitespace(str, i, line)
|
|
434
|
-
if(str[i] == ','){
|
|
435
|
-
i++ }
|
|
436
|
-
index++ }
|
|
437
|
-
|
|
438
|
-
// XXX better message -- show starting seq...
|
|
439
|
-
this.error('Unexpected end of input wile looking for "'+ end +'".', str, i, line) },
|
|
440
|
-
|
|
441
|
-
array: function(state, path, match, str, i, line){
|
|
442
|
-
debug.lex('array', str, i, line)
|
|
443
|
-
return this.sequence(
|
|
444
|
-
state, path, str, i+1, line,
|
|
445
|
-
']',
|
|
446
|
-
function(res, index, str, i, line){
|
|
447
|
-
var obj
|
|
448
|
-
;[obj, i, line] = this.value(state, [...path, index], str, i, line)
|
|
449
|
-
res[index] = obj
|
|
450
|
-
return [res, i, line] }) },
|
|
451
|
-
object: function(state, path, match, str, i, line){
|
|
452
|
-
debug.lex('object', str, i, line)
|
|
453
|
-
return this.sequence(
|
|
454
|
-
state, path, str, i+1, line,
|
|
455
|
-
'}',
|
|
456
|
-
function(res, index, str, i, line){
|
|
457
|
-
var obj, key
|
|
458
|
-
// key...
|
|
459
|
-
;[key, i, line] = '\'"`'.includes(str[i]) ?
|
|
460
|
-
this.string(state, path, str[i], str, i, line)
|
|
461
|
-
: this.identifier(state, path, str[i], str, i, line)
|
|
462
|
-
|
|
463
|
-
// ':'...
|
|
464
|
-
;[i, line] = this.skipWhitespace(str, i, line)
|
|
465
|
-
if(str[i] != ':'){
|
|
466
|
-
this.error('Expected ":", got "'+ str[i] +'".', str, i, line) }
|
|
467
|
-
i++
|
|
468
|
-
;[i, line] = this.skipWhitespace(str, i, line)
|
|
469
|
-
|
|
470
|
-
// value...
|
|
471
|
-
;[obj, i, line] = this.value(state, [...path, key], str, i, line)
|
|
472
|
-
res[key] = obj
|
|
473
|
-
return [res, i, line] },
|
|
474
|
-
{}) },
|
|
475
|
-
|
|
476
|
-
set: function(state, path, match, str, i, line){
|
|
477
|
-
debug.lex('set', str, i, line)
|
|
478
|
-
i += match.length
|
|
479
|
-
;[res, i, line] = this.sequence(
|
|
480
|
-
state, path,
|
|
481
|
-
str, i+1, line,
|
|
482
|
-
']',
|
|
483
|
-
function(res, index, str, i, line){
|
|
484
|
-
var obj
|
|
485
|
-
;[obj, i, line] = this.value(
|
|
486
|
-
state,
|
|
487
|
-
[...path, index],
|
|
488
|
-
str, i, line)
|
|
489
|
-
res.add(obj)
|
|
490
|
-
return [res, i, line] },
|
|
491
|
-
new Set())
|
|
492
|
-
if(str[i] != ')'){
|
|
493
|
-
this.error('Expected ")", got "'+ str[i] +'".', str, i, line) }
|
|
494
|
-
return [res, i+1, line] },
|
|
495
|
-
map: function(state, path, match, str, i, line){
|
|
496
|
-
debug.lex('map', str, i, line)
|
|
497
|
-
i += match.length
|
|
498
|
-
;[res, i, line] = this.sequence(
|
|
499
|
-
state, path,
|
|
500
|
-
str, i+1, line,
|
|
501
|
-
']',
|
|
502
|
-
function(res, index, str, i, line){
|
|
503
|
-
debug.lex(' map-content', str, i, line)
|
|
504
|
-
var obj
|
|
505
|
-
;[[key, value], i, line] = this.value(
|
|
506
|
-
state,
|
|
507
|
-
[...path, index],
|
|
508
|
-
str, i, line)
|
|
509
|
-
res.set(key, value)
|
|
510
|
-
return [res, i, line] },
|
|
511
|
-
new Map())
|
|
512
|
-
if(str[i] != ')'){
|
|
513
|
-
this.error('Expected ")", got "'+ str[i] +'".', str, i, line) }
|
|
514
|
-
return [res, i+1, line] },
|
|
515
|
-
|
|
516
|
-
// XXX should this be done inline or on a separate stage?
|
|
517
|
-
// for inline we need these:
|
|
518
|
-
// - all containers must be placed as soon as they are created
|
|
519
|
-
// "return" before the items are processed...
|
|
520
|
-
// - containers must be "filled" as the items are parsed
|
|
521
|
-
// like .array(..) and .object(..) but not like .map(..) and .set(..)
|
|
522
|
-
// - thread the root object...
|
|
523
|
-
// alternatively, for a two stage process we need to:
|
|
524
|
-
// - have a mechanism to write placeholders
|
|
525
|
-
// - remember their positions (paths)
|
|
526
|
-
// - build/get said path
|
|
527
|
-
// XXX another strategy would be to have an path-object map and
|
|
528
|
-
// directly reference that -- this would eliminate the need for
|
|
529
|
-
// stage two... (XXX TEST)
|
|
530
|
-
// ...need to use serialized paths as keys...
|
|
531
|
-
recursive: function(state, path, match, str, i, line){
|
|
532
|
-
debug.lex('recursive', str, i, line)
|
|
533
|
-
return this.sequence(
|
|
534
|
-
state, path, str, i+match.length, line,
|
|
535
|
-
'>',
|
|
536
|
-
function(res, index, str, i, line){
|
|
537
|
-
var obj
|
|
538
|
-
;[obj, i, line] = this.array(state, [...path, index], '[', str, i, line)
|
|
539
|
-
var rec = state.recursive ??= []
|
|
540
|
-
rec.push([path, obj])
|
|
541
|
-
return [{}, i, line] }) },
|
|
542
|
-
|
|
543
|
-
// NOTE: this uses eval(..) so care must be taken when enabling this...
|
|
544
|
-
func: function(state, path, match, str, i, line){
|
|
545
|
-
if(state.functions == null){
|
|
546
|
-
this.error('Deserializing functions disabled.', str, i, line) }
|
|
547
|
-
|
|
548
|
-
debug.lex('function', str, i, line)
|
|
549
|
-
var res
|
|
550
|
-
// length...
|
|
551
|
-
i += match.length
|
|
552
|
-
var [l, i, line] = this.number(state, path, str[i], str, i, line)
|
|
553
|
-
if(str[i] != ','){
|
|
554
|
-
this.error('Expected "," got "'+ str[i] +'"', str, i, line) }
|
|
555
|
-
i++
|
|
556
|
-
|
|
557
|
-
// func ref...
|
|
558
|
-
if(state.functions instanceof Array){
|
|
559
|
-
var [n, i, line] = this.number(state, path, str[i+1], str, i+1, line)
|
|
560
|
-
res = state.functions[n]
|
|
561
|
-
// func code...
|
|
562
|
-
} else {
|
|
563
|
-
var code = str.slice(i, i+l)
|
|
564
|
-
i += l
|
|
565
|
-
line += code.split(/\n/g).length
|
|
566
|
-
if(str.slice(i, i+2) == ']>'){
|
|
567
|
-
res = eval?.(code) } }
|
|
568
|
-
|
|
569
|
-
if(str.slice(i, i+2) != ']>'){
|
|
570
|
-
this.error('Expected "]>" got "'+ str.slice(i, i+2) +'"', str, i, line) }
|
|
571
|
-
return [res, i+2, line] },
|
|
572
|
-
|
|
573
|
-
value: function(state, path, str, i=0, line=0){
|
|
574
|
-
|
|
575
|
-
;[i, line] = this.skipWhitespace(str, i, line)
|
|
576
|
-
|
|
577
|
-
// get handler...
|
|
578
|
-
var NONE = {}
|
|
579
|
-
var handler = NONE
|
|
580
|
-
var match = str[i]
|
|
581
|
-
if(match in this.chars){
|
|
582
|
-
handler = this.chars[match]
|
|
583
|
-
} else {
|
|
584
|
-
for(match in this.words){
|
|
585
|
-
if(match == str.slice(i, i+match.length)){
|
|
586
|
-
handler = this.words[match]
|
|
587
|
-
break } } }
|
|
588
|
-
|
|
589
|
-
// syntax error...
|
|
590
|
-
if(handler === NONE){
|
|
591
|
-
var context = 8
|
|
592
|
-
var pre = Math.max(i-context, 0)
|
|
593
|
-
var post = Math.min(i+context, str.length)
|
|
594
|
-
this.error(`Unexpected: ${ str.slice(i, i+10) }`, str, i, line) }
|
|
595
|
-
|
|
596
|
-
// value...
|
|
597
|
-
if(typeof(handler) != 'string'){
|
|
598
|
-
return [handler, i+match.length, line] }
|
|
599
|
-
// func...
|
|
600
|
-
return this[handler](state, path, match, str, i, line) },
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
parse: function(str, functions){
|
|
604
|
-
|
|
605
|
-
// stage 1: build the object...
|
|
606
|
-
var state = {functions}
|
|
607
|
-
var res = this.value(state, [], str)[0]
|
|
608
|
-
|
|
609
|
-
// stage 2: link the recursive structures...
|
|
610
|
-
for(var [a, b] of state.recursive ?? []){
|
|
611
|
-
this.setItem(res, a, this.getItem(res, b)) }
|
|
612
|
-
|
|
613
|
-
return res },
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
var deserialize =
|
|
618
|
-
module.deserialize =
|
|
619
|
-
function(str, functions){
|
|
620
|
-
return eJSON.parse(str, functions) }
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
//---------------------------------------------------------------------
|
|
625
|
-
// utils...
|
|
626
|
-
|
|
627
|
-
var deepCopy =
|
|
628
|
-
module.deepCopy =
|
|
629
|
-
function(obj){
|
|
630
|
-
return deserialize(
|
|
631
|
-
serialize(obj)) }
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
var semiDeepCopy =
|
|
635
|
-
module.semiDeepCopy =
|
|
636
|
-
function(obj){
|
|
637
|
-
var funcs = []
|
|
638
|
-
return deserialize(
|
|
639
|
-
serialize(obj, null, 0, funcs),
|
|
640
|
-
funcs) }
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
/**********************************************************************
|
|
645
|
-
* vim:set ts=4 sw=4 nowrap : */ return module })
|