node-red-contrib-stoptimer-varidelay-plus 0.5.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/LICENSE +201 -0
- package/README.md +197 -0
- package/examples/StopTimerVariDelay Examples.json +406 -0
- package/package.json +28 -0
- package/stoptimer-varidelay/cycle.js +182 -0
- package/stoptimer-varidelay/icons/stoptimer.png +0 -0
- package/stoptimer-varidelay/stoptimer-varidelay.html +230 -0
- package/stoptimer-varidelay/stoptimer-varidelay.js +627 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "797eaa96c9cdb7b2",
|
|
4
|
+
"type": "stoptimer-varidelay",
|
|
5
|
+
"z": "90dddd2585facca0",
|
|
6
|
+
"duration": "5",
|
|
7
|
+
"durationType": "num",
|
|
8
|
+
"units": "Second",
|
|
9
|
+
"payloadtype": "str",
|
|
10
|
+
"payloadval": "DONE",
|
|
11
|
+
"name": "",
|
|
12
|
+
"reporting": "every_second",
|
|
13
|
+
"reportingformat": "human",
|
|
14
|
+
"persist": false,
|
|
15
|
+
"ignoretimerpass": false,
|
|
16
|
+
"x": 360,
|
|
17
|
+
"y": 160,
|
|
18
|
+
"wires": [
|
|
19
|
+
[
|
|
20
|
+
"c928952d875a224a"
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
"ec3d840d9223b48b"
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
"490703c2780c41ce"
|
|
27
|
+
]
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "31c7db01847a87ae",
|
|
32
|
+
"type": "inject",
|
|
33
|
+
"z": "90dddd2585facca0",
|
|
34
|
+
"name": "",
|
|
35
|
+
"props": [
|
|
36
|
+
{
|
|
37
|
+
"p": "payload"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"repeat": "",
|
|
41
|
+
"crontab": "",
|
|
42
|
+
"once": false,
|
|
43
|
+
"onceDelay": 0.1,
|
|
44
|
+
"topic": "",
|
|
45
|
+
"payload": "BASIC",
|
|
46
|
+
"payloadType": "str",
|
|
47
|
+
"x": 110,
|
|
48
|
+
"y": 160,
|
|
49
|
+
"wires": [
|
|
50
|
+
[
|
|
51
|
+
"797eaa96c9cdb7b2"
|
|
52
|
+
]
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "c928952d875a224a",
|
|
57
|
+
"type": "debug",
|
|
58
|
+
"z": "90dddd2585facca0",
|
|
59
|
+
"name": "Main",
|
|
60
|
+
"active": true,
|
|
61
|
+
"tosidebar": true,
|
|
62
|
+
"console": false,
|
|
63
|
+
"tostatus": false,
|
|
64
|
+
"complete": "payload",
|
|
65
|
+
"targetType": "msg",
|
|
66
|
+
"statusVal": "",
|
|
67
|
+
"statusType": "auto",
|
|
68
|
+
"x": 650,
|
|
69
|
+
"y": 120,
|
|
70
|
+
"wires": []
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "ec3d840d9223b48b",
|
|
74
|
+
"type": "debug",
|
|
75
|
+
"z": "90dddd2585facca0",
|
|
76
|
+
"name": "ALT",
|
|
77
|
+
"active": true,
|
|
78
|
+
"tosidebar": true,
|
|
79
|
+
"console": false,
|
|
80
|
+
"tostatus": false,
|
|
81
|
+
"complete": "payload",
|
|
82
|
+
"targetType": "msg",
|
|
83
|
+
"statusVal": "",
|
|
84
|
+
"statusType": "auto",
|
|
85
|
+
"x": 650,
|
|
86
|
+
"y": 160,
|
|
87
|
+
"wires": []
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "490703c2780c41ce",
|
|
91
|
+
"type": "debug",
|
|
92
|
+
"z": "90dddd2585facca0",
|
|
93
|
+
"name": "Countdown",
|
|
94
|
+
"active": true,
|
|
95
|
+
"tosidebar": true,
|
|
96
|
+
"console": false,
|
|
97
|
+
"tostatus": false,
|
|
98
|
+
"complete": "payload",
|
|
99
|
+
"targetType": "msg",
|
|
100
|
+
"statusVal": "",
|
|
101
|
+
"statusType": "auto",
|
|
102
|
+
"x": 670,
|
|
103
|
+
"y": 200,
|
|
104
|
+
"wires": []
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "3386240e756f3553",
|
|
108
|
+
"type": "comment",
|
|
109
|
+
"z": "90dddd2585facca0",
|
|
110
|
+
"name": "Basic Example",
|
|
111
|
+
"info": "This is a basic example. \n\nThe timer is preconfigured for 5 seconds.\n\n-The Inject node will send a BASIC message.\n-The timer status will countdown the 5 seconds.\n-The \"Main\" debug node will print the message from Inject in the debug tab when the countdown completes.\n-The \"ALT\" debug node will print DONE in the debug tab when the countdown completes.\n-The \"Countdown\" debug node will print the countdown in the debug tab in the hh:mm:ss format.\n\nNote that when the timer complete it indicates \"expired\"\n\n",
|
|
112
|
+
"x": 120,
|
|
113
|
+
"y": 100,
|
|
114
|
+
"wires": []
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"id": "9f0aa6082fd8d826",
|
|
118
|
+
"type": "comment",
|
|
119
|
+
"z": "90dddd2585facca0",
|
|
120
|
+
"name": "Dynamic Example",
|
|
121
|
+
"info": "This is a Dynamic example. \n\nThe timer is preconfigured for 5 seconds.\n\n-The Inject node will send a DYNAMIC message AND re-configure the timer for 1 minute*.\n-The timer status will countdown the 60 seconds.\n-The \"Main\" debug node will print the message from Inject in the debug tab when the countdown completes.\n-The \"ALT\" debug node will print DONE in the debug tab when the countdown completes.\n-The \"Countdown\" debug node will print the countdown in the debug tab as pure seconds.\n\n*note that the timer node itself will still be labelled as \"5 Second Timer\" as it is not possible to dynamically adjust that.\n\nNote that when the timer complete it indicates \"expired\"\n",
|
|
122
|
+
"x": 130,
|
|
123
|
+
"y": 320,
|
|
124
|
+
"wires": []
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "833a54bdac3f1963",
|
|
128
|
+
"type": "stoptimer-varidelay",
|
|
129
|
+
"z": "90dddd2585facca0",
|
|
130
|
+
"duration": "5",
|
|
131
|
+
"durationType": "num",
|
|
132
|
+
"units": "Second",
|
|
133
|
+
"payloadtype": "str",
|
|
134
|
+
"payloadval": "DONE",
|
|
135
|
+
"name": "",
|
|
136
|
+
"reporting": "every_second",
|
|
137
|
+
"reportingformat": "seconds",
|
|
138
|
+
"persist": false,
|
|
139
|
+
"ignoretimerpass": false,
|
|
140
|
+
"x": 340,
|
|
141
|
+
"y": 380,
|
|
142
|
+
"wires": [
|
|
143
|
+
[
|
|
144
|
+
"26e9017cebb6cdcf"
|
|
145
|
+
],
|
|
146
|
+
[
|
|
147
|
+
"9d3c6f0ab81d978f"
|
|
148
|
+
],
|
|
149
|
+
[
|
|
150
|
+
"b96e59108a65bfc1"
|
|
151
|
+
]
|
|
152
|
+
]
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"id": "354f969b37e62508",
|
|
156
|
+
"type": "inject",
|
|
157
|
+
"z": "90dddd2585facca0",
|
|
158
|
+
"name": "",
|
|
159
|
+
"props": [
|
|
160
|
+
{
|
|
161
|
+
"p": "payload"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"p": "delay",
|
|
165
|
+
"v": "1",
|
|
166
|
+
"vt": "str"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"p": "units",
|
|
170
|
+
"v": "minutes",
|
|
171
|
+
"vt": "str"
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
"repeat": "",
|
|
175
|
+
"crontab": "",
|
|
176
|
+
"once": false,
|
|
177
|
+
"onceDelay": 0.1,
|
|
178
|
+
"topic": "",
|
|
179
|
+
"payload": "DYNAMIC",
|
|
180
|
+
"payloadType": "str",
|
|
181
|
+
"x": 120,
|
|
182
|
+
"y": 380,
|
|
183
|
+
"wires": [
|
|
184
|
+
[
|
|
185
|
+
"833a54bdac3f1963"
|
|
186
|
+
]
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"id": "26e9017cebb6cdcf",
|
|
191
|
+
"type": "debug",
|
|
192
|
+
"z": "90dddd2585facca0",
|
|
193
|
+
"name": "Main",
|
|
194
|
+
"active": true,
|
|
195
|
+
"tosidebar": true,
|
|
196
|
+
"console": false,
|
|
197
|
+
"tostatus": false,
|
|
198
|
+
"complete": "payload",
|
|
199
|
+
"targetType": "msg",
|
|
200
|
+
"statusVal": "",
|
|
201
|
+
"statusType": "auto",
|
|
202
|
+
"x": 630,
|
|
203
|
+
"y": 340,
|
|
204
|
+
"wires": []
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"id": "9d3c6f0ab81d978f",
|
|
208
|
+
"type": "debug",
|
|
209
|
+
"z": "90dddd2585facca0",
|
|
210
|
+
"name": "ALT",
|
|
211
|
+
"active": true,
|
|
212
|
+
"tosidebar": true,
|
|
213
|
+
"console": false,
|
|
214
|
+
"tostatus": false,
|
|
215
|
+
"complete": "payload",
|
|
216
|
+
"targetType": "msg",
|
|
217
|
+
"statusVal": "",
|
|
218
|
+
"statusType": "auto",
|
|
219
|
+
"x": 630,
|
|
220
|
+
"y": 380,
|
|
221
|
+
"wires": []
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"id": "b96e59108a65bfc1",
|
|
225
|
+
"type": "debug",
|
|
226
|
+
"z": "90dddd2585facca0",
|
|
227
|
+
"name": "Countdown",
|
|
228
|
+
"active": true,
|
|
229
|
+
"tosidebar": true,
|
|
230
|
+
"console": false,
|
|
231
|
+
"tostatus": false,
|
|
232
|
+
"complete": "payload",
|
|
233
|
+
"targetType": "msg",
|
|
234
|
+
"statusVal": "",
|
|
235
|
+
"statusType": "auto",
|
|
236
|
+
"x": 650,
|
|
237
|
+
"y": 420,
|
|
238
|
+
"wires": []
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"id": "7aaa170e8b399531",
|
|
242
|
+
"type": "comment",
|
|
243
|
+
"z": "90dddd2585facca0",
|
|
244
|
+
"name": "Stopping a Timer",
|
|
245
|
+
"info": "This is an example of Stopping the timer. \n\nThe TIMER-A is preconfigured for 30 seconds.\n\n-The TEST Inject node will send a TEST message to TIMER-A and TIMER-B.\n-TIMER-A status will countdown the 30 seconds.\n-TIMER-B is configured for 5 seconds and to not show any countdown.\n-When TIMER-B expires, it goes to a change node which sets the payload to \"stop\" and that goes to TIMER-A.\n-TIMER-A immediately stops the countdown.\n-The \"Main\" debug node will never get called.\n-The \"ALT\" debug node will print \"stopped\" after 5 seconds.\n-The \"Countdown\" debug node will print the countdown in the debug tab as hh:mm:ss but will then print \"stopped\" after 5 seconds.\n\n\n\n",
|
|
246
|
+
"x": 120,
|
|
247
|
+
"y": 520,
|
|
248
|
+
"wires": []
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"id": "b46321ac8c26fa9e",
|
|
252
|
+
"type": "stoptimer-varidelay",
|
|
253
|
+
"z": "90dddd2585facca0",
|
|
254
|
+
"duration": "30",
|
|
255
|
+
"durationType": "num",
|
|
256
|
+
"units": "Second",
|
|
257
|
+
"payloadtype": "str",
|
|
258
|
+
"payloadval": "DONE",
|
|
259
|
+
"name": "TIMER-A",
|
|
260
|
+
"reporting": "every_second",
|
|
261
|
+
"reportingformat": "human",
|
|
262
|
+
"persist": false,
|
|
263
|
+
"ignoretimerpass": false,
|
|
264
|
+
"x": 520,
|
|
265
|
+
"y": 580,
|
|
266
|
+
"wires": [
|
|
267
|
+
[
|
|
268
|
+
"b985d5341246bbd7"
|
|
269
|
+
],
|
|
270
|
+
[
|
|
271
|
+
"605ef12ec580ab36"
|
|
272
|
+
],
|
|
273
|
+
[
|
|
274
|
+
"5f67fe9e40f9b0ea"
|
|
275
|
+
]
|
|
276
|
+
]
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"id": "6e2cc7b6cbf20576",
|
|
280
|
+
"type": "inject",
|
|
281
|
+
"z": "90dddd2585facca0",
|
|
282
|
+
"name": "",
|
|
283
|
+
"props": [
|
|
284
|
+
{
|
|
285
|
+
"p": "payload"
|
|
286
|
+
}
|
|
287
|
+
],
|
|
288
|
+
"repeat": "",
|
|
289
|
+
"crontab": "",
|
|
290
|
+
"once": false,
|
|
291
|
+
"onceDelay": 0.1,
|
|
292
|
+
"topic": "",
|
|
293
|
+
"payload": "TEST",
|
|
294
|
+
"payloadType": "str",
|
|
295
|
+
"x": 110,
|
|
296
|
+
"y": 580,
|
|
297
|
+
"wires": [
|
|
298
|
+
[
|
|
299
|
+
"b46321ac8c26fa9e",
|
|
300
|
+
"870654947ec5b53c"
|
|
301
|
+
]
|
|
302
|
+
]
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"id": "b985d5341246bbd7",
|
|
306
|
+
"type": "debug",
|
|
307
|
+
"z": "90dddd2585facca0",
|
|
308
|
+
"name": "Main",
|
|
309
|
+
"active": true,
|
|
310
|
+
"tosidebar": true,
|
|
311
|
+
"console": false,
|
|
312
|
+
"tostatus": false,
|
|
313
|
+
"complete": "payload",
|
|
314
|
+
"targetType": "msg",
|
|
315
|
+
"statusVal": "",
|
|
316
|
+
"statusType": "auto",
|
|
317
|
+
"x": 830,
|
|
318
|
+
"y": 540,
|
|
319
|
+
"wires": []
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"id": "605ef12ec580ab36",
|
|
323
|
+
"type": "debug",
|
|
324
|
+
"z": "90dddd2585facca0",
|
|
325
|
+
"name": "ALT",
|
|
326
|
+
"active": true,
|
|
327
|
+
"tosidebar": true,
|
|
328
|
+
"console": false,
|
|
329
|
+
"tostatus": false,
|
|
330
|
+
"complete": "payload",
|
|
331
|
+
"targetType": "msg",
|
|
332
|
+
"statusVal": "",
|
|
333
|
+
"statusType": "auto",
|
|
334
|
+
"x": 830,
|
|
335
|
+
"y": 580,
|
|
336
|
+
"wires": []
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"id": "5f67fe9e40f9b0ea",
|
|
340
|
+
"type": "debug",
|
|
341
|
+
"z": "90dddd2585facca0",
|
|
342
|
+
"name": "Countdown",
|
|
343
|
+
"active": true,
|
|
344
|
+
"tosidebar": true,
|
|
345
|
+
"console": false,
|
|
346
|
+
"tostatus": false,
|
|
347
|
+
"complete": "payload",
|
|
348
|
+
"targetType": "msg",
|
|
349
|
+
"statusVal": "",
|
|
350
|
+
"statusType": "auto",
|
|
351
|
+
"x": 850,
|
|
352
|
+
"y": 620,
|
|
353
|
+
"wires": []
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"id": "64154dfb1b036d21",
|
|
357
|
+
"type": "change",
|
|
358
|
+
"z": "90dddd2585facca0",
|
|
359
|
+
"name": "",
|
|
360
|
+
"rules": [
|
|
361
|
+
{
|
|
362
|
+
"t": "set",
|
|
363
|
+
"p": "payload",
|
|
364
|
+
"pt": "msg",
|
|
365
|
+
"to": "stop",
|
|
366
|
+
"tot": "str"
|
|
367
|
+
}
|
|
368
|
+
],
|
|
369
|
+
"action": "",
|
|
370
|
+
"property": "",
|
|
371
|
+
"from": "",
|
|
372
|
+
"to": "",
|
|
373
|
+
"reg": false,
|
|
374
|
+
"x": 380,
|
|
375
|
+
"y": 660,
|
|
376
|
+
"wires": [
|
|
377
|
+
[
|
|
378
|
+
"b46321ac8c26fa9e"
|
|
379
|
+
]
|
|
380
|
+
]
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
"id": "870654947ec5b53c",
|
|
384
|
+
"type": "stoptimer-varidelay",
|
|
385
|
+
"z": "90dddd2585facca0",
|
|
386
|
+
"duration": "5",
|
|
387
|
+
"durationType": "num",
|
|
388
|
+
"units": "Second",
|
|
389
|
+
"payloadtype": "num",
|
|
390
|
+
"payloadval": "0",
|
|
391
|
+
"name": "TIMER-B",
|
|
392
|
+
"reporting": "none",
|
|
393
|
+
"reportingformat": "human",
|
|
394
|
+
"persist": false,
|
|
395
|
+
"ignoretimerpass": false,
|
|
396
|
+
"x": 160,
|
|
397
|
+
"y": 680,
|
|
398
|
+
"wires": [
|
|
399
|
+
[
|
|
400
|
+
"64154dfb1b036d21"
|
|
401
|
+
],
|
|
402
|
+
[],
|
|
403
|
+
[]
|
|
404
|
+
]
|
|
405
|
+
}
|
|
406
|
+
]
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-contrib-stoptimer-varidelay-plus",
|
|
3
|
+
"version": "0.5.4",
|
|
4
|
+
"description": "An enhanced Node-RED timer node with variable delay, pause/resume, persistent state, and configurable reporting. Forked from node-red-contrib-stoptimer-varidelay by hamsando.",
|
|
5
|
+
"dependencies": {},
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/mchristegh/node-red-contrib-stoptimer-varidelay/"
|
|
9
|
+
},
|
|
10
|
+
"license": "Apache-2.0",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"node-red",
|
|
13
|
+
"timer",
|
|
14
|
+
"delay",
|
|
15
|
+
"stoptimer",
|
|
16
|
+
"pause",
|
|
17
|
+
"resume",
|
|
18
|
+
"persist"
|
|
19
|
+
],
|
|
20
|
+
"node-red": {
|
|
21
|
+
"nodes": {
|
|
22
|
+
"stoptimer-varidelay": "stoptimer-varidelay/stoptimer-varidelay.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"author": {
|
|
26
|
+
"name": "mchristegh"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/*
|
|
2
|
+
cycle.js
|
|
3
|
+
2018-05-15
|
|
4
|
+
|
|
5
|
+
Public Domain.
|
|
6
|
+
|
|
7
|
+
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
|
8
|
+
|
|
9
|
+
This code should be minified before deployment.
|
|
10
|
+
See http://javascript.crockford.com/jsmin.html
|
|
11
|
+
|
|
12
|
+
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
|
|
13
|
+
NOT CONTROL.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// The file uses the WeakMap feature of ES6.
|
|
17
|
+
|
|
18
|
+
/*jslint eval */
|
|
19
|
+
|
|
20
|
+
/*property
|
|
21
|
+
$ref, decycle, forEach, get, indexOf, isArray, keys, length, push,
|
|
22
|
+
retrocycle, set, stringify, test
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
if (typeof JSON.decycle !== "function") {
|
|
26
|
+
JSON.decycle = function decycle(object, replacer) {
|
|
27
|
+
"use strict";
|
|
28
|
+
|
|
29
|
+
// Make a deep copy of an object or array, assuring that there is at most
|
|
30
|
+
// one instance of each object or array in the resulting structure. The
|
|
31
|
+
// duplicate references (which might be forming cycles) are replaced with
|
|
32
|
+
// an object of the form
|
|
33
|
+
|
|
34
|
+
// {"$ref": PATH}
|
|
35
|
+
|
|
36
|
+
// where the PATH is a JSONPath string that locates the first occurance.
|
|
37
|
+
|
|
38
|
+
// So,
|
|
39
|
+
|
|
40
|
+
// var a = [];
|
|
41
|
+
// a[0] = a;
|
|
42
|
+
// return JSON.stringify(JSON.decycle(a));
|
|
43
|
+
|
|
44
|
+
// produces the string '[{"$ref":"$"}]'.
|
|
45
|
+
|
|
46
|
+
// If a replacer function is provided, then it will be called for each value.
|
|
47
|
+
// A replacer function receives a value and returns a replacement value.
|
|
48
|
+
|
|
49
|
+
// JSONPath is used to locate the unique object. $ indicates the top level of
|
|
50
|
+
// the object or array. [NUMBER] or [STRING] indicates a child element or
|
|
51
|
+
// property.
|
|
52
|
+
|
|
53
|
+
var objects = new WeakMap(); // object to path mappings
|
|
54
|
+
|
|
55
|
+
return (function derez(value, path) {
|
|
56
|
+
|
|
57
|
+
// The derez function recurses through the object, producing the deep copy.
|
|
58
|
+
|
|
59
|
+
var old_path; // The path of an earlier occurance of value
|
|
60
|
+
var nu; // The new object or array
|
|
61
|
+
|
|
62
|
+
// If a replacer function was provided, then call it to get a replacement value.
|
|
63
|
+
|
|
64
|
+
if (replacer !== undefined) {
|
|
65
|
+
value = replacer(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// typeof null === "object", so go on if this value is really an object but not
|
|
69
|
+
// one of the weird builtin objects.
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
typeof value === "object"
|
|
73
|
+
&& value !== null
|
|
74
|
+
&& !(value instanceof Boolean)
|
|
75
|
+
&& !(value instanceof Date)
|
|
76
|
+
&& !(value instanceof Number)
|
|
77
|
+
&& !(value instanceof RegExp)
|
|
78
|
+
&& !(value instanceof String)
|
|
79
|
+
) {
|
|
80
|
+
|
|
81
|
+
// If the value is an object or array, look to see if we have already
|
|
82
|
+
// encountered it. If so, return a {"$ref":PATH} object. This uses an
|
|
83
|
+
// ES6 WeakMap.
|
|
84
|
+
|
|
85
|
+
old_path = objects.get(value);
|
|
86
|
+
if (old_path !== undefined) {
|
|
87
|
+
return {$ref: old_path};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Otherwise, accumulate the unique value and its path.
|
|
91
|
+
|
|
92
|
+
objects.set(value, path);
|
|
93
|
+
|
|
94
|
+
// If it is an array, replicate the array.
|
|
95
|
+
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
nu = [];
|
|
98
|
+
value.forEach(function (element, i) {
|
|
99
|
+
nu[i] = derez(element, path + "[" + i + "]");
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
|
|
103
|
+
// If it is an object, replicate the object.
|
|
104
|
+
|
|
105
|
+
nu = {};
|
|
106
|
+
Object.keys(value).forEach(function (name) {
|
|
107
|
+
nu[name] = derez(
|
|
108
|
+
value[name],
|
|
109
|
+
path + "[" + JSON.stringify(name) + "]"
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return nu;
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}(object, "$"));
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if (typeof JSON.retrocycle !== "function") {
|
|
122
|
+
JSON.retrocycle = function retrocycle($) {
|
|
123
|
+
"use strict";
|
|
124
|
+
|
|
125
|
+
// Restore an object that was reduced by decycle. Members whose values are
|
|
126
|
+
// objects of the form
|
|
127
|
+
// {$ref: PATH}
|
|
128
|
+
// are replaced with references to the value found by the PATH. This will
|
|
129
|
+
// restore cycles. The object will be mutated.
|
|
130
|
+
|
|
131
|
+
// The eval function is used to locate the values described by a PATH. The
|
|
132
|
+
// root object is kept in a $ variable. A regular expression is used to
|
|
133
|
+
// assure that the PATH is extremely well formed. The regexp contains nested
|
|
134
|
+
// * quantifiers. That has been known to have extremely bad performance
|
|
135
|
+
// problems on some browsers for very long strings. A PATH is expected to be
|
|
136
|
+
// reasonably short. A PATH is allowed to belong to a very restricted subset of
|
|
137
|
+
// Goessner's JSONPath.
|
|
138
|
+
|
|
139
|
+
// So,
|
|
140
|
+
// var s = '[{"$ref":"$"}]';
|
|
141
|
+
// return JSON.retrocycle(JSON.parse(s));
|
|
142
|
+
// produces an array containing a single element which is the array itself.
|
|
143
|
+
|
|
144
|
+
var px = /^\$(?:\[(?:\d+|"(?:[^\\"\u0000-\u001f]|\\(?:[\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*")\])*$/;
|
|
145
|
+
|
|
146
|
+
(function rez(value) {
|
|
147
|
+
|
|
148
|
+
// The rez function walks recursively through the object looking for $ref
|
|
149
|
+
// properties. When it finds one that has a value that is a path, then it
|
|
150
|
+
// replaces the $ref object with a reference to the value that is found by
|
|
151
|
+
// the path.
|
|
152
|
+
|
|
153
|
+
if (value && typeof value === "object") {
|
|
154
|
+
if (Array.isArray(value)) {
|
|
155
|
+
value.forEach(function (element, i) {
|
|
156
|
+
if (typeof element === "object" && element !== null) {
|
|
157
|
+
var path = element.$ref;
|
|
158
|
+
if (typeof path === "string" && px.test(path)) {
|
|
159
|
+
value[i] = eval(path);
|
|
160
|
+
} else {
|
|
161
|
+
rez(element);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
Object.keys(value).forEach(function (name) {
|
|
167
|
+
var item = value[name];
|
|
168
|
+
if (typeof item === "object" && item !== null) {
|
|
169
|
+
var path = item.$ref;
|
|
170
|
+
if (typeof path === "string" && px.test(path)) {
|
|
171
|
+
value[name] = eval(path);
|
|
172
|
+
} else {
|
|
173
|
+
rez(item);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}($));
|
|
180
|
+
return $;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
Binary file
|