Add some WIP changes to Lesion Tracker dialogs

This commit is contained in:
Erik Ziegler 2015-12-07 17:15:14 +01:00
parent e4c24608af
commit da197368bf
37 changed files with 915 additions and 595 deletions

View File

@ -51,12 +51,6 @@ Template.viewer.onCreated(function() {
} }
OHIF.viewer.isPlaying[viewportIndex] = !OHIF.viewer.isPlaying[viewportIndex]; OHIF.viewer.isPlaying[viewportIndex] = !OHIF.viewer.isPlaying[viewportIndex];
Session.set('UpdateCINE', Random.id()); Session.set('UpdateCINE', Random.id());
},
previousPresentationGroup: function() {
WindowManager.previousPresentationGroup();
},
nextPresentationGroup: function() {
WindowManager.nextPresentationGroup();
} }
}; };
@ -103,7 +97,6 @@ Template.viewer.onCreated(function() {
var patientId = this.data.studies[0].patientId; var patientId = this.data.studies[0].patientId;
Session.set('patientId', patientId); Session.set('patientId', patientId);
initialized = false;
self.autorun(function() { self.autorun(function() {
var patientId = Session.get('patientId'); var patientId = Session.get('patientId');
self.subscribe('timepoints', patientId); self.subscribe('timepoints', patientId);
@ -113,38 +106,35 @@ Template.viewer.onCreated(function() {
return; return;
} }
if (initialized === true) { ViewerStudies.find().observe({
return; added: function(study) {
} var timepoint = Timepoints.findOne({timepointName: study.studyDate});
if (timepoint) {
log.warn("A timepoint with that study date already exists!");
return;
}
ViewerStudies.find().forEach(function(study) { var timepointID = uuid.v4();
var timepoint = Timepoints.findOne({timepointName: study.studyDate});
if (timepoint) { var testTimepoint = Timepoints.findOne({});
log.warn("A timepoint with that study date already exists!"); if (testTimepoint && testTimepoint.patientId !== study.patientId) {
return; log.warn("Timepoints collection related to the wrong subject");
return;
}
log.info('Inserting a new timepoint');
Timepoints.insert({
patientId: study.patientId,
timepointID: timepointID,
timepointName: study.studyDate
});
} }
var timepointID = uuid.v4();
var testTimepoint = Timepoints.findOne({});
if (testTimepoint && testTimepoint.patientId !== study.patientId) {
log.warn("Timepoints collection related to the wrong subject");
return;
}
log.info('Inserting a new timepoint');
Timepoints.insert({
patientId: study.patientId,
timepointID: timepointID,
timepointName: study.studyDate
});
}); });
// This is used to re-add tools from the database into the // This is used to re-add tools from the database into the
// Cornerstone ToolData structure // Cornerstone ToolData structure
Measurements.find().observe({ Measurements.find().observe({
added: function (data) { added: function (data) {
if (data.toolDataInsertedManually === true) { if (data.toolDataInsertedManually === true) {
// Activate first measurements in image box as default if exists // Activate first measurements in image box as default if exists
@ -156,19 +146,40 @@ Template.viewer.onCreated(function() {
} }
return; return;
} }
log.info('Measurement added'); log.info('Measurement added');
addMeasurementAsToolData(data); addMeasurementAsToolData(data);
updateRelatedElements(data.imageId);
},
changed: function(data) {
log.info('Measurement changed');
updateRelatedElements(data.imageId);
},
removed: function(data) {
log.info('Measurement removed');
updateRelatedElements(data.imageId);
} }
}); });
initialized = true;
}); });
OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer("CornerstoneNewImage", cornerstoneTools.updateImageSynchronizer); OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer("CornerstoneNewImage", cornerstoneTools.updateImageSynchronizer);
}); });
function updateRelatedElements(imageId) {
// Get all on-screen elements with this imageId
var enabledElements = cornerstone.getEnabledElementsByImageId(imageId);
// TODO=Check original event to prevent duplicate updateImage calls
// Loop through these elements
enabledElements.forEach(function(enabledElement) {
// Update the display so the tool is removed
var element = enabledElement.element;
cornerstone.updateImage(element)
});
}
function addMeasurementAsToolData(data) { function addMeasurementAsToolData(data) {
// Check what toolType we should be adding this to, based on the isTarget value // Check what toolType we should be adding this to, based on the isTarget value
// of the stored Measurement // of the stored Measurement
@ -184,10 +195,41 @@ function addMeasurementAsToolData(data) {
toolState[imageId] = {}; toolState[imageId] = {};
} }
// This is probably not the best approach to prevent duplicates
if (toolState[imageId][toolType] && toolState[imageId][toolType].data) {
var measurementHasNoIdYet = false;
toolState[imageId][toolType].data.forEach(function(measurement) {
if (measurement.id === 'notready') {
measurementHasNoIdYet = true;
return false;
}
});
// Stop here if it appears that we are creating this measurement right now,
// and would not like this function to add another copy of it to the toolData
if (measurementHasNoIdYet === true) {
return;
}
}
if (!toolState[imageId][toolType]) { if (!toolState[imageId][toolType]) {
toolState[imageId][toolType] = { toolState[imageId][toolType] = {
data: [] data: []
}; };
} else {
var alreadyExists = false;
if (toolState[imageId][toolType].data.length) {
toolState[imageId][toolType].data.forEach(function(measurement) {
if (measurement.id === data._id) {
alreadyExists = true;
return false;
}
});
}
if (alreadyExists === true) {
return;
}
} }
// Create measurementData structure based on the lesion data at this timepoint // Create measurementData structure based on the lesion data at this timepoint
@ -204,6 +246,7 @@ function addMeasurementAsToolData(data) {
measurementData.visible = data.visible; measurementData.visible = data.visible;
measurementData.active = data.active; measurementData.active = data.active;
measurementData.uid = data.uid; measurementData.uid = data.uid;
measurementData.id = data._id;
toolState[imageId][toolType].data.push(measurementData); toolState[imageId][toolType].data.push(measurementData);
}); });
@ -215,6 +258,46 @@ Template.viewer.onDestroyed(function() {
OHIF.viewer.updateImageSynchronizer.destroy(); OHIF.viewer.updateImageSynchronizer.destroy();
}); });
Template.viewer.events({
'CornerstoneToolsMeasurementModified .imageViewerViewport': function(e, template, eventData) {
handleMeasurementModified(e, eventData);
},
'CornerstoneToolsMeasurementRemoved .imageViewerViewport': function(e, template, eventData) {
handleMeasurementRemoved(e, eventData);
}
});
function handleMeasurementModified(e, eventData) {
log.info('CornerstoneToolsMeasurementModified');
var measurementData = eventData.measurementData;
switch (eventData.toolType) {
case 'nonTarget':
case 'lesion':
measurementManagerDAL.updateTimepointData(measurementData);
break;
}
}
function handleMeasurementRemoved(e, eventData) {
log.info('CornerstoneToolsMeasurementRemoved');
var measurementData = eventData.measurementData;
var databaseEntry;
switch (eventData.toolType) {
case 'nonTarget':
case 'lesion':
databaseEntry = Measurements.findOne(measurementData.id, {reactive: false});
if (!databaseEntry) {
return;
}
// TODO= Fix this when we have Findings that relate to more than one viewport
Measurements.remove(databaseEntry._id);
break;
}
}
// Avoid doing DOM manipulation during the resize handler // Avoid doing DOM manipulation during the resize handler
// because it is fired very often. // because it is fired very often.
// Resizing is therefore performed 100 ms after the resize event stops. // Resizing is therefore performed 100 ms after the resize event stops.

