@thatopen/components 2.4.3 → 2.4.5
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/core/Components/index.d.ts +1 -1
- package/dist/ifc/IfcPropertiesManager/index.d.ts +1 -1
- package/dist/index.cjs +118 -63
- package/dist/index.min.cjs +3 -3
- package/dist/index.min.mjs +3 -3
- package/dist/index.mjs +119 -64
- package/dist/namespace.d.ts +3908 -3908
- package/dist/openbim/IDSSpecifications/src/facets/Property.d.ts +1 -1
- package/package.json +2 -2
|
@@ -6,7 +6,7 @@ export declare class Components implements Disposable {
|
|
|
6
6
|
/**
|
|
7
7
|
* The version of the @thatopen/components library.
|
|
8
8
|
*/
|
|
9
|
-
static readonly release = "2.4.
|
|
9
|
+
static readonly release = "2.4.5";
|
|
10
10
|
/** {@link Disposable.onDisposed} */
|
|
11
11
|
readonly onDisposed: Event<void>;
|
|
12
12
|
/**
|
|
@@ -284,6 +284,6 @@ export declare class IfcPropertiesManager extends Component implements Disposabl
|
|
|
284
284
|
handle: WEBIFC.Handle<unknown>;
|
|
285
285
|
}>;
|
|
286
286
|
registerChange(model: FragmentsGroup, ...expressID: number[]): void;
|
|
287
|
-
|
|
287
|
+
newSingleProperty(model: FragmentsGroup, type: string, name: string, value: string | number | boolean): Promise<WEBIFC.IFC4.IfcPropertySingleValue | WEBIFC.IFC2X3.IfcPropertySingleValue | WEBIFC.IFC4X3.IfcPropertySingleValue>;
|
|
288
288
|
}
|
|
289
289
|
export * from "./src";
|
package/dist/index.cjs
CHANGED
|
@@ -6312,7 +6312,7 @@ const _Components = class _Components {
|
|
|
6312
6312
|
/**
|
|
6313
6313
|
* The version of the @thatopen/components library.
|
|
6314
6314
|
*/
|
|
6315
|
-
__publicField(_Components, "release", "2.4.
|
|
6315
|
+
__publicField(_Components, "release", "2.4.5");
|
|
6316
6316
|
let Components = _Components;
|
|
6317
6317
|
class SimpleWorld extends Base {
|
|
6318
6318
|
constructor() {
|
|
@@ -12775,6 +12775,9 @@ class ProjectionManager {
|
|
|
12775
12775
|
setupOrthoCamera(height, width) {
|
|
12776
12776
|
this._component.controls.mouseButtons.wheel = CameraControls.ACTION.ZOOM;
|
|
12777
12777
|
this._component.controls.mouseButtons.middle = CameraControls.ACTION.ZOOM;
|
|
12778
|
+
this._component.controls.touches.two = CameraControls.ACTION.TOUCH_ZOOM_TRUCK;
|
|
12779
|
+
this._component.controls.touches.three = CameraControls.ACTION.TOUCH_ZOOM_TRUCK;
|
|
12780
|
+
this._component.controls.dollySpeed = 5;
|
|
12778
12781
|
const pCamera = this._component.threePersp;
|
|
12779
12782
|
const oCamera = this._component.threeOrtho;
|
|
12780
12783
|
oCamera.zoom = 1;
|
|
@@ -12801,6 +12804,9 @@ class ProjectionManager {
|
|
|
12801
12804
|
pCamera.position.copy(oCamera.position);
|
|
12802
12805
|
pCamera.quaternion.copy(oCamera.quaternion);
|
|
12803
12806
|
this._component.controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY;
|
|
12807
|
+
this._component.controls.touches.two = CameraControls.ACTION.TOUCH_DOLLY_TRUCK;
|
|
12808
|
+
this._component.controls.touches.three = CameraControls.ACTION.TOUCH_DOLLY_TRUCK;
|
|
12809
|
+
this._component.controls.dollySpeed = 5;
|
|
12804
12810
|
if (this.matchOrthoDistanceEnabled) {
|
|
12805
12811
|
this._component.controls.distance = this.getDistance();
|
|
12806
12812
|
} else {
|
|
@@ -21106,10 +21112,8 @@ function readDocType$1(xmlData, i) {
|
|
|
21106
21112
|
i += 8;
|
|
21107
21113
|
else if (hasBody && isNotation(xmlData, i))
|
|
21108
21114
|
i += 9;
|
|
21109
|
-
else if (isComment)
|
|
21110
|
-
comment = true;
|
|
21111
21115
|
else
|
|
21112
|
-
|
|
21116
|
+
comment = true;
|
|
21113
21117
|
angleBracketsCount++;
|
|
21114
21118
|
exp = "";
|
|
21115
21119
|
} else if (xmlData[i] === ">") {
|
|
@@ -21153,11 +21157,6 @@ function readEntityExp(xmlData, i) {
|
|
|
21153
21157
|
}
|
|
21154
21158
|
return [entityName2, val2, i];
|
|
21155
21159
|
}
|
|
21156
|
-
function isComment(xmlData, i) {
|
|
21157
|
-
if (xmlData[i + 1] === "!" && xmlData[i + 2] === "-" && xmlData[i + 3] === "-")
|
|
21158
|
-
return true;
|
|
21159
|
-
return false;
|
|
21160
|
-
}
|
|
21161
21160
|
function isEntity(xmlData, i) {
|
|
21162
21161
|
if (xmlData[i + 1] === "!" && xmlData[i + 2] === "E" && xmlData[i + 3] === "N" && xmlData[i + 4] === "T" && xmlData[i + 5] === "I" && xmlData[i + 6] === "T" && xmlData[i + 7] === "Y")
|
|
21163
21162
|
return true;
|
|
@@ -23198,7 +23197,7 @@ ${users}
|
|
|
23198
23197
|
Comment: CommentText,
|
|
23199
23198
|
Viewpoint: Viewpoint2
|
|
23200
23199
|
} = markupComment;
|
|
23201
|
-
if (!(Guid && CommentDate && Author &&
|
|
23200
|
+
if (!(Guid && CommentDate && Author && Comment))
|
|
23202
23201
|
return null;
|
|
23203
23202
|
const viewpoints = this.components.get(Viewpoints);
|
|
23204
23203
|
const comment = new Comment(this.components, CommentText ?? "");
|
|
@@ -26760,7 +26759,7 @@ class MiniMap {
|
|
|
26760
26759
|
this._camera.left = frustumSize * aspect / -2;
|
|
26761
26760
|
this._camera.right = frustumSize * aspect / 2;
|
|
26762
26761
|
this._camera.top = frustumSize / 2;
|
|
26763
|
-
this._camera.bottom = -
|
|
26762
|
+
this._camera.bottom = -1 / 2;
|
|
26764
26763
|
this._camera.updateProjectionMatrix();
|
|
26765
26764
|
this.onResize.trigger(size);
|
|
26766
26765
|
}
|
|
@@ -27248,10 +27247,10 @@ class IDSFacet {
|
|
|
27248
27247
|
let minPass = true;
|
|
27249
27248
|
let maxPass = true;
|
|
27250
27249
|
if (min !== void 0) {
|
|
27251
|
-
minPass = minInclusive ? value
|
|
27250
|
+
minPass = minInclusive ? value >= min : value > min;
|
|
27252
27251
|
}
|
|
27253
27252
|
if (max !== void 0) {
|
|
27254
|
-
maxPass = maxInclusive ? value
|
|
27253
|
+
maxPass = maxInclusive ? value <= max : value < max;
|
|
27255
27254
|
}
|
|
27256
27255
|
pass = minPass && maxPass;
|
|
27257
27256
|
}
|
|
@@ -27294,12 +27293,12 @@ const getParameterXML = (name, parameter) => {
|
|
|
27294
27293
|
if (!parameter)
|
|
27295
27294
|
return parameterXML;
|
|
27296
27295
|
if (parameter.type === "simple") {
|
|
27297
|
-
parameterXML = `<
|
|
27296
|
+
parameterXML = `<simpleValue>${parameter.parameter}</simpleValue>`;
|
|
27298
27297
|
}
|
|
27299
27298
|
if (parameter.type === "enumeration") {
|
|
27300
27299
|
const value = parameter.parameter;
|
|
27301
27300
|
parameterXML = `<xs:restriction base="xs:string">
|
|
27302
|
-
${value.map((v) => `<xs:enumeration value="${v}" />`).join("\
|
|
27301
|
+
${value.map((v) => `<xs:enumeration value="${v}" />`).join("\n")}
|
|
27303
27302
|
</xs:restriction>`;
|
|
27304
27303
|
}
|
|
27305
27304
|
if (parameter.type === "pattern") {
|
|
@@ -27308,9 +27307,44 @@ const getParameterXML = (name, parameter) => {
|
|
|
27308
27307
|
<xs:pattern value="${value}" />
|
|
27309
27308
|
</xs:restriction>`;
|
|
27310
27309
|
}
|
|
27311
|
-
|
|
27310
|
+
if (parameter.type === "bounds") {
|
|
27311
|
+
const { min, minInclusive, max, maxInclusive } = parameter.parameter;
|
|
27312
|
+
let minTag = "";
|
|
27313
|
+
if (min !== void 0) {
|
|
27314
|
+
minTag = `<xs:min${minInclusive ? "Inclusive" : "Exclusive"} value="${min}">`;
|
|
27315
|
+
}
|
|
27316
|
+
let maxTag = "";
|
|
27317
|
+
if (max !== void 0) {
|
|
27318
|
+
maxTag = `<xs:max${maxInclusive ? "Inclusive" : "Exclusive"} value="${max}">`;
|
|
27319
|
+
}
|
|
27320
|
+
parameterXML = `<xs:restriction base="xs:double">
|
|
27321
|
+
${minTag}
|
|
27322
|
+
${maxTag}
|
|
27323
|
+
</xs:restriction>`;
|
|
27324
|
+
}
|
|
27325
|
+
if (parameter.type === "length") {
|
|
27326
|
+
const { length, min, max } = parameter.parameter;
|
|
27327
|
+
let lengthTag = "";
|
|
27328
|
+
if (length !== void 0 && min === void 0 && max === void 0) {
|
|
27329
|
+
lengthTag = `<xs:length value="${length}" />`;
|
|
27330
|
+
}
|
|
27331
|
+
let minTag = "";
|
|
27332
|
+
if (min !== void 0 && length === void 0) {
|
|
27333
|
+
minTag = `<xs:minLength value="${min}" />`;
|
|
27334
|
+
}
|
|
27335
|
+
let maxTag = "";
|
|
27336
|
+
if (max !== void 0 && length === void 0) {
|
|
27337
|
+
maxTag = `<xs:maxLength value="${max}" />`;
|
|
27338
|
+
}
|
|
27339
|
+
parameterXML = `<xs:restriction base="xs:string">
|
|
27340
|
+
${lengthTag}
|
|
27341
|
+
${minTag}
|
|
27342
|
+
${maxTag}
|
|
27343
|
+
</xs:restriction>`;
|
|
27344
|
+
}
|
|
27345
|
+
const xml = `<${name[0].toLowerCase() + name.slice(1)}>
|
|
27312
27346
|
${parameterXML}
|
|
27313
|
-
|
|
27347
|
+
</${name[0].toLowerCase() + name.slice(1)}>`;
|
|
27314
27348
|
return xml;
|
|
27315
27349
|
};
|
|
27316
27350
|
class IDSAttribute extends IDSFacet {
|
|
@@ -27329,10 +27363,10 @@ class IDSAttribute extends IDSFacet {
|
|
|
27329
27363
|
attributes += `cardinality="${this.cardinality}"`;
|
|
27330
27364
|
attributes += this.instructions ? `instructions="${this.instructions}"` : "";
|
|
27331
27365
|
}
|
|
27332
|
-
return `<
|
|
27366
|
+
return `<attribute ${attributes}>
|
|
27333
27367
|
${nameXML}
|
|
27334
27368
|
${valueXML}
|
|
27335
|
-
</
|
|
27369
|
+
</attribute>`;
|
|
27336
27370
|
}
|
|
27337
27371
|
// This can be very ineficcient as we do not have an easy way to get an entity based on an attribute
|
|
27338
27372
|
// Right now, all entities must be iterated.
|
|
@@ -27468,10 +27502,10 @@ class IDSClassification extends IDSFacet {
|
|
|
27468
27502
|
attributes += this.uri ? `uri=${this.uri}` : "";
|
|
27469
27503
|
attributes += this.instructions ? `instructions="${this.instructions}"` : "";
|
|
27470
27504
|
}
|
|
27471
|
-
return `<
|
|
27505
|
+
return `<classification ${attributes}>
|
|
27472
27506
|
${systemXML}
|
|
27473
27507
|
${valueXML}
|
|
27474
|
-
</
|
|
27508
|
+
</classification>`;
|
|
27475
27509
|
}
|
|
27476
27510
|
async getEntities(model, collector = {}) {
|
|
27477
27511
|
var _a;
|
|
@@ -27749,10 +27783,10 @@ class IDSEntity extends IDSFacet {
|
|
|
27749
27783
|
attributes += `cardinality="${this.cardinality}"`;
|
|
27750
27784
|
attributes += this.instructions ? `instructions="${this.instructions}"` : "";
|
|
27751
27785
|
}
|
|
27752
|
-
return `<
|
|
27786
|
+
return `<entity ${attributes}>
|
|
27753
27787
|
${nameXML}
|
|
27754
27788
|
${predefinedTypeXML}
|
|
27755
|
-
</
|
|
27789
|
+
</entity>`;
|
|
27756
27790
|
}
|
|
27757
27791
|
// IFCSURFACESTYLEREFRACTION is not present in the FragmentsGroup
|
|
27758
27792
|
// IFCSURFACESTYLERENDERING is not present in the FragmentsGroup
|
|
@@ -27890,11 +27924,11 @@ class IDSProperty extends IDSFacet {
|
|
|
27890
27924
|
attributes += this.uri ? `uri=${this.uri}` : "";
|
|
27891
27925
|
attributes += this.instructions ? `instructions="${this.instructions}"` : "";
|
|
27892
27926
|
}
|
|
27893
|
-
return `<
|
|
27927
|
+
return `<property ${dataTypeXML} ${attributes}>
|
|
27894
27928
|
${propertySetXML}
|
|
27895
27929
|
${baseNameXML}
|
|
27896
27930
|
${valueXML}
|
|
27897
|
-
</
|
|
27931
|
+
</property>`;
|
|
27898
27932
|
}
|
|
27899
27933
|
async getEntities(model, collector = {}) {
|
|
27900
27934
|
var _a, _b;
|
|
@@ -27976,16 +28010,15 @@ class IDSProperty extends IDSFacet {
|
|
|
27976
28010
|
this.testResult.push(result2);
|
|
27977
28011
|
const sets = await this.getPsets(model, expressID);
|
|
27978
28012
|
const matchingSets = sets.filter((set) => {
|
|
27979
|
-
var _a2;
|
|
27980
28013
|
const result3 = this.evalRequirement(
|
|
27981
|
-
|
|
28014
|
+
set.Name ?? null,
|
|
27982
28015
|
this.propertySet,
|
|
27983
28016
|
"PropertySet"
|
|
27984
28017
|
);
|
|
27985
28018
|
if (!result3)
|
|
27986
28019
|
return false;
|
|
27987
28020
|
checks.push({
|
|
27988
|
-
currentValue: set.Name
|
|
28021
|
+
currentValue: set.Name,
|
|
27989
28022
|
parameter: "PropertySet",
|
|
27990
28023
|
pass: true,
|
|
27991
28024
|
requiredValue: this.propertySet.parameter
|
|
@@ -28002,8 +28035,7 @@ class IDSProperty extends IDSFacet {
|
|
|
28002
28035
|
continue;
|
|
28003
28036
|
}
|
|
28004
28037
|
for (const set of matchingSets) {
|
|
28005
|
-
|
|
28006
|
-
if (!itemsAttrName) {
|
|
28038
|
+
if (!("Properties" in set)) {
|
|
28007
28039
|
checks.push({
|
|
28008
28040
|
currentValue: null,
|
|
28009
28041
|
parameter: "BaseName",
|
|
@@ -28012,7 +28044,7 @@ class IDSProperty extends IDSFacet {
|
|
|
28012
28044
|
});
|
|
28013
28045
|
continue;
|
|
28014
28046
|
}
|
|
28015
|
-
const items = set
|
|
28047
|
+
const items = set.Properties;
|
|
28016
28048
|
const matchingItems = items.filter((item) => {
|
|
28017
28049
|
var _a2;
|
|
28018
28050
|
if (this._unsupportedTypes.includes(item.type)) {
|
|
@@ -28067,18 +28099,22 @@ class IDSProperty extends IDSFacet {
|
|
|
28067
28099
|
(name) => name.endsWith("Value") || name.endsWith("Values")
|
|
28068
28100
|
);
|
|
28069
28101
|
}
|
|
28070
|
-
async
|
|
28071
|
-
|
|
28102
|
+
async simplifyPset(model, attrs, propsListName) {
|
|
28103
|
+
var _a;
|
|
28072
28104
|
const props = [];
|
|
28073
|
-
const list =
|
|
28105
|
+
const list = attrs[propsListName];
|
|
28074
28106
|
if (!list)
|
|
28075
|
-
return
|
|
28107
|
+
return attrs;
|
|
28076
28108
|
for (const { value } of list) {
|
|
28077
28109
|
const propAttrs = await model.getProperties(value);
|
|
28078
28110
|
if (propAttrs)
|
|
28079
28111
|
props.push(propAttrs);
|
|
28080
28112
|
}
|
|
28081
|
-
attrsClone
|
|
28113
|
+
const attrsClone = {
|
|
28114
|
+
Name: (_a = attrs.Name) == null ? void 0 : _a.value,
|
|
28115
|
+
Properties: props,
|
|
28116
|
+
type: attrs.type
|
|
28117
|
+
};
|
|
28082
28118
|
return attrsClone;
|
|
28083
28119
|
}
|
|
28084
28120
|
async getTypePsets(model, expressID) {
|
|
@@ -28096,13 +28132,14 @@ class IDSProperty extends IDSFacet {
|
|
|
28096
28132
|
if (!(psetAttrs && "HasProperties" in psetAttrs && Array.isArray(psetAttrs.HasProperties))) {
|
|
28097
28133
|
continue;
|
|
28098
28134
|
}
|
|
28099
|
-
const pset = await this.
|
|
28135
|
+
const pset = await this.simplifyPset(model, psetAttrs, "HasProperties");
|
|
28100
28136
|
sets.push(pset);
|
|
28101
28137
|
}
|
|
28102
28138
|
return sets;
|
|
28103
28139
|
}
|
|
28104
28140
|
async getPsets(model, expressID) {
|
|
28105
|
-
|
|
28141
|
+
var _a;
|
|
28142
|
+
const typePsets = await this.getTypePsets(model, expressID);
|
|
28106
28143
|
const indexer = this.components.get(IfcRelationsIndexer);
|
|
28107
28144
|
const definitions = indexer.getEntityRelations(
|
|
28108
28145
|
model,
|
|
@@ -28110,7 +28147,8 @@ class IDSProperty extends IDSFacet {
|
|
|
28110
28147
|
"IsDefinedBy"
|
|
28111
28148
|
);
|
|
28112
28149
|
if (!definitions)
|
|
28113
|
-
return
|
|
28150
|
+
return typePsets;
|
|
28151
|
+
const sets = [];
|
|
28114
28152
|
for (const definitionID of definitions) {
|
|
28115
28153
|
const attrs = await model.getProperties(definitionID);
|
|
28116
28154
|
if (!attrs)
|
|
@@ -28118,8 +28156,25 @@ class IDSProperty extends IDSFacet {
|
|
|
28118
28156
|
const propsListName = this.getItemsAttrName(attrs.type);
|
|
28119
28157
|
if (!propsListName)
|
|
28120
28158
|
continue;
|
|
28121
|
-
const
|
|
28122
|
-
|
|
28159
|
+
const occurencePset = await this.simplifyPset(
|
|
28160
|
+
model,
|
|
28161
|
+
attrs,
|
|
28162
|
+
propsListName
|
|
28163
|
+
);
|
|
28164
|
+
const typePset = typePsets.find(
|
|
28165
|
+
({ Name }) => Name === occurencePset.Name
|
|
28166
|
+
);
|
|
28167
|
+
if (typePset) {
|
|
28168
|
+
for (const prop of typePset.Properties) {
|
|
28169
|
+
const name = (_a = prop.Name) == null ? void 0 : _a.value;
|
|
28170
|
+
const existingProp = occurencePset.Properties.find(
|
|
28171
|
+
({ Name }) => Name.value === name
|
|
28172
|
+
);
|
|
28173
|
+
if (!existingProp)
|
|
28174
|
+
occurencePset.Properties.push(prop);
|
|
28175
|
+
}
|
|
28176
|
+
}
|
|
28177
|
+
sets.push(occurencePset);
|
|
28123
28178
|
}
|
|
28124
28179
|
return sets;
|
|
28125
28180
|
}
|
|
@@ -28292,14 +28347,14 @@ class IDSSpecification {
|
|
|
28292
28347
|
const identifier = this.identifier ? `identifier="${this.identifier}"` : "";
|
|
28293
28348
|
const description = this.description ? `description="${this.description}"` : "";
|
|
28294
28349
|
const instructions = this.instructions ? `instructions="${this.instructions}"` : "";
|
|
28295
|
-
const xml = `<
|
|
28296
|
-
<
|
|
28297
|
-
${[...this.applicability].map((facet) => facet.serialize("applicability"))}
|
|
28298
|
-
</
|
|
28299
|
-
<
|
|
28300
|
-
${[...this.requirements].map((facet) => facet.serialize("requirement"))}
|
|
28301
|
-
</
|
|
28302
|
-
</
|
|
28350
|
+
const xml = `<specification ifcVersion="${[...this.ifcVersion].join(" ")}" ${name} ${identifier} ${description} ${instructions}>
|
|
28351
|
+
<applicability minOccurs="1" maxOccurs="unbounded">
|
|
28352
|
+
${[...this.applicability].map((facet) => facet.serialize("applicability")).join("\n")}
|
|
28353
|
+
</applicability>
|
|
28354
|
+
<requirements>
|
|
28355
|
+
${[...this.requirements].map((facet) => facet.serialize("requirement")).join("\n")}
|
|
28356
|
+
</requirements>
|
|
28357
|
+
</specification>`;
|
|
28303
28358
|
return xml;
|
|
28304
28359
|
}
|
|
28305
28360
|
}
|
|
@@ -28548,22 +28603,22 @@ const _IDSSpecifications = class _IDSSpecifications extends Component {
|
|
|
28548
28603
|
*/
|
|
28549
28604
|
export(info, specifications = this.list.values()) {
|
|
28550
28605
|
const _specifications = specifications ?? this.list;
|
|
28551
|
-
const xml = `<ids
|
|
28606
|
+
const xml = `<ids xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://standards.buildingsmart.org/IDS http://standards.buildingsmart.org/IDS/1.0/ids.xsd" xmlns:ids="http://standards.buildingsmart.org/IDS">
|
|
28552
28607
|
<!-- Made with That Open Engine ${Components.release} (https://github.com/thatopen/engine_components) -->
|
|
28553
|
-
<
|
|
28554
|
-
<
|
|
28555
|
-
${info.copyright ? `<
|
|
28556
|
-
${info.version ? `<
|
|
28557
|
-
${info.description ? `<
|
|
28558
|
-
${info.author ? `<
|
|
28559
|
-
${info.date ? `<
|
|
28560
|
-
${info.purpose ? `<
|
|
28561
|
-
${info.milestone ? `<
|
|
28562
|
-
</
|
|
28563
|
-
<
|
|
28608
|
+
<info>
|
|
28609
|
+
<title>${info.title}</title>
|
|
28610
|
+
${info.copyright ? `<copyright>${info.copyright}</copyright>` : ""}
|
|
28611
|
+
${info.version ? `<version>${info.version}</version>` : ""}
|
|
28612
|
+
${info.description ? `<description>${info.description}</description>` : ""}
|
|
28613
|
+
${info.author ? `<author>${info.author}</author>` : ""}
|
|
28614
|
+
${info.date ? `<date>${info.date.toISOString().split("T")[0]}</date>` : ""}
|
|
28615
|
+
${info.purpose ? `<purpose>${info.purpose}</purpose>` : ""}
|
|
28616
|
+
${info.milestone ? `<milestone>${info.milestone}</milestone>` : ""}
|
|
28617
|
+
</info>
|
|
28618
|
+
<specifications>
|
|
28564
28619
|
${[..._specifications].map((spec) => spec.serialize()).join("\n")}
|
|
28565
|
-
</
|
|
28566
|
-
</ids
|
|
28620
|
+
</specifications>
|
|
28621
|
+
</ids>`;
|
|
28567
28622
|
return xml;
|
|
28568
28623
|
}
|
|
28569
28624
|
};
|