Most JS files, all helpers and simple reconciliations
This commit is contained in:
parent
21400c5836
commit
9e69c43dda
@ -1,5 +1,6 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { Viewerbase } from 'meteor/ohif:viewerbase';
|
||||
|
||||
Template.toolbarSection.helpers({
|
||||
// Returns true if the view shall be split in two viewports
|
||||
@ -141,7 +142,7 @@ Template.toolbarSection.helpers({
|
||||
title: 'CINE',
|
||||
classes: 'imageViewerCommand',
|
||||
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-cineplay-toggle',
|
||||
disableFunction: hasMultipleFrames
|
||||
disableFunction: Viewerbase.viewportUtils.hasMultipleFrames
|
||||
});
|
||||
|
||||
const buttonData = [];
|
||||
@ -168,11 +169,11 @@ Template.toolbarSection.helpers({
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'link',
|
||||
id: 'linkStackScroll',
|
||||
title: 'Link',
|
||||
classes: 'imageViewerCommand toolbarSectionButton nonAutoDisableState',
|
||||
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-link',
|
||||
disableFunction: isStackScrollLinkingDisabled
|
||||
disableFunction: Viewerbase.viewportUtils.isStackScrollLinkingDisabled
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
|
||||
@ -2,6 +2,7 @@ import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { ReactiveDict } from 'meteor/reactive-dict';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import 'meteor/ohif:viewerbase';
|
||||
import { _ } from 'meteor/underscore';
|
||||
|
||||
Session.set('ViewerMainReady', false);
|
||||
@ -32,15 +33,15 @@ Template.viewer.onCreated(() => {
|
||||
toolManager.setActiveTool('nonTarget');
|
||||
},
|
||||
// Viewport functions
|
||||
toggleCineDialog,
|
||||
clearTools,
|
||||
resetViewport,
|
||||
invert,
|
||||
flipV,
|
||||
flipH,
|
||||
rotateL,
|
||||
rotateR,
|
||||
link
|
||||
toggleCineDialog: OHIF.viewerbase.viewportUtils.toggleCineDialog,
|
||||
clearTools: OHIF.viewerbase.viewportUtils.clearTools,
|
||||
resetViewport: OHIF.viewerbase.viewportUtils.resetViewport,
|
||||
invert: OHIF.viewerbase.viewportUtils.invert,
|
||||
flipV: OHIF.viewerbase.viewportUtils.flipV,
|
||||
flipH: OHIF.viewerbase.viewportUtils.flipH,
|
||||
rotateL: OHIF.viewerbase.viewportUtils.rotateL,
|
||||
rotateR: OHIF.viewerbase.viewportUtils.rotateR,
|
||||
linkStackScroll: OHIF.viewerbase.viewportUtils.linkStackScroll
|
||||
});
|
||||
|
||||
if (ViewerData[contentId].loadedSeriesData) {
|
||||
|
||||
@ -1,9 +1,16 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { Random } from 'meteor/random';
|
||||
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import 'meteor/ohif:viewerbase';
|
||||
|
||||
/**
|
||||
* Extend the Array prototype with a Swap function
|
||||
* so we can swap stages more easily
|
||||
*/
|
||||
Array.prototype.move = function(oldIndex, newIndex) {
|
||||
var value = this[oldIndex];
|
||||
const value = this[oldIndex];
|
||||
|
||||
newIndex = Math.max(0, newIndex);
|
||||
newIndex = Math.min(this.length, newIndex);
|
||||
@ -22,13 +29,13 @@ Array.prototype.move = function(oldIndex, newIndex) {
|
||||
* @returns {number} The index of the specified stage within the Protocol,
|
||||
* or undefined if it is not present.
|
||||
*/
|
||||
function getStageIndex(protocol, id) {
|
||||
var stageIndex;
|
||||
const getStageIndex = (protocol, id) => {
|
||||
let stageIndex;
|
||||
if (!protocol || !protocol.stages) {
|
||||
return;
|
||||
}
|
||||
|
||||
protocol.stages.forEach(function(stage, index) {
|
||||
protocol.stages.forEach((stage, index) => {
|
||||
if (stage.id === id) {
|
||||
stageIndex = index;
|
||||
return false;
|
||||
@ -36,7 +43,7 @@ function getStageIndex(protocol, id) {
|
||||
});
|
||||
|
||||
return stageIndex;
|
||||
}
|
||||
};
|
||||
|
||||
Template.stageSortable.helpers({
|
||||
/**
|
||||
@ -44,7 +51,7 @@ Template.stageSortable.helpers({
|
||||
*
|
||||
* @returns {boolean} Whether or not the stage is currently being displayed
|
||||
*/
|
||||
isActiveStage: function() {
|
||||
isActiveStage() {
|
||||
// Rerun this function every time the layout manager has been updated
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
@ -53,7 +60,7 @@ Template.stageSortable.helpers({
|
||||
return;
|
||||
}
|
||||
|
||||
var currentStage = ProtocolEngine.getCurrentStageModel();
|
||||
const currentStage = ProtocolEngine.getCurrentStageModel();
|
||||
if (!currentStage) {
|
||||
return false;
|
||||
}
|
||||
@ -66,8 +73,8 @@ Template.stageSortable.helpers({
|
||||
*
|
||||
* @returns {number|*}
|
||||
*/
|
||||
stageLabel: function() {
|
||||
var stage = this;
|
||||
stageLabel() {
|
||||
const stage = this;
|
||||
|
||||
// If no Protocol Engine has been defined yet, stop here to prevent errors
|
||||
if (!ProtocolEngine) {
|
||||
@ -75,10 +82,10 @@ Template.stageSortable.helpers({
|
||||
}
|
||||
|
||||
// Retrieve the last saved copy of the current protocol
|
||||
var lastSavedCopy = HP.ProtocolStore.getProtocol(ProtocolEngine.protocol.id);
|
||||
const lastSavedCopy = HangingProtocols.findOne(ProtocolEngine.protocol._id);
|
||||
|
||||
// Try to find the index of this stage in the previously saved copy
|
||||
var stageIndex = getStageIndex(lastSavedCopy, stage.id);
|
||||
let stageIndex = getStageIndex(lastSavedCopy, stage.id);
|
||||
|
||||
// If the stage is new, and therefore wasn't present in the last save,
|
||||
// retrieve it's index in the array of new stage ids and use that for
|
||||
@ -88,11 +95,11 @@ Template.stageSortable.helpers({
|
||||
Session.get('timeAgoVariable');
|
||||
|
||||
// Find the index of the stage in the array of newly created stage IDs
|
||||
var newStageNumber = ProtocolEngine.newStageIds.indexOf(stage.id) + 1;
|
||||
const newStageNumber = ProtocolEngine.newStageIds.indexOf(stage.id) + 1;
|
||||
|
||||
// Use Moment.js to format the createdDate of this stage relative to the
|
||||
// current time
|
||||
var dateCreatedFromNow = moment(stage.createdDate).fromNow();
|
||||
const dateCreatedFromNow = moment(stage.createdDate).fromNow();
|
||||
|
||||
// Return the label for the new stage,
|
||||
// e.g. "New Stage 1 (created a few seconds ago)"
|
||||
@ -108,7 +115,7 @@ Template.stageSortable.helpers({
|
||||
*
|
||||
* @returns {boolean} Whether or not a later stage exists
|
||||
*/
|
||||
isNextAvailable: function() {
|
||||
isNextAvailable() {
|
||||
// Run this helper whenever the ProtocolEngine / LayoutManager has changed
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
@ -125,7 +132,7 @@ Template.stageSortable.helpers({
|
||||
*
|
||||
* @returns {boolean} Whether or not an earlier stage exists
|
||||
*/
|
||||
isPreviousAvailable: function() {
|
||||
isPreviousAvailable() {
|
||||
// Run this helper whenever the ProtocolEngine / LayoutManager has changed
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
@ -143,9 +150,9 @@ Template.stageSortable.events({
|
||||
/**
|
||||
* Displays a stage when its title is clicked
|
||||
*/
|
||||
'click .sortable-item span': function() {
|
||||
'click .sortable-item span'() {
|
||||
// Retrieve the index of this stage in the display set sequences
|
||||
var stageIndex = getStageIndex(ProtocolEngine.protocol, this.id);
|
||||
const stageIndex = getStageIndex(ProtocolEngine.protocol, this.id);
|
||||
|
||||
// Display the selected stage
|
||||
ProtocolEngine.setCurrentProtocolStage(stageIndex);
|
||||
@ -154,12 +161,12 @@ Template.stageSortable.events({
|
||||
* Creates a new stage and adds it to the currently loaded Protocol at
|
||||
* the end of the display set sequence
|
||||
*/
|
||||
'click #addStage': function() {
|
||||
'click #addStage'() {
|
||||
// Retrieve the model describing the current stage
|
||||
var stage = ProtocolEngine.getCurrentStageModel();
|
||||
const stage = ProtocolEngine.getCurrentStageModel();
|
||||
|
||||
// Clone this stage to create a new stage
|
||||
var newStage = stage.createClone();
|
||||
const newStage = stage.createClone();
|
||||
|
||||
// Remove the stage's name if it has one
|
||||
delete newStage.name;
|
||||
@ -171,7 +178,7 @@ Template.stageSortable.events({
|
||||
ProtocolEngine.newStageIds.push(newStage.id);
|
||||
|
||||
// Calculate the index of the last stage in the display set sequence
|
||||
var stageIndex = ProtocolEngine.protocol.stages.length - 1;
|
||||
const stageIndex = ProtocolEngine.protocol.stages.length - 1;
|
||||
|
||||
// Switch to the last stage in the display set sequence
|
||||
ProtocolEngine.setCurrentProtocolStage(stageIndex);
|
||||
@ -181,22 +188,22 @@ Template.stageSortable.events({
|
||||
* the stages array. If it is the currently active stage, the current stage is
|
||||
* set to one stage earlier in the display set sequence.
|
||||
*/
|
||||
'click .deleteStage': function() {
|
||||
'click .deleteStage'() {
|
||||
// If this is the only stage in the Protocol, stop here
|
||||
if (ProtocolEngine.protocol.stages.length === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var stageId = this.id;
|
||||
const stageId = this.id;
|
||||
|
||||
var options = {
|
||||
const options = {
|
||||
title: 'Remove Protocol Stage',
|
||||
text: 'Are you sure you would like to remove this Protocol Stage? This cannot be reversed.'
|
||||
};
|
||||
|
||||
showConfirmDialog(function() {
|
||||
OHIF.viewerbase.showConfirmDialog(() => {
|
||||
// Retrieve the index of this stage in the display set sequences
|
||||
var stageIndex = getStageIndex(ProtocolEngine.protocol, stageId);
|
||||
const stageIndex = getStageIndex(ProtocolEngine.protocol, stageId);
|
||||
|
||||
// Remove it from the display set sequence
|
||||
ProtocolEngine.protocol.stages.splice(stageIndex, 1);
|
||||
@ -204,7 +211,7 @@ Template.stageSortable.events({
|
||||
// If we have removed the currently active stage, switch to the one before it
|
||||
if (ProtocolEngine.stage === stageIndex) {
|
||||
// Make sure we don't try to switch to a stage index below zero
|
||||
var newStageIndex = Math.max(stageIndex - 1, 0);
|
||||
const newStageIndex = Math.max(stageIndex - 1, 0);
|
||||
|
||||
// Display the new stage
|
||||
ProtocolEngine.setCurrentProtocolStage(newStageIndex);
|
||||
@ -215,10 +222,10 @@ Template.stageSortable.events({
|
||||
}, options);
|
||||
},
|
||||
|
||||
'click .moveStageUp': function() {
|
||||
'click .moveStageUp'() {
|
||||
// Get the old and new indices following a 'sort' event
|
||||
var oldIndex = ProtocolEngine.stage;
|
||||
var newIndex = Math.max(ProtocolEngine.stage - 1, 0);
|
||||
const oldIndex = ProtocolEngine.stage;
|
||||
const newIndex = Math.max(ProtocolEngine.stage - 1, 0);
|
||||
|
||||
if (oldIndex === newIndex) {
|
||||
return;
|
||||
@ -235,10 +242,10 @@ Template.stageSortable.events({
|
||||
// Update the Session variable to the UI re-renders
|
||||
Session.set('LayoutManagerUpdated', Random.id());
|
||||
},
|
||||
'click .moveStageDown': function() {
|
||||
'click .moveStageDown'() {
|
||||
// Get the old and new indices following a 'sort' event
|
||||
var oldIndex = ProtocolEngine.stage;
|
||||
var newIndex = Math.min(ProtocolEngine.stage + 1, ProtocolEngine.protocol.stages.length - 1);
|
||||
const oldIndex = ProtocolEngine.stage;
|
||||
const newIndex = Math.min(ProtocolEngine.stage + 1, ProtocolEngine.protocol.stages.length - 1);
|
||||
|
||||
if (oldIndex === newIndex) {
|
||||
return;
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
var keys = {
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { Template } from 'meteor/templating';
|
||||
|
||||
import { Viewerbase } from 'meteor/ohif:viewerbase';
|
||||
|
||||
const keys = {
|
||||
ESC: 27,
|
||||
ENTER: 13
|
||||
};
|
||||
@ -9,7 +15,7 @@ var keys = {
|
||||
*
|
||||
* @param dialog The DOM element of the dialog to close
|
||||
*/
|
||||
function closeHandler(dialog) {
|
||||
const closeHandler = dialog => {
|
||||
// Hide the lesion dialog
|
||||
$(dialog).css('display', 'none');
|
||||
|
||||
@ -17,8 +23,8 @@ function closeHandler(dialog) {
|
||||
$('.removableBackdrop').remove();
|
||||
|
||||
// Restore the focus to the active viewport
|
||||
setFocusToActiveViewport();
|
||||
}
|
||||
Viewerbase.setFocusToActiveViewport();
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays and updates the UI of the Text Entry Dialog given a new title,
|
||||
@ -30,10 +36,10 @@ function closeHandler(dialog) {
|
||||
*/
|
||||
openTextEntryDialog = function(title, instructions, currentValue, doneCallback) {
|
||||
// Get the lesion location dialog
|
||||
var dialog = $('.textEntryDialog');
|
||||
const dialog = $('.textEntryDialog');
|
||||
|
||||
// Clear any input that is still on the page
|
||||
var currentValueInput = dialog.find('input.currentValue');
|
||||
const currentValueInput = dialog.find('input.currentValue');
|
||||
currentValueInput.val(currentValue);
|
||||
|
||||
// Store the Dialog DOM data, rule level and rule in the template data
|
||||
@ -49,10 +55,10 @@ openTextEntryDialog = function(title, instructions, currentValue, doneCallback)
|
||||
dialog.css('display', 'block');
|
||||
|
||||
// Show the backdrop
|
||||
UI.render(Template.removableBackdrop, document.body);
|
||||
Blaze.render(Template.removableBackdrop, document.body);
|
||||
|
||||
// Make sure the context menu is closed when the user clicks away
|
||||
$('.removableBackdrop').one('mousedown touchstart', function() {
|
||||
$('.removableBackdrop').one('mousedown touchstart', () => {
|
||||
closeHandler(dialog);
|
||||
});
|
||||
};
|
||||
@ -68,17 +74,17 @@ Template.textEntryDialog.events({
|
||||
* Save the user-specified text
|
||||
*
|
||||
*/
|
||||
'click .save': function() {
|
||||
'click .save'() {
|
||||
// Retrieve the input properties to the template
|
||||
var dialog = Template.textEntryDialog.dialog;
|
||||
var currentValue = dialog.find('input.currentValue').val();
|
||||
const dialog = Template.textEntryDialog.dialog;
|
||||
const currentValue = dialog.find('input.currentValue').val();
|
||||
|
||||
// If currentValue input is undefined, prevent saving this rule
|
||||
if (currentValue === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var doneCallback = Template.textEntryDialog.doneCallback;
|
||||
const doneCallback = Template.textEntryDialog.doneCallback;
|
||||
if (doneCallback) {
|
||||
doneCallback(currentValue);
|
||||
}
|
||||
@ -89,7 +95,7 @@ Template.textEntryDialog.events({
|
||||
/**
|
||||
* Allow the user to click the Cancel button to close the dialog
|
||||
*/
|
||||
'click .cancel': function() {
|
||||
'click .cancel'() {
|
||||
closeHandler(Template.textEntryDialog.dialog);
|
||||
},
|
||||
/**
|
||||
@ -98,22 +104,22 @@ Template.textEntryDialog.events({
|
||||
* @param event The Keydown event details
|
||||
* @returns {boolean} Return false to prevent bubbling of the event
|
||||
*/
|
||||
'keydown .textEntryDialog': function(event) {
|
||||
var dialog = Template.textEntryDialog.dialog;
|
||||
'keydown .textEntryDialog'(event) {
|
||||
const dialog = Template.textEntryDialog.dialog;
|
||||
|
||||
// If Esc key is pressed, close the dialog
|
||||
if (event.which === keys.ESC) {
|
||||
closeHandler(dialog);
|
||||
return false;
|
||||
} else if (event.which === keys.ENTER) {
|
||||
var currentValue = dialog.find('input.currentValue').val();
|
||||
const currentValue = dialog.find('input.currentValue').val();
|
||||
|
||||
// If currentValue input is undefined, prevent saving this rule
|
||||
if (currentValue === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var doneCallback = Template.textEntryDialog.doneCallback;
|
||||
const doneCallback = Template.textEntryDialog.doneCallback;
|
||||
if (doneCallback) {
|
||||
doneCallback(currentValue);
|
||||
}
|
||||
@ -128,9 +134,9 @@ Template.textEntryDialog.events({
|
||||
* @param event The Change event for the input
|
||||
* @param template The current template context
|
||||
*/
|
||||
'change input.currentValue': function(event, template) {
|
||||
'change input.currentValue'(event, template) {
|
||||
// Get the DOM element representing the input box
|
||||
var input = $(event.currentTarget);
|
||||
const input = $(event.currentTarget);
|
||||
|
||||
// Update the template data with the current value
|
||||
Template.textEntryDialog.currentValue = input.val();
|
||||
|
||||
@ -189,8 +189,8 @@ var dialogPolyfill = (function() {
|
||||
|
||||
dialogPolyfill.registerDialog = function(element) {
|
||||
if (element.show) {
|
||||
console.warn("This browser already supports <dialog>, the polyfill " +
|
||||
"may not work correctly.");
|
||||
// console.warn("This browser already supports <dialog>, the polyfill " +
|
||||
// "may not work correctly.");
|
||||
}
|
||||
element.show = dialogPolyfill.showDialog.bind(element, false);
|
||||
element.showModal = dialogPolyfill.showDialog.bind(element, true);
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
import { Session } from 'meteor/session';
|
||||
// OHIF Modules
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
// Local Modules
|
||||
import { getImageId } from '../../../lib/getImageId.js';
|
||||
|
||||
Template.imageThumbnail.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
@ -26,6 +30,10 @@ Template.imageThumbnail.onRendered(() => {
|
||||
const element = $element.get(0);
|
||||
|
||||
instance.refreshImage = () => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable cornerstone for thumbnail element and remove its canvas
|
||||
cornerstone.disable(element);
|
||||
|
||||
@ -75,7 +83,9 @@ Template.imageThumbnail.onDestroyed(() => {
|
||||
const $element = $parent.find('.imageThumbnailCanvas');
|
||||
const element = $element.get(0);
|
||||
|
||||
cornerstone.disable(element);
|
||||
if (element) {
|
||||
cornerstone.disable(element);
|
||||
}
|
||||
});
|
||||
|
||||
Template.imageThumbnail.helpers({
|
||||
|
||||
@ -1,202 +1,9 @@
|
||||
const cloneElement = (element, targetId) => {
|
||||
// Clone the DOM element
|
||||
const clone = element.cloneNode(true);
|
||||
import { Template } from 'meteor/templating';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { Session } from 'meteor/session';
|
||||
|
||||
// Find any canvas children to clone
|
||||
const clonedCanvases = $(clone).find('canvas');
|
||||
clonedCanvases.each((canvasIndex, clonedCanvas) => {
|
||||
// Draw from the original canvas to the cloned canvas
|
||||
const context = clonedCanvas.getContext('2d');
|
||||
const thumbnailCanvas = $(element).find('canvas').get(canvasIndex);
|
||||
context.drawImage(thumbnailCanvas, 0, 0);
|
||||
});
|
||||
|
||||
// Update the clone with the targetId
|
||||
clone.id = targetId;
|
||||
clone.style.visibility = 'hidden';
|
||||
return clone;
|
||||
};
|
||||
|
||||
const thumbnailDragStartHandler = (event, data) => {
|
||||
// Prevent any scrolling behaviour normally caused by the original event
|
||||
event.originalEvent.preventDefault();
|
||||
|
||||
// Identify the current study and series index from the thumbnail's DOM position
|
||||
const targetThumbnail = event.currentTarget;
|
||||
const $imageThumbnail = $(targetThumbnail);
|
||||
|
||||
// Clone the image thumbnail
|
||||
const targetId = 'DragClone';
|
||||
const clone = cloneElement(targetThumbnail, targetId);
|
||||
const $clone = $(clone);
|
||||
$clone.addClass('imageThumbnailClone');
|
||||
|
||||
// Set pointerEvents to pass through the clone DOM element
|
||||
// This is necessary in order to identify what is below it
|
||||
// when using document.elementFromPoint
|
||||
clone.style.pointerEvents = 'none';
|
||||
|
||||
// Append the clone to the body
|
||||
document.body.appendChild(clone);
|
||||
|
||||
// Set the cursor x and y positions from the current touch/mouse coordinates
|
||||
let cursorX, cursorY;
|
||||
// Handle touchStart cases
|
||||
if (event.type === 'touchstart') {
|
||||
cursorX = event.originalEvent.touches[0].pageX;
|
||||
cursorY = event.originalEvent.touches[0].pageY;
|
||||
} else {
|
||||
cursorX = event.pageX;
|
||||
cursorY = event.pageY;
|
||||
|
||||
// Also hook up event handlers for mouse events
|
||||
const handlers = {};
|
||||
handlers.mousemove = event => thumbnailDragHandler(event);
|
||||
handlers.mouseup = event => thumbnailDragEndHandler(event, data, handlers);
|
||||
$(document).on('mousemove', handlers.mousemove);
|
||||
$(document).on('mouseup', handlers.mouseup);
|
||||
}
|
||||
|
||||
// This block gets the current offset of the touch/mouse
|
||||
// relative to the window
|
||||
//
|
||||
// i.e. Where did the user grab it from?
|
||||
const offset = $imageThumbnail.offset();
|
||||
const left = offset.left;
|
||||
const top = offset.top;
|
||||
|
||||
// This difference is saved for later so the element movement looks normal
|
||||
const diff = {
|
||||
x: cursorX - left,
|
||||
y: cursorY - top
|
||||
};
|
||||
$clone.data('diff', diff);
|
||||
|
||||
// This sets the default style properties of the cloned element so it is
|
||||
// ready to be dragged around the page
|
||||
$clone.css({
|
||||
left: cursorX - diff.x,
|
||||
position: 'fixed',
|
||||
top: cursorY - diff.y,
|
||||
visibility: 'hidden',
|
||||
'z-index': 100000
|
||||
});
|
||||
};
|
||||
|
||||
const thumbnailDragHandler = event => {
|
||||
// Get the touch/mouse coordinates from the event
|
||||
let cursorX, cursorY;
|
||||
if (event.type === 'touchmove') {
|
||||
cursorX = event.originalEvent.changedTouches[0].pageX;
|
||||
cursorY = event.originalEvent.changedTouches[0].pageY;
|
||||
} else {
|
||||
cursorX = event.pageX;
|
||||
cursorY = event.pageY;
|
||||
}
|
||||
|
||||
// Find the clone element and update it's position on the page
|
||||
const $clone = $('#DragClone');
|
||||
const diff = $clone.data('diff');
|
||||
$clone.css({
|
||||
left: cursorX - diff.x,
|
||||
position: 'fixed',
|
||||
top: cursorY - diff.y,
|
||||
visibility: 'visible',
|
||||
'z-index': 100000
|
||||
});
|
||||
|
||||
// Identify the element below the current cursor position
|
||||
const elemBelow = document.elementFromPoint(cursorX, cursorY);
|
||||
|
||||
// If none exists, stop here
|
||||
if (!elemBelow) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any current faded effects on viewports
|
||||
$('.imageViewerViewport canvas').removeClass('faded');
|
||||
|
||||
// Figure out what to do depending on what we're dragging over
|
||||
const $viewportsDraggedOver = $(elemBelow).parents('.imageViewerViewport');
|
||||
if ($viewportsDraggedOver.length) {
|
||||
// If we're dragging over a non-empty viewport, fade it and change the cursor style
|
||||
$viewportsDraggedOver.find('canvas').not('.magnifyTool').addClass('faded');
|
||||
document.body.style.cursor = 'copy';
|
||||
} else if (elemBelow.classList.contains('imageViewerViewport') && elemBelow.classList.contains('empty')) {
|
||||
// If we're dragging over an empty viewport, just change the cursor style
|
||||
document.body.style.cursor = 'copy';
|
||||
} else {
|
||||
// Otherwise, keep the cursor as no-drop style
|
||||
document.body.style.cursor = 'no-drop';
|
||||
}
|
||||
};
|
||||
|
||||
const thumbnailDragEndHandler = (event, data, handlers) => {
|
||||
// Remove the mouse event listeners
|
||||
if (handlers) {
|
||||
$(document).off('mousemove', handlers.mousemove);
|
||||
$(document).off('mouseup', handlers.mouseup);
|
||||
}
|
||||
|
||||
// Reset the cursor style to the default
|
||||
document.body.style.cursor = 'auto';
|
||||
|
||||
// Get the cloned element
|
||||
const $clone = $('#DragClone');
|
||||
|
||||
// If it doesn't exist, stop here
|
||||
if (!$clone.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const top = $clone.offset().top;
|
||||
const left = $clone.offset().left;
|
||||
const diff = $clone.data('diff');
|
||||
|
||||
// Identify the element below the cloned element position
|
||||
const elemBelow = document.elementFromPoint(left + diff.x, top + diff.y);
|
||||
|
||||
// Remove all cloned elements from the page
|
||||
$('.imageThumbnailClone').remove();
|
||||
|
||||
// Remove any current faded effects on viewports
|
||||
$('.imageViewerViewport canvas').removeClass('faded');
|
||||
|
||||
// If none exists, stop here
|
||||
if (!elemBelow) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any fade effects on the element below
|
||||
elemBelow.classList.remove('faded');
|
||||
|
||||
let element;
|
||||
const $viewportsDraggedOver = $(elemBelow).closest('.imageViewerViewport');
|
||||
if ($viewportsDraggedOver.length) {
|
||||
// If we're dragging over a non-empty viewport, retrieve it
|
||||
element = $viewportsDraggedOver.get(0);
|
||||
} else if (elemBelow.classList.contains('imageViewerViewport') &&
|
||||
elemBelow.classList.contains('empty')) {
|
||||
// If we're dragging over an empty viewport, retrieve that instead
|
||||
element = elemBelow;
|
||||
} else {
|
||||
// Otherwise, stop here
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is no stored drag and drop data, stop here
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the dropped viewport index
|
||||
const viewportIndex = $('.imageViewerViewport').index(element);
|
||||
|
||||
// Rerender the viewport using the dragged thumbnail data
|
||||
window.layoutManager.rerenderViewportWithNewDisplaySet(viewportIndex, data);
|
||||
|
||||
return false;
|
||||
};
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { thumbnailDragHandlers } from '../../../lib/thumbnailDragHandlers';
|
||||
|
||||
Template.thumbnailEntry.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
@ -209,16 +16,16 @@ Template.thumbnailEntry.events({
|
||||
// Event handlers for drag and drop
|
||||
'touchstart .thumbnailEntry, mousedown .thumbnailEntry'(event, instance) {
|
||||
const data = instance.data.thumbnail.stack;
|
||||
instance.isDragAndDrop && thumbnailDragStartHandler(event, data);
|
||||
instance.isDragAndDrop && thumbnailDragHandlers.thumbnailDragStartHandler(event, data);
|
||||
},
|
||||
|
||||
'touchmove .thumbnailEntry'(event, instance) {
|
||||
instance.isDragAndDrop && thumbnailDragHandler(event);
|
||||
instance.isDragAndDrop && thumbnailDragHandlers.thumbnailDragHandler(event);
|
||||
},
|
||||
|
||||
'touchend .thumbnailEntry'(event, instance) {
|
||||
const data = instance.data.thumbnail.stack;
|
||||
instance.isDragAndDrop && thumbnailDragEndHandler(event, data);
|
||||
instance.isDragAndDrop && thumbnailDragHandlers.thumbnailDragEndHandler(event, data);
|
||||
},
|
||||
|
||||
// Event handlers for click (quick switch)
|
||||
@ -228,7 +35,7 @@ Template.thumbnailEntry.events({
|
||||
const data = instance.data.thumbnail.stack;
|
||||
|
||||
// Rerender the viewport using the clicked thumbnail data
|
||||
window.layoutManager.rerenderViewportWithNewDisplaySet(instance.data.viewportIndex, data);
|
||||
OHIF.viewerbase.layoutManager.rerenderViewportWithNewDisplaySet(instance.data.viewportIndex, data);
|
||||
}
|
||||
},
|
||||
|
||||
@ -241,7 +48,7 @@ Template.thumbnailEntry.events({
|
||||
const data = instance.data.thumbnail.stack;
|
||||
|
||||
// Rerender the viewport using the clicked thumbnail data
|
||||
window.layoutManager.rerenderViewportWithNewDisplaySet(viewportIndex, data);
|
||||
OHIF.viewerbase.layoutManager.rerenderViewportWithNewDisplaySet(viewportIndex, data);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -1,118 +1,3 @@
|
||||
// ------ Tool configuration functions ----- //
|
||||
getAnnotationTextCallback = function(doneChangingTextCallback) {
|
||||
// This handles the text entry for the annotation tool
|
||||
function keyPressHandler(e) {
|
||||
// If Enter or Esc are pressed, close the dialog
|
||||
if (e.which === 13 || e.which === 27) {
|
||||
closeHandler();
|
||||
}
|
||||
}
|
||||
|
||||
function closeHandler() {
|
||||
dialog.get(0).close();
|
||||
doneChangingTextCallback(getTextInput.val());
|
||||
// Reset the text value
|
||||
getTextInput.val('');
|
||||
|
||||
// Reset the focus to the active viewport element
|
||||
// This makes the mobile Safari keyboard close
|
||||
const element = getActiveViewportElement();
|
||||
$(element).focus();
|
||||
}
|
||||
|
||||
const dialog = $('#annotationDialog');
|
||||
if (dialog.get(0).open === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const getTextInput = $('.annotationTextInput');
|
||||
|
||||
// Focus on the text input to open the Safari keyboard
|
||||
getTextInput.focus();
|
||||
|
||||
dialog.get(0).showModal();
|
||||
|
||||
const confirm = dialog.find('.annotationDialogConfirm');
|
||||
confirm.off('click');
|
||||
confirm.on('click', () => {
|
||||
closeHandler();
|
||||
});
|
||||
|
||||
// Use keydown since keypress doesn't handle ESC in Chrome
|
||||
dialog.off('keydown');
|
||||
dialog.on('keydown', keyPressHandler);
|
||||
};
|
||||
|
||||
changeAnnotationTextCallback = function(data, eventData, doneChangingTextCallback) {
|
||||
const dialog = $('#relabelAnnotationDialog');
|
||||
if (dialog.get(0).open === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTouchDevice()) {
|
||||
// Center the dialog on screen on touch devices
|
||||
dialog.css({
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
margin: 'auto'
|
||||
});
|
||||
} else {
|
||||
// Place the dialog above the tool that is being relabelled
|
||||
dialog.css({
|
||||
top: eventData.currentPoints.page.y - dialog.outerHeight() - 20,
|
||||
left: eventData.currentPoints.page.x - dialog.outerWidth() / 2
|
||||
});
|
||||
}
|
||||
|
||||
const getTextInput = dialog.find('.annotationTextInput');
|
||||
const confirm = dialog.find('.relabelConfirm');
|
||||
const remove = dialog.find('.relabelRemove');
|
||||
|
||||
getTextInput.val(data.text);
|
||||
|
||||
// Focus on the text input to open the Safari keyboard
|
||||
getTextInput.focus();
|
||||
|
||||
dialog.get(0).showModal();
|
||||
|
||||
confirm.off('click');
|
||||
confirm.on('click', function() {
|
||||
dialog.get(0).close();
|
||||
doneChangingTextCallback(data, getTextInput.val());
|
||||
});
|
||||
|
||||
// If the remove button is clicked, delete this marker
|
||||
remove.off('click');
|
||||
remove.on('click', function() {
|
||||
dialog.get(0).close();
|
||||
doneChangingTextCallback(data, undefined, true);
|
||||
});
|
||||
|
||||
dialog.off('keydown');
|
||||
dialog.on('keydown', keyPressHandler);
|
||||
|
||||
function keyPressHandler(e) {
|
||||
// If Enter is pressed, close the dialog
|
||||
if (e.which === 13) {
|
||||
closeHandler();
|
||||
}
|
||||
}
|
||||
|
||||
function closeHandler() {
|
||||
dialog.get(0).close();
|
||||
doneChangingTextCallback(data, getTextInput.val());
|
||||
// Reset the text value
|
||||
getTextInput.val('');
|
||||
|
||||
// Reset the focus to the active viewport element
|
||||
// This makes the mobile Safari keyboard close
|
||||
const element = getActiveViewportElement();
|
||||
$(element).focus();
|
||||
}
|
||||
};
|
||||
|
||||
Template.annotationDialogs.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
const dialogIds = ['annotationDialog', 'relabelAnnotationDialog'];
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { Session } from 'meteor/session';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { viewportUtils } from '../../../lib/viewportUtils';
|
||||
import { switchToImageRelative } from '../../../lib/switchToImageRelative';
|
||||
import { switchToImageByIndex } from '../../../lib/switchToImageByIndex';
|
||||
|
||||
Template.cineDialog.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
@ -34,18 +37,15 @@ Template.cineDialog.onCreated(() => {
|
||||
OHIF.viewer.cine.framesPerSecond = rate;
|
||||
|
||||
// Update playClip toolData for this imageId
|
||||
const element = getActiveViewportElement();
|
||||
const element = viewportUtils.getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const playClipToolData = cornerstoneTools.getToolState(element, 'playClip');
|
||||
playClipToolData.data[0].framesPerSecond = OHIF.viewer.cine.framesPerSecond;
|
||||
|
||||
// If the movie is playing, stop/start to update the framerate
|
||||
if (isPlaying()) {
|
||||
if (viewportUtils.isPlaying()) {
|
||||
cornerstoneTools.stopClip(element);
|
||||
cornerstoneTools.playClip(element);
|
||||
cornerstoneTools.playClip(element, OHIF.viewer.cine.framesPerSecond);
|
||||
}
|
||||
|
||||
Session.set('UpdateCINE', Random.id());
|
||||
@ -55,7 +55,7 @@ Template.cineDialog.onCreated(() => {
|
||||
instance.api = {
|
||||
displaySetPrevious: () => OHIF.viewer.moveDisplaySets(false),
|
||||
displaySetNext: () => OHIF.viewer.moveDisplaySets(true),
|
||||
cineToggle: () => toggleCinePlay(),
|
||||
cineToggle: () => viewportUtils.toggleCinePlay(),
|
||||
cineFirst: () => switchToImageByIndex(0),
|
||||
cineLast: () => switchToImageByIndex(-1),
|
||||
cinePrevious: () => switchToImageRelative(-1),
|
||||
@ -80,13 +80,19 @@ Template.cineDialog.onCreated(() => {
|
||||
|
||||
Tracker.afterFlush(() => {
|
||||
// Get the active viewportElement
|
||||
const element = getActiveViewportElement();
|
||||
const element = viewportUtils.getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if playClip tool has been initialized...
|
||||
const playClipData = cornerstoneTools.getToolState(element, 'playClip');
|
||||
if (!playClipData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the cornerstone playClip tool data
|
||||
const toolData = cornerstoneTools.getToolState(element, 'playClip').data[0];
|
||||
const toolData = playClipData.data[0];
|
||||
|
||||
// Get the cine object
|
||||
const cine = OHIF.viewer.cine;
|
||||
@ -99,7 +105,10 @@ Template.cineDialog.onCreated(() => {
|
||||
cine.loop = _.isUndefined(cine.loop) ? true : cine.loop;
|
||||
|
||||
// Set the updated data on the form inputs
|
||||
instance.$('form:first').data('component').value(cine);
|
||||
const elementComponent = instance.$('form:first').data('component');
|
||||
if (elementComponent) {
|
||||
elementComponent.value(cine);
|
||||
}
|
||||
|
||||
// Update the session to refresh the framerate text
|
||||
Session.set('UpdateCINE', Random.id());
|
||||
@ -204,11 +213,9 @@ Template.cineDialog.onCreated(() => {
|
||||
});
|
||||
|
||||
Template.cineDialog.onRendered(() => {
|
||||
|
||||
const instance = Template.instance();
|
||||
|
||||
let dialog = instance.$('#cineDialog'),
|
||||
singleRowLayout = OHIF.uiSettings.displayEchoUltrasoundWorkflow;
|
||||
const dialog = instance.$('#cineDialog');
|
||||
const singleRowLayout = OHIF.uiSettings.displayEchoUltrasoundWorkflow;
|
||||
|
||||
// set dialog in optimal position and make sure it continues in a optimal position...
|
||||
// ... when the window has been resized
|
||||
@ -222,7 +229,10 @@ Template.cineDialog.onRendered(() => {
|
||||
instance.setResizeHandler(instance.setOptimalPosition);
|
||||
|
||||
// Make the CINE dialog bounded and draggable
|
||||
dialog.bounded().draggable({ defaultElementCursor: 'move' });
|
||||
dialog.draggable({ defaultElementCursor: 'move' });
|
||||
|
||||
// Polyfill for older browsers
|
||||
dialogPolyfill.registerDialog(dialog.get(0));
|
||||
|
||||
// Prevent dialog from being dragged when user clicks any button
|
||||
dialog.find('.cine-navigation, .cine-controls, .cine-options').on('mousedown touchstart', function (e) {
|
||||
@ -239,7 +249,7 @@ Template.cineDialog.onDestroyed(() => {
|
||||
|
||||
Template.cineDialog.events({
|
||||
'change [data-key=loop] input'(event, instance) {
|
||||
const element = getActiveViewportElement();
|
||||
const element = viewportUtils.getActiveViewportElement();
|
||||
const playClipToolData = cornerstoneTools.getToolState(element, 'playClip');
|
||||
playClipToolData.data[0].loop = $(event.currentTarget).is(':checked');
|
||||
OHIF.viewer.cine.loop = playClipToolData.data[0].loop;
|
||||
@ -254,7 +264,7 @@ Template.cineDialog.events({
|
||||
|
||||
Template.cineDialog.helpers({
|
||||
isPlaying() {
|
||||
return isPlaying();
|
||||
return viewportUtils.isPlaying();
|
||||
},
|
||||
|
||||
framerate() {
|
||||
@ -264,11 +274,11 @@ Template.cineDialog.helpers({
|
||||
|
||||
displaySetDisabled(isNext) {
|
||||
Session.get('LayoutManagerUpdated');
|
||||
return !OHIF.viewer.canMoveDisplaySets(isNext) ? 'disabled' : '';
|
||||
return !OHIF.viewerbase.layoutManager.canMoveDisplaySets(isNext) ? 'disabled' : '';
|
||||
},
|
||||
|
||||
buttonDisabled() {
|
||||
return hasMultipleFrames();
|
||||
return viewportUtils.hasMultipleFrames();
|
||||
},
|
||||
|
||||
getClassNames(baseCls) {
|
||||
|
||||
@ -1,69 +1,26 @@
|
||||
function closeHandler() {
|
||||
// Hide the lesion dialog
|
||||
$('#confirmDeleteDialog').css('display', 'none');
|
||||
import { Template } from 'meteor/templating';
|
||||
|
||||
// Remove the backdrop
|
||||
$('.removableBackdrop').remove();
|
||||
|
||||
// Remove the callback from the template data
|
||||
delete Template.confirmDeleteDialog.doneCallback;
|
||||
|
||||
// Restore the focus to the active viewport
|
||||
setFocusToActiveViewport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the confirmation dialog template and the removable backdrop element
|
||||
*
|
||||
* @param doneCallback A callback
|
||||
* @param options
|
||||
*/
|
||||
showConfirmDialog = function(doneCallback, options) {
|
||||
// Show the backdrop
|
||||
options = options || {};
|
||||
UI.renderWithData(Template.removableBackdrop, options, document.body);
|
||||
|
||||
var confirmDeleteDialog = $('#confirmDeleteDialog');
|
||||
confirmDeleteDialog.remove();
|
||||
|
||||
var viewer = document.getElementById('viewer');
|
||||
UI.renderWithData(Template.confirmDeleteDialog, options, viewer);
|
||||
|
||||
// Make sure the context menu is closed when the user clicks away
|
||||
$('.removableBackdrop').one('mousedown touchstart', function() {
|
||||
closeHandler();
|
||||
});
|
||||
|
||||
confirmDeleteDialog = $('#confirmDeleteDialog');
|
||||
confirmDeleteDialog.css('display', 'block');
|
||||
confirmDeleteDialog.focus();
|
||||
|
||||
if (doneCallback && typeof doneCallback === 'function') {
|
||||
Template.confirmDeleteDialog.doneCallback = doneCallback;
|
||||
}
|
||||
};
|
||||
import { dialogUtils } from '../../../lib/dialogUtils';
|
||||
|
||||
// Global object of key names (TODO: put this somewhere else)
|
||||
keys = {
|
||||
const keys = {
|
||||
ESC: 27,
|
||||
ENTER: 13
|
||||
};
|
||||
|
||||
Template.confirmDeleteDialog.events({
|
||||
'click #cancel, click #close': function() {
|
||||
closeHandler();
|
||||
'click #cancel, click #close'() {
|
||||
// Action canceled, just close dialog without calling callback
|
||||
dialogUtils.closeHandler(false);
|
||||
},
|
||||
'click #confirm': function() {
|
||||
var doneCallback = Template.confirmDeleteDialog.doneCallback;
|
||||
if (doneCallback && typeof doneCallback === 'function') {
|
||||
doneCallback();
|
||||
}
|
||||
|
||||
closeHandler();
|
||||
'click #confirm'() {
|
||||
// Action confirmed, close dialog and calls callback, if exists
|
||||
dialogUtils.closeHandler();
|
||||
},
|
||||
'keydown #confirmDeleteDialog': function(e) {
|
||||
'keydown #confirmDeleteDialog'(e) {
|
||||
// Action canceled, just close dialog without calling callback
|
||||
if (e.which === keys.ESC) {
|
||||
closeHandler();
|
||||
dialogUtils.closeHandler(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -71,15 +28,10 @@ Template.confirmDeleteDialog.events({
|
||||
return;
|
||||
}
|
||||
|
||||
var doneCallback = Template.confirmDeleteDialog.doneCallback;
|
||||
|
||||
// If Enter is pressed, close the dialog
|
||||
// If Enter is pressed
|
||||
if (e.which === keys.ENTER) {
|
||||
if (doneCallback && typeof doneCallback === 'function') {
|
||||
doneCallback();
|
||||
}
|
||||
|
||||
closeHandler();
|
||||
// Action confirmed, close dialog and calls callback, if exists
|
||||
dialogUtils.closeHandler();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -15,13 +15,13 @@ Template.displaySetNavigation.events({
|
||||
return;
|
||||
}
|
||||
|
||||
OHIF.viewer.moveDisplaySets(false);
|
||||
OHIF.viewerbase.layoutManager.moveDisplaySets(false);
|
||||
}
|
||||
});
|
||||
|
||||
Template.displaySetNavigation.helpers({
|
||||
disableButton(isNext) {
|
||||
Session.get('LayoutManagerUpdated');
|
||||
return !OHIF.viewer.canMoveDisplaySets(isNext);
|
||||
return !OHIF.viewerbase.layoutManager.canMoveDisplaySets(isNext);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { setActiveViewport } from '../../../lib/setActiveViewport';
|
||||
import { switchToImageByIndex } from '../../../lib/switchToImageByIndex';
|
||||
|
||||
const slideTimeoutTime = 40;
|
||||
let slideTimeout;
|
||||
|
||||
@ -33,7 +38,7 @@ Template.imageControls.events({
|
||||
OHIF.viewer.hotkeyFunctions.scrollUp();
|
||||
}
|
||||
},
|
||||
'input #imageSlider, change #imageSlider'(event) {
|
||||
'input input[type=range], change input[type=range]'(event) {
|
||||
// Note that we throttle requests to prevent the
|
||||
// user's ultrafast scrolling from firing requests too quickly.
|
||||
clearTimeout(slideTimeout);
|
||||
@ -41,7 +46,7 @@ Template.imageControls.events({
|
||||
// Using the slider in an inactive viewport
|
||||
// should cause that viewport to become active
|
||||
const slider = $(event.currentTarget);
|
||||
const newActiveElement = slider.parents().eq(2).siblings('.imageViewerViewport').get(0);
|
||||
const newActiveElement = slider.parents('.viewportContainer').find('.imageViewerViewport');
|
||||
setActiveViewport(newActiveElement);
|
||||
|
||||
// Subtract 1 here since the slider goes from 1 to N images
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { viewportUtils } from '../../../lib/viewportUtils';
|
||||
|
||||
Template.layoutButton.events({
|
||||
// TODO: Check why 'click' event won't fire?
|
||||
'mousedown .js-dropdown-toggle'(event) {
|
||||
@ -12,6 +16,6 @@ Template.layoutButton.events({
|
||||
});
|
||||
|
||||
// Open or close the layout chooser dialog
|
||||
toggleDialog($dropdown);
|
||||
viewportUtils.toggleDialog($dropdown);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import 'meteor/ohif:viewerbase';
|
||||
import { viewportUtils } from '../../../lib/viewportUtils';
|
||||
|
||||
Template.layoutChooser.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
@ -35,12 +41,12 @@ Template.layoutChooser.onRendered(() => {
|
||||
// Refresh layout chooser highlighting based on current viewports state
|
||||
instance.refreshHighlights = () => {
|
||||
// Stop here if layoutManager is not defined yet
|
||||
if (!window.layoutManager) {
|
||||
if (!OHIF.viewerbase.layoutManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the layout rows and columns amount
|
||||
const info = window.layoutManager.layoutProps;
|
||||
const info = OHIF.viewerbase.layoutManager.layoutProps;
|
||||
|
||||
// get the limiter cell
|
||||
const cell = instance.$('tr').eq(info.rows - 1).children().eq(info.columns - 1);
|
||||
@ -77,11 +83,11 @@ Template.layoutChooser.events({
|
||||
columns: columnIndex + 1
|
||||
};
|
||||
|
||||
window.layoutManager.layoutTemplateName = 'gridLayout';
|
||||
window.layoutManager.layoutProps = layoutProps;
|
||||
window.layoutManager.updateViewports();
|
||||
OHIF.viewerbase.layoutManager.layoutTemplateName = 'gridLayout';
|
||||
OHIF.viewerbase.layoutManager.layoutProps = layoutProps;
|
||||
OHIF.viewerbase.layoutManager.updateViewports();
|
||||
|
||||
const $dropdown = instance.$('.layoutChooser');
|
||||
toggleDialog($dropdown);
|
||||
viewportUtils.toggleDialog($dropdown);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Template } from 'meteor/templating';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Meteor.startup(function() {
|
||||
@ -8,43 +11,41 @@ Meteor.startup(function() {
|
||||
|
||||
var loadHandlerTimeout;
|
||||
|
||||
startLoadingHandler = function(element) {
|
||||
const startLoadingHandler = function(element) {
|
||||
clearTimeout(loadHandlerTimeout);
|
||||
loadHandlerTimeout = setTimeout(function() {
|
||||
console.log('startLoading');
|
||||
var elem = $(element);
|
||||
elem.siblings('.imageViewerErrorLoadingIndicator').css('display', 'none');
|
||||
elem.find('canvas').not('.magnifyTool').addClass("faded");
|
||||
elem.find('canvas').not('.magnifyTool').addClass('faded');
|
||||
elem.siblings('.imageViewerLoadingIndicator').css('display', 'block');
|
||||
}, OHIF.viewer.loadIndicatorDelay);
|
||||
};
|
||||
|
||||
doneLoadingHandler = function(element) {
|
||||
const doneLoadingHandler = function(element) {
|
||||
clearTimeout(loadHandlerTimeout);
|
||||
var elem = $(element);
|
||||
elem.siblings('.imageViewerErrorLoadingIndicator').css('display', 'none');
|
||||
elem.find('canvas').not('.magnifyTool').removeClass("faded");
|
||||
elem.find('canvas').not('.magnifyTool').removeClass('faded');
|
||||
elem.siblings('.imageViewerLoadingIndicator').css('display', 'none');
|
||||
};
|
||||
|
||||
errorLoadingHandler = function(element, imageId, error, source) {
|
||||
const errorLoadingHandler = function(element, imageId, error, source) {
|
||||
clearTimeout(loadHandlerTimeout);
|
||||
var elem = $(element);
|
||||
|
||||
// Could probably chain all of these, but this is more readable
|
||||
elem.find('canvas').not('.magnifyTool').removeClass("faded");
|
||||
elem.find('canvas').not('.magnifyTool').removeClass('faded');
|
||||
elem.siblings('.imageViewerLoadingIndicator').css('display', 'none');
|
||||
|
||||
// Don't display errors from the stackPrefetch tool
|
||||
if (source === "stackPrefetch") {
|
||||
if (source === 'stackPrefetch') {
|
||||
return;
|
||||
}
|
||||
|
||||
var errorLoadingIndicator = elem.siblings('.imageViewerErrorLoadingIndicator');
|
||||
errorLoadingIndicator.css('display', 'block');
|
||||
|
||||
var cleanedImageId = imageId;
|
||||
|
||||
// This is just used to expand upon some error messages that are sent
|
||||
// when things fail. An example is a network error throwing the error
|
||||
// which is only described as "network".
|
||||
@ -57,7 +58,7 @@ errorLoadingHandler = function(element, imageId, error, source) {
|
||||
error = errorDetails[error];
|
||||
}
|
||||
|
||||
errorLoadingIndicator.find('.description').text("An error has occurred while loading image: " + cleanedImageId);
|
||||
errorLoadingIndicator.find('.description').text("An error has occurred while loading image: " + imageId);
|
||||
if (error) {
|
||||
errorLoadingIndicator.find('.details').text("Details: " + error);
|
||||
}
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { viewportUtils } from '../../../lib/viewportUtils';
|
||||
|
||||
Template.playClipButton.helpers({
|
||||
isPlaying: function() {
|
||||
return isPlaying();
|
||||
return viewportUtils.isPlaying();
|
||||
},
|
||||
disableButton() {
|
||||
return hasMultipleFrames();
|
||||
return viewportUtils.hasMultipleFrames();
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { Session } from 'meteor/session';
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import 'meteor/ohif:viewerbase';
|
||||
|
||||
Template.studySeriesQuickSwitch.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
@ -10,7 +14,7 @@ Template.studySeriesQuickSwitch.onCreated(() => {
|
||||
|
||||
// Gets the viewport data for the given viewport index
|
||||
instance.getViewportData = viewportIndex => {
|
||||
const layoutManager = window.layoutManager;
|
||||
const layoutManager = OHIF.viewerbase.layoutManager;
|
||||
return layoutManager && layoutManager.viewportData && layoutManager.viewportData[viewportIndex];
|
||||
};
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@ import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
import { _ } from 'meteor/underscore';
|
||||
|
||||
import { OHIFError } from '../../../lib/classes/OHIFError';
|
||||
|
||||
Template.studyTimepointBrowser.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
@ -36,7 +38,7 @@ Template.studyTimepointBrowser.onCreated(() => {
|
||||
|
||||
const notYetLoaded = StudyListStudies.findOne(query);
|
||||
if (!notYetLoaded) {
|
||||
throw 'No study data available for Study: ' + studyInstanceUid;
|
||||
throw new OHIFError(`No study data available for Study: ${studyInstanceUid}`);
|
||||
}
|
||||
|
||||
return notYetLoaded;
|
||||
|
||||
@ -3,6 +3,7 @@ import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { sortingManager } from '../../../lib/sortingManager';
|
||||
|
||||
Template.studyTimepointStudy.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
@ -16,11 +17,6 @@ Template.studyTimepointStudy.onCreated(() => {
|
||||
return isGlobal ? $(selector) : instance.$browser.find(selector);
|
||||
};
|
||||
|
||||
// Set all the studies with the same uid to loading state
|
||||
instance.setLoading = () => {
|
||||
|
||||
};
|
||||
|
||||
// Set the current study as selected in the studies list
|
||||
instance.select = (isQuickSwitch=false) => {
|
||||
const studyInstanceUid = instance.data.study.studyInstanceUid;
|
||||
@ -110,7 +106,7 @@ Template.studyTimepointStudy.events({
|
||||
const $studies = instance.getStudyElement(true);
|
||||
$studies.trigger('loadStarted');
|
||||
getStudyMetadata(studyInstanceUid, study => {
|
||||
study.displaySets = createStacks(study);
|
||||
study.displaySets = sortingManager.getDisplaySets(study);
|
||||
instance.data.study = study;
|
||||
ViewerStudies.insert(study, () => {
|
||||
// To make sure studies are rendered in the DOM
|
||||
|
||||
@ -2,6 +2,7 @@ import { OHIF } from 'meteor/ohif:core';
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { toolManager } from '../../../lib/toolManager';
|
||||
|
||||
Template.toolbarSectionButton.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
@ -1,5 +1,20 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { unloadHandlers } from '../../../lib/unloadHandlers.js';
|
||||
// Local Modules
|
||||
import { toolManager } from '../../../lib/toolManager';
|
||||
import { unloadHandlers } from '../../../lib/unloadHandlers';
|
||||
import { hotkeyUtils } from '../../../lib/hotkeyUtils';
|
||||
import { ResizeViewportManager } from '../../../lib/classes/ResizeViewportManager';
|
||||
import { LayoutManager } from '../../../lib/classes/LayoutManager';
|
||||
|
||||
Meteor.startup(() => {
|
||||
// Create the synchronizer used to update reference lines
|
||||
OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer('CornerstoneNewImage', cornerstoneTools.updateImageSynchronizer);
|
||||
|
||||
window.ResizeViewportManager = window.ResizeViewportManager || new ResizeViewportManager();
|
||||
});
|
||||
|
||||
Template.viewerMain.onCreated(() => {
|
||||
// Attach the Window resize listener
|
||||
@ -8,23 +23,22 @@ Template.viewerMain.onCreated(() => {
|
||||
// and change it to jQuery._data(window, 'events')['resize'].
|
||||
// Otherwise this function will be probably overrided.
|
||||
// See cineDialog instance.setResizeHandler function
|
||||
window.addEventListener('resize', handleResize);
|
||||
window.addEventListener('resize', window.ResizeViewportManager.handleResize.bind(window.ResizeViewportManager));
|
||||
|
||||
// Add beforeUnload event handler to check for unsaved changes
|
||||
window.addEventListener('beforeunload', unloadHandlers.beforeUnload);
|
||||
|
||||
// Create the synchronizer used to update reference lines
|
||||
OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer('CornerstoneNewImage', cornerstoneTools.updateImageSynchronizer);
|
||||
});
|
||||
|
||||
Template.viewerMain.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
HP.ProtocolStore.onReady(() => {
|
||||
instance.subscribe('hangingprotocols', () => {
|
||||
const { studies, currentTimepointId, measurementApi, timepointIds } = instance.data;
|
||||
const parentElement = instance.$('#layoutManagerTarget').get(0);
|
||||
window.layoutManager = new LayoutManager(parentElement, studies);
|
||||
|
||||
OHIF.viewerbase.layoutManager = new LayoutManager(parentElement, studies);
|
||||
|
||||
OHIF.viewerbase.layoutManager.updateViewports();
|
||||
// Default actions for Associated Studies
|
||||
if(currentTimepointId) {
|
||||
// Follow-up studies: same as the first measurement in the table
|
||||
@ -73,18 +87,22 @@ Template.viewerMain.onRendered(() => {
|
||||
}
|
||||
|
||||
// Toggle Measurement Table
|
||||
instance.data.state.set('rightSidebar', 'measurements');
|
||||
if(instance.data.state) {
|
||||
instance.data.state.set('rightSidebar', 'measurements');
|
||||
}
|
||||
}
|
||||
// Hide as default for single study
|
||||
else {
|
||||
instance.data.state.set('rightSidebar', null);
|
||||
if(instance.data.state) {
|
||||
instance.data.state.set('rightSidebar', null);
|
||||
}
|
||||
}
|
||||
|
||||
ProtocolEngine = new HP.ProtocolEngine(window.layoutManager, studies);
|
||||
HP.setEngine(ProtocolEngine);
|
||||
// ProtocolEngine = new HP.ProtocolEngine(OHIF.viewerbase.layoutManager, studies);
|
||||
// HP.setEngine(ProtocolEngine);
|
||||
|
||||
// Enable hotkeys
|
||||
enableHotkeys();
|
||||
hotkeyUtils.enableHotkeys();
|
||||
|
||||
Session.set('ViewerMainReady', Random.id());
|
||||
});
|
||||
@ -94,7 +112,7 @@ Template.viewerMain.onDestroyed(() => {
|
||||
OHIF.log.info('viewerMain onDestroyed');
|
||||
|
||||
// Remove the Window resize listener
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('resize', window.ResizeViewportManager.handleResize.bind(window.ResizeViewportManager));
|
||||
|
||||
// Remove beforeUnload event handler...
|
||||
window.removeEventListener('beforeunload', unloadHandlers.beforeUnload);
|
||||
@ -102,6 +120,6 @@ Template.viewerMain.onDestroyed(() => {
|
||||
// Destroy the synchronizer used to update reference lines
|
||||
OHIF.viewer.updateImageSynchronizer.destroy();
|
||||
|
||||
delete window.layoutManager;
|
||||
delete OHIF.viewerbase.layoutManager;
|
||||
delete ProtocolEngine;
|
||||
});
|
||||
|
||||
@ -1,253 +1,147 @@
|
||||
function getElementIfNotEmpty(viewportIndex) {
|
||||
// Meteor template helpers run more often than expected
|
||||
// They often seem to run just before the whole template is rendered
|
||||
// This meant that the onRendered event hadn't fired yet, so the
|
||||
// element wasn't enabled / set empty yet. The check here
|
||||
// for canvases under the 'enabled' element div is to prevent
|
||||
// 'undefined' errors from the helper functions
|
||||
|
||||
var imageViewerViewports = $('.imageViewerViewport'),
|
||||
element = imageViewerViewports.get(viewportIndex),
|
||||
canvases = imageViewerViewports.eq(viewportIndex).find('canvas');
|
||||
|
||||
if (!element || $(element).hasClass('empty') || canvases.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to make sure the element is enabled.
|
||||
try {
|
||||
var enabledElement = cornerstone.getEnabledElement(element);
|
||||
} catch(error) {
|
||||
return;
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
function getStackDataIfNotEmpty(viewportIndex) {
|
||||
const element = getElementIfNotEmpty(viewportIndex);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stackToolData = cornerstoneTools.getToolState(element, 'stack');
|
||||
if (!stackToolData ||
|
||||
!stackToolData.data ||
|
||||
!stackToolData.data.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stack = stackToolData.data[0];
|
||||
if (!stack) {
|
||||
return;
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
function getPatient(property) {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
if (!this.imageId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var patient = cornerstoneTools.metaData.get('patient', this.imageId);
|
||||
if (!patient) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return patient[property];
|
||||
}
|
||||
|
||||
function getStudy(property) {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
if (!this.imageId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var study = cornerstoneTools.metaData.get('study', this.imageId);
|
||||
if (!study) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return study[property];
|
||||
}
|
||||
|
||||
function getSeries(property) {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
if (!this.imageId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var series = cornerstoneTools.metaData.get('series', this.imageId);
|
||||
if (!series) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return series[property];
|
||||
}
|
||||
|
||||
function getInstance(property) {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
if (!this.imageId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var instance = cornerstoneTools.metaData.get('instance', this.imageId);
|
||||
if (!instance) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return instance[property];
|
||||
}
|
||||
|
||||
function getTagDisplay(property) {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
if (!this.imageId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var instance = cornerstoneTools.metaData.get('tagDisplay', this.imageId);
|
||||
if (!instance) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return instance[property];
|
||||
}
|
||||
|
||||
function getImage(viewportIndex) {
|
||||
var element = getElementIfNotEmpty(viewportIndex);
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var enabledElement;
|
||||
try {
|
||||
enabledElement = cornerstone.getEnabledElement(element);
|
||||
} catch(error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!enabledElement || !enabledElement.image) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return enabledElement.image;
|
||||
}
|
||||
|
||||
function formatDateTime(date, time) {
|
||||
return `${date} ${time}`;
|
||||
}
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { viewportOverlayUtils } from '../../../lib/viewportOverlayUtils';
|
||||
import { getElementIfNotEmpty } from '../../../lib/getElementIfNotEmpty';
|
||||
import { getStackDataIfNotEmpty } from '../../../lib/getStackDataIfNotEmpty';
|
||||
|
||||
Template.viewportOverlay.helpers({
|
||||
wwwc: function() {
|
||||
wwwc() {
|
||||
Session.get('CornerstoneImageRendered' + this.viewportIndex);
|
||||
var element = getElementIfNotEmpty(this.viewportIndex);
|
||||
|
||||
const element = getElementIfNotEmpty(this.viewportIndex);
|
||||
if (!element) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
const viewport = cornerstone.getViewport(element);
|
||||
if (!viewport) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return 'W ' + viewport.voi.windowWidth.toFixed(0) + ' L ' + viewport.voi.windowCenter.toFixed(0);
|
||||
},
|
||||
zoom: function() {
|
||||
zoom() {
|
||||
Session.get('CornerstoneImageRendered' + this.viewportIndex);
|
||||
var element = getElementIfNotEmpty(this.viewportIndex);
|
||||
|
||||
const element = getElementIfNotEmpty(this.viewportIndex);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
const viewport = cornerstone.getViewport(element);
|
||||
if (!viewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (viewport.scale * 100.0);
|
||||
},
|
||||
imageDimensions: function() {
|
||||
imageDimensions() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
var image = getImage(this.viewportIndex);
|
||||
const image = viewportOverlayUtils.getImage(this.viewportIndex);
|
||||
if (!image) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return image.width + ' x ' + image.height;
|
||||
},
|
||||
patientName: function() {
|
||||
return getPatient.call(this, 'name');
|
||||
patientName() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getPatient.call(this, 'name');
|
||||
},
|
||||
patientId: function() {
|
||||
return getPatient.call(this, 'id');
|
||||
patientId() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getPatient.call(this, 'id');
|
||||
},
|
||||
patientBirthDate: function() {
|
||||
return getPatient.call(this, 'birthDate');
|
||||
patientBirthDate() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getPatient.call(this, 'birthDate');
|
||||
},
|
||||
patientSex: function() {
|
||||
return getPatient.call(this, 'sex');
|
||||
patientSex() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getPatient.call(this, 'sex');
|
||||
},
|
||||
studyDate: function() {
|
||||
return getStudy.call(this, 'studyDate');
|
||||
studyDate() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getStudy.call(this, 'studyDate');
|
||||
},
|
||||
studyTime: function() {
|
||||
return getStudy.call(this, 'studyTime');
|
||||
studyTime() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getStudy.call(this, 'studyTime');
|
||||
},
|
||||
studyDescription: function() {
|
||||
return getStudy.call(this, 'studyDescription');
|
||||
studyDescription() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getStudy.call(this, 'studyDescription');
|
||||
},
|
||||
seriesDescription: function() {
|
||||
return getSeries.call(this, 'seriesDescription');
|
||||
seriesDescription() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getSeries.call(this, 'seriesDescription');
|
||||
},
|
||||
frameRate: function() {
|
||||
var frameTime = getInstance.call(this, 'frameTime');
|
||||
frameRate() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
const frameTime = viewportOverlayUtils.getInstance.call(this, 'frameTime');
|
||||
if (!frameTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
var frameRate = 1000 / frameTime;
|
||||
|
||||
const frameRate = 1000 / frameTime;
|
||||
return frameRate.toFixed(1);
|
||||
},
|
||||
seriesNumber: function() {
|
||||
return getSeries.call(this, 'seriesNumber');
|
||||
seriesNumber() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getSeries.call(this, 'seriesNumber');
|
||||
},
|
||||
instanceNumber: function() {
|
||||
return getInstance.call(this, 'instanceNumber');
|
||||
instanceNumber() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getInstance.call(this, 'instanceNumber');
|
||||
},
|
||||
thickness() {
|
||||
// Displays Slice Thickness (0018,0050)
|
||||
return getInstance.call(this, 'sliceThickness');
|
||||
},
|
||||
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getInstance.call(this, 'sliceThickness');
|
||||
},
|
||||
location() {
|
||||
// Displays Slice Location (0020,1041), if present.
|
||||
// - Otherwise, displays Table Position (0018,9327)
|
||||
// - TODO: Otherwise, displays a value derived from Image Position (Patient) (0020,0032)
|
||||
const sliceLocation = getInstance.call(this, 'sliceLocation');
|
||||
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
const sliceLocation = viewportOverlayUtils.getInstance.call(this, 'sliceLocation');
|
||||
if (sliceLocation !== '') {
|
||||
return sliceLocation;
|
||||
}
|
||||
|
||||
const tablePosition = getInstance.call(this, 'tablePosition');
|
||||
const tablePosition = viewportOverlayUtils.getInstance.call(this, 'tablePosition');
|
||||
if (tablePosition !== '') {
|
||||
return tablePosition;
|
||||
}
|
||||
|
||||
return getInstance.call(this, 'imagePositionPatient');
|
||||
return viewportOverlayUtils.getInstance.call(this, 'imagePositionPatient');
|
||||
},
|
||||
|
||||
spacingBetweenSlices() {
|
||||
// Displays Spacing Between Slices (0018,0088), if present.
|
||||
|
||||
// TODO: Otherwise, displays a value derived from successive values
|
||||
// of Image Position (Patient) (0020,0032) perpendicular to
|
||||
// the Image Orientation (Patient) (0020,0037)
|
||||
return getInstance.call(this, 'spacingBetweenSlices');
|
||||
},
|
||||
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getInstance.call(this, 'spacingBetweenSlices');
|
||||
},
|
||||
compression() {
|
||||
// Displays whether or not lossy compression has been applied:
|
||||
//
|
||||
@ -256,11 +150,12 @@ Template.viewportOverlay.helpers({
|
||||
// and Lossy Image Compression Method (0028,2114)
|
||||
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
if (!this.imageId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var instance = cornerstoneTools.metaData.get('instance', this.imageId);
|
||||
const instance = cornerstoneTools.metaData.get('instance', this.imageId);
|
||||
if (!instance) {
|
||||
return '';
|
||||
}
|
||||
@ -274,43 +169,56 @@ Template.viewportOverlay.helpers({
|
||||
|
||||
return 'Lossless / Uncompressed';
|
||||
},
|
||||
|
||||
tagDisplayLeftOnly: function() {
|
||||
return getTagDisplay.call(this, 'side') === 'L';
|
||||
},
|
||||
tagDisplayRightOnly: function() {
|
||||
return getTagDisplay.call(this, 'side') === 'R';
|
||||
},
|
||||
tagDisplaySpecified: function() {
|
||||
return getTagDisplay.call(this, 'side');
|
||||
},
|
||||
imageIndex: function() {
|
||||
tagDisplayLeftOnly() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
var stack = getStackDataIfNotEmpty(this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getTagDisplay.call(this, 'side') === 'L';
|
||||
},
|
||||
tagDisplayRightOnly() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getTagDisplay.call(this, 'side') === 'R';
|
||||
},
|
||||
tagDisplaySpecified() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getTagDisplay.call(this, 'side');
|
||||
},
|
||||
imageNumber() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
return viewportOverlayUtils.getInstance.call(this, 'number');
|
||||
},
|
||||
imageIndex() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
const stack = getStackDataIfNotEmpty(this.viewportIndex);
|
||||
if (!stack || stack.currentImageIdIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
return stack.currentImageIdIndex + 1;
|
||||
},
|
||||
numImages: function() {
|
||||
numImages() {
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
var stack = getStackDataIfNotEmpty(this.viewportIndex);
|
||||
|
||||
const stack = getStackDataIfNotEmpty(this.viewportIndex);
|
||||
if (!stack || !stack.imageIds) {
|
||||
return;
|
||||
}
|
||||
|
||||
return stack.imageIds.length;
|
||||
},
|
||||
prior: function() {
|
||||
prior() {
|
||||
// This helper is updated whenever a new image is displayed in the viewport
|
||||
Session.get('CornerstoneNewImage' + this.viewportIndex);
|
||||
|
||||
if (!this.imageId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure there are more than two studies loaded in the viewer
|
||||
var viewportStudies = ViewerStudies.find();
|
||||
const viewportStudies = ViewerStudies.find();
|
||||
if (viewportStudies.count() < 2) {
|
||||
return;
|
||||
}
|
||||
@ -319,18 +227,18 @@ Template.viewportOverlay.helpers({
|
||||
// that we can obtain the oldest study as the first element of the array
|
||||
//
|
||||
// TODO= Find out if we should encode studyDate as a Date in the ViewerStudies Collection
|
||||
var viewportStudiesArray = _.sortBy(viewportStudies.fetch(), function(study) {
|
||||
return formatDateTime(study.studyDate, study.studyTime);
|
||||
const viewportStudiesArray = _.sortBy(viewportStudies.fetch(), function(study) {
|
||||
return viewportOverlayUtils.formatDateTime(study.studyDate, study.studyTime);
|
||||
});
|
||||
|
||||
// Get study data
|
||||
var study = cornerstoneTools.metaData.get('study', this.imageId);
|
||||
const study = cornerstoneTools.metaData.get('study', this.imageId);
|
||||
if (!study) {
|
||||
return;
|
||||
}
|
||||
|
||||
var oldestStudy = viewportStudiesArray[0];
|
||||
if (formatDateTime(study.studyDate, study.studyTime) <= formatDateTime(oldestStudy.studyDate, oldestStudy.studyTime)) {
|
||||
const oldestStudy = viewportStudiesArray[0];
|
||||
if (viewportOverlayUtils.formatDateTime(study.studyDate, study.studyTime) <= viewportOverlayUtils.formatDateTime(oldestStudy.studyDate, oldestStudy.studyTime)) {
|
||||
return 'Prior';
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,7 +185,8 @@ import { OHIFError } from './lib/classes/OHIFError';
|
||||
Viewerbase.OHIFError = OHIFError;
|
||||
|
||||
/**
|
||||
* Imports for Side Effects Only
|
||||
* Imports for Side Effects Only (Files that do not export anything...)
|
||||
*/
|
||||
|
||||
import './lib/stackImagePositionOffsetSynchronizer.js';
|
||||
import './lib/debugReactivity';
|
||||
|
||||
@ -1,28 +1,86 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { viewportUtils } from './viewportUtils';
|
||||
|
||||
applyWLPreset = function(presetName, element) {
|
||||
OHIF.log.info("Applying WL Preset: " + presetName);
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
// TODO: add this to a namespace definition
|
||||
Meteor.startup(function() {
|
||||
OHIF.viewer.defaultWLPresets = {
|
||||
SoftTissue: {
|
||||
wc: 40,
|
||||
ww: 400
|
||||
},
|
||||
Lung: {
|
||||
wc: -600,
|
||||
ww: 1500
|
||||
},
|
||||
Liver: {
|
||||
wc: 90,
|
||||
ww: 150
|
||||
},
|
||||
Bone: {
|
||||
wc: 480,
|
||||
ww: 2500
|
||||
},
|
||||
Brain: {
|
||||
wc: 40,
|
||||
ww: 80
|
||||
}
|
||||
};
|
||||
|
||||
if (presetName === 'Default') {
|
||||
var enabledElement = cornerstone.getEnabledElement(element);
|
||||
viewport.voi.windowWidth = enabledElement.image.windowWidth;
|
||||
viewport.voi.windowCenter = enabledElement.image.windowCenter;
|
||||
} else {
|
||||
var preset = OHIF.viewer.wlPresets[presetName];
|
||||
viewport.voi.windowWidth = preset.ww;
|
||||
viewport.voi.windowCenter = preset.wc;
|
||||
// For now
|
||||
OHIF.viewer.wlPresets = OHIF.viewer.defaultWLPresets;
|
||||
});
|
||||
|
||||
function applyWLPreset(presetName, element) {
|
||||
OHIF.log.info('Applying WL Preset: ' + presetName);
|
||||
if (presetName !== 'Custom') {
|
||||
const viewport = cornerstone.getViewport(element);
|
||||
|
||||
if (presetName === 'Default') {
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
viewport.voi.windowWidth = enabledElement.image.windowWidth;
|
||||
viewport.voi.windowCenter = enabledElement.image.windowCenter;
|
||||
} else {
|
||||
const preset = OHIF.viewer.wlPresets[presetName];
|
||||
if(preset) {
|
||||
viewport.voi.windowWidth = preset.ww;
|
||||
viewport.voi.windowCenter = preset.wc;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the viewport
|
||||
cornerstone.setViewport(element, viewport);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the viewport
|
||||
cornerstone.setViewport(element, viewport);
|
||||
};
|
||||
|
||||
applyWLPresetToActiveElement = function(presetName) {
|
||||
var element = getActiveViewportElement();
|
||||
function applyWLPresetToActiveElement(presetName) {
|
||||
const element = viewportUtils.getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyWLPreset(presetName, element);
|
||||
|
||||
// To perform reactivity in other components
|
||||
Session.set('WLPresetActiveElement', presetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides OHIF's wlPresets
|
||||
* @param {Object} wlPresets Object with wlPresets mapping
|
||||
*/
|
||||
function setOHIFWLPresets(wlPresets) {
|
||||
OHIF.viewer.wlPresets = wlPresets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export functions inside WLPresets namespace.
|
||||
*/
|
||||
|
||||
const WLPresets = {
|
||||
applyWLPreset,
|
||||
applyWLPresetToActiveElement,
|
||||
setOHIFWLPresets
|
||||
};
|
||||
|
||||
export { WLPresets };
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import { ImageSet } from './classes/ImageSet';
|
||||
import { isImage } from './isImage';
|
||||
|
||||
/**
|
||||
* Creates a set of series to be placed in the Study Browser
|
||||
* The series that appear in the Study Browser must represent
|
||||
@ -10,7 +13,7 @@
|
||||
* @param study The study instance to be used
|
||||
* @returns {Array} An array of series to be placed in the Study Browser
|
||||
*/
|
||||
createStacks = function(study) {
|
||||
function createStacks(study) {
|
||||
// Define an empty array of display sets
|
||||
var displaySets = [];
|
||||
|
||||
@ -38,33 +41,21 @@ createStacks = function(study) {
|
||||
let displaySet;
|
||||
if (isMultiFrame(instance)) {
|
||||
displaySet = makeDisplaySet(series, [ instance ]);
|
||||
displaySet.isClip = true;
|
||||
|
||||
// Include the study instance Uid for drag/drop purposes
|
||||
displaySet.studyInstanceUid = study.studyInstanceUid;
|
||||
|
||||
// Override the default value of instances.length
|
||||
displaySet.numImageFrames = instance.numFrames;
|
||||
|
||||
// Include the instance number
|
||||
displaySet.instanceNumber = instance.instanceNumber;
|
||||
|
||||
// Include the acquisition datetime
|
||||
displaySet.acquisitionDatetime = instance.acquisitionDatetime;
|
||||
|
||||
displaySet.setAttributes({
|
||||
isClip: true,
|
||||
studyInstanceUid: study.studyInstanceUid, // Include the study instance Uid for drag/drop purposes
|
||||
numImageFrames: instance.numFrames, // Override the default value of instances.length
|
||||
instanceNumber: instance.instanceNumber, // Include the instance number
|
||||
acquisitionDatetime: instance.acquisitionDatetime // Include the acquisition datetime
|
||||
});
|
||||
displaySets.push(displaySet);
|
||||
} else if (isSingleImageModality(instance.modality)) {
|
||||
displaySet = makeDisplaySet(series, [ instance ]);
|
||||
|
||||
// Include the study instance Uid
|
||||
displaySet.studyInstanceUid = study.studyInstanceUid;
|
||||
|
||||
// Include the instance number
|
||||
displaySet.instanceNumber = instance.instanceNumber;
|
||||
|
||||
// Include the acquisition datetime
|
||||
displaySet.acquisitionDatetime = instance.acquisitionDatetime;
|
||||
|
||||
displaySet.setAttributes({
|
||||
studyInstanceUid: study.studyInstanceUid, // Include the study instance Uid
|
||||
instanceNumber: instance.instanceNumber, // Include the instance number
|
||||
acquisitionDatetime: instance.acquisitionDatetime // Include the acquisition datetime
|
||||
});
|
||||
displaySets.push(displaySet);
|
||||
} else {
|
||||
stackableInstances.push(instance);
|
||||
@ -73,7 +64,7 @@ createStacks = function(study) {
|
||||
|
||||
if (stackableInstances.length) {
|
||||
let displaySet = makeDisplaySet(series, stackableInstances);
|
||||
displaySet.studyInstanceUid = study.studyInstanceUid;
|
||||
displaySet.setAttribute('studyInstanceUid', study.studyInstanceUid);
|
||||
displaySets.push(displaySet);
|
||||
}
|
||||
});
|
||||
@ -84,32 +75,32 @@ createStacks = function(study) {
|
||||
function makeDisplaySet(series, instances) {
|
||||
const instance = instances[0];
|
||||
|
||||
let displaySet = {
|
||||
const imageSet = new ImageSet(instances);
|
||||
|
||||
// set appropriate attributes to image set...
|
||||
imageSet.setAttributes({
|
||||
displaySetInstanceUid: imageSet.uid, // create a local alias for the imageSet UID
|
||||
seriesInstanceUid: series.seriesInstanceUid,
|
||||
seriesNumber: series.seriesNumber,
|
||||
seriesDescription: series.seriesDescription,
|
||||
numImageFrames: instances.length,
|
||||
frameRate: instance.frameTime,
|
||||
images: instances,
|
||||
modality: series.modality,
|
||||
isMultiFrame: isMultiFrame(instance)
|
||||
};
|
||||
});
|
||||
|
||||
// Sort the images in this series
|
||||
displaySet.images.sort(function(a, b) {
|
||||
imageSet.sortBy(function(a, b) {
|
||||
if (a.instanceNumber && b.instanceNumber &&
|
||||
a.instanceNumber !== b.instanceNumber) {
|
||||
return a.instanceNumber - b.instanceNumber;
|
||||
}
|
||||
});
|
||||
|
||||
// Include the first image instance number
|
||||
displaySet.instanceNumber = displaySet.images[0].instanceNumber;
|
||||
// Include the first image instance number (after sorted)
|
||||
imageSet.setAttribute('instanceNumber', imageSet.getImage(0).instanceNumber);
|
||||
|
||||
// Create a unique ID for this stack so we can reference it
|
||||
displaySet.displaySetInstanceUid = Random.id();
|
||||
|
||||
return displaySet;
|
||||
return imageSet;
|
||||
}
|
||||
|
||||
function isSingleImageModality(modality) {
|
||||
@ -121,3 +112,9 @@ function isSingleImageModality(modality) {
|
||||
function isMultiFrame(instance) {
|
||||
return instance.numFrames > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose "createStacks"...
|
||||
*/
|
||||
|
||||
export { createStacks };
|
||||
|
||||
@ -7,21 +7,30 @@ import { OHIF } from 'meteor/ohif:core';
|
||||
*
|
||||
* @param element {node} DOM Node representing the viewport element
|
||||
*/
|
||||
displayReferenceLines = element => {
|
||||
OHIF.log.info("imageViewerViewport displayReferenceLines");
|
||||
export function displayReferenceLines(element) {
|
||||
|
||||
// Check if image plane (orientation / loction) data is present for the current image
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
const imageId = enabledElement.image.imageId;
|
||||
const imagePlane = cornerstoneTools.metaData.get('imagePlane', imageId);
|
||||
|
||||
if (!OHIF.viewer.refLinesEnabled || !imagePlane || !imagePlane.frameOfReferenceUID) {
|
||||
// Check if element is already enabled and it's image was rendered
|
||||
if(!enabledElement || !enabledElement.image) {
|
||||
OHIF.log.info('displayReferenceLines enabled element is undefined or it\'s image is not rendered');
|
||||
return;
|
||||
}
|
||||
|
||||
const imageId = enabledElement.image.imageId;
|
||||
const imagePlane = cornerstoneTools.metaData.get('imagePlane', imageId);
|
||||
|
||||
// Disable reference lines for the current element
|
||||
cornerstoneTools.referenceLines.tool.disable(element);
|
||||
|
||||
if (!OHIF.viewer.refLinesEnabled || !imagePlane || !imagePlane.frameOfReferenceUID) {
|
||||
OHIF.log.info('displayReferenceLines refLinesEnabled is not enabled, no imagePlane or no frameOfReferenceUID');
|
||||
return;
|
||||
}
|
||||
|
||||
OHIF.log.info(`displayReferenceLines for image with id: ${imageId}`);
|
||||
|
||||
// Loop through all other viewport elements and enable reference lines
|
||||
$('.imageViewerViewport').not(element).each((index, viewportElement) => {
|
||||
let imageId;
|
||||
@ -40,4 +49,4 @@ displayReferenceLines = element => {
|
||||
cornerstoneTools.referenceLines.tool.enable(viewportElement, OHIF.viewer.updateImageSynchronizer);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Meteor } from "meteor/meteor";
|
||||
import { getWADORSImageId } from './getWADORSImageId';
|
||||
|
||||
/**
|
||||
* Obtain an imageId for Cornerstone from an image instance
|
||||
@ -6,17 +7,17 @@ import { Meteor } from "meteor/meteor";
|
||||
* @param instance
|
||||
* @returns {string} The imageId to be used by Cornerstone
|
||||
*/
|
||||
getImageId = function(instance, frame) {
|
||||
export function getImageId(instance, frame) {
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance.url) {
|
||||
return instance.url;
|
||||
return instance.url;
|
||||
}
|
||||
|
||||
if (instance.wadouri) {
|
||||
var imageId = 'dicomweb:' + Meteor.absoluteUrl(instance.wadouri); // WADO-URI;
|
||||
var imageId = 'dicomweb:' + Meteor.absoluteUrl(instance.wadouri);
|
||||
if (frame !== undefined) {
|
||||
imageId += '&frame=' + frame;
|
||||
}
|
||||
@ -26,4 +27,4 @@ getImageId = function(instance, frame) {
|
||||
// TODO= Check multiframe image support with WADO-RS
|
||||
return getWADORSImageId(instance); // WADO-RS Retrieve Frame
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ import { OHIF } from 'meteor/ohif:core';
|
||||
* @returns {string} The imageId to be used by Cornerstone
|
||||
*/
|
||||
|
||||
getWADORSImageId = function(instance) {
|
||||
export function getWADORSImageId(instance) {
|
||||
var columnPixelSpacing = 1.0;
|
||||
var rowPixelSpacing = 1.0;
|
||||
if (instance.pixelSpacing) {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
|
||||
/**
|
||||
* A global Blaze UI helper to capitalizes the first letter of an input String
|
||||
*
|
||||
@ -5,10 +7,10 @@
|
||||
*
|
||||
* http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript
|
||||
*/
|
||||
UI.registerHelper('capitalizeFirstLetter', function (context) {
|
||||
Blaze.registerHelper('capitalizeFirstLetter', function (context) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
return context.charAt(0).toUpperCase() + context.slice(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
import { moment } from 'meteor/momentjs:moment';
|
||||
|
||||
/**
|
||||
* A global Blaze UI helper function to format DICOM Dates using the Moment library
|
||||
*/
|
||||
UI.registerHelper('formatDA', function (context, format, options) {
|
||||
|
||||
const formatDA = (context, format, options) => {
|
||||
if (!context) {
|
||||
return undefined;
|
||||
}
|
||||
@ -11,4 +15,8 @@ UI.registerHelper('formatDA', function (context, format, options) {
|
||||
strFormat = format;
|
||||
}
|
||||
return dateAsMoment.format(strFormat);
|
||||
});
|
||||
};
|
||||
|
||||
Blaze.registerHelper('formatDA', formatDA);
|
||||
|
||||
export { formatDA };
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
import { moment } from 'meteor/momentjs:moment';
|
||||
|
||||
/**
|
||||
* A global Blaze UI helper function to format JavaScript Dates using the Moment library
|
||||
*/
|
||||
UI.registerHelper('formatJSDate', function(context, format, options) {
|
||||
Blaze.registerHelper('formatJSDate', function(context, format, options) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
|
||||
/**
|
||||
* A global Blaze UI helper to format a float value to a specified precision
|
||||
*/
|
||||
UI.registerHelper('formatNumberPrecision', function(context, precision) {
|
||||
Blaze.registerHelper('formatNumberPrecision', function(context, precision) {
|
||||
if (context != null) {
|
||||
return parseFloat(context).toFixed(precision);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
/**
|
||||
* Formats a patient name for display purposes
|
||||
*/
|
||||
formatPN = function(context) {
|
||||
const formatPN = context => {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
@ -11,4 +12,6 @@ formatPN = function(context) {
|
||||
/**
|
||||
* A global Blaze UI helper to format a patient name for display purposes
|
||||
*/
|
||||
UI.registerHelper('formatPN', formatPN);
|
||||
Blaze.registerHelper('formatPN', formatPN);
|
||||
|
||||
export { formatPN };
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
import { moment } from 'meteor/momentjs:moment';
|
||||
|
||||
/**
|
||||
* A global Blaze UI helper to format a DICOM Time for display using the Moment library
|
||||
*/
|
||||
UI.registerHelper('formatTM', function(context, options) {
|
||||
|
||||
const formatTM = (context, options) => {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
@ -21,4 +25,8 @@ UI.registerHelper('formatTM', function(context, options) {
|
||||
}
|
||||
|
||||
return dateTime.format(format);
|
||||
});
|
||||
};
|
||||
|
||||
Blaze.registerHelper('formatTM', formatTM);
|
||||
|
||||
export { formatTM };
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
|
||||
/**
|
||||
* Helper for retrieving username given userId
|
||||
*/
|
||||
UI.registerHelper('getUsername', function(userId) {
|
||||
Blaze.registerHelper('getUsername', function(userId) {
|
||||
var user = Meteor.users.findOne({
|
||||
userId: userId
|
||||
});
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
|
||||
/**
|
||||
* Helper for checking datatype of a variable
|
||||
*/
|
||||
UI.registerHelper('ifTypeIs', function(value, match, attributeName) {
|
||||
Blaze.registerHelper('ifTypeIs', function(value, match, attributeName) {
|
||||
if (typeof(value) === match) {
|
||||
return attributeName;
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
UI.registerHelper('inc', function(value) {
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
|
||||
Blaze.registerHelper('inc', function(value) {
|
||||
return parseInt(value) + 1;
|
||||
});
|
||||
|
||||
@ -1,3 +1,11 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { viewportUtils } from '../viewportUtils';
|
||||
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import 'meteor/ohif:viewerbase';
|
||||
|
||||
/**
|
||||
* Boolean helper to identify if a series instance is active in some viewport
|
||||
*/
|
||||
@ -6,7 +14,7 @@ Template.registerHelper('isDisplaySetActive', (displaySetInstanceUid, viewportIn
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
// Stop here if layoutManager is not defined yet
|
||||
if (!window.layoutManager) {
|
||||
if (!OHIF.viewerbase.layoutManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -22,22 +30,28 @@ Template.registerHelper('isDisplaySetActive', (displaySetInstanceUid, viewportIn
|
||||
if (_.isUndefined(viewportIndex)) {
|
||||
// Get the number of viewports that are currently displayed
|
||||
// (Note, viewportData may have more entries!)
|
||||
const currentNumberOfViewports = layoutManager.getNumberOfViewports();
|
||||
const currentNumberOfViewports = OHIF.viewerbase.layoutManager.getNumberOfViewports();
|
||||
|
||||
// Get active viewport element
|
||||
const activeViewport = viewportUtils.getActiveViewportElement();
|
||||
|
||||
// Get viewports elements
|
||||
const viewportsElement = $('.imageViewerViewport');
|
||||
|
||||
// Loop through the viewport data up until the currently displayed
|
||||
// number of viewports
|
||||
let viewportData = window.layoutManager.viewportData;
|
||||
const viewportData = OHIF.viewerbase.layoutManager.viewportData;
|
||||
for (let i = 0; i < currentNumberOfViewports; i++) {
|
||||
const data = viewportData[i];
|
||||
|
||||
// If the display set is displayed in this viewport, stop here
|
||||
if (data && data.displaySetInstanceUid === displaySetInstanceUid) {
|
||||
// If the display set is displayed in this viewport and is active, stop here
|
||||
if (data && data.displaySetInstanceUid === displaySetInstanceUid && viewportsElement.get(data.viewportIndex) === activeViewport) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const data = window.layoutManager.viewportData[viewportIndex];
|
||||
const data = OHIF.viewerbase.layoutManager.viewportData[viewportIndex];
|
||||
|
||||
// If the display set is displayed in this viewport, stop here
|
||||
if (data && data.displaySetInstanceUid === displaySetInstanceUid) {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
|
||||
/**
|
||||
* Helper function to determine if the current client devices
|
||||
* is touch-capable. This can be used to modify certain aspects of the UI.
|
||||
@ -6,7 +8,7 @@
|
||||
*
|
||||
* @returns {boolean} true if the client device is touch-capable, false otherwise
|
||||
*/
|
||||
isTouchDevice = function() {
|
||||
const isTouchDevice = () => {
|
||||
return (('ontouchstart' in window) ||
|
||||
(navigator.MaxTouchPoints > 0) ||
|
||||
(navigator.msMaxTouchPoints > 0));
|
||||
@ -17,4 +19,6 @@ isTouchDevice = function() {
|
||||
*
|
||||
* @returns {boolean} true if the client device is touch-capable, false otherwise
|
||||
*/
|
||||
UI.registerHelper('isTouchDevice', isTouchDevice);
|
||||
Blaze.registerHelper('isTouchDevice', isTouchDevice);
|
||||
|
||||
export { isTouchDevice };
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
import { Session } from 'meteor/session';
|
||||
import { moment } from 'meteor/momentjs:moment';
|
||||
|
||||
/**
|
||||
* A global Blaze UI helper function to format JavaScript Dates using the Moment library
|
||||
*/
|
||||
UI.registerHelper('jsDateFromNow', function(context, format, options) {
|
||||
Blaze.registerHelper('jsDateFromNow', function(context, format, options) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
UI.registerHelper('objectEach', function(object) {
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
import { _ } from 'meteor/underscore';
|
||||
|
||||
Blaze.registerHelper('objectEach', function(object) {
|
||||
// http://stackoverflow.com/questions/30234732/how-to-print-key-and-values-in-meteor-template
|
||||
return _.map(object, function(value, key) {
|
||||
return _.extend({key: key}, value);
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
UI.registerHelper('objectToPairs', function(object) {
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
import { _ } from 'meteor/underscore';
|
||||
|
||||
Blaze.registerHelper('objectToPairs', function(object) {
|
||||
// http://stackoverflow.com/questions/30234732/how-to-print-key-and-values-in-meteor-template
|
||||
return _.map(object, function(value, key) {
|
||||
return {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
|
||||
/**
|
||||
* A global Blaze UI helper to Stringify a JavaScript object
|
||||
*
|
||||
@ -5,7 +7,7 @@
|
||||
*
|
||||
* http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript
|
||||
*/
|
||||
UI.registerHelper('prettyPrintStringify', function(context) {
|
||||
Blaze.registerHelper('prettyPrintStringify', function(context) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { Template } from 'meteor/templating';
|
||||
import { _ } from 'meteor/underscore';
|
||||
|
||||
/**
|
||||
* Global Blaze UI helper to sort array elements
|
||||
@ -54,4 +54,4 @@ function getKeyValue(object, keyPath) {
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { _ } from 'meteor/underscore';
|
||||
|
||||
/**
|
||||
* A global Blaze UI helper to get the thumbnails for the given study
|
||||
*/
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
var instanceClassViewportSettingsFunctions = {};
|
||||
getInstanceClassDefaultViewport = function(series, enabledElement, imageId) {
|
||||
var instanceClass = series.sopClassUid;
|
||||
|
||||
const instanceClassViewportSettingsFunctions = {};
|
||||
|
||||
const getInstanceClassDefaultViewport = (series, enabledElement, imageId) => {
|
||||
let instanceClass = series.sopClassUid;
|
||||
|
||||
if (!instanceClassViewportSettingsFunctions[instanceClass]) {
|
||||
return;
|
||||
@ -9,6 +11,8 @@ getInstanceClassDefaultViewport = function(series, enabledElement, imageId) {
|
||||
return instanceClassViewportSettingsFunctions[instanceClass](series, enabledElement, imageId);
|
||||
};
|
||||
|
||||
setInstanceClassDefaultViewportFunction = function(instanceClass, fn) {
|
||||
const setInstanceClassDefaultViewportFunction = (instanceClass, fn) => {
|
||||
instanceClassViewportSettingsFunctions[instanceClass] = fn;
|
||||
};
|
||||
};
|
||||
|
||||
export { getInstanceClassDefaultViewport, setInstanceClassDefaultViewportFunction };
|
||||
|
||||
@ -1,59 +1,61 @@
|
||||
import { sopClassDictionary } from './sopClassDictionary';
|
||||
|
||||
/**
|
||||
* Checks whether dicom files with specified SOP Class UID have image data
|
||||
* @param {string} sopClassUid - SOP Class UID to be checked
|
||||
* @returns {boolean} - true if it has image data
|
||||
*/
|
||||
isImage = function(sopClassUid) {
|
||||
if (sopClassUid == sopClassDictionary.ComputedRadiographyImageStorage
|
||||
|| sopClassUid == sopClassDictionary.DigitalXRayImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.DigitalXRayImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.DigitalMammographyXRayImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.DigitalMammographyXRayImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.DigitalIntraOralXRayImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.DigitalIntraOralXRayImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.CTImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedCTImageStorage
|
||||
|| sopClassUid == sopClassDictionary.LegacyConvertedEnhancedCTImageStorage
|
||||
|| sopClassUid == sopClassDictionary.UltrasoundMultiframeImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MRImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedMRImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedMRColorImageStorage
|
||||
|| sopClassUid == sopClassDictionary.LegacyConvertedEnhancedMRImageStorage
|
||||
|| sopClassUid == sopClassDictionary.UltrasoundImageStorage
|
||||
|| sopClassUid == sopClassDictionary.SecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MultiframeSingleBitSecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MultiframeGrayscaleByteSecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MultiframeGrayscaleWordSecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MultiframeTrueColorSecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.XRayAngiographicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedXAImageStorage
|
||||
|| sopClassUid == sopClassDictionary.XRayRadiofluoroscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedXRFImageStorage
|
||||
|| sopClassUid == sopClassDictionary.XRay3DAngiographicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.XRay3DCraniofacialImageStorage
|
||||
|| sopClassUid == sopClassDictionary.BreastTomosynthesisImageStorage
|
||||
|| sopClassUid == sopClassDictionary.BreastProjectionXRayImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.BreastProjectionXRayImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.NuclearMedicineImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLEndoscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VideoEndoscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLMicroscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VideoMicroscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLSlideCoordinatesMicroscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLPhotographicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VideoPhotographicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.OphthalmicPhotography8BitImageStorage
|
||||
|| sopClassUid == sopClassDictionary.OphthalmicPhotography16BitImageStorage
|
||||
|| sopClassUid == sopClassDictionary.OphthalmicTomographyImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLWholeSlideMicroscopyImageStorage
|
||||
|| sopClassUid == sopClassDictionary.PositronEmissionTomographyImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedPETImageStorage
|
||||
|| sopClassUid == sopClassDictionary.LegacyConvertedEnhancedPETImageStorage
|
||||
|| sopClassUid == sopClassDictionary.RTImageStorage) {
|
||||
export function isImage(sopClassUid) {
|
||||
if (sopClassUid === sopClassDictionary.ComputedRadiographyImageStorage
|
||||
|| sopClassUid === sopClassDictionary.DigitalXRayImageStorageForPresentation
|
||||
|| sopClassUid === sopClassDictionary.DigitalXRayImageStorageForProcessing
|
||||
|| sopClassUid === sopClassDictionary.DigitalMammographyXRayImageStorageForPresentation
|
||||
|| sopClassUid === sopClassDictionary.DigitalMammographyXRayImageStorageForProcessing
|
||||
|| sopClassUid === sopClassDictionary.DigitalIntraOralXRayImageStorageForPresentation
|
||||
|| sopClassUid === sopClassDictionary.DigitalIntraOralXRayImageStorageForProcessing
|
||||
|| sopClassUid === sopClassDictionary.CTImageStorage
|
||||
|| sopClassUid === sopClassDictionary.EnhancedCTImageStorage
|
||||
|| sopClassUid === sopClassDictionary.LegacyConvertedEnhancedCTImageStorage
|
||||
|| sopClassUid === sopClassDictionary.UltrasoundMultiframeImageStorage
|
||||
|| sopClassUid === sopClassDictionary.MRImageStorage
|
||||
|| sopClassUid === sopClassDictionary.EnhancedMRImageStorage
|
||||
|| sopClassUid === sopClassDictionary.EnhancedMRColorImageStorage
|
||||
|| sopClassUid === sopClassDictionary.LegacyConvertedEnhancedMRImageStorage
|
||||
|| sopClassUid === sopClassDictionary.UltrasoundImageStorage
|
||||
|| sopClassUid === sopClassDictionary.SecondaryCaptureImageStorage
|
||||
|| sopClassUid === sopClassDictionary.MultiframeSingleBitSecondaryCaptureImageStorage
|
||||
|| sopClassUid === sopClassDictionary.MultiframeGrayscaleByteSecondaryCaptureImageStorage
|
||||
|| sopClassUid === sopClassDictionary.MultiframeGrayscaleWordSecondaryCaptureImageStorage
|
||||
|| sopClassUid === sopClassDictionary.MultiframeTrueColorSecondaryCaptureImageStorage
|
||||
|| sopClassUid === sopClassDictionary.XRayAngiographicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.EnhancedXAImageStorage
|
||||
|| sopClassUid === sopClassDictionary.XRayRadiofluoroscopicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.EnhancedXRFImageStorage
|
||||
|| sopClassUid === sopClassDictionary.XRay3DAngiographicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.XRay3DCraniofacialImageStorage
|
||||
|| sopClassUid === sopClassDictionary.BreastTomosynthesisImageStorage
|
||||
|| sopClassUid === sopClassDictionary.BreastProjectionXRayImageStorageForPresentation
|
||||
|| sopClassUid === sopClassDictionary.BreastProjectionXRayImageStorageForProcessing
|
||||
|| sopClassUid === sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForPresentation
|
||||
|| sopClassUid === sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForProcessing
|
||||
|| sopClassUid === sopClassDictionary.NuclearMedicineImageStorage
|
||||
|| sopClassUid === sopClassDictionary.VLEndoscopicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.VideoEndoscopicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.VLMicroscopicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.VideoMicroscopicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.VLSlideCoordinatesMicroscopicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.VLPhotographicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.VideoPhotographicImageStorage
|
||||
|| sopClassUid === sopClassDictionary.OphthalmicPhotography8BitImageStorage
|
||||
|| sopClassUid === sopClassDictionary.OphthalmicPhotography16BitImageStorage
|
||||
|| sopClassUid === sopClassDictionary.OphthalmicTomographyImageStorage
|
||||
|| sopClassUid === sopClassDictionary.VLWholeSlideMicroscopyImageStorage
|
||||
|| sopClassUid === sopClassDictionary.PositronEmissionTomographyImageStorage
|
||||
|| sopClassUid === sopClassDictionary.EnhancedPETImageStorage
|
||||
|| sopClassUid === sopClassDictionary.LegacyConvertedEnhancedPETImageStorage
|
||||
|| sopClassUid === sopClassDictionary.RTImageStorage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import {parsingUtils} from './parsingUtils'
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { parsingUtils } from './parsingUtils';
|
||||
|
||||
var metaDataLookup = {};
|
||||
const metaDataLookup = {};
|
||||
|
||||
/**
|
||||
* Cornerstone MetaData provider to store image meta data
|
||||
@ -14,14 +15,14 @@ var metaDataLookup = {};
|
||||
* @param {String} imageId The Cornerstone ImageId
|
||||
* @param {Object} data An object containing instance, series, and study metaData
|
||||
*/
|
||||
addMetaData = function(imageId, data) {
|
||||
var instanceMetaData = data.instance;
|
||||
var seriesMetaData = data.series;
|
||||
var studyMetaData = data.study;
|
||||
var imageIndex = data.imageIndex;
|
||||
var numImages = data.numImages;
|
||||
const addMetaData = (imageId, data) => {
|
||||
let instanceMetaData = data.instance;
|
||||
let seriesMetaData = data.series;
|
||||
let studyMetaData = data.study;
|
||||
let imageIndex = data.imageIndex;
|
||||
let numImages = data.numImages;
|
||||
|
||||
var metaData = {};
|
||||
let metaData = {};
|
||||
|
||||
metaData.study = {
|
||||
patientId: studyMetaData.patientId,
|
||||
@ -66,8 +67,8 @@ addMetaData = function(imageId, data) {
|
||||
* @param type (e.g. series, instance, tagDisplay)
|
||||
* @param data
|
||||
*/
|
||||
addSpecificMetadata = function(imageId, type, data) {
|
||||
var metaData = {};
|
||||
const addSpecificMetadata = (imageId, type, data) => {
|
||||
let metaData = {};
|
||||
metaData[type] = data;
|
||||
|
||||
metaDataLookup[imageId] = $.extend(metaDataLookup[imageId], metaData);
|
||||
@ -92,8 +93,8 @@ function getFromDataSet(dataSet, type, tag) {
|
||||
*
|
||||
* @param image
|
||||
*/
|
||||
updateMetaData = function(image) {
|
||||
var imageMetaData = metaDataLookup[image.imageId];
|
||||
const updateMetaData = image => {
|
||||
const imageMetaData = metaDataLookup[image.imageId];
|
||||
if (!imageMetaData) {
|
||||
return;
|
||||
}
|
||||
@ -141,13 +142,13 @@ function getImagePlane(instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
var imageOrientation = instance.imageOrientationPatient.split('\\');
|
||||
var imagePosition = instance.imagePositionPatient.split('\\');
|
||||
let imageOrientation = instance.imageOrientationPatient.split('\\');
|
||||
let imagePosition = instance.imagePositionPatient.split('\\');
|
||||
|
||||
var columnPixelSpacing = 1.0;
|
||||
var rowPixelSpacing = 1.0;
|
||||
let columnPixelSpacing = 1.0;
|
||||
let rowPixelSpacing = 1.0;
|
||||
if (instance.pixelSpacing) {
|
||||
var split = instance.pixelSpacing.split('\\');
|
||||
let split = instance.pixelSpacing.split('\\');
|
||||
rowPixelSpacing = parseFloat(split[0]);
|
||||
columnPixelSpacing = parseFloat(split[1]);
|
||||
}
|
||||
@ -178,9 +179,9 @@ function getImagePlane(instance) {
|
||||
* @param dataSet {Object} An instance of dicomParser.DataSet object where multiframe information can be found.
|
||||
* @return {Object} An object containing multiframe image metadata (frameIncrementPointer, frameTime, frameTimeVector, etc).
|
||||
*/
|
||||
getMultiframeModuleMetaData = function(dataSet) {
|
||||
function getMultiframeModuleMetaData(dataSet) {
|
||||
|
||||
var numberOfFrames,
|
||||
let numberOfFrames,
|
||||
frameIncrementPointer,
|
||||
frameTime,
|
||||
frameTimeVector,
|
||||
@ -232,7 +233,7 @@ getMultiframeModuleMetaData = function(dataSet) {
|
||||
}
|
||||
|
||||
return imageInfo;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up metaData for Cornerstone Tools given a specified type and imageId
|
||||
@ -244,7 +245,7 @@ getMultiframeModuleMetaData = function(dataSet) {
|
||||
* @returns {Object} Relevant metaData of the specified type
|
||||
*/
|
||||
function provider(type, imageId) {
|
||||
var imageMetaData = metaDataLookup[imageId];
|
||||
let imageMetaData = metaDataLookup[imageId];
|
||||
if (!imageMetaData) {
|
||||
return;
|
||||
}
|
||||
@ -257,3 +258,9 @@ function provider(type, imageId) {
|
||||
Meteor.startup(function() {
|
||||
cornerstoneTools.metaData.addProvider(provider);
|
||||
});
|
||||
|
||||
/**
|
||||
* Export relevant symbols
|
||||
*/
|
||||
|
||||
export { addMetaData, addSpecificMetadata, updateMetaData };
|
||||
|
||||
@ -1,14 +1,32 @@
|
||||
import { Session } from 'meteor/session';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { enablePrefetchOnElement } from './enablePrefetchOnElement';
|
||||
import { displayReferenceLines } from './displayReferenceLines';
|
||||
|
||||
setActiveViewport = element => {
|
||||
/**
|
||||
* Sets a viewport element active
|
||||
* @param {node} element DOM element to be activated
|
||||
*/
|
||||
export function setActiveViewport(element) {
|
||||
if (!element) {
|
||||
OHIF.log.info('setActiveViewport element does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
const viewportIndex = $('.imageViewerViewport').index(element);
|
||||
const viewerports = $('.imageViewerViewport');
|
||||
const viewportIndex = viewerports.index(element);
|
||||
const jQueryElement = $(element);
|
||||
|
||||
// When an ActivateViewport event is fired, update the Meteor Session
|
||||
OHIF.log.info(`setActiveViewport setting viewport index: ${viewportIndex}`);
|
||||
|
||||
// If viewport is not active
|
||||
if(!jQueryElement.parents('.viewportContainer').hasClass('active')) {
|
||||
// Trigger an event for compatibility with other systems
|
||||
jQueryElement.trigger('OHIFBeforeActivateViewport');
|
||||
}
|
||||
|
||||
// When an OHIFActivateViewport event is fired, update the Meteor Session
|
||||
// with the viewport index that it was fired from.
|
||||
Session.set('activeViewport', viewportIndex);
|
||||
|
||||
@ -28,11 +46,19 @@ setActiveViewport = element => {
|
||||
const domElement = jQueryElement.get(0);
|
||||
enablePrefetchOnElement(domElement);
|
||||
displayReferenceLines(domElement);
|
||||
OHIF.viewer.stackImagePositionOffsetSynchronizer.update();
|
||||
// @TODO Add this to OHIFAfterActivateViewport handler...
|
||||
if (OHIF.viewer.stackImagePositionOffsetSynchronizer) {
|
||||
OHIF.viewer.stackImagePositionOffsetSynchronizer.update();
|
||||
}
|
||||
}
|
||||
|
||||
// Set the div to focused, so keypress events are handled
|
||||
//$(element).focus();
|
||||
//.focus() event breaks in FF&IE
|
||||
jQueryElement.triggerHandler('focus');
|
||||
};
|
||||
|
||||
// Trigger OHIFAfterActivateViewport event on activated instance
|
||||
// for compatibility with other systems
|
||||
jQueryElement.trigger('OHIFAfterActivateViewport');
|
||||
|
||||
}
|
||||
|
||||
@ -1,19 +1,24 @@
|
||||
import { Session } from 'meteor/session';
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
/**
|
||||
* Restores the browser focus to the currently specified active viewport
|
||||
* as determined from Meteor's Session variable.
|
||||
*
|
||||
* This is allows keydown events to be captured on the focused element
|
||||
* This is allows keydown events to be captured on the focused element.
|
||||
*/
|
||||
setFocusToActiveViewport = function() {
|
||||
const setFocusToActiveViewport = () => {
|
||||
// Get the list of viewports
|
||||
var viewports = $('.imageViewerViewport');
|
||||
const viewports = $('.imageViewerViewport');
|
||||
|
||||
// Get the current active viewport index from Session
|
||||
var activeViewportIndex = Session.get('activeViewport');
|
||||
const activeViewportIndex = Session.get('activeViewport');
|
||||
|
||||
// Find the div from the list of viewports
|
||||
var activeViewport = viewports.eq(activeViewportIndex);
|
||||
const activeViewport = viewports.eq(activeViewportIndex);
|
||||
|
||||
// Set the browser focus to this div
|
||||
activeViewport.focus();
|
||||
};
|
||||
};
|
||||
|
||||
export { setFocusToActiveViewport };
|
||||
@ -1,19 +1,24 @@
|
||||
setMammogramViewportAlignment = function(series, enabledElement, imageId) {
|
||||
// Don't apply the MG viewport alignment to other series types
|
||||
var viewTypes = ['MLO', 'CC', 'LM', 'ML', 'XCCL'];
|
||||
var requiresInversion = ['LM']; // Tomo series are flipped
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { setInstanceClassDefaultViewportFunction } from './instanceClassSpecificViewport';
|
||||
import { addSpecificMetadata } from './metaDataProvider';
|
||||
|
||||
var instance = cornerstoneTools.metaData.get('instance', imageId);
|
||||
const setMammogramViewportAlignment = (series, enabledElement, imageId) => {
|
||||
// Don't apply the MG viewport alignment to other series types
|
||||
let viewTypes = ['MLO', 'CC', 'LM', 'ML', 'XCCL'];
|
||||
let requiresInversion = ['LM']; // Tomo series are flipped
|
||||
|
||||
let instance = cornerstoneTools.metaData.get('instance', imageId);
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
var laterality = instance.laterality;
|
||||
var element = enabledElement.element;
|
||||
var position;
|
||||
let laterality = instance.laterality;
|
||||
let element = enabledElement.element;
|
||||
let position;
|
||||
|
||||
var left = $(enabledElement.canvas).offset().left;
|
||||
var right = left + enabledElement.canvas.width;
|
||||
let left = $(enabledElement.canvas).offset().left;
|
||||
let right = left + enabledElement.canvas.width;
|
||||
|
||||
if (viewTypes.indexOf(instance.viewPosition) < 0) {
|
||||
return;
|
||||
@ -58,4 +63,6 @@ setMammogramViewportAlignment = function(series, enabledElement, imageId) {
|
||||
|
||||
Meteor.startup(function() {
|
||||
setInstanceClassDefaultViewportFunction("1.2.840.10008.5.1.4.1.1.1.2", setMammogramViewportAlignment);
|
||||
});
|
||||
});
|
||||
|
||||
export { setMammogramViewportAlignment };
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
sopClassDictionary = {
|
||||
|
||||
export const sopClassDictionary = {
|
||||
ComputedRadiographyImageStorage: "1.2.840.10008.5.1.4.1.1.1",
|
||||
DigitalXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.1",
|
||||
DigitalXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.1.1",
|
||||
@ -112,4 +113,4 @@ sopClassDictionary = {
|
||||
GenericImplantTemplateStorage: "1.2.840.10008.5.1.4.43.1",
|
||||
ImplantAssemblyTemplateStorage: "1.2.840.10008.5.1.4.44.1",
|
||||
ImplantTemplateGroupStorage: "1.2.840.10008.5.1.4.45.1"
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,21 +1,19 @@
|
||||
import { OHIFError } from './classes/OHIFError';
|
||||
|
||||
/**
|
||||
* Sorts the series and instances inside a study instance by their series
|
||||
* and instance numbers in ascending order.
|
||||
*
|
||||
* @param {Object} study The study instance
|
||||
*/
|
||||
sortStudy = function(study) {
|
||||
export function sortStudy(study) {
|
||||
if (!study || !study.seriesList) {
|
||||
throw "Insufficient study data was provided to sortStudy";
|
||||
throw new OHIFError('Insufficient study data was provided to sortStudy');
|
||||
}
|
||||
|
||||
study.seriesList.sort(function(a, b) {
|
||||
return a.seriesNumber - b.seriesNumber;
|
||||
});
|
||||
|
||||
study.seriesList.forEach(function(series){
|
||||
series.instances.sort(function(a, b) {
|
||||
return a.instanceNumber - b.instanceNumber;
|
||||
});
|
||||
study.seriesList.sort((a, b) => a.seriesNumber - b.seriesNumber);
|
||||
|
||||
study.seriesList.forEach(series => {
|
||||
series.instances.sort((a, b) => a.instanceNumber - b.instanceNumber);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { viewportUtils } from './viewportUtils';
|
||||
|
||||
/**
|
||||
* This function switches to an image given an element and the index of the image in the current stack
|
||||
* Note: Negative indexing is supported:
|
||||
@ -7,7 +9,7 @@
|
||||
* @param element
|
||||
* @param {number} [newImageIdIndex] The image index in the stack to switch to.
|
||||
*/
|
||||
switchToImageByIndex = function(newImageIdIndex) {
|
||||
var element = getActiveViewportElement();
|
||||
export function switchToImageByIndex(newImageIdIndex) {
|
||||
var element = viewportUtils.getActiveViewportElement();
|
||||
cornerstoneTools.scrollToIndex(element, newImageIdIndex);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { viewportUtils } from './viewportUtils';
|
||||
|
||||
/**
|
||||
* This function switches to an image given an element and
|
||||
* the relative distance from the current image in the stack
|
||||
@ -7,7 +9,7 @@
|
||||
* @param element
|
||||
* @param {number} [distanceFromCurrentIndex] The image index in the stack to switch to.
|
||||
*/
|
||||
switchToImageRelative = function(distanceFromCurrentIndex) {
|
||||
var element = getActiveViewportElement();
|
||||
export function switchToImageRelative(distanceFromCurrentIndex) {
|
||||
var element = viewportUtils.getActiveViewportElement();
|
||||
cornerstoneTools.scroll(element, distanceFromCurrentIndex);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
updateAllViewports = function() {
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
export function updateAllViewports() {
|
||||
var viewports = $('.imageViewerViewport').not('.empty');
|
||||
viewports.each(function(index, element) {
|
||||
cornerstone.updateImage(element);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { Random } from 'meteor/random';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { _ } from 'meteor/underscore';
|
||||
// Local Modules
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { updateOrientationMarkers } from './updateOrientationMarkers';
|
||||
import { getInstanceClassDefaultViewport } from './instanceClassSpecificViewport';
|
||||
|
||||
@ -120,6 +121,16 @@ function clearTools() {
|
||||
cornerstone.updateImage(element);
|
||||
}
|
||||
|
||||
function linkStackScroll() {
|
||||
const synchronizer = OHIF.viewer.stackImagePositionOffsetSynchronizer;
|
||||
|
||||
if(synchronizer.isActive()) {
|
||||
synchronizer.deactivate();
|
||||
} else {
|
||||
synchronizer.activate();
|
||||
}
|
||||
}
|
||||
|
||||
// This function was originally defined alone inside client/lib/toggleDialog.js
|
||||
// and has been moved here to avoid circular dependency issues.
|
||||
function toggleDialog(element) {
|
||||
@ -238,6 +249,17 @@ function stopAllClips() {
|
||||
}
|
||||
|
||||
|
||||
function isStackScrollLinkingDisabled() {
|
||||
// Its called everytime active viewport and/or layout change
|
||||
Session.get('activeViewport');
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
const synchronizer = OHIF.viewer.stackImagePositionOffsetSynchronizer;
|
||||
const linkableViewports = synchronizer.getLinkableViewports();
|
||||
|
||||
return linkableViewports.length <= 1;
|
||||
}
|
||||
|
||||
// Create an event listener to update playing state when a clip stops playing
|
||||
$(window).on('CornerstoneToolsClipStopped', () => Session.set('UpdateCINE', Random.id()));
|
||||
|
||||
@ -257,12 +279,14 @@ const viewportUtils = {
|
||||
flipH,
|
||||
resetViewport,
|
||||
clearTools,
|
||||
linkStackScroll,
|
||||
toggleDialog,
|
||||
toggleCinePlay,
|
||||
toggleCineDialog,
|
||||
isPlaying,
|
||||
hasMultipleFrames,
|
||||
stopAllClips
|
||||
stopAllClips,
|
||||
isStackScrollLinkingDisabled
|
||||
};
|
||||
|
||||
export { viewportUtils };
|
||||
|
||||
@ -16,9 +16,10 @@ Package.onUse(function(api) {
|
||||
api.use('validatejs');
|
||||
|
||||
// Our custom packages
|
||||
api.use('design');
|
||||
api.use('ohif:design');
|
||||
api.use('ohif:core');
|
||||
api.use('ohif:cornerstone');
|
||||
api.use('ohif:log');
|
||||
|
||||
const assets = [
|
||||
'assets/icons.svg',
|
||||
@ -137,7 +138,6 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/components/viewer/annotationDialogs/annotationDialogs.styl', 'client');
|
||||
|
||||
api.addFiles('client/components/viewer/viewportOrientationMarkers/viewportOrientationMarkers.html', 'client');
|
||||
api.addFiles('client/components/viewer/viewportOrientationMarkers/viewportOrientationMarkers.js', 'client');
|
||||
api.addFiles('client/components/viewer/viewportOrientationMarkers/viewportOrientationMarkers.styl', 'client');
|
||||
|
||||
api.addFiles('client/components/viewer/viewportOverlay/viewportOverlay.html', 'client');
|
||||
@ -178,6 +178,10 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/components/viewer/confirmDeleteDialog/confirmDeleteDialog.js', 'client');
|
||||
api.addFiles('client/components/viewer/confirmDeleteDialog/confirmDeleteDialog.styl', 'client');
|
||||
|
||||
api.addFiles('client/components/viewer/textMarkerDialogs/textMarkerDialogs.html', 'client');
|
||||
api.addFiles('client/components/viewer/textMarkerDialogs/textMarkerDialogs.js', 'client');
|
||||
api.addFiles('client/components/viewer/textMarkerDialogs/textMarkerDialogs.styl', 'client');
|
||||
|
||||
api.addFiles('client/components/viewer/displaySetNavigation/displaySetNavigation.html', 'client');
|
||||
api.addFiles('client/components/viewer/displaySetNavigation/displaySetNavigation.js', 'client');
|
||||
|
||||
@ -197,105 +201,13 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointStudy.styl', 'client');
|
||||
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointStudy.js', 'client');
|
||||
|
||||
|
||||
// Library functions
|
||||
api.addFiles('client/lib/layoutManager.js', 'client');
|
||||
api.addFiles('client/lib/createStacks.js', 'client');
|
||||
api.addFiles('client/lib/getImageId.js', 'client');
|
||||
api.addFiles('client/lib/getWADORSImageId.js', 'client');
|
||||
api.addFiles('client/lib/metaDataProvider.js', 'client');
|
||||
api.addFiles('client/lib/sortStudy.js', 'client');
|
||||
api.addFiles('client/lib/toolManager.js', 'client');
|
||||
api.addFiles('client/lib/enablePrefetchOnElement.js', 'client');
|
||||
api.addFiles('client/lib/displayReferenceLines.js', 'client');
|
||||
api.addFiles('client/lib/toggleDialog.js', 'client');
|
||||
api.addFiles('client/lib/setActiveViewport.js', 'client');
|
||||
api.addFiles('client/lib/switchToImageByIndex.js', 'client');
|
||||
api.addFiles('client/lib/switchToImageRelative.js', 'client');
|
||||
api.addFiles('client/lib/enableHotkeys.js', 'client');
|
||||
api.addFiles('client/lib/viewportFunctions.js', 'client');
|
||||
api.addFiles('client/lib/WLPresets.js', 'client');
|
||||
api.addFiles('client/lib/resizeViewportElements.js', 'client');
|
||||
api.addFiles('client/lib/setFocusToActiveViewport.js', 'client');
|
||||
api.addFiles('client/lib/updateAllViewports.js', 'client');
|
||||
api.addFiles('client/lib/stackImagePositionOffsetSynchronizer.js', 'client');
|
||||
|
||||
api.addFiles('client/lib/instanceClassSpecificViewport.js', 'client');
|
||||
api.addFiles('client/lib/setMammogramViewportAlignment.js', 'client');
|
||||
api.addFiles('client/lib/isImage.js', 'client');
|
||||
api.addFiles('client/lib/sopClassDictionary.js', 'client');
|
||||
api.addFiles('client/lib/debugReactivity.js', 'client');
|
||||
api.addFiles('client/lib/unloadHandlers.js', 'client');
|
||||
|
||||
api.export('resizeViewportElements', 'client');
|
||||
api.export('handleResize', 'client');
|
||||
api.export('enableHotkeys', 'client');
|
||||
api.export('enablePrefetchOnElement', 'client');
|
||||
api.export('displayReferenceLines', 'client');
|
||||
api.export('setActiveViewport', 'client');
|
||||
api.export('createStacks', 'client');
|
||||
api.export('getImageId', 'client');
|
||||
api.export('getWADORSImageId', 'client');
|
||||
api.export('metaDataProvider', 'client');
|
||||
api.export('sortStudy', 'client');
|
||||
api.export('updateOrientationMarkers', 'client');
|
||||
api.export('setFocusToActiveViewport', 'client');
|
||||
api.export('updateAllViewports', 'client');
|
||||
api.export('queryStudies', 'client');
|
||||
api.export('getNumberOfFilesInStudy', 'client');
|
||||
api.export('importStudies', 'client');
|
||||
api.export('getActiveViewportElement', 'client');
|
||||
api.export('getInstanceClassDefaultViewport', 'client');
|
||||
api.export('showConfirmDialog', 'client');
|
||||
api.export('applyWLPreset', 'client');
|
||||
api.export('toggleDialog', 'client');
|
||||
api.export('isImage', 'client');
|
||||
api.export('sopClassDictionary', 'client');
|
||||
api.export('addMetaData', 'client');
|
||||
api.export('hasMultipleFrames', 'client');
|
||||
api.export('isStackScrollLinkingDisabled')
|
||||
|
||||
// Viewer management objects
|
||||
api.export('toolManager', 'client');
|
||||
api.export('LayoutManager', 'client');
|
||||
|
||||
// Collections
|
||||
api.export('ViewerStudies', 'client');
|
||||
|
||||
// UI Helpers
|
||||
api.addFiles([
|
||||
'client/lib/helpers/formatDA.js',
|
||||
'client/lib/helpers/formatJSDate.js',
|
||||
'client/lib/helpers/jsDateFromNow.js',
|
||||
'client/lib/helpers/formatNumberPrecision.js',
|
||||
'client/lib/helpers/formatTM.js',
|
||||
'client/lib/helpers/inc.js',
|
||||
'client/lib/helpers/isDisplaySetActive.js',
|
||||
'client/lib/helpers/getUsername.js',
|
||||
'client/lib/helpers/capitalizeFirstLetter.js',
|
||||
'client/lib/helpers/objectToPairs.js',
|
||||
'client/lib/helpers/objectEach.js',
|
||||
'client/lib/helpers/ifTypeIs.js',
|
||||
'client/lib/helpers/prettyPrintStringify.js',
|
||||
'client/lib/helpers/sorting.js',
|
||||
'client/lib/helpers/studyThumbnails.js',
|
||||
'client/lib/helpers/formatPN.js'
|
||||
], 'client');
|
||||
api.export('formatPN', 'client');
|
||||
api.export('dialogPolyfill', 'client');
|
||||
|
||||
api.addFiles('client/lib/helpers/isTouchDevice.js', 'client');
|
||||
api.export('isTouchDevice', 'client');
|
||||
api.mainModule('main.js', 'client');
|
||||
|
||||
// TODO: Clean this up later, no real need to export these
|
||||
// Need to move functionList into viewerbase
|
||||
api.export('toggleCineDialog', 'client');
|
||||
api.export('toggleCinePlay', 'client');
|
||||
api.export('clearTools', 'client');
|
||||
api.export('resetViewport', 'client');
|
||||
api.export('invert', 'client');
|
||||
api.export('flipV', 'client');
|
||||
api.export('flipH', 'client');
|
||||
api.export('rotateR', 'client');
|
||||
api.export('rotateL', 'client');
|
||||
api.export('link', 'client');
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user