View File

@ -0,0 +1,108 @@
LesionLocations = new Meteor.Collection(null);
LesionLocations.insert({
id: 'liverLeft',
group: 'liver',
location: "Liver Left",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'liverRight',
group: 'liver',
location: "Liver Right",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'liverCaudate',
group: 'liver',
location: "Liver Caudate",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'lungLLL',
group: 'lung',
location: "Lung LLL",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'lungLUL',
group: 'lung',
location: "Lung LUL",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'lungRLL',
group: 'lung',
location: "Lung RLL",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'lungRML',
group: 'lung',
location: "Lung RML",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'lungRUL',
group: 'lung',
location: "Lung RUL",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'pleuraLeft',
group: 'pleura',
location: "Pleura Left",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'pleuraRight',
group: 'pleura',
location: "Pleura Right",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'kidneyLeft',
group: 'kidney',
location: "Kidney Left",
hasDescription: false,
description: "",
selected: false
});
LesionLocations.insert({
id: 'kidneyRight',
group: 'kidney',
location: "Kidney Right",
hasDescription: false,
description: ""
});

View File

@ -0,0 +1,55 @@
LocationResponses = new Meteor.Collection(null);
LocationResponses.insert({
text: "Complete response",
code: "CR",
description: ""
});
LocationResponses.insert({
text: "Progressive disease",
code: "PD",
description: ""
});
LocationResponses.insert({
text: "Complete response",
code: "CR",
description: ""
});
LocationResponses.insert({
text: "Stable disease",
code: "SD",
description: ""
});
LocationResponses.insert({
text: "Non-measurable",
code: "NM",
description: ""
});
LocationResponses.insert({
text: "Unknown",
code: "UN",
description: ""
});
LocationResponses.insert({
text: "Not Evaluable",
code: "NE",
description: ""
});
LocationResponses.insert({
text: "Non-CR/Non-PD",
code: "NN",
description: ""
});
LocationResponses.insert({
text: "Excluded",
code: "EX",
description: ""
});

View File

@ -71,7 +71,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
cornerstoneTools.removeToolState(element, toolType, measurementData); cornerstoneTools.removeToolState(element, toolType, measurementData);
} else { } else {
// Set lesionMeasurementData Session // Set lesionMeasurementData Session
config.getLesionLocationCallback(measurementData, mouseEventData); config.getLesionLocationCallback(measurementData, mouseEventData, doneCallback);
} }
$(element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools.lesion.mouseMoveCallback); $(element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools.lesion.mouseMoveCallback);
@ -157,7 +157,8 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
pointNearHandle: pointNearTextBox, pointNearHandle: pointNearTextBox,
active: false, active: false,
movesIndependently: true, movesIndependently: true,
drawnIndependently: true drawnIndependently: true,
allowedOutsideImage: true
} }
}, },
imageId: imageId, imageId: imageId,
@ -282,10 +283,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
// Set measurement text to show lesion table // Set measurement text to show lesion table
data.measurementText = length.toFixed(1); data.measurementText = length.toFixed(1);
context.restore(); context.restore();
updateLesionCollection(toolData.data[i]);
} }
} }
@ -301,7 +299,58 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
} }
} }
///////// END IMAGE RENDERING /////// function doubleClickCallback(e, eventData) {
// Prevent other double click handlers from firing after this one
//e.stopImmediatePropagation();
var element = eventData.element;
var data;
function doneCallback(data, deleteTool) {
if (deleteTool === true) {
cornerstoneTools.removeToolState(element, toolType, data);
cornerstone.updateImage(element);
//return;
}
/*// TODO= Find a better way to do this! This is very messy
config.setLesionNumberCallback(data, eventData, function(lesionNumber) {
data.lesionName = "Target " + lesionNumber;
data.lesionNumber = lesionNumber;
data.active = false;
cornerstone.updateImage(element);
});*/
}
if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
return false;
}
var config = cornerstoneTools.lesion.getConfiguration();
var coords = eventData.currentPoints.canvas;
var toolData = cornerstoneTools.getToolState(element, toolType);
// now check to see if there is a handle we can move
if (!toolData) {
return false;
}
for (var i = 0; i < toolData.data.length; i++) {
data = toolData.data[i];
if (pointNearTool(element, data, coords)) {
data.active = true;
cornerstone.updateImage(element);
// Allow relabelling via a callback
config.changeLesionLocationCallback(data, eventData, doneCallback);
e.stopImmediatePropagation();
return false;
}
}
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
// module exports // module exports
cornerstoneTools.lesion = cornerstoneTools.mouseButtonTool({ cornerstoneTools.lesion = cornerstoneTools.mouseButtonTool({
@ -309,6 +358,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
addNewMeasurement: addNewMeasurement, addNewMeasurement: addNewMeasurement,
onImageRendered: onImageRendered, onImageRendered: onImageRendered,
pointNearTool: pointNearTool, pointNearTool: pointNearTool,
mouseDoubleClickCallback: doubleClickCallback,
toolType: toolType toolType: toolType
}); });

View File

