neo.mjs 6.7.2 → 6.7.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/apps/ServiceWorker.mjs +2 -2
- package/examples/ServiceWorker.mjs +2 -2
- package/package.json +1 -1
- package/src/DefaultConfig.mjs +2 -2
- package/src/component/Base.mjs +5 -23
- package/src/draggable/toolbar/SortZone.mjs +4 -4
- package/src/form/field/FileUpload.mjs +2 -2
- package/src/form/field/Text.mjs +20 -0
- package/src/main/DomAccess.mjs +12 -10
- package/src/table/Container.mjs +19 -10
- package/src/table/header/Button.mjs +32 -30
- package/src/table/header/Toolbar.mjs +12 -12
- package/src/util/Rectangle.mjs +18 -0
package/apps/ServiceWorker.mjs
CHANGED
package/package.json
CHANGED
package/src/DefaultConfig.mjs
CHANGED
@@ -236,12 +236,12 @@ const DefaultConfig = {
|
|
236
236
|
useVdomWorker: true,
|
237
237
|
/**
|
238
238
|
* buildScripts/injectPackageVersion.mjs will update this value
|
239
|
-
* @default '6.7.
|
239
|
+
* @default '6.7.4'
|
240
240
|
* @memberOf! module:Neo
|
241
241
|
* @name config.version
|
242
242
|
* @type String
|
243
243
|
*/
|
244
|
-
version: '6.7.
|
244
|
+
version: '6.7.4'
|
245
245
|
};
|
246
246
|
|
247
247
|
Object.assign(DefaultConfig, {
|
package/src/component/Base.mjs
CHANGED
@@ -1362,31 +1362,13 @@ class Base extends CoreBase {
|
|
1362
1362
|
* @returns {Promise<Neo.util.Rectangle>}
|
1363
1363
|
*/
|
1364
1364
|
async getDomRect(id=this.id, appName=this.appName) {
|
1365
|
-
|
1366
|
-
return await Neo.main.DomAccess.getBoundingClientRect({appName, id});
|
1367
|
-
}
|
1368
|
-
else {
|
1369
|
-
const
|
1370
|
-
{
|
1371
|
-
x,
|
1372
|
-
y,
|
1373
|
-
width,
|
1374
|
-
height,
|
1375
|
-
minWidth,
|
1376
|
-
minHeight
|
1377
|
-
} = await Neo.main.DomAccess.getBoundingClientRect({appName, id}),
|
1378
|
-
result = new Rectangle(x, y, width, height);
|
1379
|
-
|
1380
|
-
if (minWidth) {
|
1381
|
-
result.minWidth = minWidth;
|
1382
|
-
}
|
1365
|
+
const result = await Neo.main.DomAccess.getBoundingClientRect({appName, id});
|
1383
1366
|
|
1384
|
-
|
1385
|
-
|
1386
|
-
}
|
1387
|
-
|
1388
|
-
return result;
|
1367
|
+
if (Array.isArray(result)) {
|
1368
|
+
return result.map(rect => Rectangle.clone(rect))
|
1389
1369
|
}
|
1370
|
+
|
1371
|
+
return Rectangle.clone(result)
|
1390
1372
|
}
|
1391
1373
|
|
1392
1374
|
/**
|
@@ -263,17 +263,17 @@ class SortZone extends DragZone {
|
|
263
263
|
map = me.indexMap,
|
264
264
|
rect1 = itemRects[index1],
|
265
265
|
rect2 = itemRects[index2],
|
266
|
-
rect1Copy =
|
267
|
-
rect2Copy =
|
266
|
+
rect1Copy = rect1.clone(),
|
267
|
+
rect2Copy = rect2.clone();
|
268
268
|
|
269
269
|
if (me.sortDirection === 'horizontal') {
|
270
270
|
rect1.width = rect2Copy.width;
|
271
|
-
rect2.
|
271
|
+
rect2.x = rect1Copy.x + rect2Copy.width;
|
272
272
|
rect2.width = rect1Copy.width;
|
273
273
|
} else {
|
274
274
|
rect1.height = rect2Copy.height;
|
275
275
|
rect2.height = rect1Copy.height;
|
276
|
-
rect2.
|
276
|
+
rect2.y = rect1Copy.y + rect2Copy.height;
|
277
277
|
}
|
278
278
|
|
279
279
|
tmp = map[index1];
|
@@ -535,9 +535,9 @@ class FileUpload extends Base {
|
|
535
535
|
}
|
536
536
|
// Failed network request
|
537
537
|
else {
|
538
|
+
me.error = xhr.response ? JSON.parse(xhr.response).message : `HTTP status : ${xhr.statusText}`;
|
538
539
|
me.progress = NaN;
|
539
|
-
me.
|
540
|
-
me.state = 'upload-failed';
|
540
|
+
me.state = 'upload-failed';
|
541
541
|
}
|
542
542
|
}
|
543
543
|
|
package/src/form/field/Text.mjs
CHANGED
@@ -75,6 +75,11 @@ class Text extends Base {
|
|
75
75
|
* @member {String[]|null} disabledChars_=null
|
76
76
|
*/
|
77
77
|
disabledChars_: null,
|
78
|
+
/**
|
79
|
+
* Configure the value of empty fields. null or an empty string is recommended.
|
80
|
+
* @member {String|null} emptyValue=null
|
81
|
+
*/
|
82
|
+
emptyValue: null,
|
78
83
|
/**
|
79
84
|
* @member {String|null} error_=null
|
80
85
|
*/
|
@@ -950,6 +955,21 @@ class Text extends Base {
|
|
950
955
|
return value
|
951
956
|
}
|
952
957
|
|
958
|
+
/**
|
959
|
+
* Triggered before the value config gets changed
|
960
|
+
* @param {String|null} value
|
961
|
+
* @param {String|null} oldValue
|
962
|
+
* @returns {String|null}
|
963
|
+
* @protected
|
964
|
+
*/
|
965
|
+
beforeSetValue(value, oldValue) {
|
966
|
+
if (value === null || value === '') {
|
967
|
+
return this.emptyValue
|
968
|
+
}
|
969
|
+
|
970
|
+
return value
|
971
|
+
}
|
972
|
+
|
953
973
|
/**
|
954
974
|
* Resets the field to its original value or null depending on the clearToOriginalValue config
|
955
975
|
*/
|
package/src/main/DomAccess.mjs
CHANGED
@@ -351,16 +351,18 @@ class DomAccess extends Base {
|
|
351
351
|
* Returns node.getBoundingClientRect() for a given dom node id
|
352
352
|
* @param {Object} data
|
353
353
|
* @param {Array|String} data.id either an id or an array of ids
|
354
|
-
* @returns {
|
354
|
+
* @returns {DOMRect|DOMRect[]} In case id is an array, an array of DomRects is returned, otherwise an DomRect object
|
355
355
|
*/
|
356
356
|
getBoundingClientRect(data) {
|
357
|
-
let
|
357
|
+
let me = this,
|
358
|
+
returnData;
|
358
359
|
|
359
360
|
if (Array.isArray(data.id)) {
|
360
|
-
return data.id.map(id =>
|
361
|
+
return data.id.map(id => me.getBoundingClientRect({ id }));
|
361
362
|
} else {
|
362
|
-
let node =
|
363
|
-
rect = {},
|
363
|
+
let node = me.getElementOrBody(data.nodeType ? data : data.id),
|
364
|
+
rect = {},
|
365
|
+
minWidth, minHeight, style;
|
364
366
|
|
365
367
|
returnData = {};
|
366
368
|
|
@@ -377,15 +379,15 @@ class DomAccess extends Base {
|
|
377
379
|
// Note that 0px is what the DOM reports if no minWidth is specified
|
378
380
|
// so we do not report a minimum in these cases.
|
379
381
|
if (lengthRE.test(minWidth) && minWidth !== '0px') {
|
380
|
-
returnData.minWidth =
|
382
|
+
returnData.minWidth = me.measure({ value : minWidth, id : node})
|
381
383
|
}
|
382
384
|
if (lengthRE.test(minHeight) && minHeight !== '0px') {
|
383
|
-
returnData.minHeight =
|
385
|
+
returnData.minHeight = me.measure({ value : minHeight, id : node })
|
384
386
|
}
|
385
387
|
}
|
386
388
|
}
|
387
389
|
|
388
|
-
return returnData
|
390
|
+
return returnData
|
389
391
|
}
|
390
392
|
|
391
393
|
getClippedRect(data) {
|
@@ -395,11 +397,11 @@ class DomAccess extends Base {
|
|
395
397
|
|
396
398
|
for (let parentElement = node.offsetParent; rect && parentElement !== document.documentElement; parentElement = parentElement.parentElement) {
|
397
399
|
if (defaultView.getComputedStyle(parentElement).getPropertyValue('overflow') !== 'visible') {
|
398
|
-
rect = rect.intersects(this.getBoundingClientRect(parentElement))
|
400
|
+
rect = rect.intersects(this.getBoundingClientRect(parentElement))
|
399
401
|
}
|
400
402
|
}
|
401
403
|
|
402
|
-
return rect
|
404
|
+
return rect
|
403
405
|
}
|
404
406
|
|
405
407
|
onDocumentMutation(mutations) {
|
package/src/table/Container.mjs
CHANGED
@@ -114,6 +114,22 @@ class Container extends BaseContainer {
|
|
114
114
|
]}
|
115
115
|
}
|
116
116
|
|
117
|
+
/**
|
118
|
+
* Convenience method to access the Neo.table.header.Toolbar
|
119
|
+
* @returns {Neo.table.header.Toolbar|null}
|
120
|
+
*/
|
121
|
+
get headerToolbar() {
|
122
|
+
return Neo.getComponent(this.headerToolbarId) || Neo.get(this.headerToolbarId)
|
123
|
+
}
|
124
|
+
|
125
|
+
/**
|
126
|
+
* Convenience method to access the Neo.table.View
|
127
|
+
* @returns {Neo.table.View|null}
|
128
|
+
*/
|
129
|
+
get view() {
|
130
|
+
return Neo.getComponent(this.viewId) || Neo.get(this.viewId)
|
131
|
+
}
|
132
|
+
|
117
133
|
/**
|
118
134
|
* @param {Object} config
|
119
135
|
*/
|
@@ -163,7 +179,7 @@ class Container extends BaseContainer {
|
|
163
179
|
*/
|
164
180
|
afterSetShowHeaderFilters(value, oldValue) {
|
165
181
|
if (oldValue !== undefined) {
|
166
|
-
|
182
|
+
this.headerToolbar.showHeaderFilters = value
|
167
183
|
}
|
168
184
|
}
|
169
185
|
|
@@ -175,7 +191,7 @@ class Container extends BaseContainer {
|
|
175
191
|
*/
|
176
192
|
afterSetSortable(value, oldValue) {
|
177
193
|
if (oldValue !== undefined) {
|
178
|
-
|
194
|
+
this.headerToolbar.sortable = value
|
179
195
|
}
|
180
196
|
}
|
181
197
|
|
@@ -378,13 +394,6 @@ class Container extends BaseContainer {
|
|
378
394
|
return this.vdom.cn[0]
|
379
395
|
}
|
380
396
|
|
381
|
-
/**
|
382
|
-
* @returns {Neo.table.View}
|
383
|
-
*/
|
384
|
-
getView() {
|
385
|
-
return Neo.getComponent(this.viewId) || Neo.get(this.viewId)
|
386
|
-
}
|
387
|
-
|
388
397
|
/**
|
389
398
|
* @override
|
390
399
|
* @returns {Neo.vdom.VNode}
|
@@ -487,7 +496,7 @@ class Container extends BaseContainer {
|
|
487
496
|
* @param {*} opts.value
|
488
497
|
*/
|
489
498
|
onStoreRecordChange(opts) {
|
490
|
-
|
499
|
+
this.view.onStoreRecordChange(opts)
|
491
500
|
}
|
492
501
|
|
493
502
|
/**
|
@@ -134,12 +134,12 @@ class Button extends BaseButton {
|
|
134
134
|
let me = this;
|
135
135
|
|
136
136
|
if (value === true) {
|
137
|
-
me.getVdomRoot().draggable = true
|
137
|
+
me.getVdomRoot().draggable = true
|
138
138
|
} else {
|
139
|
-
delete me.getVdomRoot().draggable
|
139
|
+
delete me.getVdomRoot().draggable
|
140
140
|
}
|
141
141
|
|
142
|
-
me.update()
|
142
|
+
me.update()
|
143
143
|
}
|
144
144
|
|
145
145
|
/**
|
@@ -173,13 +173,13 @@ class Button extends BaseButton {
|
|
173
173
|
|
174
174
|
// testing check until all example tables have a store
|
175
175
|
if (!container || !container.store) {
|
176
|
-
return
|
176
|
+
return
|
177
177
|
}
|
178
178
|
|
179
179
|
me.mounted && me.fire('sort', {
|
180
180
|
direction: value,
|
181
181
|
property : me.dataField
|
182
|
-
})
|
182
|
+
})
|
183
183
|
}
|
184
184
|
|
185
185
|
/**
|
@@ -213,15 +213,15 @@ class Button extends BaseButton {
|
|
213
213
|
...me.editorConfig
|
214
214
|
});
|
215
215
|
|
216
|
-
me.vdom.cn.push(me.filterField.vdom)
|
216
|
+
me.vdom.cn.push(me.filterField.vdom)
|
217
217
|
} else {
|
218
|
-
delete me.filterField.vdom.removeDom
|
218
|
+
delete me.filterField.vdom.removeDom
|
219
219
|
}
|
220
220
|
} else if (me.filterField) {
|
221
|
-
me.filterField.vdom.removeDom = true
|
221
|
+
me.filterField.vdom.removeDom = true
|
222
222
|
}
|
223
223
|
|
224
|
-
me.update()
|
224
|
+
me.update()
|
225
225
|
}
|
226
226
|
|
227
227
|
/**
|
@@ -236,20 +236,22 @@ class Button extends BaseButton {
|
|
236
236
|
|
237
237
|
if (value === true) {
|
238
238
|
NeoArray.remove(cls, 'neo-sort-hidden');
|
239
|
+
|
239
240
|
me.addDomListeners({
|
240
241
|
click: me.onButtonClick,
|
241
242
|
scope: me
|
242
|
-
})
|
243
|
+
})
|
243
244
|
} else {
|
244
245
|
NeoArray.add(cls, 'neo-sort-hidden');
|
246
|
+
|
245
247
|
me.removeDomListeners({
|
246
248
|
click: me.onButtonClick,
|
247
249
|
scope: me
|
248
|
-
})
|
250
|
+
})
|
249
251
|
}
|
250
252
|
|
251
253
|
me.cls = cls;
|
252
|
-
me.update()
|
254
|
+
me.update()
|
253
255
|
}
|
254
256
|
|
255
257
|
/**
|
@@ -268,7 +270,7 @@ class Button extends BaseButton {
|
|
268
270
|
destroy(...args) {
|
269
271
|
this.filterField?.destroy();
|
270
272
|
|
271
|
-
super.destroy(...args)
|
273
|
+
super.destroy(...args)
|
272
274
|
}
|
273
275
|
|
274
276
|
/**
|
@@ -277,7 +279,7 @@ class Button extends BaseButton {
|
|
277
279
|
* @returns {Object} The new vdom root
|
278
280
|
*/
|
279
281
|
getVdomRoot() {
|
280
|
-
return this.vdom.cn[0]
|
282
|
+
return this.vdom.cn[0]
|
281
283
|
}
|
282
284
|
|
283
285
|
/**
|
@@ -286,7 +288,7 @@ class Button extends BaseButton {
|
|
286
288
|
* @returns {Object} The new vnode root
|
287
289
|
*/
|
288
290
|
getVnodeRoot() {
|
289
|
-
return this.vnode.childNodes[0]
|
291
|
+
return this.vnode.childNodes[0]
|
290
292
|
}
|
291
293
|
|
292
294
|
/**
|
@@ -301,16 +303,16 @@ class Button extends BaseButton {
|
|
301
303
|
ASC : null,
|
302
304
|
DESC: 'ASC',
|
303
305
|
null: 'DESC'
|
304
|
-
}
|
306
|
+
}
|
305
307
|
} else {
|
306
308
|
map = {
|
307
309
|
ASC : 'DESC',
|
308
310
|
DESC: null,
|
309
311
|
null: 'ASC'
|
310
|
-
}
|
312
|
+
}
|
311
313
|
}
|
312
314
|
|
313
|
-
me.isSorted = map[me.isSorted + '']
|
315
|
+
me.isSorted = map[me.isSorted + '']
|
314
316
|
}
|
315
317
|
|
316
318
|
/**
|
@@ -321,7 +323,7 @@ class Button extends BaseButton {
|
|
321
323
|
style = me.style;
|
322
324
|
|
323
325
|
delete style.opacity;
|
324
|
-
me.style = style
|
326
|
+
me.style = style
|
325
327
|
}
|
326
328
|
|
327
329
|
/**
|
@@ -332,7 +334,7 @@ class Button extends BaseButton {
|
|
332
334
|
cls = me.cls;
|
333
335
|
|
334
336
|
NeoArray.add(cls, 'neo-drag-over');
|
335
|
-
me.cls = cls
|
337
|
+
me.cls = cls
|
336
338
|
}
|
337
339
|
|
338
340
|
/**
|
@@ -343,7 +345,7 @@ class Button extends BaseButton {
|
|
343
345
|
cls = me.cls;
|
344
346
|
|
345
347
|
NeoArray.remove(cls, 'neo-drag-over');
|
346
|
-
me.cls = cls
|
348
|
+
me.cls = cls
|
347
349
|
}
|
348
350
|
|
349
351
|
/**
|
@@ -361,7 +363,7 @@ class Button extends BaseButton {
|
|
361
363
|
style = me.style;
|
362
364
|
|
363
365
|
style.opacity = 0.4;
|
364
|
-
me.style = style
|
366
|
+
me.style = style
|
365
367
|
}
|
366
368
|
|
367
369
|
/**
|
@@ -378,7 +380,7 @@ class Button extends BaseButton {
|
|
378
380
|
tableContainer.createViewData(tableContainer.store.data);
|
379
381
|
|
380
382
|
style.opacity = 1;
|
381
|
-
me.style = style
|
383
|
+
me.style = style
|
382
384
|
}
|
383
385
|
|
384
386
|
/**
|
@@ -404,9 +406,9 @@ class Button extends BaseButton {
|
|
404
406
|
...me.filterConfig
|
405
407
|
});
|
406
408
|
|
407
|
-
store.filters = filters
|
409
|
+
store.filters = filters
|
408
410
|
} else {
|
409
|
-
filter.operator = operator
|
411
|
+
filter.operator = operator
|
410
412
|
}
|
411
413
|
}
|
412
414
|
}
|
@@ -427,7 +429,7 @@ class Button extends BaseButton {
|
|
427
429
|
field = model && model.getField(me.dataField);
|
428
430
|
|
429
431
|
if (value && field.type.toLowerCase() === 'date') {
|
430
|
-
value = new Date(value)
|
432
|
+
value = new Date(value)
|
431
433
|
}
|
432
434
|
|
433
435
|
if (!filter) {
|
@@ -440,9 +442,9 @@ class Button extends BaseButton {
|
|
440
442
|
...me.filterConfig
|
441
443
|
});
|
442
444
|
|
443
|
-
store.filters = filters
|
445
|
+
store.filters = filters
|
444
446
|
} else {
|
445
|
-
filter.value = value
|
447
|
+
filter.value = value
|
446
448
|
}
|
447
449
|
}
|
448
450
|
}
|
@@ -457,7 +459,7 @@ class Button extends BaseButton {
|
|
457
459
|
NeoArray.add(cls, 'neo-sort-hidden');
|
458
460
|
|
459
461
|
me.cls = cls;
|
460
|
-
me._isSorted = null
|
462
|
+
me._isSorted = null
|
461
463
|
}
|
462
464
|
|
463
465
|
/**
|
@@ -469,7 +471,7 @@ class Button extends BaseButton {
|
|
469
471
|
* @returns {*}
|
470
472
|
*/
|
471
473
|
renderer(data) {
|
472
|
-
return data.value
|
474
|
+
return data.value
|
473
475
|
}
|
474
476
|
}
|
475
477
|
|
@@ -60,10 +60,10 @@ class Toolbar extends BaseToolbar {
|
|
60
60
|
me.items.forEach(item => {
|
61
61
|
item.setSilent({
|
62
62
|
showHeaderFilter: value
|
63
|
-
})
|
63
|
+
})
|
64
64
|
});
|
65
65
|
|
66
|
-
me.update()
|
66
|
+
me.update()
|
67
67
|
}
|
68
68
|
}
|
69
69
|
|
@@ -80,10 +80,10 @@ class Toolbar extends BaseToolbar {
|
|
80
80
|
me.items.forEach(item => {
|
81
81
|
item.setSilent({
|
82
82
|
sortable: value
|
83
|
-
})
|
83
|
+
})
|
84
84
|
});
|
85
85
|
|
86
|
-
me.update()
|
86
|
+
me.update()
|
87
87
|
}
|
88
88
|
}
|
89
89
|
|
@@ -118,11 +118,11 @@ class Toolbar extends BaseToolbar {
|
|
118
118
|
style.left = dockLeftWidth + 'px';
|
119
119
|
}
|
120
120
|
|
121
|
-
dockLeftWidth += (item.width + 1)
|
121
|
+
dockLeftWidth += (item.width + 1) // todo: borders fix
|
122
122
|
} else {
|
123
|
-
item.vdom.cls = []
|
123
|
+
item.vdom.cls = [] // remove the button cls from the th tag
|
124
124
|
}
|
125
|
-
|
125
|
+
|
126
126
|
item.sortable = me.sortable;
|
127
127
|
item.wrapperStyle = style;
|
128
128
|
|
@@ -135,11 +135,11 @@ class Toolbar extends BaseToolbar {
|
|
135
135
|
|
136
136
|
item.wrapperStyle = style;
|
137
137
|
|
138
|
-
dockRightWidth += (item.width + 1)
|
138
|
+
dockRightWidth += (item.width + 1) // todo: borders fix
|
139
139
|
}
|
140
140
|
});
|
141
141
|
|
142
|
-
me.update()
|
142
|
+
me.update()
|
143
143
|
}
|
144
144
|
|
145
145
|
/**
|
@@ -148,7 +148,7 @@ class Toolbar extends BaseToolbar {
|
|
148
148
|
* @override
|
149
149
|
*/
|
150
150
|
getLayoutConfig(dock) {
|
151
|
-
return 'base'
|
151
|
+
return 'base'
|
152
152
|
}
|
153
153
|
|
154
154
|
/**
|
@@ -157,7 +157,7 @@ class Toolbar extends BaseToolbar {
|
|
157
157
|
* @returns {Object} The new vdom root
|
158
158
|
*/
|
159
159
|
getVdomRoot() {
|
160
|
-
return this.vdom.cn[0]
|
160
|
+
return this.vdom.cn[0]
|
161
161
|
}
|
162
162
|
|
163
163
|
/**
|
@@ -166,7 +166,7 @@ class Toolbar extends BaseToolbar {
|
|
166
166
|
* @returns {Object} The new vnode root
|
167
167
|
*/
|
168
168
|
getVnodeRoot() {
|
169
|
-
return this.vnode.childNodes[0]
|
169
|
+
return this.vnode.childNodes[0]
|
170
170
|
}
|
171
171
|
}
|
172
172
|
|
package/src/util/Rectangle.mjs
CHANGED
@@ -87,6 +87,15 @@ export default class Rectangle extends DOMRect {
|
|
87
87
|
className: 'Neo.util.Rectangle'
|
88
88
|
}
|
89
89
|
|
90
|
+
/**
|
91
|
+
* @member {Number|null} minHeight=null
|
92
|
+
*/
|
93
|
+
minHeight = null
|
94
|
+
/**
|
95
|
+
* @member {Number|null} minWidth=null
|
96
|
+
*/
|
97
|
+
minWidth = null
|
98
|
+
|
90
99
|
/**
|
91
100
|
* Checks if rect1 does not have an intersection with rect2
|
92
101
|
* !includes() is true for intersections as well
|
@@ -587,4 +596,13 @@ export default class Rectangle extends DOMRect {
|
|
587
596
|
setTimeout(() => div.remove(), 30000);
|
588
597
|
return div;
|
589
598
|
}
|
599
|
+
|
600
|
+
/**
|
601
|
+
* When using JSON.stringify(this), we want to add minHeight & minWidth to the output.
|
602
|
+
* @returns {Object}
|
603
|
+
*/
|
604
|
+
toJSON() {
|
605
|
+
const {bottom, height, left, minHeight, minWidth, right, top, width, x, y} = this;
|
606
|
+
return {bottom, height, left, minHeight, minWidth, right, top, width, x, y}
|
607
|
+
}
|
590
608
|
}
|