openchs-models 1.16.2 → 1.17.0
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/dist/Concept.js +3 -1
- package/dist/ObservationsHolder.js +36 -4
- package/dist/application/Form.js +6 -2
- package/dist/application/FormElement.js +5 -0
- package/dist/application/FormElementGroup.js +35 -7
- package/dist/application/ValidationResult.js +1 -0
- package/dist/index.js +8 -0
- package/dist/observation/QuestionGroup.js +18 -1
- package/dist/observation/RepeatableQuestionGroup.js +91 -0
- package/dist/utility/General.js +13 -3
- package/package.json +1 -1
package/dist/Concept.js
CHANGED
|
@@ -31,6 +31,8 @@ var _Identifier = _interopRequireDefault(require("./Identifier"));
|
|
|
31
31
|
|
|
32
32
|
var _QuestionGroup = _interopRequireDefault(require("./observation/QuestionGroup"));
|
|
33
33
|
|
|
34
|
+
var _RepeatableQuestionGroup = _interopRequireDefault(require("./observation/RepeatableQuestionGroup"));
|
|
35
|
+
|
|
34
36
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
35
37
|
|
|
36
38
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
@@ -206,7 +208,7 @@ class Concept {
|
|
|
206
208
|
}
|
|
207
209
|
|
|
208
210
|
if (this.isQuestionGroup()) {
|
|
209
|
-
return _QuestionGroup.default.fromObs(obsValue);
|
|
211
|
+
return 'repeatableObservations' in obsValue ? _RepeatableQuestionGroup.default.fromObs(obsValue) : _QuestionGroup.default.fromObs(obsValue);
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
return new _PrimitiveValue.default(obsValue, this.datatype);
|
|
@@ -27,6 +27,8 @@ var _QuestionGroup = _interopRequireDefault(require("./observation/QuestionGroup
|
|
|
27
27
|
|
|
28
28
|
var _General = _interopRequireDefault(require("./utility/General"));
|
|
29
29
|
|
|
30
|
+
var _RepeatableQuestionGroup = _interopRequireDefault(require("./observation/RepeatableQuestionGroup"));
|
|
31
|
+
|
|
30
32
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
31
33
|
|
|
32
34
|
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
|
@@ -201,7 +203,7 @@ class ObservationsHolder {
|
|
|
201
203
|
uuid
|
|
202
204
|
}) => fe.groupUuid === uuid);
|
|
203
205
|
|
|
204
|
-
this.updateGroupQuestion(
|
|
206
|
+
this.updateGroupQuestion(parentFormElement, fe, value);
|
|
205
207
|
} else {
|
|
206
208
|
concept.isCodedConcept() ? this.addOrUpdateCodedObs(concept, value, fe.isSingleSelect()) : this.addOrUpdatePrimitiveObs(concept, value);
|
|
207
209
|
}
|
|
@@ -261,9 +263,21 @@ class ObservationsHolder {
|
|
|
261
263
|
return observation;
|
|
262
264
|
}
|
|
263
265
|
|
|
264
|
-
updateGroupQuestion(
|
|
266
|
+
updateGroupQuestion(parentFormElement, childFormElement, value) {
|
|
267
|
+
const parentConcept = parentFormElement.concept;
|
|
265
268
|
const parentObservation = this.getObservation(parentConcept);
|
|
266
269
|
const childObservations = _lodash.default.isEmpty(parentObservation) ? new _QuestionGroup.default() : parentObservation.getValueWrapper();
|
|
270
|
+
this.updateChildObservations(childFormElement, childObservations, value);
|
|
271
|
+
|
|
272
|
+
this._removeExistingObs(parentConcept);
|
|
273
|
+
|
|
274
|
+
if (!childObservations.isEmpty()) {
|
|
275
|
+
this.observations.push(_Observation.default.create(parentConcept, childObservations));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
updateChildObservations(childFormElement, childObservations, value) {
|
|
280
|
+
const childConcept = childFormElement.concept;
|
|
267
281
|
|
|
268
282
|
if (childConcept.isPrimitive() && _lodash.default.isNil(childFormElement.durationOptions)) {
|
|
269
283
|
childObservations.removeExistingObs(childConcept);
|
|
@@ -290,11 +304,29 @@ class ObservationsHolder {
|
|
|
290
304
|
}
|
|
291
305
|
}
|
|
292
306
|
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
updateRepeatableGroupQuestion(index, parentFormElement, childFormElement, value, action) {
|
|
310
|
+
const parentConcept = parentFormElement.concept;
|
|
311
|
+
const observations = this.getObservation(parentConcept);
|
|
312
|
+
const repeatableObservations = _lodash.default.isEmpty(observations) ? new _RepeatableQuestionGroup.default() : observations.getValueWrapper();
|
|
313
|
+
|
|
314
|
+
if (action === _RepeatableQuestionGroup.default.actions.add) {
|
|
315
|
+
repeatableObservations.addQuestionGroup();
|
|
316
|
+
return;
|
|
317
|
+
} else if (action === _RepeatableQuestionGroup.default.actions.remove) {
|
|
318
|
+
repeatableObservations.removeQuestionGroup(index);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const childObservations = repeatableObservations.getGroupObservationAtIndex(index);
|
|
323
|
+
this.updateChildObservations(childFormElement, childObservations, value);
|
|
324
|
+
repeatableObservations.updateGroupObservationsAtIndex(childObservations, index);
|
|
293
325
|
|
|
294
326
|
this._removeExistingObs(parentConcept);
|
|
295
327
|
|
|
296
|
-
if (!
|
|
297
|
-
this.observations.push(_Observation.default.create(parentConcept,
|
|
328
|
+
if (!repeatableObservations.isEmpty()) {
|
|
329
|
+
this.observations.push(_Observation.default.create(parentConcept, repeatableObservations));
|
|
298
330
|
}
|
|
299
331
|
}
|
|
300
332
|
|
package/dist/application/Form.js
CHANGED
|
@@ -23,6 +23,8 @@ var _moment = _interopRequireDefault(require("moment"));
|
|
|
23
23
|
|
|
24
24
|
var _QuestionGroup = _interopRequireDefault(require("../observation/QuestionGroup"));
|
|
25
25
|
|
|
26
|
+
var _RepeatableQuestionGroup = _interopRequireDefault(require("../observation/RepeatableQuestionGroup"));
|
|
27
|
+
|
|
26
28
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
27
29
|
|
|
28
30
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
@@ -203,8 +205,10 @@ class Form {
|
|
|
203
205
|
|
|
204
206
|
if (!_lodash.default.isNil(foundObs) && concept.isQuestionGroup()) {
|
|
205
207
|
const clonedObs = foundObs.cloneForEdit();
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
+
const valueWrapper = clonedObs.getValueWrapper();
|
|
209
|
+
const isRepeatable = valueWrapper.isRepeatable();
|
|
210
|
+
const sortedChildObs = isRepeatable ? _lodash.default.flatMap(valueWrapper.getValue(), questionGroup => new _QuestionGroup.default(this.orderQuestionGroupObservations(questionGroup.getValue(), formElement.uuid))) : this.orderQuestionGroupObservations(valueWrapper.getValue(), formElement.uuid);
|
|
211
|
+
clonedObs.valueJSON = JSON.stringify(isRepeatable ? new _RepeatableQuestionGroup.default(sortedChildObs) : new _QuestionGroup.default(sortedChildObs));
|
|
208
212
|
if (!_lodash.default.isEmpty(sortedChildObs)) orderedObservations.push(clonedObs);
|
|
209
213
|
} else {
|
|
210
214
|
if (!_lodash.default.isNil(foundObs)) orderedObservations.push(foundObs);
|
|
@@ -178,6 +178,11 @@ class FormElement {
|
|
|
178
178
|
return _lodash.default.isNil(editable) ? true : editable.getValue();
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
get repeatable() {
|
|
182
|
+
const repeatable = this.recordByKey("repeatable");
|
|
183
|
+
return _lodash.default.isNil(repeatable) ? false : repeatable.getValue();
|
|
184
|
+
}
|
|
185
|
+
|
|
181
186
|
get datePickerMode() {
|
|
182
187
|
const datePickerMode = this.recordByKey("datePickerMode");
|
|
183
188
|
return _lodash.default.isNil(datePickerMode) ? null : datePickerMode.getValue();
|
|
@@ -19,6 +19,10 @@ var _lodash = _interopRequireDefault(require("lodash"));
|
|
|
19
19
|
|
|
20
20
|
var _QuestionGroup = _interopRequireDefault(require("../observation/QuestionGroup"));
|
|
21
21
|
|
|
22
|
+
var _RepeatableQuestionGroup = _interopRequireDefault(require("../observation/RepeatableQuestionGroup"));
|
|
23
|
+
|
|
24
|
+
var _ValidationResult = _interopRequireDefault(require("./ValidationResult"));
|
|
25
|
+
|
|
22
26
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
23
27
|
|
|
24
28
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
@@ -70,19 +74,43 @@ class FormElementGroup {
|
|
|
70
74
|
validate(observationHolder, filteredFormElements) {
|
|
71
75
|
const validationResults = [];
|
|
72
76
|
filteredFormElements.forEach(formElement => {
|
|
73
|
-
if (formElement.isQuestionGroup()) {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
if (formElement.concept.isQuestionGroup()) {
|
|
78
|
+
const childFormElements = _lodash.default.filter(filteredFormElements, fe => fe.groupUuid === formElement.uuid);
|
|
79
|
+
|
|
80
|
+
const observations = observationHolder.findObservation(formElement.concept);
|
|
81
|
+
|
|
82
|
+
if (formElement.repeatable) {
|
|
83
|
+
const repeatableQuestionGroup = _lodash.default.isEmpty(observations) ? new _RepeatableQuestionGroup.default() : observations.getValueWrapper();
|
|
84
|
+
const groupValidationResults = [];
|
|
85
|
+
|
|
86
|
+
_lodash.default.forEach(repeatableQuestionGroup.getAllQuestionGroupObservations(), questionGroup => {
|
|
87
|
+
const childValidations = [];
|
|
88
|
+
this.validateQuestionGroup(questionGroup, childFormElements, childValidations);
|
|
89
|
+
groupValidationResults.push(childValidations);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const isSuccess = _lodash.default.every(groupValidationResults, childValidations => _lodash.default.every(childValidations, ({
|
|
93
|
+
success
|
|
94
|
+
}) => success));
|
|
95
|
+
|
|
96
|
+
const validationResult = new _ValidationResult.default(isSuccess, formElement.uuid);
|
|
97
|
+
validationResult.groupValidations = groupValidationResults;
|
|
98
|
+
validationResults.push(validationResult);
|
|
99
|
+
} else {
|
|
100
|
+
const questionGroup = _lodash.default.isEmpty(observations) ? new _QuestionGroup.default() : observations.getValueWrapper();
|
|
101
|
+
this.validateQuestionGroup(questionGroup, childFormElements, validationResults);
|
|
102
|
+
}
|
|
103
|
+
} else if (!formElement.isQuestionGroup()) {
|
|
80
104
|
this.validateFormElement(formElement, observationHolder.findObservation(formElement.concept), validationResults);
|
|
81
105
|
}
|
|
82
106
|
});
|
|
83
107
|
return validationResults;
|
|
84
108
|
}
|
|
85
109
|
|
|
110
|
+
validateQuestionGroup(questionGroup, childFormElements, validationResults) {
|
|
111
|
+
_lodash.default.forEach(childFormElements, formElement => this.validateFormElement(formElement, questionGroup.findObservation(formElement.concept), validationResults));
|
|
112
|
+
}
|
|
113
|
+
|
|
86
114
|
validateFormElement(formElement, observation, validationResults) {
|
|
87
115
|
const validationResult = formElement.validate(_lodash.default.isNil(observation) ? null : observation.getValue());
|
|
88
116
|
validationResults.push(validationResult);
|
package/dist/index.js
CHANGED
|
@@ -651,6 +651,12 @@ Object.defineProperty(exports, "QuestionGroup", {
|
|
|
651
651
|
return _QuestionGroup.default;
|
|
652
652
|
}
|
|
653
653
|
});
|
|
654
|
+
Object.defineProperty(exports, "RepeatableQuestionGroup", {
|
|
655
|
+
enumerable: true,
|
|
656
|
+
get: function () {
|
|
657
|
+
return _RepeatableQuestionGroup.default;
|
|
658
|
+
}
|
|
659
|
+
});
|
|
654
660
|
|
|
655
661
|
var _AbstractEncounter = _interopRequireDefault(require("./AbstractEncounter"));
|
|
656
662
|
|
|
@@ -866,6 +872,8 @@ var _SubjectMigration = _interopRequireDefault(require("./SubjectMigration"));
|
|
|
866
872
|
|
|
867
873
|
var _QuestionGroup = _interopRequireDefault(require("./observation/QuestionGroup"));
|
|
868
874
|
|
|
875
|
+
var _RepeatableQuestionGroup = _interopRequireDefault(require("./observation/RepeatableQuestionGroup"));
|
|
876
|
+
|
|
869
877
|
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
|
870
878
|
|
|
871
879
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
@@ -25,7 +25,20 @@ class QuestionGroup {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
get toResource() {
|
|
28
|
-
|
|
28
|
+
const resource = {};
|
|
29
|
+
|
|
30
|
+
_lodash.default.forEach(this.groupObservations, obs => {
|
|
31
|
+
const {
|
|
32
|
+
conceptUUID,
|
|
33
|
+
value
|
|
34
|
+
} = obs.toResource;
|
|
35
|
+
|
|
36
|
+
_lodash.default.assign(resource, {
|
|
37
|
+
[conceptUUID]: value
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return resource;
|
|
29
42
|
}
|
|
30
43
|
|
|
31
44
|
static fromObs({
|
|
@@ -66,6 +79,10 @@ class QuestionGroup {
|
|
|
66
79
|
return this.groupObservations;
|
|
67
80
|
}
|
|
68
81
|
|
|
82
|
+
isRepeatable() {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
69
86
|
findObservation(concept) {
|
|
70
87
|
return _lodash.default.find(this.groupObservations, observation => {
|
|
71
88
|
return observation.concept.uuid === concept.uuid;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _lodash = _interopRequireDefault(require("lodash"));
|
|
9
|
+
|
|
10
|
+
var _QuestionGroup = _interopRequireDefault(require("./QuestionGroup"));
|
|
11
|
+
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
|
|
14
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
15
|
+
|
|
16
|
+
class RepeatableQuestionGroup {
|
|
17
|
+
constructor(groupObservations) {
|
|
18
|
+
this.repeatableObservations = _lodash.default.isEmpty(groupObservations) ? [new _QuestionGroup.default()] : groupObservations;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get toResource() {
|
|
22
|
+
return _lodash.default.map(this.repeatableObservations, obs => obs.toResource);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static fromObs({
|
|
26
|
+
repeatableObservations
|
|
27
|
+
}) {
|
|
28
|
+
const newObs = _lodash.default.map(repeatableObservations, obs => _QuestionGroup.default.fromObs(obs));
|
|
29
|
+
|
|
30
|
+
return new RepeatableQuestionGroup(newObs);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getValue() {
|
|
34
|
+
return this.repeatableObservations;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
isRepeatable() {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
cloneForEdit() {
|
|
42
|
+
const newObs = _lodash.default.map(this.repeatableObservations, o => o.cloneForEdit());
|
|
43
|
+
|
|
44
|
+
return new RepeatableQuestionGroup(newObs);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
isEmpty() {
|
|
48
|
+
return _lodash.default.isEmpty(this.repeatableObservations);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
updateGroupObservationsAtIndex(groupObservations, index) {
|
|
52
|
+
if (!groupObservations.isEmpty()) {
|
|
53
|
+
this.repeatableObservations.splice(index, 1, groupObservations);
|
|
54
|
+
} else {
|
|
55
|
+
this.repeatableObservations.splice(index, 1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getGroupObservationAtIndex(index) {
|
|
60
|
+
return this.repeatableObservations[index];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
size() {
|
|
64
|
+
return _lodash.default.size(this.repeatableObservations);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
nonEmptySize() {
|
|
68
|
+
return _lodash.default.size(_lodash.default.filter(this.repeatableObservations, questionGroup => !questionGroup.isEmpty()));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
addQuestionGroup() {
|
|
72
|
+
this.repeatableObservations.push(new _QuestionGroup.default());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
removeQuestionGroup(index) {
|
|
76
|
+
this.repeatableObservations.splice(index, 1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getAllQuestionGroupObservations() {
|
|
80
|
+
return this.repeatableObservations;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_defineProperty(RepeatableQuestionGroup, "actions", {
|
|
86
|
+
add: 'add',
|
|
87
|
+
remove: 'remove'
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
var _default = RepeatableQuestionGroup;
|
|
91
|
+
exports.default = _default;
|
package/dist/utility/General.js
CHANGED
|
@@ -17,6 +17,8 @@ var _Concept = _interopRequireDefault(require("../Concept"));
|
|
|
17
17
|
|
|
18
18
|
var _QuestionGroup = _interopRequireDefault(require("../observation/QuestionGroup"));
|
|
19
19
|
|
|
20
|
+
var _RepeatableQuestionGroup = _interopRequireDefault(require("../observation/RepeatableQuestionGroup"));
|
|
21
|
+
|
|
20
22
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
21
23
|
|
|
22
24
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
@@ -156,8 +158,8 @@ class General {
|
|
|
156
158
|
observation.concept = concept;
|
|
157
159
|
|
|
158
160
|
if (concept.isQuestionGroup()) {
|
|
159
|
-
const
|
|
160
|
-
observation.valueJSON = JSON.stringify(
|
|
161
|
+
const valueJson = _lodash.default.isArray(value) ? this.getRepeatableQuestionValue(value, entityService) : this.createQuestionGroupObservations(value, entityService);
|
|
162
|
+
observation.valueJSON = JSON.stringify(valueJson);
|
|
161
163
|
} else {
|
|
162
164
|
observation.valueJSON = JSON.stringify(observation.concept.getValueWrapperFor(value));
|
|
163
165
|
}
|
|
@@ -171,13 +173,21 @@ class General {
|
|
|
171
173
|
return dest;
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
static getRepeatableQuestionValue(values, entityService) {
|
|
177
|
+
const repeatableValues = _lodash.default.map(values, value => this.createQuestionGroupObservations(value, entityService));
|
|
178
|
+
|
|
179
|
+
return new _RepeatableQuestionGroup.default(repeatableValues);
|
|
180
|
+
}
|
|
181
|
+
|
|
174
182
|
static createQuestionGroupObservations(keyValues, entityService) {
|
|
175
|
-
|
|
183
|
+
const questionGroupObservations = _lodash.default.map(_lodash.default.toPairs(keyValues), ([conceptUUID, value]) => {
|
|
176
184
|
const observation = new _Observation.default();
|
|
177
185
|
observation.concept = entityService.findByKey("uuid", conceptUUID, _Concept.default.schema.name);
|
|
178
186
|
observation.valueJSON = observation.concept.getValueWrapperFor(value);
|
|
179
187
|
return observation;
|
|
180
188
|
});
|
|
189
|
+
|
|
190
|
+
return new _QuestionGroup.default(questionGroupObservations);
|
|
181
191
|
}
|
|
182
192
|
|
|
183
193
|
static pick(from, attributes, listAttributes) {
|