feat(viewer): Enhance viewer functionality with sidebar and popout support
This commit is contained in:
@@ -101,39 +101,59 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if collection.show_ohif_viewer %}
|
{% if collection.show_ohif_viewer or collection.show_ohif_viewer_link or collection.show_built_in_viewer %}
|
||||||
<div>
|
<div id="viewer-panel" class="mb-3">
|
||||||
|
{% if collection.show_built_in_viewer %}
|
||||||
{% if question_completed %}
|
<div class="d-flex align-items-center gap-2 py-1 px-2 mb-1 rounded bg-body-secondary">
|
||||||
<iframe id="viewer" class="viewer-frame-ohif" src="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% if case_number %}{% url 'atlas:collection_case_dicom_json_review' collection.pk case_number %}{% else %}{% url 'atlas:collection_case_dicom_json_review_legacy' collection.pk case.pk %}{% endif %}"></iframe>
|
<span class="text-muted fw-semibold small me-auto">Viewer</span>
|
||||||
{% else %}
|
<button type="button" class="btn btn-sm btn-outline-secondary py-0 px-2" id="viewer-sidebar-btn" title="Move viewer to sidebar">
|
||||||
<iframe id="viewer" class="viewer-frame-ohif" src="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% if case_number %}{% url 'atlas:collection_case_dicom_json' collection.pk case_number %}{% else %}{% url 'atlas:collection_case_dicom_json_legacy' collection.pk case.pk %}{% endif %}"></iframe>
|
<i class="bi bi-layout-sidebar-reverse"></i> <span class="d-none d-sm-inline">Sidebar</span>
|
||||||
{% endif %}
|
</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary py-0 px-2" id="viewer-popout-btn" title="Open viewer in a separate window">
|
||||||
|
<i class="bi bi-box-arrow-up-right"></i> <span class="d-none d-sm-inline">Pop out</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="viewer-moveable-home">
|
||||||
|
<div id="viewer-moveable">
|
||||||
|
<div id="viewer-container" class="viewer-resizable-container">
|
||||||
|
<details id="dicom-viewer-details" open>
|
||||||
|
<summary class="py-1 small text-muted">Series</summary>
|
||||||
|
<div id="main_viewer" class="dicom-viewer-root viewer-frame-standard"
|
||||||
|
data-auto-cache-stack="false"
|
||||||
|
data-named-stacks='{{casedetail.get_case_named_stacks}}'
|
||||||
|
></div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
<div id="viewer-resize-handle" class="viewer-resize-handle" title="Drag to resize viewer">
|
||||||
|
<div class="viewer-resize-grip-inner"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if collection.show_ohif_viewer %}
|
||||||
|
<div{% if collection.show_built_in_viewer %} class="mt-3"{% endif %}>
|
||||||
|
{% if question_completed %}
|
||||||
|
<iframe id="viewer" class="viewer-frame-ohif" src="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% if case_number %}{% url 'atlas:collection_case_dicom_json_review' collection.pk case_number %}{% else %}{% url 'atlas:collection_case_dicom_json_review_legacy' collection.pk case.pk %}{% endif %}"></iframe>
|
||||||
|
{% else %}
|
||||||
|
<iframe id="viewer" class="viewer-frame-ohif" src="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% if case_number %}{% url 'atlas:collection_case_dicom_json' collection.pk case_number %}{% else %}{% url 'atlas:collection_case_dicom_json_legacy' collection.pk case.pk %}{% endif %}"></iframe>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if collection.show_ohif_viewer_link %}
|
||||||
|
<div class="mt-2">
|
||||||
|
{% if question_completed %}
|
||||||
|
<a target="_blank" href="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% if case_number %}{% url 'atlas:collection_case_dicom_json_review' collection.pk case_number %}{% else %}{% url 'atlas:collection_case_dicom_json_review_legacy' collection.pk case.pk %}{% endif %}" title="Click to open the case in the advanced viewer. This will open in a new tab/window."><button class="viewer"><i class="bi bi-collection"></i> Launch advanced viewer.</button></a>
|
||||||
|
{% else %}
|
||||||
|
<a target="_blank" href="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% if case_number %}{% url 'atlas:collection_case_dicom_json' collection.pk case_number %}{% else %}{% url 'atlas:collection_case_dicom_json_legacy' collection.pk case.pk %}{% endif %}" title="Click to open the case in the advanced viewer. This will open in a new tab/window."><button class="viewer"><i class="bi bi-collection"></i> Launch advanced viewer.</button></a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if collection.show_ohif_viewer_link %}
|
|
||||||
<div>
|
|
||||||
{% if question_completed %}
|
|
||||||
<a target="_blank" href="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% if case_number %}{% url 'atlas:collection_case_dicom_json_review' collection.pk case_number %}{% else %}{% url 'atlas:collection_case_dicom_json_review_legacy' collection.pk case.pk %}{% endif %}" title="Click to open the case in the advanced viewer. This will open in a new tab/window."><button class="viewer"><i class="bi bi-collection"></i> Launch advanced viewer.</button></a>
|
|
||||||
{% else %}
|
|
||||||
<a target="_blank" href="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% if case_number %}{% url 'atlas:collection_case_dicom_json' collection.pk case_number %}{% else %}{% url 'atlas:collection_case_dicom_json_legacy' collection.pk case.pk %}{% endif %}" title="Click to open the case in the advanced viewer. This will open in a new tab/window."><button class="viewer"><i class="bi bi-collection"></i> Launch advanced viewer.</button></a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if collection.show_built_in_viewer %}
|
|
||||||
|
|
||||||
<details id="dicom-viewer-details" open>
|
|
||||||
<summary>Viewer</summary>
|
|
||||||
<div id="main_viewer" class="dicom-viewer-root viewer-frame-standard"
|
|
||||||
data-auto-cache-stack="false"
|
|
||||||
data-named-stacks='{{casedetail.get_case_named_stacks}}'
|
|
||||||
></div>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
{% comment %} <div class="pre-whitespace multi-image-block">
|
{% comment %} <div class="pre-whitespace multi-image-block">
|
||||||
<details open>
|
<details open>
|
||||||
<summary>
|
<summary>
|
||||||
@@ -165,8 +185,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div> {% endcomment %}
|
</div> {% endcomment %}
|
||||||
{% else %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if show_discussion and case.discussion %}
|
{% if show_discussion and case.discussion %}
|
||||||
<details class="card mb-3">
|
<details class="card mb-3">
|
||||||
@@ -335,6 +353,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
<aside id="viewerSidebar" class="viewer-sidebar" aria-label="Viewer sidebar">
|
||||||
|
<div class="viewer-sidebar-header">
|
||||||
|
<span class="fw-semibold">Viewer</span>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-secondary" id="viewer-sidebar-close-btn">Close</button>
|
||||||
|
</div>
|
||||||
|
<div id="viewerSidebarBody" class="viewer-sidebar-body"></div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
label {
|
label {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
@@ -443,6 +469,116 @@
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.viewer-resizable-container {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
height: min(70vh, 700px);
|
||||||
|
min-height: 220px;
|
||||||
|
max-height: 85vh;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-resizable-container .dicom-viewer-root {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-resizable-container #dicom-viewer-details {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-resizable-container #dicom-viewer-details > summary {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-resizable-container #dicom-viewer-details > div {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-resize-handle {
|
||||||
|
height: 10px;
|
||||||
|
background: var(--bs-secondary-bg);
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
border-top: none;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
cursor: ns-resize;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
user-select: none;
|
||||||
|
touch-action: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-resize-handle:hover {
|
||||||
|
background: var(--bs-tertiary-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-resize-grip-inner {
|
||||||
|
width: 40px;
|
||||||
|
height: 3px;
|
||||||
|
background: var(--bs-secondary-color);
|
||||||
|
border-radius: 2px;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: min(850px, 95vw);
|
||||||
|
height: 100vh;
|
||||||
|
background: var(--bs-body-bg);
|
||||||
|
border-left: 1px solid var(--bs-border-color);
|
||||||
|
box-shadow: -8px 0 18px rgba(0, 0, 0, 0.35);
|
||||||
|
z-index: 1045;
|
||||||
|
transform: translateX(100%);
|
||||||
|
transition: transform 0.2s ease-in-out;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-sidebar.open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-sidebar-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--bs-border-color);
|
||||||
|
background: var(--bs-secondary-bg);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-sidebar-body {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-sidebar-body #viewer-moveable {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-sidebar-body #viewer-container {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer-sidebar-body .viewer-resize-handle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -823,4 +959,191 @@
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function () {
|
||||||
|
function initViewerControls() {
|
||||||
|
var COLL_PK = '{{ collection.pk }}';
|
||||||
|
var HEIGHT_KEY = 'rad_viewer_h_' + COLL_PK;
|
||||||
|
var MODE_KEY = 'rad_viewer_mode_' + COLL_PK;
|
||||||
|
var POPOUT_STATE_KEY = 'rad_local_viewer_popout_state';
|
||||||
|
|
||||||
|
var panel = document.getElementById('viewer-panel');
|
||||||
|
var moveableHome = document.getElementById('viewer-moveable-home');
|
||||||
|
var moveable = document.getElementById('viewer-moveable');
|
||||||
|
var container = document.getElementById('viewer-container');
|
||||||
|
var resizeHandle = document.getElementById('viewer-resize-handle');
|
||||||
|
var sidebarEl = document.getElementById('viewerSidebar');
|
||||||
|
var sidebarBody = document.getElementById('viewerSidebarBody');
|
||||||
|
var sidebarBtn = document.getElementById('viewer-sidebar-btn');
|
||||||
|
var sidebarCloseBtn = document.getElementById('viewer-sidebar-close-btn');
|
||||||
|
var popoutBtn = document.getElementById('viewer-popout-btn');
|
||||||
|
var mainViewerEl = document.getElementById('main_viewer');
|
||||||
|
|
||||||
|
if (!panel || !moveableHome || !moveable || !container || !mainViewerEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panel.dataset.viewerControlsBound === '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
panel.dataset.viewerControlsBound = '1';
|
||||||
|
|
||||||
|
var savedH = localStorage.getItem(HEIGHT_KEY);
|
||||||
|
if (savedH && !isNaN(parseInt(savedH, 10))) {
|
||||||
|
container.style.height = parseInt(savedH, 10) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
var persistHeight = function () {
|
||||||
|
localStorage.setItem(HEIGHT_KEY, Math.round(container.getBoundingClientRect().height));
|
||||||
|
};
|
||||||
|
|
||||||
|
if ('ResizeObserver' in window) {
|
||||||
|
var ro = new ResizeObserver(function () {
|
||||||
|
persistHeight();
|
||||||
|
});
|
||||||
|
ro.observe(container);
|
||||||
|
} else {
|
||||||
|
window.addEventListener('mouseup', persistHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resizeHandle) {
|
||||||
|
resizeHandle.addEventListener('pointerdown', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openViewerSidebar() {
|
||||||
|
if (!sidebarEl || !sidebarBody) return;
|
||||||
|
sidebarBody.appendChild(moveable);
|
||||||
|
sidebarEl.classList.add('open');
|
||||||
|
if (sidebarBtn) {
|
||||||
|
sidebarBtn.innerHTML = '<i class="bi bi-x-lg"></i> <span class="d-none d-sm-inline">Close sidebar</span>';
|
||||||
|
sidebarBtn.classList.replace('btn-outline-secondary', 'btn-secondary');
|
||||||
|
}
|
||||||
|
localStorage.setItem(MODE_KEY, 'sidebar');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeViewerSidebar() {
|
||||||
|
if (!sidebarEl) return;
|
||||||
|
moveableHome.appendChild(moveable);
|
||||||
|
sidebarEl.classList.remove('open');
|
||||||
|
if (sidebarBtn) {
|
||||||
|
sidebarBtn.innerHTML = '<i class="bi bi-layout-sidebar-reverse"></i> <span class="d-none d-sm-inline">Sidebar</span>';
|
||||||
|
sidebarBtn.classList.replace('btn-secondary', 'btn-outline-secondary');
|
||||||
|
}
|
||||||
|
localStorage.removeItem(MODE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebarBtn) {
|
||||||
|
sidebarBtn.addEventListener('click', function () {
|
||||||
|
if (sidebarEl && sidebarEl.classList.contains('open')) {
|
||||||
|
closeViewerSidebar();
|
||||||
|
} else {
|
||||||
|
openViewerSidebar();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebarCloseBtn) {
|
||||||
|
sidebarCloseBtn.addEventListener('click', closeViewerSidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bc = null;
|
||||||
|
try { bc = new BroadcastChannel('viva_load'); } catch (e) {}
|
||||||
|
|
||||||
|
function getViewerPayload() {
|
||||||
|
var namedStacks = mainViewerEl.getAttribute('data-named-stacks');
|
||||||
|
var images = mainViewerEl.getAttribute('data-images');
|
||||||
|
var viewerState = window.exportViewerState_main_viewer ? window.exportViewerState_main_viewer() : '';
|
||||||
|
var annotations = window.exportAnnotations_main_viewer ? window.exportAnnotations_main_viewer() : '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'open',
|
||||||
|
namedStacks: namedStacks || '',
|
||||||
|
images: images || '',
|
||||||
|
viewerstate: viewerState || '',
|
||||||
|
annotations: annotations || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function broadcastViewerState(payload) {
|
||||||
|
if (!payload) return;
|
||||||
|
localStorage.setItem(POPOUT_STATE_KEY, JSON.stringify(payload));
|
||||||
|
if (bc) {
|
||||||
|
try { bc.postMessage(payload); } catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPopoutPlaceholder() {
|
||||||
|
var existing = panel.querySelector('.viewer-popout-placeholder');
|
||||||
|
if (existing) return;
|
||||||
|
var placeholder = document.createElement('div');
|
||||||
|
placeholder.className = 'alert alert-secondary small py-2 mb-0 mt-1 d-flex align-items-center gap-2 viewer-popout-placeholder';
|
||||||
|
placeholder.innerHTML = '<i class="bi bi-box-arrow-up-right"></i> Viewer is open in a separate window. <button type="button" class="btn btn-sm btn-outline-secondary ms-auto" id="viewer-popout-bring-back">Bring back</button>';
|
||||||
|
panel.insertBefore(placeholder, moveableHome);
|
||||||
|
moveableHome.style.display = 'none';
|
||||||
|
document.getElementById('viewer-popout-bring-back').addEventListener('click', closePopout);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hidePopoutPlaceholder() {
|
||||||
|
var placeholder = panel.querySelector('.viewer-popout-placeholder');
|
||||||
|
if (placeholder) placeholder.remove();
|
||||||
|
moveableHome.style.display = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPopout() {
|
||||||
|
if (sidebarEl && sidebarEl.classList.contains('open')) {
|
||||||
|
closeViewerSidebar();
|
||||||
|
}
|
||||||
|
var payload = getViewerPayload();
|
||||||
|
broadcastViewerState(payload);
|
||||||
|
window.open('{% url "atlas:collection_viewer_local" %}', 'collectionTakeViewer', 'width=1200,height=900,noopener');
|
||||||
|
setTimeout(function () { broadcastViewerState(payload); }, 700);
|
||||||
|
localStorage.setItem(MODE_KEY, 'popout');
|
||||||
|
showPopoutPlaceholder();
|
||||||
|
if (popoutBtn) {
|
||||||
|
popoutBtn.innerHTML = '<i class="bi bi-x-lg"></i> <span class="d-none d-sm-inline">Bring back</span>';
|
||||||
|
popoutBtn.classList.replace('btn-outline-secondary', 'btn-secondary');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePopout() {
|
||||||
|
hidePopoutPlaceholder();
|
||||||
|
localStorage.removeItem(MODE_KEY);
|
||||||
|
if (popoutBtn) {
|
||||||
|
popoutBtn.innerHTML = '<i class="bi bi-box-arrow-up-right"></i> <span class="d-none d-sm-inline">Pop out</span>';
|
||||||
|
popoutBtn.classList.replace('btn-secondary', 'btn-outline-secondary');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popoutBtn) {
|
||||||
|
popoutBtn.addEventListener('click', function () {
|
||||||
|
if (localStorage.getItem(MODE_KEY) === 'popout') {
|
||||||
|
closePopout();
|
||||||
|
} else {
|
||||||
|
openPopout();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var savedMode = localStorage.getItem(MODE_KEY);
|
||||||
|
if (savedMode === 'sidebar') {
|
||||||
|
openViewerSidebar();
|
||||||
|
} else if (savedMode === 'popout' && popoutBtn) {
|
||||||
|
showPopoutPlaceholder();
|
||||||
|
setTimeout(function () { broadcastViewerState(getViewerPayload()); }, 300);
|
||||||
|
popoutBtn.innerHTML = '<i class="bi bi-x-lg"></i> <span class="d-none d-sm-inline">Bring back</span>';
|
||||||
|
popoutBtn.classList.replace('btn-outline-secondary', 'btn-secondary');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initViewerControls);
|
||||||
|
} else {
|
||||||
|
initViewerControls();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
{% endblock js %}
|
{% endblock js %}
|
||||||
|
|||||||
@@ -86,6 +86,7 @@
|
|||||||
document.title = "Case: {{current_case.title}}";
|
document.title = "Case: {{current_case.title}}";
|
||||||
console.log('ready');
|
console.log('ready');
|
||||||
const bc = new BroadcastChannel("viva_load");
|
const bc = new BroadcastChannel("viva_load");
|
||||||
|
const POPOUT_STATE_KEY = 'rad_local_viewer_popout_state';
|
||||||
|
|
||||||
// Centralized hide helper and delegated close handling for local viewer
|
// Centralized hide helper and delegated close handling for local viewer
|
||||||
function hideResourceLocal(){ const disp=document.getElementById('resourceDisplay'); const inner=document.getElementById('resourceInner'); if(disp) disp.style.display='none'; if(inner) inner.innerHTML=''; }
|
function hideResourceLocal(){ const disp=document.getElementById('resourceDisplay'); const inner=document.getElementById('resourceInner'); if(disp) disp.style.display='none'; if(inner) inner.innerHTML=''; }
|
||||||
@@ -114,6 +115,83 @@
|
|||||||
} catch (err) { console.warn('Viewer initialization deferred/fallback:', err); }
|
} catch (err) { console.warn('Viewer initialization deferred/fallback:', err); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyViewerPayload(payload) {
|
||||||
|
if (!payload || payload.type !== 'open') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = document.getElementById('root');
|
||||||
|
var awaiting = document.getElementById('awaiting-modal');
|
||||||
|
if (!root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (awaiting) {
|
||||||
|
awaiting.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.namedStacks) {
|
||||||
|
root.dataset.namedStacks = payload.namedStacks;
|
||||||
|
} else {
|
||||||
|
delete root.dataset.namedStacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.images) {
|
||||||
|
root.dataset.images = payload.images;
|
||||||
|
} else {
|
||||||
|
delete root.dataset.images;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.viewerstate && Object.keys(payload.viewerstate).length > 0) {
|
||||||
|
root.dataset.viewerstate = payload.viewerstate;
|
||||||
|
} else {
|
||||||
|
delete root.dataset.viewerstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.annotations && Object.keys(payload.annotations).length > 0) {
|
||||||
|
root.dataset.annotationjson = payload.annotations;
|
||||||
|
} else {
|
||||||
|
delete root.dataset.annotationjson;
|
||||||
|
}
|
||||||
|
|
||||||
|
mountDicomViewersSafely();
|
||||||
|
}
|
||||||
|
|
||||||
|
function persistViewerPayload(payload) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(POPOUT_STATE_KEY, JSON.stringify(payload));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Could not persist viewer payload', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPersistedViewerPayload() {
|
||||||
|
try {
|
||||||
|
var saved = localStorage.getItem(POPOUT_STATE_KEY);
|
||||||
|
return saved ? JSON.parse(saved) : null;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Could not read persisted viewer payload', e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var savedPayload = readPersistedViewerPayload();
|
||||||
|
if (savedPayload) {
|
||||||
|
applyViewerPayload(savedPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('storage', function (event) {
|
||||||
|
if (event.key !== POPOUT_STATE_KEY || !event.newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
applyViewerPayload(JSON.parse(event.newValue));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Could not parse updated viewer payload', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
bc.onmessage = (event) => {
|
bc.onmessage = (event) => {
|
||||||
// Ensure any existing overlay is closed on new load
|
// Ensure any existing overlay is closed on new load
|
||||||
try { hideResourceLocal(); } catch(_) {}
|
try { hideResourceLocal(); } catch(_) {}
|
||||||
@@ -179,33 +257,13 @@
|
|||||||
if (event.data.url && isMediaUrl(event.data.url)) {
|
if (event.data.url && isMediaUrl(event.data.url)) {
|
||||||
// show resource overlay and do not attempt to mount DICOM viewer
|
// show resource overlay and do not attempt to mount DICOM viewer
|
||||||
if (showResource(event.data.url)) {
|
if (showResource(event.data.url)) {
|
||||||
|
persistViewerPayload(event.data);
|
||||||
bc.postMessage({"type": "open-complete-local"});
|
bc.postMessage({"type": "open-complete-local"});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hide modal when data is loaded
|
persistViewerPayload(event.data);
|
||||||
document.getElementById("awaiting-modal").style.display = "none";
|
applyViewerPayload(event.data);
|
||||||
document.getElementById("root").dataset.images = event.data.images;
|
|
||||||
|
|
||||||
if (
|
|
||||||
event.data.viewerstate &&
|
|
||||||
Object.keys(event.data.viewerstate).length > 0
|
|
||||||
) {
|
|
||||||
document.getElementById("root").dataset.viewerstate = event.data.viewerstate;
|
|
||||||
} else {
|
|
||||||
delete document.getElementById("root").dataset.viewerstate;
|
|
||||||
}
|
|
||||||
console.log("Annotations:", event.data.annotations);
|
|
||||||
if (
|
|
||||||
event.data.annotations &&
|
|
||||||
Object.keys(event.data.annotations).length > 0
|
|
||||||
) {
|
|
||||||
document.getElementById("root").dataset.annotationjson = event.data.annotations;
|
|
||||||
} else {
|
|
||||||
delete document.getElementById("root").dataset.annotationjson;
|
|
||||||
}
|
|
||||||
|
|
||||||
mountDicomViewersSafely();
|
|
||||||
bc.postMessage({"type": "open-complete-local"});
|
bc.postMessage({"type": "open-complete-local"});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user