@ohif/app 3.7.0-beta.6 → 3.7.0-beta.61

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.
Files changed (54) hide show
  1. package/dist/{917.bundle.57d86a03581296d2ee00.js → 12.bundle.c54b94d07d31f5716eb1.js} +6 -6
  2. package/dist/{295.bundle.957b1159fec14b9199a1.js → 125.bundle.1ae4c6313c31cd0502cc.js} +4 -4
  3. package/dist/{208.bundle.182a2d2827d0d8afce33.js → 128.bundle.b0a7aa9ae66c094c40b5.js} +6 -6
  4. package/dist/{616.bundle.0ab47fe1619e9ed278b9.js → 150.bundle.2075342642eddc35e924.js} +155 -104
  5. package/dist/{351.bundle.0742237651aef9694a65.js → 181.bundle.70c4340cbe29bd14aafc.js} +147 -129
  6. package/dist/{351.css → 181.css} +1 -1
  7. package/dist/{606.bundle.5d876f5f3dd8287f0a28.js → 202.bundle.8007de18c96daaceca7f.js} +1459 -811
  8. package/dist/{926.bundle.dbc9d0e591cb9217fda2.js → 220.bundle.f7e1c96c94245e70f2be.js} +990 -400
  9. package/dist/{664.bundle.09abae984223969d1bde.js → 23.bundle.e008ad788170f2ed5569.js} +5 -6
  10. package/dist/{976.bundle.d6c34ae072976b3d9c29.js → 236.bundle.4aab776fb3e7654f1f0c.js} +38 -24
  11. package/dist/{55.bundle.550a823e75eb608e8d5e.js → 250.bundle.0d497b1b8afb0cbbe02f.js} +37 -25
  12. package/dist/{973.bundle.1a5e3125446a7f22729b.js → 281.bundle.c35a76364b385ee72bfd.js} +18 -14
  13. package/dist/{744.bundle.2282095988d3a22f17eb.js → 30.bundle.777231cf20f408db12f1.js} +128 -119
  14. package/dist/{192.bundle.2ae9e68532962089836f.js → 348.bundle.be4810ac9ac67cd7d8c5.js} +18 -14
  15. package/dist/{404.bundle.dd49183aaac4294b003b.js → 359.bundle.d723d31672f7fe575501.js} +13 -19
  16. package/dist/{82.bundle.0b649d84e313e1928590.js → 362.bundle.9d4d81eab3e31314a932.js} +1274 -287
  17. package/dist/{50.bundle.5eb258e72b39b2216560.js → 378.bundle.3069476ee6501c83ad44.js} +10 -8
  18. package/dist/{790.bundle.d362d0c4b8cdf0493e73.js → 410.bundle.f973e56e3a7d8165d87a.js} +6 -6
  19. package/dist/{151.bundle.31ea35044218837bf73f.js → 417.bundle.720dc8f3a6e99f378aa9.js} +9 -11
  20. package/dist/{569.bundle.21f8ad57c06a210448b5.js → 451.bundle.a573877dff7b43382138.js} +63 -81
  21. package/dist/{581.bundle.dc6197189f7c88c27d4c.js → 471.bundle.e9ecd59867a19e323234.js} +44 -72
  22. package/dist/{199.bundle.ec85a189dbc481dc980b.js → 506.bundle.8b2c661a99271b31663c.js} +79 -10
  23. package/dist/{935.bundle.deeffff0e4f7b528e3c3.js → 604.bundle.a51f83e64004bca5f497.js} +2 -3
  24. package/dist/{984.bundle.e48272bf3c41816e19fc.js → 663.bundle.80190c7364dd4095010b.js} +67 -37
  25. package/dist/{531.bundle.2a82fb1d69e5b57cc72b.js → 677.bundle.ec5f2b4707db33bd4d8e.js} +731 -447
  26. package/dist/{707.bundle.7fd06edc9852075913ec.js → 678.bundle.adc83cac2fd0ea4f19f2.js} +209 -216
  27. package/dist/{205.bundle.b5a473c200dcf2bbcdb4.js → 686.bundle.70565410179f1e7d22e6.js} +4 -4
  28. package/dist/{331.bundle.bd0c13931a21d53086c9.js → 754.bundle.1884c227b361aefa66b4.js} +12383 -7530
  29. package/dist/{728.bundle.d13856835357400fef82.js → 774.bundle.8ba82ee206266eb2da5e.js} +90 -63
  30. package/dist/{381.bundle.0905e683605fcbc0895f.js → 775.bundle.2285e7e0e67878948c0d.js} +16 -16
  31. package/dist/{283.bundle.426df6a7412bc2932775.js → 782.bundle.fc7e353f1dce39953254.js} +57 -26
  32. package/dist/{642.bundle.0a98bca321a959b489f2.js → 814.bundle.9218ffc353490526c320.js} +6 -6
  33. package/dist/{799.bundle.758558e64147e5aad612.js → 822.bundle.99bf759483aa32086346.js} +66 -17
  34. package/dist/{953.bundle.3b0189ebc11cf0946f18.js → 886.bundle.c5a6b96760ea43352375.js} +17 -11
  35. package/dist/{270.bundle.4564621556b0f963a004.js → 925.bundle.7b06521fc75e0d3f71c3.js} +770 -607
  36. package/dist/945.min.worker.js +1 -1
  37. package/dist/945.min.worker.js.map +1 -1
  38. package/dist/app-config.js +7 -7
  39. package/dist/{app.bundle.3f8ce0b8b48882f2ff10.js → app.bundle.2a91a8b9b9e61b42cc21.js} +70344 -68432
  40. package/dist/app.bundle.css +9 -9
  41. package/dist/cornerstoneDICOMImageLoader.min.js +1 -1
  42. package/dist/cornerstoneDICOMImageLoader.min.js.map +1 -1
  43. package/dist/{dicom-microscopy-viewer.bundle.aa60bdf008c32c39cfd7.js → dicom-microscopy-viewer.bundle.2c146384eb9466d02ff8.js} +5 -4
  44. package/dist/google.js +7 -5
  45. package/dist/index.html +1 -1
  46. package/dist/{index.worker.1c69152d710fa7b84bce.worker.js → index.worker.e62ecca63f1a2e124230.worker.js} +2 -2
  47. package/dist/index.worker.e62ecca63f1a2e124230.worker.js.map +1 -0
  48. package/dist/sw.js +1 -1
  49. package/package.json +19 -20
  50. package/dist/780.bundle.fd0f13dc92e9caa0581e.js +0 -4769
  51. package/dist/index.worker.1c69152d710fa7b84bce.worker.js.map +0 -1
  52. /package/dist/{55.css → 250.css} +0 -0
  53. /package/dist/{806.css → 579.css} +0 -0
  54. /package/dist/{707.css → 678.css} +0 -0
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
- (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[82],{
2
+ (self["webpackChunk"] = self["webpackChunk"] || []).push([[362],{
3
3
 
4
- /***/ 96082:
4
+ /***/ 13362:
5
5
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
6
6
 
7
7
  // ESM COMPAT FLAG
@@ -28,11 +28,11 @@ __webpack_require__.d(utils_namespaceObject, {
28
28
  });
29
29
 
30
30
  // EXTERNAL MODULE: ../../../node_modules/dicomweb-client/build/dicomweb-client.es.js
31
- var dicomweb_client_es = __webpack_require__(75935);
32
- // EXTERNAL MODULE: ../../core/src/index.ts + 101 modules
33
- var src = __webpack_require__(48501);
31
+ var dicomweb_client_es = __webpack_require__(97604);
32
+ // EXTERNAL MODULE: ../../core/src/index.ts + 106 modules
33
+ var src = __webpack_require__(38930);
34
34
  // EXTERNAL MODULE: ../../core/src/utils/sortStudy.ts
35
- var sortStudy = __webpack_require__(87853);
35
+ var sortStudy = __webpack_require__(62971);
36
36
  ;// CONCATENATED MODULE: ../../../extensions/default/src/DicomWebDataSource/qido.js
37
37
  /**
38
38
  * QIDO - Query based on ID for DICOM Objects
@@ -378,7 +378,7 @@ function getImageId(_ref) {
378
378
  }
379
379
  }
380
380
  // EXTERNAL MODULE: ../../../node_modules/dcmjs/build/dcmjs.es.js
381
- var dcmjs_es = __webpack_require__(22737);
381
+ var dcmjs_es = __webpack_require__(67540);
382
382
  ;// CONCATENATED MODULE: ../../../extensions/default/src/DicomWebDataSource/wado/retrieveMetadataLoader.js
383
383
  /**
384
384
  * Class to define inheritance of load retrieve strategy.
@@ -708,12 +708,16 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
708
708
  * @returns
709
709
  */
710
710
  async searchForStudies(options) {
711
- if (!this.staticWado) return super.searchForStudies(options);
711
+ if (!this.staticWado) {
712
+ return super.searchForStudies(options);
713
+ }
712
714
  const searchResult = await super.searchForStudies(options);
713
715
  const {
714
716
  queryParams
715
717
  } = options;
716
- if (!queryParams) return searchResult;
718
+ if (!queryParams) {
719
+ return searchResult;
720
+ }
717
721
  const lowerParams = this.toLowerParams(queryParams);
718
722
  const filtered = searchResult.filter(study => {
719
723
  for (const key of Object.keys(StaticWadoClient.studyFilterKeys)) {
@@ -726,12 +730,16 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
726
730
  return filtered;
727
731
  }
728
732
  async searchForSeries(options) {
729
- if (!this.staticWado) return super.searchForSeries(options);
733
+ if (!this.staticWado) {
734
+ return super.searchForSeries(options);
735
+ }
730
736
  const searchResult = await super.searchForSeries(options);
731
737
  const {
732
738
  queryParams
733
739
  } = options;
734
- if (!queryParams) return searchResult;
740
+ if (!queryParams) {
741
+ return searchResult;
742
+ }
735
743
  const lowerParams = this.toLowerParams(queryParams);
736
744
  const filtered = searchResult.filter(series => {
737
745
  for (const key of Object.keys(StaticWadoClient.seriesFilterKeys)) {
@@ -767,8 +775,12 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
767
775
  actual = actual.Alphabetic;
768
776
  }
769
777
  if (typeof actual == 'string') {
770
- if (actual.length === 0) return true;
771
- if (desired.length === 0 || desired === '*') return true;
778
+ if (actual.length === 0) {
779
+ return true;
780
+ }
781
+ if (desired.length === 0 || desired === '*') {
782
+ return true;
783
+ }
772
784
  if (desired[0] === '*' && desired[desired.length - 1] === '*') {
773
785
  // console.log(`Comparing ${actual} to ${desired.substring(1, desired.length - 1)}`)
774
786
  return actual.indexOf(desired.substring(1, desired.length - 1)) != -1;
@@ -783,9 +795,13 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
783
795
 
784
796
  /** Compares a pair of dates to see if the value is within the range */
785
797
  compareDateRange(range, value) {
786
- if (!value) return true;
798
+ if (!value) {
799
+ return true;
800
+ }
787
801
  const dash = range.indexOf('-');
788
- if (dash === -1) return this.compareValues(range, value);
802
+ if (dash === -1) {
803
+ return this.compareValues(range, value);
804
+ }
789
805
  const start = range.substring(0, dash);
790
806
  const end = range.substring(dash + 1);
791
807
  return (!start || value >= start) && (!end || value <= end);
@@ -802,11 +818,17 @@ class StaticWadoClient extends dicomweb_client_es.api.DICOMwebClient {
802
818
  */
803
819
  filterItem(key, queryParams, study, sourceFilterMap) {
804
820
  const altKey = sourceFilterMap[key] || key;
805
- if (!queryParams) return true;
821
+ if (!queryParams) {
822
+ return true;
823
+ }
806
824
  const testValue = queryParams[key] || queryParams[altKey];
807
- if (!testValue) return true;
825
+ if (!testValue) {
826
+ return true;
827
+ }
808
828
  const valueElem = study[key] || study[altKey];
809
- if (!valueElem) return false;
829
+ if (!valueElem) {
830
+ return false;
831
+ }
810
832
  if (valueElem.vr == 'DA') {
811
833
  return this.compareDateRange(testValue, valueElem.Value[0]);
812
834
  }
@@ -867,8 +889,12 @@ const getDirectURL = (config, params) => {
867
889
  singlepart: fetchPart = 'video'
868
890
  } = params;
869
891
  const value = instance[tag];
870
- if (!value) return undefined;
871
- if (value.DirectRetrieveURL) return value.DirectRetrieveURL;
892
+ if (!value) {
893
+ return undefined;
894
+ }
895
+ if (value.DirectRetrieveURL) {
896
+ return value.DirectRetrieveURL;
897
+ }
872
898
  if (value.InlineBinary) {
873
899
  const blob = src.utils.b64toBlob(value.InlineBinary, defaultType);
874
900
  value.DirectRetrieveURL = URL.createObjectURL(blob);
@@ -991,65 +1017,69 @@ const metadataProvider = src.classes.MetadataProvider;
991
1017
  * @param {string|bool} singlepart - indicates of the retrieves can fetch singlepart. Options are bulkdata, video, image or boolean true
992
1018
  */
993
1019
  function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
994
- const {
995
- qidoRoot,
996
- wadoRoot,
997
- enableStudyLazyLoad,
998
- supportsFuzzyMatching,
999
- supportsWildcard,
1000
- supportsReject,
1001
- staticWado,
1002
- singlepart
1003
- } = dicomWebConfig;
1004
- const dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig));
1005
- const qidoConfig = {
1006
- url: qidoRoot,
1007
- staticWado,
1008
- singlepart,
1009
- headers: userAuthenticationService.getAuthorizationHeader(),
1010
- errorInterceptor: src/* errorHandler */.Po.getHTTPErrorHandler()
1011
- };
1012
- const wadoConfig = {
1013
- url: wadoRoot,
1014
- staticWado,
1015
- singlepart,
1016
- headers: userAuthenticationService.getAuthorizationHeader(),
1017
- errorInterceptor: src/* errorHandler */.Po.getHTTPErrorHandler()
1018
- };
1019
-
1020
- // TODO -> Two clients sucks, but its better than 1000.
1021
- // TODO -> We'll need to merge auth later.
1022
- const qidoDicomWebClient = staticWado ? new StaticWadoClient(qidoConfig) : new dicomweb_client_es.api.DICOMwebClient(qidoConfig);
1023
- const wadoDicomWebClient = staticWado ? new StaticWadoClient(wadoConfig) : new dicomweb_client_es.api.DICOMwebClient(wadoConfig);
1020
+ let dicomWebConfigCopy, qidoConfig, wadoConfig, qidoDicomWebClient, wadoDicomWebClient, getAuthrorizationHeader, generateWadoHeader;
1024
1021
  const implementation = {
1025
1022
  initialize: _ref => {
1026
1023
  let {
1027
1024
  params,
1028
1025
  query
1029
1026
  } = _ref;
1030
- const {
1031
- StudyInstanceUIDs: paramsStudyInstanceUIDs
1032
- } = params;
1033
- const queryStudyInstanceUIDs = src.utils.splitComma(query.getAll('StudyInstanceUIDs'));
1034
- const StudyInstanceUIDs = queryStudyInstanceUIDs.length && queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
1035
- const StudyInstanceUIDsAsArray = StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs) ? StudyInstanceUIDs : [StudyInstanceUIDs];
1036
- return StudyInstanceUIDsAsArray;
1027
+ if (dicomWebConfig.onConfiguration && typeof dicomWebConfig.onConfiguration === 'function') {
1028
+ dicomWebConfig = dicomWebConfig.onConfiguration(dicomWebConfig, {
1029
+ params,
1030
+ query
1031
+ });
1032
+ }
1033
+ dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig));
1034
+ getAuthrorizationHeader = () => {
1035
+ const xhrRequestHeaders = {};
1036
+ const authHeaders = userAuthenticationService.getAuthorizationHeader();
1037
+ if (authHeaders && authHeaders.Authorization) {
1038
+ xhrRequestHeaders.Authorization = authHeaders.Authorization;
1039
+ }
1040
+ return xhrRequestHeaders;
1041
+ };
1042
+ generateWadoHeader = () => {
1043
+ let authorizationHeader = getAuthrorizationHeader();
1044
+ //Generate accept header depending on config params
1045
+ let formattedAcceptHeader = src.utils.generateAcceptHeader(dicomWebConfig.acceptHeader, dicomWebConfig.requestTransferSyntaxUID, dicomWebConfig.omitQuotationForMultipartRequest);
1046
+ return {
1047
+ ...authorizationHeader,
1048
+ Accept: formattedAcceptHeader
1049
+ };
1050
+ };
1051
+ qidoConfig = {
1052
+ url: dicomWebConfig.qidoRoot,
1053
+ staticWado: dicomWebConfig.staticWado,
1054
+ singlepart: dicomWebConfig.singlepart,
1055
+ headers: userAuthenticationService.getAuthorizationHeader(),
1056
+ errorInterceptor: src/* errorHandler */.Po.getHTTPErrorHandler()
1057
+ };
1058
+ wadoConfig = {
1059
+ url: dicomWebConfig.wadoRoot,
1060
+ staticWado: dicomWebConfig.staticWado,
1061
+ singlepart: dicomWebConfig.singlepart,
1062
+ headers: userAuthenticationService.getAuthorizationHeader(),
1063
+ errorInterceptor: src/* errorHandler */.Po.getHTTPErrorHandler()
1064
+ };
1065
+
1066
+ // TODO -> Two clients sucks, but its better than 1000.
1067
+ // TODO -> We'll need to merge auth later.
1068
+ qidoDicomWebClient = dicomWebConfig.staticWado ? new StaticWadoClient(qidoConfig) : new dicomweb_client_es.api.DICOMwebClient(qidoConfig);
1069
+ wadoDicomWebClient = dicomWebConfig.staticWado ? new StaticWadoClient(wadoConfig) : new dicomweb_client_es.api.DICOMwebClient(wadoConfig);
1037
1070
  },
1038
1071
  query: {
1039
1072
  studies: {
1040
1073
  mapParams: mapParams.bind(),
1041
1074
  search: async function (origParams) {
1042
- const headers = userAuthenticationService.getAuthorizationHeader();
1043
- if (headers) {
1044
- qidoDicomWebClient.headers = headers;
1045
- }
1075
+ qidoDicomWebClient.headers = getAuthrorizationHeader();
1046
1076
  const {
1047
1077
  studyInstanceUid,
1048
1078
  seriesInstanceUid,
1049
1079
  ...mappedParams
1050
1080
  } = mapParams(origParams, {
1051
- supportsFuzzyMatching,
1052
- supportsWildcard
1081
+ supportsFuzzyMatching: dicomWebConfig.supportsFuzzyMatching,
1082
+ supportsWildcard: dicomWebConfig.supportsWildcard
1053
1083
  }) || {};
1054
1084
  const results = await search(qidoDicomWebClient, undefined, undefined, mappedParams);
1055
1085
  return processResults(results);
@@ -1059,10 +1089,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1059
1089
  series: {
1060
1090
  // mapParams: mapParams.bind(),
1061
1091
  search: async function (studyInstanceUid) {
1062
- const headers = userAuthenticationService.getAuthorizationHeader();
1063
- if (headers) {
1064
- qidoDicomWebClient.headers = headers;
1065
- }
1092
+ qidoDicomWebClient.headers = getAuthrorizationHeader();
1066
1093
  const results = await seriesInStudy(qidoDicomWebClient, studyInstanceUid);
1067
1094
  return processSeriesResults(results);
1068
1095
  }
@@ -1071,10 +1098,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1071
1098
 
1072
1099
  instances: {
1073
1100
  search: (studyInstanceUid, queryParameters) => {
1074
- const headers = userAuthenticationService.getAuthorizationHeader();
1075
- if (headers) {
1076
- qidoDicomWebClient.headers = headers;
1077
- }
1101
+ qidoDicomWebClient.headers = getAuthrorizationHeader();
1078
1102
  search.call(undefined, qidoDicomWebClient, studyInstanceUid, null, queryParameters);
1079
1103
  }
1080
1104
  }
@@ -1093,8 +1117,8 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1093
1117
  */
1094
1118
  directURL: params => {
1095
1119
  return utils_getDirectURL({
1096
- wadoRoot,
1097
- singlepart
1120
+ wadoRoot: dicomWebConfig.wadoRoot,
1121
+ singlepart: dicomWebConfig.singlepart
1098
1122
  }, params);
1099
1123
  },
1100
1124
  bulkDataURI: async _ref2 => {
@@ -1102,6 +1126,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1102
1126
  StudyInstanceUID,
1103
1127
  BulkDataURI
1104
1128
  } = _ref2;
1129
+ qidoDicomWebClient.headers = getAuthrorizationHeader();
1105
1130
  const options = {
1106
1131
  multipart: false,
1107
1132
  BulkDataURI,
@@ -1121,14 +1146,10 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1121
1146
  sortFunction,
1122
1147
  madeInClient = false
1123
1148
  } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1124
- const headers = userAuthenticationService.getAuthorizationHeader();
1125
- if (headers) {
1126
- wadoDicomWebClient.headers = headers;
1127
- }
1128
1149
  if (!StudyInstanceUID) {
1129
1150
  throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
1130
1151
  }
1131
- if (enableStudyLazyLoad) {
1152
+ if (dicomWebConfig.enableStudyLazyLoad) {
1132
1153
  return implementation._retrieveSeriesMetadataAsync(StudyInstanceUID, filters, sortCriteria, sortFunction, madeInClient);
1133
1154
  }
1134
1155
  return implementation._retrieveSeriesMetadataSync(StudyInstanceUID, filters, sortCriteria, sortFunction, madeInClient);
@@ -1137,10 +1158,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1137
1158
  },
1138
1159
  store: {
1139
1160
  dicom: async (dataset, request) => {
1140
- const headers = userAuthenticationService.getAuthorizationHeader();
1141
- if (headers) {
1142
- wadoDicomWebClient.headers = headers;
1143
- }
1161
+ wadoDicomWebClient.headers = getAuthrorizationHeader();
1144
1162
  if (dataset instanceof ArrayBuffer) {
1145
1163
  const options = {
1146
1164
  datasets: [dataset],
@@ -1170,7 +1188,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1170
1188
  },
1171
1189
  _retrieveSeriesMetadataSync: async (StudyInstanceUID, filters, sortCriteria, sortFunction, madeInClient) => {
1172
1190
  const enableStudyLazyLoad = false;
1173
-
1191
+ wadoDicomWebClient.headers = generateWadoHeader();
1174
1192
  // data is all SOPInstanceUIDs
1175
1193
  const data = await retrieveStudyMetadata(wadoDicomWebClient, StudyInstanceUID, enableStudyLazyLoad, filters, sortCriteria, sortFunction);
1176
1194
 
@@ -1215,6 +1233,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1215
1233
  _retrieveSeriesMetadataAsync: async function (StudyInstanceUID, filters, sortCriteria, sortFunction) {
1216
1234
  let madeInClient = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
1217
1235
  const enableStudyLazyLoad = true;
1236
+ wadoDicomWebClient.headers = generateWadoHeader();
1218
1237
  // Get Series
1219
1238
  const {
1220
1239
  preLoadData: seriesSummaryMetadata,
@@ -1356,10 +1375,23 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
1356
1375
  },
1357
1376
  getConfig() {
1358
1377
  return dicomWebConfigCopy;
1378
+ },
1379
+ getStudyInstanceUIDs(_ref4) {
1380
+ let {
1381
+ params,
1382
+ query
1383
+ } = _ref4;
1384
+ const {
1385
+ StudyInstanceUIDs: paramsStudyInstanceUIDs
1386
+ } = params;
1387
+ const queryStudyInstanceUIDs = src.utils.splitComma(query.getAll('StudyInstanceUIDs'));
1388
+ const StudyInstanceUIDs = queryStudyInstanceUIDs.length && queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
1389
+ const StudyInstanceUIDsAsArray = StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs) ? StudyInstanceUIDs : [StudyInstanceUIDs];
1390
+ return StudyInstanceUIDsAsArray;
1359
1391
  }
1360
1392
  };
1361
- if (supportsReject) {
1362
- implementation.reject = dcm4cheeReject(wadoRoot);
1393
+ if (dicomWebConfig.supportsReject) {
1394
+ implementation.reject = dcm4cheeReject(dicomWebConfig.wadoRoot);
1363
1395
  }
1364
1396
  return src/* IWebApiDataSource */.Is.create(implementation);
1365
1397
  }
@@ -1375,7 +1407,8 @@ const mappings = {
1375
1407
  patientId: 'PatientID'
1376
1408
  };
1377
1409
  let _store = {
1378
- urls: []
1410
+ urls: [],
1411
+ studyInstanceUIDMap: new Map() // map of urls to array of study instance UIDs
1379
1412
  // {
1380
1413
  // url: url1
1381
1414
  // studies: [Study1, Study2], // if multiple studies
@@ -1403,17 +1436,17 @@ const findStudies = (key, value) => {
1403
1436
  };
1404
1437
  function createDicomJSONApi(dicomJsonConfig) {
1405
1438
  const {
1406
- name,
1407
1439
  wadoRoot
1408
1440
  } = dicomJsonConfig;
1409
1441
  const implementation = {
1410
1442
  initialize: async _ref => {
1411
1443
  let {
1412
- params,
1413
1444
  query,
1414
1445
  url
1415
1446
  } = _ref;
1416
- if (!url) url = query.get('url');
1447
+ if (!url) {
1448
+ url = query.get('url');
1449
+ }
1417
1450
  let metaData = getMetaDataByURL(url);
1418
1451
 
1419
1452
  // if we have already cached the data from this specific url
@@ -1425,8 +1458,7 @@ function createDicomJSONApi(dicomJsonConfig) {
1425
1458
  });
1426
1459
  }
1427
1460
  const response = await fetch(url);
1428
- let data = await response.json();
1429
- const studyInstanceUIDs = data.studies.map(study => study.StudyInstanceUID);
1461
+ const data = await response.json();
1430
1462
  let StudyInstanceUID;
1431
1463
  let SeriesInstanceUID;
1432
1464
  data.studies.forEach(study => {
@@ -1452,7 +1484,7 @@ function createDicomJSONApi(dicomJsonConfig) {
1452
1484
  url,
1453
1485
  studies: [...data.studies]
1454
1486
  });
1455
- return studyInstanceUIDs;
1487
+ _store.studyInstanceUIDMap.set(url, data.studies.map(study => study.StudyInstanceUID));
1456
1488
  },
1457
1489
  query: {
1458
1490
  studies: {
@@ -1561,7 +1593,9 @@ function createDicomJSONApi(dicomJsonConfig) {
1561
1593
  return obj;
1562
1594
  });
1563
1595
  storeInstances(instances);
1564
- if (index === numberOfSeries - 1) setSuccessFlag();
1596
+ if (index === numberOfSeries - 1) {
1597
+ setSuccessFlag();
1598
+ }
1565
1599
  });
1566
1600
  }
1567
1601
  }
@@ -1608,6 +1642,14 @@ function createDicomJSONApi(dicomJsonConfig) {
1608
1642
  frame
1609
1643
  });
1610
1644
  return imageIds;
1645
+ },
1646
+ getStudyInstanceUIDs: _ref3 => {
1647
+ let {
1648
+ params,
1649
+ query
1650
+ } = _ref3;
1651
+ const url = query.get('url');
1652
+ return _store.studyInstanceUIDMap.get(url);
1611
1653
  }
1612
1654
  };
1613
1655
  return src/* IWebApiDataSource */.Is.create(implementation);
@@ -1628,8 +1670,12 @@ const END_MODALITIES = {
1628
1670
  };
1629
1671
  const compareValue = function (v1, v2) {
1630
1672
  let def = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
1631
- if (v1 === v2) return def;
1632
- if (v1 < v2) return -1;
1673
+ if (v1 === v2) {
1674
+ return def;
1675
+ }
1676
+ if (v1 < v2) {
1677
+ return -1;
1678
+ }
1633
1679
  return 1;
1634
1680
  };
1635
1681
 
@@ -1660,19 +1706,6 @@ function createDicomLocalApi(dicomLocalConfig) {
1660
1706
  params,
1661
1707
  query
1662
1708
  } = _ref;
1663
- const {
1664
- StudyInstanceUIDs: paramsStudyInstanceUIDs
1665
- } = params;
1666
- const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
1667
- const StudyInstanceUIDs = queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
1668
- const StudyInstanceUIDsAsArray = StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs) ? StudyInstanceUIDs : [StudyInstanceUIDs];
1669
-
1670
- // Put SRs at the end of series list to make sure images are loaded first
1671
- StudyInstanceUIDsAsArray.forEach(StudyInstanceUID => {
1672
- const study = src.DicomMetadataStore.getStudy(StudyInstanceUID);
1673
- study.series = study.series.sort(customSort);
1674
- });
1675
- return StudyInstanceUIDsAsArray;
1676
1709
  },
1677
1710
  query: {
1678
1711
  studies: {
@@ -1854,6 +1887,29 @@ function createDicomLocalApi(dicomLocalConfig) {
1854
1887
  },
1855
1888
  deleteStudyMetadataPromise() {
1856
1889
  console.log('deleteStudyMetadataPromise not implemented');
1890
+ },
1891
+ getStudyInstanceUIDs: _ref3 => {
1892
+ let {
1893
+ params,
1894
+ query
1895
+ } = _ref3;
1896
+ const {
1897
+ StudyInstanceUIDs: paramsStudyInstanceUIDs
1898
+ } = params;
1899
+ const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
1900
+ const StudyInstanceUIDs = queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
1901
+ const StudyInstanceUIDsAsArray = StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs) ? StudyInstanceUIDs : [StudyInstanceUIDs];
1902
+
1903
+ // Put SRs at the end of series list to make sure images are loaded first
1904
+ let isStudyInCache = false;
1905
+ StudyInstanceUIDsAsArray.forEach(StudyInstanceUID => {
1906
+ const study = src.DicomMetadataStore.getStudy(StudyInstanceUID);
1907
+ if (study) {
1908
+ study.series = study.series.sort(customSort);
1909
+ isStudyInCache = true;
1910
+ }
1911
+ });
1912
+ return isStudyInCache ? StudyInstanceUIDsAsArray : [];
1857
1913
  }
1858
1914
  };
1859
1915
  return src/* IWebApiDataSource */.Is.create(implementation);
@@ -1882,13 +1938,6 @@ function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService)
1882
1938
  params,
1883
1939
  query
1884
1940
  } = _ref;
1885
- let studyInstanceUIDs = [];
1886
-
1887
- // there seem to be a couple of variations of the case for this parameter
1888
- const queryStudyInstanceUIDs = query.get('studyInstanceUIDs') || query.get('studyInstanceUids');
1889
- if (!queryStudyInstanceUIDs) {
1890
- throw new Error(`No studyInstanceUids in request for '${name}'`);
1891
- }
1892
1941
  const url = query.get('url');
1893
1942
  if (!url) {
1894
1943
  throw new Error(`No url for '${name}'`);
@@ -1898,10 +1947,12 @@ function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService)
1898
1947
  if (!data.servers?.dicomWeb?.[0]) {
1899
1948
  throw new Error('Invalid configuration returned by url');
1900
1949
  }
1901
- dicomWebDelegate = createDicomWebApi(data.servers.dicomWeb[0], UserAuthenticationService);
1902
- studyInstanceUIDs = queryStudyInstanceUIDs.split(';');
1950
+ dicomWebDelegate = createDicomWebApi(data.servers.dicomWeb[0].configuration, UserAuthenticationService);
1951
+ dicomWebDelegate.initialize({
1952
+ params,
1953
+ query
1954
+ });
1903
1955
  }
1904
- return studyInstanceUIDs;
1905
1956
  },
1906
1957
  query: {
1907
1958
  studies: {
@@ -1939,6 +1990,21 @@ function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService)
1939
1990
  },
1940
1991
  getImageIdsForInstance: function () {
1941
1992
  return dicomWebDelegate.getImageIdsForInstance(...arguments);
1993
+ },
1994
+ getStudyInstanceUIDs(_ref2) {
1995
+ let {
1996
+ params,
1997
+ query
1998
+ } = _ref2;
1999
+ let studyInstanceUIDs = [];
2000
+
2001
+ // there seem to be a couple of variations of the case for this parameter
2002
+ const queryStudyInstanceUIDs = query.get('studyInstanceUIDs') || query.get('studyInstanceUids');
2003
+ if (!queryStudyInstanceUIDs) {
2004
+ throw new Error(`No studyInstanceUids in request for '${name}'`);
2005
+ }
2006
+ studyInstanceUIDs = queryStudyInstanceUIDs.split(';');
2007
+ return studyInstanceUIDs;
1942
2008
  }
1943
2009
  };
1944
2010
  return src/* IWebApiDataSource */.Is.create(implementation);
@@ -1978,24 +2044,24 @@ function getDataSourcesModule() {
1978
2044
  }
1979
2045
  /* harmony default export */ const src_getDataSourcesModule = (getDataSourcesModule);
1980
2046
  // EXTERNAL MODULE: ../../../node_modules/react/index.js
1981
- var react = __webpack_require__(32735);
2047
+ var react = __webpack_require__(43001);
1982
2048
  // EXTERNAL MODULE: ../../../node_modules/prop-types/index.js
1983
- var prop_types = __webpack_require__(60216);
2049
+ var prop_types = __webpack_require__(3827);
1984
2050
  var prop_types_default = /*#__PURE__*/__webpack_require__.n(prop_types);
1985
2051
  // EXTERNAL MODULE: ../node_modules/react-router-dom/dist/index.js
1986
- var dist = __webpack_require__(65783);
2052
+ var dist = __webpack_require__(62474);
1987
2053
  // EXTERNAL MODULE: ../../../node_modules/react-i18next/dist/es/index.js + 15 modules
1988
- var es = __webpack_require__(21572);
2054
+ var es = __webpack_require__(69190);
1989
2055
  // EXTERNAL MODULE: ../node_modules/react-router/dist/index.js
1990
- var react_router_dist = __webpack_require__(14935);
1991
- // EXTERNAL MODULE: ../../ui/src/index.js + 452 modules
1992
- var ui_src = __webpack_require__(28619);
1993
- // EXTERNAL MODULE: ../../i18n/src/index.js + 95 modules
1994
- var i18n_src = __webpack_require__(93058);
2056
+ var react_router_dist = __webpack_require__(85066);
2057
+ // EXTERNAL MODULE: ../../ui/src/index.js + 458 modules
2058
+ var ui_src = __webpack_require__(33570);
2059
+ // EXTERNAL MODULE: ../../i18n/src/index.js + 98 modules
2060
+ var i18n_src = __webpack_require__(43040);
1995
2061
  // EXTERNAL MODULE: ./state/index.js + 1 modules
1996
- var state = __webpack_require__(22896);
2062
+ var state = __webpack_require__(62657);
1997
2063
  // EXTERNAL MODULE: ../../../node_modules/classnames/index.js
1998
- var classnames = __webpack_require__(40841);
2064
+ var classnames = __webpack_require__(44921);
1999
2065
  var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
2000
2066
  ;// CONCATENATED MODULE: ../../../extensions/default/src/Toolbar/Toolbar.tsx
2001
2067
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
@@ -2030,7 +2096,7 @@ function Toolbar(_ref) {
2030
2096
  unsub2();
2031
2097
  };
2032
2098
  }, [toolbarService]);
2033
- return /*#__PURE__*/react.createElement(react.Fragment, null, toolbarButtons.map((toolDef, index) => {
2099
+ return /*#__PURE__*/react.createElement(react.Fragment, null, toolbarButtons.map(toolDef => {
2034
2100
  const {
2035
2101
  id,
2036
2102
  Component,
@@ -2116,8 +2182,10 @@ function ViewerLayout(_ref) {
2116
2182
  // Todo: Handle parameters in a better way.
2117
2183
  const query = new URLSearchParams(window.location.search);
2118
2184
  const configUrl = query.get('configUrl');
2185
+ const dataSourceName = pathname.substring(dataSourceIdx + 1);
2186
+ const existingDataSource = extensionManager.getDataSources(dataSourceName);
2119
2187
  const searchQuery = new URLSearchParams();
2120
- if (dataSourceIdx !== -1) {
2188
+ if (dataSourceIdx !== -1 && existingDataSource) {
2121
2189
  searchQuery.append('datasources', pathname.substring(dataSourceIdx + 1));
2122
2190
  }
2123
2191
  if (configUrl) {
@@ -2143,8 +2211,8 @@ function ViewerLayout(_ref) {
2143
2211
  hotkeyDefinitions,
2144
2212
  hotkeyDefaults
2145
2213
  } = hotkeysManager;
2146
- const versionNumber = "3.7.0-beta.6";
2147
- const commitHash = "e6f73a202501898856ad3edad3c395b8e030d11a";
2214
+ const versionNumber = "3.7.0-beta.61";
2215
+ const commitHash = "35fc30c5359d8199cc38ffa670c08687d2672f11";
2148
2216
  const menuOptions = [{
2149
2217
  title: t('Header:About'),
2150
2218
  icon: 'info',
@@ -2364,6 +2432,7 @@ ViewerLayout.propTypes = {
2364
2432
 
2365
2433
 
2366
2434
 
2435
+
2367
2436
  const {
2368
2437
  sortStudyInstances,
2369
2438
  formatDate
@@ -2386,6 +2455,8 @@ function PanelStudyBrowser(_ref) {
2386
2455
  displaySetService,
2387
2456
  uiNotificationService
2388
2457
  } = servicesManager.services;
2458
+ const navigate = (0,dist/* useNavigate */.s0)();
2459
+
2389
2460
  // Normally you nest the components so the tree isn't so deep, and the data
2390
2461
  // doesn't have to have such an intense shape. This works well enough for now.
2391
2462
  // Tabs --> Studies --> DisplaySets --> Thumbnails
@@ -2427,6 +2498,10 @@ function PanelStudyBrowser(_ref) {
2427
2498
  const qidoForStudyUID = await dataSource.query.studies.search({
2428
2499
  studyInstanceUid: StudyInstanceUID
2429
2500
  });
2501
+ if (!qidoForStudyUID?.length) {
2502
+ navigate('/notfoundstudy', '_self');
2503
+ throw new Error('Invalid study URL');
2504
+ }
2430
2505
  let qidoStudiesForPatient = qidoForStudyUID;
2431
2506
 
2432
2507
  // try to fetch the prior studies based on the patientID if the
@@ -2470,7 +2545,7 @@ function PanelStudyBrowser(_ref) {
2470
2545
  const imageId = imageIds[Math.floor(imageIds.length / 2)];
2471
2546
 
2472
2547
  // TODO: Is it okay that imageIds are not returned here for SR displaySets?
2473
- if (imageId) {
2548
+ if (imageId && !displaySet?.unsupported) {
2474
2549
  // When the image arrives, render it and store the result in the thumbnailImgSrcMap
2475
2550
  newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
2476
2551
  if (isMounted.current) {
@@ -2509,20 +2584,22 @@ function PanelStudyBrowser(_ref) {
2509
2584
  displaySetsAdded.forEach(async dSet => {
2510
2585
  const newImageSrcEntry = {};
2511
2586
  const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
2512
- const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
2513
- const imageId = imageIds[Math.floor(imageIds.length / 2)];
2514
-
2515
- // TODO: Is it okay that imageIds are not returned here for SR displaysets?
2516
- if (imageId) {
2517
- // When the image arrives, render it and store the result in the thumbnailImgSrcMap
2518
- newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId, dSet.initialViewport);
2519
- if (isMounted.current) {
2520
- setThumbnailImageSrcMap(prevState => {
2521
- return {
2522
- ...prevState,
2523
- ...newImageSrcEntry
2524
- };
2525
- });
2587
+ if (!displaySet?.unsupported) {
2588
+ const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
2589
+ const imageId = imageIds[Math.floor(imageIds.length / 2)];
2590
+
2591
+ // TODO: Is it okay that imageIds are not returned here for SR displaysets?
2592
+ if (imageId) {
2593
+ // When the image arrives, render it and store the result in the thumbnailImgSrcMap
2594
+ newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId, dSet.initialViewport);
2595
+ if (isMounted.current) {
2596
+ setThumbnailImageSrcMap(prevState => {
2597
+ return {
2598
+ ...prevState,
2599
+ ...newImageSrcEntry
2600
+ };
2601
+ });
2602
+ }
2526
2603
  }
2527
2604
  }
2528
2605
  });
@@ -2534,9 +2611,14 @@ function PanelStudyBrowser(_ref) {
2534
2611
  const mappedDisplaySets = _mapDisplaySets(changedDisplaySets, thumbnailImageSrcMap);
2535
2612
  setDisplaySets(mappedDisplaySets);
2536
2613
  });
2614
+ const SubscriptionDisplaySetMetaDataInvalidated = displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SET_SERIES_METADATA_INVALIDATED, () => {
2615
+ const mappedDisplaySets = _mapDisplaySets(displaySetService.getActiveDisplaySets(), thumbnailImageSrcMap);
2616
+ setDisplaySets(mappedDisplaySets);
2617
+ });
2537
2618
  return () => {
2538
2619
  SubscriptionDisplaySetsAdded.unsubscribe();
2539
2620
  SubscriptionDisplaySetsChanged.unsubscribe();
2621
+ SubscriptionDisplaySetMetaDataInvalidated.unsubscribe();
2540
2622
  };
2541
2623
  // eslint-disable-next-line react-hooks/exhaustive-deps
2542
2624
  }, []);
@@ -2605,7 +2687,7 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
2605
2687
  const thumbnailNoImageDisplaySets = [];
2606
2688
  displaySets.filter(ds => !ds.excludeFromThumbnailBrowser).forEach(ds => {
2607
2689
  const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
2608
- const componentType = _getComponentType(ds.Modality);
2690
+ const componentType = _getComponentType(ds);
2609
2691
  const array = componentType === 'thumbnail' ? thumbnailDisplaySets : thumbnailNoImageDisplaySets;
2610
2692
  array.push({
2611
2693
  displaySetInstanceUID: ds.displaySetInstanceUID,
@@ -2617,21 +2699,23 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
2617
2699
  numInstances: ds.numImageFrames,
2618
2700
  countIcon: ds.countIcon,
2619
2701
  StudyInstanceUID: ds.StudyInstanceUID,
2702
+ messages: ds.messages,
2620
2703
  componentType,
2621
2704
  imageSrc,
2622
2705
  dragData: {
2623
2706
  type: 'displayset',
2624
2707
  displaySetInstanceUID: ds.displaySetInstanceUID
2625
2708
  // .. Any other data to pass
2626
- }
2709
+ },
2710
+
2711
+ isHydratedForDerivedDisplaySet: ds.isHydrated
2627
2712
  });
2628
2713
  });
2629
-
2630
2714
  return [...thumbnailDisplaySets, ...thumbnailNoImageDisplaySets];
2631
2715
  }
2632
2716
  const thumbnailNoImageModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
2633
- function _getComponentType(Modality) {
2634
- if (thumbnailNoImageModalities.includes(Modality)) {
2717
+ function _getComponentType(ds) {
2718
+ if (thumbnailNoImageModalities.includes(ds.Modality) || ds?.unsupported) {
2635
2719
  // TODO probably others.
2636
2720
  return 'thumbnailNoImage';
2637
2721
  }
@@ -2819,7 +2903,7 @@ ActionButtons.defaultProps = {
2819
2903
  };
2820
2904
  /* harmony default export */ const Panels_ActionButtons = (ActionButtons);
2821
2905
  // EXTERNAL MODULE: ../../../node_modules/lodash.debounce/index.js
2822
- var lodash_debounce = __webpack_require__(40001);
2906
+ var lodash_debounce = __webpack_require__(8324);
2823
2907
  var lodash_debounce_default = /*#__PURE__*/__webpack_require__.n(lodash_debounce);
2824
2908
  ;// CONCATENATED MODULE: ../../../extensions/default/src/Panels/createReportDialogPrompt.tsx
2825
2909
  /* eslint-disable react/display-name */
@@ -3336,7 +3420,9 @@ function _mapMeasurementToDisplay(measurement, index, types) {
3336
3420
  if (findingSites) {
3337
3421
  const siteText = [];
3338
3422
  findingSites.forEach(site => {
3339
- if (site?.text !== label) siteText.push(site.text);
3423
+ if (site?.text !== label) {
3424
+ siteText.push(site.text);
3425
+ }
3340
3426
  });
3341
3427
  displayText = [...siteText, ...displayText];
3342
3428
  }
@@ -3403,119 +3489,424 @@ function getPanelModule(_ref) {
3403
3489
  }
3404
3490
  /* harmony default export */ const src_getPanelModule = (getPanelModule);
3405
3491
  // EXTERNAL MODULE: ../../core/src/utils/isImage.js
3406
- var isImage = __webpack_require__(81596);
3492
+ var isImage = __webpack_require__(11835);
3407
3493
  // EXTERNAL MODULE: ../../core/src/utils/sopClassDictionary.js
3408
- var sopClassDictionary = __webpack_require__(32746);
3494
+ var sopClassDictionary = __webpack_require__(24369);
3409
3495
  // EXTERNAL MODULE: ../../core/src/classes/ImageSet.ts
3410
- var ImageSet = __webpack_require__(4797);
3496
+ var ImageSet = __webpack_require__(13950);
3411
3497
  // EXTERNAL MODULE: ../../core/src/utils/isDisplaySetReconstructable.js
3412
- var isDisplaySetReconstructable = __webpack_require__(51227);
3498
+ var isDisplaySetReconstructable = __webpack_require__(89359);
3413
3499
  ;// CONCATENATED MODULE: ../../../extensions/default/package.json
3414
3500
  const package_namespaceObject = JSON.parse('{"u2":"@ohif/extension-default"}');
3415
3501
  ;// CONCATENATED MODULE: ../../../extensions/default/src/id.js
3416
3502
 
3417
3503
  const id = package_namespaceObject.u2;
3418
3504
 
3419
- ;// CONCATENATED MODULE: ../../../extensions/default/src/getSopClassHandlerModule.js
3505
+ // EXTERNAL MODULE: ../../core/src/utils/sortInstancesByPosition.ts
3506
+ var sortInstancesByPosition = __webpack_require__(87425);
3507
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/checkMultiframe.ts
3420
3508
 
3421
3509
 
3422
3510
 
3511
+ /**
3512
+ * Check various multi frame issues. It calls OHIF core functions
3513
+ * @param {*} multiFrameInstance
3514
+ * @param {*} warnings
3515
+ */
3516
+ function checkMultiFrame(multiFrameInstance, messages) {
3517
+ if (!(0,isDisplaySetReconstructable/* hasPixelMeasurements */.hu)(multiFrameInstance)) {
3518
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MULTIFRAME_NO_PIXEL_MEASUREMENTS);
3519
+ }
3520
+ if (!(0,isDisplaySetReconstructable/* hasOrientation */.sb)(multiFrameInstance)) {
3521
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MULTIFRAME_NO_ORIENTATION);
3522
+ }
3523
+ if (!(0,isDisplaySetReconstructable/* hasPosition */.kN)(multiFrameInstance)) {
3524
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MULTIFRAME_NO_POSITION_INFORMATION);
3525
+ }
3526
+ }
3527
+ // EXTERNAL MODULE: ../../core/src/utils/toNumber.js
3528
+ var toNumber = __webpack_require__(94972);
3529
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageDimensionsEqual.ts
3423
3530
 
3424
3531
 
3425
- const sopClassHandlerName = 'stack';
3426
- const isMultiFrame = instance => {
3427
- return instance.NumberOfFrames > 1;
3428
- };
3429
- const makeDisplaySet = instances => {
3430
- const instance = instances[0];
3431
- const imageSet = new ImageSet/* default */.Z(instances);
3432
- const {
3433
- value: isReconstructable,
3434
- averageSpacingBetweenFrames
3435
- } = (0,isDisplaySetReconstructable/* default */.Z)(instances);
3532
+ /**
3533
+ * Check if the frames in a series has different dimensions
3534
+ * @param {*} instances
3535
+ * @returns
3536
+ */
3537
+ function areAllImageDimensionsEqual(instances) {
3538
+ if (!instances?.length) {
3539
+ return false;
3540
+ }
3541
+ const firstImage = instances[0];
3542
+ const firstImageRows = (0,toNumber/* default */.Z)(firstImage.Rows);
3543
+ const firstImageColumns = (0,toNumber/* default */.Z)(firstImage.Columns);
3544
+ for (let i = 1; i < instances.length; i++) {
3545
+ const instance = instances[i];
3546
+ const {
3547
+ Rows,
3548
+ Columns
3549
+ } = instance;
3550
+ if (Rows !== firstImageRows || Columns !== firstImageColumns) {
3551
+ return false;
3552
+ }
3553
+ }
3554
+ return true;
3555
+ }
3556
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageComponentsEqual.ts
3436
3557
 
3437
- // set appropriate attributes to image set...
3438
- imageSet.setAttributes({
3439
- displaySetInstanceUID: imageSet.uid,
3440
- // create a local alias for the imageSet UID
3441
- SeriesDate: instance.SeriesDate,
3442
- SeriesTime: instance.SeriesTime,
3443
- SeriesInstanceUID: instance.SeriesInstanceUID,
3444
- StudyInstanceUID: instance.StudyInstanceUID,
3445
- SeriesNumber: instance.SeriesNumber || 0,
3446
- FrameRate: instance.FrameTime,
3447
- SOPClassUID: instance.SOPClassUID,
3448
- SeriesDescription: instance.SeriesDescription || '',
3449
- Modality: instance.Modality,
3450
- isMultiFrame: isMultiFrame(instance),
3451
- countIcon: isReconstructable ? 'icon-mpr' : undefined,
3452
- numImageFrames: instances.length,
3453
- SOPClassHandlerId: `${id}.sopClassHandlerModule.${sopClassHandlerName}`,
3454
- isReconstructable,
3455
- averageSpacingBetweenFrames: averageSpacingBetweenFrames || null
3456
- });
3457
3558
 
3458
- // Sort the images in this series if needed
3459
- const shallSort = true; //!OHIF.utils.ObjectPath.get(Meteor, 'settings.public.ui.sortSeriesByIncomingOrder');
3460
- if (shallSort) {
3461
- imageSet.sortBy((a, b) => {
3462
- // Sort by InstanceNumber (0020,0013)
3463
- return (parseInt(a.InstanceNumber) || 0) - (parseInt(b.InstanceNumber) || 0);
3464
- });
3559
+ /**
3560
+ * Check if all voxels in series images has same number of components (samplesPerPixel)
3561
+ * @param {*} instances
3562
+ * @returns
3563
+ */
3564
+ function areAllImageComponentsEqual(instances) {
3565
+ if (!instances?.length) {
3566
+ return false;
3567
+ }
3568
+ const firstImage = instances[0];
3569
+ const firstImageSamplesPerPixel = (0,toNumber/* default */.Z)(firstImage.SamplesPerPixel);
3570
+ for (let i = 1; i < instances.length; i++) {
3571
+ const instance = instances[i];
3572
+ const {
3573
+ SamplesPerPixel
3574
+ } = instance;
3575
+ if (SamplesPerPixel !== firstImageSamplesPerPixel) {
3576
+ return false;
3577
+ }
3465
3578
  }
3579
+ return true;
3580
+ }
3581
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageOrientationsEqual.ts
3466
3582
 
3467
- // Include the first image instance number (after sorted)
3468
- /*imageSet.setAttribute(
3469
- 'instanceNumber',
3470
- imageSet.getImage(0).InstanceNumber
3471
- );*/
3472
3583
 
3473
- /*const isReconstructable = isDisplaySetReconstructable(series, instances);
3474
- imageSet.isReconstructable = isReconstructable.value;
3475
- if (isReconstructable.missingFrames) {
3476
- // TODO -> This is currently unused, but may be used for reconstructing
3477
- // Volumes with gaps later on.
3478
- imageSet.missingFrames = isReconstructable.missingFrames;
3479
- }*/
3480
3584
 
3481
- return imageSet;
3482
- };
3483
- const isSingleImageModality = modality => {
3484
- return modality === 'CR' || modality === 'MG' || modality === 'DX';
3485
- };
3486
- function getSopClassUids(instances) {
3487
- const uniqueSopClassUidsInSeries = new Set();
3488
- instances.forEach(instance => {
3489
- uniqueSopClassUidsInSeries.add(instance.SOPClassUID);
3490
- });
3491
- const sopClassUids = Array.from(uniqueSopClassUidsInSeries);
3492
- return sopClassUids;
3585
+ /**
3586
+ * Check is the series has frames with different orientations
3587
+ * @param {*} instances
3588
+ * @returns
3589
+ */
3590
+ function areAllImageOrientationsEqual(instances) {
3591
+ if (!instances?.length) {
3592
+ return false;
3593
+ }
3594
+ const firstImage = instances[0];
3595
+ const firstImageOrientationPatient = (0,toNumber/* default */.Z)(firstImage.ImageOrientationPatient);
3596
+ for (let i = 1; i < instances.length; i++) {
3597
+ const instance = instances[i];
3598
+ const imageOrientationPatient = (0,toNumber/* default */.Z)(instance.ImageOrientationPatient);
3599
+ if (!(0,isDisplaySetReconstructable/* _isSameOrientation */.NB)(imageOrientationPatient, firstImageOrientationPatient)) {
3600
+ return false;
3601
+ }
3602
+ }
3603
+ return true;
3493
3604
  }
3605
+ // EXTERNAL MODULE: ../../../node_modules/gl-matrix/esm/index.js + 10 modules
3606
+ var esm = __webpack_require__(45451);
3607
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/calculateScanAxisNormal.ts
3608
+
3494
3609
 
3495
3610
  /**
3496
- * Basic SOPClassHandler:
3497
- * - For all Image types that are stackable, create
3498
- * a displaySet with a stack of images
3499
- *
3500
- * @param {Array} sopClassHandlerModules List of SOP Class Modules
3501
- * @param {SeriesMetadata} series The series metadata object from which the display sets will be created
3502
- * @returns {Array} The list of display sets created for the given series object
3611
+ * Calculates the scanAxisNormal based on a image orientation vector extract from a frame
3612
+ * @param {*} imageOrientation
3613
+ * @returns
3503
3614
  */
3504
- function getDisplaySetsFromSeries(instances) {
3505
- // If the series has no instances, stop here
3506
- if (!instances || !instances.length) {
3507
- throw new Error('No instances were provided');
3508
- }
3509
- const displaySets = [];
3510
- const sopClassUids = getSopClassUids(instances);
3615
+ function calculateScanAxisNormal(imageOrientation) {
3616
+ const rowCosineVec = esm/* vec3.fromValues */.R3.fromValues(imageOrientation[0], imageOrientation[1], imageOrientation[2]);
3617
+ const colCosineVec = esm/* vec3.fromValues */.R3.fromValues(imageOrientation[3], imageOrientation[4], imageOrientation[5]);
3618
+ return esm/* vec3.cross */.R3.cross(esm/* vec3.create */.R3.create(), rowCosineVec, colCosineVec);
3619
+ }
3620
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImagePositionsEqual.ts
3511
3621
 
3512
- // Search through the instances (InstanceMetadata object) of this series
3513
- // Split Multi-frame instances and Single-image modalities
3514
- // into their own specific display sets. Place the rest of each
3515
- // series into another display set.
3516
- const stackableInstances = [];
3517
- instances.forEach(instance => {
3518
- // All imaging modalities must have a valid value for sopClassUid (x00080016) or rows (x00280010)
3622
+
3623
+
3624
+
3625
+
3626
+ /**
3627
+ * Checks if there is a position shift between consecutive frames
3628
+ * @param {*} previousPosition
3629
+ * @param {*} actualPosition
3630
+ * @param {*} scanAxisNormal
3631
+ * @param {*} averageSpacingBetweenFrames
3632
+ * @returns
3633
+ */
3634
+ function _checkSeriesPositionShift(previousPosition, actualPosition, scanAxisNormal, averageSpacingBetweenFrames) {
3635
+ // predicted position should be the previous position added by the multiplication
3636
+ // of the scanAxisNormal and the average spacing between frames
3637
+ const predictedPosition = esm/* vec3.scaleAndAdd */.R3.scaleAndAdd(esm/* vec3.create */.R3.create(), previousPosition, scanAxisNormal, averageSpacingBetweenFrames);
3638
+ return esm/* vec3.distance */.R3.distance(actualPosition, predictedPosition) > averageSpacingBetweenFrames;
3639
+ }
3640
+
3641
+ /**
3642
+ * Checks if a series has position shifts between consecutive frames
3643
+ * @param {*} instances
3644
+ * @returns
3645
+ */
3646
+ function areAllImagePositionsEqual(instances) {
3647
+ if (!instances?.length) {
3648
+ return false;
3649
+ }
3650
+ const firstImageOrientationPatient = (0,toNumber/* default */.Z)(instances[0].ImageOrientationPatient);
3651
+ const scanAxisNormal = calculateScanAxisNormal(firstImageOrientationPatient);
3652
+ const firstImagePositionPatient = (0,toNumber/* default */.Z)(instances[0].ImagePositionPatient);
3653
+ const lastIpp = (0,toNumber/* default */.Z)(instances[instances.length - 1].ImagePositionPatient);
3654
+ const averageSpacingBetweenFrames = (0,isDisplaySetReconstructable/* _getPerpendicularDistance */.Xn)(firstImagePositionPatient, lastIpp) / (instances.length - 1);
3655
+ let previousImagePositionPatient = firstImagePositionPatient;
3656
+ for (let i = 1; i < instances.length; i++) {
3657
+ const instance = instances[i];
3658
+ const imagePositionPatient = (0,toNumber/* default */.Z)(instance.ImagePositionPatient);
3659
+ if (_checkSeriesPositionShift(previousImagePositionPatient, imagePositionPatient, scanAxisNormal, averageSpacingBetweenFrames)) {
3660
+ return false;
3661
+ }
3662
+ previousImagePositionPatient = imagePositionPatient;
3663
+ }
3664
+ return true;
3665
+ }
3666
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/areAllImageSpacingEqual.ts
3667
+
3668
+
3669
+
3670
+ /**
3671
+ * Checks if series has spacing issues
3672
+ * @param {*} instances
3673
+ * @param {*} warnings
3674
+ */
3675
+ function areAllImageSpacingEqual(instances, messages) {
3676
+ if (!instances?.length) {
3677
+ return;
3678
+ }
3679
+ const firstImagePositionPatient = (0,toNumber/* default */.Z)(instances[0].ImagePositionPatient);
3680
+ const lastIpp = (0,toNumber/* default */.Z)(instances[instances.length - 1].ImagePositionPatient);
3681
+ const averageSpacingBetweenFrames = (0,isDisplaySetReconstructable/* _getPerpendicularDistance */.Xn)(firstImagePositionPatient, lastIpp) / (instances.length - 1);
3682
+ let previousImagePositionPatient = firstImagePositionPatient;
3683
+ const issuesFound = [];
3684
+ for (let i = 1; i < instances.length; i++) {
3685
+ const instance = instances[i];
3686
+ const imagePositionPatient = (0,toNumber/* default */.Z)(instance.ImagePositionPatient);
3687
+ const spacingBetweenFrames = (0,isDisplaySetReconstructable/* _getPerpendicularDistance */.Xn)(imagePositionPatient, previousImagePositionPatient);
3688
+ const spacingIssue = (0,isDisplaySetReconstructable/* _getSpacingIssue */.bg)(spacingBetweenFrames, averageSpacingBetweenFrames);
3689
+ if (spacingIssue) {
3690
+ const issue = spacingIssue.issue;
3691
+
3692
+ // avoid multiple warning of the same thing
3693
+ if (!issuesFound.includes(issue)) {
3694
+ issuesFound.push(issue);
3695
+ if (issue === isDisplaySetReconstructable/* reconstructionIssues */.e1.MISSING_FRAMES) {
3696
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.MISSING_FRAMES);
3697
+ } else if (issue === isDisplaySetReconstructable/* reconstructionIssues */.e1.IRREGULAR_SPACING) {
3698
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.IRREGULAR_SPACING);
3699
+ }
3700
+ }
3701
+ // we just want to find issues not how many
3702
+ if (issuesFound.length > 1) {
3703
+ break;
3704
+ }
3705
+ }
3706
+ previousImagePositionPatient = imagePositionPatient;
3707
+ }
3708
+ }
3709
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/utils/validations/checkSingleFrames.ts
3710
+
3711
+
3712
+
3713
+
3714
+
3715
+
3716
+
3717
+ /**
3718
+ * Runs various checks in a single frame series
3719
+ * @param {*} instances
3720
+ * @param {*} warnings
3721
+ */
3722
+ function checkSingleFrames(instances, messages) {
3723
+ if (instances.length > 2) {
3724
+ if (!areAllImageDimensionsEqual(instances)) {
3725
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_DIMENSIONS);
3726
+ }
3727
+ if (!areAllImageComponentsEqual(instances)) {
3728
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_COMPONENTS);
3729
+ }
3730
+ if (!areAllImageOrientationsEqual(instances)) {
3731
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_ORIENTATIONS);
3732
+ }
3733
+ if (!areAllImagePositionsEqual(instances)) {
3734
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.INCONSISTENT_POSITION_INFORMATION);
3735
+ }
3736
+ areAllImageSpacingEqual(instances, messages);
3737
+ }
3738
+ }
3739
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/getDisplaySetMessages.ts
3740
+
3741
+
3742
+
3743
+
3744
+
3745
+ /**
3746
+ * Checks if a series is reconstructable to a 3D volume.
3747
+ *
3748
+ * @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
3749
+ */
3750
+ function getDisplaySetMessages(instances, isReconstructable) {
3751
+ const messages = new src/* DisplaySetMessageList */.iK();
3752
+ if (!instances.length) {
3753
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.NO_VALID_INSTANCES);
3754
+ }
3755
+ const firstInstance = instances[0];
3756
+ // Due to current requirements, LOCALIZER series doesn't have any messages
3757
+ if (firstInstance.ImageType.includes('LOCALIZER')) {
3758
+ return messages;
3759
+ }
3760
+ const Modality = firstInstance.Modality;
3761
+ if (!isDisplaySetReconstructable/* constructableModalities */.M6.includes(Modality)) {
3762
+ return messages;
3763
+ }
3764
+ const isMultiframe = firstInstance.NumberOfFrames > 1;
3765
+ // Can't reconstruct if all instances don't have the ImagePositionPatient.
3766
+ if (!isMultiframe && !instances.every(instance => instance.ImagePositionPatient)) {
3767
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.NO_POSITION_INFORMATION);
3768
+ }
3769
+ const sortedInstances = (0,sortInstancesByPosition/* default */.Z)(instances);
3770
+ isMultiframe ? checkMultiFrame(sortedInstances[0], messages) : checkSingleFrames(sortedInstances, messages);
3771
+ if (!isReconstructable) {
3772
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.NOT_RECONSTRUCTABLE);
3773
+ }
3774
+ return messages;
3775
+ }
3776
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/getDisplaySetsFromUnsupportedSeries.js
3777
+
3778
+
3779
+ /**
3780
+ * Default handler for a instance list with an unsupported sopClassUID
3781
+ */
3782
+ function getDisplaySetsFromUnsupportedSeries(instances) {
3783
+ const imageSet = new ImageSet/* default */.Z(instances);
3784
+ const messages = new src/* DisplaySetMessageList */.iK();
3785
+ messages.addMessage(src/* DisplaySetMessage */.Lt.CODES.UNSUPPORTED_DISPLAYSET);
3786
+ const instance = instances[0];
3787
+ imageSet.setAttributes({
3788
+ displaySetInstanceUID: imageSet.uid,
3789
+ // create a local alias for the imageSet UID
3790
+ SeriesDate: instance.SeriesDate,
3791
+ SeriesTime: instance.SeriesTime,
3792
+ SeriesInstanceUID: instance.SeriesInstanceUID,
3793
+ StudyInstanceUID: instance.StudyInstanceUID,
3794
+ SeriesNumber: instance.SeriesNumber || 0,
3795
+ FrameRate: instance.FrameTime,
3796
+ SOPClassUID: instance.SOPClassUID,
3797
+ SeriesDescription: instance.SeriesDescription || '',
3798
+ Modality: instance.Modality,
3799
+ numImageFrames: instances.length,
3800
+ unsupported: true,
3801
+ SOPClassHandlerId: 'unsupported',
3802
+ isReconstructable: false,
3803
+ messages
3804
+ });
3805
+ return [imageSet];
3806
+ }
3807
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/getSopClassHandlerModule.js
3808
+
3809
+
3810
+
3811
+
3812
+
3813
+
3814
+
3815
+ const sopClassHandlerName = 'stack';
3816
+ const isMultiFrame = instance => {
3817
+ return instance.NumberOfFrames > 1;
3818
+ };
3819
+ const makeDisplaySet = instances => {
3820
+ const instance = instances[0];
3821
+ const imageSet = new ImageSet/* default */.Z(instances);
3822
+ const {
3823
+ value: isReconstructable,
3824
+ averageSpacingBetweenFrames
3825
+ } = (0,isDisplaySetReconstructable/* default */.ZP)(instances);
3826
+ // set appropriate attributes to image set...
3827
+ const messages = getDisplaySetMessages(instances, isReconstructable);
3828
+ imageSet.setAttributes({
3829
+ displaySetInstanceUID: imageSet.uid,
3830
+ // create a local alias for the imageSet UID
3831
+ SeriesDate: instance.SeriesDate,
3832
+ SeriesTime: instance.SeriesTime,
3833
+ SeriesInstanceUID: instance.SeriesInstanceUID,
3834
+ StudyInstanceUID: instance.StudyInstanceUID,
3835
+ SeriesNumber: instance.SeriesNumber || 0,
3836
+ FrameRate: instance.FrameTime,
3837
+ SOPClassUID: instance.SOPClassUID,
3838
+ SeriesDescription: instance.SeriesDescription || '',
3839
+ Modality: instance.Modality,
3840
+ isMultiFrame: isMultiFrame(instance),
3841
+ countIcon: isReconstructable ? 'icon-mpr' : undefined,
3842
+ numImageFrames: instances.length,
3843
+ SOPClassHandlerId: `${id}.sopClassHandlerModule.${sopClassHandlerName}`,
3844
+ isReconstructable,
3845
+ messages,
3846
+ averageSpacingBetweenFrames: averageSpacingBetweenFrames || null
3847
+ });
3848
+
3849
+ // Sort the images in this series if needed
3850
+ const shallSort = true; //!OHIF.utils.ObjectPath.get(Meteor, 'settings.public.ui.sortSeriesByIncomingOrder');
3851
+ if (shallSort) {
3852
+ imageSet.sortBy((a, b) => {
3853
+ // Sort by InstanceNumber (0020,0013)
3854
+ return (parseInt(a.InstanceNumber) || 0) - (parseInt(b.InstanceNumber) || 0);
3855
+ });
3856
+ }
3857
+
3858
+ // Include the first image instance number (after sorted)
3859
+ /*imageSet.setAttribute(
3860
+ 'instanceNumber',
3861
+ imageSet.getImage(0).InstanceNumber
3862
+ );*/
3863
+
3864
+ /*const isReconstructable = isDisplaySetReconstructable(series, instances);
3865
+ imageSet.isReconstructable = isReconstructable.value;
3866
+ if (isReconstructable.missingFrames) {
3867
+ // TODO -> This is currently unused, but may be used for reconstructing
3868
+ // Volumes with gaps later on.
3869
+ imageSet.missingFrames = isReconstructable.missingFrames;
3870
+ }*/
3871
+
3872
+ return imageSet;
3873
+ };
3874
+ const isSingleImageModality = modality => {
3875
+ return modality === 'CR' || modality === 'MG' || modality === 'DX';
3876
+ };
3877
+ function getSopClassUids(instances) {
3878
+ const uniqueSopClassUidsInSeries = new Set();
3879
+ instances.forEach(instance => {
3880
+ uniqueSopClassUidsInSeries.add(instance.SOPClassUID);
3881
+ });
3882
+ const sopClassUids = Array.from(uniqueSopClassUidsInSeries);
3883
+ return sopClassUids;
3884
+ }
3885
+
3886
+ /**
3887
+ * Basic SOPClassHandler:
3888
+ * - For all Image types that are stackable, create
3889
+ * a displaySet with a stack of images
3890
+ *
3891
+ * @param {Array} sopClassHandlerModules List of SOP Class Modules
3892
+ * @param {SeriesMetadata} series The series metadata object from which the display sets will be created
3893
+ * @returns {Array} The list of display sets created for the given series object
3894
+ */
3895
+ function getDisplaySetsFromSeries(instances) {
3896
+ // If the series has no instances, stop here
3897
+ if (!instances || !instances.length) {
3898
+ throw new Error('No instances were provided');
3899
+ }
3900
+ const displaySets = [];
3901
+ const sopClassUids = getSopClassUids(instances);
3902
+
3903
+ // Search through the instances (InstanceMetadata object) of this series
3904
+ // Split Multi-frame instances and Single-image modalities
3905
+ // into their own specific display sets. Place the rest of each
3906
+ // series into another display set.
3907
+ const stackableInstances = [];
3908
+ instances.forEach(instance => {
3909
+ // All imaging modalities must have a valid value for sopClassUid (x00080016) or rows (x00280010)
3519
3910
  if (!(0,isImage/* isImage */.O)(instance.SOPClassUID) && !instance.Rows) {
3520
3911
  return;
3521
3912
  }
@@ -3558,6 +3949,10 @@ function getSopClassHandlerModule() {
3558
3949
  name: sopClassHandlerName,
3559
3950
  sopClassUids,
3560
3951
  getDisplaySetsFromSeries
3952
+ }, {
3953
+ name: 'not-supported-display-sets-handler',
3954
+ sopClassUids: [],
3955
+ getDisplaySetsFromSeries: getDisplaySetsFromUnsupportedSeries
3561
3956
  }];
3562
3957
  }
3563
3958
  /* harmony default export */ const src_getSopClassHandlerModule = (getSopClassHandlerModule);
@@ -3856,7 +4251,7 @@ function adaptItem(item, subProps) {
3856
4251
  return newItem;
3857
4252
  }
3858
4253
  // EXTERNAL MODULE: ../../ui/src/components/ContextMenu/ContextMenu.tsx
3859
- var ContextMenu = __webpack_require__(63021);
4254
+ var ContextMenu = __webpack_require__(5638);
3860
4255
  ;// CONCATENATED MODULE: ../../../extensions/default/src/CustomizableContextMenu/ContextMenuController.tsx
3861
4256
 
3862
4257
 
@@ -4065,10 +4460,10 @@ const defaultContextMenu = {
4065
4460
 
4066
4461
 
4067
4462
  // EXTERNAL MODULE: ../../../node_modules/moment/moment.js
4068
- var moment = __webpack_require__(53806);
4463
+ var moment = __webpack_require__(71271);
4069
4464
  var moment_default = /*#__PURE__*/__webpack_require__.n(moment);
4070
4465
  // EXTERNAL MODULE: ../../../node_modules/react-window/dist/index.esm.js
4071
- var index_esm = __webpack_require__(21767);
4466
+ var index_esm = __webpack_require__(94614);
4072
4467
  ;// CONCATENATED MODULE: ../../../extensions/default/src/DicomTagBrowser/DicomTagTable.tsx
4073
4468
 
4074
4469
 
@@ -4266,7 +4661,6 @@ function DicomTagTable(_ref2) {
4266
4661
 
4267
4662
 
4268
4663
 
4269
-
4270
4664
  const {
4271
4665
  ImageSet: DicomTagBrowser_ImageSet
4272
4666
  } = src.classes;
@@ -4295,7 +4689,6 @@ const DicomTagBrowser = _ref => {
4295
4689
  setSelectedDisplaySetInstanceUID(value.value);
4296
4690
  setInstanceNumber(1);
4297
4691
  };
4298
- const searchInputRef = (0,react.useRef)(null);
4299
4692
  const activeDisplaySet = displaySets.find(ds => ds.displaySetInstanceUID === selectedDisplaySetInstanceUID);
4300
4693
  const isImageStack = _isImageStack(activeDisplaySet);
4301
4694
  const showInstanceList = isImageStack && activeDisplaySet.images.length > 1;
@@ -4399,29 +4792,11 @@ const DicomTagBrowser = _ref => {
4399
4792
  className: "w-full h-1 bg-black"
4400
4793
  }), /*#__PURE__*/react.createElement("div", {
4401
4794
  className: "flex flex-row my-3 w-1/2"
4402
- }, /*#__PURE__*/react.createElement("label", {
4403
- className: "relative block w-full mr-8"
4404
- }, /*#__PURE__*/react.createElement("span", {
4405
- className: "absolute inset-y-0 left-0 flex items-center pl-2"
4406
- }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
4407
- name: "icon-search"
4408
- })), /*#__PURE__*/react.createElement("input", {
4409
- ref: searchInputRef,
4410
- type: "text",
4411
- className: "block bg-black w-full shadow transition duration-300 appearance-none border border-inputfield-main focus:border-inputfield-focus focus:outline-none disabled:border-inputfield-disabled rounded w-full py-2 px-9 text-base leading-tight placeholder:text-inputfield-placeholder",
4795
+ }, /*#__PURE__*/react.createElement(ui_src/* InputFilterText */.Xe, {
4796
+ className: "block w-full mr-8",
4412
4797
  placeholder: "Search metadata...",
4413
- onChange: event => debouncedSetFilterValue(event.target.value),
4414
- autoComplete: "off"
4415
- }), /*#__PURE__*/react.createElement("span", {
4416
- className: "absolute inset-y-0 right-0 flex items-center pr-2"
4417
- }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
4418
- name: "icon-clear-field",
4419
- className: classnames_default()('cursor-pointer', filterValue ? '' : 'hidden'),
4420
- onClick: () => {
4421
- searchInputRef.current.value = '';
4422
- debouncedSetFilterValue('');
4423
- }
4424
- })))), /*#__PURE__*/react.createElement(DicomTagBrowser_DicomTagTable, {
4798
+ onDebounceChange: setFilterValue
4799
+ })), /*#__PURE__*/react.createElement(DicomTagBrowser_DicomTagTable, {
4425
4800
  rows: filteredRows
4426
4801
  }));
4427
4802
  };
@@ -4637,10 +5012,14 @@ const reuseCachedLayout = (state, hangingProtocolService, syncService) => {
4637
5012
  displaySetOptions,
4638
5013
  displaySetInstanceUIDs
4639
5014
  } = viewport;
4640
- if (!displaySetOptions) continue;
5015
+ if (!displaySetOptions) {
5016
+ continue;
5017
+ }
4641
5018
  for (let i = 0; i < displaySetOptions.length; i++) {
4642
5019
  const displaySetUID = displaySetInstanceUIDs[i];
4643
- if (!displaySetUID) continue;
5020
+ if (!displaySetUID) {
5021
+ continue;
5022
+ }
4644
5023
  if (idx === activeViewportIndex && i === 0) {
4645
5024
  displaySetSelectorMap[`${activeStudyUID}:activeDisplaySet:0`] = displaySetUID;
4646
5025
  }
@@ -4673,9 +5052,11 @@ const reuseCachedLayout = (state, hangingProtocolService, syncService) => {
4673
5052
  */
4674
5053
  const findViewportsByPosition_findOrCreateViewport = (hangingProtocolService, viewportsByPosition, viewportIndex, positionId, options) => {
4675
5054
  const byPositionViewport = viewportsByPosition?.[positionId];
4676
- if (byPositionViewport) return {
4677
- ...byPositionViewport
4678
- };
5055
+ if (byPositionViewport) {
5056
+ return {
5057
+ ...byPositionViewport
5058
+ };
5059
+ }
4679
5060
  const {
4680
5061
  protocolId,
4681
5062
  stageIndex
@@ -4756,8 +5137,8 @@ const findViewportsByPosition = (state, _ref, syncService) => {
4756
5137
  };
4757
5138
  };
4758
5139
  /* harmony default export */ const src_findViewportsByPosition = (findViewportsByPosition);
4759
- // EXTERNAL MODULE: ./index.js + 32 modules
4760
- var index = __webpack_require__(97837);
5140
+ // EXTERNAL MODULE: ./index.js + 33 modules
5141
+ var index = __webpack_require__(59754);
4761
5142
  ;// CONCATENATED MODULE: ../../../extensions/default/src/commandsModule.ts
4762
5143
 
4763
5144
 
@@ -4859,7 +5240,9 @@ const commandsModule = _ref => {
4859
5240
  stage
4860
5241
  } = hangingProtocolService.getActiveProtocol();
4861
5242
  const enableListener = button => {
4862
- if (!button.id) return;
5243
+ if (!button.id) {
5244
+ return;
5245
+ }
4863
5246
  const {
4864
5247
  commands,
4865
5248
  items
@@ -4868,7 +5251,9 @@ const commandsModule = _ref => {
4868
5251
  items.forEach(enableListener);
4869
5252
  }
4870
5253
  const hpCommand = commands?.find?.(isHangingProtocolCommand);
4871
- if (!hpCommand) return;
5254
+ if (!hpCommand) {
5255
+ return;
5256
+ }
4872
5257
  const {
4873
5258
  protocolId,
4874
5259
  stageIndex,
@@ -5397,7 +5782,6 @@ const commandsModule = _ref => {
5397
5782
  * It is not included in the viewer mode by default.
5398
5783
  */
5399
5784
  const hpMN = {
5400
- hasUpdatedPriorsInformation: false,
5401
5785
  id: '@ohif/mnGrid',
5402
5786
  description: 'Has various hanging protocol grid layouts',
5403
5787
  name: '2x2',
@@ -5606,15 +5990,182 @@ const hpMN = {
5606
5990
  numberOfPriorsReferenced: -1
5607
5991
  };
5608
5992
  /* harmony default export */ const hpMNGrid = (hpMN);
5993
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/hpCompare.ts
5994
+ const defaultDisplaySetSelector = {
5995
+ studyMatchingRules: [{
5996
+ // The priorInstance is a study counter that indicates what position this study is in
5997
+ // and the value comes from the options parameter.
5998
+ attribute: 'studyInstanceUIDsIndex',
5999
+ from: 'options',
6000
+ required: true,
6001
+ constraint: {
6002
+ equals: {
6003
+ value: 0
6004
+ }
6005
+ }
6006
+ }],
6007
+ seriesMatchingRules: [{
6008
+ attribute: 'numImageFrames',
6009
+ constraint: {
6010
+ greaterThan: {
6011
+ value: 0
6012
+ }
6013
+ }
6014
+ },
6015
+ // This display set will select the specified items by preference
6016
+ // It has no affect if nothing is specified in the URL.
6017
+ {
6018
+ attribute: 'isDisplaySetFromUrl',
6019
+ weight: 10,
6020
+ constraint: {
6021
+ equals: true
6022
+ }
6023
+ }]
6024
+ };
6025
+ const priorDisplaySetSelector = {
6026
+ studyMatchingRules: [{
6027
+ // The priorInstance is a study counter that indicates what position this study is in
6028
+ // and the value comes from the options parameter.
6029
+ attribute: 'studyInstanceUIDsIndex',
6030
+ from: 'options',
6031
+ required: true,
6032
+ constraint: {
6033
+ equals: {
6034
+ value: 1
6035
+ }
6036
+ }
6037
+ }],
6038
+ seriesMatchingRules: [{
6039
+ attribute: 'numImageFrames',
6040
+ constraint: {
6041
+ greaterThan: {
6042
+ value: 0
6043
+ }
6044
+ }
6045
+ },
6046
+ // This display set will select the specified items by preference
6047
+ // It has no affect if nothing is specified in the URL.
6048
+ {
6049
+ attribute: 'isDisplaySetFromUrl',
6050
+ weight: 10,
6051
+ constraint: {
6052
+ equals: true
6053
+ }
6054
+ }]
6055
+ };
6056
+ const currentDisplaySet = {
6057
+ id: 'defaultDisplaySetId'
6058
+ };
6059
+ const priorDisplaySet = {
6060
+ id: 'priorDisplaySetId'
6061
+ };
6062
+ const currentViewport0 = {
6063
+ viewportOptions: {
6064
+ toolGroupId: 'default',
6065
+ allowUnmatchedView: true
6066
+ },
6067
+ displaySets: [currentDisplaySet]
6068
+ };
6069
+ const currentViewport1 = {
6070
+ ...currentViewport0,
6071
+ displaySets: [{
6072
+ ...currentDisplaySet,
6073
+ matchedDisplaySetsIndex: 1
6074
+ }]
6075
+ };
6076
+ const priorViewport0 = {
6077
+ ...currentViewport0,
6078
+ displaySets: [priorDisplaySet]
6079
+ };
6080
+ const priorViewport1 = {
6081
+ ...priorViewport0,
6082
+ displaySets: [{
6083
+ ...priorDisplaySet,
6084
+ matchedDisplaySetsIndex: 1
6085
+ }]
6086
+ };
6087
+
6088
+ /**
6089
+ * This hanging protocol can be activated on the primary mode by directly
6090
+ * referencing it in a URL or by directly including it within a mode, e.g.:
6091
+ * `&hangingProtocolId=@ohif/mnGrid` added to the viewer URL
6092
+ * It is not included in the viewer mode by default.
6093
+ */
6094
+ const hpMNCompare = {
6095
+ id: '@ohif/hpCompare',
6096
+ description: 'Compare two studies in various layouts',
6097
+ name: 'Compare Two Studies',
6098
+ numberOfPriorsReferenced: 1,
6099
+ protocolMatchingRules: [{
6100
+ id: 'Two Studies',
6101
+ weight: 1000,
6102
+ attribute: 'StudyInstanceUID',
6103
+ // The 'from' attribute says where to get the 'attribute' value from. In this case
6104
+ // prior means the second study in the study list.
6105
+ from: 'prior',
6106
+ required: true,
6107
+ constraint: {
6108
+ notNull: true
6109
+ }
6110
+ }],
6111
+ toolGroupIds: ['default'],
6112
+ displaySetSelectors: {
6113
+ defaultDisplaySetId: defaultDisplaySetSelector,
6114
+ priorDisplaySetId: priorDisplaySetSelector
6115
+ },
6116
+ defaultViewport: {
6117
+ viewportOptions: {
6118
+ viewportType: 'stack',
6119
+ toolGroupId: 'default',
6120
+ allowUnmatchedView: true
6121
+ },
6122
+ displaySets: [{
6123
+ id: 'defaultDisplaySetId',
6124
+ matchedDisplaySetsIndex: -1
6125
+ }]
6126
+ },
6127
+ stages: [{
6128
+ name: '2x2',
6129
+ stageActivation: {
6130
+ enabled: {
6131
+ minViewportsMatched: 4
6132
+ }
6133
+ },
6134
+ viewportStructure: {
6135
+ layoutType: 'grid',
6136
+ properties: {
6137
+ rows: 2,
6138
+ columns: 2
6139
+ }
6140
+ },
6141
+ viewports: [currentViewport0, priorViewport0, currentViewport1, priorViewport1]
6142
+ }, {
6143
+ name: '2x1',
6144
+ stageActivation: {
6145
+ enabled: {
6146
+ minViewportsMatched: 2
6147
+ }
6148
+ },
6149
+ viewportStructure: {
6150
+ layoutType: 'grid',
6151
+ properties: {
6152
+ rows: 1,
6153
+ columns: 2
6154
+ }
6155
+ },
6156
+ viewports: [currentViewport0, priorViewport0]
6157
+ }]
6158
+ };
6159
+ /* harmony default export */ const hpCompare = (hpMNCompare);
5609
6160
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getHangingProtocolModule.js
5610
6161
 
6162
+
5611
6163
  const defaultProtocol = {
5612
6164
  id: 'default',
5613
6165
  locked: true,
5614
6166
  // Don't store this hanging protocol as it applies to the currently active
5615
6167
  // display set by default
5616
6168
  // cacheId: null,
5617
- hasUpdatedPriorsInformation: false,
5618
6169
  name: 'Default',
5619
6170
  createdDate: '2021-02-23T19:22:08.894Z',
5620
6171
  modifiedDate: '2023-04-01',
@@ -5708,6 +6259,11 @@ function getHangingProtocolModule() {
5708
6259
  {
5709
6260
  name: hpMNGrid.id,
5710
6261
  protocol: hpMNGrid
6262
+ },
6263
+ // Create a MxN comparison hanging protocol available by default
6264
+ {
6265
+ name: hpCompare.id,
6266
+ protocol: hpCompare
5711
6267
  }];
5712
6268
  }
5713
6269
  /* harmony default export */ const src_getHangingProtocolModule = (getHangingProtocolModule);
@@ -5743,7 +6299,7 @@ function DataSourceSelector() {
5743
6299
  key: ds.sourceName
5744
6300
  }, /*#__PURE__*/react.createElement("h1", {
5745
6301
  className: "text-white"
5746
- }, ds.friendlyName), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
6302
+ }, ds.configuration?.friendlyName || ds.friendlyName), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
5747
6303
  type: ui_src/* ButtonEnums.type */.LZ.U.primary,
5748
6304
  className: classnames_default()('ml-2'),
5749
6305
  onClick: () => {
@@ -5755,10 +6311,419 @@ function DataSourceSelector() {
5755
6311
  }, ds.sourceName), /*#__PURE__*/react.createElement("br", null)))))));
5756
6312
  }
5757
6313
  /* harmony default export */ const Panels_DataSourceSelector = (DataSourceSelector);
6314
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/ItemListComponent.tsx
6315
+
6316
+
6317
+
6318
+
6319
+ function ItemListComponent(_ref) {
6320
+ let {
6321
+ itemLabel,
6322
+ itemList,
6323
+ onItemClicked
6324
+ } = _ref;
6325
+ const {
6326
+ t
6327
+ } = (0,es/* useTranslation */.$G)('DataSourceConfiguration');
6328
+ const [filterValue, setFilterValue] = (0,react.useState)('');
6329
+ (0,react.useEffect)(() => {
6330
+ setFilterValue('');
6331
+ }, [itemList]);
6332
+ return /*#__PURE__*/react.createElement("div", {
6333
+ className: "flex flex-col gap-4 min-h-[1px] grow"
6334
+ }, /*#__PURE__*/react.createElement("div", {
6335
+ className: "flex justify-between items-center"
6336
+ }, /*#__PURE__*/react.createElement("div", {
6337
+ className: "text-primary-light text-[20px]"
6338
+ }, t(`Select ${itemLabel}`)), /*#__PURE__*/react.createElement(ui_src/* InputFilterText */.Xe, {
6339
+ className: "grow max-w-[40%]",
6340
+ value: filterValue,
6341
+ onDebounceChange: setFilterValue,
6342
+ placeholder: t(`Search ${itemLabel} list`)
6343
+ })), /*#__PURE__*/react.createElement("div", {
6344
+ className: "flex flex-col relative min-h-[1px] grow text-[14px] bg-black"
6345
+ }, itemList == null ? /*#__PURE__*/react.createElement(ui_src/* LoadingIndicatorProgress */.LE, {
6346
+ className: 'w-full h-full'
6347
+ }) : itemList.length === 0 ? /*#__PURE__*/react.createElement("div", {
6348
+ className: "flex flex-col h-full px-6 py-4 items-center justify-center text-primary-light"
6349
+ }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6350
+ name: "magnifier",
6351
+ className: "mb-4"
6352
+ }), /*#__PURE__*/react.createElement("span", null, t(`No ${itemLabel} available`))) : /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("div", {
6353
+ className: "bg-secondary-dark text-white px-3 py-1.5"
6354
+ }, t(itemLabel)), /*#__PURE__*/react.createElement("div", {
6355
+ className: "overflow-auto ohif-scrollbar"
6356
+ }, itemList.filter(item => !filterValue || item.name.toLowerCase().includes(filterValue.toLowerCase())).map(item => {
6357
+ const border = 'rounded border-transparent border-b-secondary-light border-[1px] hover:border-primary-light';
6358
+ return /*#__PURE__*/react.createElement("div", {
6359
+ className: classnames_default()('group mx-2 px-6 py-2 flex justify-between items-center hover:text-primary-light hover:bg-primary-dark', border),
6360
+ key: item.id
6361
+ }, /*#__PURE__*/react.createElement("div", null, item.name), /*#__PURE__*/react.createElement(ui_src/* Button */.zx, {
6362
+ onClick: () => onItemClicked(item),
6363
+ className: "invisible group-hover:visible",
6364
+ endIcon: /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6365
+ name: "arrow-left"
6366
+ })
6367
+ }, t('Select')));
6368
+ })))));
6369
+ }
6370
+ /* harmony default export */ const Components_ItemListComponent = (ItemListComponent);
6371
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx
6372
+
6373
+
6374
+
6375
+
6376
+
6377
+ const NO_WRAP_ELLIPSIS_CLASS_NAMES = 'text-ellipsis whitespace-nowrap overflow-hidden';
6378
+ function DataSourceConfigurationModalComponent(_ref) {
6379
+ let {
6380
+ configurationAPI,
6381
+ configuredItems,
6382
+ onHide
6383
+ } = _ref;
6384
+ const {
6385
+ t
6386
+ } = (0,es/* useTranslation */.$G)('DataSourceConfiguration');
6387
+ const [itemList, setItemList] = (0,react.useState)();
6388
+ const [selectedItems, setSelectedItems] = (0,react.useState)(configuredItems);
6389
+
6390
+ // Determines whether to show the full configuration for the data source.
6391
+ // This typically occurs when the configuration component is first displayed.
6392
+ const [showFullConfig, setShowFullConfig] = (0,react.useState)(true);
6393
+ const [errorMessage, setErrorMessage] = (0,react.useState)();
6394
+ const [itemLabels] = (0,react.useState)(configurationAPI.getItemLabels());
6395
+
6396
+ /**
6397
+ * The index of the selected item that is considered current and for which
6398
+ * its sub-items should be displayed in the items list component. When the
6399
+ * full/existing configuration for a data source is to be shown, the current
6400
+ * selected item is the second to last in the `selectedItems` list.
6401
+ */
6402
+ const currentSelectedItemIndex = showFullConfig ? selectedItems.length - 2 : selectedItems.length - 1;
6403
+ (0,react.useEffect)(() => {
6404
+ let shouldUpdate = true;
6405
+ setErrorMessage(null);
6406
+
6407
+ // Clear out the former/old list while we fetch the next sub item list.
6408
+ setItemList(null);
6409
+ if (selectedItems.length === 0) {
6410
+ configurationAPI.initialize().then(items => {
6411
+ if (shouldUpdate) {
6412
+ setItemList(items);
6413
+ }
6414
+ }).catch(error => setErrorMessage(error.message));
6415
+ } else if (!showFullConfig && selectedItems.length === itemLabels.length) {
6416
+ // The last item to configure the data source (path) has been selected.
6417
+ configurationAPI.setCurrentItem(selectedItems[selectedItems.length - 1]);
6418
+ // We can hide the modal dialog now.
6419
+ onHide();
6420
+ } else {
6421
+ configurationAPI.setCurrentItem(selectedItems[currentSelectedItemIndex]).then(items => {
6422
+ if (shouldUpdate) {
6423
+ setItemList(items);
6424
+ }
6425
+ }).catch(error => setErrorMessage(error.message));
6426
+ }
6427
+ return () => {
6428
+ shouldUpdate = false;
6429
+ };
6430
+ }, [selectedItems, configurationAPI, onHide, itemLabels, showFullConfig, currentSelectedItemIndex]);
6431
+ const getSelectedItemCursorClasses = itemIndex => itemIndex !== itemLabels.length - 1 && itemIndex < selectedItems.length ? 'cursor-pointer' : 'cursor-auto';
6432
+ const getSelectedItemBackgroundClasses = itemIndex => itemIndex < selectedItems.length ? classnames_default()('bg-black/[.4]', itemIndex !== itemLabels.length - 1 ? 'hover:bg-transparent active:bg-secondary-dark' : '') : 'bg-transparent';
6433
+ const getSelectedItemBorderClasses = itemIndex => itemIndex === currentSelectedItemIndex + 1 ? classnames_default()('border-2', 'border-solid', 'border-primary-light') : itemIndex < selectedItems.length ? 'border border-solid border-primary-active hover:border-primary-light active:border-white' : 'border border-dashed border-secondary-light';
6434
+ const getSelectedItemTextClasses = itemIndex => itemIndex <= selectedItems.length ? 'text-primary-light' : 'text-primary-active';
6435
+ const getErrorComponent = () => {
6436
+ return /*#__PURE__*/react.createElement("div", {
6437
+ className: "flex flex-col gap-4 min-h-[1px] grow"
6438
+ }, /*#__PURE__*/react.createElement("div", {
6439
+ className: "text-primary-light text-[20px]"
6440
+ }, t(`Error fetching ${itemLabels[selectedItems.length]} list`)), /*#__PURE__*/react.createElement("div", {
6441
+ className: "bg-black text-[14px] grow p-4"
6442
+ }, errorMessage));
6443
+ };
6444
+ const getSelectedItemsComponent = () => {
6445
+ return /*#__PURE__*/react.createElement("div", {
6446
+ className: "flex gap-4"
6447
+ }, itemLabels.map((itemLabel, itemLabelIndex) => {
6448
+ return /*#__PURE__*/react.createElement("div", {
6449
+ key: itemLabel,
6450
+ className: classnames_default()('rounded-md p-3.5 flex flex-col gap-1 shrink min-w-[1px] basis-[200px]', getSelectedItemCursorClasses(itemLabelIndex), getSelectedItemBackgroundClasses(itemLabelIndex), getSelectedItemBorderClasses(itemLabelIndex), getSelectedItemTextClasses(itemLabelIndex)),
6451
+ onClick: showFullConfig && itemLabelIndex < currentSelectedItemIndex || itemLabelIndex <= currentSelectedItemIndex ? () => {
6452
+ setShowFullConfig(false);
6453
+ setSelectedItems(theList => theList.slice(0, itemLabelIndex));
6454
+ } : undefined
6455
+ }, /*#__PURE__*/react.createElement("div", {
6456
+ className: "flex gap-2 items-center text-"
6457
+ }, itemLabelIndex < selectedItems.length ? /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6458
+ name: "status-tracked"
6459
+ }) : /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6460
+ name: "status-untracked"
6461
+ }), /*#__PURE__*/react.createElement("div", {
6462
+ className: classnames_default()(NO_WRAP_ELLIPSIS_CLASS_NAMES)
6463
+ }, t(itemLabel))), itemLabelIndex < selectedItems.length ? /*#__PURE__*/react.createElement("div", {
6464
+ className: classnames_default()('text-white text-[14px]', NO_WRAP_ELLIPSIS_CLASS_NAMES)
6465
+ }, selectedItems[itemLabelIndex].name) : /*#__PURE__*/react.createElement("br", null));
6466
+ }));
6467
+ };
6468
+ return /*#__PURE__*/react.createElement("div", {
6469
+ className: "h-[calc(100vh-300px)] flex flex-col pt-0.5 gap-4 select-none"
6470
+ }, getSelectedItemsComponent(), /*#__PURE__*/react.createElement("div", {
6471
+ className: "w-full h-0.5 shrink-0 bg-black"
6472
+ }), errorMessage ? getErrorComponent() : /*#__PURE__*/react.createElement(Components_ItemListComponent, {
6473
+ itemLabel: itemLabels[currentSelectedItemIndex + 1],
6474
+ itemList: itemList,
6475
+ onItemClicked: item => {
6476
+ setShowFullConfig(false);
6477
+ setSelectedItems(theList => [...theList.slice(0, currentSelectedItemIndex + 1), item]);
6478
+ }
6479
+ }));
6480
+ }
6481
+ /* harmony default export */ const Components_DataSourceConfigurationModalComponent = (DataSourceConfigurationModalComponent);
6482
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/Components/DataSourceConfigurationComponent.tsx
6483
+
6484
+
6485
+
6486
+
6487
+ function DataSourceConfigurationComponent(_ref) {
6488
+ let {
6489
+ servicesManager,
6490
+ extensionManager
6491
+ } = _ref;
6492
+ const {
6493
+ t
6494
+ } = (0,es/* useTranslation */.$G)('DataSourceConfiguration');
6495
+ const {
6496
+ show,
6497
+ hide
6498
+ } = (0,ui_src/* useModal */.dd)();
6499
+ const {
6500
+ customizationService
6501
+ } = servicesManager.services;
6502
+ const [configurationAPI, setConfigurationAPI] = (0,react.useState)();
6503
+ const [configuredItems, setConfiguredItems] = (0,react.useState)();
6504
+ (0,react.useEffect)(() => {
6505
+ let shouldUpdate = true;
6506
+ const dataSourceChangedCallback = async () => {
6507
+ const activeDataSourceDef = extensionManager.getActiveDataSourceDefinition();
6508
+ if (!activeDataSourceDef.configuration.configurationAPI) {
6509
+ return;
6510
+ }
6511
+ const {
6512
+ factory: configurationAPIFactory
6513
+ } = customizationService.get(activeDataSourceDef.configuration.configurationAPI) ?? {};
6514
+ if (!configurationAPIFactory) {
6515
+ return;
6516
+ }
6517
+ const configAPI = configurationAPIFactory(activeDataSourceDef.sourceName);
6518
+ setConfigurationAPI(configAPI);
6519
+ configAPI.getConfiguredItems().then(list => {
6520
+ if (shouldUpdate) {
6521
+ setConfiguredItems(list);
6522
+ }
6523
+ });
6524
+ };
6525
+ const sub = extensionManager.subscribe(extensionManager.EVENTS.ACTIVE_DATA_SOURCE_CHANGED, dataSourceChangedCallback);
6526
+ dataSourceChangedCallback();
6527
+ return () => {
6528
+ shouldUpdate = false;
6529
+ sub.unsubscribe();
6530
+ };
6531
+ }, []);
6532
+ return configuredItems ? /*#__PURE__*/react.createElement("div", {
6533
+ className: "flex text-aqua-pale overflow-hidden items-center"
6534
+ }, /*#__PURE__*/react.createElement(ui_src/* Icon */.JO, {
6535
+ name: "settings",
6536
+ className: "cursor-pointer shrink-0 w-3.5 h-3.5 mr-2.5",
6537
+ onClick: () => show({
6538
+ content: Components_DataSourceConfigurationModalComponent,
6539
+ title: t('Configure Data Source'),
6540
+ contentProps: {
6541
+ configurationAPI,
6542
+ configuredItems,
6543
+ onHide: hide
6544
+ }
6545
+ })
6546
+ }), configuredItems.map((item, itemIndex) => {
6547
+ return /*#__PURE__*/react.createElement("div", {
6548
+ key: itemIndex,
6549
+ className: "flex overflow-hidden"
6550
+ }, /*#__PURE__*/react.createElement("div", {
6551
+ key: itemIndex,
6552
+ className: "text-ellipsis whitespace-nowrap overflow-hidden"
6553
+ }, item.name), itemIndex !== configuredItems.length - 1 && /*#__PURE__*/react.createElement("div", {
6554
+ className: "px-2.5"
6555
+ }, "|"));
6556
+ })) : /*#__PURE__*/react.createElement(react.Fragment, null);
6557
+ }
6558
+ /* harmony default export */ const Components_DataSourceConfigurationComponent = (DataSourceConfigurationComponent);
6559
+ ;// CONCATENATED MODULE: ../../../extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts
6560
+ /**
6561
+ * This file contains the implementations of BaseDataSourceConfigurationAPIItem
6562
+ * and BaseDataSourceConfigurationAPI for the Google cloud healthcare API. To
6563
+ * better understand this implementation and/or to implement custom implementations,
6564
+ * see the platform\core\src\types\DataSourceConfigurationAPI.ts and its JS doc
6565
+ * comments as a guide.
6566
+ */
6567
+ /**
6568
+ * The various Google Cloud Healthcare path item types.
6569
+ */
6570
+ var ItemType = /*#__PURE__*/function (ItemType) {
6571
+ ItemType[ItemType["projects"] = 0] = "projects";
6572
+ ItemType[ItemType["locations"] = 1] = "locations";
6573
+ ItemType[ItemType["datasets"] = 2] = "datasets";
6574
+ ItemType[ItemType["dicomStores"] = 3] = "dicomStores";
6575
+ return ItemType;
6576
+ }(ItemType || {});
6577
+ const initialUrl = 'https://cloudresourcemanager.googleapis.com/v1';
6578
+ const baseHealthcareUrl = 'https://healthcare.googleapis.com/v1';
6579
+ class GoogleCloudDataSourceConfigurationAPIItem {
6580
+ constructor() {
6581
+ this.id = void 0;
6582
+ this.name = void 0;
6583
+ this.url = void 0;
6584
+ this.itemType = void 0;
6585
+ }
6586
+ }
6587
+ class GoogleCloudDataSourceConfigurationAPI {
6588
+ constructor(dataSourceName, servicesManager, extensionManager) {
6589
+ this._extensionManager = void 0;
6590
+ this._fetchOptions = void 0;
6591
+ this._dataSourceName = void 0;
6592
+ this.getItemLabels = () => ['Project', 'Location', 'Data set', 'DICOM store'];
6593
+ this._dataSourceName = dataSourceName;
6594
+ this._extensionManager = extensionManager;
6595
+ const userAuthenticationService = servicesManager.services.userAuthenticationService;
6596
+ this._fetchOptions = {
6597
+ method: 'GET',
6598
+ headers: userAuthenticationService.getAuthorizationHeader()
6599
+ };
6600
+ }
6601
+ async initialize() {
6602
+ const url = `${initialUrl}/projects`;
6603
+ const projects = await GoogleCloudDataSourceConfigurationAPI._doFetch(url, ItemType.projects, this._fetchOptions);
6604
+ if (!projects?.length) {
6605
+ return [];
6606
+ }
6607
+ const projectItems = projects.map(project => {
6608
+ return {
6609
+ id: project.projectId,
6610
+ name: project.name,
6611
+ itemType: ItemType.projects,
6612
+ url: `${baseHealthcareUrl}/projects/${project.projectId}`
6613
+ };
6614
+ });
6615
+ return projectItems;
6616
+ }
6617
+ async setCurrentItem(anItem) {
6618
+ const googleCloudItem = anItem;
6619
+ if (googleCloudItem.itemType === ItemType.dicomStores) {
6620
+ // Last configurable item, so update the data source configuration.
6621
+ const url = `${googleCloudItem.url}/dicomWeb`;
6622
+ const dataSourceDefCopy = JSON.parse(JSON.stringify(this._extensionManager.getDataSourceDefinition(this._dataSourceName)));
6623
+ dataSourceDefCopy.configuration = {
6624
+ ...dataSourceDefCopy.configuration,
6625
+ wadoUriRoot: url,
6626
+ qidoRoot: url,
6627
+ wadoRoot: url
6628
+ };
6629
+ this._extensionManager.updateDataSourceConfiguration(dataSourceDefCopy.sourceName, dataSourceDefCopy.configuration);
6630
+ return [];
6631
+ }
6632
+ const subItemType = googleCloudItem.itemType + 1;
6633
+ const subItemField = `${ItemType[subItemType]}`;
6634
+ const url = `${googleCloudItem.url}/${subItemField}`;
6635
+ const fetchedSubItems = await GoogleCloudDataSourceConfigurationAPI._doFetch(url, subItemType, this._fetchOptions);
6636
+ if (!fetchedSubItems?.length) {
6637
+ return [];
6638
+ }
6639
+ const subItems = fetchedSubItems.map(subItem => {
6640
+ const nameSplit = subItem.name.split('/');
6641
+ return {
6642
+ id: subItem.name,
6643
+ name: nameSplit[nameSplit.length - 1],
6644
+ itemType: subItemType,
6645
+ url: `${baseHealthcareUrl}/${subItem.name}`
6646
+ };
6647
+ });
6648
+ return subItems;
6649
+ }
6650
+ async getConfiguredItems() {
6651
+ const dataSourceDefinition = this._extensionManager.getDataSourceDefinition(this._dataSourceName);
6652
+ const url = dataSourceDefinition.configuration.wadoUriRoot;
6653
+ const projectsIndex = url.indexOf('projects');
6654
+ const urlSplit = url.substring(projectsIndex).split('/');
6655
+ const configuredItems = [];
6656
+ for (let itemType = 0; itemType < 4; itemType += 1) {
6657
+ if (itemType === ItemType.projects) {
6658
+ const projectId = urlSplit[1];
6659
+ const projectUrl = `${initialUrl}/projects/${projectId}`;
6660
+ const data = await GoogleCloudDataSourceConfigurationAPI._doFetch(projectUrl, ItemType.projects, this._fetchOptions);
6661
+ const project = data[0];
6662
+ configuredItems.push({
6663
+ id: project.projectId,
6664
+ name: project.name,
6665
+ itemType: itemType,
6666
+ url: `${baseHealthcareUrl}/projects/${project.projectId}`
6667
+ });
6668
+ } else {
6669
+ const relativePath = urlSplit.slice(0, itemType * 2 + 2).join('/');
6670
+ configuredItems.push({
6671
+ id: relativePath,
6672
+ name: urlSplit[itemType * 2 + 1],
6673
+ itemType: itemType,
6674
+ url: `${baseHealthcareUrl}/${relativePath}`
6675
+ });
6676
+ }
6677
+ }
6678
+ return configuredItems;
6679
+ }
6680
+
6681
+ /**
6682
+ * Fetches an array of items the specified item type.
6683
+ * @param urlStr the fetch url
6684
+ * @param fetchItemType the type to fetch
6685
+ * @param fetchOptions the header options for the fetch (e.g. authorization header)
6686
+ * @param fetchSearchParams any search query params; currently only used for paging results
6687
+ * @returns an array of items of the specified type
6688
+ */
6689
+ static async _doFetch(urlStr, fetchItemType) {
6690
+ let fetchOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
6691
+ let fetchSearchParams = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
6692
+ try {
6693
+ const url = new URL(urlStr);
6694
+ url.search = new URLSearchParams(fetchSearchParams).toString();
6695
+ const response = await fetch(url, fetchOptions);
6696
+ const data = await response.json();
6697
+ if (response.status >= 200 && response.status < 300 && data != null) {
6698
+ if (data.nextPageToken != null) {
6699
+ fetchSearchParams.pageToken = data.nextPageToken;
6700
+ const subPageData = await this._doFetch(urlStr, fetchItemType, fetchOptions, fetchSearchParams);
6701
+ data[ItemType[fetchItemType]] = data[ItemType[fetchItemType]].concat(subPageData);
6702
+ }
6703
+ if (data[ItemType[fetchItemType]]) {
6704
+ return data[ItemType[fetchItemType]];
6705
+ } else if (data.name) {
6706
+ return [data];
6707
+ } else {
6708
+ return [];
6709
+ }
6710
+ } else {
6711
+ const message = data?.error?.message || `Error returned from Google Cloud Healthcare: ${response.status} - ${response.statusText}`;
6712
+ throw new Error(message);
6713
+ }
6714
+ } catch (err) {
6715
+ const message = err?.message || 'Error occurred during fetch request.';
6716
+ throw new Error(message);
6717
+ }
6718
+ }
6719
+ }
6720
+
5758
6721
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getCustomizationModule.tsx
5759
6722
 
5760
6723
 
5761
6724
 
6725
+
6726
+
5762
6727
  /**
5763
6728
  *
5764
6729
  * Note: this is an example of how the customization module can be used
@@ -5768,7 +6733,11 @@ function DataSourceSelector() {
5768
6733
  * custom page for the user to view their profile, or to add a custom
5769
6734
  * page for login etc.
5770
6735
  */
5771
- function getCustomizationModule() {
6736
+ function getCustomizationModule(_ref) {
6737
+ let {
6738
+ servicesManager,
6739
+ extensionManager
6740
+ } = _ref;
5772
6741
  return [{
5773
6742
  name: 'helloPage',
5774
6743
  value: {
@@ -5835,12 +6804,16 @@ function getCustomizationModule() {
5835
6804
  {
5836
6805
  id: 'ohif.overlayItem',
5837
6806
  content: function (props) {
5838
- if (this.condition && !this.condition(props)) return null;
6807
+ if (this.condition && !this.condition(props)) {
6808
+ return null;
6809
+ }
5839
6810
  const {
5840
6811
  instance
5841
6812
  } = props;
5842
6813
  const value = instance && this.attribute ? instance[this.attribute] : this.contentF && typeof this.contentF === 'function' ? this.contentF(props) : null;
5843
- if (!value) return null;
6814
+ if (!value) {
6815
+ return null;
6816
+ }
5844
6817
  return /*#__PURE__*/react.createElement("span", {
5845
6818
  className: "overlay-item flex flex-row",
5846
6819
  style: {
@@ -5878,11 +6851,22 @@ function getCustomizationModule() {
5878
6851
  }
5879
6852
  return clonedObject;
5880
6853
  }
6854
+ }, {
6855
+ // the generic GUI component to configure a data source using an instance of a BaseDataSourceConfigurationAPI
6856
+ id: 'ohif.dataSourceConfigurationComponent',
6857
+ component: Components_DataSourceConfigurationComponent.bind(null, {
6858
+ servicesManager,
6859
+ extensionManager
6860
+ })
6861
+ }, {
6862
+ // The factory for creating an instance of a BaseDataSourceConfigurationAPI for Google Cloud Healthcare
6863
+ id: 'ohif.dataSourceConfigurationAPI.google',
6864
+ factory: dataSourceName => new GoogleCloudDataSourceConfigurationAPI(dataSourceName, servicesManager, extensionManager)
5881
6865
  }]
5882
6866
  }];
5883
6867
  }
5884
6868
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/calculate-suv/dist/calculate-suv.esm.js
5885
- var calculate_suv_esm = __webpack_require__(71251);
6869
+ var calculate_suv_esm = __webpack_require__(15747);
5886
6870
  ;// CONCATENATED MODULE: ../../../extensions/default/src/getPTImageIdInstanceMetadata.ts
5887
6871
 
5888
6872
  const getPTImageIdInstanceMetadata_metadataProvider = src["default"].classes.MetadataProvider;
@@ -5891,9 +6875,12 @@ function getPTImageIdInstanceMetadata(imageId) {
5891
6875
  if (!dicomMetaData) {
5892
6876
  throw new Error('dicom metadata are required');
5893
6877
  }
5894
- if (dicomMetaData.SeriesDate === undefined || dicomMetaData.SeriesTime === undefined || dicomMetaData.PatientWeight === undefined || dicomMetaData.CorrectedImage === undefined || dicomMetaData.Units === undefined || !dicomMetaData.RadiopharmaceuticalInformationSequence || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideHalfLife === undefined || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideTotalDose === undefined || dicomMetaData.DecayCorrection === undefined || dicomMetaData.AcquisitionDate === undefined || dicomMetaData.AcquisitionTime === undefined || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartDateTime === undefined && dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartTime === undefined) {
6878
+ if (dicomMetaData.SeriesDate === undefined || dicomMetaData.SeriesTime === undefined || dicomMetaData.CorrectedImage === undefined || dicomMetaData.Units === undefined || !dicomMetaData.RadiopharmaceuticalInformationSequence || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideHalfLife === undefined || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideTotalDose === undefined || dicomMetaData.DecayCorrection === undefined || dicomMetaData.AcquisitionDate === undefined || dicomMetaData.AcquisitionTime === undefined || dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartDateTime === undefined && dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartTime === undefined) {
5895
6879
  throw new Error('required metadata are missing');
5896
6880
  }
6881
+ if (dicomMetaData.PatientWeight === undefined) {
6882
+ console.warn('PatientWeight missing from PT instance metadata');
6883
+ }
5897
6884
  const instanceMetadata = {
5898
6885
  CorrectedImage: dicomMetaData.CorrectedImage,
5899
6886
  Units: dicomMetaData.Units,