Final adjustments to routing behaviors

This commit is contained in:
Bruno Alves de Faria 2017-03-16 22:39:41 -03:00
parent 9ccebb76bd
commit e12fb0de35
24 changed files with 226 additions and 362 deletions

View File

@ -8,7 +8,7 @@
{{/section}} {{/section}}
{{#section 'headerAfterBrand'}} {{#section 'headerAfterBrand'}}
{{#if studyListToggleText}} {{#if studyListToggleText}}
<a class="btn studyListLinkSection pull-left js-toggle-studyList" id="{{dasherize studyListToggleText}}-btn"> <a href="#" class="btn studyListLinkSection pull-left js-toggle-studyList" id="{{dasherize studyListToggleText}}-btn">
{{studyListToggleText}} {{studyListToggleText}}
</a> </a>
{{/if}} {{/if}}

View File

@ -4,16 +4,10 @@ import { Router } from 'meteor/iron:router';
import { ReactiveVar } from 'meteor/reactive-var'; import { ReactiveVar } from 'meteor/reactive-var';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
const studylistContentId = 'studylistTab';
const viewerContentId = 'viewerTab';
// Define the OHIF.viewer.data global object
Template.app.onCreated(() => { Template.app.onCreated(() => {
const instance = Template.instance(); const instance = Template.instance();
instance.headerClasses = new ReactiveVar(''); instance.headerClasses = new ReactiveVar('');
OHIF.viewer.data = instance.data.viewerData || {};
OHIF.header.dropdown.setItems([{ OHIF.header.dropdown.setItems([{
action: OHIF.user.audit, action: OHIF.user.audit,
text: 'View Audit Log', text: 'View Audit Log',
@ -50,6 +44,9 @@ Template.app.onCreated(() => {
const routeName = currentRoute.route.getName(); const routeName = currentRoute.route.getName();
const isViewer = routeName.indexOf('viewer') === 0; const isViewer = routeName.indexOf('viewer') === 0;
// Define the OHIF.viewer.data global object
OHIF.viewer.data = OHIF.viewer.data || Session.get('ViewerData') || {};
// Add or remove the strech class from body // Add or remove the strech class from body
$(document.body)[isViewer ? 'addClass' : 'removeClass']('stretch'); $(document.body)[isViewer ? 'addClass' : 'removeClass']('stretch');
@ -62,23 +59,41 @@ Template.app.onCreated(() => {
}); });
Template.app.events({ Template.app.events({
'click .js-toggle-studyList'(event) { 'click .js-toggle-studyList'(event, instance) {
OHIF.ui.unsavedChanges.presentProactiveDialog('viewer.*', (hasChanges, userChoice) => { event.preventDefault();
if (hasChanges) { event.stopPropagation();
const isViewer = Session.get('ViewerOpened');
switch (userChoice) {
case 'abort-action':
return;
case 'save-changes':
OHIF.ui.unsavedChanges.trigger('viewer', 'save', false);
OHIF.ui.unsavedChanges.clear('viewer.*');
break;
case 'abandon-changes':
OHIF.ui.unsavedChanges.clear('viewer.*');
break;
}
if (!isViewer) {
const timepointId = OHIF.viewer.data.currentTimepointId;
if (timepointId) {
Router.go('viewerTimepoint', { timepointId });
} else {
const { studyInstanceUids } = OHIF.viewer.data;
Router.go('viewerStudies', { studyInstanceUids });
} }
return;
}
OHIF.ui.unsavedChanges.presentProactiveDialog('viewer.*', (hasChanges, userChoice) => {
if (!hasChanges) {
return Router.go('studylist');
}
switch (userChoice) {
case 'abort-action':
return;
case 'save-changes':
OHIF.ui.unsavedChanges.trigger('viewer', 'save', false);
OHIF.ui.unsavedChanges.clear('viewer.*');
break;
case 'abandon-changes':
OHIF.ui.unsavedChanges.clear('viewer.*');
break;
}
Router.go('studylist');
}, { }, {
position: { position: {
x: event.clientX + 15, x: event.clientX + 15,
@ -92,24 +107,12 @@ Template.app.helpers({
userName: OHIF.user.getName, userName: OHIF.user.getName,
studyListToggleText() { studyListToggleText() {
const contentId = Session.get('activeContentId'); const isViewer = Session.get('ViewerOpened');
// If the Viewer has not been opened yet, 'Back to viewer' should // Return empty if viewer was not opened yet
// not be displayed if (!OHIF.viewer.data.studyInstanceUids) return;
const viewerContentExists = !!Object.keys(OHIF.viewer.data).length;
if (!viewerContentExists) {
return;
}
if (contentId === studylistContentId) { return isViewer ? 'Study list' : 'Back to viewer';
return 'Back to viewer';
} else if (contentId === viewerContentId) {
return 'Study list';
}
},
onStudyList() {
return (Session.get('activeContentId') === 'studylistTab');
}, },
dasherize(text) { dasherize(text) {

View File

@ -86,12 +86,3 @@ body>.header
padding: 0 padding: 0
position: absolute position: absolute
top: 0 top: 0
#studylistTabs
height: "calc(100% - %s)" % $topBarHeight
.tab-pane
.viewerContainer
.loadingTextDiv
theme('color', '$textSecondaryColor')
font-size: 30px

View File

@ -1,15 +1,17 @@
<template name="viewer"> <template name="viewer">
{{#if and Template.subscriptionsReady dataSourcesReady}} <div class="viewerDialogs">
<div class="viewerDialogs"> {{#if and Template.subscriptionsReady dataSourcesReady}}
{{>confirmDeleteDialog}} {{>confirmDeleteDialog}}
{{>measurementTableHUD (clone this)}} {{>measurementTableHUD (clone this)}}
{{>cineDialog}} {{>cineDialog}}
</div> {{/if}}
<div id="viewer"> </div>
<div id="viewer">
{{#if and Template.subscriptionsReady dataSourcesReady}}
{{>toolbarSection (clone this)}} {{>toolbarSection (clone this)}}
{{>viewerSection (clone this)}} {{>viewerSection (clone this)}}
</div> {{else}}
{{else}} {{>loadingText}}
{{>loadingText}} {{/if}}
{{/if}} </div>
</template> </template>

View File

@ -36,7 +36,8 @@ Template.viewer.onCreated(() => {
const instance = Template.instance(); const instance = Template.instance();
const { TimepointApi, MeasurementApi, ConformanceCriteria } = OHIF.measurements; const { TimepointApi, MeasurementApi, ConformanceCriteria } = OHIF.measurements;
const currentTimepointId = instance.data.currentTimepointId;
const currentTimepointId = OHIF.viewer.data.currentTimepointId;
const timepointApi = new TimepointApi(currentTimepointId); const timepointApi = new TimepointApi(currentTimepointId);
const measurementApi = new MeasurementApi(timepointApi); const measurementApi = new MeasurementApi(timepointApi);
const conformanceCriteria = new ConformanceCriteria(measurementApi, timepointApi); const conformanceCriteria = new ConformanceCriteria(measurementApi, timepointApi);
@ -88,6 +89,9 @@ Template.viewer.onCreated(() => {
OHIF.viewer.data.loadedSeriesData = {}; OHIF.viewer.data.loadedSeriesData = {};
} }
// Store the viewer data in session for further user
Session.setPersistent('ViewerData', OHIF.viewer.data);
Session.set('activeViewport', OHIF.viewer.data.activeViewport || false); Session.set('activeViewport', OHIF.viewer.data.activeViewport || false);
// Set lesion tool buttons as disabled if pixel spacing is not available for active element // Set lesion tool buttons as disabled if pixel spacing is not available for active element
@ -156,13 +160,8 @@ Template.viewer.onCreated(() => {
measurementApi.priorTimepointId = prior.timepointId; measurementApi.priorTimepointId = prior.timepointId;
} }
if (instance.data.currentTimepointId) { // Enable/Disable Lesion Tracker Tools if the opened study is associated or not
// Enable Lesion Tracker Tools if the opened study is associated OHIF.lesiontracker.toggleLesionTrackerToolsButtons(!!currentTimepointId);
OHIF.lesiontracker.toggleLesionTrackerToolsButtons(true);
} else {
// Disable Lesion Tracker Tools if the opened study is not associated
OHIF.lesiontracker.toggleLesionTrackerToolsButtons(false);
}
let firstMeasurementActivated = false; let firstMeasurementActivated = false;
instance.autorun(() => { instance.autorun(() => {
@ -311,7 +310,7 @@ const initHangingProtocol = () => {
const layoutManager = OHIF.viewerbase.layoutManager; const layoutManager = OHIF.viewerbase.layoutManager;
// Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata // Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata
const studyMetadataSource = new StudyList.classes.OHIFStudyMetadataSource(); const studyMetadataSource = new OHIF.studylist.classes.OHIFStudyMetadataSource();
// Creates Protocol Engine object with required arguments // Creates Protocol Engine object with required arguments
const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, [], studyMetadataSource); const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, [], studyMetadataSource);

View File

@ -2,9 +2,6 @@ import { Meteor } from 'meteor/meteor';
import { Router } from 'meteor/iron:router'; import { Router } from 'meteor/iron:router';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
// TODO: remove the line below
window.Router = Router;
Router.configure({ Router.configure({
layoutTemplate: 'layout', layoutTemplate: 'layout',
loadingTemplate: 'layout' loadingTemplate: 'layout'
@ -30,11 +27,17 @@ Router.onBeforeAction(function() {
}); });
Router.route('/', function() { Router.route('/', function() {
this.redirect('/studylist'); Router.go('studylist', {}, { replaceState: true });
}, { name: 'home' }); }, { name: 'home' });
Router.route('/studylist', function() { Router.route('/studylist', function() {
this.render('app', { data: { template: 'studylist' } }); // Retrieve the timepoints data to display in studylist
const promise = OHIF.studylist.timepointApi.retrieveTimepoints({}).then(() => {
this.render('app', { data: { template: 'studylist' } });
});
// Show loading state while preparing the studylist data
OHIF.ui.showDialog('dialogLoading', { promise });
}, { name: 'studylist' }); }, { name: 'studylist' });
Router.route('/viewer/timepoints/:timepointId', function() { Router.route('/viewer/timepoints/:timepointId', function() {

View File

@ -1,46 +1,65 @@
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { Session } from 'meteor/session'; import { Session } from 'meteor/session';
import { Router } from 'meteor/iron:router';
import { ReactiveVar } from 'meteor/reactive-var';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
const studylistContentId = 'studylistTab'; Template.ohifViewer.onCreated(() => {
let lastContentId; const instance = Template.instance();
instance.headerClasses = new ReactiveVar('');
// Define the OHIF.viewer.data global object
OHIF.viewer.data = instance.data.viewerData || {};
OHIF.header.dropdown.setItems([{
action: () => OHIF.ui.showDialog('serverInformationModal'),
text: 'Server Information',
icon: 'fa fa-server fa-lg',
separatorAfter: true
}, {
action: () => $('#aboutModal').show(),
text: 'About',
icon: 'fa fa-info'
}]);
instance.autorun(() => {
const currentRoute = Router.current();
if (!currentRoute) return;
const routeName = currentRoute.route.getName();
const isViewer = routeName.indexOf('viewer') === 0;
// Add or remove the strech class from body
$(document.body)[isViewer ? 'addClass' : 'removeClass']('stretch');
// Set the header on its bigger version if the viewer is not opened
instance.headerClasses.set(isViewer ? '' : 'header-big');
// Set the viewer open state on session
Session.set('ViewerOpened', isViewer);
});
});
Template.ohifViewer.events({ Template.ohifViewer.events({
'click .js-toggle-studyList'() { 'click .js-toggle-studyList'(event, instance) {
const contentId = Session.get('activeContentId'); event.preventDefault();
const isViewer = Session.get('ViewerOpened');
if (contentId !== studylistContentId) { if (isViewer) {
switchToTab(studylistContentId); Router.go('studylist');
} else { } else {
switchToTab(lastContentId); const { studyInstanceUids } = OHIF.viewer.data;
Router.go('viewerStudies', { studyInstanceUids });
} }
},
'click #serverInformation'() {
OHIF.ui.showDialog('serverInformationModal');
} }
}); });
Template.ohifViewer.helpers({ Template.ohifViewer.helpers({
studyListToggleText() { studyListToggleText() {
const contentId = Session.get('activeContentId'); const isViewer = Session.get('ViewerOpened');
// If the Viewer has not been opened yet, 'Back to viewer' should // Return empty if viewer was not opened yet
// not be displayed if (!OHIF.viewer.data.studyInstanceUids) return;
const viewerContentExists = !!Object.keys(OHIF.viewer.data).length;
if (!viewerContentExists) {
return;
}
if (contentId === studylistContentId) { return isViewer ? 'Study list' : 'Back to viewer';
return 'Back to viewer';
} else {
lastContentId = contentId;
return 'Study list';
}
},
onStudyList() {
return (Session.get('activeContentId') === 'studylistTab');
} }
}); });

View File

@ -76,12 +76,3 @@ $expandedHeight = 160px
padding: 0 padding: 0
position: absolute position: absolute
top: 0 top: 0
#studylistTabs
height: "calc(100% - %s)" % $topBarHeight
.tab-pane
.viewerContainer
.loadingTextDiv
theme('color', '$textSecondaryColor')
font-size: 30px

View File

@ -25,10 +25,10 @@ const initHangingProtocol = () => {
const layoutManager = OHIF.viewerbase.layoutManager; const layoutManager = OHIF.viewerbase.layoutManager;
// Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata // Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata
const studyMetadataSource = new StudyList.classes.OHIFStudyMetadataSource(); const studyMetadataSource = new OHIF.studylist.classes.OHIFStudyMetadataSource();
// Get prior studies map // Get prior studies map
const studyPriorsMap = StudyList.functions.getStudyPriorsMap(studyMetadataList); const studyPriorsMap = OHIF.studylist.functions.getStudyPriorsMap(studyMetadataList);
// Creates Protocol Engine object with required arguments // Creates Protocol Engine object with required arguments
const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, studyPriorsMap, studyMetadataSource); const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, studyPriorsMap, studyMetadataSource);

View File

@ -1,5 +1,4 @@
// StudyList-related functions // StudyList-related functions
import './studylist/openNewTabWithTimepoint.js';
import './studylist/studylistModification.js'; import './studylist/studylistModification.js';
// Library functions // Library functions

View File

@ -1,92 +0,0 @@
import { OHIF } from 'meteor/ohif:core';
/**
* Opens a new tab in the tabbed studylist environment using
* a given timepoint and new tab title.
*
* @param timepointId The UID of the Timepoint to be opened
* @param title The title to be used for the tab heading
*/
OHIF.lesiontracker.openNewTabWithTimepoint = timepointId => {
const Timepoints = OHIF.studylist.timepointApi.timepoints;
const timepoint = Timepoints.findOne({
timepointId: timepointId
});
if (!timepoint) {
throw 'No such timepoint exists';
}
// Get the relevant studyInstanceUids given the timepoints
const data = getDataFromTimepoint(timepoint);
if (!data.studyInstanceUids) {
throw 'No studies found that are related to this timepoint';
}
// Update the OHIF.viewer.data global object
OHIF.viewer.data = {
studyInstanceUids: data.studyInstanceUids,
timepointIds: data.timepointIds,
currentTimepointId: timepointId
};
// Switch to the new tab
switchToTab(contentId);
};
/**
* Retrieves related studies given a Baseline or Follow-up Timepoint
*
* @param timepoint
* @returns {Array}
*/
function getDataFromTimepoint(timepoint) {
let relatedStudies = [timepoint.studyInstanceUids[0]];
// If this is the baseline, we should stop here and return the relevant studies
if (isBaseline(timepoint)) {
return {
studyInstanceUids: relatedStudies,
timepointIds: [timepoint.timepointId]
};
}
// Otherwise, this is a follow-up exam, so we should also find the baseline timepoint,
// and all studies related to it. We also enforce that the Baseline should have a studyDate
// prior to the latest studyDate in the current (Follow-up) Timepoint.
const Timepoints = OHIF.studylist.timepointApi.timepoints;
const baseline = Timepoints.findOne({
timepointType: 'baseline',
patientId: timepoint.patientId,
latestDate: {
$lte: timepoint.latestDate
}
});
let timepointIds = [];
if (baseline) {
// NOTE: Temporarily added [0], since we only need one study per timepoint to load immediately?
relatedStudies = relatedStudies.concat(baseline.studyInstanceUids[0]);
timepointIds.push(baseline.timepointId);
} else {
OHIF.log.warn('No Baseline found while opening a Follow-up Timepoint');
}
timepointIds.push(timepoint.timepointId);
return {
studyInstanceUids: relatedStudies,
timepointIds: timepointIds
};
}
/**
* Checks if a Timepoints is a baseline or not
* (abstracting this for later use, since I expect it to get more complex)
*
* @param timepoint a document from the Timepoints Collection
* @returns {boolean} Whether or not the timepoint is stored as a Baseline
*/
function isBaseline(timepoint) {
return (timepoint.timepointType === 'baseline');
}

View File

@ -1,4 +1,5 @@
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { Router } from 'meteor/iron:router';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
Meteor.startup(function() { Meteor.startup(function() {
@ -22,28 +23,8 @@ const dblClickOnStudy = data => {
const timepoint = timepointApi.study(data.studyInstanceUid)[0]; const timepoint = timepointApi.study(data.studyInstanceUid)[0];
if (timepoint) { if (timepoint) {
OHIF.lesiontracker.openNewTabWithTimepoint(timepoint.timepointId); Router.go('viewerTimepoint', { timepointId: timepoint.timepointId });
} else { } else {
openTab(data.studyInstanceUid); Router.go('viewerStudies', { studyInstanceUids: data.studyInstanceUid });
} }
}; };
/**
* Opens a study
*
* @param studyInstanceUid The UID of the Study to be opened
* @param title The title to be used for the tab heading
*/
const openTab = studyInstanceUid => {
const contentId = 'viewerTab';
// Update the OHIF.viewer.data global object
OHIF.viewer.data = {
contentId: contentId,
isUnassociatedStudy: true,
studyInstanceUids: [studyInstanceUid]
};
// Switch to the new tab
switchToTab(contentId);
};

View File

@ -181,6 +181,6 @@ Template.caseProgress.events({
return; return;
} }
instance.saveData().then(() => switchToTab('studylistTab')); instance.saveData();
} }
}); });

View File

@ -1,4 +1,5 @@
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { Router } from 'meteor/iron:router';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
/** /**
@ -43,17 +44,9 @@ const viewStudies = () => {
return; return;
} }
const studyInstanceUids = selectedStudies.map(study => study.studyInstanceUid); const studyInstanceUids = selectedStudies.map(study => study.studyInstanceUid).join(';');
const contentId = 'viewerTab';
// Update the OHIF.viewer.data global object Router.go('viewerStudies', { studyInstanceUids });
OHIF.viewer.data = {
contentId: contentId,
studyInstanceUids: studyInstanceUids
};
// Switch to the new tab
switchToTab(contentId);
}; };
/** /**

View File

@ -245,7 +245,7 @@ Template.studylistResult.onRendered(() => {
endDate = today; endDate = today;
} }
const datePicker = $studyDate.daterangepicker({ instance.datePicker = $studyDate.daterangepicker({
maxDate: today, maxDate: today,
autoUpdateInput: true, autoUpdateInput: true,
startDate: startDate, startDate: startDate,
@ -258,7 +258,7 @@ Template.studylistResult.onRendered(() => {
}).data('daterangepicker'); }).data('daterangepicker');
if (startDate && endDate) { if (startDate && endDate) {
datePicker.updateInputText(); instance.datePicker.updateInputText();
$studyDate.trigger('change'); $studyDate.trigger('change');
} else { } else {
// Retrieve all studies // Retrieve all studies
@ -266,6 +266,13 @@ Template.studylistResult.onRendered(() => {
} }
}); });
Template.studylistResult.onDestroyed(() => {
const instance = Template.instance();
// Destroy the daterangepicker to prevent residual elements on DOM
instance.datePicker.remove();
});
function resetSortingColumns(instance, sortingColumn) { function resetSortingColumns(instance, sortingColumn) {
Object.keys(instance.sortingColumns.keys).forEach(key => { Object.keys(instance.sortingColumns.keys).forEach(key => {
if (key !== sortingColumn) { if (key !== sortingColumn) {

View File

@ -1,5 +1,6 @@
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { Blaze } from 'meteor/blaze'; import { Blaze } from 'meteor/blaze';
import * as Hammer from 'hammerjs';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
// Clear all selected studies // Clear all selected studies

View File

@ -1,41 +1,27 @@
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import { Viewerbase } from 'meteor/ohif:viewerbase'; import { Router } from 'meteor/iron:router';
// Classes // Classes
import { OHIFStudyMetadataSource } from './OHIFStudyMetadataSource'; import { OHIFStudyMetadataSource } from './OHIFStudyMetadataSource';
import { OHIFStudySummary } from './OHIFStudySummary'; import { OHIFStudySummary } from './OHIFStudySummary';
// Functions // Functions
import { getStudyPriors } from './getStudyPriors'; import { getStudyPriors } from './getStudyPriors';
import { getStudyPriorsMap } from './getStudyPriorsMap'; import { getStudyPriorsMap } from './getStudyPriorsMap';
StudyList = { OHIF.studylist.functions = {
functions: { getStudyPriors,
getStudyPriors, getStudyPriorsMap
getStudyPriorsMap };
},
classes: { OHIF.studylist.classes = {
OHIFStudyMetadataSource, OHIFStudyMetadataSource,
OHIFStudySummary OHIFStudySummary
} };
const dblClickOnStudy = data => {
Router.go('viewerStudies', { studyInstanceUids: data.studyInstanceUid });
}; };
OHIF.studylist.callbacks.dblClickOnStudy = dblClickOnStudy; OHIF.studylist.callbacks.dblClickOnStudy = dblClickOnStudy;
OHIF.studylist.callbacks.middleClickOnStudy = dblClickOnStudy; OHIF.studylist.callbacks.middleClickOnStudy = dblClickOnStudy;
function dblClickOnStudy(data) {
openNewTab(data.studyInstanceUid);
}
let currentServerChangeHandlerFirstRun = true;
const currentServerChangeHandler = () => {
if (currentServerChangeHandlerFirstRun) {
currentServerChangeHandlerFirstRun = false;
return;
}
switchToTab('studylistTab');
};
CurrentServer.find().observe({
added: currentServerChangeHandler,
changed: currentServerChangeHandler
});

View File

@ -52,10 +52,4 @@ Package.onUse(function(api) {
api.export('getCurrentServer', ['client', 'server']); api.export('getCurrentServer', ['client', 'server']);
api.export('Services', 'server'); api.export('Services', 'server');
// Export StudyList helper functions for usage in Routes
api.export('StudyList');
// Export the Collections
api.export('StudyListSelectedStudies', 'client');
}); });

View File

@ -46,20 +46,11 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
layoutManager.viewportData[viewportIndex] = layoutManager.viewportData[viewportIndex] || {}; layoutManager.viewportData[viewportIndex] = layoutManager.viewportData[viewportIndex] || {};
layoutManager.viewportData[viewportIndex].viewportIndex = viewportIndex; layoutManager.viewportData[viewportIndex].viewportIndex = viewportIndex;
// Get the contentID of the current study list tab, if the viewport is running // Stop here if no data was defined for the viewer
// alongside the study list package if (!OHIF.viewer.data) return;
const contentId = Session.get('activeContentId');
// If the viewer is inside a tab, create an object related to the specified viewport // This data will be saved so that the viewport can be reloaded to the same state later
// This data will be saved so that the tab can be reloaded to the same state after tabs OHIF.viewer.data.loadedSeriesData[viewportIndex] = {};
// are switched
if (contentId) {
if (!OHIF.viewer.data) {
return;
}
OHIF.viewer.data.loadedSeriesData[viewportIndex] = {};
}
// Create shortcut to displaySet // Create shortcut to displaySet
const displaySet = data.displaySet; const displaySet = data.displaySet;
@ -67,7 +58,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
// Get stack from Stack Manager // Get stack from Stack Manager
let stack = StackManager.findStack(displaySet.displaySetInstanceUid); let stack = StackManager.findStack(displaySet.displaySetInstanceUid);
// Make sure if the stack is already loaded in the stack manager, otherwise create it // Make sure if the stack is already loaded in the stack manager, otherwise create it
if(!stack || !stack.imageIds) { if (!stack || !stack.imageIds) {
stack = StackManager.makeAndAddStack(data.study, displaySet); stack = StackManager.makeAndAddStack(data.study, displaySet);
} }
@ -144,7 +135,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
try { try {
imagePromise = cornerstone.loadAndCacheImage(imageId); imagePromise = cornerstone.loadAndCacheImage(imageId);
} catch (error) { } catch (error) {
console.log(error); OHIF.log.info(error);
if (!imagePromise) { if (!imagePromise) {
errorLoadingHandler(element, imageId, error); errorLoadingHandler(element, imageId, error);
return; return;
@ -163,7 +154,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
const callbacks = imageViewerViewportData.callbacks; const callbacks = imageViewerViewportData.callbacks;
// Check if it has before loadAndCacheImage callback // Check if it has before loadAndCacheImage callback
if(typeof callbacks.before === 'function') { if (typeof callbacks.before === 'function') {
OHIF.log.info('imageViewerViewport before loadAndCacheImage callback'); OHIF.log.info('imageViewerViewport before loadAndCacheImage callback');
callbacks.before(imagePromise, templateData); callbacks.before(imagePromise, templateData);
} }
@ -262,8 +253,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
let fps; let fps;
if (multiframeMetadata && multiframeMetadata.averageFrameRate > 0) { if (multiframeMetadata && multiframeMetadata.averageFrameRate > 0) {
fps = multiframeMetadata.averageFrameRate; fps = multiframeMetadata.averageFrameRate;
} } else {
else {
fps = OHIF.viewer.cine.framesPerSecond; fps = OHIF.viewer.cine.framesPerSecond;
} }
@ -431,7 +421,6 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
// Check if image plane (orientation / location) data is present for the current image // Check if image plane (orientation / location) data is present for the current image
const imagePlane = cornerstoneTools.metaData.get('imagePlane', image.imageId); const imagePlane = cornerstoneTools.metaData.get('imagePlane', image.imageId);
if (imagePlane && imagePlane.frameOfReferenceUID) { if (imagePlane && imagePlane.frameOfReferenceUID) {
// If it is, add this element to the global synchronizer... // If it is, add this element to the global synchronizer...
OHIF.viewer.updateImageSynchronizer.add(element); OHIF.viewer.updateImageSynchronizer.add(element);
@ -449,7 +438,6 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
} }
} }
// Set the active viewport based on the Session variable // Set the active viewport based on the Session variable
// This is done to ensure that the active element has the current // This is done to ensure that the active element has the current
// focus, so that keyboard events are triggered. // focus, so that keyboard events are triggered.
@ -465,8 +453,8 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
// Update the LayoutManagerUpdated session key // Update the LayoutManagerUpdated session key
layoutManager.updateSession(); layoutManager.updateSession();
// Check if it has after loadAndCacheImage callback // Check if it has after loadAndCacheImage callback
if(typeof callbacks.after === 'function') { if (typeof callbacks.after === 'function') {
OHIF.log.info('imageViewerViewport after callback'); OHIF.log.info('imageViewerViewport after callback');
callbacks.after(image, templateData, element); callbacks.after(image, templateData, element);
} }
@ -531,7 +519,7 @@ Template.imageViewerViewport.onRendered(function() {
let { currentImageIdIndex } = templateData; let { currentImageIdIndex } = templateData;
const { viewport, studyInstanceUid, seriesInstanceUid, renderedCallback, displaySetInstanceUid } = templateData; const { viewport, studyInstanceUid, seriesInstanceUid, renderedCallback, displaySetInstanceUid } = templateData;
if(!currentImageIdIndex) { if (!currentImageIdIndex) {
currentImageIdIndex = 0; currentImageIdIndex = 0;
} }
@ -570,8 +558,9 @@ Template.imageViewerViewport.onDestroyed(function() {
OHIF.log.info('imageViewerViewport onDestroyed'); OHIF.log.info('imageViewerViewport onDestroyed');
// When a viewport element is being destroyed // When a viewport element is being destroyed
var element = this.find('.imageViewerViewport'); const element = this.find('.imageViewerViewport');
if (!element || $(element).hasClass('empty') || !$(element).find('canvas').length) { const $element = $(element);
if (!element || $element.hasClass('empty') || !$element.find('canvas').length) {
return; return;
} }
@ -600,7 +589,7 @@ Template.imageViewerViewport.onDestroyed(function() {
// Trigger custom Destroy Viewport event // Trigger custom Destroy Viewport event
// for compatibility with other systems // for compatibility with other systems
$(element).trigger('OHIFDestroyedViewport'); $element.trigger('OHIFDestroyedViewport');
// Disable the viewport element with Cornerstone // Disable the viewport element with Cornerstone
// This also triggers the removal of the element from all available // This also triggers the removal of the element from all available

View File

@ -1,5 +1,14 @@
@import "{ohif:design}/app" @import "{ohif:design}/app"
#viewer
height: "calc(100% - %s)" % $topBarHeight
&>.loadingTextDiv
theme('color', '$textSecondaryColor')
font-size: 30px
height: 100%
line-height: "calc(100vh - %s)" % $topBarHeight
.viewerMain .viewerMain
width: 100% width: 100%
height: 100% height: 100%

View File

@ -14,65 +14,65 @@ export class ResizeViewportManager {
repositionStudySeriesQuickSwitch() { repositionStudySeriesQuickSwitch() {
OHIF.log.info('ResizeViewportManager repositionStudySeriesQuickSwitch'); OHIF.log.info('ResizeViewportManager repositionStudySeriesQuickSwitch');
const activeTab = Session.get('activeContentId'); // Stop here if viewer is not displayed
if(activeTab === 'viewerTab') { const isViewer = Session.get('ViewerOpened');
const nViewports = OHIF.viewerbase.layoutManager.viewportData.length; if (!isViewer) return;
if(nViewports && nViewports > 1) { // Stop here if there is no one or only one viewport
const viewer = $('#viewer'); const nViewports = OHIF.viewerbase.layoutManager.viewportData.length;
const leftSidebar = viewer.find('.sidebar-left.sidebar-open'); if (!nViewports || nViewports <= 1) return;
const rightSidebar = viewer.find('.sidebar-right.sidebar-open');
const leftQuickSwitch = $('.quickSwitchWrapper.left'); const $viewer = $('#viewer');
const rightQuickSwitch = $('.quickSwitchWrapper.right'); const leftSidebar = $viewer.find('.sidebar-left.sidebar-open');
const rightSidebar = $viewer.find('.sidebar-right.sidebar-open');
const hasLeftSidebar = leftSidebar.length > 0; const $leftQuickSwitch = $('.quickSwitchWrapper.left');
const hasRightSidebar = rightSidebar.length > 0; const $rightQuickSwitch = $('.quickSwitchWrapper.right');
rightQuickSwitch.removeClass('left-sidebar-only'); const hasLeftSidebar = leftSidebar.length > 0;
leftQuickSwitch.removeClass('right-sidebar-only'); const hasRightSidebar = rightSidebar.length > 0;
let leftOffset = 0; $rightQuickSwitch.removeClass('left-sidebar-only');
$leftQuickSwitch.removeClass('right-sidebar-only');
if(hasLeftSidebar) { let leftOffset = 0;
leftOffset = ( leftSidebar.width()/$(window).width() ) * 100;
if(!hasRightSidebar) { if (hasLeftSidebar) {
rightQuickSwitch.addClass('left-sidebar-only'); leftOffset = (leftSidebar.width() / $(window).width()) * 100;
}
}
if(hasRightSidebar && !hasLeftSidebar) { if (!hasRightSidebar) {
leftQuickSwitch.addClass('right-sidebar-only'); $rightQuickSwitch.addClass('left-sidebar-only');
}
const leftPosition = ( ($('#imageViewerViewports').width() / nViewports) / $(window).width() ) * 100 + leftOffset;
const rightPosition = 100 - leftPosition;
leftQuickSwitch.css('right', rightPosition + '%');
rightQuickSwitch.css('left', leftPosition + '%');
} }
} }
if (hasRightSidebar && !hasLeftSidebar) {
$leftQuickSwitch.addClass('right-sidebar-only');
}
const leftPosition = (($('#imageViewerViewports').width() / nViewports) / $(window).width()) * 100 + leftOffset;
const rightPosition = 100 - leftPosition;
$leftQuickSwitch.css('right', rightPosition + '%');
$rightQuickSwitch.css('left', leftPosition + '%');
} }
// Relocate dialogs positions // Relocate dialogs positions
relocateDialogs(){ relocateDialogs(){
OHIF.log.info('ResizeViewportManager relocateDialogs'); OHIF.log.info('ResizeViewportManager relocateDialogs');
const bottomRightDialogs = $('#cineDialog, #annotationDialog, #textMarkerOptionsDialog'); const $bottomRightDialogs = $('#cineDialog, #annotationDialog, #textMarkerOptionsDialog');
bottomRightDialogs.css({ $bottomRightDialogs.css({
top: '', // This removes the CSS property completely top: '', // This removes the CSS property completely
left: '', left: '',
bottom: 0, bottom: 0,
right: 0 right: 0
}); });
const centerDialogs = $('.draggableDialog').not(bottomRightDialogs); const centerDialogs = $('.draggableDialog').not($bottomRightDialogs);
centerDialogs.css({ centerDialogs.css({
top: 0, top: 0,
left:0, left: 0,
bottom: 0, bottom: 0,
right: 0 right: 0
}); });
@ -83,7 +83,6 @@ export class ResizeViewportManager {
OHIF.log.info('ResizeViewportManager resizeScrollbars'); OHIF.log.info('ResizeViewportManager resizeScrollbars');
const currentOverlay = $(element).siblings('.imageViewerViewportOverlay'); const currentOverlay = $(element).siblings('.imageViewerViewportOverlay');
const imageControls = currentOverlay.find('.imageControls');
currentOverlay.find('.imageControls').height($(element).height()); currentOverlay.find('.imageControls').height($(element).height());
// Set it's width to its parent's height // Set it's width to its parent's height
@ -125,7 +124,7 @@ export class ResizeViewportManager {
resizeViewportElements() { resizeViewportElements() {
this.relocateDialogs(); this.relocateDialogs();
const viewportResizeTimer = setTimeout(() => { setTimeout(() => {
this.repositionStudySeriesQuickSwitch(); this.repositionStudySeriesQuickSwitch();
const elements = $('.imageViewerViewport').not('.empty'); const elements = $('.imageViewerViewport').not('.empty');
@ -162,6 +161,7 @@ export class ResizeViewportManager {
resizeHandler = this.handleResize.bind(this); resizeHandler = this.handleResize.bind(this);
this._resizeHandler = resizeHandler; this._resizeHandler = resizeHandler;
} }
return resizeHandler; return resizeHandler;
} }
} }

View File

@ -15,11 +15,11 @@ export const renderViewer = (context, params) => {
// Render the viewer when the data is ready // Render the viewer when the data is ready
promise.then(({ studies, viewerData }) => { promise.then(({ studies, viewerData }) => {
OHIF.viewer.data = viewerData;
context.render('app', { context.render('app', {
data: { data: {
template: 'viewer', template: 'viewer',
studies, studies
viewerData
} }
}); });
}); });

View File

@ -76,12 +76,3 @@ $expandedHeight = 160px
padding: 0 padding: 0
position: absolute position: absolute
top: 0 top: 0
#studylistTabs
height: "calc(100% - %s)" % $topBarHeight
.tab-pane
.viewerContainer
.loadingTextDiv
theme('color', '$textSecondaryColor')
font-size: 30px

View File

@ -1,3 +1,7 @@
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session';
import { Tracker } from 'meteor/tracker';
import { ReactiveDict } from 'meteor/reactive-dict';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import 'meteor/ohif:viewerbase'; import 'meteor/ohif:viewerbase';
import 'meteor/ohif:metadata'; import 'meteor/ohif:metadata';
@ -12,8 +16,6 @@ OHIF.viewer.functionList = {
invert: viewportUtils.invert invert: viewportUtils.invert
}; };
Session.set('activeContentId', 'standalone');
Session.setDefault('activeViewport', false); Session.setDefault('activeViewport', false);
Session.setDefault('leftSidebar', false); Session.setDefault('leftSidebar', false);
Session.setDefault('rightSidebar', false); Session.setDefault('rightSidebar', false);
@ -34,7 +36,7 @@ const initHangingProtocol = () => {
const layoutManager = OHIF.viewerbase.layoutManager; const layoutManager = OHIF.viewerbase.layoutManager;
// Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata // Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata
const studyMetadataSource = new StudyList.classes.OHIFStudyMetadataSource(); const studyMetadataSource = new OHIF.studylist.classes.OHIFStudyMetadataSource();
// Creates Protocol Engine object with required arguments // Creates Protocol Engine object with required arguments
const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, [], studyMetadataSource); const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, [], studyMetadataSource);
@ -52,39 +54,35 @@ Template.viewer.onCreated(() => {
instance.data.state.set('leftSidebar', Session.get('leftSidebar')); instance.data.state.set('leftSidebar', Session.get('leftSidebar'));
instance.data.state.set('rightSidebar', Session.get('rightSidebar')); instance.data.state.set('rightSidebar', Session.get('rightSidebar'));
const contentId = instance.data.contentId; if (OHIF.viewer.data && OHIF.viewer.data.loadedSeriesData) {
if (ViewerData[contentId] && ViewerData[contentId].loadedSeriesData) {
OHIF.log.info('Reloading previous loadedSeriesData'); OHIF.log.info('Reloading previous loadedSeriesData');
OHIF.viewer.loadedSeriesData = ViewerData[contentId].loadedSeriesData; OHIF.viewer.loadedSeriesData = OHIF.viewer.data.loadedSeriesData;
} else { } else {
OHIF.log.info('Setting default ViewerData'); OHIF.log.info('Setting default viewer data');
OHIF.viewer.loadedSeriesData = {}; OHIF.viewer.loadedSeriesData = {};
ViewerData[contentId] = {}; OHIF.viewer.data = {};
ViewerData[contentId].loadedSeriesData = OHIF.viewer.loadedSeriesData; OHIF.viewer.data.loadedSeriesData = OHIF.viewer.loadedSeriesData;
// Update the viewer data object // Update the viewer data object
ViewerData[contentId].viewportColumns = 1; OHIF.viewer.data.viewportColumns = 1;
ViewerData[contentId].viewportRows = 1; OHIF.viewer.data.viewportRows = 1;
ViewerData[contentId].activeViewport = 0; OHIF.viewer.data.activeViewport = 0;
} }
Session.set('activeViewport', ViewerData[contentId].activeViewport || 0); Session.set('activeViewport', OHIF.viewer.data.activeViewport || 0);
// @TypeSafeStudies // @TypeSafeStudies
// Update the OHIF.viewer.Studies collection with the loaded studies // Update the OHIF.viewer.Studies collection with the loaded studies
OHIF.viewer.Studies.removeAll(); OHIF.viewer.Studies.removeAll();
ViewerData[contentId].studyInstanceUids = []; OHIF.viewer.data.studyInstanceUids = [];
instance.data.studies.forEach(study => { instance.data.studies.forEach(study => {
study.selected = true; study.selected = true;
const studyMetadata = new OHIF.metadata.StudyMetadata(study); const studyMetadata = new OHIF.metadata.StudyMetadata(study);
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata); study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
OHIF.viewer.Studies.insert(study); OHIF.viewer.Studies.insert(study);
ViewerData[contentId].studyInstanceUids.push(study.studyInstanceUid); OHIF.viewer.data.studyInstanceUids.push(study.studyInstanceUid);
}); });
Session.set('ViewerData', ViewerData);
}); });
Template.viewer.onRendered(function() { Template.viewer.onRendered(function() {