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>
|
||||
+6
-14
@@ -10,11 +10,9 @@ function closeHandler(dialog) {
|
||||
* 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;
|
||||
openStudyContextMenu = function(e) {
|
||||
Template.studyContextMenu.study = $(e.currentTarget);
|
||||
|
||||
var dialog = $('#studyContextMenu');
|
||||
|
||||
@@ -24,7 +22,7 @@ openStudyContextMenu = function(e, template) {
|
||||
};
|
||||
|
||||
// Device is touch device or not
|
||||
// If device is touch device, set position center of screen vertically and horizontally
|
||||
// If device is touch device, set position center of screen vertically and horizontally// jscs:ignore
|
||||
if (isTouchDevice()) {
|
||||
// add dialogMobile class to provide a black, transparent background
|
||||
dialog.addClass('dialogMobile');
|
||||
@@ -33,8 +31,8 @@ openStudyContextMenu = function(e, template) {
|
||||
dialogProperty.right = 0;
|
||||
dialogProperty.bottom = 0;
|
||||
} else {
|
||||
dialogProperty.top = e.pageY;// - dialog.outerHeight() - 40;
|
||||
dialogProperty.left = e.pageX;// - dialog.outerWidth() / 2;
|
||||
dialogProperty.top = e.pageY;
|
||||
dialogProperty.left = e.pageX;
|
||||
|
||||
var pageHeight = $(document.body).height();
|
||||
dialogProperty.top = Math.max(dialogProperty.top, 0);
|
||||
@@ -48,9 +46,6 @@ openStudyContextMenu = function(e, template) {
|
||||
dialog.css(dialogProperty);
|
||||
dialog.focus();
|
||||
|
||||
log.info(e);
|
||||
log.info(template);
|
||||
|
||||
// Show the backdrop
|
||||
UI.render(Template.removableBackdrop, document.body);
|
||||
|
||||
@@ -60,15 +55,12 @@ openStudyContextMenu = function(e, template) {
|
||||
});
|
||||
};
|
||||
|
||||
// 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];
|
||||
var fn = Worklist.functions[id];
|
||||
if (fn && typeof(fn) === 'function') {
|
||||
fn(study);
|
||||
}
|
||||
+6
-1
@@ -41,11 +41,16 @@
|
||||
line-height: 25px
|
||||
color: #808080
|
||||
|
||||
&:hover
|
||||
&: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
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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];
|
||||
}
|
||||
});
|
||||
File renamed without changes.
File renamed without changes.
@@ -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);
|
||||
}
|
||||
});
|
||||
File renamed without changes.
@@ -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>
|
||||
+24
-26
@@ -1,10 +1,6 @@
|
||||
// Define the Studies Collection
|
||||
// This is a client-side only Collection which
|
||||
// Stores the list of studies in the Worklist
|
||||
|
||||
Template.worklistResult.helpers({
|
||||
/**
|
||||
* Returns a sorted instance of the Studies Collection
|
||||
* Returns a sorted instance of the WorklistStudies Collection
|
||||
* by Patient name and Study Date in Ascending order.
|
||||
*/
|
||||
studies: function() {
|
||||
@@ -14,10 +10,6 @@ Template.worklistResult.helpers({
|
||||
studyDate: 1
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
isTouchDevice: function() {
|
||||
return isTouchDevice();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -59,8 +51,8 @@ function isIndexOf(mainVal, searchVal) {
|
||||
/**
|
||||
* Replace object if undefined
|
||||
*/
|
||||
function replaceUndefinedColumnValue (text) {
|
||||
if (text == undefined || text === 'undefined') {
|
||||
function replaceUndefinedColumnValue(text) {
|
||||
if (text === undefined || text === 'undefined') {
|
||||
return '';
|
||||
} else {
|
||||
return text;
|
||||
@@ -70,34 +62,40 @@ function replaceUndefinedColumnValue (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);
|
||||
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 Studies Collection
|
||||
* Inserts the identified studies into the WorklistStudies Collection
|
||||
*/
|
||||
function search() {
|
||||
// Create the filters to be used for the Worklist Search
|
||||
filter = {
|
||||
patientName: getFilter($('#patientName').val()),
|
||||
patientId: getFilter($('#patientId').val()),
|
||||
accessionNumber: getFilter($('#patientAccessionNumber').val())
|
||||
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($('#modality').val());
|
||||
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;
|
||||
}
|
||||
@@ -107,10 +105,10 @@ function search() {
|
||||
|
||||
// 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)) {
|
||||
(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 Studies Collection
|
||||
// Insert any matching studies into the WorklistStudies Collection
|
||||
WorklistStudies.insert(study);
|
||||
}
|
||||
});
|
||||
@@ -118,12 +116,12 @@ function search() {
|
||||
}
|
||||
|
||||
Template.worklistResult.events({
|
||||
'keydown input': function(event) {
|
||||
if (event.keyCode === 13) { // Enter
|
||||
'keydown input': function(e) {
|
||||
if (e.which === 13) { // Enter
|
||||
search();
|
||||
}
|
||||
},
|
||||
'onsearch input': function(event) {
|
||||
'onsearch input': function() {
|
||||
search();
|
||||
}
|
||||
});
|
||||
+7
-3
@@ -9,7 +9,12 @@ table#tblStudyList
|
||||
|
||||
&: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
|
||||
@@ -29,5 +34,4 @@ table#tblStudyList
|
||||
background-color: #202020
|
||||
|
||||
td
|
||||
white-space: nowrap
|
||||
|
||||
white-space: nowrap
|
||||
+3
-3
@@ -23,9 +23,9 @@
|
||||
{{studyDescription}}
|
||||
</td>
|
||||
{{#unless isTouchDevice}}
|
||||
<td>
|
||||
{{numberOfStudyRelatedInstances}}
|
||||
</td>
|
||||
<td>
|
||||
{{numberOfStudyRelatedInstances}}
|
||||
</td>
|
||||
{{/unless}}
|
||||
</tr>
|
||||
</template>
|
||||
+24
-19
@@ -1,6 +1,3 @@
|
||||
Worklist = {};
|
||||
Worklist.previouslySelected = undefined;
|
||||
|
||||
// Maybe we should use regular Worklist collection?
|
||||
WorklistSelectedStudies = new Meteor.Collection(null);
|
||||
|
||||
@@ -52,11 +49,7 @@ function handleShiftClick(studyRow, data) {
|
||||
row.addClass('active');
|
||||
|
||||
// When we reach the currently clicked-on row, stop the loop
|
||||
if (row.is(studyRow)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !row.is(studyRow);
|
||||
});
|
||||
} else {
|
||||
// Set the current study as selected
|
||||
@@ -117,12 +110,29 @@ Template.worklistStudy.events({
|
||||
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);
|
||||
'mousedown tr.worklistStudy': function(e) {
|
||||
// This event handler is meant to handle middle-click on a study
|
||||
if (e.which !== 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Open a new tab with this study
|
||||
openNewTab(this.studyInstanceUid, title);
|
||||
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');
|
||||
@@ -134,10 +144,5 @@ Template.worklistStudy.events({
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Template.worklistStudy.helpers({
|
||||
isTouchDevice: function() {
|
||||
return isTouchDevice();
|
||||
}
|
||||
});
|
||||
});
|
||||
File renamed without changes.
@@ -1,30 +0,0 @@
|
||||
<template name="studyContextMenu">
|
||||
<div id="studyContextMenu" class="studyContextMenu noselect"
|
||||
oncontextmenu='return false;'
|
||||
unselectable='on'
|
||||
onselectstart='return false;'>
|
||||
<ul>
|
||||
<li>
|
||||
<!--<a id="deleteTool">Delete</a>-->
|
||||
<!-- TODO: Make this dynamically accept options-->
|
||||
<a id="launchStudyAssociation" type="button"
|
||||
data-toggle="modal"
|
||||
data-target="#associationModal"
|
||||
title="Launch Study Association">
|
||||
<i class="fa fa-calendar-plus-o fa-lg"></i>
|
||||
Associate
|
||||
</a>
|
||||
<a><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><i class="fa fa-trash fa-lg"></i> Delete</a>
|
||||
<a><i class="fa fa-send-o fa-lg"></i> Send</a>
|
||||
<a><i class="fa fa-exchange fa-lg"></i> Export</a>
|
||||
<a><i class="fa fa-download fa-lg"></i> Download</a>
|
||||
<a><i class="fa fa-photo fa-lg"></i> View Series Details</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,87 +0,0 @@
|
||||
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 tabObjectId = WorklistTabs.findOne({contentid: contentId})._id;
|
||||
|
||||
// Remove this tab from the Tabs collection so it is no longer rendered
|
||||
WorklistTabs.remove(tabObjectId);
|
||||
|
||||
// Remove any stored data related to this tab from the global ViewerData structure
|
||||
delete ViewerData[contentId];
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Set tab width when a tab is added or removed
|
||||
function setTabWidth (){
|
||||
var allTabTitles = $(".tabTitle");
|
||||
var widthTabList = $("#tablist").width();
|
||||
var totalTitleWidths = 0;
|
||||
|
||||
var tabCount = 0;
|
||||
allTabTitles.each( function( index, tabItem ) {
|
||||
totalTitleWidths += $(tabItem).width();
|
||||
tabCount ++;
|
||||
});
|
||||
|
||||
if(totalTitleWidths > widthTabList) {
|
||||
var newTabWidth = widthTabList / tabCount;
|
||||
allTabTitles.each( function( index, tabItem ) {
|
||||
$(tabItem).css("width",newTabWidth+"px");
|
||||
});
|
||||
} else {
|
||||
var newTabWidth = widthTabList / tabCount;
|
||||
allTabTitles.each( function( index, tabItem ) {
|
||||
if (index === 0) {
|
||||
if(newTabWidth > 95) {
|
||||
$(tabItem).css("width","95px");
|
||||
} else {
|
||||
$(tabItem).css("width",newTabWidth+"px");
|
||||
}
|
||||
} else {
|
||||
if(newTabWidth > 130) {
|
||||
$(tabItem).css("width","130px");
|
||||
} else {
|
||||
$(tabItem).css("width",newTabWidth+"px");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
Template.tabTitle.onRendered(function(){
|
||||
});
|
||||
@@ -1,218 +0,0 @@
|
||||
/**
|
||||
* 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') || {};
|
||||
|
||||
// Define the StudyMetaData object. This is used as a cache
|
||||
// to store study meta data information to prevent unnecessary
|
||||
// calls to the server
|
||||
var StudyMetaData = {};
|
||||
|
||||
// Create the WorklistTabs collection
|
||||
WorklistTabs = new Meteor.Collection(null);
|
||||
|
||||
// Create the WorklistStudies collection
|
||||
WorklistStudies = new Meteor.Collection(null);
|
||||
|
||||
/**
|
||||
* Retrieves study metadata using a server call, and fires a callback
|
||||
* when completed.
|
||||
*
|
||||
* @params {string} studyInstanceUid The UID of the Study to be retrieved
|
||||
* @params {function} doneCallback The callback function to be executed when the study retrieval has finished
|
||||
*/
|
||||
getStudyMetadata = function(studyInstanceUid, doneCallback) {
|
||||
log.info('worklistStudy getStudyMetadata');
|
||||
|
||||
// If the StudyMetaData cache already has data related to this
|
||||
// studyInstanceUid, then we should fire the doneCallback with this data
|
||||
// and stop here.
|
||||
var study = StudyMetaData[studyInstanceUid];
|
||||
if (study) {
|
||||
doneCallback(study);
|
||||
return;
|
||||
}
|
||||
|
||||
// If no study metadata is in the cache variable, we need to retrieve it from
|
||||
// the server with a call.
|
||||
Meteor.call('GetStudyMetadata', studyInstanceUid, function(error, study) {
|
||||
if (error) {
|
||||
log.warn(error);
|
||||
return;
|
||||
}
|
||||
// Once we have retrieved the data, we sort the series' by series
|
||||
// and instance number in ascending order
|
||||
sortStudy(study);
|
||||
|
||||
// Then we store this data in the cache variable
|
||||
StudyMetaData[studyInstanceUid] = study;
|
||||
|
||||
// Finally, we fire the doneCallback with this study meta data
|
||||
doneCallback(study);
|
||||
|
||||
// Temporary: Testing out the Clinical Meteor HIPAA Audit log
|
||||
if (Meteor.user()) {
|
||||
log.info('Adding access to HIPAA Log');
|
||||
var hipaaEvent = {
|
||||
eventType: "access",
|
||||
userId: Meteor.userId(),
|
||||
userName: Meteor.user().profile.fullName,
|
||||
collectionName: "Studies",
|
||||
recordId: studyInstanceUid,
|
||||
patientId: null,
|
||||
patientName: null
|
||||
};
|
||||
HipaaLogger.logEvent(hipaaEvent);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Switches to a new tab in the tabbed worklist container
|
||||
* This function renders either the Worklist or the Viewer template with new data.
|
||||
*
|
||||
* @param contentId The unique ID of the tab to be switched to
|
||||
*/
|
||||
switchToTab = function(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();
|
||||
|
||||
// Update the 'activeContentId' variable in Session
|
||||
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.
|
||||
// Then stop here, since nothing needs to be re-rendered.
|
||||
if (contentId === 'worklistTab') {
|
||||
document.body.style.overflow = null;
|
||||
document.body.style.height = null;
|
||||
document.body.style.minWidth = null;
|
||||
document.body.style.position = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!container) {
|
||||
log.warn('No container present with the contentId: ' + contentId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the stored ViewerData global object to retrieve the studyInstanceUid
|
||||
// related to this tab
|
||||
var studyInstanceUid = ViewerData[contentId].studyInstanceUid;
|
||||
|
||||
// Attempt to retrieve the meta data (it might be cached)
|
||||
getStudyMetadata(studyInstanceUid, function(study) {
|
||||
|
||||
// Once we have the study data, store it in a structure with
|
||||
// any other saved data about this tab (e.g. layout structure)
|
||||
var data = {
|
||||
viewportRows: ViewerData[contentId].viewportRows,
|
||||
viewportColumns: ViewerData[contentId].viewportColumns,
|
||||
contentId: contentId,
|
||||
studies: [ study ]
|
||||
};
|
||||
|
||||
if (ViewerData[contentId].studies && ViewerData[contentId].studies.length) {
|
||||
data.studies = ViewerData[contentId].studies;
|
||||
}
|
||||
|
||||
// Remove the loading text template that is inside the tab container by default
|
||||
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');
|
||||
|
||||
// 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.height = '100%';
|
||||
document.body.style.width = '100%';
|
||||
document.body.style.minWidth = 0;
|
||||
document.body.style.position = 'fixed'; // Prevent overscroll on mobile devices
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens a new tab in the tabbed worklist environment using
|
||||
* a given study and new tab title.
|
||||
*
|
||||
* @param studyInstanceUid The UID of the Study to be opened
|
||||
* @param title The title to be used for the tab heading
|
||||
*/
|
||||
openNewTab = function(studyInstanceUid, title) {
|
||||
// Generate a unique ID to represent this tab
|
||||
// We can't just use the Mongo entry ID because
|
||||
// then it will change after hot-reloading.
|
||||
var contentid = generateUUID();
|
||||
|
||||
// Create a new entry in the WorklistTabs Collection
|
||||
WorklistTabs.insert({
|
||||
title: title,
|
||||
contentid: contentid,
|
||||
active: false
|
||||
});
|
||||
|
||||
// Update the ViewerData global object
|
||||
ViewerData[contentid] = {
|
||||
title: title,
|
||||
contentid: contentid,
|
||||
studyInstanceUid: studyInstanceUid
|
||||
};
|
||||
|
||||
// Switch to the new tab
|
||||
switchToTab(contentid);
|
||||
};
|
||||
|
||||
Template.worklist.onRendered(function() {
|
||||
// If there is a tab set as active in the Session,
|
||||
// switch to that now.
|
||||
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() {
|
||||
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);
|
||||
}
|
||||
});
|
||||
@@ -1,68 +0,0 @@
|
||||
<template name="worklistResult">
|
||||
<table id="tblStudyList" class="table table-striped noselect">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<div>Patient Name</div>
|
||||
<div class="patient-name-input">
|
||||
<input type="text" class="form-control worklist-search" id="patientName">
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div>Patient ID</div>
|
||||
<div class="patientid-input">
|
||||
<input type="text" class="form-control worklist-search" id="patientId">
|
||||
</div>
|
||||
</th>
|
||||
|
||||
{{#unless isTouchDevice}}
|
||||
<th>
|
||||
<div>Accession #</div>
|
||||
<div class="worklist-input">
|
||||
<input type="text" class="form-control worklist-search" id="accessionNumber">
|
||||
</div>
|
||||
</th>
|
||||
{{/unless}}
|
||||
|
||||
<th>
|
||||
<div>Study Date</div>
|
||||
<div class="worklist-input">
|
||||
<input type="text" class="form-control worklist-search" id="studyDate">
|
||||
</div>
|
||||
</th>
|
||||
|
||||
{{#unless isTouchDevice}}
|
||||
<th>
|
||||
<div>Modality</div>
|
||||
<div class="modality-input">
|
||||
<input type="text" class="form-control worklist-search" id="modality" style="visibility: hidden; ">
|
||||
</div>
|
||||
</th>
|
||||
{{/unless}}
|
||||
<th>
|
||||
<div>Study Description</div>
|
||||
<div class="study-description-input">
|
||||
<input type="text" class="form-control worklist-search" id="studyDescription" style="visibility: hidden; ">
|
||||
</div>
|
||||
</th>
|
||||
|
||||
{{#unless isTouchDevice}}
|
||||
<th>
|
||||
<div># Images</div>
|
||||
<div class="worklist-input">
|
||||
<input type="text" class="form-control worklist-search" id="numberOfImages" style="visibility: hidden; ">
|
||||
</div>
|
||||
</th>
|
||||
{{/unless}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="studyListData">
|
||||
{{#each studies}}
|
||||
{{ >worklistStudy }}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ #unless studies.count }}
|
||||
{{ >loadingText }}
|
||||
{{ /unless }}
|
||||
</template>
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
generateUUID = function() {
|
||||
var d = new Date().getTime();
|
||||
var uuid = 'xxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r =(d + Math.random()*8)%8 | 0;
|
||||
d = Math.floor(d/8);
|
||||
return(c=='x' ? r :(r&0x3|0x8)).toString(8);
|
||||
});
|
||||
return uuid;
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Retrieves metaData for multiple studies at once.
|
||||
*
|
||||
* This function calls getStudyMetadata several times, asynchronously,
|
||||
* and waits for all of the results to be returned.
|
||||
*
|
||||
* @param studyInstanceUids The UIDs of the Studies to be retrieved
|
||||
* @param doneCallback The callback function to be executed when the study retrieval has finished
|
||||
* @param failCallback The callback function to be executed when the study retrieval has failed
|
||||
*/
|
||||
getStudiesMetadata = function(studyInstanceUids, doneCallback, failCallback) {
|
||||
// Check to make sure studyInstanceUids were actually input
|
||||
if (!studyInstanceUids || !studyInstanceUids.length) {
|
||||
if (failCallback && typeof failCallback === 'function') {
|
||||
failCallback('No studyInstanceUids were input');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an empty array to store the Promises for each metaData
|
||||
// retrieval call
|
||||
var promises = [];
|
||||
|
||||
// Create an empty array to hold all the results of the promises
|
||||
var studies = [];
|
||||
|
||||
|
||||
// Loop through the array of studyInstanceUids
|
||||
studyInstanceUids.forEach(function(studyInstanceUid) {
|
||||
// Create a new Deferred to monitor the progress of the asynchronous
|
||||
// metaData retrieval
|
||||
var deferred = new $.Deferred();
|
||||
|
||||
// Send the call, and attach doneCallbacks and failCallbacks
|
||||
// which can resolve or reject the related promise based on its outcome
|
||||
getStudyMetadata(studyInstanceUid, function(study) {
|
||||
deferred.resolve(study);
|
||||
}, function(error) {
|
||||
deferred.reject(error);
|
||||
});
|
||||
|
||||
// Add the current promise to the array of promises
|
||||
promises.push(deferred.promise());
|
||||
});
|
||||
|
||||
// When all of the promises are complete, this callback runs
|
||||
$.when.apply($, promises).done(function() {
|
||||
// Convert the Arguments Array-like Object to an actual array
|
||||
var studies = $.makeArray(arguments);
|
||||
|
||||
// Pass the studies array to the doneCallback, if one exists
|
||||
if (doneCallback && typeof doneCallback === 'function') {
|
||||
doneCallback(studies);
|
||||
}
|
||||
}).fail(function(error) {
|
||||
log.warn(error);
|
||||
if (failCallback && typeof failCallback === 'function') {
|
||||
failCallback(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
// Define the StudyMetaData object. This is used as a cache
|
||||
// to store study meta data information to prevent unnecessary
|
||||
// calls to the server
|
||||
var StudyMetaData = {};
|
||||
|
||||
/**
|
||||
* Retrieves study metadata using a server call, and fires a callback
|
||||
* when completed.
|
||||
*
|
||||
* @params {string} studyInstanceUid The UID of the Study to be retrieved
|
||||
* @params {function} doneCallback The callback function to be executed when the study retrieval has finished
|
||||
* @param failCallback The callback function to be executed when the study retrieval has failed
|
||||
*/
|
||||
getStudyMetadata = function(studyInstanceUid, doneCallback, failCallback) {
|
||||
log.info('worklistStudy getStudyMetadata');
|
||||
|
||||
// If the StudyMetaData cache already has data related to this
|
||||
// studyInstanceUid, then we should fire the doneCallback with this data
|
||||
// and stop here.
|
||||
var study = StudyMetaData[studyInstanceUid];
|
||||
if (study) {
|
||||
doneCallback(study);
|
||||
return;
|
||||
}
|
||||
|
||||
// If no study metadata is in the cache variable, we need to retrieve it from
|
||||
// the server with a call.
|
||||
Meteor.call('GetStudyMetadata', studyInstanceUid, function(error, study) {
|
||||
if (error) {
|
||||
log.warn(error);
|
||||
failCallback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Once we have retrieved the data, we sort the series' by series
|
||||
// and instance number in ascending order
|
||||
sortStudy(study);
|
||||
|
||||
// Then we store this data in the cache variable
|
||||
StudyMetaData[studyInstanceUid] = study;
|
||||
|
||||
// Finally, we fire the doneCallback with this study meta data
|
||||
doneCallback(study);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Opens a new tab in the tabbed worklist environment using
|
||||
* a given study and new tab title.
|
||||
*
|
||||
* @param studyInstanceUid The UID of the Study to be opened
|
||||
* @param title The title to be used for the tab heading
|
||||
*/
|
||||
openNewTab = function(studyInstanceUid, title) {
|
||||
// Generate a unique ID to represent this tab
|
||||
// We can't just use the Mongo entry ID because
|
||||
// then it will change after hot-reloading.
|
||||
var contentid = uuid.new();
|
||||
|
||||
// Create a new entry in the WorklistTabs Collection
|
||||
WorklistTabs.insert({
|
||||
title: title,
|
||||
contentid: contentid,
|
||||
active: false
|
||||
});
|
||||
|
||||
// Update the ViewerData global object
|
||||
ViewerData[contentid] = {
|
||||
title: title,
|
||||
contentid: contentid,
|
||||
studyInstanceUids: [studyInstanceUid]
|
||||
};
|
||||
|
||||
// Switch to the new tab
|
||||
switchToTab(contentid);
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Switches to a new tab in the tabbed worklist container
|
||||
* This function renders either the Worklist or the Viewer template with new data.
|
||||
*
|
||||
* @param contentId The unique ID of the tab to be switched to
|
||||
*/
|
||||
switchToTab = function(contentId) {
|
||||
if (!contentId) {
|
||||
return;
|
||||
}
|
||||
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();
|
||||
|
||||
// Update the 'activeContentId' variable in Session
|
||||
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.
|
||||
// Then stop here, since nothing needs to be re-rendered.
|
||||
if (contentId === 'worklistTab') {
|
||||
document.body.style.overflow = null;
|
||||
document.body.style.height = null;
|
||||
document.body.style.minWidth = null;
|
||||
document.body.style.position = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Tab was closed at some point, stop here
|
||||
if (!ViewerData[contentId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the stored ViewerData global object to retrieve the studyInstanceUid
|
||||
// related to this tab
|
||||
var studyInstanceUids = ViewerData[contentId].studyInstanceUids;
|
||||
|
||||
// Attempt to retrieve the meta data (it might be cached)
|
||||
getStudiesMetadata(studyInstanceUids, function(studies) {
|
||||
// Tab closed while study data was being retrieved, stop here
|
||||
if (!ViewerData[contentId]) {
|
||||
log.warn('Tab closed while study data was being retrieved');
|
||||
return;
|
||||
}
|
||||
|
||||
// Once we have the study data, store it in a structure with
|
||||
// any other saved data about this tab (e.g. layout structure)
|
||||
var data = jQuery.extend({}, ViewerData[contentId]);
|
||||
data.studies = studies;
|
||||
data.contentId = contentId;
|
||||
|
||||
if (ViewerData[contentId].studies && ViewerData[contentId].studies.length) {
|
||||
data.studies = ViewerData[contentId].studies;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!container) {
|
||||
log.warn('No container present with the contentId: ' + contentId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the loading text template that is inside the tab container by default
|
||||
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');
|
||||
|
||||
// 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.height = '100%';
|
||||
document.body.style.width = '100%';
|
||||
document.body.style.minWidth = 0;
|
||||
|
||||
// Prevent overscroll on mobile devices
|
||||
document.body.style.position = 'fixed';
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
Worklist = {
|
||||
functions: {},
|
||||
callbacks: {}
|
||||
};
|
||||
|
||||
Worklist.callbacks.dblClickOnStudy = dblClickOnStudy;
|
||||
Worklist.callbacks.middleClickOnStudy = dblClickOnStudy;
|
||||
|
||||
function dblClickOnStudy(data) {
|
||||
// Use the formatPN template helper to clean up the patient name
|
||||
var title = formatPN(data.patientName);
|
||||
openNewTab(data.studyInstanceUid, title);
|
||||
}
|
||||
@@ -11,8 +11,8 @@ Package.onUse(function (api) {
|
||||
api.use('jquery');
|
||||
api.use('stylus');
|
||||
api.use('http');
|
||||
|
||||
api.use('practicalmeteor:loglevel');
|
||||
api.use('rwatts:uuid');
|
||||
|
||||
// Our custom packages
|
||||
api.use('dicomweb');
|
||||
@@ -23,36 +23,44 @@ Package.onUse(function (api) {
|
||||
api.addFiles('log.js', 'client');
|
||||
|
||||
// Components
|
||||
api.addFiles('components/worklist.html', 'client');
|
||||
api.addFiles('components/worklist.js', 'client');
|
||||
api.addFiles('components/worklist.styl', 'client');
|
||||
api.addFiles('client/components/worklist.html', 'client');
|
||||
api.addFiles('client/components/worklist.js', 'client');
|
||||
api.addFiles('client/components/worklist.styl', 'client');
|
||||
|
||||
api.addFiles('components/tabTitle/tabTitle.html', 'client');
|
||||
api.addFiles('components/tabTitle/tabTitle.js', 'client');
|
||||
api.addFiles('components/tabTitle/tabTitle.styl', 'client');
|
||||
api.addFiles('client/components/tabTitle/tabTitle.html', 'client');
|
||||
api.addFiles('client/components/tabTitle/tabTitle.js', 'client');
|
||||
api.addFiles('client/components/tabTitle/tabTitle.styl', 'client');
|
||||
|
||||
api.addFiles('components/tabContent/tabContent.html', 'client');
|
||||
api.addFiles('components/tabContent/tabContent.styl', 'client');
|
||||
api.addFiles('client/components/tabContent/tabContent.html', 'client');
|
||||
api.addFiles('client/components/tabContent/tabContent.styl', 'client');
|
||||
|
||||
api.addFiles('components/worklistStudy/worklistStudy.html', 'client');
|
||||
api.addFiles('components/worklistStudy/worklistStudy.js', 'client');
|
||||
api.addFiles('components/worklistStudy/worklistStudy.styl', 'client');
|
||||
api.addFiles('client/components/worklistStudy/worklistStudy.html', 'client');
|
||||
api.addFiles('client/components/worklistStudy/worklistStudy.js', 'client');
|
||||
api.addFiles('client/components/worklistStudy/worklistStudy.styl', 'client');
|
||||
|
||||
api.addFiles('components/worklistResult/worklistResult.html', 'client');
|
||||
api.addFiles('components/worklistResult/worklistResult.js', 'client');
|
||||
api.addFiles('components/worklistResult/worklistResult.styl', 'client');
|
||||
api.addFiles('client/components/worklistResult/worklistResult.html', 'client');
|
||||
api.addFiles('client/components/worklistResult/worklistResult.js', 'client');
|
||||
api.addFiles('client/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('client/components/studyContextMenu/studyContextMenu.html', 'client');
|
||||
api.addFiles('client/components/studyContextMenu/studyContextMenu.js', 'client');
|
||||
api.addFiles('client/components/studyContextMenu/studyContextMenu.styl', 'client');
|
||||
|
||||
api.addFiles('lib/generateUUID.js', 'client');
|
||||
api.export('generateUUID', 'client');
|
||||
// Library functions
|
||||
api.addFiles('lib/getStudyMetadata.js', 'client');
|
||||
api.addFiles('lib/getStudiesMetadata.js', 'client');
|
||||
api.addFiles('lib/openNewTab.js', 'client');
|
||||
api.addFiles('lib/switchToTab.js', 'client');
|
||||
api.addFiles('lib/worklist.js', 'client');
|
||||
|
||||
// Export Worklist helper functions for usage in Routes
|
||||
api.export('getTimepointName', 'client');
|
||||
api.export('getStudyMetadata', 'client');
|
||||
api.export('getStudiesMetadata', 'client');
|
||||
api.export('openNewTab', 'client');
|
||||
api.export('setWorklistSubscriptions', 'client');
|
||||
api.export('switchToTab', 'client');
|
||||
api.export('Worklist');
|
||||
|
||||
// Export the global ViewerData object
|
||||
api.export('ViewerData', 'client');
|
||||
@@ -61,5 +69,4 @@ Package.onUse(function (api) {
|
||||
api.export('WorklistTabs', 'client');
|
||||
api.export('WorklistStudies', 'client');
|
||||
api.export('WorklistSelectedStudies', 'client');
|
||||
});
|
||||
|
||||
});
|
||||
Reference in new issue
Block a user