xstate 3.2.1 → 3.3.3
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/.vscode/launch.json +15 -13
- package/README.md +37 -9
- package/dist/xstate.js +1 -1
- package/dist/xstate.utils.js +1 -1
- package/es/Machine.d.ts +2 -2
- package/es/Machine.js +2 -2
- package/es/State.d.ts +8 -7
- package/es/State.js +3 -2
- package/es/StateNode.d.ts +50 -13
- package/es/StateNode.js +617 -412
- package/es/graph.d.ts +9 -6
- package/es/graph.js +31 -24
- package/es/patterns.js +1 -1
- package/es/scxml.d.ts +2 -1
- package/es/scxml.js +33 -10
- package/es/types.d.ts +38 -7
- package/es/utils.d.ts +14 -1
- package/es/utils.js +33 -5
- package/lib/Machine.d.ts +2 -2
- package/lib/Machine.js +2 -2
- package/lib/State.d.ts +8 -7
- package/lib/State.js +3 -2
- package/lib/StateNode.d.ts +50 -13
- package/lib/StateNode.js +616 -411
- package/lib/graph.d.ts +9 -6
- package/lib/graph.js +30 -22
- package/lib/patterns.js +1 -1
- package/lib/scxml.d.ts +2 -1
- package/lib/scxml.js +33 -10
- package/lib/types.d.ts +38 -7
- package/lib/utils.d.ts +14 -1
- package/lib/utils.js +35 -5
- package/package.json +3 -3
- package/src/Machine.ts +5 -3
- package/src/State.ts +10 -2
- package/src/StateNode.ts +966 -590
- package/src/graph.ts +60 -31
- package/src/scxml.ts +80 -49
- package/src/types.ts +48 -7
- package/src/utils.ts +52 -7
- package/test/actions.test.ts +24 -1
- package/test/activities.test.ts +165 -0
- package/test/deep.test.ts +14 -16
- package/test/deterministic.test.ts +26 -5
- package/test/examples/6.17.test.ts +64 -0
- package/test/fixtures/id.ts +1 -1
- package/test/graph.test.ts +39 -16
- package/test/guards.test.ts +172 -15
- package/test/history.test.ts +193 -58
- package/test/invalid.test.ts +48 -0
- package/test/multiple.test.ts +12 -18
- package/test/parallel.test.ts +472 -1
- package/test/scxml.test.ts +13 -4
- package/test/stateIn.test.ts +1 -1
- package/test/transient.test.ts +183 -1
package/test/history.test.ts
CHANGED
|
@@ -7,7 +7,7 @@ describe('history states', () => {
|
|
|
7
7
|
initial: 'off',
|
|
8
8
|
states: {
|
|
9
9
|
off: {
|
|
10
|
-
on: { POWER: 'on.$history' }
|
|
10
|
+
on: { POWER: 'on.$history', H_POWER: 'on.H' }
|
|
11
11
|
},
|
|
12
12
|
on: {
|
|
13
13
|
initial: 'first',
|
|
@@ -18,10 +18,14 @@ describe('history states', () => {
|
|
|
18
18
|
second: {
|
|
19
19
|
on: { SWITCH: 'third' }
|
|
20
20
|
},
|
|
21
|
-
third: {}
|
|
21
|
+
third: {},
|
|
22
|
+
H: {
|
|
23
|
+
history: true
|
|
24
|
+
}
|
|
22
25
|
},
|
|
23
26
|
on: {
|
|
24
|
-
POWER: 'off'
|
|
27
|
+
POWER: 'off',
|
|
28
|
+
H_POWER: 'off'
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
}
|
|
@@ -37,12 +41,36 @@ describe('history states', () => {
|
|
|
37
41
|
);
|
|
38
42
|
});
|
|
39
43
|
|
|
44
|
+
it('should go to the most recently visited state (explicit)', () => {
|
|
45
|
+
const onSecondState = historyMachine.transition('on', 'SWITCH');
|
|
46
|
+
const offState = historyMachine.transition(onSecondState, 'H_POWER');
|
|
47
|
+
|
|
48
|
+
assert.equal(
|
|
49
|
+
historyMachine.transition(offState, 'H_POWER').toString(),
|
|
50
|
+
'on.second'
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
40
54
|
it('should go to the initial state when no history present', () => {
|
|
41
55
|
assert.equal(
|
|
42
56
|
historyMachine.transition('off', 'POWER').toString(),
|
|
43
57
|
'on.first'
|
|
44
58
|
);
|
|
45
59
|
});
|
|
60
|
+
|
|
61
|
+
it('should go to the initial state when no history present (explicit)', () => {
|
|
62
|
+
assert.equal(
|
|
63
|
+
historyMachine.transition('off', 'H_POWER').toString(),
|
|
64
|
+
'on.first'
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should dispose of previous histories', () => {
|
|
69
|
+
const onSecondState = historyMachine.transition('on', 'SWITCH');
|
|
70
|
+
const offState = historyMachine.transition(onSecondState, 'H_POWER');
|
|
71
|
+
const nextState = historyMachine.transition(offState, 'H_POWER');
|
|
72
|
+
assert.isUndefined(nextState.history!.history);
|
|
73
|
+
});
|
|
46
74
|
});
|
|
47
75
|
|
|
48
76
|
describe('deep history states', () => {
|
|
@@ -53,8 +81,7 @@ describe('deep history states', () => {
|
|
|
53
81
|
off: {
|
|
54
82
|
on: {
|
|
55
83
|
POWER: 'on.$history',
|
|
56
|
-
DEEP_POWER: 'on
|
|
57
|
-
DEEPEST_POWER: 'on.$history.$history.$history'
|
|
84
|
+
DEEP_POWER: 'on.deepHistory'
|
|
58
85
|
}
|
|
59
86
|
},
|
|
60
87
|
on: {
|
|
@@ -79,6 +106,9 @@ describe('deep history states', () => {
|
|
|
79
106
|
}
|
|
80
107
|
}
|
|
81
108
|
}
|
|
109
|
+
},
|
|
110
|
+
deepHistory: {
|
|
111
|
+
history: 'deep'
|
|
82
112
|
}
|
|
83
113
|
},
|
|
84
114
|
on: {
|
|
@@ -104,7 +134,8 @@ describe('deep history states', () => {
|
|
|
104
134
|
'on.second.A'
|
|
105
135
|
);
|
|
106
136
|
});
|
|
107
|
-
|
|
137
|
+
|
|
138
|
+
it('should go to the deep history (explicit)', () => {
|
|
108
139
|
// on.second.B.P -> off
|
|
109
140
|
const stateOff = historyMachine.transition(state2BP, 'POWER');
|
|
110
141
|
assert.equal(
|
|
@@ -112,21 +143,15 @@ describe('deep history states', () => {
|
|
|
112
143
|
'on.second.B.P'
|
|
113
144
|
);
|
|
114
145
|
});
|
|
146
|
+
|
|
115
147
|
it('should go to the deepest history', () => {
|
|
116
148
|
// on.second.B.Q -> off
|
|
117
149
|
const stateOff = historyMachine.transition(state2BQ, 'POWER');
|
|
118
150
|
assert.equal(
|
|
119
|
-
historyMachine.transition(stateOff, '
|
|
151
|
+
historyMachine.transition(stateOff, 'DEEP_POWER').toString(),
|
|
120
152
|
'on.second.B.Q'
|
|
121
153
|
);
|
|
122
154
|
});
|
|
123
|
-
it('can go to the shallow histor even when $history.$history is used', () => {
|
|
124
|
-
const stateOff = historyMachine.transition(state2A, 'POWER');
|
|
125
|
-
assert.equal(
|
|
126
|
-
historyMachine.transition(stateOff, 'DEEPEST_POWER').toString(),
|
|
127
|
-
'on.second.A'
|
|
128
|
-
);
|
|
129
|
-
});
|
|
130
155
|
});
|
|
131
156
|
});
|
|
132
157
|
|
|
@@ -139,12 +164,11 @@ describe('parallel history states', () => {
|
|
|
139
164
|
on: {
|
|
140
165
|
SWITCH: 'on', // go to the initial states
|
|
141
166
|
POWER: 'on.$history',
|
|
142
|
-
DEEP_POWER: 'on
|
|
143
|
-
DEEPEST_POWER: 'on.$history.$history.$history',
|
|
167
|
+
DEEP_POWER: 'on.deepHistory',
|
|
144
168
|
PARALLEL_HISTORY: [{ target: ['on.A.$history', 'on.K.$history'] }],
|
|
145
169
|
PARALLEL_SOME_HISTORY: [{ target: ['on.A.C', 'on.K.$history'] }],
|
|
146
170
|
PARALLEL_DEEP_HISTORY: [
|
|
147
|
-
{ target: ['on.A
|
|
171
|
+
{ target: ['on.A.deepHistory', 'on.K.deepHistory'] }
|
|
148
172
|
]
|
|
149
173
|
}
|
|
150
174
|
},
|
|
@@ -165,6 +189,9 @@ describe('parallel history states', () => {
|
|
|
165
189
|
},
|
|
166
190
|
E: {}
|
|
167
191
|
}
|
|
192
|
+
},
|
|
193
|
+
deepHistory: {
|
|
194
|
+
history: 'deep'
|
|
168
195
|
}
|
|
169
196
|
}
|
|
170
197
|
},
|
|
@@ -182,8 +209,17 @@ describe('parallel history states', () => {
|
|
|
182
209
|
},
|
|
183
210
|
O: {}
|
|
184
211
|
}
|
|
212
|
+
},
|
|
213
|
+
deepHistory: {
|
|
214
|
+
history: 'deep'
|
|
185
215
|
}
|
|
186
216
|
}
|
|
217
|
+
},
|
|
218
|
+
shallowHistory: {
|
|
219
|
+
history: 'shallow'
|
|
220
|
+
},
|
|
221
|
+
deepHistory: {
|
|
222
|
+
history: 'deep'
|
|
187
223
|
}
|
|
188
224
|
},
|
|
189
225
|
on: {
|
|
@@ -224,50 +260,10 @@ describe('parallel history states', () => {
|
|
|
224
260
|
);
|
|
225
261
|
});
|
|
226
262
|
|
|
227
|
-
it('should
|
|
228
|
-
const stateOff = historyMachine.transition(stateACDKL, 'POWER');
|
|
229
|
-
assert.deepEqual(
|
|
230
|
-
historyMachine.transition(stateOff, 'DEEPEST_POWER').value,
|
|
231
|
-
{
|
|
232
|
-
on: { A: { C: 'D' }, K: 'L' }
|
|
233
|
-
}
|
|
234
|
-
);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
it('should remember second level state history, ignoring too many levels of $history', () => {
|
|
238
|
-
const stateOff = historyMachine.transition(stateACDKL, 'POWER');
|
|
239
|
-
assert.deepEqual(
|
|
240
|
-
historyMachine.transition(stateOff, 'DEEPEST_POWER').value,
|
|
241
|
-
{
|
|
242
|
-
on: { A: { C: 'D' }, K: 'L' }
|
|
243
|
-
}
|
|
244
|
-
);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it('should remember three levels of state history', () => {
|
|
248
|
-
const stateOff = historyMachine.transition(stateACEKL, 'POWER');
|
|
249
|
-
assert.deepEqual(
|
|
250
|
-
historyMachine.transition(stateOff, 'DEEPEST_POWER').value,
|
|
251
|
-
{
|
|
252
|
-
on: { A: { C: 'E' }, K: 'L' }
|
|
253
|
-
}
|
|
254
|
-
);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
xit('should re-enter each regions of parallel state correctly', () => {
|
|
263
|
+
it('should re-enter each regions of parallel state correctly', () => {
|
|
258
264
|
const stateOff = historyMachine.transition(stateACEKMO, 'POWER');
|
|
259
265
|
assert.deepEqual(
|
|
260
266
|
historyMachine.transition(stateOff, 'DEEP_POWER').value,
|
|
261
|
-
{
|
|
262
|
-
on: { A: { C: 'D' }, K: { M: 'N' } }
|
|
263
|
-
}
|
|
264
|
-
);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
xit('should retain all regions of parallel state', () => {
|
|
268
|
-
const stateOff = historyMachine.transition(stateACEKMO, 'POWER');
|
|
269
|
-
assert.deepEqual(
|
|
270
|
-
historyMachine.transition(stateOff, 'DEEPEST_POWER').value,
|
|
271
267
|
{
|
|
272
268
|
on: { A: { C: 'E' }, K: { M: 'O' } }
|
|
273
269
|
}
|
|
@@ -305,3 +301,142 @@ describe('parallel history states', () => {
|
|
|
305
301
|
});
|
|
306
302
|
});
|
|
307
303
|
});
|
|
304
|
+
|
|
305
|
+
describe('transient history', () => {
|
|
306
|
+
const transientMachine = Machine({
|
|
307
|
+
initial: 'A',
|
|
308
|
+
parallel: false,
|
|
309
|
+
states: {
|
|
310
|
+
A: {
|
|
311
|
+
on: { EVENT: 'B' }
|
|
312
|
+
},
|
|
313
|
+
B: {
|
|
314
|
+
on: {
|
|
315
|
+
// eventless transition
|
|
316
|
+
'': 'C'
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
C: {}
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
it('should have history on transient transitions', () => {
|
|
323
|
+
const nextState = transientMachine.transition('A', 'EVENT');
|
|
324
|
+
assert.equal(nextState.value, 'C');
|
|
325
|
+
assert.isDefined(nextState.history);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe('internal transition with history', () => {
|
|
330
|
+
const machine = Machine({
|
|
331
|
+
key: 'test',
|
|
332
|
+
initial: 'first',
|
|
333
|
+
states: {
|
|
334
|
+
first: {
|
|
335
|
+
initial: 'foo',
|
|
336
|
+
states: {
|
|
337
|
+
foo: {}
|
|
338
|
+
},
|
|
339
|
+
on: {
|
|
340
|
+
NEXT: 'second.other'
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
second: {
|
|
344
|
+
initial: 'nested',
|
|
345
|
+
states: {
|
|
346
|
+
nested: {},
|
|
347
|
+
other: {},
|
|
348
|
+
hist: {
|
|
349
|
+
history: true
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
on: {
|
|
353
|
+
NEXT: [
|
|
354
|
+
{
|
|
355
|
+
target: '.hist'
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should transition internally to the most recently visited state', () => {
|
|
364
|
+
// {
|
|
365
|
+
// $current: 'first',
|
|
366
|
+
// first: undefined,
|
|
367
|
+
// second: {
|
|
368
|
+
// $current: 'nested',
|
|
369
|
+
// nested: undefined,
|
|
370
|
+
// other: undefined
|
|
371
|
+
// }
|
|
372
|
+
// }
|
|
373
|
+
const state2 = machine.transition(machine.initialState, 'NEXT');
|
|
374
|
+
// {
|
|
375
|
+
// $current: 'second',
|
|
376
|
+
// first: undefined,
|
|
377
|
+
// second: {
|
|
378
|
+
// $current: 'other',
|
|
379
|
+
// nested: undefined,
|
|
380
|
+
// other: undefined
|
|
381
|
+
// }
|
|
382
|
+
// }
|
|
383
|
+
const state3 = machine.transition(state2, 'NEXT');
|
|
384
|
+
// {
|
|
385
|
+
// $current: 'second',
|
|
386
|
+
// first: undefined,
|
|
387
|
+
// second: {
|
|
388
|
+
// $current: 'other',
|
|
389
|
+
// nested: undefined,
|
|
390
|
+
// other: undefined
|
|
391
|
+
// }
|
|
392
|
+
// }
|
|
393
|
+
|
|
394
|
+
assert.deepEqual(state3.value, { second: 'other' });
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe('multistage history states', () => {
|
|
399
|
+
const pcWithTurboButtonMachine = Machine({
|
|
400
|
+
key: 'pc-with-turbo-button',
|
|
401
|
+
initial: 'off',
|
|
402
|
+
states: {
|
|
403
|
+
off: {
|
|
404
|
+
on: { POWER: 'starting' }
|
|
405
|
+
},
|
|
406
|
+
starting: {
|
|
407
|
+
on: { STARTED: 'running.H' }
|
|
408
|
+
},
|
|
409
|
+
running: {
|
|
410
|
+
initial: 'normal',
|
|
411
|
+
states: {
|
|
412
|
+
normal: {
|
|
413
|
+
on: { SWITCH_TURBO: 'turbo' }
|
|
414
|
+
},
|
|
415
|
+
turbo: {
|
|
416
|
+
on: { SWITCH_TURBO: 'normal' }
|
|
417
|
+
},
|
|
418
|
+
H: {
|
|
419
|
+
history: true
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
on: {
|
|
423
|
+
POWER: 'off'
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('should go to the most recently visited state', () => {
|
|
430
|
+
const onTurboState = pcWithTurboButtonMachine.transition(
|
|
431
|
+
'running',
|
|
432
|
+
'SWITCH_TURBO'
|
|
433
|
+
);
|
|
434
|
+
const offState = pcWithTurboButtonMachine.transition(onTurboState, 'POWER');
|
|
435
|
+
const loadingState = pcWithTurboButtonMachine.transition(offState, 'POWER');
|
|
436
|
+
|
|
437
|
+
assert.equal(
|
|
438
|
+
pcWithTurboButtonMachine.transition(loadingState, 'STARTED').toString(),
|
|
439
|
+
'running.turbo'
|
|
440
|
+
);
|
|
441
|
+
});
|
|
442
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { assert } from 'chai';
|
|
2
|
+
import { Machine } from '../src/index';
|
|
3
|
+
|
|
4
|
+
const machine = Machine({
|
|
5
|
+
parallel: true,
|
|
6
|
+
states: {
|
|
7
|
+
A: {
|
|
8
|
+
initial: 'A1',
|
|
9
|
+
states: {
|
|
10
|
+
A1: {},
|
|
11
|
+
A2: {}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
B: {
|
|
15
|
+
initial: 'B1',
|
|
16
|
+
states: {
|
|
17
|
+
B1: {},
|
|
18
|
+
B2: {}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('invalid states', () => {
|
|
25
|
+
xit('should reject transitioning from a String state', () => {
|
|
26
|
+
assert.throws(() => machine.transition('A', 'E'));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
xit('should reject transitioning from empty states', () => {
|
|
30
|
+
assert.throws(() => machine.transition({ A: {}, B: {} }, 'E'));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should allow transitioning from valid states', () => {
|
|
34
|
+
machine.transition({ A: 'A1', B: 'B1' }, 'E');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should reject transitioning from bad state configs', () => {
|
|
38
|
+
assert.throws(() => machine.transition({ A: 'A3', B: 'B3' }, 'E'));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
xit('should reject transitioning from partially valid states', () => {
|
|
42
|
+
assert.throws(() => machine.transition({ A: 'A1' }, 'E'));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
xit("should reject transitioning from regions that don't exist", () => {
|
|
46
|
+
assert.throws(() => machine.transition({ A: 'A1', B: 'B1', Z: 'Z1' }, 'E'));
|
|
47
|
+
});
|
|
48
|
+
});
|
package/test/multiple.test.ts
CHANGED
|
@@ -163,44 +163,38 @@ describe('multiple', () => {
|
|
|
163
163
|
});
|
|
164
164
|
|
|
165
165
|
it('should reject two targets in the same region', () => {
|
|
166
|
-
assert.throws(
|
|
167
|
-
|
|
168
|
-
"Event 'BROKEN_SAME_REGION' on state 'machine.simple' leads to an invalid configuration: Two or more states in the orthogonal region 'machine.para.A'"
|
|
166
|
+
assert.throws(() =>
|
|
167
|
+
machine.transition(stateSimple, 'BROKEN_SAME_REGION')
|
|
169
168
|
);
|
|
170
169
|
});
|
|
171
170
|
|
|
172
171
|
it('should reject targets inside and outside a region', () => {
|
|
173
|
-
assert.throws(
|
|
174
|
-
|
|
175
|
-
"Event 'BROKEN_DIFFERENT_REGIONS' on state 'machine.simple' leads to an invalid configuration: Two or more states in the orthogonal region 'machine'"
|
|
172
|
+
assert.throws(() =>
|
|
173
|
+
machine.transition(stateSimple, 'BROKEN_DIFFERENT_REGIONS')
|
|
176
174
|
);
|
|
177
175
|
});
|
|
178
176
|
|
|
179
177
|
it('should reject two targets in different regions', () => {
|
|
180
|
-
assert.throws(
|
|
181
|
-
|
|
182
|
-
"Event 'BROKEN_DIFFERENT_REGIONS_2' on state 'machine.simple' leads to an invalid configuration: Two or more states in the orthogonal region 'machine'"
|
|
178
|
+
assert.throws(() =>
|
|
179
|
+
machine.transition(stateSimple, 'BROKEN_DIFFERENT_REGIONS_2')
|
|
183
180
|
);
|
|
184
181
|
});
|
|
185
182
|
|
|
186
183
|
it('should reject two targets in different regions at different levels', () => {
|
|
187
|
-
assert.throws(
|
|
188
|
-
|
|
189
|
-
"Event 'BROKEN_DIFFERENT_REGIONS_3' on state 'machine.simple' leads to an invalid configuration: Two or more states in the orthogonal region 'machine'"
|
|
184
|
+
assert.throws(() =>
|
|
185
|
+
machine.transition(stateSimple, 'BROKEN_DIFFERENT_REGIONS_3')
|
|
190
186
|
);
|
|
191
187
|
});
|
|
192
188
|
|
|
193
189
|
it('should reject two deep targets in different regions at top level', () => {
|
|
194
|
-
assert.throws(
|
|
195
|
-
|
|
196
|
-
"Event 'BROKEN_DIFFERENT_REGIONS_3' on state 'machine.simple' leads to an invalid configuration: Two or more states in the orthogonal region 'machine'"
|
|
190
|
+
assert.throws(() =>
|
|
191
|
+
machine.transition(stateSimple, 'BROKEN_DIFFERENT_REGIONS_3')
|
|
197
192
|
);
|
|
198
193
|
});
|
|
199
194
|
|
|
200
195
|
it('should reject two deep targets in different regions at different levels', () => {
|
|
201
|
-
assert.throws(
|
|
202
|
-
|
|
203
|
-
"Event 'BROKEN_DIFFERENT_REGIONS_4' on state 'machine.simple' leads to an invalid configuration: Two or more states in the orthogonal region 'machine.para2.K2'"
|
|
196
|
+
assert.throws(() =>
|
|
197
|
+
machine.transition(stateSimple, 'BROKEN_DIFFERENT_REGIONS_4')
|
|
204
198
|
);
|
|
205
199
|
});
|
|
206
200
|
});
|