Add some WIP changes to Lesion Tracker dialogs
This commit is contained in:
parent
e4c24608af
commit
da197368bf
@ -51,12 +51,6 @@ Template.viewer.onCreated(function() {
|
||||
}
|
||||
OHIF.viewer.isPlaying[viewportIndex] = !OHIF.viewer.isPlaying[viewportIndex];
|
||||
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;
|
||||
Session.set('patientId', patientId);
|
||||
|
||||
initialized = false;
|
||||
self.autorun(function() {
|
||||
var patientId = Session.get('patientId');
|
||||
self.subscribe('timepoints', patientId);
|
||||
@ -113,38 +106,35 @@ Template.viewer.onCreated(function() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (initialized === true) {
|
||||
return;
|
||||
}
|
||||
ViewerStudies.find().observe({
|
||||
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 timepoint = Timepoints.findOne({timepointName: study.studyDate});
|
||||
if (timepoint) {
|
||||
log.warn("A timepoint with that study date already exists!");
|
||||
return;
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
// Cornerstone ToolData structure
|
||||
Measurements.find().observe({
|
||||
added: function (data) {
|
||||
|
||||
if (data.toolDataInsertedManually === true) {
|
||||
|
||||
// Activate first measurements in image box as default if exists
|
||||
@ -156,19 +146,40 @@ Template.viewer.onCreated(function() {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log.info('Measurement added');
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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) {
|
||||
// Check what toolType we should be adding this to, based on the isTarget value
|
||||
// of the stored Measurement
|
||||
@ -184,10 +195,41 @@ function addMeasurementAsToolData(data) {
|
||||
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]) {
|
||||
toolState[imageId][toolType] = {
|
||||
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
|
||||
@ -204,6 +246,7 @@ function addMeasurementAsToolData(data) {
|
||||
measurementData.visible = data.visible;
|
||||
measurementData.active = data.active;
|
||||
measurementData.uid = data.uid;
|
||||
measurementData.id = data._id;
|
||||
|
||||
toolState[imageId][toolType].data.push(measurementData);
|
||||
});
|
||||
@ -215,6 +258,46 @@ Template.viewer.onDestroyed(function() {
|
||||
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
|
||||
// because it is fired very often.
|
||||
// Resizing is therefore performed 100 ms after the resize event stops.
|
||||
|
||||
108
Packages/lesiontracker/client/collections/LesionLocations.js
Normal file
108
Packages/lesiontracker/client/collections/LesionLocations.js
Normal 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: ""
|
||||
});
|
||||
@ -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: ""
|
||||
});
|
||||
@ -71,7 +71,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
|
||||
cornerstoneTools.removeToolState(element, toolType, measurementData);
|
||||
} else {
|
||||
// Set lesionMeasurementData Session
|
||||
config.getLesionLocationCallback(measurementData, mouseEventData);
|
||||
config.getLesionLocationCallback(measurementData, mouseEventData, doneCallback);
|
||||
}
|
||||
|
||||
$(element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools.lesion.mouseMoveCallback);
|
||||
@ -157,7 +157,8 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
|
||||
pointNearHandle: pointNearTextBox,
|
||||
active: false,
|
||||
movesIndependently: true,
|
||||
drawnIndependently: true
|
||||
drawnIndependently: true,
|
||||
allowedOutsideImage: true
|
||||
}
|
||||
},
|
||||
imageId: imageId,
|
||||
@ -282,10 +283,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
|
||||
// Set measurement text to show lesion table
|
||||
data.measurementText = length.toFixed(1);
|
||||
|
||||
|
||||
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
|
||||
cornerstoneTools.lesion = cornerstoneTools.mouseButtonTool({
|
||||
@ -309,6 +358,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
|
||||
addNewMeasurement: addNewMeasurement,
|
||||
onImageRendered: onImageRendered,
|
||||
pointNearTool: pointNearTool,
|
||||
mouseDoubleClickCallback: doubleClickCallback,
|
||||
toolType: toolType
|
||||
});
|
||||
|
||||
@ -46,12 +46,9 @@ var measurementManagerDAL = (function() {
|
||||
lesionDataObject.timepoints = timepointsObject;
|
||||
|
||||
// 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.toolDataInsertedManually = true;
|
||||
|
||||
Measurements.insert(lesionDataObject);
|
||||
lesionDataObject.id = Measurements.insert(lesionDataObject);
|
||||
}
|
||||
|
||||
// Update timepoint data in Measurements collection
|
||||
@ -82,6 +79,7 @@ var measurementManagerDAL = (function() {
|
||||
timepoints[timepointID].studyInstanceUid = lesionData.studyInstanceUid;
|
||||
timepoints[timepointID].handles = lesionData.handles;
|
||||
|
||||
lesionData.id = measurement._id;
|
||||
|
||||
Measurements.update(measurement._id, {
|
||||
$set: {
|
||||
@ -133,7 +131,7 @@ var measurementManagerDAL = (function() {
|
||||
|
||||
if (!timepoints[timepointID]) {
|
||||
// Find lesion number for this timepointID
|
||||
return measurement.lesionNumber
|
||||
return measurement.lesionNumber;
|
||||
}
|
||||
|
||||
if (timepoints[timepointID].longestDiameter === '') {
|
||||
@ -118,7 +118,8 @@
|
||||
pointNearHandle: pointNearTextBox,
|
||||
active: false,
|
||||
movesIndependently: true,
|
||||
drawnIndependently: true
|
||||
drawnIndependently: true,
|
||||
allowedOutsideImage: true
|
||||
}
|
||||
},
|
||||
imageId: imageId,
|
||||
@ -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>
|
||||
@ -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();
|
||||
}
|
||||
});
|
||||
@ -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
|
||||
@ -41,6 +41,11 @@ activateLesion = function(measurementId, templateData) {
|
||||
$(".imageViewerViewport").each(function(viewportIndex, element) {
|
||||
// Stop if we run out of timepoints before viewports
|
||||
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;
|
||||
}
|
||||
|
||||
@ -57,6 +62,10 @@ activateLesion = function(measurementId, templateData) {
|
||||
|
||||
// If there is no measurement data to display, stop here
|
||||
if (!measurementAtTimepoint) {
|
||||
// Update the element anyway, to remove any other highlights that are present
|
||||
deactivateAllToolData(element, 'lesion');
|
||||
deactivateAllToolData(element, 'nonTarget');
|
||||
cornerstone.updateImage(element);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -247,7 +256,6 @@ Template.lesionTable.events({
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Template.lesionTable.onCreated(function(){
|
||||
@ -275,14 +283,13 @@ Tracker.autorun(function () {
|
||||
dates: []
|
||||
};
|
||||
|
||||
$("#"+contentId+" .imageViewerViewport").each(function(viewportIndex, element) {
|
||||
$(".imageViewerViewport").each(function(viewportIndex, element) {
|
||||
var enabledElement = cornerstone.getEnabledElement(element);
|
||||
if(enabledElement && enabledElement.image){
|
||||
var imageId = enabledElement.image.imageId;
|
||||
var study = cornerstoneTools.metaData.get('study', imageId);
|
||||
var studyDate = study.studyDate;
|
||||
var patientId = study.patientId;
|
||||
loadedStudyDates.patientId = patientId;
|
||||
loadedStudyDates.patientId = study.patientId;
|
||||
// Check studyDate is added before
|
||||
if (loadedStudyDates.dates.indexOf(studyDate) < 0) {
|
||||
loadedStudyDates.dates.push(studyDate);
|
||||
@ -306,7 +313,6 @@ Tracker.autorun(function () {
|
||||
$set: {
|
||||
timepointLoaded: timepointLoaded
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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>
|
||||
@ -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) {
|
||||
// Get the current element's timepointID from the study date metadata
|
||||
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
|
||||
// If there already exists a lesion with this specific lesion number,
|
||||
// related to the chosen location.
|
||||
|
||||
function getNonTargetLesionLocationCallback(measurementData, eventData) {
|
||||
Template.nonTargetLesionDialog.measurementData = measurementData;
|
||||
|
||||
// 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
|
||||
var selectorLocation = nonTargetlesionDialog.find("select#selectNonTargetLesionLocation");
|
||||
var selectorResponse = nonTargetlesionDialog.find("select#selectNonTargetLesionLocationResponse");
|
||||
var selectorLocation = dialog.find("select#selectNonTargetLesionLocation");
|
||||
var selectorResponse = dialog.find("select#selectNonTargetLesionLocationResponse");
|
||||
|
||||
// OK button
|
||||
var btnOK = nonTargetlesionDialog.find("button#nonTargetLesionOK");
|
||||
selectorLocation.find("option:first").prop("selected", "selected");
|
||||
selectorResponse.find("option:first").prop("selected", "selected");
|
||||
|
||||
|
||||
// If selector location is disabled, make it enable
|
||||
enableLocationSelection();
|
||||
// Allow location selection
|
||||
selectorLocation.removeAttr("disabled");
|
||||
|
||||
// Find out if this lesion number is already added in the lesion manager for another timepoint
|
||||
// If it is, disable selector location
|
||||
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;
|
||||
// Disable the selection of a new location
|
||||
disableLocationSelection(measurementData.locationUID);
|
||||
@ -58,48 +81,16 @@ function getNonTargetLesionLocationCallback(measurementData, eventData) {
|
||||
// Disable selector location to prevent selecting a new location
|
||||
function disableLocationSelection(locationUID) {
|
||||
var locationName = measurementManagerDAL.getLocationName(locationUID);
|
||||
selectorLocation.find('option').each(function()
|
||||
{
|
||||
selectorLocation.find('option').each(function() {
|
||||
if ($(this).text() === locationName) {
|
||||
// Select location in locations dropdown list
|
||||
selectorLocation.find('option').eq($(this).index()).prop("selected", true);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
var dialogProperty = {
|
||||
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 (isTouchDevice()) {
|
||||
// add dialogMobile class to provide a black,transparent background
|
||||
$(nonTargetlesionDialog).addClass("dialogMobile");
|
||||
dialog.addClass("dialogMobile");
|
||||
dialogProperty.top = 0;
|
||||
dialogProperty.left = 0;
|
||||
dialogProperty.right = 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
|
||||
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)
|
||||
// then stop here
|
||||
@ -141,7 +145,7 @@ function getNonTargetLesionLocationCallback(measurementData, eventData) {
|
||||
|
||||
// If the selected response option is still the default (-1)
|
||||
// then stop here
|
||||
if (responseOptionCode < 0) {
|
||||
if (responseOptionId < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -157,102 +161,61 @@ function getNonTargetLesionLocationCallback(measurementData, eventData) {
|
||||
id = PatientLocations.insert({location: locationObj.location});
|
||||
}
|
||||
|
||||
// Link locationUID with active lesion measurementData
|
||||
measurementData.locationUID = id;
|
||||
if (!measurementData.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
|
||||
measurementData.isTarget = false;
|
||||
// Link locationUID with active lesion measurementData
|
||||
measurementData.locationUID = id;
|
||||
|
||||
// measurementText is set from location response list
|
||||
measurementData.measurementText = responseOptionCode;
|
||||
/// Set the isTarget value to true, since this is the target-lesion dialog callback
|
||||
measurementData.isTarget = false;
|
||||
|
||||
// Adds lesion data to timepoints array
|
||||
measurementManagerDAL.addLesionData(measurementData);
|
||||
// measurementText is set from location response list
|
||||
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
|
||||
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) {
|
||||
doneCallback(prompt('Change your lesion location:'));
|
||||
}
|
||||
if (doneCallback && typeof doneCallback === 'function') {
|
||||
var deleteTool = true;
|
||||
doneCallback(measurementData, deleteTool);
|
||||
}
|
||||
|
||||
var config = {
|
||||
setLesionNumberCallback: setLesionNumberCallback,
|
||||
getNonTargetLesionLocationCallback: getNonTargetLesionLocationCallback,
|
||||
changeNonTargetLesionLocationCallback: changeNonTargetLesionLocationCallback
|
||||
};
|
||||
closeHandler(dialog);
|
||||
},
|
||||
'click #btnCloseLesionPopup': function() {
|
||||
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({
|
||||
'lesionLocations': function() {
|
||||
return LesionLocations.find();
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
@ -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();
|
||||
}
|
||||
});
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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>
|
||||
@ -17,38 +17,41 @@ Package.onUse(function (api) {
|
||||
|
||||
api.addFiles('log.js', ['client', 'server']);
|
||||
|
||||
api.addFiles('compatibility/lesionTool.js', 'client', {bare: true});
|
||||
api.addFiles('compatibility/nonTargetTool.js', 'client', {bare: true});
|
||||
api.addFiles('compatibility/measurementManagerDAL.js', 'client', {bare: true});
|
||||
api.addFiles('client/collections/LesionLocations.js', 'client');
|
||||
api.addFiles('client/collections/LocationResponses.js', 'client');
|
||||
|
||||
api.addFiles('components/lesionLocationDialog/lesionLocationDialog.html', 'client');
|
||||
api.addFiles('components/lesionLocationDialog/lesionLocationDialog.js', 'client');
|
||||
api.addFiles('components/lesionLocationDialog/lesionLocationDialog.styl', 'client');
|
||||
api.addFiles('client/compatibility/lesionTool.js', 'client', {bare: true});
|
||||
api.addFiles('client/compatibility/nonTargetTool.js', 'client', {bare: true});
|
||||
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('components/lesionTable/lesionTable.styl', 'client');
|
||||
api.addFiles('components/lesionTable/lesionTable.js', 'client');
|
||||
api.addFiles('client/components/lesionTable/lesionTable.html', 'client');
|
||||
api.addFiles('client/components/lesionTable/lesionTable.styl', 'client');
|
||||
api.addFiles('client/components/lesionTable/lesionTable.js', 'client');
|
||||
|
||||
api.addFiles('components/lesionTableRow/lesionTableRow.html', 'client');
|
||||
api.addFiles('components/lesionTableRow/lesionTableRow.js', 'client');
|
||||
api.addFiles('client/components/lesionTableRow/lesionTableRow.html', 'client');
|
||||
api.addFiles('client/components/lesionTableRow/lesionTableRow.js', 'client');
|
||||
|
||||
api.addFiles('components/lesionTableTimepointCell/lesionTableTimepointCell.html', 'client');
|
||||
api.addFiles('components/lesionTableTimepointCell/lesionTableTimepointCell.js', 'client');
|
||||
api.addFiles('client/components/lesionTableTimepointCell/lesionTableTimepointCell.html', 'client');
|
||||
api.addFiles('client/components/lesionTableTimepointCell/lesionTableTimepointCell.js', 'client');
|
||||
|
||||
api.addFiles('components/lesionTableTimepointHeader/lesionTableTimepointHeader.html', 'client');
|
||||
api.addFiles('components/lesionTableTimepointHeader/lesionTableTimepointHeader.js', 'client');
|
||||
api.addFiles('components/lesionTableTimepointHeader/lesionTableTimepointHeader.styl', 'client');
|
||||
api.addFiles('client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.html', 'client');
|
||||
api.addFiles('client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.js', 'client');
|
||||
api.addFiles('client/components/lesionTableTimepointHeader/lesionTableTimepointHeader.styl', 'client');
|
||||
|
||||
api.addFiles('components/nonTargetLesionDialog/nonTargetLesionDialog.html', 'client');
|
||||
api.addFiles('components/nonTargetLesionDialog/nonTargetLesionDialog.css', 'client');
|
||||
api.addFiles('components/nonTargetLesionDialog/nonTargetLesionDialog.js', 'client');
|
||||
api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.html', 'client');
|
||||
api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.styl', 'client');
|
||||
api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.js', 'client');
|
||||
|
||||
api.addFiles('components/studyDateList/studyDateList.html', 'client');
|
||||
api.addFiles('components/studyDateList/studyDateList.styl', 'client');
|
||||
api.addFiles('components/studyDateList/studyDateList.js', 'client');
|
||||
api.addFiles('client/components/studyDateList/studyDateList.html', 'client');
|
||||
api.addFiles('client/components/studyDateList/studyDateList.styl', 'client');
|
||||
api.addFiles('client/components/studyDateList/studyDateList.js', 'client');
|
||||
|
||||
api.addFiles('components/timepointTextDialog/timepointTextDialog.html', 'client');
|
||||
api.addFiles('components/timepointTextDialog/timepointTextDialog.styl', 'client');
|
||||
api.addFiles('client/components/timepointTextDialog/timepointTextDialog.html', 'client');
|
||||
api.addFiles('client/components/timepointTextDialog/timepointTextDialog.styl', 'client');
|
||||
|
||||
// Server functions
|
||||
api.addFiles('server/collections.js', 'server');
|
||||
@ -57,7 +60,6 @@ Package.onUse(function (api) {
|
||||
// Both client and server functions
|
||||
api.addFiles('both/collections.js', ['client', 'server']);
|
||||
|
||||
|
||||
// Library functions
|
||||
api.addFiles('lib/uuid.js', 'client');
|
||||
api.addFiles('lib/resizeViewportElements.js', 'client');
|
||||
@ -66,7 +68,14 @@ Package.onUse(function (api) {
|
||||
// Export lesionTable function for activate measurements
|
||||
api.export('activateLesion','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('Timepoints', ['client', 'server']);
|
||||
});
|
||||
@ -121,7 +121,7 @@ var savedSeriesData,
|
||||
savedViewportColumns;
|
||||
|
||||
Template.imageViewerViewports.events({
|
||||
'dblclick .imageViewerViewport': function(e) {
|
||||
'CornerstoneMouseDoubleClick .imageViewerViewport': function(e) {
|
||||
var container = $(".viewerMain").get(0);
|
||||
var data;
|
||||
var contentId = this.contentId || $("#viewer").parents(".tab-pane.active").attr('id');
|
||||
@ -140,7 +140,7 @@ Template.imageViewerViewports.events({
|
||||
|
||||
data = {
|
||||
viewportRows: ViewerData[contentId].viewportRows,
|
||||
viewportColumns: ViewerData[contentId].viewportColumns,
|
||||
viewportColumns: ViewerData[contentId].viewportColumns
|
||||
};
|
||||
|
||||
// Render the imageViewerViewports template with these settings
|
||||
@ -181,7 +181,6 @@ Template.imageViewerViewports.events({
|
||||
|
||||
// Add the 'zoomed' class to the lone remaining viewport
|
||||
$('.imageViewerViewport').eq(0).addClass('zoomed');
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1,3 +1,3 @@
|
||||
// Create package logger using loglevel
|
||||
// https://atmospherejs.com/spacejamio/loglevel
|
||||
log = loglevel.createPackageLogger('viewerbase', defaultLevel = 'info');
|
||||
log = loglevel.createPackageLogger('viewerbase', defaultLevel = 'warn');
|
||||
@ -4,7 +4,7 @@
|
||||
{
|
||||
"name": "Orthanc",
|
||||
"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",
|
||||
"wadoRoot": "http://localhost:8042/dicom-web",
|
||||
"qidoSupportsIncludeField": false,
|
||||
|
||||
20
etc/OrthancProxy.js
Normal file
20
etc/OrthancProxy.js
Normal 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);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user