Replacing switchToTab with routes

This commit is contained in:
Bruno Alves de Faria 2017-03-15 18:46:07 -03:00
parent 0cf4dbf13f
commit 7718d31a77
46 changed files with 358 additions and 753 deletions

View File

@ -21,13 +21,6 @@
</div> </div>
{{/section}} {{/section}}
{{/header}} {{/header}}
<div id="studylistTabs" class="tab-content">
<div class="tab-pane active" id="studylistTab"> {{>Template.dynamic template=(choose this.template 'studylist') data=this}}
<div class="studylistContainer">
{{>studylistResult}}
</div>
</div>
<div class="tab-pane" id="viewerTab">
</div>
</div>
</template> </template>

View File

@ -1,19 +1,18 @@
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 { ReactiveVar } from 'meteor/reactive-var';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
const studylistContentId = 'studylistTab'; const studylistContentId = 'studylistTab';
const viewerContentId = 'viewerTab'; const viewerContentId = 'viewerTab';
// Define the ViewerData global object // Define the OHIF.viewer.data global object
// If there is currently any Session data for this object,
// use this to repopulate the variable
Template.app.onCreated(() => { Template.app.onCreated(() => {
const instance = Template.instance(); const instance = Template.instance();
instance.headerClasses = new ReactiveVar(''); instance.headerClasses = new ReactiveVar('');
ViewerData = Session.get('ViewerData') || {}; OHIF.viewer.data = instance.data.viewerData || {};
OHIF.header.dropdown.setItems([{ OHIF.header.dropdown.setItems([{
action: OHIF.user.audit, action: OHIF.user.audit,
@ -46,24 +45,25 @@ Template.app.onCreated(() => {
}]); }]);
instance.autorun(() => { instance.autorun(() => {
const contentId = Session.get('activeContentId'); const currentRoute = Router.current();
instance.headerClasses.set(contentId === viewerContentId ? '' : 'header-big'); if (!currentRoute) return;
}); const routeName = currentRoute.route.getName();
}); const isViewer = routeName.indexOf('viewer') === 0;
Template.app.onRendered(() => { // Add or remove the strech class from body
const contentId = Session.get('activeContentId'); $(document.body)[isViewer ? 'addClass' : 'removeClass']('stretch');
if (contentId === viewerContentId) {
switchToTab(contentId); // 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.app.events({ Template.app.events({
'click .js-toggle-studyList'(event) { 'click .js-toggle-studyList'(event) {
const contentId = Session.get('activeContentId');
OHIF.ui.unsavedChanges.presentProactiveDialog('viewer.*', (hasChanges, userChoice) => { OHIF.ui.unsavedChanges.presentProactiveDialog('viewer.*', (hasChanges, userChoice) => {
if (hasChanges) { if (hasChanges) {
switch (userChoice) { switch (userChoice) {
@ -79,20 +79,12 @@ Template.app.events({
} }
} }
if (contentId !== studylistContentId) {
switchToTab(studylistContentId);
} else {
switchToTab(viewerContentId);
}
}, { }, {
position: { position: {
x: event.clientX + 15, x: event.clientX + 15,
y: event.clientY + 15 y: event.clientY + 15
} }
}); });
} }
}); });
@ -104,7 +96,7 @@ Template.app.helpers({
// If the Viewer has not been opened yet, 'Back to viewer' should // If the Viewer has not been opened yet, 'Back to viewer' should
// not be displayed // not be displayed
const viewerContentExists = !!Object.keys(ViewerData).length; const viewerContentExists = !!Object.keys(OHIF.viewer.data).length;
if (!viewerContentExists) { if (!viewerContentExists) {
return; return;
} }

View File

@ -31,12 +31,8 @@ Meteor.startup(() => {
}); });
Template.viewer.onCreated(() => { Template.viewer.onCreated(() => {
Session.set('ViewerReady', false); Session.set('ViewerReady', false);
const toolManager = OHIF.viewerbase.toolManager;
ViewerData = window.ViewerData || ViewerData;
const instance = Template.instance(); const instance = Template.instance();
const { TimepointApi, MeasurementApi, ConformanceCriteria } = OHIF.measurements; const { TimepointApi, MeasurementApi, ConformanceCriteria } = OHIF.measurements;
@ -59,7 +55,6 @@ 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;
const viewportUtils = OHIF.viewerbase.viewportUtils; const viewportUtils = OHIF.viewerbase.viewportUtils;
OHIF.viewer.functionList = $.extend(OHIF.viewer.functionList, { OHIF.viewer.functionList = $.extend(OHIF.viewer.functionList, {
@ -84,18 +79,16 @@ Template.viewer.onCreated(() => {
linkStackScroll: viewportUtils.linkStackScroll linkStackScroll: viewportUtils.linkStackScroll
}); });
if (ViewerData[contentId].loadedSeriesData) { if (OHIF.viewer.data.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].loadedSeriesData = {}; OHIF.viewer.data.loadedSeriesData = {};
Session.setPersistent('ViewerData', ViewerData);
} }
Session.set('activeViewport', ViewerData[contentId].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
instance.autorun(OHIF.lesiontracker.pixelSpacingAutorunCheck); instance.autorun(OHIF.lesiontracker.pixelSpacingAutorunCheck);

View File

@ -1,9 +1,9 @@
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session';
import { Router } from 'meteor/iron:router'; import { Router } from 'meteor/iron:router';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
Session.setDefault('ViewerData', {}); // TODO: remove the line below
window.Router = Router;
// verifyEmail controls whether emailVerification template will be rendered or not // verifyEmail controls whether emailVerification template will be rendered or not
const verifyEmail = Meteor.settings && Meteor.settings.public && Meteor.settings.public.verifyEmail || false; const verifyEmail = Meteor.settings && Meteor.settings.public && Meteor.settings.public.verifyEmail || false;
@ -19,27 +19,21 @@ const data = {};
const routerOptions = { data }; const routerOptions = { data };
Router.route('/', function() { Router.route('/', {
// Check user is logged in name: 'home',
if (Meteor.user() && Meteor.userId()) { onBeforeAction: function() {
// Check if user needs to verify its email
if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) { if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) {
this.render('emailVerification', routerOptions); this.render('emailVerification', routerOptions);
} else { } else {
const contentId = Session.get('activeContentId');
if (!contentId) {
Session.setPersistent('activeContentId', 'studylistTab');
}
this.render('app', routerOptions); this.render('app', routerOptions);
} }
} else {
this.render('entrySignIn', routerOptions);
} }
}); });
Router.route('/viewer/timepoints/:_id', { Router.route('/viewer/timepoints/:_id', {
layoutTemplate: 'layout', layoutTemplate: 'layout',
name: 'viewer', name: 'viewerTimepoint',
onBeforeAction: function() { onBeforeAction: function() {
const timepointId = this.params._id; const timepointId = this.params._id;
@ -48,6 +42,47 @@ Router.route('/viewer/timepoints/:_id', {
} }
}); });
OHIF.viewer.prepare = ({ studyInstanceUids, timepointId }) => {
// Clear the cornerstone tool data to sync the measurements with the measurements API
cornerstoneTools.globalImageIdSpecificToolStateManager = cornerstoneTools.newImageIdSpecificToolStateManager();
return new Promise((resolve, reject) => {
OHIF.studylist.retrieveStudiesMetadata(studyInstanceUids).then(studies => {
// Add additional metadata to our study from the studylist
studies.forEach(study => {
const studylistStudy = OHIF.studylist.collections.Studies.findOne({
studyInstanceUid: study.studyInstanceUid
});
if (!studylistStudy) {
return;
}
Object.assign(study, studylistStudy);
});
resolve(studies);
}).catch(reject);
});
};
Router.route('/viewer/studies/:studyInstanceUids', {
name: 'viewerStudies',
onBeforeAction: function() {
this.render('app', { data: { template: 'loadingText' } });
const studyInstanceUids = this.params.studyInstanceUids.split(';');
OHIF.viewer.prepare({ studyInstanceUids }).then(studies => {
this.render('app', {
data: {
template: 'viewer',
studies
}
});
});
}
});
Router.onBeforeAction(function() { Router.onBeforeAction(function() {
if (!Meteor.userId() && !Meteor.loggingIn()) { if (!Meteor.userId() && !Meteor.loggingIn()) {
this.render('entrySignIn'); this.render('entrySignIn');

View File

@ -5,13 +5,6 @@ import { OHIF } from 'meteor/ohif:core';
const studylistContentId = 'studylistTab'; const studylistContentId = 'studylistTab';
let lastContentId; let lastContentId;
// Define the ViewerData global object
// If there is currently any Session data for this object,
// use this to repopulate the variable
Template.ohifViewer.onCreated(() => {
ViewerData = Session.get('ViewerData') || {};
});
Template.ohifViewer.events({ Template.ohifViewer.events({
'click .js-toggle-studyList'() { 'click .js-toggle-studyList'() {
const contentId = Session.get('activeContentId'); const contentId = Session.get('activeContentId');
@ -31,11 +24,10 @@ Template.ohifViewer.events({
Template.ohifViewer.helpers({ Template.ohifViewer.helpers({
studyListToggleText() { studyListToggleText() {
const contentId = Session.get('activeContentId'); const contentId = Session.get('activeContentId');
Session.get('ViewerData');
// If the Viewer has not been opened yet, 'Back to viewer' should // If the Viewer has not been opened yet, 'Back to viewer' should
// not be displayed // not be displayed
const viewerContentExists = !!Object.keys(ViewerData).length; const viewerContentExists = !!Object.keys(OHIF.viewer.data).length;
if (!viewerContentExists) { if (!viewerContentExists) {
return; return;
} }

View File

@ -2,14 +2,13 @@ import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session'; import { Session } from 'meteor/session';
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { ReactiveDict } from 'meteor/reactive-dict'; import { ReactiveDict } from 'meteor/reactive-dict';
import { Tracker } from 'meteor/tracker' import { Tracker } from 'meteor/tracker';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import 'meteor/ohif:cornerstone'; import 'meteor/ohif:cornerstone';
import 'meteor/ohif:viewerbase'; import 'meteor/ohif:viewerbase';
import 'meteor/ohif:metadata'; import 'meteor/ohif:metadata';
/** /**
* Inits OHIF Hanging Protocol's onReady. * Inits OHIF Hanging Protocol's onReady.
* It waits for OHIF Hanging Protocol to be ready to instantiate the ProtocolEngine * It waits for OHIF Hanging Protocol to be ready to instantiate the ProtocolEngine
@ -47,7 +46,6 @@ Meteor.startup(() => {
Session.setDefault('leftSidebar', false); Session.setDefault('leftSidebar', false);
Session.setDefault('rightSidebar', false); Session.setDefault('rightSidebar', false);
OHIF.viewer = OHIF.viewer || {};
OHIF.viewer.defaultTool = 'wwwc'; OHIF.viewer.defaultTool = 'wwwc';
OHIF.viewer.refLinesEnabled = true; OHIF.viewer.refLinesEnabled = true;
OHIF.viewer.cine = { OHIF.viewer.cine = {
@ -77,9 +75,7 @@ Meteor.startup(() => {
cornerstoneTools.metaData.addProvider(metadataProvider.provider.bind(metadataProvider)); cornerstoneTools.metaData.addProvider(metadataProvider.provider.bind(metadataProvider));
}); });
Template.viewer.onCreated(() => { Template.viewer.onCreated(() => {
Session.set('ViewerReady', false); Session.set('ViewerReady', false);
const instance = Template.instance(); const instance = Template.instance();
@ -88,24 +84,22 @@ 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
// Clears OHIF.viewer.Studies collection // Clears OHIF.viewer.Studies collection
@ -115,7 +109,7 @@ Template.viewer.onCreated(() => {
// Clears OHIF.viewer.StudyMetadataList collection // Clears OHIF.viewer.StudyMetadataList collection
OHIF.viewer.StudyMetadataList.removeAll(); OHIF.viewer.StudyMetadataList.removeAll();
ViewerData[contentId].studyInstanceUids = []; OHIF.viewer.data.studyInstanceUids = [];
instance.data.studies.forEach(study => { instance.data.studies.forEach(study => {
const studyMetadata = new OHIF.metadata.StudyMetadata(study, study.studyInstanceUid); const studyMetadata = new OHIF.metadata.StudyMetadata(study, study.studyInstanceUid);
const displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata); const displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
@ -126,10 +120,8 @@ Template.viewer.onCreated(() => {
study.displaySets = displaySets; study.displaySets = displaySets;
OHIF.viewer.Studies.insert(study); OHIF.viewer.Studies.insert(study);
OHIF.viewer.StudyMetadataList.insert(studyMetadata); OHIF.viewer.StudyMetadataList.insert(studyMetadata);
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() {
@ -154,6 +146,7 @@ Template.viewer.events({
const current = instance.data.state.get('leftSidebar'); const current = instance.data.state.get('leftSidebar');
instance.data.state.set('leftSidebar', !current); instance.data.state.set('leftSidebar', !current);
}, },
'click .js-toggle-protocol-editor'() { 'click .js-toggle-protocol-editor'() {
const instance = Template.instance(); const instance = Template.instance();
const current = instance.data.state.get('rightSidebar'); const current = instance.data.state.get('rightSidebar');

View File

@ -1,8 +1,6 @@
import { Session } from 'meteor/session'; import { Session } from 'meteor/session';
import { Router } from 'meteor/iron:router'; import { Router } from 'meteor/iron:router';
Session.setDefault('ViewerData', {});
Router.configure({ Router.configure({
layoutTemplate: 'layout', layoutTemplate: 'layout',
loadingTemplate: 'layout' loadingTemplate: 'layout'
@ -31,4 +29,4 @@ Router.route('/viewer/:_id', {
} }
}); });
} }
}); });

View File

@ -1,8 +1,16 @@
@import "{ohif:design}/app" @import "{ohif:design}/app"
html body html body
theme('background-color', '$primaryBackgroundColor')
font-family: 'Roboto', 'OpenSans', 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif font-family: 'Roboto', 'OpenSans', 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif
html body.stretch
height: 100%
minWidth: 0
overflow: hidden
position: fixed
width: 100%
html hr html hr
theme('border-top', '1px solid $uiBorderColor') theme('border-top', '1px solid $uiBorderColor')

View File

@ -138,7 +138,7 @@ function getAbstractPriorValue(imageId) {
return; return;
} }
const studies = StudyListStudies.find({ const studies = OHIF.studylist.collections.Studies.find({
patientId: currentStudy.patientId, patientId: currentStudy.patientId,
studyDate: { studyDate: {
$lt: currentStudy.studyDate $lt: currentStudy.studyDate

View File

@ -10,7 +10,7 @@ import { OHIF } from 'meteor/ohif:core';
OHIF.lesiontracker.openNewTabWithTimepoint = timepointId => { OHIF.lesiontracker.openNewTabWithTimepoint = timepointId => {
const contentId = 'viewerTab'; const contentId = 'viewerTab';
const Timepoints = StudyList.timepointApi.timepoints; const Timepoints = OHIF.studylist.timepointApi.timepoints;
const timepoint = Timepoints.findOne({ const timepoint = Timepoints.findOne({
timepointId: timepointId timepointId: timepointId
}); });
@ -25,10 +25,8 @@ OHIF.lesiontracker.openNewTabWithTimepoint = timepointId => {
throw 'No studies found that are related to this timepoint'; throw 'No studies found that are related to this timepoint';
} }
ViewerData = window.ViewerData || ViewerData; // Update the OHIF.viewer.data global object
OHIF.viewer.data = {
// Update the ViewerData global object
ViewerData[contentId] = {
contentId: contentId, contentId: contentId,
studyInstanceUids: data.studyInstanceUids, studyInstanceUids: data.studyInstanceUids,
timepointIds: data.timepointIds, timepointIds: data.timepointIds,
@ -59,7 +57,7 @@ function getDataFromTimepoint(timepoint) {
// Otherwise, this is a follow-up exam, so we should also find the baseline timepoint, // 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 // 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. // prior to the latest studyDate in the current (Follow-up) Timepoint.
const Timepoints = StudyList.timepointApi.timepoints; const Timepoints = OHIF.studylist.timepointApi.timepoints;
const baseline = Timepoints.findOne({ const baseline = Timepoints.findOne({
timepointType: 'baseline', timepointType: 'baseline',
patientId: timepoint.patientId, patientId: timepoint.patientId,

View File

@ -2,11 +2,11 @@ import { Meteor } from 'meteor/meteor';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
Meteor.startup(function() { Meteor.startup(function() {
StudyList.callbacks.dblClickOnStudy = dblClickOnStudy; OHIF.studylist.callbacks.dblClickOnStudy = dblClickOnStudy;
StudyList.callbacks.middleClickOnStudy = dblClickOnStudy; OHIF.studylist.callbacks.middleClickOnStudy = dblClickOnStudy;
StudyList.timepointApi = new OHIF.measurements.TimepointApi(); OHIF.studylist.timepointApi = new OHIF.measurements.TimepointApi();
StudyList.timepointApi.retrieveTimepoints(); OHIF.studylist.timepointApi.retrieveTimepoints();
}); });
/** /**
@ -14,7 +14,7 @@ Meteor.startup(function() {
*/ */
const dblClickOnStudy = data => { const dblClickOnStudy = data => {
// Find the relevant timepoint given the clicked-on study // Find the relevant timepoint given the clicked-on study
const timepointApi = StudyList.timepointApi; const timepointApi = OHIF.studylist.timepointApi;
if (!timepointApi) { if (!timepointApi) {
OHIF.log.warn('No timepoint api on dbl-clicked study?'); OHIF.log.warn('No timepoint api on dbl-clicked study?');
return; return;
@ -37,10 +37,8 @@ const dblClickOnStudy = data => {
const openTab = studyInstanceUid => { const openTab = studyInstanceUid => {
const contentId = 'viewerTab'; const contentId = 'viewerTab';
ViewerData = window.ViewerData || ViewerData; // Update the OHIF.viewer.data global object
OHIF.viewer.data = {
// Update the ViewerData global object
ViewerData[contentId] = {
contentId: contentId, contentId: contentId,
isUnassociatedStudy: true, isUnassociatedStudy: true,
studyInstanceUids: [studyInstanceUid] studyInstanceUids: [studyInstanceUid]

View File

@ -11,7 +11,7 @@ Template.dialogStudyAssociation.onCreated(() => {
instance.data.confirmCallback = (formData, resolve) => { instance.data.confirmCallback = (formData, resolve) => {
OHIF.log.info('Saving associations'); OHIF.log.info('Saving associations');
const Timepoints = StudyList.timepointApi.timepoints; const Timepoints = OHIF.studylist.timepointApi.timepoints;
// Find the rows of the study association table // Find the rows of the study association table
const $tableRows = instance.$('#studyAssociationTable table tbody tr'); const $tableRows = instance.$('#studyAssociationTable table tbody tr');
@ -138,7 +138,7 @@ Template.dialogStudyAssociation.onCreated(() => {
}; };
}); });
StudyList.timepointApi.storeTimepoints(); OHIF.studylist.timepointApi.storeTimepoints();
resolve(); resolve();
}; };

View File

@ -52,7 +52,7 @@ function autoSelectStudies(selectedStudies) {
// Fetch autoselected studies based on the date range // Fetch autoselected studies based on the date range
// Note that we used MongoDB's fetch here so we have a mutable array, // Note that we used MongoDB's fetch here so we have a mutable array,
// rather than a Cursor // rather than a Cursor
const autoselected = StudyListStudies.find({ const autoselected = OHIF.studylist.collections.Studies.find({
studyDate: { studyDate: {
$gte: range.earliestDate.format('YYYYMMDD'), $gte: range.earliestDate.format('YYYYMMDD'),
$lte: range.latestDate.format('YYYYMMDD') $lte: range.latestDate.format('YYYYMMDD')

View File

@ -12,7 +12,7 @@ const getAssociationAssessment = () => {
}; };
// check if timepointApi is available // check if timepointApi is available
const timepointApi = StudyList.timepointApi; const timepointApi = OHIF.studylist.timepointApi;
if (timepointApi) { if (timepointApi) {
// Get a Cursor pointing to the selected Studies from the StudyList // Get a Cursor pointing to the selected Studies from the StudyList
const selectedStudies = OHIF.studylist.getSelectedStudies(); const selectedStudies = OHIF.studylist.getSelectedStudies();
@ -46,10 +46,8 @@ const viewStudies = () => {
const studyInstanceUids = selectedStudies.map(study => study.studyInstanceUid); const studyInstanceUids = selectedStudies.map(study => study.studyInstanceUid);
const contentId = 'viewerTab'; const contentId = 'viewerTab';
ViewerData = window.ViewerData || ViewerData; // Update the OHIF.viewer.data global object
OHIF.viewer.data = {
// Update the ViewerData global object
ViewerData[contentId] = {
contentId: contentId, contentId: contentId,
studyInstanceUids: studyInstanceUids studyInstanceUids: studyInstanceUids
}; };
@ -77,7 +75,7 @@ const removeTimepointAssociations = event => {
const selectedStudies = OHIF.studylist.getSelectedStudies(); const selectedStudies = OHIF.studylist.getSelectedStudies();
// Find the Timepoint that was previously referenced // Find the Timepoint that was previously referenced
const timepointApi = StudyList.timepointApi; const timepointApi = OHIF.studylist.timepointApi;
if (!timepointApi) { if (!timepointApi) {
OHIF.log.error('Remove Study/Timepoint Association: No Timepoint API found.'); OHIF.log.error('Remove Study/Timepoint Association: No Timepoint API found.');
return; return;

View File

@ -1,5 +1,5 @@
<template name="longitudinalStudyListStudy"> <template name="longitudinalStudyListStudy">
<tr class="studylistStudy noselect"> <tr class="studylistStudy noselect {{#if this.selected}}active{{/if}}">
<td> <td>
{{#if reviewerTip}} {{#if reviewerTip}}
<i class="fa fa-exclamation-circle reviewerTip" data-toggle="tooltip" data-placement="bottom" title="{{reviewerTip}}"></i> <i class="fa fa-exclamation-circle reviewerTip" data-toggle="tooltip" data-placement="bottom" title="{{reviewerTip}}"></i>

View File

@ -1,3 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { OHIF } from 'meteor/ohif:core';
// Use Aldeed's meteor-template-extension package to replace the // Use Aldeed's meteor-template-extension package to replace the
// default StudyListStudy template. // default StudyListStudy template.
// See https://github.com/aldeed/meteor-template-extension // See https://github.com/aldeed/meteor-template-extension
@ -9,7 +13,7 @@ Template.longitudinalStudyListStudy.replaces(defaultTemplate);
Template[defaultTemplate].helpers({ Template[defaultTemplate].helpers({
timepointName: function() { timepointName: function() {
const instance = Template.instance(); const instance = Template.instance();
const timepointApi = StudyList.timepointApi; const timepointApi = OHIF.studylist.timepointApi;
if (!timepointApi) { if (!timepointApi) {
return; return;
} }
@ -21,9 +25,10 @@ Template[defaultTemplate].helpers({
return timepointApi.name(timepoint); return timepointApi.name(timepoint);
}, },
reviewerTip: function() { reviewerTip: function() {
const instance = Template.instance(); const instance = Template.instance();
const timepointApi = StudyList.timepointApi; const timepointApi = OHIF.studylist.timepointApi;
if (!timepointApi) { if (!timepointApi) {
return; return;
} }
@ -33,10 +38,7 @@ Template[defaultTemplate].helpers({
return; return;
} }
var timepointReviewers = Reviewers.findOne({ const timepointReviewers = Reviewers.findOne({ timepointId: timepoint.timepointId });
timepointId: timepoint.timepointId
});
if (!timepointReviewers) { if (!timepointReviewers) {
return; return;
} }
@ -50,7 +52,7 @@ function getReviewerTipText(reviewers) {
return; return;
} }
var newReviewers = reviewers.filter(function(reviewer) { const newReviewers = reviewers.filter(function(reviewer) {
return reviewer.userId !== Meteor.userId(); return reviewer.userId !== Meteor.userId();
}); });
@ -58,7 +60,7 @@ function getReviewerTipText(reviewers) {
return; return;
} }
var tipText = 'The study is being reviewed by '; let tipText = 'The study is being reviewed by ';
newReviewers.forEach(function(reviewer, index) { newReviewers.forEach(function(reviewer, index) {
if (reviewer.userId === Meteor.userId()) { if (reviewer.userId === Meteor.userId()) {
return; return;

View File

@ -1,7 +1,11 @@
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session';
import { OHIF } from 'meteor/ohif:core';
// Use Aldeed's meteor-template-extension package to replace the // Use Aldeed's meteor-template-extension package to replace the
// default viewportOverlay template. // default viewportOverlay template.
// See https://github.com/aldeed/meteor-template-extension // See https://github.com/aldeed/meteor-template-extension
var defaultTemplate = 'viewportOverlay'; const defaultTemplate = 'viewportOverlay';
Template.longitudinalViewportOverlay.replaces(defaultTemplate); Template.longitudinalViewportOverlay.replaces(defaultTemplate);
// Add the TimepointName helper to the default template. The // Add the TimepointName helper to the default template. The
@ -12,10 +16,8 @@ Template[defaultTemplate].helpers({
const studyInstanceUid = instance.data.studyInstanceUid; const studyInstanceUid = instance.data.studyInstanceUid;
// TODO: Find a better way to obtain the timepointApi from the viewer.js template // TODO: Find a better way to obtain the timepointApi from the viewer.js template
const timepointApi = StudyList.timepointApi; const timepointApi = OHIF.studylist.timepointApi;
if (!timepointApi) { if (!timepointApi) return;
return;
}
const timepoints = timepointApi.study(studyInstanceUid); const timepoints = timepointApi.study(studyInstanceUid);
if (!timepoints || !timepoints.length) { if (!timepoints || !timepoints.length) {
@ -31,4 +33,4 @@ Template[defaultTemplate].helpers({
const linkedViewports = Session.get('StackImagePositionOffsetSynchronizerLinkedViewports') || []; const linkedViewports = Session.get('StackImagePositionOffsetSynchronizerLinkedViewports') || [];
return (linkedViewports.indexOf(this.viewportIndex) !== -1); return (linkedViewports.indexOf(this.viewportIndex) !== -1);
} }
}); });

View File

@ -1,4 +1,4 @@
import './base.js';
import './lib'; import './lib';
import './schema'; import './schema';
import './base.js';
import './collections.js'; import './collections.js';

View File

@ -0,0 +1,5 @@
import { OHIF } from 'meteor/ohif:core';
Object.assign(OHIF.studylist, {
callbacks: {}
});

View File

@ -1 +1,2 @@
import './studies.js';
import './subscriptions.js'; import './subscriptions.js';

View File

@ -0,0 +1,11 @@
import { Meteor } from 'meteor/meteor';
import { OHIF } from 'meteor/ohif:core';
// Define the Studies Collection. This is a client-side only Collection which stores the list of
// studies in the StudyList
Meteor.startup(() => {
const Studies = new Meteor.Collection(null);
Studies._debugName = 'Studies';
OHIF.studylist.collections.Studies = Studies;
});

View File

@ -1,5 +1,4 @@
import './studylist.html'; import './studylist.html';
import './studylist.js';
import './studylist.styl'; import './studylist.styl';
import './studylistStudy/studylistStudy.html'; import './studylistStudy/studylistStudy.html';

View File

@ -1,13 +1,5 @@
<template name="studylist"> <template name="studylist">
<div id="studylistTabs" class="tab-content"> <div class="study-list-wrapper">
<div role="tabpanel" class="tab-pane active" id="studylistTab"> {{>studylistResult (clone this)}}
<div class="studylistContainer">
{{>studylistResult (clone this)}}
</div>
</div>
<div role="tabpanel" class="tab-pane" id="viewerTab">
<div class="viewerContainer">
</div>
</div>
</div> </div>
</template> </template>

View File

@ -1,44 +0,0 @@
/**
* Template: StudyList
*
* This is the main component of the StudyList package
*/
// Define the ViewerData global object
// If there is currently any Session data for this object,
// use this to repopulate the variable
ViewerData = Session.get('ViewerData') || {};
// Define the StudyListStudies Collection
// This is a client-side only Collection which
// Stores the list of studies in the StudyList
StudyListStudies = new Meteor.Collection(null);
StudyListStudies._debugName = 'StudyListStudies';
Session.setDefault('activeContentId', 'studylistTab');
Template.studylist.onCreated(() => {
const instance = Template.instance();
if (StudyList.subscriptions) {
StudyList.subscriptions.forEach(subscriptionName => {
instance.subscribe(subscriptionName);
});
}
});
Template.studylist.onRendered(() => {
const instance = Template.instance();
if (instance.data && instance.data.studyInstanceUid) {
const studyInstanceUid = instance.data.studyInstanceUid;
openNewTab(studyInstanceUid);
} else {
// If there is a tab set as active in the Session,
// switch to that now.
console.log('studylist onRendered');
const contentId = Session.get('activeContentId');
if (contentId !== 'studylistTab' && ViewerData && ViewerData[contentId]) {
switchToTab(contentId);
}
}
});

View File

@ -1,34 +1,13 @@
@import "{ohif:design}/app" @import "{ohif:design}/app"
body .study-list-wrapper
theme('background-color', '$primaryBackgroundColor')
#tblStudyList
tr
height: 20px
#studylistTab
theme('background-color', '$primaryBackgroundColor')
.studylistContainer
theme('background-color', '$primaryBackgroundColor') theme('background-color', '$primaryBackgroundColor')
color: white color: white
margin: 2px auto 0 margin: 2px auto 0
height: calc(100% - 91px)
position: relative position: relative
width: 100% width: 100%
.tab-content
width: 100%
height: calc(100% - 91px)
.loadingTextDiv .loadingTextDiv
theme('color', '$textSecondaryColor') theme('color', '$textSecondaryColor')
font-size: 30px font-size: 30px
.tab-pane
width: 100%
height: 100%
.viewerContainer
height: 100%
width: 100%

View File

@ -1,14 +1,8 @@
<template name="studylistResult"> <template name="studylistResult">
<div class="studyListToolbar clearfix"> <div class="studyListToolbar clearfix">
<div class="header pull-left"> <div class="header pull-left">Study List</div>
Study List <div class="studyCount pull-right">{{numberOfStudies}}</div>
</div> <div class="pull-right">{{>studylistToolbar}}</div>
<div class="studyCount pull-right">
{{numberOfStudies}}
</div>
<div class="pull-right">
{{>studylistToolbar}}
</div>
</div> </div>
<div class="theadBackground"> <div class="theadBackground">
</div> </div>
@ -74,18 +68,18 @@
</tr> </tr>
</thead> </thead>
<tbody id="studyListData"> <tbody id="studyListData">
{{#each study in studies}} {{#each study in studies}}
{{>studylistStudy (clone study this)}} {{>studylistStudy (clone study this)}}
{{/each}} {{/each}}
</tbody> </tbody>
</table> </table>
<!-- Pagination --> <!-- Pagination -->
{{> studylistPagination }} {{>studylistPagination}}
{{#if session "showLoadingText"}} {{#if session "showLoadingText"}}
{{>loadingText}} {{>loadingText}}
{{ else }} {{else}}
{{#unless numberOfStudies}} {{#unless numberOfStudies}}
<div class="notFound">No matching results</div> <div class="notFound">No matching results</div>
{{/unless}} {{/unless}}

View File

@ -1,11 +1,16 @@
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session';
import { ReactiveVar } from 'meteor/reactive-var';
import { ReactiveDict } from 'meteor/reactive-dict';
import { moment } from 'meteor/momentjs:moment';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
Session.setDefault('showLoadingText', true); Session.setDefault('showLoadingText', true);
Template.studylistResult.helpers({ Template.studylistResult.helpers({
/** /**
* Returns a sorted instance of the StudyListStudies Collection * Returns a ascending sorted instance of the Studies Collection by Patient name and Study Date
* by Patient name and Study Date in Ascending order.
*/ */
studies() { studies() {
const instance = Template.instance(); const instance = Template.instance();
@ -26,7 +31,7 @@ Template.studylistResult.helpers({
const offset = rowsPerPage * currentPage; const offset = rowsPerPage * currentPage;
const limit = offset + rowsPerPage; const limit = offset + rowsPerPage;
studies = StudyListStudies.find({}, { studies = OHIF.studylist.collections.Studies.find({}, {
sort: sortOption sort: sortOption
}).fetch(); }).fetch();
@ -42,7 +47,7 @@ Template.studylistResult.helpers({
}, },
numberOfStudies() { numberOfStudies() {
return StudyListStudies.find().count(); return OHIF.studylist.collections.Studies.find().count();
}, },
sortingColumnsIcons() { sortingColumnsIcons() {
@ -65,9 +70,9 @@ Template.studylistResult.helpers({
} }
}); });
var studyDateFrom; let studyDateFrom;
var studyDateTo; let studyDateTo;
var filter; let filter;
/** /**
* Transforms an input string into a search filter for * Transforms an input string into a search filter for
@ -106,9 +111,6 @@ function replaceUndefinedColumnValue(text) {
} }
} }
/**
/** /**
* Convert string to study date * Convert string to study date
*/ */
@ -122,10 +124,10 @@ function convertStringToStudyDate(dateStr) {
/** /**
* Runs a search for studies matching the studylist query parameters * Runs a search for studies matching the studylist query parameters
* Inserts the identified studies into the StudyListStudies Collection * Inserts the identified studies into the Studies Collection
*/ */
function search() { function search() {
console.log('search()'); OHIF.log.info('search()');
// Show loading message // Show loading message
Session.set('showLoadingText', true); Session.set('showLoadingText', true);
@ -138,7 +140,7 @@ function search() {
studyDescription: getFilter($('input#studyDescription').val()), studyDescription: getFilter($('input#studyDescription').val()),
studyDateFrom: studyDateFrom, studyDateFrom: studyDateFrom,
studyDateTo: studyDateTo, studyDateTo: studyDateTo,
modalitiesInStudy: $('input#modality').val() ? $('input#modality').val() : "" modalitiesInStudy: $('input#modality').val() ? $('input#modality').val() : ''
}; };
// Make sure that modality has a reasonable value, since it is occasionally // Make sure that modality has a reasonable value, since it is occasionally
@ -146,10 +148,10 @@ function search() {
const modality = replaceUndefinedColumnValue($('input#modality').val()); const modality = replaceUndefinedColumnValue($('input#modality').val());
// Clear all current studies // Clear all current studies
StudyListStudies.remove({}); OHIF.studylist.collections.Studies.remove({});
Meteor.call('StudyListSearch', filter, (error, studies) => { Meteor.call('StudyListSearch', filter, (error, studies) => {
console.log('StudyListSearch'); OHIF.log.info('StudyListSearch');
if (error) { if (error) {
OHIF.log.warn(error); OHIF.log.warn(error);
return; return;
@ -172,8 +174,8 @@ function search() {
// Convert numberOfStudyRelatedInstance string into integer // Convert numberOfStudyRelatedInstance string into integer
study.numberOfStudyRelatedInstances = !isNaN(study.numberOfStudyRelatedInstances) ? parseInt(study.numberOfStudyRelatedInstances) : undefined; study.numberOfStudyRelatedInstances = !isNaN(study.numberOfStudyRelatedInstances) ? parseInt(study.numberOfStudyRelatedInstances) : undefined;
// Insert any matching studies into the StudyListStudies Collection // Insert any matching studies into the Studies Collection
StudyListStudies.insert(study); OHIF.studylist.collections.Studies.insert(study);
} }
}); });
}); });
@ -181,7 +183,7 @@ function search() {
const getRowsPerPage = () => sessionStorage.getItem('rowsPerPage'); const getRowsPerPage = () => sessionStorage.getItem('rowsPerPage');
// Wraps ReactiveVar equalsFunc function. Whenever ReactiveVar is // Wraps ReactiveVar equalsFunc function. Whenever ReactiveVar is
// set to a new value, it will save it in the Session Storage. // set to a new value, it will save it in the Session Storage.
// The return is the default ReactiveVar equalsFunc if available // The return is the default ReactiveVar equalsFunc if available
// or values are === compared // or values are === compared
@ -200,9 +202,10 @@ Template.studylistResult.onCreated(() => {
// Rows per page // Rows per page
// Check session storage or set 25 as default // Check session storage or set 25 as default
const cachedRowsPerPage = getRowsPerPage(); const cachedRowsPerPage = getRowsPerPage();
if(!cachedRowsPerPage) { if (!cachedRowsPerPage) {
setRowsPerPage(0, 25); setRowsPerPage(0, 25);
} }
const rowsPerPage = getRowsPerPage(); const rowsPerPage = getRowsPerPage();
instance.rowsPerPage = new ReactiveVar(parseInt(rowsPerPage, 10), setRowsPerPage); instance.rowsPerPage = new ReactiveVar(parseInt(rowsPerPage, 10), setRowsPerPage);
@ -214,8 +217,7 @@ Template.studylistResult.onCreated(() => {
const sortOptionSession = Session.get('sortOption'); const sortOptionSession = Session.get('sortOption');
if (sortOptionSession) { if (sortOptionSession) {
instance.sortingColumns.set(sortOptionSession); instance.sortingColumns.set(sortOptionSession);
} } else {
else {
instance.sortingColumns.set({ instance.sortingColumns.set({
patientName: 1, patientName: 1,
studyDate: 1, studyDate: 1,

View File

@ -76,6 +76,9 @@ $bodyCellHeight = 40px
text-align: center text-align: center
table#tblStudyList table#tblStudyList
tr
height: 20px
thead thead
white-space: nowrap white-space: nowrap
@ -172,10 +175,10 @@ $bodyCellHeight = 40px
@media only screen and (max-width: 1362px) @media only screen and (max-width: 1362px)
$tablePadding = 5% $tablePadding = 5%
#studyListContainer #studyListContainer
padding: 0 $tablePadding padding: 0 $tablePadding
table#tblStudyList table#tblStudyList
thead, thead,
tbody tbody
@ -190,10 +193,10 @@ $bodyCellHeight = 40px
@media only screen and (max-width: 1161px) @media only screen and (max-width: 1161px)
$tablePadding = 3% $tablePadding = 3%
#studyListContainer #studyListContainer
padding: 0 $tablePadding padding: 0 $tablePadding
table#tblStudyList table#tblStudyList
thead, thead,
tbody tbody
@ -208,31 +211,31 @@ $bodyCellHeight = 40px
@media only screen and (max-width: 1069px) @media only screen and (max-width: 1069px)
$tablePaddingMediumScreen = 5px $tablePaddingMediumScreen = 5px
.theadBackground .theadBackground
height: 101px height: 101px
.studyListToolbar .studyListToolbar
padding: 0 $tablePaddingMediumScreen padding: 0 $tablePaddingMediumScreen
#studyListContainer #studyListContainer
padding: 0 padding: 0
table#tblStudyList table#tblStudyList
thead > tr > th thead > tr > th
&:first-child &:first-child
padding-left: $tablePaddingMediumScreen padding-left: $tablePaddingMediumScreen
&:last-child &:last-child
padding-right: $tablePaddingMediumScreen padding-right: $tablePaddingMediumScreen
input.worklist-search input.worklist-search
padding: 10px padding: 10px
div.sortingCell div.sortingCell
padding: 10px 5px padding: 10px 5px
i i
width: auto width: auto
@ -242,8 +245,8 @@ $bodyCellHeight = 40px
&:last-child &:last-child
padding-right: $tablePaddingMediumScreen padding-right: $tablePaddingMediumScreen
.worklistPagination .worklistPagination
.row .row
margin-left: 0 margin-left: 0
margin-right: 0 margin-right: 0

View File

@ -1,5 +1,5 @@
<template name="studylistStudy"> <template name="studylistStudy">
<tr class="studylistStudy noselect"> <tr class="studylistStudy noselect {{#if this.selected}}active{{/if}}">
<td class="patientName"> <td class="patientName">
{{formatPN patientName}} {{formatPN patientName}}
</td> </td>
@ -19,4 +19,4 @@
{{studyDescription}} {{studyDescription}}
</td> </td>
</tr> </tr>
</template> </template>

View File

@ -1,100 +1,82 @@
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { Blaze } from 'meteor/blaze'; import { Blaze } from 'meteor/blaze';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
// Maybe we should use regular StudyList collection? // Clear all selected studies
StudyListSelectedStudies = new Meteor.Collection(null);
StudyListSelectedStudies._debugName = 'StudyListSelectedStudies';
function isStudySelected(study) {
// Search StudyListSelectedStudies for given study and return true if found
let count = StudyListSelectedStudies.find({
studyInstanceUid: study.studyInstanceUid
}).count();
return count > 0;
}
function doClearStudySelections() { function doClearStudySelections() {
// Clear all selected studies OHIF.studylist.collections.Studies.update({}, {
StudyListSelectedStudies.remove({}); $set: { selected: false }
$('tr.studylistStudy').removeClass('active'); }, { multi: true });
} }
function doSelectRow(studyRow, data) { function doSelectRow($studyRow, data) {
// Insert current study into selection list if it's not there already... // Mark the current study as selected if it's not marked yet
if (!isStudySelected(data)) { if (!data.selected) {
StudyListSelectedStudies.insert(data); const filter = { studyInstanceUid: data.studyInstanceUid };
const modifiers = { $set: { selected: true } };
OHIF.studylist.collections.Studies.update(filter, modifiers);
} }
// Make sure the study row has "active" class
studyRow.addClass('active'); // Set it as the previously selected row, so the user can use Shift to select from this point on
// Set this as the previously selected row, so the user can OHIF.studylist.$lastSelectedRow = $studyRow;
// use Shift to select from this point onwards
StudyList.previouslySelected = studyRow;
} }
function doSelectSingleRow(studyRow, data) { function doSelectSingleRow($studyRow, data) {
// Clear all selected studies // Clear all selected studies
doClearStudySelections(); doClearStudySelections();
// ... And add selected row to selection list
doSelectRow(studyRow, data); // Add selected row to selection list
doSelectRow($studyRow, data);
} }
function doUnselectRow(studyRow, data) { function doUnselectRow($studyRow, data) {
// Find the current studyInstanceUid in the stored list and remove it // Find the current studyInstanceUid in the stored list and mark as unselected
StudyListSelectedStudies.remove({ const filter = { studyInstanceUid: data.studyInstanceUid };
studyInstanceUid: data.studyInstanceUid const modifiers = { $set: { selected: false } };
}); OHIF.studylist.collections.Studies.update(filter, modifiers);
studyRow.removeClass('active');
} }
function handleShiftClick($studyRow, data) { function handleShiftClick($studyRow, data) {
//OHIF.log.info('shiftKey'); let study;
let $previousRow = OHIF.studylist.$lastSelectedRow;
let study, previous = StudyList.previouslySelected ? $(StudyList.previouslySelected) : null; if ($previousRow && $previousRow.length > 0) {
if (previous && previous.length > 0) { study = Blaze.getData($previousRow.get(0));
study = Blaze.getData(previous.get(0)); if (!study.selected) {
if (!isStudySelected(study)) { $previousRow = $(); // undefined
previous = void 0; // undefined OHIF.studylist.$lastSelectedRow = $previousRow;
StudyList.previouslySelected = previous;
} }
} }
// Select all rows in between these two rows // Select all rows in between these two rows
if (previous) { if ($previousRow.length) {
let $rowsInBetween; let $rowsInBetween;
if (previous.index() < $studyRow.index()) { if ($previousRow.index() < $studyRow.index()) {
// The previously selected row is above (lower index) the // The previously selected row is above (lower index) the
// currently selected row. // currently selected row.
// Fill in the rows upwards from the previously selected row // Fill in the rows upwards from the previously selected row
$rowsInBetween = previous.nextAll('tr'); $rowsInBetween = $previousRow.nextAll('tr');
} else if (previous.index() > $studyRow.index()) { } else if ($previousRow.index() > $studyRow.index()) {
// The previously selected row is below the currently // The previously selected row is below the currently
// selected row. // selected row.
// Fill in the rows upwards from the previously selected row // Fill in the rows upwards from the previously selected row
$rowsInBetween = previous.prevAll('tr'); $rowsInBetween = $previousRow.prevAll('tr');
} else { } else {
// nothing to do since previous.index() === $studyRow.index() // nothing to do since $previousRow.index() === $studyRow.index()
// the user is shift-clicking the same row... // the user is shift-clicking the same row...
return; return;
} }
// Loop through the rows in between current and previous selected studies // Loop through the rows in between current and previous selected studies
$rowsInBetween.forEach(row => { $rowsInBetween.each((index, row) => {
const $row = $(row); const $row = $(row);
if ($row.hasClass('active')) {
// If we find one that is already selected, do nothing
return;
}
// Get the relevant studyInstanceUid
let studyInstanceUid = $row.attr('studyInstanceUid');
// Retrieve the data context through Blaze // Retrieve the data context through Blaze
let data = Blaze.getData(this); const data = Blaze.getData(row);
// If we find one that is already selected, do nothing
if (data.selected) return;
// Set the current study as selected // Set the current study as selected
doSelectRow($row, data); doSelectRow($row, data);
@ -108,37 +90,31 @@ function handleShiftClick($studyRow, data) {
} }
} }
function handleCtrlClick(studyRow, data) { function handleCtrlClick($studyRow, data) {
//OHIF.log.info('ctrlKey'); const handler = data.selected ? doUnselectRow : doSelectRow;
if (isStudySelected(data)) { handler($studyRow, data);
doUnselectRow(studyRow, data);
} else {
doSelectRow(studyRow, data);
OHIF.log.info('StudyList PreviouslySelected set: ' + studyRow.index());
}
} }
Template.studylistStudy.onRendered(function() { Template.studylistStudy.onRendered(() => {
let instance = this, const instance = Template.instance();
data = instance.data, const data = instance.data;
row = instance.$('tr.studylistStudy').first(); const $row = instance.$('tr.studylistStudy').first();
// Enable HammerJS to allow touch support // Enable HammerJS to allow touch support
let mc = new Hammer.Manager(row.get(0)), const mc = new Hammer.Manager($row.get(0));
doubleTapRecognizer = new Hammer.Tap({ const doubleTapRecognizer = new Hammer.Tap({
event: 'doubletap', event: 'doubletap',
taps: 2, taps: 2,
interval: 500, interval: 500,
threshold: 30, threshold: 30,
posThreshold: 30 posThreshold: 30
}); });
mc.add(doubleTapRecognizer); mc.add(doubleTapRecognizer);
// Check if current row has been previously selected // Check if current row has been previously selected
if (isStudySelected(data)) { if (data.selected) {
doSelectRow(row, data); doSelectRow($row, data);
} }
}); });
Template.studylistStudy.events({ Template.studylistStudy.events({
@ -164,7 +140,7 @@ Template.studylistStudy.events({
return; return;
} }
const middleClickOnStudy = StudyList.callbacks.middleClickOnStudy; const middleClickOnStudy = OHIF.studylist.callbacks.middleClickOnStudy;
if (middleClickOnStudy && typeof middleClickOnStudy === 'function') { if (middleClickOnStudy && typeof middleClickOnStudy === 'function') {
middleClickOnStudy(instance.data); middleClickOnStudy(instance.data);
} }
@ -175,7 +151,7 @@ Template.studylistStudy.events({
return; return;
} }
const dblClickOnStudy = StudyList.callbacks.dblClickOnStudy; const dblClickOnStudy = OHIF.studylist.callbacks.dblClickOnStudy;
if (dblClickOnStudy && typeof dblClickOnStudy === 'function') { if (dblClickOnStudy && typeof dblClickOnStudy === 'function') {
dblClickOnStudy(instance.data); dblClickOnStudy(instance.data);
@ -185,7 +161,7 @@ Template.studylistStudy.events({
'contextmenu tr.studylistStudy, press tr.studylistStudy'(event, instance) { 'contextmenu tr.studylistStudy, press tr.studylistStudy'(event, instance) {
const $studyRow = $(event.currentTarget); const $studyRow = $(event.currentTarget);
if (!isStudySelected(instance.data)) { if (!instance.data.selected) {
doSelectSingleRow($studyRow, instance.data); doSelectSingleRow($studyRow, instance.data);
} }

View File

@ -1,3 +1,4 @@
import './base';
import './collections'; import './collections';
import './lib'; import './lib';
import './components'; import './components';

View File

@ -1,7 +1,7 @@
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
OHIF.studylist.getSelectedStudies = () => { OHIF.studylist.getSelectedStudies = () => {
return StudyListSelectedStudies.find({}, { return OHIF.studylist.collections.Studies.find({ selected: true }, {
sort: { sort: {
studyDate: 1 studyDate: 1
} }

View File

@ -1,82 +0,0 @@
import { OHIF } from 'meteor/ohif:core';
import 'meteor/ohif:viewerbase';
// DEPRECATED - use OHIF.studylist.retrieveStudyMetadata instead
// Define the StudyMetaData object. This is used as a cache
// to store study meta data information to prevent unnecessary
// calls to the server
var StudyMetaData = {};
/**
* Retrieves study metadata using a server call, and fires a callback
* when completed.
*
* @params {string} studyInstanceUid The UID of the Study to be retrieved
* @params {function} doneCallback The callback function to be executed when the study retrieval has finished
* @param failCallback The callback function to be executed when the study retrieval has failed
*/
getStudyMetadata = function(studyInstanceUid, doneCallback, failCallback) {
// If the StudyMetaData cache already has data related to this
// studyInstanceUid, then we should fire the doneCallback with this data
// and stop here.
var study = StudyMetaData[studyInstanceUid];
if (study) {
doneCallback(study);
return;
}
console.time('getStudyMetadata');
// If no study metadata is in the cache variable, we need to retrieve it from
// the server with a call.
Meteor.call('GetStudyMetadata', studyInstanceUid, function(error, study) {
console.timeEnd('getStudyMetadata');
if (Meteor.user && Meteor.user()) {
var hipaaEvent = {
eventType: 'viewed',
userId: Meteor.userId(),
userName: Meteor.user().profile.fullName,
collectionName: 'Study',
recordId: studyInstanceUid,
patientId: study.patientId,
patientName: study.patientName
};
HipaaLogger.logEvent(hipaaEvent);
}
if (error) {
OHIF.log.warn(error);
failCallback(error);
return;
}
if (!study) {
throw "GetStudyMetadata: No study data returned from server";
}
// Once we have retrieved the data, we sort the series' by series
// and instance number in ascending order
OHIF.viewerbase.sortStudy(study);
// Updates WADO-RS metaDataManager
OHIF.viewerbase.updateMetaDataManager(study);
// Add additional metadata to our study from the studylist
var studylistStudy = StudyListStudies.findOne({
studyInstanceUid: study.studyInstanceUid
});
if (!studylistStudy) {
// return;
}
$.extend(study, studylistStudy);
// Then we store this data in the cache variable
StudyMetaData[studyInstanceUid] = study;
// Finally, we fire the doneCallback with this study meta data
doneCallback(study);
});
};

View File

@ -26,8 +26,8 @@ const getStudyPriors = study => {
const patientID = study.getTagValue(PATIENT_ID); // PatientID const patientID = study.getTagValue(PATIENT_ID); // PatientID
const studyDate = study.getTagValue(STUDY_DATE); // StudyDate const studyDate = study.getTagValue(STUDY_DATE); // StudyDate
// Find prior studies in global StudyListStudies Minimongo collection // Find prior studies in global Studies Minimongo collection
const cursor = StudyListStudies.find({ const cursor = OHIF.studylist.collections.Studies.find({
patientId: patientID, patientId: patientID,
studyDate: { studyDate: {
$lt: studyDate $lt: studyDate

View File

@ -3,12 +3,9 @@ import './third-party/jquery.twbsPagination.min.js';
import './exportSelectedStudies.js'; import './exportSelectedStudies.js';
import './exportStudies.js'; import './exportStudies.js';
import './getSelectedStudies.js'; import './getSelectedStudies.js';
import './getStudyMetadata.js';
import './getStudiesMetadata.js';
import './importStudies.js'; import './importStudies.js';
import './openNewTab.js';
import './queryStudies.js'; import './queryStudies.js';
import './retrieveStudiesMetadata.js';
import './retrieveStudyMetadata.js'; import './retrieveStudyMetadata.js';
import './studylist.js'; import './studylist.js';
import './switchToTab.js';
import './viewSeriesDetails.js'; import './viewSeriesDetails.js';

View File

@ -1,27 +0,0 @@
import { Random } from 'meteor/random';
import { OHIF } from 'meteor/ohif:core';
/**
* Opens a new tab in the tabbed studylist environment using
* a given study and new tab title.
*
* @param studyInstanceUid The UID of the Study to be opened
*/
const openNewTab = studyInstanceUid => {
OHIF.log.info('openNewTab');
// Generate a unique ID to represent this tab
// We can't just use the Mongo entry ID because
// then it will change after hot-reloading.
const contentId = Random.id();
// Update the ViewerData global object
ViewerData = window.ViewerData || ViewerData;
ViewerData[contentId] = {
contentId: contentId,
studyInstanceUids: [studyInstanceUid]
};
// Switch to the new tab
switchToTab(contentId);
};

View File

@ -7,10 +7,9 @@ import { OHIF } from 'meteor/ohif:core';
* and waits for all of the results to be returned. * and waits for all of the results to be returned.
* *
* @param studyInstanceUids The UIDs of the Studies to be retrieved * @param studyInstanceUids The UIDs of the Studies to be retrieved
* @param doneCallback The callback function to be executed when the study retrieval has finished * @return Promise
* @param failCallback The callback function to be executed when the study retrieval has failed
*/ */
OHIF.studylist.getStudiesMetadata = (studyInstanceUids, doneCallback, failCallback) => { OHIF.studylist.retrieveStudiesMetadata = (studyInstanceUids, doneCallback, failCallback) => {
// Check to make sure studyInstanceUids were actually input // Check to make sure studyInstanceUids were actually input
if (!studyInstanceUids || !studyInstanceUids.length) { if (!studyInstanceUids || !studyInstanceUids.length) {
if (failCallback && typeof failCallback === 'function') { if (failCallback && typeof failCallback === 'function') {
@ -20,8 +19,7 @@ OHIF.studylist.getStudiesMetadata = (studyInstanceUids, doneCallback, failCallba
return; return;
} }
// Create an empty array to store the Promises for each metaData // Create an empty array to store the Promises for each metaData retrieval call
// retrieval call
const promises = []; const promises = [];
// Loop through the array of studyInstanceUids // Loop through the array of studyInstanceUids
@ -35,15 +33,10 @@ OHIF.studylist.getStudiesMetadata = (studyInstanceUids, doneCallback, failCallba
}); });
// When all of the promises are complete, this callback runs // When all of the promises are complete, this callback runs
Promise.all(promises).then(studies => { const promise = Promise.all(promises);
// Pass the studies array to the doneCallback, if one exists
if (doneCallback && typeof doneCallback === 'function') { // Warn the error on console if some retrieval failed
doneCallback(studies); promise.catch(error => OHIF.log.warn(error));
}
}).catch(error => { return promise;
OHIF.log.warn(error);
if (failCallback && typeof failCallback === 'function') {
failCallback(error);
}
});
}; };

View File

@ -62,13 +62,11 @@ OHIF.studylist.retrieveStudyMetadata = studyInstanceUid => {
OHIF.viewerbase.updateMetaDataManager(study); OHIF.viewerbase.updateMetaDataManager(study);
// Add additional metadata to our study from the studylist // Add additional metadata to our study from the studylist
const studylistStudy = StudyListStudies.findOne({ const studylistStudy = OHIF.studylist.collections.Studies.findOne({
studyInstanceUid: study.studyInstanceUid studyInstanceUid: study.studyInstanceUid
}); });
if (!studylistStudy) { if (studylistStudy) {
// return;
} else {
Object.assign(study, studylistStudy); Object.assign(study, studylistStudy);
} }

View File

@ -1,3 +1,4 @@
import { OHIF } from 'meteor/ohif:core';
import { Viewerbase } from 'meteor/ohif:viewerbase'; import { Viewerbase } from 'meteor/ohif:viewerbase';
// Classes // Classes
import { OHIFStudyMetadataSource } from './OHIFStudyMetadataSource'; import { OHIFStudyMetadataSource } from './OHIFStudyMetadataSource';
@ -11,15 +12,14 @@ StudyList = {
getStudyPriors, getStudyPriors,
getStudyPriorsMap getStudyPriorsMap
}, },
callbacks: {},
classes: { classes: {
OHIFStudyMetadataSource, OHIFStudyMetadataSource,
OHIFStudySummary OHIFStudySummary
} }
}; };
StudyList.callbacks.dblClickOnStudy = dblClickOnStudy; OHIF.studylist.callbacks.dblClickOnStudy = dblClickOnStudy;
StudyList.callbacks.middleClickOnStudy = dblClickOnStudy; OHIF.studylist.callbacks.middleClickOnStudy = dblClickOnStudy;
function dblClickOnStudy(data) { function dblClickOnStudy(data) {
openNewTab(data.studyInstanceUid); openNewTab(data.studyInstanceUid);

View File

@ -1,153 +0,0 @@
import { Blaze } from 'meteor/blaze';
import { Session } from 'meteor/session';
import { jQuery, $ } from 'meteor/jquery';
import { Template } from 'meteor/templating';
import { OHIF } from 'meteor/ohif:core';
/**
* Switches to a new tab in the tabbed studylist container
* This function renders either the StudyList or the Viewer template with new data.
*
* @param contentId The unique ID of the tab to be switched to
*/
switchToTab = function(contentId) {
if (!contentId) {
return;
}
OHIF.log.info('Switching to tab: ' + contentId);
// Clear the cornerstone tool data to sync the measurements with the measurements API
cornerstoneTools.globalImageIdSpecificToolStateManager = cornerstoneTools.newImageIdSpecificToolStateManager();
$('.tab-content .tab-pane').removeClass('active');
if (contentId !== 'studylistTab') {
$('.tab-content .tab-pane#viewerTab').addClass('active');
} else {
$('.tab-content .tab-pane#' + contentId).addClass('active');
}
// Remove any previous Viewers from the DOM
$('.viewerContainer').remove();
$('.studylistContainer').remove();
// Update the 'activeContentId' variable in Session
Session.set('activeContentId', contentId);
// If we are switching to the StudyList tab, reset any CSS styles
// that have been applied to prevent scrolling in the Viewer.
// Then stop here, since nothing needs to be re-rendered.
let container;
if (contentId === 'studylistTab') {
container = $('.tab-content').find('#studylistTab').get(0);
if (!container) {
return;
}
const studylistContainer = document.createElement('div');
studylistContainer.classList.add('studylistContainer');
container.appendChild(studylistContainer);
// Use Blaze to render the StudyListResult Template into the container
Blaze.render(Template.studylistResult, studylistContainer);
document.body.style.overflow = null;
document.body.style.height = null;
document.body.style.minWidth = null;
document.body.style.position = null;
return;
}
// Tab was closed at some point, stop here
ViewerData = window.ViewerData || ViewerData;
if (!ViewerData[contentId]) {
return;
}
container = $('.tab-content').find('#viewerTab').get(0);
container.innerHTML = '';
// Use Blaze to render the Loading Template into the container
// viewStudiesInTab will clear this container
Blaze.renderWithData(Template.loadingText, {}, container);
const studies = ViewerData[contentId].studies;
if (studies) {
// ViewerData already has the meta data (in cases when studylist is launched externally)
viewStudiesInTab(contentId, studies);
} else {
// Use the stored ViewerData global object to retrieve the studyInstanceUid
// related to this tab
const studyInstanceUids = ViewerData[contentId].studyInstanceUids;
// Attempt to retrieve the meta data (it might be cached)
OHIF.studylist.getStudiesMetadata(studyInstanceUids, function(studies) {
viewStudiesInTab(contentId, studies);
});
}
};
const viewStudiesInTab = (contentId, studies) => {
// Tab closed while study data was being retrieved, stop here
if (!ViewerData[contentId]) {
OHIF.log.warn('Tab closed while study data was being retrieved');
return;
}
// Once we have the study data, store it in a structure with
// any other saved data about this tab (e.g. layout structure)
const data = jQuery.extend({}, ViewerData[contentId]);
data.studies = studies;
// TODO: check if this is necessary. Since the typo bug
// was fixed (it was contentid instead of contentId)
data.contentId = contentId;
if (ViewerData[contentId].studies && ViewerData[contentId].studies.length) {
data.studies = ViewerData[contentId].studies;
}
// Add additional metadata to our study from the studylist
data.studies.forEach(study => {
const studylistStudy = StudyListStudies.findOne({
studyInstanceUid: study.studyInstanceUid
});
if (!studylistStudy) {
return;
}
$.extend(study, studylistStudy);
});
// Get tab content container given the contentId string
// If no such container exists, stop here because something is wrong
const container = $('.tab-content').find('#viewerTab').get(0);
// Remove the loading text template that is inside the tab container by default
const viewerContainer = document.createElement('div');
viewerContainer.classList.add('viewerContainer');
container.innerHTML = '';
container.appendChild(viewerContainer);
// Use Blaze to render the Viewer Template into the container
Blaze.renderWithData(Template.viewer, data, viewerContainer);
// Retrieve the DOM element of the viewer
const imageViewer = $('#viewer');
// If it is present in the DOM (it should be), then apply
// styles to prevent page scrolling and overscrolling on mobile devices
if (imageViewer) {
document.body.style.overflow = 'hidden';
document.body.style.height = '100%';
document.body.style.width = '100%';
document.body.style.minWidth = 0;
// Prevent overscroll on mobile devices
document.body.style.position = 'fixed';
}
};

View File

@ -54,15 +54,8 @@ Package.onUse(function(api) {
api.export('Services', 'server'); api.export('Services', 'server');
// Export StudyList helper functions for usage in Routes // Export StudyList helper functions for usage in Routes
api.export('getStudyMetadata', 'client');
api.export('openNewTab', 'client');
api.export('switchToTab', 'client');
api.export('StudyList'); api.export('StudyList');
// Export the global ViewerData object
api.export('ViewerData', 'client');
// Export the Collections // Export the Collections
api.export('StudyListStudies', 'client');
api.export('StudyListSelectedStudies', 'client'); api.export('StudyListSelectedStudies', 'client');
}); });

View File

@ -54,11 +54,11 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
// This data will be saved so that the tab can be reloaded to the same state after tabs // This data will be saved so that the tab can be reloaded to the same state after tabs
// are switched // are switched
if (contentId) { if (contentId) {
if (!ViewerData[contentId]) { if (!OHIF.viewer.data) {
return; return;
} }
ViewerData[contentId].loadedSeriesData[viewportIndex] = {}; OHIF.viewer.data.loadedSeriesData[viewportIndex] = {};
} }
// Create shortcut to displaySet // Create shortcut to displaySet
@ -154,7 +154,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
// Additional tasks for metadata provider. If using your own // Additional tasks for metadata provider. If using your own
// metadata provider, this may not be necessary. // metadata provider, this may not be necessary.
// updateMetadata is important, though, to update image metadata that // updateMetadata is important, though, to update image metadata that
// for any reason was missing some information such as rows, columns, // for any reason was missing some information such as rows, columns,
// sliceThickness, etc (See MetadataProvider class from ohif-cornerstone package) // sliceThickness, etc (See MetadataProvider class from ohif-cornerstone package)
const metadataProvider = OHIF.viewer.metadataProvider; const metadataProvider = OHIF.viewer.metadataProvider;
const isUpdateMetadataDefined = metadataProvider && typeof metadataProvider.updateMetadata === 'function'; const isUpdateMetadataDefined = metadataProvider && typeof metadataProvider.updateMetadata === 'function';
@ -173,7 +173,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
let enabledElement; let enabledElement;
try { try {
enabledElement = cornerstone.getEnabledElement(element); enabledElement = cornerstone.getEnabledElement(element);
} }
catch (error) { catch (error) {
OHIF.log.warn('Viewport destroyed before loaded image could be displayed'); OHIF.log.warn('Viewport destroyed before loaded image could be displayed');
return; return;
@ -305,15 +305,13 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
// This lets the viewport overlay always display correct window / zoom values // This lets the viewport overlay always display correct window / zoom values
Session.set('CornerstoneImageRendered' + viewportIndex, Random.id()); Session.set('CornerstoneImageRendered' + viewportIndex, Random.id());
// Save the current viewport into the ViewerData global variable, as well as the // Save the current viewport into the OHIF.viewer.data global variable
// Meteor Session. This lets the viewport be saved/reloaded on a hot-code reload
const viewport = cornerstone.getViewport(element); const viewport = cornerstone.getViewport(element);
layoutManager.viewportData[viewportIndex].viewport = viewport; layoutManager.viewportData[viewportIndex].viewport = viewport;
ViewerData[contentId].loadedSeriesData[viewportIndex].viewport = viewport; OHIF.viewer.data.loadedSeriesData[viewportIndex].viewport = viewport;
Session.set('ViewerData', ViewerData);
// Check if it has onImageRendered loadAndCacheImage callback // Check if it has onImageRendered loadAndCacheImage callback
if(typeof callbacks.onImageRendered === 'function') { if (typeof callbacks.onImageRendered === 'function') {
callbacks.onImageRendered(event, eventData, viewportIndex, templateData); callbacks.onImageRendered(event, eventData, viewportIndex, templateData);
} }
}; };
@ -332,7 +330,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
// since this callback function is called multiple times (eg: when a tool is // since this callback function is called multiple times (eg: when a tool is
// enabled/disabled -> cornerstone[toolName].tool.enable) // enabled/disabled -> cornerstone[toolName].tool.enable)
if(isUpdateMetadataDefined) { if (isUpdateMetadataDefined) {
// Update the metaData for missing fields // Update the metaData for missing fields
metadataProvider.updateMetadata(eventData.enabledElement.image); metadataProvider.updateMetadata(eventData.enabledElement.image);
} }
@ -355,17 +353,16 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
updateOrientationMarkers(element); updateOrientationMarkers(element);
// If this viewport is displaying a stack of images, save the current image // If this viewport is displaying a stack of images, save the current image
// index in the stack to the global ViewerData object, as well as the Meteor Session. // index in the stack to the global OHIF.viewer.data object.
const stack = cornerstoneTools.getToolState(element, 'stack'); const stack = cornerstoneTools.getToolState(element, 'stack');
if (stack && stack.data.length && stack.data[0].imageIds.length > 1) { if (stack && stack.data.length && stack.data[0].imageIds.length > 1) {
const imageIdIndex = stack.data[0].imageIds.indexOf(templateData.imageId); const imageIdIndex = stack.data[0].imageIds.indexOf(templateData.imageId);
layoutManager.viewportData[viewportIndex].currentImageIdIndex = imageIdIndex; layoutManager.viewportData[viewportIndex].currentImageIdIndex = imageIdIndex;
ViewerData[contentId].loadedSeriesData[viewportIndex].currentImageIdIndex = imageIdIndex; OHIF.viewer.data.loadedSeriesData[viewportIndex].currentImageIdIndex = imageIdIndex;
Session.set('ViewerData', ViewerData);
} }
// Check if it has onNewImage loadAndCacheImage callback // Check if it has onNewImage loadAndCacheImage callback
if(typeof callbacks.onNewImage === 'function') { if (typeof callbacks.onNewImage === 'function') {
callbacks.onNewImage(event, eventData, viewportIndex, templateData); callbacks.onNewImage(event, eventData, viewportIndex, templateData);
} }
}; };
@ -429,7 +426,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => {
$element.off(allCornerstoneEvents, sendActivationTrigger); $element.off(allCornerstoneEvents, sendActivationTrigger);
$element.on(allCornerstoneEvents, sendActivationTrigger); $element.on(allCornerstoneEvents, sendActivationTrigger);
ViewerData[contentId].loadedSeriesData = layoutManager.viewportData; OHIF.viewer.data.loadedSeriesData = layoutManager.viewportData;
// 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);

View File

@ -38,7 +38,7 @@ Template.studyTimepointBrowser.onCreated(() => {
return loadedStudy; return loadedStudy;
} }
const notYetLoaded = StudyListStudies.findOne(query); const notYetLoaded = OHIF.studylist.collections.Studies.findOne(query);
if (!notYetLoaded) { if (!notYetLoaded) {
throw new OHIFError(`No study data available for Study: ${studyInstanceUid}`); throw new OHIFError(`No study data available for Study: ${studyInstanceUid}`);
} }

View File

@ -2,7 +2,6 @@ import { Meteor } from 'meteor/meteor';
import { $ } from 'meteor/jquery'; import { $ } from 'meteor/jquery';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import { toolManager } from './toolManager'; import { toolManager } from './toolManager';
import { setActiveViewport } from './setActiveViewport';
import { switchToImageRelative } from './switchToImageRelative'; import { switchToImageRelative } from './switchToImageRelative';
import { switchToImageByIndex } from './switchToImageByIndex'; import { switchToImageByIndex } from './switchToImageByIndex';
import { viewportUtils } from './viewportUtils'; import { viewportUtils } from './viewportUtils';
@ -11,10 +10,6 @@ import { WLPresets } from './WLPresets';
// TODO: add this to namespace definitions // TODO: add this to namespace definitions
Meteor.startup(function() { Meteor.startup(function() {
if (!OHIF.viewer) {
OHIF.viewer = {};
}
OHIF.viewer.loadIndicatorDelay = 200; OHIF.viewer.loadIndicatorDelay = 200;
OHIF.viewer.defaultTool = 'wwwc'; OHIF.viewer.defaultTool = 'wwwc';
OHIF.viewer.refLinesEnabled = true; OHIF.viewer.refLinesEnabled = true;
@ -56,149 +51,134 @@ Meteor.startup(function() {
OHIF.viewer.hotkeys = OHIF.viewer.defaultHotkeys; OHIF.viewer.hotkeys = OHIF.viewer.defaultHotkeys;
OHIF.viewer.hotkeyFunctions = { OHIF.viewer.hotkeyFunctions = {
wwwc() { wwwc: () => toolManager.setActiveTool('wwwc'),
toolManager.setActiveTool('wwwc'); zoom: () => toolManager.setActiveTool('zoom'),
}, angle: () => toolManager.setActiveTool('angle'),
zoom() { dragProbe: () => toolManager.setActiveTool('dragProbe'),
toolManager.setActiveTool('zoom'); ellipticalRoi: () => toolManager.setActiveTool('ellipticalRoi'),
}, magnify: () => toolManager.setActiveTool('magnify'),
angle() { annotate: () => toolManager.setActiveTool('annotate'),
toolManager.setActiveTool('angle'); stackScroll: () => toolManager.setActiveTool('stackScroll'),
}, pan: () => toolManager.setActiveTool('pan'),
dragProbe() { length: () => toolManager.setActiveTool('length'),
toolManager.setActiveTool('dragProbe'); spine: () => toolManager.setActiveTool('spine'),
}, wwwcRegion: () => toolManager.setActiveTool('wwwcRegion'),
ellipticalRoi() {
toolManager.setActiveTool('ellipticalRoi');
},
magnify() {
toolManager.setActiveTool('magnify');
},
annotate() {
toolManager.setActiveTool('annotate');
},
stackScroll() {
toolManager.setActiveTool('stackScroll');
},
pan() {
toolManager.setActiveTool('pan');
},
length() {
toolManager.setActiveTool('length');
},
spine() {
toolManager.setActiveTool('spine');
},
wwwcRegion() {
toolManager.setActiveTool('wwwcRegion');
},
zoomIn() { zoomIn() {
const button = document.getElementById('zoomIn'); const button = document.getElementById('zoomIn');
flashButton(button); flashButton(button);
viewportUtils.zoomIn(); viewportUtils.zoomIn();
}, },
zoomOut() { zoomOut() {
const button = document.getElementById('zoomOut'); const button = document.getElementById('zoomOut');
flashButton(button); flashButton(button);
viewportUtils.zoomOut(); viewportUtils.zoomOut();
}, },
zoomToFit() { zoomToFit() {
const button = document.getElementById('zoomToFit'); const button = document.getElementById('zoomToFit');
flashButton(button); flashButton(button);
viewportUtils.zoomToFit(); viewportUtils.zoomToFit();
}, },
scrollDown() {
const container = $('.viewportContainer.active');
const button = container.find('#nextImage').get(0);
if (!container.find('.imageViewerViewport').hasClass('empty')) { scrollDown() {
const $container = $('.viewportContainer.active');
const button = $container.find('#nextImage').get(0);
if (!$container.find('.imageViewerViewport').hasClass('empty')) {
flashButton(button); flashButton(button);
switchToImageRelative(1); switchToImageRelative(1);
} }
}, },
scrollFirstImage() { scrollFirstImage() {
const container = $('.viewportContainer.active'); const $container = $('.viewportContainer.active');
if (!container.find('.imageViewerViewport').hasClass('empty')) { if (!$container.find('.imageViewerViewport').hasClass('empty')) {
switchToImageByIndex(0); switchToImageByIndex(0);
} }
}, },
scrollLastImage() { scrollLastImage() {
const container = $('.viewportContainer.active'); const $container = $('.viewportContainer.active');
if (!container.find('.imageViewerViewport').hasClass('empty')) { if (!$container.find('.imageViewerViewport').hasClass('empty')) {
switchToImageByIndex(-1); switchToImageByIndex(-1);
} }
}, },
scrollUp() { scrollUp() {
const container = $('.viewportContainer.active'); const $container = $('.viewportContainer.active');
if (!container.find('.imageViewerViewport').hasClass('empty')) { if (!$container.find('.imageViewerViewport').hasClass('empty')) {
const button = container.find('#prevImage').get(0); const button = $container.find('#prevImage').get(0);
flashButton(button); flashButton(button);
switchToImageRelative(-1); switchToImageRelative(-1);
} }
}, },
previousDisplaySet() {
OHIF.viewerbase.layoutManager.moveDisplaySets(false); previousDisplaySet: () => OHIF.viewerbase.layoutManager.moveDisplaySets(false),
}, nextDisplaySet: () => OHIF.viewerbase.layoutManager.moveDisplaySets(true),
nextDisplaySet() { nextPanel: () => panelNavigation.loadNextActivePanel(),
OHIF.viewerbase.layoutManager.moveDisplaySets(true); previousPanel: () => panelNavigation.loadPreviousActivePanel(),
},
nextPanel() {
panelNavigation.loadNextActivePanel();
},
previousPanel() {
panelNavigation.loadPreviousActivePanel();
},
invert() { invert() {
const button = document.getElementById('invert'); const button = document.getElementById('invert');
flashButton(button); flashButton(button);
viewportUtils.invert(); viewportUtils.invert();
}, },
flipV() { flipV() {
const button = document.getElementById('flipV'); const button = document.getElementById('flipV');
flashButton(button); flashButton(button);
viewportUtils.flipV(); viewportUtils.flipV();
}, },
flipH() { flipH() {
const button = document.getElementById('flipH'); const button = document.getElementById('flipH');
flashButton(button); flashButton(button);
viewportUtils.flipH(); viewportUtils.flipH();
}, },
rotateR() { rotateR() {
const button = document.getElementById('rotateR'); const button = document.getElementById('rotateR');
flashButton(button); flashButton(button);
viewportUtils.rotateR(); viewportUtils.rotateR();
}, },
rotateL() { rotateL() {
const button = document.getElementById('rotateL'); const button = document.getElementById('rotateL');
flashButton(button); flashButton(button);
viewportUtils.rotateL(); viewportUtils.rotateL();
}, },
cinePlay() {
viewportUtils.toggleCinePlay(); cinePlay: () => viewportUtils.toggleCinePlay(),
},
defaultTool() { defaultTool() {
const tool = toolManager.getDefaultTool(); const tool = toolManager.getDefaultTool();
toolManager.setActiveTool(tool); toolManager.setActiveTool(tool);
}, },
toggleOverlayTags() { toggleOverlayTags() {
const dicomTags = $('.imageViewerViewportOverlay .dicomTag'); const $dicomTags = $('.imageViewerViewportOverlay .dicomTag');
if (dicomTags.eq(0).css('display') === 'none') { if ($dicomTags.eq(0).css('display') === 'none') {
dicomTags.show(); $dicomTags.show();
} else { } else {
dicomTags.hide(); $dicomTags.hide();
} }
}, },
resetStack() { resetStack() {
const button = document.getElementById('resetStack'); const button = document.getElementById('resetStack');
flashButton(button); flashButton(button);
resetStack(); resetStack();
}, },
clearImageAnnotations() { clearImageAnnotations() {
const button = document.getElementById('clearImageAnnotations'); const button = document.getElementById('clearImageAnnotations');
flashButton(button); flashButton(button);
clearImageAnnotations(); clearImageAnnotations();
}, },
cineDialog () {
cineDialog() {
/** /**
* TODO: This won't work in OHIF's, since this element * TODO: This won't work in OHIF's, since this element
* doesn't exist * doesn't exist
@ -233,21 +213,18 @@ function setOHIFHotkeys(hotkeys) {
} }
/** /**
* Global function to merge different hotkeys configurations * Global function to merge different hotkeys configurations
* but avoiding conflicts between different keys with same action * but avoiding conflicts between different keys with same action
* When this occurs, it will delete the action from OHIF's configuration * When this occurs, it will delete the action from OHIF's configuration
* So if you want to keep all OHIF's actions, use an unused-ohif-key * So if you want to keep all OHIF's actions, use an unused-ohif-key
* Used for compatibility with others systems only * Used for compatibility with others systems only
* *
* @param hotkeysActions {object} Object with actions map * @param hotkeysActions {object} Object with actions map
* @return {object} * @return {object}
*/ */
function mergeHotkeys(hotkeysActions) { function mergeHotkeys(hotkeysActions) {
// Merge hotkeys, overriding OHIF's settings // Merge hotkeys, overriding OHIF's settings
let mergedHotkeys = { const mergedHotkeys = Object.assign({}, OHIF.viewer.defaultHotkeys, hotkeysActions);
...OHIF.viewer.defaultHotkeys,
...hotkeysActions
};
const defaultHotkeys = OHIF.viewer.defaultHotkeys; const defaultHotkeys = OHIF.viewer.defaultHotkeys;
const hotkeysKeys = Object.keys(hotkeysActions); const hotkeysKeys = Object.keys(hotkeysActions);
@ -258,7 +235,7 @@ function mergeHotkeys(hotkeysActions) {
// Different action but same key: // Different action but same key:
// Remove action from merge if is not in "hotkeysActions" // Remove action from merge if is not in "hotkeysActions"
// If it is, it's already merged so nothing to do // If it is, it's already merged so nothing to do
if(ohifAction !== definedAction && hotkeysActions[definedAction] === defaultHotkeys[ohifAction] && !hotkeysActions[ohifAction]) { if (ohifAction !== definedAction && hotkeysActions[definedAction] === defaultHotkeys[ohifAction] && !hotkeysActions[ohifAction]) {
delete mergedHotkeys[ohifAction]; delete mergedHotkeys[ohifAction];
} }
}); });
@ -272,7 +249,7 @@ function mergeHotkeys(hotkeysActions) {
* to give the impressiont the button was pressed. * to give the impressiont the button was pressed.
* This is for tools that don't keep the button "pressed" * This is for tools that don't keep the button "pressed"
* all the time the tool is active. * all the time the tool is active.
* *
* @param button DOM Element for the button to be "flashed" * @param button DOM Element for the button to be "flashed"
*/ */
function flashButton(button) { function flashButton(button) {
@ -292,16 +269,16 @@ function flashButton(button) {
* @param {String} task task function name * @param {String} task task function name
*/ */
function bindHotkey(hotkey, task) { function bindHotkey(hotkey, task) {
var hotkeyFunctions = OHIF.viewer.hotkeyFunctions; const hotkeyFunctions = OHIF.viewer.hotkeyFunctions;
// Only bind defined, non-empty HotKeys // Only bind defined, non-empty HotKeys
if (!hotkey || hotkey === '') { if (!hotkey || hotkey === '') {
return; return;
} }
var fn; let fn;
if (task.indexOf('WLPreset') > -1) { if (task.indexOf('WLPreset') > -1) {
var presetName = task.replace('WLPreset', ''); const presetName = task.replace('WLPreset', '');
fn = function() { fn = function() {
WLPresets.applyWLPresetToActiveElement(presetName); WLPresets.applyWLPresetToActiveElement(presetName);
}; };
@ -319,7 +296,7 @@ function bindHotkey(hotkey, task) {
return; return;
} }
var hotKeyForBinding = hotkey.toLowerCase(); const hotKeyForBinding = hotkey.toLowerCase();
$(document).bind('keydown', hotKeyForBinding, fn); $(document).bind('keydown', hotKeyForBinding, fn);
} }
@ -341,9 +318,7 @@ function enableHotkeys(hotkeys) {
} }
if (taskHotkeys instanceof Array) { if (taskHotkeys instanceof Array) {
taskHotkeys.forEach(function(hotkey) { taskHotkeys.forEach(hotkey => bindHotkey(hotkey, task));
bindHotkey(hotkey, task);
});
} else { } else {
// taskHotkeys represents a single key // taskHotkeys represents a single key
bindHotkey(taskHotkeys, task); bindHotkey(taskHotkeys, task);

View File

@ -1,6 +0,0 @@
// Define the ViewerData global object
// If there is currently any Session data for this object,
// use this to repopulate the variable
Template.standaloneViewer.onCreated(() => {
ViewerData = Session.get('ViewerData') || {};
});

View File

@ -2,7 +2,6 @@ import { OHIF } from 'meteor/ohif:core';
import 'meteor/ohif:viewerbase'; import 'meteor/ohif:viewerbase';
import 'meteor/ohif:metadata'; import 'meteor/ohif:metadata';
OHIF.viewer = OHIF.viewer || {};
const viewportUtils = OHIF.viewerbase.viewportUtils; const viewportUtils = OHIF.viewerbase.viewportUtils;
OHIF.viewer.functionList = { OHIF.viewer.functionList = {