Most JS files, all helpers and simple reconciliations

This commit is contained in:
Emanuel F. Oliveira committed 2017-01-29 22:11:17 -02:00
1 parent 21400c5836
commit 9e69c43dda
58 files changed
+776 -1025

No files matched your search

@@ -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';
}
}