@cornerstonejs/adapters 3.0.0-beta.5 → 3.0.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/esm/adapters/Cornerstone/Angle.js +61 -74
- package/dist/esm/adapters/Cornerstone/ArrowAnnotate.js +76 -76
- package/dist/esm/adapters/Cornerstone/Bidirectional.js +130 -146
- package/dist/esm/adapters/Cornerstone/CircleRoi.js +96 -95
- package/dist/esm/adapters/Cornerstone/CobbAngle.js +66 -79
- package/dist/esm/adapters/Cornerstone/EllipticalRoi.js +152 -152
- package/dist/esm/adapters/Cornerstone/FreehandRoi.js +68 -69
- package/dist/esm/adapters/Cornerstone/Length.js +54 -63
- package/dist/esm/adapters/Cornerstone/MeasurementReport.js +221 -224
- package/dist/esm/adapters/Cornerstone/ParametricMap.js +88 -110
- package/dist/esm/adapters/Cornerstone/RectangleRoi.js +72 -78
- package/dist/esm/adapters/Cornerstone/Segmentation.js +7 -7
- package/dist/esm/adapters/Cornerstone/Segmentation_3X.js +113 -104
- package/dist/esm/adapters/Cornerstone/Segmentation_4X.js +434 -462
- package/dist/esm/adapters/Cornerstone/index.js +14 -14
- package/dist/esm/adapters/Cornerstone3D/Angle.d.ts +2 -5
- package/dist/esm/adapters/Cornerstone3D/Angle.js +89 -100
- package/dist/esm/adapters/Cornerstone3D/ArrowAnnotate.d.ts +5 -11
- package/dist/esm/adapters/Cornerstone3D/ArrowAnnotate.js +106 -118
- package/dist/esm/adapters/Cornerstone3D/BaseAdapter3D.d.ts +43 -0
- package/dist/esm/adapters/Cornerstone3D/BaseAdapter3D.js +92 -0
- package/dist/esm/adapters/Cornerstone3D/Bidirectional.d.ts +2 -5
- package/dist/esm/adapters/Cornerstone3D/Bidirectional.js +118 -133
- package/dist/esm/adapters/Cornerstone3D/CircleROI.d.ts +2 -7
- package/dist/esm/adapters/Cornerstone3D/CircleROI.js +85 -85
- package/dist/esm/adapters/Cornerstone3D/CobbAngle.d.ts +2 -5
- package/dist/esm/adapters/Cornerstone3D/CobbAngle.js +93 -104
- package/dist/esm/adapters/Cornerstone3D/CodingScheme.js +5 -5
- package/dist/esm/adapters/Cornerstone3D/EllipticalROI.d.ts +2 -7
- package/dist/esm/adapters/Cornerstone3D/EllipticalROI.js +148 -149
- package/dist/esm/adapters/Cornerstone3D/KeyImage.d.ts +24 -0
- package/dist/esm/adapters/Cornerstone3D/KeyImage.js +49 -0
- package/dist/esm/adapters/Cornerstone3D/Length.d.ts +2 -10
- package/dist/esm/adapters/Cornerstone3D/Length.js +83 -93
- package/dist/esm/adapters/Cornerstone3D/MeasurementReport.d.ts +23 -4
- package/dist/esm/adapters/Cornerstone3D/MeasurementReport.js +259 -240
- package/dist/esm/adapters/Cornerstone3D/ParametricMap/generateToolState.js +8 -4
- package/dist/esm/adapters/Cornerstone3D/PlanarFreehandROI.d.ts +3 -5
- package/dist/esm/adapters/Cornerstone3D/PlanarFreehandROI.js +99 -115
- package/dist/esm/adapters/Cornerstone3D/Probe.d.ts +4 -17
- package/dist/esm/adapters/Cornerstone3D/Probe.js +38 -79
- package/dist/esm/adapters/Cornerstone3D/RTStruct/RTSS.js +60 -52
- package/dist/esm/adapters/Cornerstone3D/RTStruct/index.js +3 -1
- package/dist/esm/adapters/Cornerstone3D/RTStruct/utilities/getPatientModule.js +5 -5
- package/dist/esm/adapters/Cornerstone3D/RTStruct/utilities/getReferencedFrameOfReferenceSequence.js +14 -10
- package/dist/esm/adapters/Cornerstone3D/RTStruct/utilities/getReferencedSeriesSequence.js +17 -11
- package/dist/esm/adapters/Cornerstone3D/RTStruct/utilities/getStructureSetModule.js +3 -1
- package/dist/esm/adapters/Cornerstone3D/RectangleROI.d.ts +2 -5
- package/dist/esm/adapters/Cornerstone3D/RectangleROI.js +73 -85
- package/dist/esm/adapters/Cornerstone3D/Segmentation/generateLabelMaps2DFrom3D.js +15 -13
- package/dist/esm/adapters/Cornerstone3D/Segmentation/generateSegmentation.js +15 -9
- package/dist/esm/adapters/Cornerstone3D/Segmentation/generateToolState.js +10 -10
- package/dist/esm/adapters/Cornerstone3D/Segmentation/labelmapImagesFromBuffer.js +134 -151
- package/dist/esm/adapters/Cornerstone3D/UltrasoundDirectional.d.ts +2 -5
- package/dist/esm/adapters/Cornerstone3D/UltrasoundDirectional.js +68 -84
- package/dist/esm/adapters/Cornerstone3D/index.d.ts +4 -0
- package/dist/esm/adapters/Cornerstone3D/index.js +21 -17
- package/dist/esm/adapters/VTKjs/Segmentation.js +66 -72
- package/dist/esm/adapters/VTKjs/index.js +2 -2
- package/dist/esm/adapters/helpers/checkIfPerpendicular.js +2 -2
- package/dist/esm/adapters/helpers/checkOrientation.js +8 -8
- package/dist/esm/adapters/helpers/codeMeaningEquals.js +2 -2
- package/dist/esm/adapters/helpers/compareArrays.js +4 -2
- package/dist/esm/adapters/helpers/downloadDICOMData.js +6 -4
- package/dist/esm/adapters/helpers/getDatasetsFromImages.js +20 -18
- package/dist/esm/adapters/helpers/graphicTypeEquals.js +2 -2
- package/dist/esm/adapters/helpers/toArray.js +1 -3
- package/dist/esm/adapters/index.d.ts +2 -0
- package/dist/esm/adapters/index.js +6 -5
- package/dist/esm/node_modules/@babel/runtime/helpers/esm/defineProperty.js +18 -0
- package/dist/esm/node_modules/@babel/runtime/helpers/esm/toPrimitive.js +14 -0
- package/dist/esm/node_modules/@babel/runtime/helpers/esm/toPropertyKey.js +9 -0
- package/dist/esm/node_modules/@babel/runtime/helpers/esm/typeof.js +11 -0
- package/package.json +4 -4
- package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +0 -493
- package/dist/esm/adapters/Cornerstone3D/isValidCornerstoneTrackingIdentifier.js +0 -18
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
function getReferencedSeriesSequence(metadata, _index, metadataProvider, DicomMetadataStore) {
|
|
2
2
|
// grab imageId from toolData
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const {
|
|
4
|
+
referencedImageId: imageId
|
|
5
|
+
} = metadata;
|
|
6
|
+
const instance = metadataProvider.get("instance", imageId);
|
|
7
|
+
const {
|
|
8
|
+
SeriesInstanceUID,
|
|
9
|
+
StudyInstanceUID
|
|
10
|
+
} = instance;
|
|
11
|
+
const ReferencedSeriesSequence = [];
|
|
8
12
|
if (SeriesInstanceUID) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
SeriesInstanceUID
|
|
13
|
+
const series = DicomMetadataStore.getSeries(StudyInstanceUID, SeriesInstanceUID);
|
|
14
|
+
const ReferencedSeries = {
|
|
15
|
+
SeriesInstanceUID,
|
|
12
16
|
ReferencedInstanceSequence: []
|
|
13
17
|
};
|
|
14
|
-
series.instances.forEach(
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
series.instances.forEach(instance => {
|
|
19
|
+
const {
|
|
20
|
+
SOPInstanceUID,
|
|
21
|
+
SOPClassUID
|
|
22
|
+
} = instance;
|
|
17
23
|
ReferencedSeries.ReferencedInstanceSequence.push({
|
|
18
24
|
ReferencedSOPClassUID: SOPClassUID,
|
|
19
25
|
ReferencedSOPInstanceUID: SOPInstanceUID
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
static utilityToolType: string;
|
|
4
|
-
static TID300Representation: any;
|
|
5
|
-
static isValidCornerstoneTrackingIdentifier: (TrackingIdentifier: any) => boolean;
|
|
1
|
+
import BaseAdapter3D from "./BaseAdapter3D";
|
|
2
|
+
declare class RectangleROI extends BaseAdapter3D {
|
|
6
3
|
static getMeasurementData(MeasurementGroup: any, sopInstanceUIDToImageIdMap: any, imageToWorldCoords: any, metadata: any): {
|
|
7
4
|
description: any;
|
|
8
5
|
sopInstanceUid: any;
|
|
@@ -1,94 +1,82 @@
|
|
|
1
|
-
import { slicedToArray as _slicedToArray, createClass as _createClass, defineProperty as _defineProperty, classCallCheck as _classCallCheck } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
2
1
|
import { utilities } from 'dcmjs';
|
|
3
|
-
import CORNERSTONE_3D_TAG from './cornerstone3DTag.js';
|
|
4
2
|
import MeasurementReport from './MeasurementReport.js';
|
|
3
|
+
import BaseAdapter3D from './BaseAdapter3D.js';
|
|
5
4
|
|
|
6
5
|
var _RectangleROI;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
var point = imageToWorldCoords(referencedImageId, [GraphicData[i], GraphicData[i + 1]]);
|
|
27
|
-
worldCoords.push(point);
|
|
28
|
-
}
|
|
29
|
-
var state = defaultState;
|
|
30
|
-
state.annotation.data = {
|
|
31
|
-
handles: {
|
|
32
|
-
points: [worldCoords[0], worldCoords[1], worldCoords[3], worldCoords[2]],
|
|
33
|
-
activeHandleIndex: 0,
|
|
34
|
-
textBox: {
|
|
35
|
-
hasMoved: false
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
cachedStats: _defineProperty({}, "imageId:".concat(referencedImageId), {
|
|
39
|
-
area: NUMGroup ? NUMGroup.MeasuredValueSequence.NumericValue : null
|
|
40
|
-
}),
|
|
41
|
-
frameNumber: ReferencedFrameNumber
|
|
42
|
-
};
|
|
43
|
-
return state;
|
|
6
|
+
const {
|
|
7
|
+
Polyline: TID300Polyline
|
|
8
|
+
} = utilities.TID300;
|
|
9
|
+
class RectangleROI extends BaseAdapter3D {
|
|
10
|
+
static getMeasurementData(MeasurementGroup, sopInstanceUIDToImageIdMap, imageToWorldCoords, metadata) {
|
|
11
|
+
const {
|
|
12
|
+
defaultState,
|
|
13
|
+
NUMGroup,
|
|
14
|
+
SCOORDGroup,
|
|
15
|
+
ReferencedFrameNumber
|
|
16
|
+
} = MeasurementReport.getSetupMeasurementData(MeasurementGroup, sopInstanceUIDToImageIdMap, metadata, RectangleROI.toolType);
|
|
17
|
+
const referencedImageId = defaultState.annotation.metadata.referencedImageId;
|
|
18
|
+
const {
|
|
19
|
+
GraphicData
|
|
20
|
+
} = SCOORDGroup;
|
|
21
|
+
const worldCoords = [];
|
|
22
|
+
for (let i = 0; i < GraphicData.length; i += 2) {
|
|
23
|
+
const point = imageToWorldCoords(referencedImageId, [GraphicData[i], GraphicData[i + 1]]);
|
|
24
|
+
worldCoords.push(point);
|
|
44
25
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
var area = cachedStats.area,
|
|
63
|
-
perimeter = cachedStats.perimeter;
|
|
64
|
-
return {
|
|
65
|
-
points: [corners[0], corners[1], corners[3], corners[2], corners[0]],
|
|
66
|
-
area: area,
|
|
67
|
-
perimeter: perimeter,
|
|
68
|
-
trackingIdentifierTextValue: trackingIdentifierTextValue,
|
|
69
|
-
finding: finding,
|
|
70
|
-
findingSites: findingSites || []
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
}]);
|
|
74
|
-
}();
|
|
75
|
-
_RectangleROI = RectangleROI;
|
|
76
|
-
_RectangleROI.toolType = TOOLTYPE;
|
|
77
|
-
_RectangleROI.utilityToolType = TOOLTYPE;
|
|
78
|
-
_RectangleROI.TID300Representation = TID300Polyline;
|
|
79
|
-
_RectangleROI.isValidCornerstoneTrackingIdentifier = function (TrackingIdentifier) {
|
|
80
|
-
if (!TrackingIdentifier.includes(":")) {
|
|
81
|
-
return false;
|
|
26
|
+
const state = defaultState;
|
|
27
|
+
state.annotation.data = {
|
|
28
|
+
handles: {
|
|
29
|
+
points: [worldCoords[0], worldCoords[1], worldCoords[3], worldCoords[2]],
|
|
30
|
+
activeHandleIndex: 0,
|
|
31
|
+
textBox: {
|
|
32
|
+
hasMoved: false
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
cachedStats: {
|
|
36
|
+
["imageId:".concat(referencedImageId)]: {
|
|
37
|
+
area: NUMGroup ? NUMGroup.MeasuredValueSequence.NumericValue : null
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
frameNumber: ReferencedFrameNumber
|
|
41
|
+
};
|
|
42
|
+
return state;
|
|
82
43
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
44
|
+
static getTID300RepresentationArguments(tool, worldToImageCoords) {
|
|
45
|
+
const {
|
|
46
|
+
data,
|
|
47
|
+
finding,
|
|
48
|
+
findingSites,
|
|
49
|
+
metadata
|
|
50
|
+
} = tool;
|
|
51
|
+
const {
|
|
52
|
+
cachedStats = {},
|
|
53
|
+
handles
|
|
54
|
+
} = data;
|
|
55
|
+
const {
|
|
56
|
+
referencedImageId
|
|
57
|
+
} = metadata;
|
|
58
|
+
if (!referencedImageId) {
|
|
59
|
+
throw new Error("CobbAngle.getTID300RepresentationArguments: referencedImageId is not defined");
|
|
60
|
+
}
|
|
61
|
+
const corners = handles.points.map(point => worldToImageCoords(referencedImageId, point));
|
|
62
|
+
const {
|
|
63
|
+
area,
|
|
64
|
+
perimeter
|
|
65
|
+
} = cachedStats;
|
|
66
|
+
return {
|
|
67
|
+
points: [corners[0], corners[1], corners[3], corners[2], corners[0]],
|
|
68
|
+
area,
|
|
69
|
+
perimeter,
|
|
70
|
+
trackingIdentifierTextValue: this.trackingIdentifierTextValue,
|
|
71
|
+
finding,
|
|
72
|
+
findingSites: findingSites || []
|
|
73
|
+
};
|
|
89
74
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
75
|
+
}
|
|
76
|
+
_RectangleROI = RectangleROI;
|
|
77
|
+
(() => {
|
|
78
|
+
_RectangleROI.init("RectangleROI", TID300Polyline);
|
|
79
|
+
_RectangleROI.registerLegacy();
|
|
80
|
+
})();
|
|
93
81
|
|
|
94
82
|
export { RectangleROI as default };
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
function generateLabelMaps2DFrom3D(labelmap3D) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
const {
|
|
3
|
+
scalarData,
|
|
4
|
+
dimensions
|
|
5
|
+
} = labelmap3D;
|
|
6
|
+
const labelmaps2D = [];
|
|
7
|
+
const segmentsOnLabelmap3D = new Set();
|
|
8
|
+
for (let z = 0; z < dimensions[2]; z++) {
|
|
9
|
+
const pixelData = scalarData.slice(z * dimensions[0] * dimensions[1], (z + 1) * dimensions[0] * dimensions[1]);
|
|
10
|
+
const segmentsOnLabelmap = [];
|
|
11
|
+
for (let i = 0; i < pixelData.length; i++) {
|
|
12
|
+
const segment = pixelData[i];
|
|
11
13
|
if (!segmentsOnLabelmap.includes(segment) && segment !== 0) {
|
|
12
14
|
segmentsOnLabelmap.push(segment);
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
|
-
|
|
16
|
-
segmentsOnLabelmap
|
|
17
|
-
pixelData
|
|
17
|
+
const labelmap2D = {
|
|
18
|
+
segmentsOnLabelmap,
|
|
19
|
+
pixelData,
|
|
18
20
|
rows: dimensions[1],
|
|
19
21
|
columns: dimensions[0]
|
|
20
22
|
};
|
|
21
23
|
if (segmentsOnLabelmap.length === 0) {
|
|
22
24
|
continue;
|
|
23
25
|
}
|
|
24
|
-
segmentsOnLabelmap.forEach(
|
|
26
|
+
segmentsOnLabelmap.forEach(segmentIndex => {
|
|
25
27
|
segmentsOnLabelmap3D.add(segmentIndex);
|
|
26
28
|
});
|
|
27
29
|
labelmaps2D[dimensions[2] - 1 - z] = labelmap2D;
|
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import _defineProperty from '../../../node_modules/@babel/runtime/helpers/esm/defineProperty.js';
|
|
2
2
|
import { normalizers, derivations } from 'dcmjs';
|
|
3
3
|
import { fillSegmentation } from '../../Cornerstone/Segmentation_4X.js';
|
|
4
4
|
|
|
5
|
-
var
|
|
6
|
-
var
|
|
5
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
6
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
7
|
+
const {
|
|
8
|
+
Normalizer
|
|
9
|
+
} = normalizers;
|
|
10
|
+
const {
|
|
11
|
+
Segmentation: SegmentationDerivation
|
|
12
|
+
} = derivations;
|
|
7
13
|
function generateSegmentation(images, labelmaps, metadata) {
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
15
|
+
const segmentation = _createMultiframeSegmentationFromReferencedImages(images, metadata, options);
|
|
10
16
|
return fillSegmentation(segmentation, labelmaps, options);
|
|
11
17
|
}
|
|
12
18
|
function _createMultiframeSegmentationFromReferencedImages(images, metadata, options) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return
|
|
19
|
+
const datasets = images.map(image => {
|
|
20
|
+
const instance = metadata.get("instance", image.imageId);
|
|
21
|
+
return _objectSpread(_objectSpread(_objectSpread({}, image), instance), {}, {
|
|
16
22
|
SOPClassUID: instance.SopClassUID || instance.SOPClassUID,
|
|
17
23
|
SOPInstanceUID: instance.SopInstanceUID || instance.SOPInstanceUID,
|
|
18
24
|
PixelData: image.voxelManager.getScalarData(),
|
|
@@ -22,7 +28,7 @@ function _createMultiframeSegmentationFromReferencedImages(images, metadata, opt
|
|
|
22
28
|
_meta: {}
|
|
23
29
|
});
|
|
24
30
|
});
|
|
25
|
-
|
|
31
|
+
const multiframe = Normalizer.normalizeToDataset(datasets);
|
|
26
32
|
if (!multiframe) {
|
|
27
33
|
throw new Error("Failed to normalize the multiframe dataset, the data is not multi-frame.");
|
|
28
34
|
}
|
|
@@ -2,20 +2,20 @@ import { generateToolState as generateToolState$1 } from '../../Cornerstone/Segm
|
|
|
2
2
|
import { createLabelmapsFromBufferInternal } from './labelmapImagesFromBuffer.js';
|
|
3
3
|
|
|
4
4
|
function generateToolState(imageIds, arrayBuffer, metadataProvider) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
let skipOverlapping = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
6
|
+
let tolerance = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1e-3;
|
|
7
|
+
let cs3dVersion = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 4;
|
|
8
8
|
return generateToolState$1(imageIds, arrayBuffer, metadataProvider, skipOverlapping, tolerance, cs3dVersion);
|
|
9
9
|
}
|
|
10
10
|
function createFromDICOMSegBuffer(referencedImageIds, arrayBuffer, _ref) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
skipOverlapping =
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
let {
|
|
12
|
+
metadataProvider,
|
|
13
|
+
skipOverlapping = false,
|
|
14
|
+
tolerance = 1e-3
|
|
15
|
+
} = _ref;
|
|
16
16
|
return createLabelmapsFromBufferInternal(referencedImageIds, arrayBuffer, metadataProvider, {
|
|
17
|
-
skipOverlapping
|
|
18
|
-
tolerance
|
|
17
|
+
skipOverlapping,
|
|
18
|
+
tolerance
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
|