Fixed OHIF Viewer layout / CINE / Hanging Protocol buttons (OHIF-49)
This commit is contained in:
parent
59c851fa57
commit
8b364ce9d0
@ -8,6 +8,9 @@
|
||||
<div class="pull-right m-t-1 rm-x-1">
|
||||
{{>roundedButtonGroup rightSidebarToggleButtonData}}
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{{> toolbarSectionTools toolbarButtons=hangingProtocolButtons}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -82,6 +82,27 @@ Template.toolbarSection.helpers({
|
||||
iconClasses: 'fa fa-undo'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'toggleCinePlay',
|
||||
title: 'Toggle CINE Play',
|
||||
classes: 'imageViewerCommand',
|
||||
buttonTemplateName: 'playClipButton'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'toggleCineDialog',
|
||||
title: 'CINE',
|
||||
classes: 'imageViewerCommand',
|
||||
iconClasses: 'fa fa-youtube-play'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'layout',
|
||||
title: 'Layout',
|
||||
iconClasses: 'fa fa-th-large',
|
||||
buttonTemplateName: 'layoutButton'
|
||||
});
|
||||
|
||||
return buttonData;
|
||||
},
|
||||
|
||||
@ -145,6 +166,26 @@ Template.toolbarSection.helpers({
|
||||
});
|
||||
|
||||
return buttonData;
|
||||
},
|
||||
|
||||
hangingProtocolButtons() {
|
||||
let buttonData = [];
|
||||
|
||||
buttonData.push({
|
||||
id: 'previousPresentationGroup',
|
||||
title: 'Prev. Stage',
|
||||
iconClasses: 'fa fa-step-backward',
|
||||
buttonTemplateName: 'previousPresentationGroupButton'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'nextPresentationGroup',
|
||||
title: 'Next Stage',
|
||||
iconClasses: 'fa fa-step-forward',
|
||||
buttonTemplateName: 'nextPresentationGroupButton'
|
||||
});
|
||||
|
||||
return buttonData;
|
||||
}
|
||||
|
||||
});
|
||||
@ -152,6 +193,8 @@ Template.toolbarSection.helpers({
|
||||
Template.toolbarSection.onRendered(function() {
|
||||
const instance = Template.instance();
|
||||
|
||||
instance.$('#layout').dropdown();
|
||||
|
||||
// Set disabled/enabled tool buttons that are set in toolManager
|
||||
const states = toolManager.getToolDefaultStates();
|
||||
const disabledToolButtons = states.disabledToolButtons;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
{{#if Template.subscriptionsReady}}
|
||||
<div class="viewerDialogs">
|
||||
{{> cineDialog}}
|
||||
{{> layoutChooser }}
|
||||
</div>
|
||||
<div id="viewer">
|
||||
{{> toolbarSection (clone this)}}
|
||||
|
||||
@ -1,5 +1,22 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
OHIF.viewer = OHIF.viewer || {};
|
||||
OHIF.viewer.loadIndicatorDelay = 500;
|
||||
OHIF.viewer.defaultTool = 'wwwc';
|
||||
OHIF.viewer.refLinesEnabled = true;
|
||||
OHIF.viewer.isPlaying = {};
|
||||
OHIF.viewer.cine = {
|
||||
framesPerSecond: 24,
|
||||
loop: true
|
||||
};
|
||||
|
||||
OHIF.viewer.functionList = {
|
||||
toggleCineDialog: toggleCineDialog,
|
||||
toggleCinePlay: toggleCinePlay,
|
||||
clearTools: clearTools,
|
||||
resetViewport: resetViewport,
|
||||
invert: invert
|
||||
};
|
||||
|
||||
Session.setDefault('activeViewport', false);
|
||||
Session.setDefault('leftSidebar', false);
|
||||
@ -8,9 +25,6 @@ Session.setDefault('rightSidebar', false);
|
||||
Template.viewer.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
// Attach the Window resize listener
|
||||
$(window).on('resize', handleResize);
|
||||
|
||||
instance.data.state = new ReactiveDict();
|
||||
instance.data.state.set('leftSidebar', Session.get('leftSidebar'));
|
||||
instance.data.state.set('rightSidebar', Session.get('rightSidebar'));
|
||||
@ -19,42 +33,6 @@ Template.viewer.onCreated(() => {
|
||||
|
||||
log.info('viewer onCreated');
|
||||
|
||||
OHIF.viewer.loadIndicatorDelay = 500;
|
||||
OHIF.viewer.defaultTool = 'wwwc';
|
||||
OHIF.viewer.refLinesEnabled = true;
|
||||
OHIF.viewer.isPlaying = {};
|
||||
OHIF.viewer.cine = {
|
||||
framesPerSecond: 24,
|
||||
loop: true
|
||||
};
|
||||
|
||||
OHIF.viewer.functionList = {
|
||||
invert: element => {
|
||||
const viewport = cornerstone.getViewport(element);
|
||||
viewport.invert = !viewport.invert;
|
||||
cornerstone.setViewport(element, viewport);
|
||||
},
|
||||
resetViewport: element => {
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
if (enabledElement.fitToWindow === false) {
|
||||
const imageId = enabledElement.image.imageId;
|
||||
const instance = cornerstoneTools.metaData.get('instance', imageId);
|
||||
|
||||
enabledElement.viewport = cornerstone.getDefaultViewport(enabledElement.canvas, enabledElement.image);
|
||||
|
||||
const instanceClassDefaultViewport = getInstanceClassDefaultViewport(instance, enabledElement, imageId);
|
||||
cornerstone.setViewport(element, instanceClassDefaultViewport);
|
||||
} else {
|
||||
cornerstone.reset(element);
|
||||
}
|
||||
},
|
||||
clearTools: element => {
|
||||
const toolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager;
|
||||
toolStateManager.clear(element);
|
||||
cornerstone.updateImage(element);
|
||||
}
|
||||
};
|
||||
|
||||
if (isTouchDevice()) {
|
||||
OHIF.viewer.tooltipConfig = {
|
||||
trigger: 'manual'
|
||||
@ -96,27 +74,6 @@ Template.viewer.onCreated(() => {
|
||||
});
|
||||
});
|
||||
|
||||
Template.viewer.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
// Enable hotkeys
|
||||
enableHotkeys();
|
||||
|
||||
const parentNode = document.getElementById('layoutManagerTarget');
|
||||
const studies = instance.data.studies;
|
||||
layoutManager = new LayoutManager(parentNode, studies);
|
||||
|
||||
ProtocolEngine = new HP.ProtocolEngine(layoutManager, studies);
|
||||
HP.setEngine(ProtocolEngine);
|
||||
});
|
||||
|
||||
Template.viewer.onDestroyed(() => {
|
||||
log.info('onDestroyed');
|
||||
|
||||
// Remove the Window resize listener
|
||||
$(window).off('resize', handleResize);
|
||||
});
|
||||
|
||||
Template.viewer.events({
|
||||
'click .js-toggle-studies'() {
|
||||
const instance = Template.instance();
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
<template name="hangingProtocolButtons">
|
||||
<button id="previousPresentationGroup" type="button"
|
||||
class="btn btn-sm"
|
||||
data-container="body"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
title="Previous presentation group"
|
||||
disabled="{{ #unless isPreviousAvailable }} true {{ /unless }}">
|
||||
<span class="fa fa-step-backward"></span>
|
||||
</button>
|
||||
<button id="nextPresentationGroup" type="button"
|
||||
class="btn btn-sm"
|
||||
data-container="body"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
title="Next presentation group"
|
||||
disabled="{{ #unless isNextAvailable }} true {{ /unless }}">
|
||||
<span class="fa fa-step-forward"></span>
|
||||
</button>
|
||||
</template>
|
||||
@ -1,73 +0,0 @@
|
||||
Template.hangingProtocolButtons.helpers({
|
||||
/**
|
||||
* Check if a later stage exists for the user to switch to
|
||||
*
|
||||
* @returns {boolean} Whether or not a later stage exists
|
||||
*/
|
||||
isNextAvailable: function() {
|
||||
// Run this helper whenever the ProtocolEngine / LayoutManager has changed
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
// If no ProtocolEngine has been defined yet, stop here
|
||||
if (!ProtocolEngine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return whether or not the current stage is the last stage
|
||||
return ProtocolEngine.stage < ProtocolEngine.getNumProtocolStages() - 1;
|
||||
},
|
||||
/**
|
||||
* Check if an earlier stage exists for the user to switch to
|
||||
*
|
||||
* @returns {boolean} Whether or not an earlier stage exists
|
||||
*/
|
||||
isPreviousAvailable: function() {
|
||||
// Run this helper whenever the ProtocolEngine / LayoutManager has changed
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
// If no ProtocolEngine has been defined yet, stop here
|
||||
if (!ProtocolEngine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return whether or not the current stage is the first stage
|
||||
return ProtocolEngine.stage > 0;
|
||||
}
|
||||
});
|
||||
|
||||
Template.hangingProtocolButtons.events({
|
||||
/**
|
||||
* Switch to the previous Presentation group
|
||||
*
|
||||
* @param event The click event on the button
|
||||
*/
|
||||
'click #previousPresentationGroup': function(event) {
|
||||
// If no ProtocolEngine has been defined yet, do nothing
|
||||
if (!ProtocolEngine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the button's Bootstrap tooltip in case it was shown
|
||||
$(event.currentTarget).tooltip('hide');
|
||||
|
||||
// Instruct the ProtocolEngine to switch to the next stage
|
||||
ProtocolEngine.previousProtocolStage();
|
||||
},
|
||||
/**
|
||||
* Switch to the next Presentation group
|
||||
*
|
||||
* @param event The click event on the button
|
||||
*/
|
||||
'click #nextPresentationGroup': function(event) {
|
||||
// If no ProtocolEngine has been defined yet, do nothing
|
||||
if (!ProtocolEngine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the button's Bootstrap tooltip in case it was shown
|
||||
$(event.currentTarget).tooltip('hide');
|
||||
|
||||
// Instruct the ProtocolEngine to switch to the next stage
|
||||
ProtocolEngine.nextProtocolStage();
|
||||
}
|
||||
});
|
||||
@ -6,7 +6,8 @@
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
title="Select Hanging Protocol">
|
||||
<i class="fa fa-list-ol"></i><span class="caret"></span>
|
||||
<i class="fa fa-list-ol"></i>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
@ -14,7 +15,9 @@
|
||||
</li>
|
||||
{{ #each matchedProtocols }}
|
||||
<li>
|
||||
<a class='matchedProtocol {{#if active}}active{{/if}}'>{{name}}</a>
|
||||
<a class='matchedProtocol {{#if active}}active{{/if}}'>
|
||||
{{name}}
|
||||
</a>
|
||||
</li>
|
||||
{{ /each }}
|
||||
</ul>
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
<template name="nextPresentationGroupButton">
|
||||
<div id="{{this.id}}"
|
||||
class="toolbarSectionButton rp-x-1 {{this.classes}} {{activeClass}} {{ #if nextNotAvailable }}disabled{{ /if }}"
|
||||
title="{{this.title}}"
|
||||
disabled="{{ #if nextNotAvailable }}true{{ /if }}">
|
||||
<div class="svgContainer">
|
||||
{{ #if this.svgLink }}
|
||||
<svg>
|
||||
<use xlink:href={{this.svgLink}}></use>
|
||||
</svg>
|
||||
{{ else }}
|
||||
<i class={{iconClasses}}></i>
|
||||
{{ /if }}
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
{{this.title}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,39 @@
|
||||
Template.nextPresentationGroupButton.helpers({
|
||||
/**
|
||||
* Check if a later stage exists for the user to switch to
|
||||
*
|
||||
* @returns {boolean} Whether or not a later stage exists
|
||||
*/
|
||||
nextNotAvailable() {
|
||||
// Run this helper whenever the ProtocolEngine / LayoutManager has changed
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
// If no ProtocolEngine has been defined yet, stop here
|
||||
if (!ProtocolEngine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return whether or not the current stage is the last stage
|
||||
return ProtocolEngine.stage === ProtocolEngine.getNumProtocolStages() - 1;
|
||||
}
|
||||
});
|
||||
|
||||
Template.nextPresentationGroupButton.events({
|
||||
/**
|
||||
* Switch to the next Presentation group
|
||||
*
|
||||
* @param event The click event on the button
|
||||
*/
|
||||
'click #nextPresentationGroup'(event) {
|
||||
// If no ProtocolEngine has been defined yet, do nothing
|
||||
if (!ProtocolEngine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the button's Bootstrap tooltip in case it was shown
|
||||
$(event.currentTarget).tooltip('hide');
|
||||
|
||||
// Instruct the ProtocolEngine to switch to the next stage
|
||||
ProtocolEngine.nextProtocolStage();
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,19 @@
|
||||
<template name="previousPresentationGroupButton">
|
||||
<div id="{{this.id}}"
|
||||
class="toolbarSectionButton rp-x-1 {{this.classes}} {{activeClass}} {{ #if previousNotAvailable }}disabled{{ /if }}"
|
||||
title="{{this.title}}"
|
||||
disabled="{{ #if previousNotAvailable }}true{{ /if }}">
|
||||
<div class="svgContainer">
|
||||
{{ #if this.svgLink }}
|
||||
<svg>
|
||||
<use xlink:href={{this.svgLink}}></use>
|
||||
</svg>
|
||||
{{ else }}
|
||||
<i class={{iconClasses}}></i>
|
||||
{{ /if }}
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
{{this.title}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,39 @@
|
||||
Template.previousPresentationGroupButton.helpers({
|
||||
/**
|
||||
* Check if an earlier stage exists for the user to switch to
|
||||
*
|
||||
* @returns {boolean} Whether or not an earlier stage exists
|
||||
*/
|
||||
previousNotAvailable() {
|
||||
// Run this helper whenever the ProtocolEngine / LayoutManager has changed
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
// If no ProtocolEngine has been defined yet, stop here
|
||||
if (!ProtocolEngine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return whether or not the current stage is the first stage
|
||||
return ProtocolEngine.stage === 0;
|
||||
}
|
||||
});
|
||||
|
||||
Template.previousPresentationGroupButton.events({
|
||||
/**
|
||||
* Switch to the previous Presentation group
|
||||
*
|
||||
* @param event The click event on the button
|
||||
*/
|
||||
'click #previousPresentationGroup'(event) {
|
||||
// If no ProtocolEngine has been defined yet, do nothing
|
||||
if (!ProtocolEngine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the button's Bootstrap tooltip in case it was shown
|
||||
$(event.currentTarget).tooltip('hide');
|
||||
|
||||
// Instruct the ProtocolEngine to switch to the next stage
|
||||
ProtocolEngine.previousProtocolStage();
|
||||
}
|
||||
});
|
||||
@ -45,8 +45,11 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/helpers/attributes.js', 'client');
|
||||
|
||||
// UI Components
|
||||
api.addFiles('client/components/hangingProtocolButtons/hangingProtocolButtons.html', 'client');
|
||||
api.addFiles('client/components/hangingProtocolButtons/hangingProtocolButtons.js', 'client');
|
||||
api.addFiles('client/components/previousPresentationGroupButton/previousPresentationGroupButton.html', 'client');
|
||||
api.addFiles('client/components/previousPresentationGroupButton/previousPresentationGroupButton.js', 'client');
|
||||
|
||||
api.addFiles('client/components/nextPresentationGroupButton/nextPresentationGroupButton.html', 'client');
|
||||
api.addFiles('client/components/nextPresentationGroupButton/nextPresentationGroupButton.js', 'client');
|
||||
|
||||
api.addFiles('client/components/matchedProtocols/matchedProtocols.html', 'client');
|
||||
api.addFiles('client/components/matchedProtocols/matchedProtocols.styl', 'client');
|
||||
|
||||
@ -3,20 +3,20 @@
|
||||
// use this to repopulate the variable
|
||||
ViewerData = Session.get('ViewerData') || {};
|
||||
|
||||
var worklistContentId = 'worklistTab';
|
||||
var viewerContentId = 'viewerTab';
|
||||
const worklistContentId = 'worklistTab';
|
||||
const viewerContentId = 'viewerTab';
|
||||
|
||||
Template.lesionTracker.onRendered(function() {
|
||||
var templateData = Template.currentData();
|
||||
const templateData = Template.currentData();
|
||||
if (templateData && templateData.studyInstanceUid) {
|
||||
var studyInstanceUid = templateData.studyInstanceUid;
|
||||
const studyInstanceUid = templateData.studyInstanceUid;
|
||||
openNewTab(studyInstanceUid, studyInstanceUid);
|
||||
} else {
|
||||
// If there is a tab set as active in the Session,
|
||||
// switch to that now.
|
||||
var contentId = Session.get('activeContentId');
|
||||
const contentId = Session.get('activeContentId');
|
||||
|
||||
// TODO: FIx this it seems to be forcing two switches
|
||||
// TODO: Fix this it seems to be forcing two switches
|
||||
switchToTab(contentId);
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ Template.lesionTracker.onRendered(function() {
|
||||
|
||||
Template.lesionTracker.events({
|
||||
'click .js-toggle-studyList': function() {
|
||||
var contentId = Session.get('activeContentId');
|
||||
const contentId = Session.get('activeContentId');
|
||||
|
||||
if (contentId !== worklistContentId) {
|
||||
switchToTab(worklistContentId);
|
||||
@ -39,11 +39,11 @@ Session.set('defaultSignInMessage', 'Tumor tracking in your browser.');
|
||||
|
||||
Template.lesionTracker.helpers({
|
||||
studyListToggleText: function() {
|
||||
var contentId = Session.get('activeContentId');
|
||||
const contentId = Session.get('activeContentId');
|
||||
|
||||
// If the Viewer has not been opened yet, 'Back to viewer' should
|
||||
// not be displayed
|
||||
var viewerContentExists = !!Object.keys(ViewerData).length;
|
||||
const viewerContentExists = !!Object.keys(ViewerData).length;
|
||||
if (!viewerContentExists) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2,6 +2,14 @@ import { OHIF } from 'meteor/ohif:core';
|
||||
import { TimepointApi } from 'meteor/lesiontracker/lib/api/timepoint';
|
||||
|
||||
OHIF.viewer = OHIF.viewer || {};
|
||||
OHIF.viewer.loadIndicatorDelay = 3000;
|
||||
OHIF.viewer.defaultTool = 'wwwc';
|
||||
OHIF.viewer.refLinesEnabled = true;
|
||||
OHIF.viewer.isPlaying = {};
|
||||
OHIF.viewer.cine = {
|
||||
framesPerSecond: 24,
|
||||
loop: true
|
||||
};
|
||||
|
||||
Session.setDefault('activeViewport', false);
|
||||
Session.setDefault('leftSidebar', null);
|
||||
@ -12,9 +20,6 @@ Template.viewer.onCreated(() => {
|
||||
|
||||
instance.data.timepointApi = new TimepointApi();
|
||||
|
||||
// Attach the Window resize listener
|
||||
$(window).on('resize', handleResize);
|
||||
|
||||
ValidationErrors.remove({});
|
||||
|
||||
instance.data.state = new ReactiveDict();
|
||||
@ -23,49 +28,20 @@ Template.viewer.onCreated(() => {
|
||||
|
||||
Session.set('currentTimepointId', instance.data.currentTimepointId);
|
||||
|
||||
var contentId = instance.data.contentId;
|
||||
const contentId = instance.data.contentId;
|
||||
|
||||
OHIF.viewer.loadIndicatorDelay = 3000;
|
||||
OHIF.viewer.defaultTool = 'wwwc';
|
||||
OHIF.viewer.refLinesEnabled = true;
|
||||
OHIF.viewer.isPlaying = {};
|
||||
OHIF.viewer.cine = {
|
||||
framesPerSecond: 24,
|
||||
loop: true
|
||||
};
|
||||
|
||||
OHIF.viewer.functionList = {
|
||||
invert: function(element) {
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
viewport.invert = !viewport.invert;
|
||||
cornerstone.setViewport(element, viewport);
|
||||
},
|
||||
resetViewport: function(element) {
|
||||
cornerstone.reset(element);
|
||||
},
|
||||
playClip: function(element) {
|
||||
var viewportIndex = $('.imageViewerViewport').index(element);
|
||||
var isPlaying = OHIF.viewer.isPlaying[viewportIndex] || false;
|
||||
if (isPlaying === true) {
|
||||
cornerstoneTools.stopClip(element);
|
||||
} else {
|
||||
cornerstoneTools.playClip(element);
|
||||
}
|
||||
|
||||
OHIF.viewer.isPlaying[viewportIndex] = !OHIF.viewer.isPlaying[viewportIndex];
|
||||
Session.set('UpdateCINE', Random.id());
|
||||
},
|
||||
OHIF.viewer.functionList = $.extend(OHIF.viewer.functionList, {
|
||||
toggleLesionTrackerTools: toggleLesionTrackerTools,
|
||||
clearTools: clearTools,
|
||||
bidirectional: function() {
|
||||
bidirectional: () => {
|
||||
// Used for hotkeys
|
||||
toolManager.setActiveTool('bidirectional');
|
||||
},
|
||||
nonTarget: function() {
|
||||
nonTarget: () => {
|
||||
// Used for hotkeys
|
||||
toolManager.setActiveTool('nonTarget');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// The hotkey can also be an array (e.g. ["NUMPAD0", "0"])
|
||||
OHIF.viewer.defaultHotkeys = OHIF.viewer.defaultHotkeys || {};
|
||||
@ -117,23 +93,23 @@ Template.viewer.onCreated(() => {
|
||||
// Update the ViewerStudies collection with the loaded studies
|
||||
ViewerStudies.remove({});
|
||||
|
||||
instance.data.studies.forEach(function(study) {
|
||||
instance.data.studies.forEach(study => {
|
||||
study.selected = true;
|
||||
ViewerStudies.insert(study);
|
||||
});
|
||||
|
||||
var patientId = instance.data.studies[0].patientId;
|
||||
const patientId = instance.data.studies[0].patientId;
|
||||
Session.set('patientId', patientId);
|
||||
|
||||
instance.autorun(function() {
|
||||
var dataContext = Template.currentData();
|
||||
instance.autorun(() => {
|
||||
const dataContext = Template.currentData();
|
||||
instance.subscribe('singlePatientAssociatedStudies', dataContext.studies[0].patientId);
|
||||
instance.subscribe('singlePatientTimepoints', dataContext.studies[0].patientId);
|
||||
instance.subscribe('singlePatientMeasurements', dataContext.studies[0].patientId);
|
||||
instance.subscribe('singlePatientImageMeasurements', dataContext.studies[0].patientId);
|
||||
instance.subscribe('singlePatientAdditionalFindings', dataContext.studies[0].patientId);
|
||||
|
||||
var subscriptionsReady = instance.subscriptionsReady();
|
||||
const subscriptionsReady = instance.subscriptionsReady();
|
||||
log.info('autorun viewer.js. Ready: ' + subscriptionsReady);
|
||||
|
||||
if (subscriptionsReady) {
|
||||
@ -143,9 +119,9 @@ Template.viewer.onCreated(() => {
|
||||
TrialResponseCriteria.validateAllDelayed();
|
||||
|
||||
ViewerStudies.find().observe({
|
||||
added: function(study) {
|
||||
added: study => {
|
||||
// Find the relevant timepoint given the newly added study
|
||||
var timepoint = Timepoints.findOne({
|
||||
const timepoint = Timepoints.findOne({
|
||||
studyInstanceUids: {
|
||||
$in: [study.studyInstanceUid]
|
||||
}
|
||||
@ -166,7 +142,7 @@ Template.viewer.onCreated(() => {
|
||||
});
|
||||
|
||||
ImageMeasurements.find().observe({
|
||||
added: function(data) {
|
||||
added: data => {
|
||||
if (data.clientId === ClientId) {
|
||||
return;
|
||||
}
|
||||
@ -176,7 +152,7 @@ Template.viewer.onCreated(() => {
|
||||
// Update each displayed viewport
|
||||
updateAllViewports();
|
||||
},
|
||||
changed: function(data) {
|
||||
changed: data => {
|
||||
if (data.clientId === ClientId) {
|
||||
return;
|
||||
}
|
||||
@ -186,7 +162,7 @@ Template.viewer.onCreated(() => {
|
||||
// Update each displayed viewport
|
||||
updateAllViewports();
|
||||
},
|
||||
removed: function(data) {
|
||||
removed: data => {
|
||||
if (data.clientId === ClientId) {
|
||||
return;
|
||||
}
|
||||
@ -199,7 +175,7 @@ Template.viewer.onCreated(() => {
|
||||
});
|
||||
|
||||
Measurements.find().observe({
|
||||
added: function(data) {
|
||||
added: data => {
|
||||
if (data.clientId === ClientId) {
|
||||
TrialResponseCriteria.validateAllDelayed();
|
||||
return;
|
||||
@ -214,7 +190,7 @@ Template.viewer.onCreated(() => {
|
||||
// Update each displayed viewport
|
||||
updateAllViewports();
|
||||
},
|
||||
changed: function(data) {
|
||||
changed: data => {
|
||||
if (data.clientId === ClientId) {
|
||||
TrialResponseCriteria.validateAllDelayed();
|
||||
return;
|
||||
@ -231,7 +207,7 @@ Template.viewer.onCreated(() => {
|
||||
|
||||
TrialResponseCriteria.validateAllDelayed();
|
||||
},
|
||||
removed: function(data) {
|
||||
removed: data => {
|
||||
log.info('Measurement removed');
|
||||
|
||||
// Check that this Measurement actually contains timepoint data
|
||||
@ -241,14 +217,14 @@ Template.viewer.onCreated(() => {
|
||||
|
||||
// Get the Measurement ID and relevant tool so we can remove
|
||||
// tool data for this Measurement
|
||||
var measurementId = data._id;
|
||||
var toolType = data.toolType;
|
||||
const measurementId = data._id;
|
||||
const toolType = data.toolType;
|
||||
|
||||
// Remove the measurement from all the imageIds on which it exists
|
||||
// as toolData
|
||||
Object.keys(data.timepoints).forEach(function(timepointId) {
|
||||
Object.keys(data.timepoints).forEach(timepointId => {
|
||||
// Clear the toolData for this timepoint
|
||||
var imageId = data.timepoints[timepointId].imageId;
|
||||
const imageId = data.timepoints[timepointId].imageId;
|
||||
removeToolDataWithMeasurementId(imageId, toolType, measurementId);
|
||||
|
||||
// Set reviewer for this timepoint
|
||||
@ -264,14 +240,14 @@ Template.viewer.onCreated(() => {
|
||||
// find the Measurements, whereas on the server it's
|
||||
// only "greater than", since inside this callback the
|
||||
// Measurements have already been decremented.
|
||||
var measurements = Measurements.find({
|
||||
const measurements = Measurements.find({
|
||||
patientId: data.patientId,
|
||||
lesionNumberAbsolute: {
|
||||
$gte: data.lesionNumberAbsolute
|
||||
}
|
||||
});
|
||||
|
||||
measurements.forEach(function(measurement) {
|
||||
measurements.forEach(measurement => {
|
||||
syncMeasurementAndToolData(measurement);
|
||||
});
|
||||
|
||||
@ -289,26 +265,14 @@ Template.viewer.onCreated(() => {
|
||||
});
|
||||
});
|
||||
|
||||
Template.viewer.onRendered(function() {
|
||||
// Enable hotkeys
|
||||
enableHotkeys();
|
||||
});
|
||||
|
||||
Template.viewer.onDestroyed(function() {
|
||||
// Remove the Window resize listener
|
||||
$(window).off('resize', handleResize);
|
||||
|
||||
OHIF.viewer.updateImageSynchronizer.destroy();
|
||||
});
|
||||
|
||||
Template.viewer.events({
|
||||
'CornerstoneToolsMeasurementAdded .imageViewerViewport': function(e, template, eventData) {
|
||||
handleMeasurementAdded(e, eventData);
|
||||
'CornerstoneToolsMeasurementAdded .imageViewerViewport'(event, instance, eventData) {
|
||||
handleMeasurementAdded(event, eventData);
|
||||
},
|
||||
'CornerstoneToolsMeasurementModified .imageViewerViewport': function(e, template, eventData) {
|
||||
handleMeasurementModified(e, eventData);
|
||||
'CornerstoneToolsMeasurementModified .imageViewerViewport'(event, instance, eventData) {
|
||||
handleMeasurementModified(event, eventData);
|
||||
},
|
||||
'CornerstoneToolsMeasurementRemoved .imageViewerViewport': function(e, template, eventData) {
|
||||
handleMeasurementRemoved(e, eventData);
|
||||
'CornerstoneToolsMeasurementRemoved .imageViewerViewport'(event, instance, eventData) {
|
||||
handleMeasurementRemoved(event, eventData);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,41 +1,47 @@
|
||||
<template name="cineDialog">
|
||||
<div id="cineDialog">
|
||||
<h5>Cine Controls</h5>
|
||||
<div id="cineButtons">
|
||||
<a id="cineFirstButton" title="Skip to first image" class="cineButton" data-toggle="tooltip">
|
||||
<i class="fa fa-lg fa-fast-backward"></i>
|
||||
<div id="cineDialog" class="noselect">
|
||||
<h5>CINE Controls</h5>
|
||||
<div class="btn-group">
|
||||
<a id="cineFirstButton" title="Skip to first image" class="btn" data-toggle="tooltip">
|
||||
<i class="fa fa-fast-backward"></i>
|
||||
</a>
|
||||
<a id="cineBackButton" title="Previous image" class="cineButton" data-toggle="tooltip">
|
||||
<i class="fa fa-lg fa-step-backward"></i>
|
||||
<a id="cineBackButton" title="Previous image" class="btn" data-toggle="tooltip">
|
||||
<i class="fa fa-step-backward"></i>
|
||||
</a>
|
||||
<a id="cineSlowPlaybackButton" title="Slow playback" class="cineButton" data-toggle="tooltip">
|
||||
<i class="fa fa-lg fa-backward"></i>
|
||||
<a id="cineSlowPlaybackButton" title="Slow playback" class="btn" data-toggle="tooltip">
|
||||
<i class="fa fa-backward"></i>
|
||||
</a>
|
||||
<a id="cinePlayButton" title="Play / Pause" class="cineButton" data-toggle="tooltip">
|
||||
<a id="cinePlayButton" title="Play / Stop" class="btn {{#if isPlaying}}active{{/if}}" data-toggle="tooltip">
|
||||
{{#if isPlaying}}
|
||||
<i class="fa fa-lg fa-pause"></i>
|
||||
<i class="fa fa-fw fa-stop"></i>
|
||||
{{else}}
|
||||
<i class="fa fa-lg fa-play"></i>
|
||||
<i class="fa fa-fw fa-play"></i>
|
||||
{{/if}}
|
||||
</a>
|
||||
<a id="cineFastForwardButton" title="Increase playback speed" class="cineButton" data-toggle="tooltip">
|
||||
<i class="fa fa-lg fa-forward"></i>
|
||||
<a id="cineFastForwardButton" title="Increase playback speed" class="btn" data-toggle="tooltip">
|
||||
<i class="fa fa-forward"></i>
|
||||
</a>
|
||||
<a id="cineNextButton" title="Next image" class="cineButton" data-toggle="tooltip">
|
||||
<i class="fa fa-lg fa-step-forward"></i>
|
||||
<a id="cineNextButton" title="Next image" class="btn" data-toggle="tooltip">
|
||||
<i class="fa fa-step-forward"></i>
|
||||
</a>
|
||||
<a id="cineLastButton" title="Skip to last image" class="cineButton" data-toggle="tooltip">
|
||||
<i class="fa fa-lg fa-fast-forward"></i>
|
||||
<a id="cineLastButton" title="Skip to last image" class="btn" data-toggle="tooltip">
|
||||
<i class="fa fa-fast-forward"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div id="cineOptions">
|
||||
<div id="loopSection">
|
||||
<label>Loop?</label>
|
||||
<label for="cineLoopCheckbox">Loop?</label>
|
||||
<input type="checkbox" checked id="cineLoopCheckbox" />
|
||||
</div>
|
||||
<div id="fpsSection">
|
||||
<label>Cine Speed: <span id="fps">{{framerate}}</span></label>
|
||||
<input type="range" id="cineSlider" min="1" max="90" value="{{framerate}}"/>
|
||||
<label for="cineSlider">
|
||||
Cine Speed: <span id="fps">{{framerate}}</span>
|
||||
</label>
|
||||
<input type="range"
|
||||
id="cineSlider"
|
||||
min="1"
|
||||
max="90"
|
||||
value="{{framerate}}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,16 +1,5 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
toggleCinePlay = function() {
|
||||
var element = getActiveViewportElement();
|
||||
var playClipToolData = cornerstoneTools.getToolState(element, 'playClip');
|
||||
|
||||
if (isPlaying()) {
|
||||
cornerstoneTools.stopClip(element);
|
||||
} else {
|
||||
cornerstoneTools.playClip(element);
|
||||
}
|
||||
};
|
||||
|
||||
function updateFramerate(rate) {
|
||||
OHIF.viewer.cine.framesPerSecond = rate;
|
||||
|
||||
@ -74,6 +63,7 @@ Template.cineDialog.events({
|
||||
});
|
||||
|
||||
Template.cineDialog.onRendered(function() {
|
||||
var dialog = $('#cineDialog');
|
||||
const instance = Template.instance();
|
||||
const dialog = instance.$('#cineDialog');
|
||||
dialog.draggable();
|
||||
});
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
@import "{design}/app.styl"
|
||||
|
||||
$borderColor = rgba(77, 99, 110, 0.81)
|
||||
|
||||
#cineDialog
|
||||
display: none
|
||||
z-index: 1000
|
||||
@ -9,32 +13,29 @@
|
||||
|
||||
overflow: hidden
|
||||
padding: 10px
|
||||
|
||||
width: 250px
|
||||
width: 300px
|
||||
height: 175px
|
||||
|
||||
border: 1px black solid
|
||||
border-radius: 3px
|
||||
background: white
|
||||
color: black
|
||||
|
||||
opacity: 0.95
|
||||
border-radius: 8px
|
||||
border: 1px solid $borderColor;
|
||||
background: $uiGrayDarkest
|
||||
color: $textSecondaryColor
|
||||
|
||||
h5, label
|
||||
font-weight: 100
|
||||
|
||||
box-sizing: border-box
|
||||
|
||||
-webkit-user-select: none
|
||||
-moz-user-select: none
|
||||
-o-user-select: none
|
||||
-ms-user-select: none
|
||||
-khtml-user-select: none
|
||||
user-select: none
|
||||
|
||||
#cineButtons
|
||||
padding-bottom: 10px
|
||||
overflow: auto
|
||||
.btn
|
||||
background: $uiGrayDarkest
|
||||
color: $textSecondaryColor
|
||||
|
||||
.cineButton
|
||||
cursor: pointer
|
||||
margin: 0 6px
|
||||
text-decoration: none
|
||||
&:hover
|
||||
color: $hoverColor
|
||||
|
||||
&:active, &.active
|
||||
color: $activeColor
|
||||
|
||||
#cineOptions
|
||||
padding: 5px 0
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
<template name="layoutButton">
|
||||
<div id="{{this.id}}"
|
||||
class="toolbarSectionButton rp-x-1 {{this.classes}} {{activeClass}} js-dropdown-toggle"
|
||||
title="{{this.title}}"
|
||||
data-target="#layoutChooser"
|
||||
>
|
||||
<div class="svgContainer">
|
||||
{{ #if this.svgLink }}
|
||||
<svg>
|
||||
<use xlink:href={{this.svgLink}}></use>
|
||||
</svg>
|
||||
{{ else }}
|
||||
<i class={{iconClasses}}></i>
|
||||
{{ /if }}
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
{{this.title}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,16 @@
|
||||
Template.layoutButton.events({
|
||||
// TODO: Check why 'click' event won't fire?
|
||||
'mousedown .js-dropdown-toggle'(event) {
|
||||
// Select the button and it's target dropdown menu
|
||||
const $button = $(event.currentTarget);
|
||||
const $dropdown = $($button.data('target'));
|
||||
|
||||
// Adjust the dropdown's CSS to properly place it on the page
|
||||
$dropdown.css({
|
||||
top: $button.offset().top + $button.outerHeight() + 'px',
|
||||
left: $button.offset().left + 'px'
|
||||
});
|
||||
|
||||
toggleDialog($dropdown);
|
||||
}
|
||||
});
|
||||
@ -1,5 +1,5 @@
|
||||
<template name="layoutChooser">
|
||||
<div class="layoutChooser pull-left dropdown-menu" role="menu">
|
||||
<div id="layoutChooser" class="layoutChooser pull-left dropdown-menu" role="menu">
|
||||
<table>
|
||||
<tr>
|
||||
<td></td>
|
||||
|
||||
@ -6,39 +6,41 @@
|
||||
* @param currentCell
|
||||
*/
|
||||
function highlightCells(currentCell) {
|
||||
var cells = $('.layoutChooser table td');
|
||||
const cells = $('.layoutChooser table td');
|
||||
cells.removeClass('hover');
|
||||
|
||||
currentCell = $(currentCell);
|
||||
var table = currentCell.parents('.layoutChooser table').get(0);
|
||||
var rowIndex = currentCell.closest('tr').index();
|
||||
var columnIndex = currentCell.index();
|
||||
const table = currentCell.parents('.layoutChooser table').get(0);
|
||||
const rowIndex = currentCell.closest('tr').index();
|
||||
const columnIndex = currentCell.index();
|
||||
|
||||
// Loop through the table row by row
|
||||
// and cell by cell to apply the highlighting
|
||||
for (var i = 0; i < table.rows.length; i++) {
|
||||
row = table.rows[i];
|
||||
const row = table.rows[i];
|
||||
if (i <= rowIndex) {
|
||||
for (var j = 0; j < row.cells.length; j++) {
|
||||
for (var j = 0; j < row.cells.length; j++) {
|
||||
if (j <= columnIndex) {
|
||||
cell = row.cells[j];
|
||||
const cell = row.cells[j];
|
||||
cell.classList.add('hover');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Template.layoutChooser.events({
|
||||
'touchstart .layoutChooser table td, mouseenter .layoutChooser table td': function(evt) {
|
||||
'touchstart .layoutChooser table td, mouseenter .layoutChooser table td'(evt) {
|
||||
highlightCells(evt.currentTarget);
|
||||
},
|
||||
'click .layoutChooser table td': function(evt) {
|
||||
var currentCell = $(evt.currentTarget);
|
||||
var rowIndex = currentCell.closest('tr').index();
|
||||
var columnIndex = currentCell.index();
|
||||
|
||||
'click .layoutChooser table td'(evt) {
|
||||
const currentCell = $(evt.currentTarget);
|
||||
const rowIndex = currentCell.closest('tr').index();
|
||||
const columnIndex = currentCell.index();
|
||||
|
||||
// Add 1 because the indices start from zero
|
||||
var layoutProps = {
|
||||
const layoutProps = {
|
||||
rows: rowIndex + 1,
|
||||
columns: columnIndex + 1
|
||||
};
|
||||
@ -46,5 +48,8 @@ Template.layoutChooser.events({
|
||||
layoutManager.layoutTemplateName = 'gridLayout';
|
||||
layoutManager.layoutProps = layoutProps;
|
||||
layoutManager.updateViewports();
|
||||
|
||||
const $dropdown = $('.layoutChooser');
|
||||
toggleDialog($dropdown);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,19 +1,28 @@
|
||||
@import "{design}/app.styl"
|
||||
|
||||
$borderColor = rgba(77, 99, 110, 0.81)
|
||||
|
||||
.layoutChooser
|
||||
width: 92px
|
||||
height: 92px
|
||||
min-width: 92px // to override bootstrap's dropdown-menu class
|
||||
min-height: 92px
|
||||
background-color: $primaryBackgroundColor
|
||||
border: 2px solid $uiBorderColorDark
|
||||
|
||||
|
||||
//background-color: $primaryBackgroundColor
|
||||
opacity: 0.95
|
||||
border-radius: 8px
|
||||
border: 1px solid $borderColor;
|
||||
background: $uiGrayDarkest
|
||||
|
||||
&.open
|
||||
display: block
|
||||
|
||||
table
|
||||
margin: 0 auto
|
||||
|
||||
td
|
||||
cursor: pointer
|
||||
border: 2px solid $uiBorderColorDark
|
||||
border: 1px solid $uiBorderColorDark
|
||||
width: 20px
|
||||
height: 20px
|
||||
transition(background-color 0.1s ease)
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
p, h4
|
||||
text-align: center
|
||||
color: $textColorActive
|
||||
padding: 4px 0px
|
||||
padding: 4px 0
|
||||
|
||||
p
|
||||
font-size: 11pt
|
||||
|
||||
@ -1,24 +1,20 @@
|
||||
<template name="playClipButton">
|
||||
<button id="playClip"
|
||||
type="button"
|
||||
class="imageViewerCommand btn btn-sm {{ #if isPlaying }}active{{/if}}"
|
||||
data-container="body"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
title="Play/Stop Clip">
|
||||
<div id="{{this.id}}"
|
||||
class="toolbarSectionButton rp-x-1 {{this.classes}} {{activeClass}} {{ #if isPlaying }}active{{/if}}"
|
||||
title="{{this.title}}">
|
||||
<div class="svgContainer">
|
||||
{{ #if isPlaying }}
|
||||
<span class="fa fa-stop"></span>
|
||||
<i class="fa fa-stop fa-fw"></i>
|
||||
{{ else }}
|
||||
<span class="fa fa-play"></span>
|
||||
<i class="fa fa-play fa-fw"></i>
|
||||
{{ /if }}
|
||||
</button>
|
||||
<button id="toggleCineDialog"
|
||||
type="button"
|
||||
class="imageViewerCommand btn btn-sm"
|
||||
data-container="body"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
title="Toggle CINE Dialog">
|
||||
<span class="fa fa-youtube-play"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
{{ #if isPlaying }}
|
||||
Stop
|
||||
{{ else }}
|
||||
Play
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,54 +1,5 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
toggleCinePlay = function(element) {
|
||||
var viewports = $('.imageViewerViewport');
|
||||
|
||||
if (!element) {
|
||||
var viewportIndex = Session.get('activeViewport');
|
||||
element = viewports.get(viewportIndex);
|
||||
}
|
||||
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isPlaying = OHIF.viewer.isPlaying[viewportIndex] || false;
|
||||
if (isPlaying) {
|
||||
cornerstoneTools.stopClip(element);
|
||||
} else {
|
||||
cornerstoneTools.playClip(element);
|
||||
}
|
||||
|
||||
OHIF.viewer.isPlaying[viewportIndex] = !OHIF.viewer.isPlaying[viewportIndex];
|
||||
Session.set('UpdateCINE', Random.id());
|
||||
};
|
||||
|
||||
isPlaying = function() {
|
||||
Session.get('UpdateCINE');
|
||||
var activeViewport = Session.get('activeViewport');
|
||||
|
||||
// TODO=Check best way to make sure this is always defined
|
||||
// Right now it is initialized in enableHotkeys AND in
|
||||
// imageViewer onCreated, but this appears to break some things
|
||||
if (!OHIF.viewer.isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
return !!OHIF.viewer.isPlaying[activeViewport];
|
||||
};
|
||||
|
||||
Template.playClipButton.helpers({
|
||||
isPlaying: function() {
|
||||
return isPlaying();
|
||||
}
|
||||
});
|
||||
|
||||
Template.playClipButton.events({
|
||||
'click #playClip': function() {
|
||||
toggleCinePlay();
|
||||
},
|
||||
'click #toggleCineDialog': function() {
|
||||
var cineDialog = document.getElementById('cineDialog');
|
||||
toggleDialog(cineDialog);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
<template name="simpleToolbarButton">
|
||||
<button id="{{id}}"
|
||||
type="button"
|
||||
class="{{classes}} btn btn-sm {{activeClass}}"
|
||||
data-container="body"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
title="{{title}}">
|
||||
<span class="{{iconClasses}}"></span>
|
||||
</button>
|
||||
</template>
|
||||
@ -1,13 +0,0 @@
|
||||
Template.simpleToolbarButton.helpers({
|
||||
activeClass() {
|
||||
const instance = Template.instance();
|
||||
|
||||
// Check if the current tool is the active one
|
||||
if (instance.data.id === Session.get('ToolManagerActiveTool')) {
|
||||
// Return the active class
|
||||
return 'active';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
});
|
||||
@ -1,37 +0,0 @@
|
||||
<template name="toolbar">
|
||||
<div class="btn-group">
|
||||
{{ #each button }}
|
||||
{{ >simpleToolbarButton}}
|
||||
{{ /each }}
|
||||
|
||||
{{ #if includePlayClipButton }}
|
||||
{{ >playClipButton }}
|
||||
{{ /if }}
|
||||
|
||||
{{ #if includeLayoutButton }}
|
||||
<div class="btn-group">
|
||||
<button id="layout"
|
||||
type="button"
|
||||
class="btn btn-sm dropdown-toggle"
|
||||
data-container="body"
|
||||
data-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
data-placement="right"
|
||||
title="Layout"
|
||||
rel="tooltip">
|
||||
<span class="fa fa-th-large"></span>
|
||||
</button>
|
||||
{{ >layoutChooser }}
|
||||
</div>
|
||||
{{ /if }}
|
||||
|
||||
{{ #if includeHangingProtocolButtons }}
|
||||
{{ >hangingProtocolButtons }}
|
||||
{{ >matchedProtocols }}
|
||||
{{ /if }}
|
||||
|
||||
{{ #each btnGroup }}
|
||||
{{ >toolbarGroupButton}}
|
||||
{{ /each }}
|
||||
</div>
|
||||
</template>
|
||||
@ -1,205 +0,0 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
function getDefaultButtonData() {
|
||||
var buttonData = [];
|
||||
|
||||
buttonData.push({
|
||||
id: 'wwwc',
|
||||
title: 'WW/WC',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-sun-o'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'wwwcRegion',
|
||||
title: 'Window by Region',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-square'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'magnify',
|
||||
title: 'Magnify',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-circle'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'annotate',
|
||||
title: 'Annotation',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-arrows-h'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'invert',
|
||||
title: 'Invert',
|
||||
classes: 'imageViewerCommand',
|
||||
iconClasses: 'fa fa-adjust'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'zoom',
|
||||
title: 'Zoom',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-search'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'pan',
|
||||
title: 'Pan',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-arrows'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'stackScroll',
|
||||
title: 'Stack Scroll',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-bars'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'length',
|
||||
title: 'Length Measurement',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-arrows-v'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'angle',
|
||||
title: 'Angle Measurement',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-angle-left'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'dragProbe',
|
||||
title: 'Pixel Probe',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-dot-circle-o'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'ellipticalRoi',
|
||||
title: 'Elliptical ROI',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-circle-o'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'rectangleRoi',
|
||||
title: 'Rectangle ROI',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-square-o'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'resetViewport',
|
||||
title: 'Reset Viewport',
|
||||
classes: 'imageViewerCommand',
|
||||
iconClasses: 'fa fa-undo'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'clearTools',
|
||||
title: 'Clear tools',
|
||||
classes: 'imageViewerCommand',
|
||||
iconClasses: 'fa fa-trash'
|
||||
});
|
||||
return buttonData;
|
||||
}
|
||||
|
||||
Template.toolbar.events({
|
||||
'click .imageViewerTool': function(e) {
|
||||
$(e.currentTarget).tooltip('hide');
|
||||
|
||||
var tool = e.currentTarget.id;
|
||||
|
||||
var elements = $('.imageViewerViewport');
|
||||
|
||||
var activeTool = toolManager.getActiveTool();
|
||||
if (tool === activeTool) {
|
||||
var defaultTool = toolManager.getDefaultTool();
|
||||
console.log('Setting active tool to: ' + defaultTool);
|
||||
toolManager.setActiveTool(defaultTool, elements);
|
||||
} else {
|
||||
console.log('Setting active tool to: ' + tool);
|
||||
toolManager.setActiveTool(tool, elements);
|
||||
}
|
||||
},
|
||||
'click .imageViewerCommand': function(e) {
|
||||
$(e.currentTarget).tooltip('hide');
|
||||
|
||||
var command = e.currentTarget.id;
|
||||
if (!OHIF.viewer.functionList.hasOwnProperty(command)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var activeViewport = Session.get('activeViewport');
|
||||
var element = $('.imageViewerViewport').get(activeViewport);
|
||||
OHIF.viewer.functionList[command](element);
|
||||
}
|
||||
});
|
||||
|
||||
Template.toolbar.onRendered(function() {
|
||||
var tooltipButtons = $('[data-toggle="tooltip"]');
|
||||
tooltipButtons.tooltip(OHIF.viewer.tooltipConfig);
|
||||
|
||||
// Enable tooltips for the layout button
|
||||
var extraTooltipButtons = $('[rel="tooltip"]');
|
||||
extraTooltipButtons.tooltip(OHIF.viewer.tooltipConfig);
|
||||
|
||||
// Set disabled/enabled tool buttons that are set in toolManager
|
||||
var states = toolManager.getToolDefaultStates();
|
||||
var disabledToolButtons = states.disabledToolButtons;
|
||||
var allToolbarButtons = $('#toolbar').find('button');
|
||||
if (disabledToolButtons && disabledToolButtons.length > 0) {
|
||||
for (var i = 0; i < allToolbarButtons.length; i++) {
|
||||
var toolbarButton = allToolbarButtons[i];
|
||||
$(toolbarButton).prop('disabled', false);
|
||||
var index = disabledToolButtons.indexOf($(toolbarButton).attr('id'));
|
||||
if (index !== -1) {
|
||||
$(toolbarButton).prop('disabled', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Template.toolbar.helpers({
|
||||
button: function() {
|
||||
if (this.toolbarOptions && this.toolbarOptions.buttonData) {
|
||||
return this.toolbarOptions.buttonData;
|
||||
}
|
||||
|
||||
return getDefaultButtonData();
|
||||
},
|
||||
includePlayClipButton: function() {
|
||||
if (this.toolbarOptions && this.toolbarOptions.includePlayClipButton !== undefined) {
|
||||
return this.toolbarOptions.includePlayClipButton;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
includeLayoutButton: function() {
|
||||
if (this.toolbarOptions && this.toolbarOptions.includeLayoutButton !== undefined) {
|
||||
return this.toolbarOptions.includeLayoutButton;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
includeHangingProtocolButtons: function() {
|
||||
if (this.toolbarOptions && this.toolbarOptions.includeHangingProtocolButtons !== undefined) {
|
||||
return this.toolbarOptions.includeHangingProtocolButtons;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
btnGroup: function() {
|
||||
if (this.toolbarOptions && this.toolbarOptions.btnGroup !== undefined) {
|
||||
return this.toolbarOptions.btnGroup;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
});
|
||||
@ -1,21 +0,0 @@
|
||||
@import "{design}/app.styl"
|
||||
|
||||
.btn-group
|
||||
background-color: $primaryBackgroundColor
|
||||
display: inline-block
|
||||
|
||||
button
|
||||
text-align: center
|
||||
color: $defaultColor
|
||||
background-color: $primaryBackgroundColor
|
||||
border-color: $uiBorderColor
|
||||
transition(all 0.1s ease)
|
||||
|
||||
// Prevent the blue outline in Chrome when a viewport is selected
|
||||
outline: 0 !important
|
||||
|
||||
&:hover
|
||||
color: $hoverColor
|
||||
|
||||
&.active, &:active
|
||||
color: $activeColor
|
||||
@ -1,7 +1,12 @@
|
||||
<template name="toolbarSectionButton">
|
||||
<div id="{{this.id}}" class="toolbarSectionButton rp-x-1 {{this.classes}} {{activeClass}}" title="{{this.title}}">
|
||||
{{ #if this.buttonTemplateName }}
|
||||
{{> UI.dynamic template=this.buttonTemplateName data=this }}
|
||||
{{ else }}
|
||||
<div id="{{this.id}}"
|
||||
class="toolbarSectionButton rp-x-1 {{this.classes}} {{activeClass}}"
|
||||
title="{{this.title}}">
|
||||
<div class="svgContainer">
|
||||
{{ #if this.svgLink }}
|
||||
{{ #if this.svgLink }}
|
||||
<svg>
|
||||
<use xlink:href={{this.svgLink}}></use>
|
||||
</svg>
|
||||
@ -13,4 +18,5 @@
|
||||
{{this.title}}
|
||||
</div>
|
||||
</div>
|
||||
{{ /if }}
|
||||
</template>
|
||||
|
||||
@ -30,7 +30,7 @@ Template.toolbarSectionButton.events({
|
||||
},
|
||||
'click .imageViewerCommand'(event, instance) {
|
||||
const command = event.currentTarget.id;
|
||||
if (!OHIF.viewer.functionList.hasOwnProperty(command)) {
|
||||
if (!OHIF.viewer.functionList || !OHIF.viewer.functionList.hasOwnProperty(command)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
{{>toolbarSectionButton toolbarButton}}
|
||||
{{/each}}
|
||||
|
||||
{{ #if extraToolbarButtons}}
|
||||
<div id="moreTools" class="js-open-more-tools toolbarSectionButton rp-x-1 rm-l-3">
|
||||
<div class="svgContainer">
|
||||
<svg>
|
||||
@ -24,5 +25,6 @@
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{ /if }}
|
||||
</div>
|
||||
</template>
|
||||
@ -1,10 +1,26 @@
|
||||
Template.viewerMain.onRendered(function() {
|
||||
var instance = this;
|
||||
var studies = instance.data.studies;
|
||||
Template.viewerMain.onCreated(() => {
|
||||
// Attach the Window resize listener
|
||||
$(window).on('resize', handleResize);
|
||||
});
|
||||
|
||||
var parentElement = instance.$("#layoutManagerTarget").get(0);
|
||||
Template.viewerMain.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
const studies = instance.data.studies;
|
||||
const parentElement = instance.$("#layoutManagerTarget").get(0);
|
||||
window.layoutManager = new LayoutManager(parentElement, studies);
|
||||
|
||||
ProtocolEngine = new HP.ProtocolEngine(window.layoutManager, studies);
|
||||
HP.setEngine(ProtocolEngine);
|
||||
|
||||
// Enable hotkeys
|
||||
enableHotkeys();
|
||||
});
|
||||
|
||||
Template.viewerMain.onDestroyed(() => {
|
||||
// Remove the Window resize listener
|
||||
$(window).off('resize', handleResize);
|
||||
|
||||
// Destory the synchronizer used to update reference lines
|
||||
OHIF.viewer.updateImageSynchronizer.destroy();
|
||||
});
|
||||
@ -5,8 +5,8 @@
|
||||
* @param {Object} study The study instance
|
||||
*/
|
||||
sortStudy = function(study) {
|
||||
if (!study) {
|
||||
return;
|
||||
if (!study || !study.seriesList) {
|
||||
throw "Insufficient study data was provided to sortStudy";
|
||||
}
|
||||
|
||||
study.seriesList.sort(function(a, b) {
|
||||
|
||||
@ -4,65 +4,120 @@ getActiveViewportElement = function() {
|
||||
};
|
||||
|
||||
zoomIn = function() {
|
||||
var element = getActiveViewportElement();
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
viewport.scale += 0.15;
|
||||
if (viewport.scale > 10.0) {
|
||||
viewport.scale = 10.0;
|
||||
}
|
||||
const element = getActiveViewportElement();
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
const scaleIncrement = 0.15;
|
||||
const maximumScale = 10;
|
||||
viewport.scale = Math.min(viewport.scale + increment, maximumScale);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
};
|
||||
|
||||
zoomOut = function() {
|
||||
var element = getActiveViewportElement();
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
viewport.scale -= 0.15;
|
||||
if (viewport.scale < 0.05) {
|
||||
viewport.scale = 0.05;
|
||||
}
|
||||
const element = getActiveViewportElement();
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
const scaleIncrement = 0.15;
|
||||
const minimumScale = 0.05;
|
||||
viewport.scale = Math.max(viewport.scale - increment, minimumScale);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
};
|
||||
|
||||
zoomToFit = function() {
|
||||
var element = getActiveViewportElement();
|
||||
const element = getActiveViewportElement();
|
||||
cornerstone.fitToWindow(element);
|
||||
};
|
||||
|
||||
rotateL = function() {
|
||||
var element = getActiveViewportElement();
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
const element = getActiveViewportElement();
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.rotation -= 90;
|
||||
cornerstone.setViewport(element, viewport);
|
||||
updateOrientationMarkers(element, viewport);
|
||||
};
|
||||
|
||||
rotateR = function() {
|
||||
var element = getActiveViewportElement();
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
const element = getActiveViewportElement();
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.rotation += 90;
|
||||
cornerstone.setViewport(element, viewport);
|
||||
updateOrientationMarkers(element, viewport);
|
||||
};
|
||||
|
||||
invert = function() {
|
||||
var element = getActiveViewportElement();
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
const element = getActiveViewportElement();
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.invert = (viewport.invert === false);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
};
|
||||
|
||||
flipV = function() {
|
||||
var element = getActiveViewportElement();
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
const element = getActiveViewportElement();
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.vflip = (viewport.vflip === false);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
updateOrientationMarkers(element, viewport);
|
||||
};
|
||||
|
||||
flipH = function() {
|
||||
var element = getActiveViewportElement();
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
const element = getActiveViewportElement();
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.hflip = (viewport.hflip === false);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
updateOrientationMarkers(element, viewport);
|
||||
};
|
||||
|
||||
resetViewport = function() {
|
||||
const element = getActiveViewportElement();
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
if (enabledElement.fitToWindow === false) {
|
||||
const imageId = enabledElement.image.imageId;
|
||||
const instance = cornerstoneTools.metaData.get('instance', imageId);
|
||||
|
||||
enabledElement.viewport = cornerstone.getDefaultViewport(enabledElement.canvas, enabledElement.image);
|
||||
|
||||
const instanceClassDefaultViewport = getInstanceClassDefaultViewport(instance, enabledElement, imageId);
|
||||
cornerstone.setViewport(element, instanceClassDefaultViewport);
|
||||
} else {
|
||||
cornerstone.reset(element);
|
||||
}
|
||||
};
|
||||
|
||||
clearTools = function() {
|
||||
const element = getActiveViewportElement();
|
||||
const toolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager;
|
||||
toolStateManager.clear(element);
|
||||
cornerstone.updateImage(element);
|
||||
};
|
||||
|
||||
toggleCinePlay = function() {
|
||||
const element = getActiveViewportElement();
|
||||
var viewportIndex = Session.get('activeViewport');
|
||||
|
||||
var isPlaying = OHIF.viewer.isPlaying[viewportIndex] || false;
|
||||
if (isPlaying) {
|
||||
cornerstoneTools.stopClip(element);
|
||||
} else {
|
||||
cornerstoneTools.playClip(element);
|
||||
}
|
||||
|
||||
OHIF.viewer.isPlaying[viewportIndex] = !OHIF.viewer.isPlaying[viewportIndex];
|
||||
Session.set('UpdateCINE', Random.id());
|
||||
};
|
||||
|
||||
toggleCineDialog = function() {
|
||||
var cineDialog = document.getElementById('cineDialog');
|
||||
toggleDialog(cineDialog);
|
||||
}
|
||||
|
||||
isPlaying = function() {
|
||||
Session.get('UpdateCINE');
|
||||
var activeViewport = Session.get('activeViewport');
|
||||
|
||||
// TODO=Check best way to make sure this is always defined
|
||||
// Right now it is initialized in enableHotkeys AND in
|
||||
// imageViewer onCreated, but this appears to break some things
|
||||
if (!OHIF.viewer.isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
return !!OHIF.viewer.isPlaying[activeViewport];
|
||||
};
|
||||
@ -90,6 +90,9 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/components/viewer/imageControls/imageControls.js', 'client');
|
||||
api.addFiles('client/components/viewer/imageControls/imageControls.styl', 'client');
|
||||
|
||||
api.addFiles('client/components/viewer/layoutButton/layoutButton.html', 'client');
|
||||
api.addFiles('client/components/viewer/layoutButton/layoutButton.js', 'client');
|
||||
|
||||
api.addFiles('client/components/viewer/layoutChooser/layoutChooser.html', 'client');
|
||||
api.addFiles('client/components/viewer/layoutChooser/layoutChooser.js', 'client');
|
||||
api.addFiles('client/components/viewer/layoutChooser/layoutChooser.styl', 'client');
|
||||
@ -106,16 +109,9 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/components/viewer/toolbarSectionTools/toolbarSectionTools.js', 'client');
|
||||
api.addFiles('client/components/viewer/toolbarSectionTools/toolbarSectionTools.styl', 'client');
|
||||
|
||||
api.addFiles('client/components/viewer/simpleToolbarButton/simpleToolbarButton.html', 'client');
|
||||
api.addFiles('client/components/viewer/simpleToolbarButton/simpleToolbarButton.js', 'client');
|
||||
|
||||
api.addFiles('client/components/viewer/playClipButton/playClipButton.html', 'client');
|
||||
api.addFiles('client/components/viewer/playClipButton/playClipButton.js', 'client');
|
||||
|
||||
api.addFiles('client/components/viewer/toolbar/toolbar.html', 'client');
|
||||
api.addFiles('client/components/viewer/toolbar/toolbar.js', 'client');
|
||||
api.addFiles('client/components/viewer/toolbar/toolbar.styl', 'client');
|
||||
|
||||
// Library functions
|
||||
api.addFiles('lib/layoutManager.js', 'client');
|
||||
api.addFiles('lib/createStacks.js', 'client');
|
||||
@ -204,6 +200,14 @@ Package.onUse(function(api) {
|
||||
api.addFiles('lib/helpers/isTouchDevice.js', 'client');
|
||||
api.export('isTouchDevice', 'client');
|
||||
|
||||
// TODO: Clean this up later, no real need to export these
|
||||
// Need to move functionList into viewerbase
|
||||
api.export('toggleCineDialog', 'client');
|
||||
api.export('toggleCinePlay', 'client');
|
||||
api.export('clearTools', 'client');
|
||||
api.export('resetViewport', 'client');
|
||||
api.export('invert', 'client');
|
||||
|
||||
api.addFiles('server/methods/importStudies.js', 'server');
|
||||
|
||||
api.export('importStudies', 'server');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user