@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
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ var __publicField = (obj, key, value) => {
|
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
7
|
import * as THREE$1 from "three";
|
|
8
|
-
import { BufferAttribute, Vector3, Plane, Line3,
|
|
8
|
+
import { BufferAttribute, Vector3, Vector2, Plane, Line3, Triangle, Sphere, Matrix4, Box3, BackSide, DoubleSide, FrontSide, Mesh, Ray, Raycaster, Quaternion, Object3D, Euler, MeshBasicMaterial, LineBasicMaterial, CylinderGeometry, BoxGeometry, BufferGeometry, Float32BufferAttribute, OctahedronGeometry, Line, SphereGeometry, TorusGeometry, PlaneGeometry } from "three";
|
|
9
9
|
import * as FRAGS from "@thatopen/fragments";
|
|
10
10
|
import { FragmentsGroup, Serializer } from "@thatopen/fragments";
|
|
11
11
|
import * as WEBIFC from "web-ifc";
|
|
@@ -6293,7 +6293,7 @@ const _Components = class _Components {
|
|
|
6293
6293
|
/**
|
|
6294
6294
|
* The version of the @thatopen/components library.
|
|
6295
6295
|
*/
|
|
6296
|
-
__publicField(_Components, "release", "2.4.
|
|
6296
|
+
__publicField(_Components, "release", "2.4.5");
|
|
6297
6297
|
let Components = _Components;
|
|
6298
6298
|
class SimpleWorld extends Base {
|
|
6299
6299
|
constructor() {
|
|
@@ -12756,6 +12756,9 @@ class ProjectionManager {
|
|
|
12756
12756
|
setupOrthoCamera(height, width) {
|
|
12757
12757
|
this._component.controls.mouseButtons.wheel = CameraControls.ACTION.ZOOM;
|
|
12758
12758
|
this._component.controls.mouseButtons.middle = CameraControls.ACTION.ZOOM;
|
|
12759
|
+
this._component.controls.touches.two = CameraControls.ACTION.TOUCH_ZOOM_TRUCK;
|
|
12760
|
+
this._component.controls.touches.three = CameraControls.ACTION.TOUCH_ZOOM_TRUCK;
|
|
12761
|
+
this._component.controls.dollySpeed = 5;
|
|
12759
12762
|
const pCamera = this._component.threePersp;
|
|
12760
12763
|
const oCamera = this._component.threeOrtho;
|
|
12761
12764
|
oCamera.zoom = 1;
|
|
@@ -12782,6 +12785,9 @@ class ProjectionManager {
|
|
|
12782
12785
|
pCamera.position.copy(oCamera.position);
|
|
12783
12786
|
pCamera.quaternion.copy(oCamera.quaternion);
|
|
12784
12787
|
this._component.controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY;
|
|
12788
|
+
this._component.controls.touches.two = CameraControls.ACTION.TOUCH_DOLLY_TRUCK;
|
|
12789
|
+
this._component.controls.touches.three = CameraControls.ACTION.TOUCH_DOLLY_TRUCK;
|
|
12790
|
+
this._component.controls.dollySpeed = 5;
|
|
12785
12791
|
if (this.matchOrthoDistanceEnabled) {
|
|
12786
12792
|
this._component.controls.distance = this.getDistance();
|
|
12787
12793
|
} else {
|
|
@@ -21087,10 +21093,8 @@ function readDocType$1(xmlData, i) {
|
|
|
21087
21093
|
i += 8;
|
|
21088
21094
|
else if (hasBody && isNotation(xmlData, i))
|
|
21089
21095
|
i += 9;
|
|
21090
|
-
else if (isComment)
|
|
21091
|
-
comment = true;
|
|
21092
21096
|
else
|
|
21093
|
-
|
|
21097
|
+
comment = true;
|
|
21094
21098
|
angleBracketsCount++;
|
|
21095
21099
|
exp = "";
|
|
21096
21100
|
} else if (xmlData[i] === ">") {
|
|
@@ -21134,11 +21138,6 @@ function readEntityExp(xmlData, i) {
|
|
|
21134
21138
|
}
|
|
21135
21139
|
return [entityName2, val2, i];
|
|
21136
21140
|
}
|
|
21137
|
-
function isComment(xmlData, i) {
|
|
21138
|
-
if (xmlData[i + 1] === "!" && xmlData[i + 2] === "-" && xmlData[i + 3] === "-")
|
|
21139
|
-
return true;
|
|
21140
|
-
return false;
|
|
21141
|
-
}
|
|
21142
21141
|
function isEntity(xmlData, i) {
|
|
21143
21142
|
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")
|
|
21144
21143
|
return true;
|
|
@@ -23179,7 +23178,7 @@ ${users}
|
|
|
23179
23178
|
Comment: CommentText,
|
|
23180
23179
|
Viewpoint: Viewpoint2
|
|
23181
23180
|
} = markupComment;
|
|
23182
|
-
if (!(Guid && CommentDate && Author &&
|
|
23181
|
+
if (!(Guid && CommentDate && Author && Comment))
|
|
23183
23182
|
return null;
|
|
23184
23183
|
const viewpoints = this.components.get(Viewpoints);
|
|
23185
23184
|
const comment = new Comment(this.components, CommentText ?? "");
|
|
@@ -26741,7 +26740,7 @@ class MiniMap {
|
|
|
26741
26740
|
this._camera.left = frustumSize * aspect / -2;
|
|
26742
26741
|
this._camera.right = frustumSize * aspect / 2;
|
|
26743
26742
|
this._camera.top = frustumSize / 2;
|
|
26744
|
-
this._camera.bottom = -
|
|
26743
|
+
this._camera.bottom = -1 / 2;
|
|
26745
26744
|
this._camera.updateProjectionMatrix();
|
|
26746
26745
|
this.onResize.trigger(size);
|
|
26747
26746
|
}
|
|
@@ -27229,10 +27228,10 @@ class IDSFacet {
|
|
|
27229
27228
|
let minPass = true;
|
|
27230
27229
|
let maxPass = true;
|
|
27231
27230
|
if (min !== void 0) {
|
|
27232
|
-
minPass = minInclusive ? value
|
|
27231
|
+
minPass = minInclusive ? value >= min : value > min;
|
|
27233
27232
|
}
|
|
27234
27233
|
if (max !== void 0) {
|
|
27235
|
-
maxPass = maxInclusive ? value
|
|
27234
|
+
maxPass = maxInclusive ? value <= max : value < max;
|
|
27236
27235
|
}
|
|
27237
27236
|
pass = minPass && maxPass;
|
|
27238
27237
|
}
|
|
@@ -27275,12 +27274,12 @@ const getParameterXML = (name, parameter) => {
|
|
|
27275
27274
|
if (!parameter)
|
|
27276
27275
|
return parameterXML;
|
|
27277
27276
|
if (parameter.type === "simple") {
|
|
27278
|
-
parameterXML = `<
|
|
27277
|
+
parameterXML = `<simpleValue>${parameter.parameter}</simpleValue>`;
|
|
27279
27278
|
}
|
|
27280
27279
|
if (parameter.type === "enumeration") {
|
|
27281
27280
|
const value = parameter.parameter;
|
|
27282
27281
|
parameterXML = `<xs:restriction base="xs:string">
|
|
27283
|
-
${value.map((v) => `<xs:enumeration value="${v}" />`).join("\
|
|
27282
|
+
${value.map((v) => `<xs:enumeration value="${v}" />`).join("\n")}
|
|
27284
27283
|
</xs:restriction>`;
|
|
27285
27284
|
}
|
|
27286
27285
|
if (parameter.type === "pattern") {
|
|
@@ -27289,9 +27288,44 @@ const getParameterXML = (name, parameter) => {
|
|
|
27289
27288
|
<xs:pattern value="${value}" />
|
|
27290
27289
|
</xs:restriction>`;
|
|
27291
27290
|
}
|
|
27292
|
-
|
|
27291
|
+
if (parameter.type === "bounds") {
|
|
27292
|
+
const { min, minInclusive, max, maxInclusive } = parameter.parameter;
|
|
27293
|
+
let minTag = "";
|
|
27294
|
+
if (min !== void 0) {
|
|
27295
|
+
minTag = `<xs:min${minInclusive ? "Inclusive" : "Exclusive"} value="${min}">`;
|
|
27296
|
+
}
|
|
27297
|
+
let maxTag = "";
|
|
27298
|
+
if (max !== void 0) {
|
|
27299
|
+
maxTag = `<xs:max${maxInclusive ? "Inclusive" : "Exclusive"} value="${max}">`;
|
|
27300
|
+
}
|
|
27301
|
+
parameterXML = `<xs:restriction base="xs:double">
|
|
27302
|
+
${minTag}
|
|
27303
|
+
${maxTag}
|
|
27304
|
+
</xs:restriction>`;
|
|
27305
|
+
}
|
|
27306
|
+
if (parameter.type === "length") {
|
|
27307
|
+
const { length, min, max } = parameter.parameter;
|
|
27308
|
+
let lengthTag = "";
|
|
27309
|
+
if (length !== void 0 && min === void 0 && max === void 0) {
|
|
27310
|
+
lengthTag = `<xs:length value="${length}" />`;
|
|
27311
|
+
}
|
|
27312
|
+
let minTag = "";
|
|
27313
|
+
if (min !== void 0 && length === void 0) {
|
|
27314
|
+
minTag = `<xs:minLength value="${min}" />`;
|
|
27315
|
+
}
|
|
27316
|
+
let maxTag = "";
|
|
27317
|
+
if (max !== void 0 && length === void 0) {
|
|
27318
|
+
maxTag = `<xs:maxLength value="${max}" />`;
|
|
27319
|
+
}
|
|
27320
|
+
parameterXML = `<xs:restriction base="xs:string">
|
|
27321
|
+
${lengthTag}
|
|
27322
|
+
${minTag}
|
|
27323
|
+
${maxTag}
|
|
27324
|
+
</xs:restriction>`;
|
|
27325
|
+
}
|
|
27326
|
+
const xml = `<${name[0].toLowerCase() + name.slice(1)}>
|
|
27293
27327
|
${parameterXML}
|
|
27294
|
-
|
|
27328
|
+
</${name[0].toLowerCase() + name.slice(1)}>`;
|
|
27295
27329
|
return xml;
|
|
27296
27330
|
};
|
|
27297
27331
|
class IDSAttribute extends IDSFacet {
|
|
@@ -27310,10 +27344,10 @@ class IDSAttribute extends IDSFacet {
|
|
|
27310
27344
|
attributes += `cardinality="${this.cardinality}"`;
|
|
27311
27345
|
attributes += this.instructions ? `instructions="${this.instructions}"` : "";
|
|
27312
27346
|
}
|
|
27313
|
-
return `<
|
|
27347
|
+
return `<attribute ${attributes}>
|
|
27314
27348
|
${nameXML}
|
|
27315
27349
|
${valueXML}
|
|
27316
|
-
</
|
|
27350
|
+
</attribute>`;
|
|
27317
27351
|
}
|
|
27318
27352
|
// This can be very ineficcient as we do not have an easy way to get an entity based on an attribute
|
|
27319
27353
|
// Right now, all entities must be iterated.
|
|
@@ -27449,10 +27483,10 @@ class IDSClassification extends IDSFacet {
|
|
|
27449
27483
|
attributes += this.uri ? `uri=${this.uri}` : "";
|
|
27450
27484
|
attributes += this.instructions ? `instructions="${this.instructions}"` : "";
|
|
27451
27485
|
}
|
|
27452
|
-
return `<
|
|
27486
|
+
return `<classification ${attributes}>
|
|
27453
27487
|
${systemXML}
|
|
27454
27488
|
${valueXML}
|
|
27455
|
-
</
|
|
27489
|
+
</classification>`;
|
|
27456
27490
|
}
|
|
27457
27491
|
async getEntities(model, collector = {}) {
|
|
27458
27492
|
var _a;
|
|
@@ -27730,10 +27764,10 @@ class IDSEntity extends IDSFacet {
|
|
|
27730
27764
|
attributes += `cardinality="${this.cardinality}"`;
|
|
27731
27765
|
attributes += this.instructions ? `instructions="${this.instructions}"` : "";
|
|
27732
27766
|
}
|
|
27733
|
-
return `<
|
|
27767
|
+
return `<entity ${attributes}>
|
|
27734
27768
|
${nameXML}
|
|
27735
27769
|
${predefinedTypeXML}
|
|
27736
|
-
</
|
|
27770
|
+
</entity>`;
|
|
27737
27771
|
}
|
|
27738
27772
|
// IFCSURFACESTYLEREFRACTION is not present in the FragmentsGroup
|
|
27739
27773
|
// IFCSURFACESTYLERENDERING is not present in the FragmentsGroup
|
|
@@ -27871,11 +27905,11 @@ class IDSProperty extends IDSFacet {
|
|
|
27871
27905
|
attributes += this.uri ? `uri=${this.uri}` : "";
|
|
27872
27906
|
attributes += this.instructions ? `instructions="${this.instructions}"` : "";
|
|
27873
27907
|
}
|
|
27874
|
-
return `<
|
|
27908
|
+
return `<property ${dataTypeXML} ${attributes}>
|
|
27875
27909
|
${propertySetXML}
|
|
27876
27910
|
${baseNameXML}
|
|
27877
27911
|
${valueXML}
|
|
27878
|
-
</
|
|
27912
|
+
</property>`;
|
|
27879
27913
|
}
|
|
27880
27914
|
async getEntities(model, collector = {}) {
|
|
27881
27915
|
var _a, _b;
|
|
@@ -27957,16 +27991,15 @@ class IDSProperty extends IDSFacet {
|
|
|
27957
27991
|
this.testResult.push(result2);
|
|
27958
27992
|
const sets = await this.getPsets(model, expressID);
|
|
27959
27993
|
const matchingSets = sets.filter((set) => {
|
|
27960
|
-
var _a2;
|
|
27961
27994
|
const result3 = this.evalRequirement(
|
|
27962
|
-
|
|
27995
|
+
set.Name ?? null,
|
|
27963
27996
|
this.propertySet,
|
|
27964
27997
|
"PropertySet"
|
|
27965
27998
|
);
|
|
27966
27999
|
if (!result3)
|
|
27967
28000
|
return false;
|
|
27968
28001
|
checks.push({
|
|
27969
|
-
currentValue: set.Name
|
|
28002
|
+
currentValue: set.Name,
|
|
27970
28003
|
parameter: "PropertySet",
|
|
27971
28004
|
pass: true,
|
|
27972
28005
|
requiredValue: this.propertySet.parameter
|
|
@@ -27983,8 +28016,7 @@ class IDSProperty extends IDSFacet {
|
|
|
27983
28016
|
continue;
|
|
27984
28017
|
}
|
|
27985
28018
|
for (const set of matchingSets) {
|
|
27986
|
-
|
|
27987
|
-
if (!itemsAttrName) {
|
|
28019
|
+
if (!("Properties" in set)) {
|
|
27988
28020
|
checks.push({
|
|
27989
28021
|
currentValue: null,
|
|
27990
28022
|
parameter: "BaseName",
|
|
@@ -27993,7 +28025,7 @@ class IDSProperty extends IDSFacet {
|
|
|
27993
28025
|
});
|
|
27994
28026
|
continue;
|
|
27995
28027
|
}
|
|
27996
|
-
const items = set
|
|
28028
|
+
const items = set.Properties;
|
|
27997
28029
|
const matchingItems = items.filter((item) => {
|
|
27998
28030
|
var _a2;
|
|
27999
28031
|
if (this._unsupportedTypes.includes(item.type)) {
|
|
@@ -28048,18 +28080,22 @@ class IDSProperty extends IDSFacet {
|
|
|
28048
28080
|
(name) => name.endsWith("Value") || name.endsWith("Values")
|
|
28049
28081
|
);
|
|
28050
28082
|
}
|
|
28051
|
-
async
|
|
28052
|
-
|
|
28083
|
+
async simplifyPset(model, attrs, propsListName) {
|
|
28084
|
+
var _a;
|
|
28053
28085
|
const props = [];
|
|
28054
|
-
const list =
|
|
28086
|
+
const list = attrs[propsListName];
|
|
28055
28087
|
if (!list)
|
|
28056
|
-
return
|
|
28088
|
+
return attrs;
|
|
28057
28089
|
for (const { value } of list) {
|
|
28058
28090
|
const propAttrs = await model.getProperties(value);
|
|
28059
28091
|
if (propAttrs)
|
|
28060
28092
|
props.push(propAttrs);
|
|
28061
28093
|
}
|
|
28062
|
-
attrsClone
|
|
28094
|
+
const attrsClone = {
|
|
28095
|
+
Name: (_a = attrs.Name) == null ? void 0 : _a.value,
|
|
28096
|
+
Properties: props,
|
|
28097
|
+
type: attrs.type
|
|
28098
|
+
};
|
|
28063
28099
|
return attrsClone;
|
|
28064
28100
|
}
|
|
28065
28101
|
async getTypePsets(model, expressID) {
|
|
@@ -28077,13 +28113,14 @@ class IDSProperty extends IDSFacet {
|
|
|
28077
28113
|
if (!(psetAttrs && "HasProperties" in psetAttrs && Array.isArray(psetAttrs.HasProperties))) {
|
|
28078
28114
|
continue;
|
|
28079
28115
|
}
|
|
28080
|
-
const pset = await this.
|
|
28116
|
+
const pset = await this.simplifyPset(model, psetAttrs, "HasProperties");
|
|
28081
28117
|
sets.push(pset);
|
|
28082
28118
|
}
|
|
28083
28119
|
return sets;
|
|
28084
28120
|
}
|
|
28085
28121
|
async getPsets(model, expressID) {
|
|
28086
|
-
|
|
28122
|
+
var _a;
|
|
28123
|
+
const typePsets = await this.getTypePsets(model, expressID);
|
|
28087
28124
|
const indexer = this.components.get(IfcRelationsIndexer);
|
|
28088
28125
|
const definitions = indexer.getEntityRelations(
|
|
28089
28126
|
model,
|
|
@@ -28091,7 +28128,8 @@ class IDSProperty extends IDSFacet {
|
|
|
28091
28128
|
"IsDefinedBy"
|
|
28092
28129
|
);
|
|
28093
28130
|
if (!definitions)
|
|
28094
|
-
return
|
|
28131
|
+
return typePsets;
|
|
28132
|
+
const sets = [];
|
|
28095
28133
|
for (const definitionID of definitions) {
|
|
28096
28134
|
const attrs = await model.getProperties(definitionID);
|
|
28097
28135
|
if (!attrs)
|
|
@@ -28099,8 +28137,25 @@ class IDSProperty extends IDSFacet {
|
|
|
28099
28137
|
const propsListName = this.getItemsAttrName(attrs.type);
|
|
28100
28138
|
if (!propsListName)
|
|
28101
28139
|
continue;
|
|
28102
|
-
const
|
|
28103
|
-
|
|
28140
|
+
const occurencePset = await this.simplifyPset(
|
|
28141
|
+
model,
|
|
28142
|
+
attrs,
|
|
28143
|
+
propsListName
|
|
28144
|
+
);
|
|
28145
|
+
const typePset = typePsets.find(
|
|
28146
|
+
({ Name }) => Name === occurencePset.Name
|
|
28147
|
+
);
|
|
28148
|
+
if (typePset) {
|
|
28149
|
+
for (const prop of typePset.Properties) {
|
|
28150
|
+
const name = (_a = prop.Name) == null ? void 0 : _a.value;
|
|
28151
|
+
const existingProp = occurencePset.Properties.find(
|
|
28152
|
+
({ Name }) => Name.value === name
|
|
28153
|
+
);
|
|
28154
|
+
if (!existingProp)
|
|
28155
|
+
occurencePset.Properties.push(prop);
|
|
28156
|
+
}
|
|
28157
|
+
}
|
|
28158
|
+
sets.push(occurencePset);
|
|
28104
28159
|
}
|
|
28105
28160
|
return sets;
|
|
28106
28161
|
}
|
|
@@ -28273,14 +28328,14 @@ class IDSSpecification {
|
|
|
28273
28328
|
const identifier = this.identifier ? `identifier="${this.identifier}"` : "";
|
|
28274
28329
|
const description = this.description ? `description="${this.description}"` : "";
|
|
28275
28330
|
const instructions = this.instructions ? `instructions="${this.instructions}"` : "";
|
|
28276
|
-
const xml = `<
|
|
28277
|
-
<
|
|
28278
|
-
${[...this.applicability].map((facet) => facet.serialize("applicability"))}
|
|
28279
|
-
</
|
|
28280
|
-
<
|
|
28281
|
-
${[...this.requirements].map((facet) => facet.serialize("requirement"))}
|
|
28282
|
-
</
|
|
28283
|
-
</
|
|
28331
|
+
const xml = `<specification ifcVersion="${[...this.ifcVersion].join(" ")}" ${name} ${identifier} ${description} ${instructions}>
|
|
28332
|
+
<applicability minOccurs="1" maxOccurs="unbounded">
|
|
28333
|
+
${[...this.applicability].map((facet) => facet.serialize("applicability")).join("\n")}
|
|
28334
|
+
</applicability>
|
|
28335
|
+
<requirements>
|
|
28336
|
+
${[...this.requirements].map((facet) => facet.serialize("requirement")).join("\n")}
|
|
28337
|
+
</requirements>
|
|
28338
|
+
</specification>`;
|
|
28284
28339
|
return xml;
|
|
28285
28340
|
}
|
|
28286
28341
|
}
|
|
@@ -28529,22 +28584,22 @@ const _IDSSpecifications = class _IDSSpecifications extends Component {
|
|
|
28529
28584
|
*/
|
|
28530
28585
|
export(info, specifications = this.list.values()) {
|
|
28531
28586
|
const _specifications = specifications ?? this.list;
|
|
28532
|
-
const xml = `<ids
|
|
28587
|
+
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">
|
|
28533
28588
|
<!-- Made with That Open Engine ${Components.release} (https://github.com/thatopen/engine_components) -->
|
|
28534
|
-
<
|
|
28535
|
-
<
|
|
28536
|
-
${info.copyright ? `<
|
|
28537
|
-
${info.version ? `<
|
|
28538
|
-
${info.description ? `<
|
|
28539
|
-
${info.author ? `<
|
|
28540
|
-
${info.date ? `<
|
|
28541
|
-
${info.purpose ? `<
|
|
28542
|
-
${info.milestone ? `<
|
|
28543
|
-
</
|
|
28544
|
-
<
|
|
28589
|
+
<info>
|
|
28590
|
+
<title>${info.title}</title>
|
|
28591
|
+
${info.copyright ? `<copyright>${info.copyright}</copyright>` : ""}
|
|
28592
|
+
${info.version ? `<version>${info.version}</version>` : ""}
|
|
28593
|
+
${info.description ? `<description>${info.description}</description>` : ""}
|
|
28594
|
+
${info.author ? `<author>${info.author}</author>` : ""}
|
|
28595
|
+
${info.date ? `<date>${info.date.toISOString().split("T")[0]}</date>` : ""}
|
|
28596
|
+
${info.purpose ? `<purpose>${info.purpose}</purpose>` : ""}
|
|
28597
|
+
${info.milestone ? `<milestone>${info.milestone}</milestone>` : ""}
|
|
28598
|
+
</info>
|
|
28599
|
+
<specifications>
|
|
28545
28600
|
${[..._specifications].map((spec) => spec.serialize()).join("\n")}
|
|
28546
|
-
</
|
|
28547
|
-
</ids
|
|
28601
|
+
</specifications>
|
|
28602
|
+
</ids>`;
|
|
28548
28603
|
return xml;
|
|
28549
28604
|
}
|
|
28550
28605
|
};
|