Updates for Timepoint assocation (LT-60) and Conformance Checks (LT-92, LT-93, LT-94)
This commit is contained in:
1 parent
6c4eae3699
commit
beaa56c0b3
144 files changed
+5050
-2416
No files matched your search
@@ -0,0 +1,21 @@
|
||||
<template name="studyContextMenu">
|
||||
<div id="studyContextMenu" class="studyContextMenu noselect"
|
||||
oncontextmenu='return false;'
|
||||
unselectable='on'
|
||||
onselectstart='return false;'>
|
||||
<ul>
|
||||
<li>
|
||||
<a class="disabled"><span class="fa-stack fa-lg">
|
||||
<i class="fa fa-user fa-stack-1x"></i>
|
||||
<i class="fa fa-ban fa-stack-2x text-danger"></i>
|
||||
</span> Anonymize
|
||||
</a>
|
||||
<a class="disabled"><i class="fa fa-trash fa-lg"></i> Delete</a>
|
||||
<a class="disabled"><i class="fa fa-send-o fa-lg"></i> Send</a>
|
||||
<a class="disabled"><i class="fa fa-exchange fa-lg"></i> Export</a>
|
||||
<a class="disabled"><i class="fa fa-download fa-lg"></i> Download</a>
|
||||
<a class="disabled"><i class="fa fa-photo fa-lg"></i> View Series Details</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,71 @@
|
||||
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
|
||||
*/
|
||||
openStudyContextMenu = function(e) {
|
||||
Template.studyContextMenu.study = $(e.currentTarget);
|
||||
|
||||
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// jscs:ignore
|
||||
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;
|
||||
dialogProperty.left = e.pageX;
|
||||
|
||||
var pageHeight = $(document.body).height();
|
||||
dialogProperty.top = Math.max(dialogProperty.top, 0);
|
||||
dialogProperty.top = Math.min(dialogProperty.top, pageHeight - dialog.outerHeight());
|
||||
|
||||
var pageWidth = $(document.body).width();
|
||||
dialogProperty.left = Math.max(dialogProperty.left, 0);
|
||||
dialogProperty.left = Math.min(dialogProperty.left, pageWidth - dialog.outerWidth());
|
||||
}
|
||||
|
||||
dialog.css(dialogProperty);
|
||||
dialog.focus();
|
||||
|
||||
// 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);
|
||||
});
|
||||
};
|
||||
|
||||
Template.studyContextMenu.events({
|
||||
'click a': function(e) {
|
||||
var study = Template.studyContextMenu.study;
|
||||
var id = $(e.currentTarget).attr('id');
|
||||
|
||||
var fn = Worklist.functions[id];
|
||||
if (fn && typeof(fn) === 'function') {
|
||||
fn(study);
|
||||
}
|
||||
|
||||
var dialog = $('#studyContextMenu');
|
||||
closeHandler(dialog);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
.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
|
||||
|
||||
&:not(.disabled):hover
|
||||
color: #439193
|
||||
text-decoration: none
|
||||
background-color: #1d1d1d
|
||||
|
||||
&.disabled
|
||||
opacity: 0.4
|
||||
pointer-events: none
|
||||
cursor: default
|
||||
|
||||
.fa-lg
|
||||
width: 40px
|
||||
text-align: center
|
||||
@@ -0,0 +1,5 @@
|
||||
<template name="tabContent">
|
||||
<div role="tabpanel" class="tab-pane {{active}}" id="{{contentid}}">
|
||||
{{ >loadingText }}
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,4 @@
|
||||
.tab-pane
|
||||
.loadingTextDiv
|
||||
h5
|
||||
color: #777
|
||||
@@ -0,0 +1,8 @@
|
||||
<template name="tabTitle">
|
||||
<li role="presentation" class="tabTitle">
|
||||
<a data-target="#{{contentid}}" role="tab" data-toggle="tab">
|
||||
{{title}}
|
||||
<button class="close">×</button>
|
||||
</a>
|
||||
</li>
|
||||
</template>
|
||||
@@ -0,0 +1,48 @@
|
||||
Template.tabTitle.events({
|
||||
/**
|
||||
* Closes a tab when the close button is pressed in the title
|
||||
* The next tab to the left is loaded if the current tab is closed
|
||||
*
|
||||
* @param e The click event used to close the tab
|
||||
*/
|
||||
'click .close': function(e) {
|
||||
// Identify the tab title DOM node
|
||||
var tab = $(e.currentTarget).parents('a[data-toggle="tab"]').eq(0);
|
||||
|
||||
// Get the relevent contentId that this tab title represents
|
||||
// Replace any hash marks (#) that were required by Bootstrap's tab switching
|
||||
var contentId = tab.data('target').replace('#', '');
|
||||
|
||||
// Check if we are closing the active tab. If we are, prepare to switch
|
||||
// to the next tab to the left.
|
||||
var activeContentId = Session.get('activeContentId');
|
||||
if (activeContentId === contentId) {
|
||||
// Find the index of the tab that is being closed
|
||||
var tabIndex = tab.parent('li').index();
|
||||
|
||||
// Find the index the tab to its left
|
||||
var newActiveTabIndex = Math.max(tabIndex - 1, 0);
|
||||
|
||||
// Find the DOM node of the tab that will be activated
|
||||
var newActiveTab = $('.tabTitle').eq(newActiveTabIndex);
|
||||
|
||||
// Find the content ID of the tab that will be switched to
|
||||
var newActiveTabLink = newActiveTab.find('a[data-toggle=tab]');
|
||||
var newContentId = newActiveTabLink.data('target').replace('#', '');
|
||||
|
||||
// Switch to this tab
|
||||
switchToTab(newContentId);
|
||||
}
|
||||
|
||||
// Find the tab to be closed in the Tabs collection
|
||||
var tabObject = WorklistTabs.findOne({
|
||||
contentid: contentId
|
||||
});
|
||||
|
||||
// Remove this tab from the Tabs collection so it is no longer rendered
|
||||
WorklistTabs.remove(tabObject._id);
|
||||
|
||||
// Remove any stored data related to this tab from the global ViewerData structure
|
||||
delete ViewerData[contentId];
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
.tabTitle
|
||||
button.close
|
||||
// Prevent blue glow around button in Chrome
|
||||
outline: none
|
||||
@@ -0,0 +1,24 @@
|
||||
<template name="worklist">
|
||||
<ul class="nav noselect" role="tablist" id="tablist">
|
||||
<li role="presentation" class="tabTitle active">
|
||||
<a data-target="#worklistTab" role="tab" data-toggle="tab">Study List</a>
|
||||
</li>
|
||||
{{ #each worklistTabs }}
|
||||
{{ >tabTitle }}
|
||||
{{ /each }}
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="worklistTab">
|
||||
<div id="worklistContainer" class="container">
|
||||
{{> worklistResult }}
|
||||
</div>
|
||||
</div>
|
||||
{{ #each worklistTabs }}
|
||||
{{ >tabContent }}
|
||||
{{ /each }}
|
||||
</div>
|
||||
{{#each additionalTemplates}}
|
||||
{{> UI.dynamic template=this}}
|
||||
{{/each}}
|
||||
{{ >studyContextMenu }}
|
||||
</template>
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Template: Worklist
|
||||
*
|
||||
* This is the main component of the Worklist package
|
||||
*/
|
||||
|
||||
// Define the ViewerData global object
|
||||
// If there is currently any Session data for this object,
|
||||
// use this to repopulate the variable
|
||||
ViewerData = Session.get('ViewerData') || {};
|
||||
|
||||
// Create the WorklistTabs collection
|
||||
WorklistTabs = new Meteor.Collection(null);
|
||||
|
||||
// Define the WorklistStudies Collection
|
||||
// This is a client-side only Collection which
|
||||
// Stores the list of studies in the Worklist
|
||||
WorklistStudies = new Meteor.Collection(null);
|
||||
|
||||
Template.worklist.onCreated(function() {
|
||||
var self = this;
|
||||
if (Worklist.subscriptions) {
|
||||
Worklist.subscriptions.forEach(function(collectionName) {
|
||||
self.subscribe(collectionName);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Template.worklist.onRendered(function() {
|
||||
// If there is a tab set as active in the Session,
|
||||
// switch to that now.
|
||||
var contentId = Session.get('activeContentId');
|
||||
switchToTab(contentId);
|
||||
});
|
||||
|
||||
Template.worklist.helpers({
|
||||
/**
|
||||
* Returns the current set of Worklist Tabs
|
||||
* @returns Meteor.Collection The current state of the WorklistTabs Collection
|
||||
*/
|
||||
worklistTabs: function() {
|
||||
return WorklistTabs.find();
|
||||
}
|
||||
});
|
||||
|
||||
Template.worklist.events({
|
||||
'click #tablist a[data-toggle="tab"]': function(e) {
|
||||
// If this tab is already active, do nothing
|
||||
var tabButton = $(e.currentTarget);
|
||||
var tabTitle = tabButton.parents('.tabTitle');
|
||||
if (tabTitle.hasClass('active')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, switch to the tab
|
||||
var contentId = tabButton.data('target').replace('#', '');
|
||||
switchToTab(contentId);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
body
|
||||
background-color: #202020
|
||||
|
||||
.noselect
|
||||
-webkit-touch-callout: none
|
||||
-webkit-user-select: none
|
||||
-khtml-user-select: none
|
||||
-moz-user-select: none
|
||||
-ms-user-select: none
|
||||
user-select: none
|
||||
|
||||
input.worklist-search
|
||||
height: 25px
|
||||
background-color: #888888
|
||||
width: 100%
|
||||
|
||||
#tblStudyList
|
||||
tr
|
||||
height: 20px
|
||||
|
||||
.patient-name-input
|
||||
width: 100%
|
||||
|
||||
.worklist-input
|
||||
width: 100%
|
||||
|
||||
.modality-input
|
||||
width: 100%
|
||||
|
||||
.patientid-input
|
||||
width: 100%
|
||||
|
||||
.study-description-input
|
||||
width: 100%
|
||||
|
||||
#worklistTab
|
||||
background-color: #202020
|
||||
|
||||
#worklistContainer
|
||||
background-color: #202020
|
||||
color: white
|
||||
width:90%
|
||||
padding-top: 10px
|
||||
|
||||
#tabs
|
||||
width: 100%
|
||||
|
||||
#tablist
|
||||
border-bottom: 1px solid #424242
|
||||
background-color: #202020
|
||||
height: 41px
|
||||
|
||||
a:hover
|
||||
background-color: transparent
|
||||
|
||||
.tabTitle
|
||||
background-color: #202020
|
||||
border-color: #424242
|
||||
float: left
|
||||
line-height:30px
|
||||
|
||||
&:hover
|
||||
color: black
|
||||
background-color: white
|
||||
|
||||
button.close
|
||||
color: black
|
||||
|
||||
&.active
|
||||
background-color: #424242
|
||||
border-color: #424242
|
||||
|
||||
&:hover
|
||||
background-color: #424242
|
||||
cursor: none
|
||||
|
||||
a
|
||||
color: white
|
||||
|
||||
button.close
|
||||
color: white
|
||||
|
||||
a
|
||||
padding: 5px 15px
|
||||
cursor: pointer
|
||||
color: #424242
|
||||
text-decoration: none
|
||||
|
||||
button.close
|
||||
margin: 4px 0 4px 8px
|
||||
text-shadow: none
|
||||
color: white
|
||||
text-decoration: none
|
||||
|
||||
&:hover
|
||||
color: red
|
||||
|
||||
|
||||
.tab-content
|
||||
width: 100%
|
||||
height: calc(100% - 91px)
|
||||
|
||||
.tab-pane
|
||||
width: 100%
|
||||
height: 100%
|
||||
@@ -0,0 +1,54 @@
|
||||
<template name="worklistResult">
|
||||
<table id="tblStudyList" class="table table-striped noselect">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<h5>Patient Name</h5>
|
||||
<input type="text" class="form-control worklist-search" id="patientName">
|
||||
</th>
|
||||
<th>
|
||||
<h5>Patient ID</h5>
|
||||
<input type="text" class="form-control worklist-search" id="patientId">
|
||||
</th>
|
||||
|
||||
{{#unless isTouchDevice}}
|
||||
<th>
|
||||
<h5>Accession #</h5>
|
||||
<input type="text" class="form-control worklist-search" id="accessionNumber">
|
||||
</th>
|
||||
{{/unless}}
|
||||
|
||||
<th>
|
||||
<h5>Study Date</h5>
|
||||
<input type="text" class="form-control worklist-search" id="studyDate">
|
||||
</th>
|
||||
|
||||
{{#unless isTouchDevice}}
|
||||
<th>
|
||||
<h5>Modality</h5>
|
||||
<input type="text" class="form-control worklist-search" id="modality">
|
||||
</th>
|
||||
{{/unless}}
|
||||
<th>
|
||||
<h5>Study Description</h5>
|
||||
<input type="text" class="form-control worklist-search" id="studyDescription">
|
||||
</th>
|
||||
|
||||
{{#unless isTouchDevice}}
|
||||
<th>
|
||||
<h5># Images</h5>
|
||||
<input type="text" class="form-control worklist-search" id="numImages">
|
||||
</th>
|
||||
{{/unless}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="studyListData">
|
||||
{{#each studies}}
|
||||
{{ >worklistStudy }}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ #unless studies.count }}
|
||||
{{ >loadingText }}
|
||||
{{ /unless }}
|
||||
</template>
|
||||
@@ -0,0 +1,128 @@
|
||||
Template.worklistResult.helpers({
|
||||
/**
|
||||
* Returns a sorted instance of the WorklistStudies Collection
|
||||
* by Patient name and Study Date in Ascending order.
|
||||
*/
|
||||
studies: function() {
|
||||
return WorklistStudies.find({}, {
|
||||
sort: {
|
||||
patientName: 1,
|
||||
studyDate: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Retrieve all studies
|
||||
search();
|
||||
|
||||
var studyDateFrom;
|
||||
var studyDateTo;
|
||||
var checkFrom = false;
|
||||
var checkTo = false;
|
||||
var filter;
|
||||
|
||||
/**
|
||||
* Transforms an input string into a search filter for
|
||||
* the Worklist Search call
|
||||
*
|
||||
* @param filter The input string to be searched for
|
||||
* @returns {*}
|
||||
*/
|
||||
function getFilter(filter) {
|
||||
if (filter && filter.length && filter.substr(filter.length - 1) !== '*') {
|
||||
filter += '*';
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a value in a string
|
||||
*/
|
||||
function isIndexOf(mainVal, searchVal) {
|
||||
if (mainVal === undefined || mainVal === '' || mainVal.indexOf(searchVal) > -1){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace object if undefined
|
||||
*/
|
||||
function replaceUndefinedColumnValue(text) {
|
||||
if (text === undefined || text === 'undefined') {
|
||||
return '';
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to study date
|
||||
*/
|
||||
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;
|
||||
return new Date(newDateStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a search for studies matching the worklist query parameters
|
||||
* Inserts the identified studies into the WorklistStudies Collection
|
||||
*/
|
||||
function search() {
|
||||
// Create the filters to be used for the Worklist Search
|
||||
filter = {
|
||||
patientName: getFilter($('input#patientName').val()),
|
||||
patientId: getFilter($('input#patientId').val()),
|
||||
accessionNumber: getFilter($('input#accessionNumber').val()),
|
||||
studyDescription: getFilter($('input#studyDescription').val())
|
||||
};
|
||||
|
||||
// Make sure that modality has a reasonable value, since it is occasionally
|
||||
// returned as 'undefined'
|
||||
var modality = replaceUndefinedColumnValue($('input#modality').val());
|
||||
|
||||
// Clear all current studies
|
||||
WorklistStudies.remove({});
|
||||
|
||||
Meteor.call('WorklistSearch', filter, function(error, studies) {
|
||||
if (error) {
|
||||
log.warn(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!studies) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through all identified studies
|
||||
studies.forEach(function(study) {
|
||||
|
||||
// Search the rest of the parameters that aren't done via the server call
|
||||
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)) {
|
||||
|
||||
// Insert any matching studies into the WorklistStudies Collection
|
||||
WorklistStudies.insert(study);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Template.worklistResult.events({
|
||||
'keydown input': function(e) {
|
||||
if (e.which === 13) { // Enter
|
||||
search();
|
||||
}
|
||||
},
|
||||
'onsearch input': function() {
|
||||
search();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
table#tblStudyList
|
||||
color: #888888
|
||||
border-collapse: collapse
|
||||
border-color: gray
|
||||
|
||||
thead
|
||||
background-color: black
|
||||
white-space: nowrap
|
||||
|
||||
&:hover
|
||||
color: #888888
|
||||
|
||||
// TODO: Use padding instead of hidden inputs to keep titles
|
||||
// at the same height in the header.
|
||||
#numImages.worklist-search, #modality.worklist-search
|
||||
visibility: hidden
|
||||
|
||||
tbody
|
||||
tr
|
||||
padding: 4px
|
||||
border: 1px solid #282828
|
||||
background-color: black
|
||||
|
||||
&:hover, &:active, &.active
|
||||
background-color: #009BD2
|
||||
color: white
|
||||
|
||||
td
|
||||
// This selector is necessary to override bootstrap's 'table' class
|
||||
color: white
|
||||
background-color: #009BD2
|
||||
|
||||
&:nth-of-type(odd)
|
||||
background-color: #202020
|
||||
|
||||
td
|
||||
white-space: nowrap
|
||||
@@ -0,0 +1,31 @@
|
||||
<template name="worklistStudy">
|
||||
<tr class="worklistStudy noselect">
|
||||
<td>
|
||||
{{formatPN patientName}}
|
||||
</td>
|
||||
<td>
|
||||
{{patientId}}
|
||||
</td>
|
||||
{{#unless isTouchDevice}}
|
||||
<td>
|
||||
{{accessionNumber}}
|
||||
</td>
|
||||
{{/unless}}
|
||||
<td>
|
||||
{{formatDA studyDate}}
|
||||
</td>
|
||||
{{#unless isTouchDevice}}
|
||||
<td>
|
||||
{{modalities}}
|
||||
</td>
|
||||
{{/unless}}
|
||||
<td>
|
||||
{{studyDescription}}
|
||||
</td>
|
||||
{{#unless isTouchDevice}}
|
||||
<td>
|
||||
{{numberOfStudyRelatedInstances}}
|
||||
</td>
|
||||
{{/unless}}
|
||||
</tr>
|
||||
</template>
|
||||
@@ -0,0 +1,148 @@
|
||||
// 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() {
|
||||
var row = $(this);
|
||||
|
||||
if (row.hasClass('active')) {
|
||||
// If we find one that is already selected, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the relevant studyInstanceUid
|
||||
var studyInstanceUid = row.attr('studyInstanceUid');
|
||||
|
||||
// Retrieve the data context through Blaze
|
||||
var data = Blaze.getData(this);
|
||||
|
||||
// Set the current study as selected
|
||||
WorklistSelectedStudies.insert(data);
|
||||
row.addClass('active');
|
||||
|
||||
// When we reach the currently clicked-on row, stop the loop
|
||||
return !row.is(studyRow);
|
||||
});
|
||||
} 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;
|
||||
log.info('Worklist PreviouslySelected set: ' + studyRow.index());
|
||||
}
|
||||
}
|
||||
|
||||
Template.worklistStudy.events({
|
||||
'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;
|
||||
log.info('Worklist PreviouslySelected set: ' + studyRow.index());
|
||||
|
||||
// Set the current study as selected
|
||||
WorklistSelectedStudies.insert(data);
|
||||
studyRow.addClass('active');
|
||||
}
|
||||
},
|
||||
'mousedown tr.worklistStudy': function(e) {
|
||||
// This event handler is meant to handle middle-click on a study
|
||||
if (e.which !== 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = this;
|
||||
var middleClickOnStudy = Worklist.callbacks.middleClickOnStudy;
|
||||
if (middleClickOnStudy && typeof middleClickOnStudy === 'function') {
|
||||
middleClickOnStudy(data);
|
||||
}
|
||||
},
|
||||
'dblclick tr.worklistStudy': function(e) {
|
||||
if (e.which !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = this;
|
||||
var dblClickOnStudy = Worklist.callbacks.dblClickOnStudy;
|
||||
|
||||
if (dblClickOnStudy && typeof dblClickOnStudy === 'function') {
|
||||
dblClickOnStudy(data);
|
||||
}
|
||||
},
|
||||
'contextmenu tr.worklistStudy': function(e, template) {
|
||||
$(e.currentTarget).addClass('active');
|
||||
|
||||
if (openStudyContextMenu && typeof openStudyContextMenu === 'function') {
|
||||
e.preventDefault();
|
||||
|
||||
openStudyContextMenu(e, template);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
.worklistStudy
|
||||
cursor: pointer
|
||||
Reference in new issue
Block a user