Replacing switchToTab with routes
This commit is contained in:
parent
0cf4dbf13f
commit
7718d31a77
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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');
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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');
|
||||||
|
|||||||
@ -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'
|
||||||
|
|||||||
@ -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')
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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')
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
|
import './base.js';
|
||||||
import './lib';
|
import './lib';
|
||||||
import './schema';
|
import './schema';
|
||||||
import './base.js';
|
|
||||||
import './collections.js';
|
import './collections.js';
|
||||||
|
|||||||
5
Packages/ohif-study-list/client/base.js
Normal file
5
Packages/ohif-study-list/client/base.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
Object.assign(OHIF.studylist, {
|
||||||
|
callbacks: {}
|
||||||
|
});
|
||||||
@ -1 +1,2 @@
|
|||||||
|
import './studies.js';
|
||||||
import './subscriptions.js';
|
import './subscriptions.js';
|
||||||
|
|||||||
11
Packages/ohif-study-list/client/collections/studies.js
Normal file
11
Packages/ohif-study-list/client/collections/studies.js
Normal 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;
|
||||||
|
});
|
||||||
@ -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';
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -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%
|
|
||||||
|
|||||||
@ -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}}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import './base';
|
||||||
import './collections';
|
import './collections';
|
||||||
import './lib';
|
import './lib';
|
||||||
import './components';
|
import './components';
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@ -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
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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);
|
|
||||||
};
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -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');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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
|
||||||
@ -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);
|
||||||
|
|||||||
@ -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}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
@ -244,10 +224,7 @@ function setOHIFHotkeys(hotkeys) {
|
|||||||
*/
|
*/
|
||||||
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];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -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);
|
||||||
|
|||||||
@ -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') || {};
|
|
||||||
});
|
|
||||||
@ -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 = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user