diff --git a/LesionTracker/client/components/toolbarSection/toolbarSection.js b/LesionTracker/client/components/toolbarSection/toolbarSection.js
index c5e3827b6..d6a5a05b5 100644
--- a/LesionTracker/client/components/toolbarSection/toolbarSection.js
+++ b/LesionTracker/client/components/toolbarSection/toolbarSection.js
@@ -124,6 +124,14 @@ Template.toolbarSection.helpers({
svgLink: '/packages/ohif_viewerbase/assets/icons.svg#icon-tools-elliptical-roi'
});
+ extraTools.push({
+ id: 'toggleDownloadDialog',
+ title: 'Download',
+ classes: 'imageViewerCommand',
+ iconClasses: 'fa fa-camera',
+ active: () => $('#downloadDialog').is(':visible')
+ });
+
extraTools.push({
id: 'toggleCineDialog',
title: 'CINE',
diff --git a/LesionTracker/client/components/viewer/viewer.html b/LesionTracker/client/components/viewer/viewer.html
index 6c66556ef..b058d11c3 100644
--- a/LesionTracker/client/components/viewer/viewer.html
+++ b/LesionTracker/client/components/viewer/viewer.html
@@ -3,6 +3,7 @@
{{#if and Template.subscriptionsReady dataSourcesReady}}
{{>confirmDeleteDialog}}
{{>measurementTableHUD (clone this)}}
+ {{>downloadDialog}}
{{>cineDialog}}
{{/if}}
diff --git a/OHIFViewer/client/components/toolbarSection/toolbarSection.js b/OHIFViewer/client/components/toolbarSection/toolbarSection.js
index 113d9ac2b..204a18e30 100644
--- a/OHIFViewer/client/components/toolbarSection/toolbarSection.js
+++ b/OHIFViewer/client/components/toolbarSection/toolbarSection.js
@@ -104,6 +104,14 @@ Template.toolbarSection.helpers({
iconClasses: 'fa fa-square-o'
});
+ extraTools.push({
+ id: 'toggleDownloadDialog',
+ title: 'Download',
+ classes: 'imageViewerCommand',
+ iconClasses: 'fa fa-camera',
+ active: () => $('#downloadDialog').is(':visible')
+ });
+
extraTools.push({
id: 'invert',
title: 'Invert',
diff --git a/OHIFViewer/client/components/viewer/viewer.html b/OHIFViewer/client/components/viewer/viewer.html
index 38c84ba1b..014a4632b 100644
--- a/OHIFViewer/client/components/viewer/viewer.html
+++ b/OHIFViewer/client/components/viewer/viewer.html
@@ -2,6 +2,7 @@
{{#if and Template.subscriptionsReady}}
{{>cineDialog}}
+ {{>downloadDialog}}
{{>layoutChooser}}
{{>annotationDialogs}}
diff --git a/Packages/ohif-viewerbase/client/components/viewer/downloadDialog/downloadDialog.html b/Packages/ohif-viewerbase/client/components/viewer/downloadDialog/downloadDialog.html
new file mode 100644
index 000000000..404c8bda1
--- /dev/null
+++ b/Packages/ohif-viewerbase/client/components/viewer/downloadDialog/downloadDialog.html
@@ -0,0 +1,41 @@
+
+
+
\ No newline at end of file
diff --git a/Packages/ohif-viewerbase/client/components/viewer/downloadDialog/downloadDialog.js b/Packages/ohif-viewerbase/client/components/viewer/downloadDialog/downloadDialog.js
new file mode 100644
index 000000000..f0e45e021
--- /dev/null
+++ b/Packages/ohif-viewerbase/client/components/viewer/downloadDialog/downloadDialog.js
@@ -0,0 +1,110 @@
+import { Template } from 'meteor/templating';
+import { Session } from 'meteor/session';
+import { OHIF } from 'meteor/ohif:core';
+import { viewportUtils } from '../../../lib/viewportUtils';
+
+function setElementSize(element, canvas, size, value) {
+ $(element)[size](value);
+ canvas[size] = value;
+ canvas.style[size] = `${value}px`;
+}
+
+Template.downloadDialog.onCreated(() => {
+ const instance = Template.instance();
+
+ instance.autorun(() => {
+ Session.get('UpdateDownloadViewport');
+ const activeViewport = viewportUtils.getActiveViewportElement();
+
+ if (activeViewport) {
+ const enabledElement = cornerstone.getEnabledElement(activeViewport);
+
+ cornerstone.loadImage(enabledElement.image.imageId).then(function (image) {
+ cornerstone.displayImage(instance.$previewElement, image);
+ cornerstone.displayImage(instance.$downloadElement, image);
+ cornerstone.resize(instance.$previewElement, true);
+
+ setElementSize(instance.$downloadElement, instance.$downloadCanvas, 'width', 300);
+ setElementSize(instance.$downloadElement, instance.$downloadCanvas, 'height', 200);
+
+ cornerstone.fitToWindow(instance.$downloadElement);
+ });
+ }
+ })
+});
+
+Template.downloadDialog.onRendered(() => {
+ const instance = Template.instance();
+ const $dialog = instance.$('#downloadDialog');
+
+ instance.$previewElement = $('#previewElement')[0];
+ instance.$downloadElement = document.createElement('div');
+ instance.availableTools = ['length', 'probe', 'simpleAngle', 'arrowAnnotate', 'ellipticalRoi', 'rectangleRoi'];
+ instance.showAnnotations = false;
+
+ cornerstone.enable(instance.$previewElement);
+ cornerstone.enable(instance.$downloadElement);
+ instance.$downloadCanvas = $(instance.$downloadElement).find('canvas')[0];
+
+ // Make the dialog bounded and draggable
+ $dialog.draggable({ defaultElementCursor: 'move' });
+
+ // Polyfill for older browsers
+ dialogPolyfill.registerDialog($dialog.get(0));
+
+ // // Prevent dialog from being dragged when user clicks any button
+ const $controls = $dialog.find('.form-group, .instructions');
+ $controls.on('mousedown touchstart', event => event.stopPropagation());
+});
+
+Template.downloadDialog.events({
+ 'change .form-group input[name=width]'(event, instance){
+ const width = $(event.currentTarget).val();
+
+ setElementSize(instance.$downloadElement, instance.$downloadCanvas, 'width', width);
+ cornerstone.fitToWindow(instance.$downloadElement);
+ },
+
+ 'change .form-group input[name=height]'(event, instance){
+ const height = $(event.currentTarget).val();
+
+ setElementSize(instance.$downloadElement, instance.$downloadCanvas, 'height', height);
+ cornerstone.fitToWindow(instance.$downloadElement);
+ },
+
+ 'change #downloadDialog .form-group .form-check input[type=checkbox]'(event, instance) {
+ const $previewElement = instance.$previewElement;
+ const $downloadElement = instance.$downloadElement;
+
+ instance.showAnnotations = !instance.showAnnotations;
+
+ const action = (instance.showAnnotations) ? 'enable' : 'disable';
+
+ instance.availableTools.forEach(tool => {
+ cornerstoneTools[tool][action]($previewElement);
+ cornerstoneTools[tool][action]($downloadElement);
+ });
+ },
+
+ 'click .dropdown-menu .dropdown-item'(event, instance) {
+ const extension = $(event.currentTarget).text();
+ const $extensionButton = $('.btn.extension');
+
+ $extensionButton.text(extension);
+ },
+
+ 'click button.download'(event, instance) {
+ const fileName = $('.fileName').val();
+ const extension = $('.btn.extension').text().trim();
+
+ if (!fileName || !extension) {
+ return;
+ }
+
+ cornerstoneTools.saveAs(instance.$downloadElement, `${fileName}.${extension}`, `image/${extension}`);
+ },
+
+ 'click button.cancel'(event, instance) {
+ viewportUtils.toggleDownloadDialog();
+ }
+});
\ No newline at end of file
diff --git a/Packages/ohif-viewerbase/client/components/viewer/downloadDialog/downloadDialog.styl b/Packages/ohif-viewerbase/client/components/viewer/downloadDialog/downloadDialog.styl
new file mode 100644
index 000000000..7d0f1ec2a
--- /dev/null
+++ b/Packages/ohif-viewerbase/client/components/viewer/downloadDialog/downloadDialog.styl
@@ -0,0 +1,42 @@
+@import "{ohif:design}/app"
+
+#downloadDialog
+ theme('border', '2px solid $uiBorderColor', 0.95)
+ theme('background', '$uiGrayDarkest', 0.95)
+ theme('color', '$textSecondaryColor')
+ width: 626px
+ z-index: 1000
+
+ .instructions
+ margin-bottom: 20px
+ text-align: center
+
+ .extension
+ color: #000000
+ background-color: #888
+
+ .col-form-label
+ margin-top: 5px
+
+ .dropdown-menu .dropdown-item
+ display: block;
+ width: 100%;
+ padding: 3px 1.5rem;
+ clear: both;
+ font-weight: 400;
+ color: #292b2c;
+ text-align: inherit;
+ white-space: nowrap;
+ background: 0 0;
+ border: 0;
+
+ #previewElement, #downloadElement
+ width: 300px
+ height: 200px
+ margin: 0 auto
+
+ #downloadElement
+ display: none
+
+ .button-holder
+ margin-top: 15px
\ No newline at end of file
diff --git a/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js b/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js
index b4d717df4..c4a28a061 100644
--- a/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js
+++ b/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js
@@ -62,6 +62,7 @@ Meteor.startup(function() {
toggleOverlayTags: 'SHIFT',
toggleCinePlay: 'SPACE',
toggleCineDialog: '',
+ toggleDownloadDialog: '',
// Preset hotkeys
WLPreset0: '1',
@@ -218,6 +219,11 @@ Meteor.startup(function() {
name: 'Show/Hide Cine Controls',
action: viewportUtils.toggleCineDialog,
disabled: OHIF.viewerbase.viewportUtils.hasMultipleFrames
+ },
+ toggleDownloadDialog: {
+ name: 'Show/Hide Download Dialog',
+ action: viewportUtils.toggleDownloadDialog,
+ disabled: () => !viewportUtils.isDownloadEnabled()
}
}, true);
diff --git a/Packages/ohif-viewerbase/client/lib/viewportUtils.js b/Packages/ohif-viewerbase/client/lib/viewportUtils.js
index 484708cb1..701dda8c3 100644
--- a/Packages/ohif-viewerbase/client/lib/viewportUtils.js
+++ b/Packages/ohif-viewerbase/client/lib/viewportUtils.js
@@ -172,11 +172,11 @@ const linkStackScroll = () => {
// This function was originally defined alone inside client/lib/toggleDialog.js
// and has been moved here to avoid circular dependency issues.
-const toggleDialog = element => {
+const toggleDialog = (element, closeAction) => {
const $element = $(element);
if($element.is('dialog')) {
if (element.hasAttribute('open')) {
- stopAllClips();
+ if (closeAction) closeAction();
element.close();
} else {
element.show();
@@ -187,8 +187,6 @@ const toggleDialog = element => {
$element.toggleClass('dialog-closed', isClosed);
$element.toggleClass('dialog-open', !isClosed);
}
-
- Session.set('UpdateCINE', Math.random());
};
// Toggle the play/stop state for the cornerstone clip tool
@@ -210,7 +208,24 @@ const toggleCinePlay = () => {
// Show/hide the CINE dialog
const toggleCineDialog = () => {
const dialog = document.getElementById('cineDialog');
+
+ toggleDialog(dialog, stopAllClips);
+ Session.set('UpdateCINE', Random.id());
+};
+
+const toggleDownloadDialog = () => {
+ const dialog = document.getElementById('downloadDialog');
+
+ stopActiveClip();
toggleDialog(dialog);
+
+ Session.set('UpdateDownloadViewport', Random.id());
+};
+
+const isDownloadEnabled = () => {
+ const activeViewport = getActiveViewportElement();
+
+ return activeViewport ? true : false;
};
// Check if the clip is playing on the active viewport
@@ -287,6 +302,14 @@ const stopAllClips = () => {
});
};
+const stopActiveClip = () => {
+ const activeElement = getActiveViewportElement();
+
+ if ($(activeElement).find('canvas').length) {
+ cornerstoneTools.stopClip(activeElement);
+ }
+}
+
const isStackScrollLinkingDisabled = () => {
let linkableViewportsCount = 0;
@@ -329,7 +352,9 @@ const viewportUtils = {
toggleDialog,
toggleCinePlay,
toggleCineDialog,
+ toggleDownloadDialog,
isPlaying,
+ isDownloadEnabled,
hasMultipleFrames,
stopAllClips,
isStackScrollLinkingDisabled
diff --git a/Packages/ohif-viewerbase/package.js b/Packages/ohif-viewerbase/package.js
index a16929787..92fbb48cb 100644
--- a/Packages/ohif-viewerbase/package.js
+++ b/Packages/ohif-viewerbase/package.js
@@ -163,6 +163,10 @@ Package.onUse(function(api) {
api.addFiles('client/components/viewer/cineDialog/cineDialog.js', 'client');
api.addFiles('client/components/viewer/cineDialog/cineDialog.styl', 'client');
+ api.addFiles('client/components/viewer/downloadDialog/downloadDialog.html', 'client');
+ api.addFiles('client/components/viewer/downloadDialog/downloadDialog.js', 'client');
+ api.addFiles('client/components/viewer/downloadDialog/downloadDialog.styl', 'client');
+
api.addFiles('client/components/viewer/toolbarSectionButton/toolbarSectionButton.html', 'client');
api.addFiles('client/components/viewer/toolbarSectionButton/toolbarSectionButton.js', 'client');
api.addFiles('client/components/viewer/toolbarSectionButton/toolbarSectionButton.styl', 'client');