diff --git a/.docker/Viewer-v3.x/default.conf.template b/.docker/Viewer-v3.x/default.conf.template
index 153ea7086..11206da2a 100644
--- a/.docker/Viewer-v3.x/default.conf.template
+++ b/.docker/Viewer-v3.x/default.conf.template
@@ -5,8 +5,6 @@ server {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
- add_header Cross-Origin-Opener-Policy same-origin;
- add_header Cross-Origin-Embedder-Policy require-corp;
add_header Cross-Origin-Resource-Policy same-origin;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
diff --git a/.docker/Viewer-v3.x/default.ssl.conf.template b/.docker/Viewer-v3.x/default.ssl.conf.template
index 06ed4e505..04a36da8b 100644
--- a/.docker/Viewer-v3.x/default.ssl.conf.template
+++ b/.docker/Viewer-v3.x/default.ssl.conf.template
@@ -7,8 +7,6 @@ server {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
- add_header Cross-Origin-Opener-Policy same-origin;
- add_header Cross-Origin-Embedder-Policy require-corp;
add_header Cross-Origin-Resource-Policy same-origin;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
diff --git a/.webpack/webpack.base.js b/.webpack/webpack.base.js
index 440c26c95..025c8bd13 100644
--- a/.webpack/webpack.base.js
+++ b/.webpack/webpack.base.js
@@ -57,10 +57,6 @@ if (!process.env.APP_CONFIG) {
}
module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
- if (!process.env.NODE_ENV) {
- throw new Error('process.env.NODE_ENV not set');
- }
-
const mode = NODE_ENV === 'production' ? 'production' : 'development';
const isProdBuild = NODE_ENV === 'production';
const isQuickBuild = QUICK_BUILD === 'true';
@@ -108,7 +104,7 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
exclude: /node_modules/,
loader: 'babel-loader',
options: {
- plugins: ['react-refresh/babel'],
+ plugins: isProdBuild ? [] : ['react-refresh/babel'],
},
},
]),
diff --git a/extensions/cornerstone-dicom-pmap/package.json b/extensions/cornerstone-dicom-pmap/package.json
index 1a9ca0469..4f8996285 100644
--- a/extensions/cornerstone-dicom-pmap/package.json
+++ b/extensions/cornerstone-dicom-pmap/package.json
@@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^2.2.13",
- "@cornerstonejs/core": "^2.2.13",
+ "@cornerstonejs/adapters": "^2.2.20",
+ "@cornerstonejs/core": "^2.2.20",
"@kitware/vtk.js": "32.1.1",
"react-color": "^2.19.3"
}
diff --git a/extensions/cornerstone-dicom-seg/package.json b/extensions/cornerstone-dicom-seg/package.json
index 84b4645e9..860ec946a 100644
--- a/extensions/cornerstone-dicom-seg/package.json
+++ b/extensions/cornerstone-dicom-seg/package.json
@@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^2.2.13",
- "@cornerstonejs/core": "^2.2.13",
+ "@cornerstonejs/adapters": "^2.2.20",
+ "@cornerstonejs/core": "^2.2.20",
"@kitware/vtk.js": "32.1.1",
"react-color": "^2.19.3"
}
diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json
index d9f7b6d1f..a87a251c3 100644
--- a/extensions/cornerstone-dicom-sr/package.json
+++ b/extensions/cornerstone-dicom-sr/package.json
@@ -46,9 +46,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^2.2.13",
- "@cornerstonejs/core": "^2.2.13",
- "@cornerstonejs/tools": "^2.2.13",
+ "@cornerstonejs/adapters": "^2.2.20",
+ "@cornerstonejs/core": "^2.2.20",
+ "@cornerstonejs/tools": "^2.2.20",
"classnames": "^2.3.2"
}
}
diff --git a/extensions/cornerstone-dynamic-volume/package.json b/extensions/cornerstone-dynamic-volume/package.json
index 00ff9fc5d..dc642c619 100644
--- a/extensions/cornerstone-dynamic-volume/package.json
+++ b/extensions/cornerstone-dynamic-volume/package.json
@@ -42,8 +42,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/core": "^2.2.13",
- "@cornerstonejs/tools": "^2.2.13",
+ "@cornerstonejs/core": "^2.2.20",
+ "@cornerstonejs/tools": "^2.2.20",
"classnames": "^2.3.2"
}
}
diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json
index 437a942a5..65fc09deb 100644
--- a/extensions/cornerstone/package.json
+++ b/extensions/cornerstone/package.json
@@ -38,7 +38,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
- "@cornerstonejs/dicom-image-loader": "^2.2.13",
+ "@cornerstonejs/dicom-image-loader": "^2.2.20",
"@icr/polyseg-wasm": "^0.4.0",
"@ohif/core": "3.10.0-beta.10",
"@ohif/ui": "3.10.0-beta.10",
@@ -55,9 +55,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^2.2.13",
- "@cornerstonejs/core": "^2.2.13",
- "@cornerstonejs/tools": "^2.2.13",
+ "@cornerstonejs/adapters": "^2.2.20",
+ "@cornerstonejs/core": "^2.2.20",
+ "@cornerstonejs/tools": "^2.2.20",
"@icr/polyseg-wasm": "^0.4.0",
"@kitware/vtk.js": "32.1.1",
"html2canvas": "^1.4.1",
diff --git a/extensions/cornerstone/src/index.tsx b/extensions/cornerstone/src/index.tsx
index 47d630c10..41f4c33ff 100644
--- a/extensions/cornerstone/src/index.tsx
+++ b/extensions/cornerstone/src/index.tsx
@@ -45,7 +45,7 @@ import {
useSegmentationPresentationStore,
useSynchronizersStore,
} from './stores';
-import { useToggleOneUpViewportGridStore } from '../../default/src/stores/useToggleOneUpViewportGridStore';
+import { useToggleOneUpViewportGridStore } from '@ohif/extension-default';
import { useActiveViewportSegmentationRepresentations } from './hooks/useActiveViewportSegmentationRepresentations';
import { useMeasurements } from './hooks/useMeasurements';
import getPanelModule from './getPanelModule';
diff --git a/extensions/cornerstone/src/init.tsx b/extensions/cornerstone/src/init.tsx
index 4b9c13d54..565844169 100644
--- a/extensions/cornerstone/src/init.tsx
+++ b/extensions/cornerstone/src/init.tsx
@@ -38,6 +38,7 @@ import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
import { useLutPresentationStore } from './stores/useLutPresentationStore';
import { usePositionPresentationStore } from './stores/usePositionPresentationStore';
import { useSegmentationPresentationStore } from './stores/useSegmentationPresentationStore';
+import { imageRetrieveMetadataProvider } from '@cornerstonejs/core/utilities';
const { registerColormap } = csUtilities.colormap;
@@ -135,9 +136,21 @@ export default async function init({
cornerstoneStreamingDynamicImageVolumeLoader
);
- hangingProtocolService.registerImageLoadStrategy('interleaveCenter', interleaveCenterLoader);
- hangingProtocolService.registerImageLoadStrategy('interleaveTopToBottom', interleaveTopToBottom);
- hangingProtocolService.registerImageLoadStrategy('nth', nthLoader);
+ // Register strategies using the wrapper
+ const imageLoadStrategies = {
+ interleaveCenter: interleaveCenterLoader,
+ interleaveTopToBottom: interleaveTopToBottom,
+ nth: nthLoader,
+ };
+
+ Object.entries(imageLoadStrategies).forEach(([name, strategyFn]) => {
+ hangingProtocolService.registerImageLoadStrategy(
+ name,
+ createMetadataWrappedStrategy(strategyFn)
+ );
+ });
+
+ // ... existing code ...
// add metadata providers
metaData.addProvider(
@@ -296,6 +309,28 @@ function initializeWebWorkerProgressHandler(uiNotificationService) {
});
}
+/**
+ * Creates a wrapped image load strategy with metadata handling
+ * @param strategyFn - The image loading strategy function to wrap
+ * @returns A wrapped strategy function that handles metadata configuration
+ */
+const createMetadataWrappedStrategy = (strategyFn: (args: any) => any) => {
+ return (args: any) => {
+ const clonedConfig = imageRetrieveMetadataProvider.clone();
+ imageRetrieveMetadataProvider.clear();
+
+ try {
+ const result = strategyFn(args);
+ return result;
+ } finally {
+ // Ensure metadata is always restored, even if there's an error
+ setTimeout(() => {
+ imageRetrieveMetadataProvider.restore(clonedConfig);
+ }, 10);
+ }
+ };
+};
+
function CPUModal() {
return (
diff --git a/extensions/cornerstone/src/initMeasurementService.ts b/extensions/cornerstone/src/initMeasurementService.ts
index 9433bb5ef..f0d863363 100644
--- a/extensions/cornerstone/src/initMeasurementService.ts
+++ b/extensions/cornerstone/src/initMeasurementService.ts
@@ -59,10 +59,10 @@ const initMeasurementService = (
'Crosshairs',
Length.matchingCriteria,
() => {
- console.warn('Crosshairs mapping not implemented.');
+ return null;
},
() => {
- console.warn('Crosshairs mapping not implemented.');
+ return null;
}
);
diff --git a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts
index 444f7a49b..c07a4de01 100644
--- a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts
+++ b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts
@@ -1058,6 +1058,11 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
// Store the current position presentations for each viewport.
viewports.forEach(({ id: viewportId }) => {
const presentation = this._getPositionPresentation(viewportId);
+
+ // During a resize, the slice index should remain unchanged. This is a temporary fix for
+ // a larger issue regarding the definition of slice index with slab thickness.
+ // We need to revisit this to make it more robust and understandable.
+ delete presentation.viewReference.sliceIndex;
this.beforeResizePositionPresentations.set(viewportId, presentation);
});
@@ -1066,10 +1071,12 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
renderingEngine.resize(isImmediate);
renderingEngine.render();
- // Reset the camera for viewports that should reset their camera on resize,
+ // Reset the camera for all viewports using position presentation to maintain relative size/position
// which means only those viewports that have a zoom level of 1.
this.beforeResizePositionPresentations.forEach((positionPresentation, viewportId) => {
- this.setPresentations(viewportId, { positionPresentation });
+ this.setPresentations(viewportId, {
+ positionPresentation,
+ });
});
// Resize and render the rendering engine again.
diff --git a/extensions/cornerstone/src/utils/interleaveTopToBottom.ts b/extensions/cornerstone/src/utils/interleaveTopToBottom.ts
index fc5447a42..9caad9087 100644
--- a/extensions/cornerstone/src/utils/interleaveTopToBottom.ts
+++ b/extensions/cornerstone/src/utils/interleaveTopToBottom.ts
@@ -108,7 +108,7 @@ export default function interleaveTopToBottom({
volumes.forEach(volume => {
const requests = volume.getImageLoadRequests();
- if (!requests.length || !requests[0] || !requests[0].imageId) {
+ if (!requests?.[0]?.imageId) {
return;
}
diff --git a/extensions/default/src/DicomJSONDataSource/index.js b/extensions/default/src/DicomJSONDataSource/index.js
index bb0a9b43d..fa4fd60fc 100644
--- a/extensions/default/src/DicomJSONDataSource/index.js
+++ b/extensions/default/src/DicomJSONDataSource/index.js
@@ -93,7 +93,7 @@ function createDicomJSONApi(dicomJsonConfig) {
const { query } = qs.parseUrl(instance.url);
- // Add imageId specific mapping to this data as the URL isn't necessarliy WADO-URI.
+ // Add imageId specific mapping to this data as the URL isn't necessarily WADO-URI.
metadataProvider.addImageIdToUIDs(imageId, {
StudyInstanceUID,
SeriesInstanceUID,
diff --git a/extensions/default/src/DicomLocalDataSource/index.js b/extensions/default/src/DicomLocalDataSource/index.js
index 42c1c6033..a0ea9fb4f 100644
--- a/extensions/default/src/DicomLocalDataSource/index.js
+++ b/extensions/default/src/DicomLocalDataSource/index.js
@@ -142,9 +142,7 @@ function createDicomLocalApi(dicomLocalConfig) {
study.series.forEach(aSeries => {
const { SeriesInstanceUID } = aSeries;
- const isMultiframe = aSeries.instances[0].NumberOfFrames > 1;
-
- aSeries.instances.forEach((instance, index) => {
+ aSeries.instances.forEach(instance => {
const {
url: imageId,
StudyInstanceUID,
@@ -153,14 +151,22 @@ function createDicomLocalApi(dicomLocalConfig) {
} = instance;
instance.imageId = imageId;
-
- // Add imageId specific mapping to this data as the URL isn't necessarily WADO-URI.
- metadataProvider.addImageIdToUIDs(imageId, {
- StudyInstanceUID,
- SeriesInstanceUID,
- SOPInstanceUID,
- frameIndex: isMultiframe ? index : 1,
- });
+ const numberOfFrames = instance.NumberOfFrames || 1;
+ // Process all frames consistently, whether single or multiframe
+ for (let i = 0; i < numberOfFrames; i++) {
+ const frameNumber = i + 1;
+ const frameImageId = implementation.getImageIdsForInstance({
+ instance,
+ frame: frameNumber,
+ });
+ // Add imageId specific mapping to this data as the URL isn't necessarily WADO-URI.
+ metadataProvider.addImageIdToUIDs(frameImageId, {
+ StudyInstanceUID,
+ SeriesInstanceUID,
+ SOPInstanceUID,
+ frameNumber: numberOfFrames > 1 ? frameNumber : undefined,
+ });
+ }
});
DicomMetadataStore._broadcastEvent(EVENTS.INSTANCES_ADDED, {
@@ -209,9 +215,11 @@ function createDicomLocalApi(dicomLocalConfig) {
return imageIds;
},
getImageIdsForInstance({ instance, frame }) {
- if (instance.imageId) {
- return instance.imageId;
- }
+ // Important: Never use instance.imageId because it might be multiframe,
+ // which would make it an invalid imageId.
+ // if (instance.imageId) {
+ // return instance.imageId;
+ // }
const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = instance;
const storedInstance = DicomMetadataStore.getInstance(
diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js
index 8f5f9ae86..29dbdd324 100644
--- a/extensions/default/src/DicomWebDataSource/index.js
+++ b/extensions/default/src/DicomWebDataSource/index.js
@@ -434,23 +434,32 @@ function createDicomWebApi(dicomWebConfig, servicesManager) {
instance.wadoRoot = dicomWebConfig.wadoRoot;
instance.wadoUri = dicomWebConfig.wadoUri;
- const imageId = implementation.getImageIdsForInstance({
- instance,
- });
+ const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = instance;
+
+ const numberOfFrames = instance.NumberOfFrames || 1;
+ // Process all frames consistently, whether single or multiframe
+ for (let i = 0; i < numberOfFrames; i++) {
+ const frameNumber = i + 1;
+ const frameImageId = implementation.getImageIdsForInstance({
+ instance,
+ frame: frameNumber,
+ });
+ // Add imageId specific mapping to this data as the URL isn't necessarily WADO-URI.
+ metadataProvider.addImageIdToUIDs(frameImageId, {
+ StudyInstanceUID,
+ SeriesInstanceUID,
+ SOPInstanceUID,
+ frameNumber: numberOfFrames > 1 ? frameNumber : undefined,
+ });
+ }
// Adding imageId to each instance
// Todo: This is not the best way I can think of to let external
// metadata handlers know about the imageId that is stored in the store
- instance.imageId = imageId;
-
- // Adding UIDs to metadataProvider
- // Note: storing imageURI in metadataProvider since stack viewports
- // will use the same imageURI
- metadataProvider.addImageIdToUIDs(imageId, {
- StudyInstanceUID,
- SeriesInstanceUID: instance.SeriesInstanceUID,
- SOPInstanceUID: instance.SOPInstanceUID,
+ const imageId = implementation.getImageIdsForInstance({
+ instance,
});
+ instance.imageId = imageId;
});
DicomMetadataStore.addInstances(naturalizedInstances, madeInClient);
diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json
index 36edfd904..fac09d9a5 100644
--- a/extensions/measurement-tracking/package.json
+++ b/extensions/measurement-tracking/package.json
@@ -32,8 +32,8 @@
"start": "yarn run dev"
},
"peerDependencies": {
- "@cornerstonejs/core": "^2.2.13",
- "@cornerstonejs/tools": "^2.2.13",
+ "@cornerstonejs/core": "^2.2.20",
+ "@cornerstonejs/tools": "^2.2.20",
"@ohif/core": "3.10.0-beta.10",
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.10",
"@ohif/extension-default": "3.10.0-beta.10",
diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx
index 3c1df9190..9ed5e1e91 100644
--- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx
+++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx
@@ -9,6 +9,7 @@ import { StudyBrowser } from '@ohif/ui-next';
import { useTrackedMeasurements } from '../../getContextModule';
import { Separator } from '@ohif/ui-next';
import { PanelStudyBrowserHeader } from '@ohif/extension-default';
+import { useAppConfig } from '@state';
import { defaultActionIcons, defaultViewPresets } from './constants';
const { formatDate, createStudyBrowserTabs } = utils;
@@ -27,7 +28,7 @@ const thumbnailNoImageModalities = [
*
* @param {*} param0
*/
-function PanelStudyBrowserTracking({
+export default function PanelStudyBrowserTracking({
servicesManager,
getImageSrc,
getStudiesForPatientByMRN,
@@ -45,6 +46,10 @@ function PanelStudyBrowserTracking({
customizationService,
} = servicesManager.services;
const navigate = useNavigate();
+ const { mode: studyMode } = customizationService.getCustomization('PanelStudyBrowser.studyMode', {
+ id: 'default',
+ mode: 'all',
+ });
const { t } = useTranslation('Common');
@@ -55,7 +60,8 @@ function PanelStudyBrowserTracking({
const [{ activeViewportId, viewports, isHangingProtocolLayout }, viewportGridService] =
useViewportGrid();
const [trackedMeasurements, sendTrackedMeasurementsEvent] = useTrackedMeasurements();
- const [activeTabName, setActiveTabName] = useState('all');
+
+ const [activeTabName, setActiveTabName] = useState(studyMode);
const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState([
...StudyInstanceUIDs,
]);
@@ -117,6 +123,10 @@ function PanelStudyBrowserTracking({
const { trackedSeries } = trackedMeasurements.context;
+ useEffect(() => {
+ setActiveTabName(studyMode);
+ }, [studyMode]);
+
// ~~ studyDisplayList
useEffect(() => {
// Fetch all studies for the patient in each primary study
@@ -528,8 +538,6 @@ PanelStudyBrowserTracking.propTypes = {
requestDisplaySetCreationForStudy: PropTypes.func.isRequired,
};
-export default PanelStudyBrowserTracking;
-
function getImageIdForThumbnail(displaySet: any, imageIds: any) {
let imageId;
if (displaySet.isDynamicVolume) {
diff --git a/modes/longitudinal/src/customizations.tsx b/modes/longitudinal/src/customizations.tsx
index 09f59d9ac..279b611ff 100644
--- a/modes/longitudinal/src/customizations.tsx
+++ b/modes/longitudinal/src/customizations.tsx
@@ -19,6 +19,11 @@ export const performCustomizations = customizationService => {
id: 'PanelSegmentation.disableEditing',
disableEditing: true,
},
+ // to only show current study in the panel study browser
+ // {
+ // id: 'PanelStudyBrowser.studyMode',
+ // mode: 'primary',
+ // },
// To disable editing in the MeasurementTable
// {
// id: 'PanelMeasurement.disableEditing',
diff --git a/modes/preclinical-4d/src/index.tsx b/modes/preclinical-4d/src/index.tsx
index 23ac99294..21fc1baed 100644
--- a/modes/preclinical-4d/src/index.tsx
+++ b/modes/preclinical-4d/src/index.tsx
@@ -84,7 +84,7 @@ function modeFactory({ modeConfiguration }) {
lesionStats: 'Lesion Statistics',
minValue: 'Minimum Value',
maxValue: 'Maximum Value',
- meanValue: 'Mean Value',
+ meanValue: 'Mean Value (ml)',
volume: 'Volume',
suvPeak: 'SUV Peak',
suvMax: 'Maximum SUV',
diff --git a/modes/tmtv/src/index.ts b/modes/tmtv/src/index.ts
index ef383e65d..9c1ba1702 100644
--- a/modes/tmtv/src/index.ts
+++ b/modes/tmtv/src/index.ts
@@ -120,7 +120,7 @@ function modeFactory({ modeConfiguration }) {
minValue: 'Minimum Value',
maxValue: 'Maximum Value',
meanValue: 'Mean Value',
- volume: 'Volume',
+ volume: 'Volume (ml)',
suvPeak: 'SUV Peak',
suvMax: 'Maximum SUV',
suvMaxIJK: 'SUV Max IJK',
diff --git a/platform/app/.recipes/Nginx-Dcm4chee-Keycloak/config/nginx.conf b/platform/app/.recipes/Nginx-Dcm4chee-Keycloak/config/nginx.conf
index 30b0089c4..79043d928 100644
--- a/platform/app/.recipes/Nginx-Dcm4chee-Keycloak/config/nginx.conf
+++ b/platform/app/.recipes/Nginx-Dcm4chee-Keycloak/config/nginx.conf
@@ -221,8 +221,6 @@ http {
location / {
add_header Cache-Control "no-store, no-cache, must-revalidate";
- add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
- add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
location /keycloak/ {
diff --git a/platform/app/.recipes/Nginx-Dcm4chee/config/nginx.conf b/platform/app/.recipes/Nginx-Dcm4chee/config/nginx.conf
index 8d9ca8f54..ae7e84ad6 100644
--- a/platform/app/.recipes/Nginx-Dcm4chee/config/nginx.conf
+++ b/platform/app/.recipes/Nginx-Dcm4chee/config/nginx.conf
@@ -79,8 +79,6 @@ http {
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
- add_header Cross-Origin-Opener-Policy 'same-origin' always;
- add_header Cross-Origin-Embedder-Policy 'require-corp' always;
}
}
}
diff --git a/platform/app/.recipes/Nginx-Orthanc-Keycloak/config/nginx.conf b/platform/app/.recipes/Nginx-Orthanc-Keycloak/config/nginx.conf
index 1ad9f512d..272a478a3 100644
--- a/platform/app/.recipes/Nginx-Orthanc-Keycloak/config/nginx.conf
+++ b/platform/app/.recipes/Nginx-Orthanc-Keycloak/config/nginx.conf
@@ -191,8 +191,6 @@ http {
location / {
add_header Cache-Control "no-store, no-cache, must-revalidate";
- add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
- add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
location /keycloak/ {
diff --git a/platform/app/.recipes/Nginx-Orthanc/config/nginx.conf b/platform/app/.recipes/Nginx-Orthanc/config/nginx.conf
index 57946cddf..0b559ca73 100644
--- a/platform/app/.recipes/Nginx-Orthanc/config/nginx.conf
+++ b/platform/app/.recipes/Nginx-Orthanc/config/nginx.conf
@@ -78,8 +78,6 @@ http {
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
- add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
- add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
}
diff --git a/platform/app/.webpack/webpack.pwa.js b/platform/app/.webpack/webpack.pwa.js
index cd6487589..1d11c0dff 100644
--- a/platform/app/.webpack/webpack.pwa.js
+++ b/platform/app/.webpack/webpack.pwa.js
@@ -133,8 +133,6 @@ module.exports = (env, argv) => {
new InjectManifest({
swDest: 'sw.js',
swSrc: path.join(SRC_DIR, 'service-worker.js'),
- // Increase the limit to 4mb:
- maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
// Need to exclude the theme as it is updated independently
exclude: [/theme/],
// Cache large files for the manifests to avoid warning messages
@@ -174,10 +172,6 @@ module.exports = (env, argv) => {
disableDotRule: true,
index: PUBLIC_URL + 'index.html',
},
- headers: {
- 'Cross-Origin-Embedder-Policy': 'require-corp',
- 'Cross-Origin-Opener-Policy': 'same-origin',
- },
devMiddleware: {
writeToDisk: true,
},
@@ -206,5 +200,9 @@ module.exports = (env, argv) => {
);
}
+ mergedConfig.watchOptions = {
+ ignored: /node_modules\/@cornerstonejs/,
+ };
+
return mergedConfig;
};
diff --git a/platform/app/.webpack/writePluginImportsFile.js b/platform/app/.webpack/writePluginImportsFile.js
index c913e779e..29accd913 100644
--- a/platform/app/.webpack/writePluginImportsFile.js
+++ b/platform/app/.webpack/writePluginImportsFile.js
@@ -73,7 +73,10 @@ function getRuntimeLoadModesExtensions(modules) {
dynamicLoad.push(
` if( module==="${packageName}") {`,
` const imported = await window.browserImportFunction('${module.importPath}');`,
- ' return ' + (module.globalName ? `window["${module.globalName}"];` : `imported["${module.importName || 'default'}"];`),
+ ' return ' +
+ (module.globalName
+ ? `window["${module.globalName}"];`
+ : `imported["${module.importName || 'default'}"];`),
' }'
);
return;
@@ -201,12 +204,6 @@ function writePluginImportsFile(SRC_DIR, DIST_DIR) {
'dist'
);
- console.warn('copy plugins', [
- ...copyPluginPublicToDistBuild,
- ...copyPluginPublicToDistLink,
- ...copyPluginDistToDistBuild,
- ...copyPluginDistToDistLink,
- ]);
return [
...copyPluginPublicToDistBuild,
...copyPluginPublicToDistLink,
diff --git a/platform/app/package.json b/platform/app/package.json
index 6a9710331..5d62f56b8 100644
--- a/platform/app/package.json
+++ b/platform/app/package.json
@@ -53,7 +53,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
- "@cornerstonejs/dicom-image-loader": "^2.2.13",
+ "@cornerstonejs/dicom-image-loader": "^2.2.20",
"@emotion/serialize": "^1.1.3",
"@ohif/core": "3.10.0-beta.10",
"@ohif/extension-cornerstone": "3.10.0-beta.10",
diff --git a/platform/app/public/serve.json b/platform/app/public/serve.json
index dbbe3ba69..8616a96ba 100644
--- a/platform/app/public/serve.json
+++ b/platform/app/public/serve.json
@@ -1,12 +1,3 @@
{
- "rewrites": [{ "source": "*", "destination": "index.html" }],
- "headers": [
- {
- "source": "**/*",
- "headers": [
- { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" },
- { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }
- ]
- }
- ]
+ "rewrites": [{ "source": "*", "destination": "index.html" }]
}
diff --git a/platform/app/tailwind.css b/platform/app/tailwind.css
index 510fdf6ec..492349705 100644
--- a/platform/app/tailwind.css
+++ b/platform/app/tailwind.css
@@ -2,7 +2,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
-
-* {
- min-height: 0 !important;
-}
diff --git a/platform/core/package.json b/platform/core/package.json
index d615897cb..9cba4012d 100644
--- a/platform/core/package.json
+++ b/platform/core/package.json
@@ -37,7 +37,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
- "@cornerstonejs/dicom-image-loader": "^2.2.13",
+ "@cornerstonejs/dicom-image-loader": "^2.2.20",
"@ohif/ui": "3.10.0-beta.10",
"cornerstone-math": "0.1.9",
"dicom-parser": "^1.8.21"
diff --git a/platform/core/src/classes/MetadataProvider.ts b/platform/core/src/classes/MetadataProvider.ts
index d9cc2e323..08c421fef 100644
--- a/platform/core/src/classes/MetadataProvider.ts
+++ b/platform/core/src/classes/MetadataProvider.ts
@@ -8,7 +8,6 @@ import toNumber from '../utils/toNumber';
import combineFrameInstance from '../utils/combineFrameInstance';
class MetadataProvider {
- private readonly studies: Map
= new Map();
private readonly imageURIToUIDs: Map = new Map();
private readonly imageUIDsByImageId: Map = new Map();
// Can be used to store custom metadata for a specific type.
@@ -17,6 +16,10 @@ class MetadataProvider {
private readonly customMetadata: Map = new Map();
addImageIdToUIDs(imageId, uids) {
+ if (!imageId) {
+ throw new Error('MetadataProvider::Empty imageId');
+ }
+
// This method is a fallback for when you don't have WADO-URI or WADO-RS.
// You can add instances fetched by any method by calling addInstance, and hook an imageId to point at it here.
// An example would be dicom hosted at some random site.
@@ -35,6 +38,10 @@ class MetadataProvider {
}
_getInstance(imageId) {
+ if (!imageId) {
+ throw new Error('MetadataProvider::Empty imageId');
+ }
+
const uids = this.getUIDsFromImageID(imageId);
if (!uids) {
@@ -455,17 +462,6 @@ class MetadataProvider {
}
getUIDsFromImageID(imageId) {
- if (!imageId) {
- throw new Error('MetadataProvider::Empty imageId');
- }
- // TODO: adding csiv here is not really correct. Probably need to use
- // metadataProvider.addImageIdToUIDs(imageId, {
- // StudyInstanceUID,
- // SeriesInstanceUID,
- // SOPInstanceUID,
- // })
- // somewhere else
-
const cachedUIDs = this.imageUIDsByImageId.get(imageId);
if (cachedUIDs) {
return cachedUIDs;
diff --git a/platform/core/src/services/CustomizationService/CustomizationService.ts b/platform/core/src/services/CustomizationService/CustomizationService.ts
index a36104d50..4830dfe59 100644
--- a/platform/core/src/services/CustomizationService/CustomizationService.ts
+++ b/platform/core/src/services/CustomizationService/CustomizationService.ts
@@ -280,7 +280,7 @@ export default class CustomizationService extends PubSubService {
return customization;
}
const parent = this.getCustomization(customizationType);
- const result = parent ? Object.assign(Object.create(parent), customization) : customization;
+ const result = parent ? Object.assign({}, parent, customization) : customization;
// Execute an nested type information
return result.transform?.(this) || result;
}
diff --git a/platform/core/src/services/StudyPrefetcherService/StudyPrefetcherService.ts b/platform/core/src/services/StudyPrefetcherService/StudyPrefetcherService.ts
index 5b5853d65..398974eb3 100644
--- a/platform/core/src/services/StudyPrefetcherService/StudyPrefetcherService.ts
+++ b/platform/core/src/services/StudyPrefetcherService/StudyPrefetcherService.ts
@@ -679,7 +679,7 @@ class StudyPrefetcherService extends PubSubService {
this._displaySetLoadingStates.clear();
this._imageIdsToDisplaySetsMap.clear();
this._inflightRequests.clear();
- this.imageLoadPoolManager.clearRequestStack(IMAGE_REQUEST_TYPE);
+ this.imageLoadPoolManager.clearRequestStack(this.requestType);
this._broadcastEvent(this.EVENTS.SERVICE_STOPPED, {});
}
diff --git a/platform/core/src/types/AppTypes.ts b/platform/core/src/types/AppTypes.ts
index 07db43b48..8d820b956 100644
--- a/platform/core/src/types/AppTypes.ts
+++ b/platform/core/src/types/AppTypes.ts
@@ -81,6 +81,7 @@ declare global {
}
export interface Config {
+ studyBrowserMode?: 'all' | 'primary';
routerBasename?: string;
customizationService?: CustomizationServiceType;
extensions?: string[];
diff --git a/platform/core/src/utils/combineFrameInstance.ts b/platform/core/src/utils/combineFrameInstance.ts
index 6385bfbd0..cf70232cf 100644
--- a/platform/core/src/utils/combineFrameInstance.ts
+++ b/platform/core/src/utils/combineFrameInstance.ts
@@ -1,3 +1,5 @@
+import { vec3 } from 'gl-matrix';
+
/**
* Combine the Per instance frame data, the shared frame data
* and the root data objects.
@@ -9,25 +11,28 @@
* single frame data. (eg frame is undefined is the same as frame===1).
*/
const combineFrameInstance = (frame, instance) => {
- const { PerFrameFunctionalGroupsSequence, SharedFunctionalGroupsSequence, NumberOfFrames } =
- instance;
+ const {
+ PerFrameFunctionalGroupsSequence,
+ SharedFunctionalGroupsSequence,
+ NumberOfFrames,
+ SpacingBetweenSlices,
+ } = instance;
if (PerFrameFunctionalGroupsSequence || NumberOfFrames > 1) {
const frameNumber = Number.parseInt(frame || 1);
- const shared = (
- SharedFunctionalGroupsSequence ? Object.values(SharedFunctionalGroupsSequence[0]) : []
- )
- .filter(it => !!it)
- .map(it => it[0])
- .filter(it => it !== undefined && typeof it === 'object');
- const perFrame = (
- PerFrameFunctionalGroupsSequence
- ? Object.values(PerFrameFunctionalGroupsSequence[frameNumber - 1])
- : []
- )
- .filter(it => !!it)
- .map(it => it[0])
- .filter(it => it !== undefined && typeof it === 'object');
+ const shared = SharedFunctionalGroupsSequence
+ ? Object.values(SharedFunctionalGroupsSequence[0])
+ .filter(Boolean)
+ .map(it => it[0])
+ .filter(it => typeof it === 'object')
+ : [];
+
+ const perFrame = PerFrameFunctionalGroupsSequence
+ ? Object.values(PerFrameFunctionalGroupsSequence[frameNumber - 1])
+ .filter(Boolean)
+ .map(it => it[0])
+ .filter(it => typeof it === 'object')
+ : [];
// this is to fix NM multiframe datasets with position and orientation
// information inside DetectorInformationSequence
@@ -35,9 +40,40 @@ const combineFrameInstance = (frame, instance) => {
instance.ImageOrientationPatient =
instance.DetectorInformationSequence[0].ImageOrientationPatient;
}
+
+ let ImagePositionPatientToUse = instance.ImagePositionPatient;
+
if (!instance.ImagePositionPatient && instance.DetectorInformationSequence) {
- instance.ImagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
+ const imagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
+ const imageOrientationPatient = instance.ImageOrientationPatient;
+
+ // Calculate the position for the current frame
+ if (imageOrientationPatient && SpacingBetweenSlices) {
+ const rowOrientation = vec3.fromValues(
+ imageOrientationPatient[0],
+ imageOrientationPatient[1],
+ imageOrientationPatient[2]
+ );
+
+ const colOrientation = vec3.fromValues(
+ imageOrientationPatient[3],
+ imageOrientationPatient[4],
+ imageOrientationPatient[5]
+ );
+
+ const normalVector = vec3.cross(vec3.create(), rowOrientation, colOrientation);
+
+ const position = vec3.scaleAndAdd(
+ vec3.create(),
+ imagePositionPatient,
+ normalVector,
+ SpacingBetweenSlices * (frameNumber - 1)
+ );
+
+ ImagePositionPatientToUse = [position[0], position[1], position[2]];
+ }
}
+ console.debug('🚀 ~ ImagePositionPatientToUse:', ImagePositionPatientToUse);
const newInstance = Object.assign(instance, { frameNumber: frameNumber });
@@ -52,7 +88,8 @@ const combineFrameInstance = (frame, instance) => {
// back to the dicomMetaStore so we don't have to do this again.
return {
...newInstance,
- ImagePositionPatient: newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
+ ImagePositionPatient: ImagePositionPatientToUse ??
+ newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
};
} else {
return instance;
diff --git a/platform/docs/docs/assets/img/iframe-basic.png b/platform/docs/docs/assets/img/iframe-basic.png
index 25ea20708..5934a9422 100644
Binary files a/platform/docs/docs/assets/img/iframe-basic.png and b/platform/docs/docs/assets/img/iframe-basic.png differ
diff --git a/platform/docs/docs/deployment/build-for-production.md b/platform/docs/docs/deployment/build-for-production.md
index d1dd508bd..dbd89a8a9 100644
--- a/platform/docs/docs/deployment/build-for-production.md
+++ b/platform/docs/docs/deployment/build-for-production.md
@@ -102,18 +102,12 @@ directory:
yarn global add http-server
# Change the directory to the platform/app
+cd platform/app
# Serve the files in our current directory
npx serve ./dist -c ../public/serve.json
```
-:::caution
-In the video below notice that there is `platform/viewer` which has been renamed to `platform/app` in the latest version
-:::
-
-
-
-
### Automating Builds and Deployments
diff --git a/platform/docs/docs/deployment/cors.md b/platform/docs/docs/deployment/cors.md
index 135c9f961..b55e72f7b 100644
--- a/platform/docs/docs/deployment/cors.md
+++ b/platform/docs/docs/deployment/cors.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 7
+sidebar_position: 8
---
# Cross-Origin Information for OHIF
@@ -7,42 +7,10 @@ sidebar_position: 7
This document describes various security configurations, settings and environments/contexts needed to fully leverage OHIF’s capabilities. One may need some configurations while others might need ALL of them - it all depends on the environment OHIF is expected to run in.
In particular, three of OHIF’s features depend on these configurations:
-- [OHIF’s use of SharedArrayBuffer](#sharedarraybuffer)
- [Embedding OHIF in an iframe](#embedding-ohif-in-an-iframe)
- [XMLHttpRequests to fetch data from data sources](#cors-in-ohif)
-
-## SharedArrayBuffer
-A `SharedArrayBuffer` is a JavaScript object that is similar to an `ArrayBuffer` but can be shared between web workers and the window that spawned them via the `postMessage` API. See [SharedArrayBuffer in MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) for more information.
-
-:::tip
-To turn off Shared Array Buffer completely, just set `useSharedArrayBuffer` to `false` in the [OHIF configuration](../configuration/configurationFiles.md). But keep in mind that you will not get the performance boost that Shared Array Buffer offers for decoding and rendering big volumes where web workers write to the same memory space.
-:::
-
-### Security Requirements
-
-In order to use `SharedArrayBuffer` objects in the browser, the following security conditions must be met:
-
-- The page must be served in a [secure context](#secure-context).
-- The page must have [cross-origin isolation](#cross-origin-isolation) enabled.
-
-### `SharedArrayBuffer` in OHIF
-
-OHIF uses `SharedArrayBuffer` in its volume loader (from Cornerstone3D). It comes with the benefit of improved performance and optimization at the cost of some configuration to use it.
-
-As such, if the following popup is shown when launching OHIF then the OHIF server will have to be configured to permit the loading of volumetric images and data. Note that stack viewports are still available and functional even when this error is present.
-
-
-
-To better determine which (if not all) of the [security requirements](#security-requirements) are lacking, have a look at the browser console.
-
-Output in the console similar to the following indicates that OHIF is not running in a [secure context](#secure-context).
-
-
-
-Absence of the above error in the console together with the presence of the Cross Origin Isolation popup warning, likely indicates that either or both of the [COOP](#coop---cross-origin-opener-policy) and/or [COEP](#coep---cross-origin-embedder-policy) headers are not set for OHIF.
-
## Embedding OHIF in an iframe
As described [here](./iframe.md), there are cases where OHIF will be embedded in an iframe. The following links provide more information for setting up and configuring OHIF to work in an iframe:
@@ -64,9 +32,6 @@ URLs that are NOT local must be delivered over `https://` or `wss://` (i.e. TLS)
A page embedded in an iframe is considered secure if it itself and every one of its embedding ancestors are delivered securely. Otherwise it is deemed insecure.
-### Why does OHIF require a secure context?
-
-Beyond all of the inherent benefits of a secure connection, OHIF requires a secure context so that it can utilize [SharedArrayBuffer](#sharedarraybuffer) objects for volume rendering.
### Configuring/setting up a secure context
@@ -143,91 +108,8 @@ add_header 'Access-Control-Allow-Origin' '*' always;
```
:::
-## COOP - Cross-Origin Opener Policy
-The COOP HTTP response header restricts the global, root document of the page from being referenced and accessed by another cross-origin document that might open the page in a window. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) for more information.
-### Header Values Pertinent to OHIF (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy#syntax) for more information)
-
-|Value|Description|
-|-----|-----------|
-|same-origin|Restricts the document to be referenced by openers of the same origin only.|
-
-### COOP in OHIF
-
-COOP is required for [SharedArrayBuffer](#sharedarraybuffer) usage in OHIF. See also [Troubleshooting Cross-origin Isolation in OHIF](#troubleshooting-cross-origin-isolation-in-ohif).
-
-## COEP - Cross-Origin Embedder Policy
-
-The COEP HTTP response header restricts cross-origin documents from being embedded into a document (e.g. in an iframe, video, image, etc). See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) for more information.
-
-### Header Values Pertinent to OHIF (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy#syntax) for more information)
-
-|Value|Description|
-|-----|-----------|
-|require-corp|Permits the document to load either of the following embedded resources: - Those from the same origin
- Cross-origin resources embedded by a DOM element that has the appropriate [crossorigin attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) set
- Cross-origin resources with the appropriate [CORP response header](#corp---cross-origin-resource-policy)
-|credentialless|See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy#syntax) for more information|
-
-### COEP in OHIF
-
-COEP is required for [SharedArrayBuffer](#sharedarraybuffer) usage in OHIF. See also [Troubleshooting Cross-origin Isolation in OHIF](#troubleshooting-cross-origin-isolation-in-ohif).
-
-## Cross-origin Isolation
-
-Cross-origin isolation is [enabled](https://web.dev/cross-origin-isolation-guide/#enable-cross-origin-isolation) for a web page when the following COOP and COEP headers are set.
-- [COOP](#coop---cross-origin-opener-policy) with `same-origin`
-- [COEP](#coep---cross-origin-embedder-policy) with `require-corp` or `credentialless`
-
-### iframe
-
-An iframe is considered to have cross-origin isolation enabled if it itself has the appropriate COOP and COEP headers set as well as every one of its embedding ancestors.
-
-### Troubleshooting Cross-origin Isolation in OHIF
-
-The [SharedArrayBuffer in OHIF](#sharedarraybuffer-in-ohif) section describes how to determine if there are problems with cross-origin isolation in OHIF. If it is determined that COOP and/or COEP is indeed an issue, then the COOP and COEP headers must be set for OHIF. How to accomplish this varies per server or service that is hosting OHIF. The following are just a few examples.
-
-:::tip
-In the default dev environment, the following can be set in the webpack.pwa.js file…
-
-```javascript
-devServer: {
- headers: {
- "Cross-Origin-Opener-Policy": "same-origin",
- "Cross-Origin-Embedder-Policy": "require-corp"
- }
-}
-```
-:::
-
-:::tip
-If deploying OHIF using Netlify, the Netlify configuration [file](https://docs.netlify.com/configure-builds/file-based-configuration/) can be used to configure the headers as such…
-
-```
-[[headers]]
- # Define which paths this specific [[headers]] block will cover.
- for = "/*"
-
- [headers.values]
- Cross-Origin-Opener-Policy = "same-origin"
- Cross-Origin-Embedder-Policy = "require-corp"
-```
-:::
-
-:::tip
-If OHIF is served behind nginx, then the headers can be set in the nginx.conf file as follows. The [template nginx configuration file](https://github.com/OHIF/Viewers/blob/master/.docker/Viewer-v3.x/default.conf.template) for creating a [OHIF Docker image](./docker.md#building-the-docker-image) has an example of this too.
-```nginx
-server {
- location / {
- add_header Cross-Origin-Opener-Policy same-origin;
- add_header Cross-Origin-Embedder-Policy require-corp;
- }
-}
-```
-:::
-
-## CORP - Cross-Origin Resource Policy
-
-The CORP HTTP response header indicates which origins can read and use a resource. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy) for more information.
### Header Values (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy#usage) for more information)
@@ -239,12 +121,6 @@ The CORP HTTP response header indicates which origins can read and use a resourc
### OHIF and CORP
-There are two scenarios where the CORP header is relevant to OHIF:
-
-- [PDF from a Cross Origin DICOMweb Data Source](#pdf-from-a-cross-origin-dicomweb-data-source)
-- [OHIF as a Cross-origin Resource in an iframe](#ohif-as-a-cross-origin-resource-in-an-iframe)
-
-Both these scenarios stem from the fact that OHIF has to be served with the [COEP](#coep---cross-origin-embedder-policy) header to support [SharedArrayBuffer](#sharedarraybuffer).
#### PDF from a Cross Origin DICOMweb Data Source
@@ -266,48 +142,3 @@ If the dcm4chee server and the OHIF server are hosted on the same site, then the
add_header 'Cross-Origin-Resource-Policy' 'same-site' always;
```
:::
-
-#### OHIF as a Cross-origin Resource in an iframe
-
-There are cases where [OHIF is embedded in an iframe](./iframe.md) and the embedding page is from a different origin. Again due to the [security requirements for SharedArrayBuffer](#security-requirements), [both OHIF and the embedding page](#iframe) must have the appropriate COEP header. In this scenario, OHIF is the cross-origin resource and since the `
);
};
@@ -199,14 +192,14 @@ const DataRow: React.FC