@ -46,12 +46,9 @@ var measurementManagerDAL = (function() {
lesionDataObject.timepoints = timepointsObject; lesionDataObject.timepoints = timepointsObject;
// Is there a use for this? // Is there a use for this?
lesionDataObject.number = Measurements.find().count() + 1; lesionDataObject.number = Measurements.find().count() + 1;
// TODO=Fix this workaround to prevent the observe hook from adding another set of toolData lesionDataObject.id = Measurements.insert(lesionDataObject);
lesionDataObject.toolDataInsertedManually = true;
Measurements.insert(lesionDataObject);
} }
// Update timepoint data in Measurements collection // Update timepoint data in Measurements collection
@ -82,6 +79,7 @@ var measurementManagerDAL = (function() {
timepoints[timepointID].studyInstanceUid = lesionData.studyInstanceUid; timepoints[timepointID].studyInstanceUid = lesionData.studyInstanceUid;
timepoints[timepointID].handles = lesionData.handles; timepoints[timepointID].handles = lesionData.handles;
lesionData.id = measurement._id;
Measurements.update(measurement._id, { Measurements.update(measurement._id, {
$set: { $set: {
@ -133,7 +131,7 @@ var measurementManagerDAL = (function() {
if (!timepoints[timepointID]) { if (!timepoints[timepointID]) {
// Find lesion number for this timepointID // Find lesion number for this timepointID
return measurement.lesionNumber return measurement.lesionNumber;
} }
if (timepoints[timepointID].longestDiameter === '') { if (timepoints[timepointID].longestDiameter === '') {

View File

@ -118,7 +118,8 @@
pointNearHandle: pointNearTextBox, pointNearHandle: pointNearTextBox,
active: false, active: false,
movesIndependently: true, movesIndependently: true,
drawnIndependently: true drawnIndependently: true,
allowedOutsideImage: true
} }
}, },
imageId: imageId, imageId: imageId,

View File

@ -0,0 +1,37 @@
<template name="lesionLocationDialog">
<div id="lesionLocationDialog">
<div class="dialogHeader">
<h4>Select Lesion Location</h4>
</div>
<div class="dialogContent">
<div class="lesionLocation">
<label>Lesion Location</label>
<select class="selectLesionLocation">
<option value="-1"></option>
{{ #each lesionLocations}}
<option value='{{_id}}'>{{location}}</option>
{{ /each}}
</select>
</div>
</div>
</div>
<div id="lesionLocationRelabelDialog">
<div class="dialogHeader">
<h4>Change Lesion Location</h4>
</div>
<a></a>
<div class="dialogContent">
<div class="lesionLocation">
<label>Lesion Location</label>
<select class="selectLesionLocation">
<option value="-1"></option>
{{ #each lesionLocations}}
<option value='{{_id}}' selected={{selected}}>{{location}}</option>
{{ /each}}
</select>
</div>
<button id="removeLesion" class="btn btn-link">Remove</button>
</div>
</div>
</template>

View File

@ -0,0 +1,247 @@
function closeHandler(dialog) {
// Hide the lesion dialog
$(dialog).css('display', 'none');
// Remove the backdrop
$(".removableBackdrop").remove();
}
// This event sets lesion number for new lesion
function setLesionNumberCallback(measurementData, eventData, doneCallback) {
// Get the current element's timepointID from the study date metadata
var element = eventData.element;
var enabledElement = cornerstone.getEnabledElement(element);
var imageId = enabledElement.image.imageId;
var study = cornerstoneTools.metaData.get('study', imageId);
var timepoint = Timepoints.findOne({timepointName: study.studyDate});
if (!timepoint) {
return;
}
measurementData.timepointID = timepoint.timepointID;
// Get a lesion number for this lesion, depending on whether or not the same lesion previously
// exists at a different timepoint
var lesionNumber = measurementManagerDAL.getNewLesionNumber(measurementData.timepointID, isTarget=true);
measurementData.lesionNumber = lesionNumber;
// Set lesion number
doneCallback(lesionNumber);
}
// This event determines whether or not to show the lesion dialog
// If there already exists a lesion with this specific lesion number,
// related to the chosen location.
function getLesionLocationCallback(measurementData, eventData) {
Template.lesionLocationDialog.measurementData = measurementData;
// Reset the doneCallback saved in the template so we don't call the change event's done callback
Template.lesionLocationDialog.doneCallback = undefined;
// Get the lesion location dialog
var dialog = $("#lesionLocationDialog");
Template.lesionLocationDialog.dialog = dialog;
// Show the backdrop
UI.render(Template.removableBackdrop, document.body);
// Make sure the context menu is closed when the user clicks away
$(".removableBackdrop").one('mousedown touchstart', function() {
closeHandler(dialog);
});
// Select the first option for now
var selector = dialog.find("select.selectLesionLocation");
selector.find("option:first").prop("selected", true);
// Find out if this lesion number is already added in the lesion manager for another timepoint
// If it is, stop here because we don't need the dialog.
var locationUID = measurementManagerDAL.lesionNumberExists(measurementData);
if (locationUID) {
// Add an ID value to the tool data to link it to the Measurements collection
measurementData.id = 'notready';
measurementData.locationUID = locationUID;
measurementManagerDAL.updateTimepointData(measurementData);
closeHandler();
return;
}
// If it isn't, continue to open the dialog and have the user choose a lesion location
// Show the lesion location dialog above
var dialogProperty = {
top: eventData.currentPoints.page.y - dialog.outerHeight() - 40,
left: eventData.currentPoints.page.x - dialog.outerWidth() / 2,
display: 'block'
};
// Device is touch device or not
// If device is touch device, set position center of screen vertically and horizontally
if (isTouchDevice()) {
// add dialogMobile class to provide a black,transparent background
dialog.addClass("dialogMobile");
dialogProperty.top = 0;
dialogProperty.left = 0;
dialogProperty.right = 0;
dialogProperty.bottom = 0;
}
dialog.css(dialogProperty);
}
function changeLesionLocationCallback(measurementData, eventData, doneCallback) {
Template.lesionLocationDialog.measurementData = measurementData;
Template.lesionLocationDialog.doneCallback = doneCallback;
// Get the lesion location dialog
var dialog = $("#lesionLocationRelabelDialog");
Template.lesionLocationDialog.dialog = dialog;
// Show the backdrop
UI.render(Template.removableBackdrop, document.body);
// Make sure the context menu is closed when the user clicks away
$(".removableBackdrop").one('mousedown touchstart', function() {
closeHandler(dialog);
});
// Show the lesion location dialog above
var dialogProperty = {
top: eventData.currentPoints.page.y - dialog.outerHeight() - 40,
left: eventData.currentPoints.page.x - dialog.outerWidth() / 2,
display: 'block'
};
// Device is touch device or not
// If device is touch device, set position center of screen vertically and horizontally
if (isTouchDevice()) {
// add dialogMobile class to provide a black,transparent background
dialog.addClass("dialogMobile");
dialogProperty.top = 0;
dialogProperty.left = 0;
dialogProperty.right = 0;
dialogProperty.bottom = 0;
}
dialog.css(dialogProperty);
var measurement = Measurements.findOne(measurementData.id);
if (!measurement) {
return;
}
var currentLocation = LesionLocations.findOne({
id: measurement.locationId
});
LesionLocations.update({},
{$set: {selected: false}},
{ multi: true });
LesionLocations.update(currentLocation._id, {
$set: {
selected: true
}
});
}
var config = {
setLesionNumberCallback: setLesionNumberCallback,
getLesionLocationCallback: getLesionLocationCallback,
changeLesionLocationCallback: changeLesionLocationCallback
};
cornerstoneTools.lesion.setConfiguration(config);
Template.lesionLocationDialog.events({
'change .selectLesionLocation': function(e) {
var measurementData = Template.lesionLocationDialog.measurementData;
var doneCallback = Template.lesionLocationDialog.doneCallback;
var dialog = Template.lesionLocationDialog.dialog;
// Get the current value of the selector
var selectedOptionId = e.currentTarget.value;
// If the selected option is still the default (-1)
// then stop here
if (selectedOptionId < 0) {
return;
}
// Get selected location data
var locationObj = LesionLocations.findOne({_id: selectedOptionId});
var id;
var existingLocation = PatientLocations.findOne({location: locationObj.location});
if (existingLocation) {
id = existingLocation._id;
} else {
// Adds location data to PatientLocation and retrieve the location ID
id = PatientLocations.insert({
location: locationObj.location,
locationId: locationObj._id
});
}
// Add an ID value to the tool data to link it to the Measurements collection
if (!measurementData.id) {
measurementData.id = 'notready';
// Link locationUID with active lesion measurementData
measurementData.locationId = locationObj.id;
measurementData.locationUID = id;
/// Set the isTarget value to true, since this is the target-lesion dialog callback
measurementData.isTarget = true;
// Adds lesion data to timepoints array
measurementManagerDAL.addLesionData(measurementData);
} else {
Measurements.update(measurementData.id, {
$set: {
location: locationObj.location,
locationId: locationObj.id,
locationUID: id
}
});
}
// Close the dialog
closeHandler(dialog);
if (doneCallback && typeof doneCallback === 'function') {
doneCallback(measurementData);
}
},
'click #removeLesion': function() {
var measurementData = Template.lesionLocationDialog.measurementData;
var doneCallback = Template.lesionLocationDialog.doneCallback;
var dialog = Template.lesionLocationDialog.dialog;
if (doneCallback && typeof doneCallback === 'function') {
var deleteTool = true;
doneCallback(measurementData, deleteTool);
}
closeHandler(dialog);
},
'click #btnCloseLesionPopup': function() {
var dialog = Template.lesionLocationDialog.dialog;
closeHandler(dialog);
},
'keypress #lesionLocationDialog, keypress #lesionLocationRelabelDialog': function(e) {
var dialog = Template.lesionLocationDialog.dialog;
// If Enter is pressed, close the dialog
if (e.which === 13) {
closeHandler(dialog);
}
}
});
Template.lesionLocationDialog.helpers({
'lesionLocations': function() {
return LesionLocations.find();
}
});

View File

@ -0,0 +1,40 @@
#lesionLocationDialog, #lesionLocationRelabelDialog
display: none
position: absolute
top: 0
bottom: 0
left: 0
right: 0
z-index: 100
width: 300px
border-radius: 5px
padding: 10px 20px 10px 20px
background-color: rgba(255,255,255,1)
/* dialogMobile is added to #lesionLocationDialog when touchDevices is detected */
.dialogMobile
margin: 0 auto
float: none
background-color: rgba(0,0,0,0.5)
text-align: center
#closeLesionPopup
outline: none
border: none
background-color: transparent
#selectLesionLocation
margin-top: 5px
.dialogContent
margin-bottom: 10px
#removeLesion
outline: none
text-decoration: none
#lesionLocationDialog
height: 110px
#lesionLocationRelabelDialog
height: 125px

View File

@ -41,6 +41,11 @@ activateLesion = function(measurementId, templateData) {
$(".imageViewerViewport").each(function(viewportIndex, element) { $(".imageViewerViewport").each(function(viewportIndex, element) {
// Stop if we run out of timepoints before viewports // Stop if we run out of timepoints before viewports
if (viewportIndex >= timepointsWithEntries.length) { if (viewportIndex >= timepointsWithEntries.length) {
// Update the element anyway, to remove any other highlights that are present
deactivateAllToolData(element, 'lesion');
deactivateAllToolData(element, 'nonTarget');
cornerstone.updateImage(element);
return false; return false;
} }
@ -57,6 +62,10 @@ activateLesion = function(measurementId, templateData) {
// If there is no measurement data to display, stop here // If there is no measurement data to display, stop here
if (!measurementAtTimepoint) { if (!measurementAtTimepoint) {
// Update the element anyway, to remove any other highlights that are present
deactivateAllToolData(element, 'lesion');
deactivateAllToolData(element, 'nonTarget');
cornerstone.updateImage(element);
return; return;
} }
@ -247,7 +256,6 @@ Template.lesionTable.events({
}); });
} }
}); });
Template.lesionTable.onCreated(function(){ Template.lesionTable.onCreated(function(){
@ -275,14 +283,13 @@ Tracker.autorun(function () {
dates: [] dates: []
}; };
$("#"+contentId+" .imageViewerViewport").each(function(viewportIndex, element) { $(".imageViewerViewport").each(function(viewportIndex, element) {
var enabledElement = cornerstone.getEnabledElement(element); var enabledElement = cornerstone.getEnabledElement(element);
if(enabledElement && enabledElement.image){ if(enabledElement && enabledElement.image){
var imageId = enabledElement.image.imageId; var imageId = enabledElement.image.imageId;
var study = cornerstoneTools.metaData.get('study', imageId); var study = cornerstoneTools.metaData.get('study', imageId);
var studyDate = study.studyDate; var studyDate = study.studyDate;
var patientId = study.patientId; loadedStudyDates.patientId = study.patientId;
loadedStudyDates.patientId = patientId;
// Check studyDate is added before // Check studyDate is added before
if (loadedStudyDates.dates.indexOf(studyDate) < 0) { if (loadedStudyDates.dates.indexOf(studyDate) < 0) {
loadedStudyDates.dates.push(studyDate); loadedStudyDates.dates.push(studyDate);
@ -306,7 +313,6 @@ Tracker.autorun(function () {
$set: { $set: {
timepointLoaded: timepointLoaded timepointLoaded: timepointLoaded
} }
}); });
}); });
} }

View File

@ -0,0 +1,40 @@
<template name="nonTargetLesionDialog">
<div id="nonTargetLesionLocationDialog">
<div class="dialogHeader">
<div class="dialogHeader">
<div><h4>Select Lesion Location</h4></div>
</div>
</div>
<div class="dialogContent">
<div class="lesionLocation">
<div class="elementContainer">
<label>Lesion Location</label>
</div>
<div class="elementContainer">
<select id="selectNonTargetLesionLocation">
<option value="-1"></option>
{{ #each lesionLocations}}
<option value='{{_id}}'>{{location}}</option>
{{ /each}}
</select>
</div>
</div>
<div class="locationResponse">
<div class="elementContainer">
<label>Response</label>
</div>
<div class="elementContainer">
<select id="selectNonTargetLesionLocationResponse">
<option value="-1"></option>
{{ #each locationResponses}}
<option value='{{code}}'>{{code}} - {{text}}</option>
{{ /each}}
</select>
</div>
</div>
<div class="locationOK">
<button class="btn btn-primary" id="nonTargetLesionOK">OK</button>
</div>
</div>
</div>
</template>

View File

@ -1,5 +1,12 @@
// This event sets lesion number for new lesion function closeHandler(dialog) {
// Hide the lesion dialog
$(dialog).css('display', 'none');
// Remove the backdrop
$(".removableBackdrop").remove();
}
// This event sets lesion number for new lesion
function setLesionNumberCallback(measurementData, eventData, doneCallback) { function setLesionNumberCallback(measurementData, eventData, doneCallback) {
// Get the current element's timepointID from the study date metadata // Get the current element's timepointID from the study date metadata
var element = eventData.element; var element = eventData.element;
@ -29,27 +36,43 @@ function setLesionNumberCallback(measurementData, eventData, doneCallback) {
// This event determines whether or not to show the Non-Target lesion dialog // This event determines whether or not to show the Non-Target lesion dialog
// If there already exists a lesion with this specific lesion number, // If there already exists a lesion with this specific lesion number,
// related to the chosen location. // related to the chosen location.
function getNonTargetLesionLocationCallback(measurementData, eventData) { function getNonTargetLesionLocationCallback(measurementData, eventData) {
Template.nonTargetLesionDialog.measurementData = measurementData;
// Get the non-target lesion location dialog // Get the non-target lesion location dialog
var nonTargetlesionDialog = $("#nonTargetLesionLocationDialog"); var dialog = $("#nonTargetLesionLocationDialog");
Template.nonTargetLesionDialog.dialog = dialog;
// Show the backdrop
UI.render(Template.removableBackdrop, document.body);
// Make sure the context menu is closed when the user clicks away
$(".removableBackdrop").one('mousedown touchstart', function() {
closeHandler(dialog);
if (doneCallback && typeof doneCallback === 'function') {
var deleteTool = true;
doneCallback(measurementData, deleteTool);
}
});
// Find the select option box // Find the select option box
var selectorLocation = nonTargetlesionDialog.find("select#selectNonTargetLesionLocation"); var selectorLocation = dialog.find("select#selectNonTargetLesionLocation");
var selectorResponse = nonTargetlesionDialog.find("select#selectNonTargetLesionLocationResponse"); var selectorResponse = dialog.find("select#selectNonTargetLesionLocationResponse");
// OK button selectorLocation.find("option:first").prop("selected", "selected");
var btnOK = nonTargetlesionDialog.find("button#nonTargetLesionOK"); selectorResponse.find("option:first").prop("selected", "selected");
// Allow location selection
// If selector location is disabled, make it enable selectorLocation.removeAttr("disabled");
enableLocationSelection();
// Find out if this lesion number is already added in the lesion manager for another timepoint // Find out if this lesion number is already added in the lesion manager for another timepoint
// If it is, disable selector location // If it is, disable selector location
var locationUID = measurementManagerDAL.lesionNumberExists(measurementData); var locationUID = measurementManagerDAL.lesionNumberExists(measurementData);
if (locationUID) { if (locationUID) {
// Add an ID value to the tool data to link it to the Measurements collection
measurementData.id = 'notready';
measurementData.locationUID = locationUID; measurementData.locationUID = locationUID;
// Disable the selection of a new location // Disable the selection of a new location
disableLocationSelection(measurementData.locationUID); disableLocationSelection(measurementData.locationUID);
@ -58,48 +81,16 @@ function getNonTargetLesionLocationCallback(measurementData, eventData) {
// Disable selector location to prevent selecting a new location // Disable selector location to prevent selecting a new location
function disableLocationSelection(locationUID) { function disableLocationSelection(locationUID) {
var locationName = measurementManagerDAL.getLocationName(locationUID); var locationName = measurementManagerDAL.getLocationName(locationUID);
selectorLocation.find('option').each(function() selectorLocation.find('option').each(function() {
{
if ($(this).text() === locationName) { if ($(this).text() === locationName) {
// Select location in locations dropdown list // Select location in locations dropdown list
selectorLocation.find('option').eq($(this).index()).prop("selected", true); selectorLocation.find('option').eq($(this).index()).prop("selected", true);
return;
} }
}); });
selectorLocation.prop("disabled", true); selectorLocation.prop("disabled", true);
} }
// Enable selector location
function enableLocationSelection() {
selectorLocation.removeAttr("disabled");
}
function closeHandler() {
// Hide the lesion dialog
nonTargetlesionDialog.css('display', 'none');
// Enable selector location
enableLocationSelection();
// Get the current value of the select option box
selectorLocation.find("option:first").prop("selected", "selected");
selectorResponse.find("option:first").prop("selected", "selected");
}
// Attach keypress handlers so the user can close with the Enter button
nonTargetlesionDialog.off("keypress");
nonTargetlesionDialog.on('keypress', keyPressHandler);
// This is the keypress callback function
function keyPressHandler(e) {
// If Enter is pressed, close the dialog
if (e.which === 13) {
closeHandler();
}
}
// Show the nonTargetLesion dialog above // Show the nonTargetLesion dialog above
var dialogProperty = { var dialogProperty = {
top: eventData.currentPoints.page.y, top: eventData.currentPoints.page.y,
@ -111,27 +102,40 @@ function getNonTargetLesionLocationCallback(measurementData, eventData) {
// If device is touch device, set position center of screen vertically and horizontally // If device is touch device, set position center of screen vertically and horizontally
if (isTouchDevice()) { if (isTouchDevice()) {
// add dialogMobile class to provide a black,transparent background // add dialogMobile class to provide a black,transparent background
$(nonTargetlesionDialog).addClass("dialogMobile"); dialog.addClass("dialogMobile");
dialogProperty.top = 0; dialogProperty.top = 0;
dialogProperty.left = 0; dialogProperty.left = 0;
dialogProperty.right = 0; dialogProperty.right = 0;
dialogProperty.bottom = 0; dialogProperty.bottom = 0;
$(".contentWrapper").css({
left: ($(window).width() - $(".contentWrapper").width()) / 2,
top: ($(window).height() - $(".contentWrapper").height()) / 2
});
} }
nonTargetlesionDialog.css(dialogProperty); dialog.css(dialogProperty);
}
function changeNonTargetLesionLocationCallback(measurementData, eventData, doneCallback) {
doneCallback(prompt('Change your lesion location:'));
}
var config = {
setLesionNumberCallback: setLesionNumberCallback,
getNonTargetLesionLocationCallback: getNonTargetLesionLocationCallback,
changeNonTargetLesionLocationCallback: changeNonTargetLesionLocationCallback
};
cornerstoneTools.nonTarget.setConfiguration(config);
Template.nonTargetLesionDialog.events({
'click #nonTargetLesionOK': function() {
var dialog = Template.nonTargetLesionDialog.dialog;
var measurementData = Template.nonTargetLesionDialog.measurementData;
// Find the select option box
var selectorLocation = dialog.find("select#selectNonTargetLesionLocation");
var selectorResponse = dialog.find("select#selectNonTargetLesionLocationResponse");
// Click OK button
// Add lesion to lesion table
btnOK.off('click');
btnOK.on('click', function(e) {
// Get the current value of the selector // Get the current value of the selector
var selectedOptionId = selectorLocation.find("option:selected").val(); var selectedOptionId = selectorLocation.find("option:selected").val();
var responseOptionCode = nonTargetlesionDialog.find("select#selectNonTargetLesionLocationResponse option:selected").val(); var responseOptionId = selectorResponse.find("option:selected").val();
// If the selected option is still the default (-1) // If the selected option is still the default (-1)
// then stop here // then stop here
@ -141,7 +145,7 @@ function getNonTargetLesionLocationCallback(measurementData, eventData) {
// If the selected response option is still the default (-1) // If the selected response option is still the default (-1)
// then stop here // then stop here
if (responseOptionCode < 0) { if (responseOptionId < 0) {
return; return;
} }
@ -157,102 +161,61 @@ function getNonTargetLesionLocationCallback(measurementData, eventData) {
id = PatientLocations.insert({location: locationObj.location}); id = PatientLocations.insert({location: locationObj.location});
} }
// Link locationUID with active lesion measurementData if (!measurementData.id) {
measurementData.locationUID = id; // Add an ID value to the tool data to link it to the Measurements collection
measurementData.id = 'notready';
/// Set the isTarget value to true, since this is the target-lesion dialog callback // Link locationUID with active lesion measurementData
measurementData.isTarget = false; measurementData.locationUID = id;
// measurementText is set from location response list /// Set the isTarget value to true, since this is the target-lesion dialog callback
measurementData.measurementText = responseOptionCode; measurementData.isTarget = false;
// Adds lesion data to timepoints array // measurementText is set from location response list
measurementManagerDAL.addLesionData(measurementData); measurementData.measurementText = responseOptionId;
// Adds lesion data to timepoints array
measurementManagerDAL.addLesionData(measurementData);
} else {
Measurements.update(measurementData.id, {
$set: {
location: locationObj.location,
locationId: locationObj.id,
locationUID: id
}
});
}
// Close the dialog // Close the dialog
closeHandler(); closeHandler(dialog);
}); },
} 'click #removeLesion': function() {
var measurementData = Template.nonTargetLesionDialog.measurementData;
var doneCallback = Template.nonTargetLesionDialog.doneCallback;
var dialog = Template.nonTargetLesionDialog.dialog;
function changeNonTargetLesionLocationCallback(measurementData, eventData, doneCallback) { if (doneCallback && typeof doneCallback === 'function') {
doneCallback(prompt('Change your lesion location:')); var deleteTool = true;
} doneCallback(measurementData, deleteTool);
}
var config = { closeHandler(dialog);
setLesionNumberCallback: setLesionNumberCallback, },
getNonTargetLesionLocationCallback: getNonTargetLesionLocationCallback, 'click #btnCloseLesionPopup': function() {
changeNonTargetLesionLocationCallback: changeNonTargetLesionLocationCallback var dialog = Template.nonTargetLesionDialog.dialog;
}; closeHandler(dialog);
},
'keypress #lesionLocationDialog, keypress #lesionLocationRelabelDialog': function(e) {
var dialog = Template.nonTargetLesionDialog.dialog;
cornerstoneTools.nonTarget.setConfiguration(config); // If Enter is pressed, close the dialog
if (e.which === 13) {
closeHandler(dialog);
}
}
});
LocationResponses = new Meteor.Collection(null);
LocationResponses.insert(
{
text: "Complete response",
code: "CR",
description: ""
}
);
LocationResponses.insert(
{
text: "Progressive disease",
code: "PD",
description: ""
}
);
LocationResponses.insert(
{
text: "Complete response",
code: "CR",
description: ""
}
);
LocationResponses.insert(
{
text: "Stable disease",
code: "SD",
description: ""
}
);
LocationResponses.insert(
{
text: "Non-measurable",
code: "NM",
description: ""
}
);
LocationResponses.insert(
{
text: "Unknown",
code: "UN",
description: ""
}
);
LocationResponses.insert(
{
text: "Not Evaluable",
code: "NE",
description: ""
}
);
LocationResponses.insert(
{
text: "Non-CR/Non-PD",
code: "NN",
description: ""
}
);
LocationResponses.insert(
{
text: "Excluded",
code: "EX",
description: ""
}
);
Template.nonTargetLesionDialog.helpers({ Template.nonTargetLesionDialog.helpers({
'lesionLocations': function() { 'lesionLocations': function() {
return LesionLocations.find(); return LesionLocations.find();

View File

@ -0,0 +1,35 @@
#nonTargetLesionLocationDialog
display: none
position: absolute
top: 0
bottom: 0
left: 0
right: 0
z-index: 100
width: 300px
height: 230px
border-radius: 5px
padding: 10px 20px 10px 20px
background-color: rgba(255,255,255,1)
/* dialogMobile is added to #nonTargetLesionLocationDialog when touchDevices is detected */
.dialogMobile
margin: 0 auto
float: none
background-color: rgba(0,0,0,0.5)
text-align: center
#selectNonTargetLesionLocation, #selectNonTargetLesionLocationResponse
width: 100%
.dialogContent, .lesionLocation, .locationResponse
margin-bottom: 20px
.elementContainer
width: 100%
margin: 0 auto
float: none
.locationOK
text-align: center

View File

@ -1,20 +0,0 @@
<template name="lesionLocationDialog">
<div id="lesionLocationDialog">
<div class="lesionContentWrapper">
<div class="dialogHeader">
<h4>Select Lesion Location</h4>
</div>
<div class="dialogContent">
<div class="lesionLocation">
<label>Lesion Location</label>
<select id="selectLesionLocation">
<option value="-1"></option>
{{ #each lesionLocations}}
<option value='{{_id}}'>{{location}}</option>
{{ /each}}
</select>
</div>
</div>
</div>
</div>
</template>

View File

@ -1,226 +0,0 @@
// This event sets lesion number for new lesion
function setLesionNumberCallback(measurementData, eventData, doneCallback) {
// Get the current element's timepointID from the study date metadata
var element = eventData.element;
var enabledElement = cornerstone.getEnabledElement(element);
var imageId = enabledElement.image.imageId;
var study = cornerstoneTools.metaData.get('study', imageId);
var timepoint = Timepoints.findOne({timepointName: study.studyDate});
if (!timepoint) {
return;
}
measurementData.timepointID = timepoint.timepointID;
// Get a lesion number for this lesion, depending on whether or not the same lesion previously
// exists at a different timepoint
var lesionNumber = measurementManagerDAL.getNewLesionNumber(measurementData.timepointID, isTarget=true);
measurementData.lesionNumber = lesionNumber;
// Set lesion number
doneCallback(lesionNumber);
}
// This event determines whether or not to show the lesion dialog
// If there already exists a lesion with this specific lesion number,
// related to the chosen location.
function getLesionLocationCallback(measurementData, eventData) {
// Get the lesion location dialog
var lesionDialog = $("#lesionLocationDialog");
// Find the select option box
var selector = lesionDialog.find("select#selectLesionLocation");
// Find out if this lesion number is already added in the lesion manager for another timepoint
// If it is, stop here because we don't need the dialog.
var locationUID = measurementManagerDAL.lesionNumberExists(measurementData);
if (locationUID) {
measurementData.locationUID = locationUID;
measurementManagerDAL.updateTimepointData(measurementData);
closeHandler();
return;
}
// If it isn't, continue to open the dialog and have the user choose a lesion location
// Attach close handler if the user clicks the close button
var close = lesionDialog.find("#btnCloseLesionPopup");
close.off('click');
close.on('click', function() {
closeHandler();
});
function closeHandler() {
// Hide the lesion dialog
lesionDialog.css('display', 'none');
// Select the first option for the next time the dialog is opened
selector.find("option:first").prop("selected", true);
}
// Attach keypress handlers so the user can close with the Enter button
lesionDialog.off("keypress");
lesionDialog.on('keypress', keyPressHandler);
// This is the keypress callback function
function keyPressHandler(e) {
// If Enter is pressed, close the dialog
if (e.which === 13) {
closeHandler();
}
}
// Show the lesion location dialog above
var dialogProperty = {
top: eventData.currentPoints.page.y - lesionDialog.outerHeight() - 40,
left: eventData.currentPoints.page.x - lesionDialog.outerWidth() / 2,
display: 'block'
};
// Device is touch device or not
// If device is touch device, set position center of screen vertically and horizontally
if (isTouchDevice()) {
// add dialogMobile class to provide a black,transparent background
$(lesionDialog).addClass("dialogMobile");
dialogProperty.top = 0;
dialogProperty.left = 0;
dialogProperty.right = 0;
dialogProperty.bottom = 0;
$(".lesionContentWrapper").css({
left: ($(window).width() - $(".lesionContentWrapper").width()) / 2,
top: ($(window).height() - $(".lesionContentWrapper").height()) / 2
});
}
lesionDialog.css(dialogProperty);
// Attach a callback for the select box
selector.off('change');
selector.on('change', function(e) {
// Get the current value of the selector
var selectedOptionId = this.value;
// If the selected option is still the default (-1)
// then stop here
if (selectedOptionId < 0) {
return;
}
// Get selected location data
var locationObj = LesionLocations.findOne({_id: selectedOptionId});
var id;
var existingLocation = PatientLocations.findOne({location: locationObj.location});
if (existingLocation) {
id = existingLocation._id;
} else {
// Adds location data to PatientLocation and retrieve the location ID
id = PatientLocations.insert({location: locationObj.location});
}
// Link locationUID with active lesion measurementData
measurementData.locationUID = id;
/// Set the isTarget value to true, since this is the target-lesion dialog callback
measurementData.isTarget = true;
// Adds lesion data to timepoints array
measurementManagerDAL.addLesionData(measurementData);
// Close the dialog
closeHandler();
});
}
function changeLesionLocationCallback(measurementData, eventData, doneCallback) {
doneCallback(prompt('Change your lesion location:'));
}
var config = {
setLesionNumberCallback: setLesionNumberCallback,
getLesionLocationCallback: getLesionLocationCallback,
changeLesionLocationCallback: changeLesionLocationCallback
};
cornerstoneTools.lesion.setConfiguration(config);
LesionLocations = new Meteor.Collection(null);
LesionLocations.insert({
location: "Liver Left",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Liver Right",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Liver Caudate",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Lung LLL",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Lung LUL",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Lung RLL",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Lung RML",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Lung RUL",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Pleura Left",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Pleura Right",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Kidney Left",
hasDescription: false,
description: ""
});
LesionLocations.insert({
location: "Kidney Right",
hasDescription: false,
description: ""
});
Template.lesionLocationDialog.helpers({
'lesionLocations': function() {
return LesionLocations.find();
}
});

View File

@ -1,32 +0,0 @@
#lesionLocationDialog
display: none
position: absolute
z-index: 100
/* dialogMobile is added to #lesionLocationDialog when touchDevices is detected */
.dialogMobile
margin: 0 auto;
float: none;
background-color: rgba(0,0,0,0.5);
text-align: center;
.lesionContentWrapper
position: absolute;
width: 250px;
height: 150px;
border-radius: 10px;
padding: 10px 20px 10px 20px;
background-color: rgba(255,255,255,1);
text-align: left
#closeLesionPopup
outline: 0
border: none
background-color: transparent
#selectLesionLocation
margin-top: 5px
.dialogContent
margin-bottom: 20px

View File

@ -1,48 +0,0 @@
#nonTargetLesionLocationDialog {
display: none;
position: absolute;
z-index: 100;
}
/* dialogMobile is added to #nonTargetLesionLocationDialog when touchDevices is detected */
.dialogMobile {
margin: 0 auto;
float: none;
background-color: rgba(0,0,0,0.5);
text-align: center;
}
.contentWrapper {
position: absolute;
width: 250px;
height: 250px;
border-radius: 10px;
padding: 10px 20px 10px 20px;
background-color: rgba(255,255,255,1);
text-align: left;
}
#selectNonTargetLesionLocation, #selectNonTargetLesionLocationResponse {
width: 100%;
}
.dialogContent, .lesionLocation, .locationResponse {
margin-bottom: 20px;
}
.elementContainer {
width: 100%;
margin: 0 auto;
float: none;
text-align: left;
}
.locationOK {
text-align: center;
}
.btn{
background-color: #bbb;
border: 1px solid #888;
color: #000000;
}

View File

@ -1,45 +0,0 @@
<template name="nonTargetLesionDialog">
<div id="nonTargetLesionLocationDialog">
<div class="contentWrapper">
<div class="dialogHeader">
<div class="dialogHeader">
<div><h4>Select Lesion Location</h4></div>
</div>
</div>
<div class="dialogContent">
<div class="lesionLocation">
<div class="elementContainer">
<label>Lesion Location</label>
</div>
<div class="elementContainer">
<select id="selectNonTargetLesionLocation">
<option value="-1"></option>
{{ #each lesionLocations}}
<option value='{{_id}}'>{{location}}</option>
{{ /each}}
</select>
</div>
</div>
<div class="locationResponse">
<div class="elementContainer">
<label>Response</label>
</div>
<div class="elementContainer">
<select id="selectNonTargetLesionLocationResponse">
<option value="-1"></option>
{{ #each locationResponses}}
<option value='{{code}}'>{{code}} - {{text}}</option>
{{ /each}}
</select>
</div>
</div>
<div class="locationOK">
<button class="btn" id="nonTargetLesionOK">OK</button>
</div>
</div>
</div>
</div>
</template>

View File

@ -17,38 +17,41 @@ Package.onUse(function (api) {
api.addFiles('log.js', ['client', 'server']); api.addFiles('log.js', ['client', 'server']);
api.addFiles('compatibility/lesionTool.js', 'client', {bare: true}); api.addFiles('client/collections/LesionLocations.js', 'client');
api.addFiles('compatibility/nonTargetTool.js', 'client', {bare: true}); api.addFiles('client/collections/LocationResponses.js', 'client');
api.addFiles('compatibility/measurementManagerDAL.js', 'client', {bare: true});
api.addFiles('components/lesionLocationDialog/lesionLocationDialog.html', 'client'); api.addFiles('client/compatibility/lesionTool.js', 'client', {bare: true});
api.addFiles('components/lesionLocationDialog/lesionLocationDialog.js', 'client'); api.addFiles('client/compatibility/nonTargetTool.js', 'client', {bare: true});
api.addFiles('components/lesionLocationDialog/lesionLocationDialog.styl', 'client'); api.addFiles('client/compatibility/measurementManagerDAL.js', 'client', {bare: true});
api.addFiles('client/components/lesionLocationDialog/lesionLocationDialog.html', 'client');
api.addFiles('client/components/lesionLocationDialog/lesionLocationDialog.js', 'client');
api.addFiles('client/components/lesionLocationDialog/lesionLocationDialog.styl', 'client');
api.addFiles('components/lesionTable/lesionTable.html', 'client'); api.addFiles('client/components/lesionTable/lesionTable.html', 'client');
api.addFiles('components/lesionTable/lesionTable.styl', 'client'); api.addFiles('client/components/lesionTable/lesionTable.styl', 'client');
api.addFiles('components/lesionTable/lesionTable.js', 'client'); api.addFiles('client/components/lesionTable/lesionTable.js', 'client');
api.addFiles('components/lesionTableRow/lesionTableRow.html', 'client'); api.addFiles('client/components/lesionTableRow/lesionTableRow.html', 'client');
api.addFiles('components/lesionTableRow/lesionTableRow.js', 'client'); api.addFiles('client/components/lesionTableRow/lesionTableRow.js', 'client');
api.addFiles('components/lesionTableTimepointCell/lesionTableTimepointCell.html', 'client'); api.addFiles('client/components/lesionTableTimepointCell/lesionTableTimepointCell.html', 'client');
api.addFiles('components/lesionTableTimepointCell/lesionTableTimepointCell.js', 'client'); api.addFiles('client/components/lesionTableTimepointCell/lesionTableTimepointCell.js', 'client');
api.addFiles('components/lesionTableTimepointHeader/lesionTableTimepointHeader.html', 'client'); api.addFiles('client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.html', 'client');
api.addFiles('components/lesionTableTimepointHeader/lesionTableTimepointHeader.js', 'client'); api.addFiles('client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.js', 'client');
api.addFiles('components/lesionTableTimepointHeader/lesionTableTimepointHeader.styl', 'client'); api.addFiles('client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.styl', 'client');
api.addFiles('components/nonTargetLesionDialog/nonTargetLesionDialog.html', 'client'); api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.html', 'client');
api.addFiles('components/nonTargetLesionDialog/nonTargetLesionDialog.css', 'client'); api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.styl', 'client');
api.addFiles('components/nonTargetLesionDialog/nonTargetLesionDialog.js', 'client'); api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.js', 'client');
api.addFiles('components/studyDateList/studyDateList.html', 'client'); api.addFiles('client/components/studyDateList/studyDateList.html', 'client');
api.addFiles('components/studyDateList/studyDateList.styl', 'client'); api.addFiles('client/components/studyDateList/studyDateList.styl', 'client');
api.addFiles('components/studyDateList/studyDateList.js', 'client'); api.addFiles('client/components/studyDateList/studyDateList.js', 'client');
api.addFiles('components/timepointTextDialog/timepointTextDialog.html', 'client'); api.addFiles('client/components/timepointTextDialog/timepointTextDialog.html', 'client');
api.addFiles('components/timepointTextDialog/timepointTextDialog.styl', 'client'); api.addFiles('client/components/timepointTextDialog/timepointTextDialog.styl', 'client');
// Server functions // Server functions
api.addFiles('server/collections.js', 'server'); api.addFiles('server/collections.js', 'server');
@ -57,7 +60,6 @@ Package.onUse(function (api) {
// Both client and server functions // Both client and server functions
api.addFiles('both/collections.js', ['client', 'server']); api.addFiles('both/collections.js', ['client', 'server']);
// Library functions // Library functions
api.addFiles('lib/uuid.js', 'client'); api.addFiles('lib/uuid.js', 'client');
api.addFiles('lib/resizeViewportElements.js', 'client'); api.addFiles('lib/resizeViewportElements.js', 'client');
@ -66,7 +68,14 @@ Package.onUse(function (api) {
// Export lesionTable function for activate measurements // Export lesionTable function for activate measurements
api.export('activateLesion','client'); api.export('activateLesion','client');
api.export('resizeViewportElements','client'); api.export('resizeViewportElements','client');
api.export('measurementManagerDAL', 'client');
// Export client-side collections
api.export('LesionLocations', 'client');
api.export('LocationResponses', 'client');
api.export('PatientLocations', 'client');
// Export collections spanning both client and server
api.export('Measurements', ['client', 'server']); api.export('Measurements', ['client', 'server']);
api.export('Timepoints', ['client', 'server']); api.export('Timepoints', ['client', 'server']);
}); });

View File

@ -121,7 +121,7 @@ var savedSeriesData,
savedViewportColumns; savedViewportColumns;
Template.imageViewerViewports.events({ Template.imageViewerViewports.events({
'dblclick .imageViewerViewport': function(e) { 'CornerstoneMouseDoubleClick .imageViewerViewport': function(e) {
var container = $(".viewerMain").get(0); var container = $(".viewerMain").get(0);
var data; var data;
var contentId = this.contentId || $("#viewer").parents(".tab-pane.active").attr('id'); var contentId = this.contentId || $("#viewer").parents(".tab-pane.active").attr('id');
@ -140,7 +140,7 @@ Template.imageViewerViewports.events({
data = { data = {
viewportRows: ViewerData[contentId].viewportRows, viewportRows: ViewerData[contentId].viewportRows,
viewportColumns: ViewerData[contentId].viewportColumns, viewportColumns: ViewerData[contentId].viewportColumns
}; };
// Render the imageViewerViewports template with these settings // Render the imageViewerViewports template with these settings
@ -181,7 +181,6 @@ Template.imageViewerViewports.events({
// Add the 'zoomed' class to the lone remaining viewport // Add the 'zoomed' class to the lone remaining viewport
$('.imageViewerViewport').eq(0).addClass('zoomed'); $('.imageViewerViewport').eq(0).addClass('zoomed');
} }
} }
}); });

View File

@ -1,3 +1,3 @@
// Create package logger using loglevel // Create package logger using loglevel
// https://atmospherejs.com/spacejamio/loglevel // https://atmospherejs.com/spacejamio/loglevel
log = loglevel.createPackageLogger('viewerbase', defaultLevel = 'info'); log = loglevel.createPackageLogger('viewerbase', defaultLevel = 'warn');

View File

@ -4,7 +4,7 @@
{ {
"name": "Orthanc", "name": "Orthanc",
"wadoUriRootNOTE" : "either this uri is not correct for wado-uri or wado-uri is not configured on orthanc currently", "wadoUriRootNOTE" : "either this uri is not correct for wado-uri or wado-uri is not configured on orthanc currently",
"wadoUriRoot" : "http://localhost:8042/wado", "wadoUriRoot" : "http://localhost:8043/wado",
"qidoRoot": "http://localhost:8042/dicom-web", "qidoRoot": "http://localhost:8042/dicom-web",
"wadoRoot": "http://localhost:8042/dicom-web", "wadoRoot": "http://localhost:8042/dicom-web",
"qidoSupportsIncludeField": false, "qidoSupportsIncludeField": false,

20
etc/OrthancProxy.js Normal file
View File

@ -0,0 +1,20 @@
// This sets up a proxy to work around CORS.
// See http://chafey.blogspot.be/2014/09/working-around-cors.html
// For more information
var http = require('http'),
httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({
target: 'http://localhost:8042',
auth: 'orthanc:orthanc'
}).listen(8043);
proxy.on('proxyRes', function(proxyReq, req, res, options) {
// add the CORS header to the response
res.setHeader('Access-Control-Allow-Origin', '*');
});
proxy.on('error', function(e) {
// suppress errors
console.log(e);
});