}
+ */
+ timepointOptions: function() {
+ return [
+ {
+ value: 'baseline',
+ name: 'Baseline'
+ },
+ {
+ value: 'followup',
+ name: 'Follow-up'
+ }
+ ];
+ }
+});
+
+//trial criteria!
\ No newline at end of file
diff --git a/Packages/lesiontracker/client/components/studyAssociationTable/studyAssociationTable.styl b/Packages/lesiontracker/client/components/studyAssociationTable/studyAssociationTable.styl
new file mode 100644
index 000000000..b041bc84c
--- /dev/null
+++ b/Packages/lesiontracker/client/components/studyAssociationTable/studyAssociationTable.styl
@@ -0,0 +1,16 @@
+#studyAssociationTable
+ .header
+ text-align: center
+
+ .center
+ text-align: center
+
+ table
+ tbody
+ tr
+ td.timepointOptions
+ input
+ margin: 0 3px
+
+ label
+ padding: 0 5px
\ No newline at end of file
diff --git a/Packages/lesiontracker/lib/mathUtils.js b/Packages/lesiontracker/lib/mathUtils.js
index abda7f953..031027b39 100644
--- a/Packages/lesiontracker/lib/mathUtils.js
+++ b/Packages/lesiontracker/lib/mathUtils.js
@@ -30,9 +30,9 @@ getLineIntersection = function(point1, point2, point3, point4) {
* same side of line 1, the line segments do not intersect.
*/
- if (r3 != 0 &&
- r4 != 0 &&
- sign(r3) == sign(r4)) {
+ if (r3 !== 0 &&
+ r4 !== 0 &&
+ sign(r3) === sign(r4)) {
intersectionPoint.x = 0;
intersectionPoint.y = 0;
intersectionPoint.intersected = false;
@@ -55,9 +55,9 @@ getLineIntersection = function(point1, point2, point3, point4) {
* not intersect.
*/
- if (r1 != 0 &&
- r2 != 0 &&
- sign(r1) == sign(r2)) {
+ if (r1 !== 0 &&
+ r2 !== 0 &&
+ sign(r1) === sign(r2)) {
intersectionPoint.x = 0;
intersectionPoint.y = 0;
intersectionPoint.intersected = false;
@@ -103,7 +103,7 @@ getDistanceFromPointToLine = function(ptTest, pt1, pt2) {
var dy = pt2.y - pt1.y;
// It's a point, not a line
- if (dx == 0 && dy == 0) {
+ if (dx === 0 && dy === 0) {
ptNearest.x = pt1.x;
ptNearest.y = pt1.y;
} else {
diff --git a/Packages/lesiontracker/package.js b/Packages/lesiontracker/package.js
index 8d38631bd..7ab698516 100644
--- a/Packages/lesiontracker/package.js
+++ b/Packages/lesiontracker/package.js
@@ -36,6 +36,10 @@ Package.onUse(function(api) {
bare: true
});
+ api.addFiles('client/components/associationModal/associationModal.html', 'client');
+ api.addFiles('client/components/associationModal/associationModal.styl', 'client');
+ api.addFiles('client/components/associationModal/associationModal.js', 'client');
+
api.addFiles('client/components/lesionLocationDialog/lesionLocationDialog.html', 'client');
api.addFiles('client/components/lesionLocationDialog/lesionLocationDialog.js', 'client');
api.addFiles('client/components/lesionLocationDialog/lesionLocationDialog.styl', 'client');
@@ -63,6 +67,10 @@ Package.onUse(function(api) {
api.addFiles('client/components/studyDateList/studyDateList.styl', 'client');
api.addFiles('client/components/studyDateList/studyDateList.js', 'client');
+ api.addFiles('client/components/studyAssociationTable/studyAssociationTable.html', 'client');
+ api.addFiles('client/components/studyAssociationTable/studyAssociationTable.styl', 'client');
+ api.addFiles('client/components/studyAssociationTable/studyAssociationTable.js', 'client');
+
api.addFiles('client/components/confirmDeleteDialog/confirmDeleteDialog.html', 'client');
api.addFiles('client/components/confirmDeleteDialog/confirmDeleteDialog.styl', 'client');
api.addFiles('client/components/confirmDeleteDialog/confirmDeleteDialog.js', 'client');
diff --git a/Packages/worklist/components/studyContextMenu/studyContextMenu.html b/Packages/worklist/components/studyContextMenu/studyContextMenu.html
new file mode 100644
index 000000000..bd4255250
--- /dev/null
+++ b/Packages/worklist/components/studyContextMenu/studyContextMenu.html
@@ -0,0 +1,28 @@
+
+
+
\ No newline at end of file
diff --git a/Packages/worklist/components/studyContextMenu/studyContextMenu.js b/Packages/worklist/components/studyContextMenu/studyContextMenu.js
new file mode 100644
index 000000000..cbd417041
--- /dev/null
+++ b/Packages/worklist/components/studyContextMenu/studyContextMenu.js
@@ -0,0 +1,79 @@
+function closeHandler(dialog) {
+ // Hide the dialog
+ $(dialog).css('display', 'none');
+
+ // Remove the backdrop
+ $('.removableBackdrop').remove();
+}
+
+/**
+ * This function is used inside the Worklist package to define a right click callback
+ *
+ * @param e
+ * @param template
+ */
+openStudyContextMenu = function(e, template) {
+ var study = $(e.currentTarget);
+ Template.studyContextMenu.study = study;
+
+ var dialog = $('#studyContextMenu');
+
+ // Show the nonTargetLesion dialog above
+ var dialogProperty = {
+ 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;
+ } else {
+ dialogProperty.top = e.pageY;// - dialog.outerHeight() - 40;
+ dialogProperty.left = e.pageX;// - dialog.outerWidth() / 2;
+
+ var pageHeight = $(window).height();
+ dialogProperty.top = Math.max(dialogProperty.top, 0);
+ dialogProperty.top = Math.min(dialogProperty.top, pageHeight - dialog.outerHeight());
+
+ var pageWidth = $(window).width();
+ dialogProperty.left = Math.max(dialogProperty.left, 0);
+ dialogProperty.left = Math.min(dialogProperty.left, pageWidth - dialog.outerWidth());
+ }
+
+ dialog.css(dialogProperty);
+ dialog.focus();
+
+ log.info(e);
+ log.info(template);
+
+ // 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);
+ });
+};
+
+// Temporary for now
+functionList = {};
+
+Template.studyContextMenu.events({
+ 'click a': function(e) {
+ var study = Template.studyContextMenu.study;
+ var id = $(e.currentTarget).attr('id');
+
+ var fn = functionList[id];
+ if (fn && typeof(fn) === 'function') {
+ fn(study);
+ }
+
+ var dialog = $('#studyContextMenu');
+ closeHandler(dialog);
+ }
+});
diff --git a/Packages/worklist/components/studyContextMenu/studyContextMenu.styl b/Packages/worklist/components/studyContextMenu/studyContextMenu.styl
new file mode 100644
index 000000000..f3dfdc217
--- /dev/null
+++ b/Packages/worklist/components/studyContextMenu/studyContextMenu.styl
@@ -0,0 +1,51 @@
+.studyContextMenu
+ z-index: 10000
+ display: none
+ position: absolute
+ color: whitesmoke
+ background-color: #2d2d2d
+
+ width: -moz-fit-content
+ width: -webkit-fit-content
+ width: fit-content
+
+ height: -moz-fit-content
+ height: -webkit-fit-content
+ height: fit-content
+
+ min-width: 100px
+ font-size: 14px
+ text-align: left
+ -webkit-background-clip: padding-box
+ background-clip: padding-box
+ border: 1px solid rgba(0, 0, 0, .15)
+ border-radius: 4px
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175)
+ box-shadow: 0 6px 12px rgba(0, 0, 0, .175)
+
+ ul
+ width: 100%
+ margin: 5px 0
+ list-style: none
+ padding: 0
+
+ li
+ clear: both
+ white-space: nowrap
+
+ a
+ padding: 3px 15px
+ display: block
+ text-decoration: none
+ cursor: pointer
+ line-height: 25px
+ color: #808080
+
+ &:hover
+ color: #439193
+ text-decoration: none
+ background-color: #1d1d1d
+
+ .fa-lg
+ width: 40px
+ text-align: center
\ No newline at end of file
diff --git a/Packages/worklist/components/worklist.html b/Packages/worklist/components/worklist.html
index 2de587ab9..09c8ae6a1 100644
--- a/Packages/worklist/components/worklist.html
+++ b/Packages/worklist/components/worklist.html
@@ -17,4 +17,8 @@
{{ >tabContent }}
{{ /each }}
+ {{#each additionalTemplates}}
+ {{> UI.dynamic template=this}}
+ {{/each}}
+ {{ >studyContextMenu }}
\ No newline at end of file
diff --git a/Packages/worklist/components/worklist.js b/Packages/worklist/components/worklist.js
index 6730a8a62..1f717c387 100644
--- a/Packages/worklist/components/worklist.js
+++ b/Packages/worklist/components/worklist.js
@@ -65,17 +65,17 @@ getStudyMetadata = function(studyInstanceUid, doneCallback) {
* @param contentId The unique ID of the tab to be switched to
*/
switchToTab = function(contentId) {
- log.info("Switching to tab: " + contentId);
+ log.info('Switching to tab: ' + contentId);
// Use Bootstrap's Tab JavaScript to show the contents of the current tab
// Unless it is the worklist, it is currently an empty div
$('.tabTitle a[data-target="#' + contentId + '"]').tab('show');
// Remove any previous Viewers from the DOM
- $("#viewer").remove();
+ $('#viewer').remove();
// Update the 'activeContentId' variable in Session
- Session.set("activeContentId", contentId);
+ Session.set('activeContentId', contentId);
// If we are switching to the Worklist tab, reset any CSS styles
// that have been applied to prevent scrolling in the Viewer.
@@ -90,7 +90,7 @@ switchToTab = function(contentId) {
// Get tab content container given the contentId string
// If no such container exists, stop here because something is wrong
- var container = $('.tab-content').find("#" + contentId).get(0);
+ var container = $('.tab-content').find('#' + contentId).get(0);
if (!container) {
log.warn('No container present with the contentId: ' + contentId);
return;
@@ -109,7 +109,7 @@ switchToTab = function(contentId) {
viewportRows: ViewerData[contentId].viewportRows,
viewportColumns: ViewerData[contentId].viewportColumns,
contentId: contentId,
- studies: [study]
+ studies: [ study ]
};
if (ViewerData[contentId].studies && ViewerData[contentId].studies.length) {
@@ -117,18 +117,18 @@ switchToTab = function(contentId) {
}
// Remove the loading text template that is inside the tab container by default
- container.innerHTML = "";
+ container.innerHTML = '';
// Use Blaze to render the Viewer Template into the container
UI.renderWithData(Template.viewer, data, container);
// Retrieve the DOM element of the viewer
- var imageViewer = $("#viewer");
+ var imageViewer = $('#viewer');
// If it is present in the DOM (it should be), then apply
// styles to prevent page scrolling and overscrolling on mobile devices
if (imageViewer) {
- document.body.style.overflow = "hidden";
+ document.body.style.overflow = 'hidden';
document.body.style.height = '100%';
document.body.style.width = '100%';
document.body.style.minWidth = 0;
@@ -171,21 +171,20 @@ openNewTab = function(studyInstanceUid, title) {
Template.worklist.onRendered(function() {
// If there is a tab set as active in the Session,
// switch to that now.
- var contentId = Session.get("activeContentId");
+ var contentId = Session.get('activeContentId');
if (contentId) {
switchToTab(contentId);
}
});
-
Template.worklist.helpers({
/**
* Returns the current set of Worklist Tabs
* @returns Meteor.Collection The current state of the WorklistTabs Collection
*/
- 'worklistTabs': function() {
+ worklistTabs: function() {
return WorklistTabs.find();
- }
+ },
});
Template.worklist.events({
@@ -193,12 +192,12 @@ Template.worklist.events({
// If this tab is already active, do nothing
var tabButton = $(e.currentTarget);
var tabTitle = tabButton.parents('.tabTitle');
- if (tabTitle.hasClass("active")) {
+ if (tabTitle.hasClass('active')) {
return;
}
// Otherwise, switch to the tab
- var contentId = tabButton.data('target').replace("#", "");
+ var contentId = tabButton.data('target').replace('#', '');
switchToTab(contentId);
}
-});
\ No newline at end of file
+});
diff --git a/Packages/worklist/components/worklistResult/worklistResult.js b/Packages/worklist/components/worklistResult/worklistResult.js
index a4813b89b..c9dffda46 100644
--- a/Packages/worklist/components/worklistResult/worklistResult.js
+++ b/Packages/worklist/components/worklistResult/worklistResult.js
@@ -7,8 +7,13 @@ Template.worklistResult.helpers({
* Returns a sorted instance of the Studies Collection
* by Patient name and Study Date in Ascending order.
*/
- studies : function() {
- return WorklistStudies.find({}, {sort: {patientName : 1, studyDate : 1}});
+ studies: function() {
+ return WorklistStudies.find({}, {
+ sort: {
+ patientName: 1,
+ studyDate: 1
+ }
+ });
},
isTouchDevice: function() {
@@ -33,9 +38,10 @@ var filter;
* @returns {*}
*/
function getFilter(filter) {
- if(filter && filter.length && filter.substr(filter.length - 1) !== '*') {
+ if (filter && filter.length && filter.substr(filter.length - 1) !== '*') {
filter += '*';
}
+
return filter;
}
@@ -46,6 +52,7 @@ function isIndexOf(mainVal, searchVal) {
if (mainVal === undefined || mainVal === '' || mainVal.indexOf(searchVal) > -1){
return true;
}
+
return false;
}
@@ -53,8 +60,8 @@ function isIndexOf(mainVal, searchVal) {
* Replace object if undefined
*/
function replaceUndefinedColumnValue (text) {
- if (text == undefined || text === "undefined") {
- return "";
+ if (text == undefined || text === 'undefined') {
+ return '';
} else {
return text;
}
@@ -67,7 +74,7 @@ function convertStringToStudyDate (dateStr) {
var y = dateStr.substring(0,4);
var m = dateStr.substring(4,6);
var d = dateStr.substring(6,8);
- var newDateStr = y+"/"+m+"/"+d;
+ var newDateStr = y + '/' + m + '/' + d;
return new Date(newDateStr);
}
@@ -99,7 +106,7 @@ function search() {
studies.forEach(function(study) {
// Search the rest of the parameters that aren't done via the server call
- if(isIndexOf(study.modalities, modality) &&
+ if (isIndexOf(study.modalities, modality) &&
(new Date(studyDateFrom).setHours(0,0,0,0) <= convertStringToStudyDate(study.studyDate) || !checkFrom) &&
(convertStringToStudyDate(study.studyDate) <= new Date(studyDateTo).setHours(0,0,0,0) || !checkTo)) {
@@ -111,13 +118,12 @@ function search() {
}
Template.worklistResult.events({
- 'keydown': function(event) {
+ 'keydown input': function(event) {
if (event.keyCode === 13) { // Enter
search();
}
},
- 'onsearch': function(event) {
+ 'onsearch input': function(event) {
search();
-
}
-});
\ No newline at end of file
+});
diff --git a/Packages/worklist/components/worklistResult/worklistResult.styl b/Packages/worklist/components/worklistResult/worklistResult.styl
index f2eb3e4fe..abbdd1589 100644
--- a/Packages/worklist/components/worklistResult/worklistResult.styl
+++ b/Packages/worklist/components/worklistResult/worklistResult.styl
@@ -10,19 +10,24 @@ table#tblStudyList
&:hover
color: #888888
- tbody > tr
- padding: 4px
- border: 1px solid #282828
- background-color: black
+ tbody
+ tr
+ padding: 4px
+ border: 1px solid #282828
+ background-color: black
- td
- white-space:nowrap
+ &:hover, &:active, &.active
+ background-color: #009BD2
+ color: white
- &:nth-of-type(odd)
- background-color: #202020
+ td
+ // This selector is necessary to override bootstrap's 'table' class
+ color: white
+ background-color: #009BD2
- &:hover
- background-color: #009BD2
- color: white
+ &:nth-of-type(odd)
+ background-color: #202020
+ td
+ white-space: nowrap
diff --git a/Packages/worklist/components/worklistStudy/worklistStudy.html b/Packages/worklist/components/worklistStudy/worklistStudy.html
index cf2bc2592..f767ca4b0 100644
--- a/Packages/worklist/components/worklistStudy/worklistStudy.html
+++ b/Packages/worklist/components/worklistStudy/worklistStudy.html
@@ -7,18 +7,17 @@
{{patientId}}
{{#unless isTouchDevice}}
- |
- {{accessionNumber}}
- |
+
+ {{accessionNumber}}
+ |
{{/unless}}
-
{{formatDA studyDate}}
|
{{#unless isTouchDevice}}
-
- {{modalities}}
- |
+
+ {{modalities}}
+ |
{{/unless}}
{{studyDescription}}
diff --git a/Packages/worklist/components/worklistStudy/worklistStudy.js b/Packages/worklist/components/worklistStudy/worklistStudy.js
index f1d51b4b1..6d2b20c2a 100644
--- a/Packages/worklist/components/worklistStudy/worklistStudy.js
+++ b/Packages/worklist/components/worklistStudy/worklistStudy.js
@@ -1,8 +1,129 @@
+Worklist = {};
+Worklist.previouslySelected = undefined;
+
+// Maybe we should use regular Worklist collection?
+WorklistSelectedStudies = new Meteor.Collection(null);
+
+function handleShiftClick(studyRow, data) {
+ log.info('shiftKey');
+ var studyInstanceUid = studyRow.attr('studyInstanceUid');
+
+ // Select all rows in between these two rows
+ if (Worklist.previouslySelected) {
+ var previous = $(Worklist.previouslySelected);
+ var rowsInBetween;
+ if (previous.index() < studyRow.index()) {
+ // The previously selected row is above (lower index) the
+ // currently selected row.
+
+ // Fill in the rows upwards from the previously selected row
+ rowsInBetween = previous.nextAll('tr');
+ } else if (previous.index() > studyRow.index()) {
+ // The previously selected row is below the currently
+ // selected row.
+
+ // Fill in the rows upwards from the previously selected row
+ rowsInBetween = previous.prevAll('tr');
+ } else {
+ // The rows are the same, deselect the current row.
+ // TODO: CHECK THIS, pretty sure this is the wrong behaviour
+ WorklistSelectedStudies.remove({});
+ Worklist.previouslySelected = undefined;
+ return;
+ }
+
+ // Loop through the rows in between current and previous selected studies
+ rowsInBetween.each(function(index, row) {
+ if ($(row).is(studyRow)) {
+ // When we reach the currently clicked-on row, stop
+ return false;
+ } else if ($(row).hasClass('active')) {
+ // If we find one that is already selected, do nothing
+ return;
+ }
+
+ // Get the relevant studyInstanceUid
+ var studyInstanceUid = $(row).attr('studyInstanceUid');
+
+ // Set the current study as selected
+ WorklistSelectedStudies.insert(data);
+ studyRow.addClass('active');
+
+ return true;
+ });
+ } else {
+ // Set the current study as selected
+ WorklistSelectedStudies.insert(data);
+ studyRow.addClass('active');
+ }
+}
+
+function handleCtrlClick(studyRow, data) {
+ log.info('ctrlKey');
+ var studyInstanceUid = studyRow.attr('studyInstanceUid');
+
+ if (studyRow.hasClass('active')) {
+ studyRow.removeClass('active');
+
+ // Find the current studyInstanceUid in the stored list and remove it
+ WorklistSelectedStudies.remove({
+ studyInstanceUid: data.studyInstanceUid
+ });
+ } else {
+ // Set the current study as selected
+ WorklistSelectedStudies.insert(data);
+ studyRow.addClass('active');
+
+ // Set this as the previously selected row, so the user can
+ // use Shift to select from this point onwards
+ Worklist.previouslySelected = studyRow;
+ }
+}
+
Template.worklistStudy.events({
- 'click': function () {
+ 'click tr.worklistStudy': function(e) {
+ var studyRow = $(e.currentTarget);
+ var data = this;
+
+ // Remove the ID so we can directly insert this into our client-side collection
+ delete data._id;
+
+ if (e.shiftKey) {
+ handleShiftClick(studyRow, data);
+ } else if (e.ctrlKey || e.metaKey) {
+ handleCtrlClick(studyRow, data);
+ } else {
+ // Select a single study
+ log.info('Regular click');
+
+ // Clear all selected studies
+ WorklistSelectedStudies.remove({});
+ $('tr.worklistStudy').removeClass('active');
+
+ // Set the previous study to the currently clicked-on study
+ Worklist.previouslySelected = studyRow;
+
+ // Set the current study as selected
+ WorklistSelectedStudies.insert(data);
+ studyRow.addClass('active');
+ }
+ },
+ 'dblclick tr.worklistStudy': function() {
// Use the formatPN template helper to clean up the patient name
var title = Blaze._globalHelpers['formatPN'](this.patientName);
+
+ // Open a new tab with this study
openNewTab(this.studyInstanceUid, title);
+ },
+ 'contextmenu tr.worklistStudy': function(e, template) {
+ $(e.currentTarget).addClass('active');
+
+ if (openStudyContextMenu && typeof openStudyContextMenu === 'function') {
+ e.preventDefault();
+
+ openStudyContextMenu(e, template);
+ return false;
+ }
}
});
@@ -10,4 +131,4 @@ Template.worklistStudy.helpers({
isTouchDevice: function() {
return isTouchDevice();
}
-});
\ No newline at end of file
+});
diff --git a/Packages/worklist/package.js b/Packages/worklist/package.js
index 10eecb5d1..799224e4c 100644
--- a/Packages/worklist/package.js
+++ b/Packages/worklist/package.js
@@ -42,6 +42,10 @@ Package.onUse(function (api) {
api.addFiles('components/worklistResult/worklistResult.js', 'client');
api.addFiles('components/worklistResult/worklistResult.styl', 'client');
+ api.addFiles('components/studyContextMenu/studyContextMenu.html', 'client');
+ api.addFiles('components/studyContextMenu/studyContextMenu.js', 'client');
+ api.addFiles('components/studyContextMenu/studyContextMenu.styl', 'client');
+
api.addFiles('lib/generateUUID.js', 'client');
api.export('generateUUID', 'client');
@@ -56,5 +60,6 @@ Package.onUse(function (api) {
// Export the Collections
api.export('WorklistTabs', 'client');
api.export('WorklistStudies', 'client');
+ api.export('WorklistSelectedStudies', 'client');
});
|