Fix double-click to enlarge viewport bugs. Add annotation dialog box to OHIF Viewer
This commit is contained in:
parent
f963fec586
commit
81b011f111
@ -3,6 +3,7 @@
|
|||||||
<div class="viewerDialogs">
|
<div class="viewerDialogs">
|
||||||
{{> cineDialog}}
|
{{> cineDialog}}
|
||||||
{{> layoutChooser }}
|
{{> layoutChooser }}
|
||||||
|
{{> annotationDialogs }}
|
||||||
|
|
||||||
<!-- Hanging Protocol dialogs -->
|
<!-- Hanging Protocol dialogs -->
|
||||||
{{ >ruleEntryDialog }}
|
{{ >ruleEntryDialog }}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
/*! cornerstoneTools - v0.7.8 - 2016-05-17 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */
|
/*! cornerstoneTools - v0.7.8 - 2016-08-05 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */
|
||||||
// Begin Source: src/header.js
|
// Begin Source: src/header.js
|
||||||
if (typeof cornerstone === 'undefined') {
|
if (typeof cornerstone === 'undefined') {
|
||||||
cornerstone = {};
|
cornerstone = {};
|
||||||
@ -22,64 +22,39 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var scrollTimeout;
|
|
||||||
var scrollTimeoutDelay = 1;
|
|
||||||
|
|
||||||
function mouseWheel(e) {
|
function mouseWheel(e) {
|
||||||
clearTimeout(scrollTimeout);
|
// !!!HACK/NOTE/WARNING!!!
|
||||||
|
// for some reason I am getting mousewheel and DOMMouseScroll events on my
|
||||||
|
// mac os x mavericks system when middle mouse button dragging.
|
||||||
|
// I couldn't find any info about this so this might break other systems
|
||||||
|
// webkit hack
|
||||||
|
if (e.originalEvent.type === 'mousewheel' && e.originalEvent.wheelDeltaY === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// firefox hack
|
||||||
|
if (e.originalEvent.type === 'DOMMouseScroll' && e.originalEvent.axis === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
scrollTimeout = setTimeout(function() {
|
var element = e.currentTarget;
|
||||||
// !!!HACK/NOTE/WARNING!!!
|
var startingCoords = cornerstone.pageToPixel(element, e.pageX || e.originalEvent.pageX, e.pageY || e.originalEvent.pageY);
|
||||||
// for some reason I am getting mousewheel and DOMMouseScroll events on my
|
|
||||||
// mac os x mavericks system when middle mouse button dragging.
|
|
||||||
// I couldn't find any info about this so this might break other systems
|
|
||||||
// webkit hack
|
|
||||||
if (e.originalEvent.type === 'mousewheel' && e.originalEvent.wheelDeltaY === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// firefox hack
|
|
||||||
if (e.originalEvent.type === 'DOMMouseScroll' && e.originalEvent.axis === 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var element = e.currentTarget;
|
e = window.event || e; // old IE support
|
||||||
|
var wheelDelta = e.wheelDelta || -e.detail || -e.originalEvent.detail;
|
||||||
|
var direction = Math.max(-1, Math.min(1, (wheelDelta)));
|
||||||
|
|
||||||
var x;
|
var mouseWheelData = {
|
||||||
var y;
|
element: element,
|
||||||
|
viewport: cornerstone.getViewport(element),
|
||||||
|
image: cornerstone.getEnabledElement(element).image,
|
||||||
|
direction: direction,
|
||||||
|
pageX: e.pageX || e.originalEvent.pageX,
|
||||||
|
pageY: e.pageY || e.originalEvent.pageY,
|
||||||
|
imageX: startingCoords.x,
|
||||||
|
imageY: startingCoords.y
|
||||||
|
};
|
||||||
|
|
||||||
if (e.pageX !== undefined && e.pageY !== undefined) {
|
$(element).trigger('CornerstoneToolsMouseWheel', mouseWheelData);
|
||||||
x = e.pageX;
|
|
||||||
y = e.pageY;
|
|
||||||
} else if (e.originalEvent &&
|
|
||||||
e.originalEvent.pageX !== undefined &&
|
|
||||||
e.originalEvent.pageY !== undefined) {
|
|
||||||
x = e.originalEvent.pageX;
|
|
||||||
y = e.originalEvent.pageY;
|
|
||||||
} else {
|
|
||||||
// IE9 & IE10
|
|
||||||
x = e.x;
|
|
||||||
y = e.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
var startingCoords = cornerstone.pageToPixel(element, x, y);
|
|
||||||
|
|
||||||
e = window.event || e; // old IE support
|
|
||||||
var wheelDelta = e.wheelDelta || -e.detail || -e.originalEvent.detail || -e.originalEvent.deltaY;
|
|
||||||
var direction = Math.max(-1, Math.min(1, (wheelDelta)));
|
|
||||||
|
|
||||||
var mouseWheelData = {
|
|
||||||
element: element,
|
|
||||||
viewport: cornerstone.getViewport(element),
|
|
||||||
image: cornerstone.getEnabledElement(element).image,
|
|
||||||
direction: direction,
|
|
||||||
pageX: x,
|
|
||||||
pageY: y,
|
|
||||||
imageX: startingCoords.x,
|
|
||||||
imageY: startingCoords.y
|
|
||||||
};
|
|
||||||
|
|
||||||
$(element).trigger('CornerstoneToolsMouseWheel', mouseWheelData);
|
|
||||||
}, scrollTimeoutDelay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var mouseWheelEvents = 'mousewheel DOMMouseScroll';
|
var mouseWheelEvents = 'mousewheel DOMMouseScroll';
|
||||||
@ -2138,7 +2113,6 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
$(mouseEventData.element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools.arrowAnnotate.mouseMoveCallback);
|
$(mouseEventData.element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools.arrowAnnotate.mouseMoveCallback);
|
||||||
$(mouseEventData.element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools.arrowAnnotate.mouseDownCallback);
|
$(mouseEventData.element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools.arrowAnnotate.mouseDownCallback);
|
||||||
$(mouseEventData.element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.arrowAnnotate.mouseDownActivateCallback);
|
$(mouseEventData.element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.arrowAnnotate.mouseDownActivateCallback);
|
||||||
$(mouseEventData.element).on('CornerstoneToolsMouseDoubleClick', eventData, cornerstoneTools.arrowAnnotate.mouseDoubleClickCallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// associate this data with this imageId so we can render it and manipulate it
|
// associate this data with this imageId so we can render it and manipulate it
|
||||||
@ -2149,7 +2123,6 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
$(mouseEventData.element).off('CornerstoneToolsMouseMove', cornerstoneTools.arrowAnnotate.mouseMoveCallback);
|
$(mouseEventData.element).off('CornerstoneToolsMouseMove', cornerstoneTools.arrowAnnotate.mouseMoveCallback);
|
||||||
$(mouseEventData.element).off('CornerstoneToolsMouseDown', cornerstoneTools.arrowAnnotate.mouseDownCallback);
|
$(mouseEventData.element).off('CornerstoneToolsMouseDown', cornerstoneTools.arrowAnnotate.mouseDownCallback);
|
||||||
$(mouseEventData.element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.arrowAnnotate.mouseDownActivateCallback);
|
$(mouseEventData.element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.arrowAnnotate.mouseDownActivateCallback);
|
||||||
$(mouseEventData.element).off('CornerstoneToolsMouseDoubleClick', cornerstoneTools.arrowAnnotate.mouseDoubleClickCallback);
|
|
||||||
|
|
||||||
cornerstone.updateImage(mouseEventData.element);
|
cornerstone.updateImage(mouseEventData.element);
|
||||||
cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() {
|
cornerstoneTools.moveNewHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() {
|
||||||
@ -2429,7 +2402,7 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
|
|
||||||
// now check to see if there is a handle we can move
|
// now check to see if there is a handle we can move
|
||||||
if (!toolData) {
|
if (!toolData) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < toolData.data.length; i++) {
|
for (var i = 0; i < toolData.data.length; i++) {
|
||||||
@ -2445,8 +2418,6 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pressCallback(e, eventData) {
|
function pressCallback(e, eventData) {
|
||||||
@ -3415,7 +3386,7 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// First we calculate the ellipse points (top, left, right, and bottom)
|
// First we calculate the ellipse points (top, left, right, and bottom)
|
||||||
var ellipsePoints = [ {
|
var ellipsePoints = [{
|
||||||
// Top middle point of ellipse
|
// Top middle point of ellipse
|
||||||
x: leftCanvas + widthCanvas / 2,
|
x: leftCanvas + widthCanvas / 2,
|
||||||
y: topCanvas
|
y: topCanvas
|
||||||
@ -3431,14 +3402,14 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
// Right middle point of ellipse
|
// Right middle point of ellipse
|
||||||
x: leftCanvas + widthCanvas,
|
x: leftCanvas + widthCanvas,
|
||||||
y: topCanvas + heightCanvas / 2
|
y: topCanvas + heightCanvas / 2
|
||||||
} ];
|
}];
|
||||||
|
|
||||||
// We obtain the link starting point by finding the closest point on the ellipse to the
|
// We obtain the link starting point by finding the closest point on the ellipse to the
|
||||||
// center of the textbox
|
// center of the textbox
|
||||||
link.start = cornerstoneMath.point.findClosestPoint(ellipsePoints, link.end);
|
link.start = cornerstoneMath.point.findClosestPoint(ellipsePoints, link.end);
|
||||||
|
|
||||||
// Next we calculate the corners of the textbox bounding box
|
// Next we calculate the corners of the textbox bounding box
|
||||||
var boundingBoxPoints = [ {
|
var boundingBoxPoints = [{
|
||||||
// Top middle point of bounding box
|
// Top middle point of bounding box
|
||||||
x: boundingBox.left + boundingBox.width / 2,
|
x: boundingBox.left + boundingBox.width / 2,
|
||||||
y: boundingBox.top
|
y: boundingBox.top
|
||||||
@ -3464,7 +3435,7 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.strokeStyle = color;
|
context.strokeStyle = color;
|
||||||
context.lineWidth = lineWidth;
|
context.lineWidth = lineWidth;
|
||||||
context.setLineDash([ 2, 3 ]);
|
context.setLineDash([2, 3]);
|
||||||
context.moveTo(link.start.x, link.start.y);
|
context.moveTo(link.start.x, link.start.y);
|
||||||
context.lineTo(link.end.x, link.end.y);
|
context.lineTo(link.end.x, link.end.y);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
@ -3489,8 +3460,7 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
toolType: toolType
|
toolType: toolType
|
||||||
});
|
});
|
||||||
|
|
||||||
})($, cornerstone, cornerstoneMath, cornerstoneTools);
|
})($, cornerstone, cornerstoneMath, cornerstoneTools);
|
||||||
|
|
||||||
// End Source; src/imageTools/ellipticalRoi.js
|
// End Source; src/imageTools/ellipticalRoi.js
|
||||||
|
|
||||||
// Begin Source: src/imageTools/freehand.js
|
// Begin Source: src/imageTools/freehand.js
|
||||||
@ -5636,8 +5606,7 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
x: mouseEventData.currentPoints.image.x,
|
x: mouseEventData.currentPoints.image.x,
|
||||||
y: mouseEventData.currentPoints.image.y,
|
y: mouseEventData.currentPoints.image.y,
|
||||||
highlight: true,
|
highlight: true,
|
||||||
active: true,
|
active: true
|
||||||
hasBoundingBox: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -5686,13 +5655,12 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
|
|
||||||
///////// BEGIN IMAGE RENDERING ///////
|
///////// BEGIN IMAGE RENDERING ///////
|
||||||
function pointNearTool(element, data, coords) {
|
function pointNearTool(element, data, coords) {
|
||||||
if (!data.handles.end.boundingBox) {
|
if (!data.textBoundingBox) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var distanceToPoint = cornerstoneMath.rect.distanceToPoint(data.handles.end.boundingBox, coords);
|
var distanceToPoint = cornerstoneMath.rect.distanceToPoint(data.textBoundingBox, coords);
|
||||||
var insideBoundingBox = cornerstoneTools.pointInsideBoundingBox(data.handles.end, coords);
|
return (distanceToPoint < 10);
|
||||||
return (distanceToPoint < 10) || insideBoundingBox;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onImageRendered(e, eventData) {
|
function onImageRendered(e, eventData) {
|
||||||
@ -5739,7 +5707,7 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var boundingBox = cornerstoneTools.drawTextBox(context, data.text, textCoords.x, textCoords.y - 10, color, options);
|
var boundingBox = cornerstoneTools.drawTextBox(context, data.text, textCoords.x, textCoords.y - 10, color, options);
|
||||||
data.handles.end.boundingBox = boundingBox;
|
data.textBoundingBox = boundingBox;
|
||||||
|
|
||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
@ -6379,94 +6347,118 @@ if (typeof cornerstoneTools === 'undefined') {
|
|||||||
// Now that the scale has been updated, determine the offset we need to apply to the center so we can
|
// Now that the scale has been updated, determine the offset we need to apply to the center so we can
|
||||||
// keep the original start location in the same position
|
// keep the original start location in the same position
|
||||||
var newCoords = cornerstone.pageToPixel(element, eventData.startPoints.page.x, eventData.startPoints.page.y);
|
var newCoords = cornerstone.pageToPixel(element, eventData.startPoints.page.x, eventData.startPoints.page.y);
|
||||||
|
|
||||||
|
// The shift we will use is the difference between the original image coordinates of the point we've selected
|
||||||
|
// and the image coordinates of the same point on the page after the viewport scaling above has been performed
|
||||||
|
// This shift is in image coordinates, and is designed to keep the target location fixed on the page.
|
||||||
var shift = {
|
var shift = {
|
||||||
x: eventData.startPoints.image.x - newCoords.x,
|
x: eventData.startPoints.image.x - newCoords.x,
|
||||||
y: eventData.startPoints.image.y - newCoords.y
|
y: eventData.startPoints.image.y - newCoords.y
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Correct the required shift using the viewport rotation and flip parameters
|
||||||
shift = correctShift(shift, viewport);
|
shift = correctShift(shift, viewport);
|
||||||
|
|
||||||
|
// Apply the shift to the Viewport's translation setting
|
||||||
viewport.translation.x -= shift.x;
|
viewport.translation.x -= shift.x;
|
||||||
viewport.translation.y -= shift.y;
|
viewport.translation.y -= shift.y;
|
||||||
|
|
||||||
|
// Update the Viewport with the new translation value
|
||||||
cornerstone.setViewport(element, viewport);
|
cornerstone.setViewport(element, viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateStrategy(eventData, ticks) {
|
function translateStrategy(eventData, ticks) {
|
||||||
var element = eventData.element;
|
var element = eventData.element;
|
||||||
var image = eventData.image;
|
var image = eventData.image;
|
||||||
|
var config = cornerstoneTools.zoom.getConfiguration();
|
||||||
|
|
||||||
// Calculate the new scale factor based on how far the mouse has changed
|
// Calculate the new scale factor based on how far the mouse has changed
|
||||||
|
// Note that in this case we don't need to update the viewport after the initial
|
||||||
|
// zoom step since we aren't don't intend to keep the target position static on
|
||||||
|
// the page
|
||||||
var viewport = changeViewportScale(eventData.viewport, ticks);
|
var viewport = changeViewportScale(eventData.viewport, ticks);
|
||||||
cornerstone.setViewport(element, viewport);
|
|
||||||
|
|
||||||
var config = cornerstoneTools.zoom.getConfiguration();
|
// Define the default shift to take place during this zoom step
|
||||||
var shift,
|
var shift = {
|
||||||
newCoords;
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
|
||||||
var outwardsTranslateSpeed = 8;
|
// Define the parameters for the translate strategy
|
||||||
var inwardsTranslateSpeed = 8;
|
var translateSpeed = 8;
|
||||||
var outwardsMinScaleToTranslate = 3;
|
var outwardsMinScaleToTranslate = 3;
|
||||||
var minTranslation = 0.01;
|
var minTranslation = 0.01;
|
||||||
|
|
||||||
if (ticks < 0) {
|
if (ticks < 0) {
|
||||||
// Zoom outwards from the image center
|
// Zoom outwards from the image center
|
||||||
shift = {
|
if (viewport.scale < outwardsMinScaleToTranslate) {
|
||||||
x: viewport.scale < outwardsMinScaleToTranslate ? viewport.translation.x / outwardsTranslateSpeed : 0,
|
// If the current translation is smaller than the minimum desired translation,
|
||||||
y: viewport.scale < outwardsMinScaleToTranslate ? viewport.translation.y / outwardsTranslateSpeed : 0
|
// set the translation to zero
|
||||||
};
|
if (Math.abs(viewport.translation.x) < minTranslation) {
|
||||||
|
viewport.translation.x = 0;
|
||||||
if (Math.abs(viewport.translation.x) < minTranslation) {
|
} else {
|
||||||
viewport.translation.x = 0;
|
shift.x = viewport.translation.x / translateSpeed;
|
||||||
shift.x = 0;
|
}
|
||||||
} else if (Math.abs(viewport.translation.y) < minTranslation) {
|
|
||||||
viewport.translation.y = 0;
|
// If the current translation is smaller than the minimum desired translation,
|
||||||
shift.y = 0;
|
// set the translation to zero
|
||||||
} else if (Math.abs(viewport.translation.x) < minTranslation &&
|
if (Math.abs(viewport.translation.y) < minTranslation) {
|
||||||
Math.abs(viewport.translation.y) < minTranslation) {
|
viewport.translation.y = 0;
|
||||||
cornerstone.setViewport(element, viewport);
|
} else {
|
||||||
return false;
|
shift.y = viewport.translation.y / translateSpeed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newCoords = cornerstone.pageToPixel(element, startPoints.page.x, startPoints.page.y);
|
// Zoom inwards to the current image point
|
||||||
|
|
||||||
|
// Identify the coordinates of the point the user is trying to zoom into
|
||||||
|
// If we are not allowed to zoom outside the image, bound the user-selected position to
|
||||||
|
// a point inside the image
|
||||||
if (config && config.preventZoomOutsideImage) {
|
if (config && config.preventZoomOutsideImage) {
|
||||||
startPoints.image = boundPosition(startPoints.image, image.width, image.height);
|
startPoints.image = boundPosition(startPoints.image, image.width, image.height);
|
||||||
newCoords = boundPosition(newCoords, image.width, image.height);
|
|
||||||
}
|
}
|
||||||
// Zoom inwards to the current image point
|
|
||||||
|
// Calculate the translation value that would place the desired image point in the center
|
||||||
|
// of the viewport
|
||||||
var desiredTranslation = {
|
var desiredTranslation = {
|
||||||
x: image.width / 2 - startPoints.image.x,
|
x: image.width / 2 - startPoints.image.x,
|
||||||
y: image.height / 2 - startPoints.image.y
|
y: image.height / 2 - startPoints.image.y
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Correct the target location using the viewport rotation and flip parameters
|
||||||
|
desiredTranslation = correctShift(desiredTranslation, viewport);
|
||||||
|
|
||||||
|
// Calculate the difference between the current viewport translation value and the
|
||||||
|
// final desired translation values
|
||||||
var distanceToDesired = {
|
var distanceToDesired = {
|
||||||
x: viewport.translation.x - desiredTranslation.x,
|
x: viewport.translation.x - desiredTranslation.x,
|
||||||
y: viewport.translation.y - desiredTranslation.y
|
y: viewport.translation.y - desiredTranslation.y
|
||||||
};
|
};
|
||||||
|
|
||||||
shift = {
|
// If the current translation is smaller than the minimum desired translation,
|
||||||
x: distanceToDesired.x / inwardsTranslateSpeed,
|
// stop translating in the x-direction
|
||||||
y: distanceToDesired.y / inwardsTranslateSpeed
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Math.abs(distanceToDesired.x) < minTranslation) {
|
if (Math.abs(distanceToDesired.x) < minTranslation) {
|
||||||
viewport.translation.x = desiredTranslation.x;
|
viewport.translation.x = desiredTranslation.x;
|
||||||
shift.x = 0;
|
} else {
|
||||||
} else if (Math.abs(distanceToDesired.y) < minTranslation) {
|
// Otherwise, shift the viewport by one step
|
||||||
|
shift.x = distanceToDesired.x / translateSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current translation is smaller than the minimum desired translation,
|
||||||
|
// stop translating in the y-direction
|
||||||
|
if (Math.abs(distanceToDesired.y) < minTranslation) {
|
||||||
viewport.translation.y = desiredTranslation.y;
|
viewport.translation.y = desiredTranslation.y;
|
||||||
shift.y = 0;
|
} else {
|
||||||
} else if (Math.abs(distanceToDesired.x) < minTranslation &&
|
// Otherwise, shift the viewport by one step
|
||||||
Math.abs(distanceToDesired.y) < minTranslation) {
|
shift.y = distanceToDesired.y / translateSpeed;
|
||||||
cornerstone.setViewport(element, viewport);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shift = correctShift(shift, viewport);
|
// Apply the shift to the Viewport's translation setting
|
||||||
if (!shift.x && !shift.y) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
viewport.translation.x -= shift.x;
|
viewport.translation.x -= shift.x;
|
||||||
viewport.translation.y -= shift.y;
|
viewport.translation.y -= shift.y;
|
||||||
|
|
||||||
|
// Update the Viewport with the new translation value
|
||||||
cornerstone.setViewport(element, viewport);
|
cornerstone.setViewport(element, viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1082,7 +1082,7 @@
|
|||||||
|
|
||||||
// now check to see if there is a handle we can move
|
// now check to see if there is a handle we can move
|
||||||
if (!toolData) {
|
if (!toolData) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < toolData.data.length; i++) {
|
for (var i = 0; i < toolData.data.length; i++) {
|
||||||
@ -1097,8 +1097,6 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// module exports
|
// module exports
|
||||||
|
|||||||
@ -374,7 +374,7 @@
|
|||||||
|
|
||||||
// now check to see if there is a handle we can move
|
// now check to see if there is a handle we can move
|
||||||
if (!toolData) {
|
if (!toolData) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < toolData.data.length; i++) {
|
for (var i = 0; i < toolData.data.length; i++) {
|
||||||
@ -389,8 +389,6 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -321,7 +321,7 @@
|
|||||||
|
|
||||||
// now check to see if there is a handle we can move
|
// now check to see if there is a handle we can move
|
||||||
if (!toolData) {
|
if (!toolData) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < toolData.data.length; i++) {
|
for (var i = 0; i < toolData.data.length; i++) {
|
||||||
@ -336,8 +336,6 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cornerstoneTools.nonTarget = cornerstoneTools.mouseButtonTool({
|
cornerstoneTools.nonTarget = cornerstoneTools.mouseButtonTool({
|
||||||
|
|||||||
411
Packages/viewerbase/client/compatibility/dialogPolyfill.js
Normal file
411
Packages/viewerbase/client/compatibility/dialogPolyfill.js
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
var dialogPolyfill = (function() {
|
||||||
|
|
||||||
|
var supportCustomEvent = window.CustomEvent;
|
||||||
|
if (!supportCustomEvent || typeof supportCustomEvent == "object") {
|
||||||
|
supportCustomEvent = function CustomEvent(event, x) {
|
||||||
|
x = x || {};
|
||||||
|
var ev = document.createEvent('CustomEvent');
|
||||||
|
ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);
|
||||||
|
return ev;
|
||||||
|
};
|
||||||
|
supportCustomEvent.prototype = window.Event.prototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the nearest <dialog> from the passed element.
|
||||||
|
*
|
||||||
|
* @param {Element} el to search from
|
||||||
|
* @param {HTMLDialogElement} dialog found
|
||||||
|
*/
|
||||||
|
function findNearestDialog(el) {
|
||||||
|
while (el) {
|
||||||
|
if (el.nodeName == 'DIALOG') {
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
el = el.parentElement;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dialogPolyfill = {};
|
||||||
|
|
||||||
|
dialogPolyfill.reposition = function(element) {
|
||||||
|
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
|
||||||
|
var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;
|
||||||
|
element.style.top = Math.max(0, topValue) + 'px';
|
||||||
|
element.dialogPolyfillInfo.isTopOverridden = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.inNodeList = function(nodeList, node) {
|
||||||
|
for (var i = 0; i < nodeList.length; ++i) {
|
||||||
|
if (nodeList[i] == node)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.isInlinePositionSetByStylesheet = function(element) {
|
||||||
|
for (var i = 0; i < document.styleSheets.length; ++i) {
|
||||||
|
var styleSheet = document.styleSheets[i];
|
||||||
|
var cssRules = null;
|
||||||
|
// Some browsers throw on cssRules.
|
||||||
|
try {
|
||||||
|
cssRules = styleSheet.cssRules;
|
||||||
|
} catch (e) {}
|
||||||
|
if (!cssRules)
|
||||||
|
continue;
|
||||||
|
for (var j = 0; j < cssRules.length; ++j) {
|
||||||
|
var rule = cssRules[j];
|
||||||
|
var selectedNodes = null;
|
||||||
|
// Ignore errors on invalid selector texts.
|
||||||
|
try {
|
||||||
|
selectedNodes = document.querySelectorAll(rule.selectorText);
|
||||||
|
} catch(e) {}
|
||||||
|
if (!selectedNodes || !dialogPolyfill.inNodeList(selectedNodes, element))
|
||||||
|
continue;
|
||||||
|
var cssTop = rule.style.getPropertyValue('top');
|
||||||
|
var cssBottom = rule.style.getPropertyValue('bottom');
|
||||||
|
if ((cssTop && cssTop != 'auto') || (cssBottom && cssBottom != 'auto'))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.needsCentering = function(dialog) {
|
||||||
|
var computedStyle = window.getComputedStyle(dialog);
|
||||||
|
if (computedStyle.position != 'absolute') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must determine whether the top/bottom specified value is non-auto. In
|
||||||
|
// WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but
|
||||||
|
// Firefox returns the used value. So we do this crazy thing instead: check
|
||||||
|
// the inline style and then go through CSS rules.
|
||||||
|
if ((dialog.style.top != 'auto' && dialog.style.top != '') ||
|
||||||
|
(dialog.style.bottom != 'auto' && dialog.style.bottom != ''))
|
||||||
|
return false;
|
||||||
|
return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.showDialog = function(isModal) {
|
||||||
|
if (this.open) {
|
||||||
|
throw 'InvalidStateError: showDialog called on open dialog';
|
||||||
|
}
|
||||||
|
this.open = true; // TODO: should be a getter mapped to attribute
|
||||||
|
this.setAttribute('open', 'open');
|
||||||
|
|
||||||
|
if (isModal) {
|
||||||
|
// Find element with `autofocus` attribute or first form control
|
||||||
|
var first_form_ctrl = null;
|
||||||
|
var autofocus = null;
|
||||||
|
var findElementToFocus = function(root) {
|
||||||
|
if (!root.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < root.children.length; i++) {
|
||||||
|
var elem = root.children[i];
|
||||||
|
if (first_form_ctrl === null && !elem.disabled && (
|
||||||
|
elem.nodeName == 'BUTTON' ||
|
||||||
|
elem.nodeName == 'INPUT' ||
|
||||||
|
elem.nodeName == 'KEYGEN' ||
|
||||||
|
elem.nodeName == 'SELECT' ||
|
||||||
|
elem.nodeName == 'TEXTAREA')) {
|
||||||
|
first_form_ctrl = elem;
|
||||||
|
}
|
||||||
|
if (elem.autofocus) {
|
||||||
|
autofocus = elem;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
findElementToFocus(elem);
|
||||||
|
if (autofocus !== null) return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
findElementToFocus(this);
|
||||||
|
|
||||||
|
if (autofocus !== null) {
|
||||||
|
autofocus.focus();
|
||||||
|
} else if (first_form_ctrl !== null) {
|
||||||
|
first_form_ctrl.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialogPolyfill.needsCentering(this))
|
||||||
|
dialogPolyfill.reposition(this);
|
||||||
|
if (isModal) {
|
||||||
|
this.dialogPolyfillInfo.modal = true;
|
||||||
|
dialogPolyfill.dm.pushDialog(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IE sometimes complains when calling .focus() that it
|
||||||
|
// "Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus."
|
||||||
|
try {
|
||||||
|
if (autofocus !== null) {
|
||||||
|
autofocus.focus();
|
||||||
|
} else if (first_form_ctrl !== null) {
|
||||||
|
first_form_ctrl.focus();
|
||||||
|
}
|
||||||
|
} catch(e) {}
|
||||||
|
this.style.zoom = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.close = function(retval) {
|
||||||
|
if (!this.open && !window.HTMLDialogElement) {
|
||||||
|
// Native implementations will set .open to false, so ignore this error.
|
||||||
|
throw 'InvalidStateError: close called on closed dialog';
|
||||||
|
}
|
||||||
|
this.open = false;
|
||||||
|
this.removeAttribute('open');
|
||||||
|
|
||||||
|
// Leave returnValue untouched in case it was set directly on the element
|
||||||
|
if (typeof retval != 'undefined') {
|
||||||
|
this.returnValue = retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This won't match the native <dialog> exactly because if the user sets top
|
||||||
|
// on a centered polyfill dialog, that top gets thrown away when the dialog is
|
||||||
|
// closed. Not sure it's possible to polyfill this perfectly.
|
||||||
|
if (this.dialogPolyfillInfo.isTopOverridden) {
|
||||||
|
this.style.top = 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.dialogPolyfillInfo.modal) {
|
||||||
|
dialogPolyfill.dm.removeDialog(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triggering "close" event for any attached listeners on the <dialog>
|
||||||
|
var event;
|
||||||
|
if (document.createEvent) {
|
||||||
|
event = document.createEvent('HTMLEvents');
|
||||||
|
event.initEvent('close', true, true);
|
||||||
|
} else {
|
||||||
|
event = new Event('close');
|
||||||
|
}
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
|
||||||
|
return this.returnValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.registerDialog = function(element) {
|
||||||
|
if (element.show) {
|
||||||
|
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);
|
||||||
|
element.close = dialogPolyfill.close.bind(element);
|
||||||
|
element.dialogPolyfillInfo = {};
|
||||||
|
element.open = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The overlay is used to simulate how a modal dialog blocks the document. The
|
||||||
|
// blocking dialog is positioned on top of the overlay, and the rest of the
|
||||||
|
// dialogs on the pending dialog stack are positioned below it. In the actual
|
||||||
|
// implementation, the modal dialog stacking is controlled by the top layer,
|
||||||
|
// where z-index has no effect.
|
||||||
|
var TOP_LAYER_ZINDEX = 100000;
|
||||||
|
var MAX_PENDING_DIALOGS = 100000;
|
||||||
|
|
||||||
|
dialogPolyfill.DialogManager = function() {
|
||||||
|
this.pendingDialogStack = [];
|
||||||
|
this.overlay = document.createElement('div');
|
||||||
|
this.overlay.style.width = '100%';
|
||||||
|
this.overlay.style.height = '100%';
|
||||||
|
this.overlay.style.position = 'fixed';
|
||||||
|
this.overlay.style.left = '0px';
|
||||||
|
this.overlay.style.top = '0px';
|
||||||
|
this.overlay.style.backgroundColor = 'rgba(0,0,0,0.0)';
|
||||||
|
|
||||||
|
this.focusPageLast = this.createFocusable();
|
||||||
|
this.overlay.appendChild(this.focusPageLast);
|
||||||
|
|
||||||
|
this.overlay.addEventListener('click', function(e) {
|
||||||
|
var redirectedEvent = document.createEvent('MouseEvents');
|
||||||
|
redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window,
|
||||||
|
e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey,
|
||||||
|
e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
|
||||||
|
document.body.dispatchEvent(redirectedEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Only install when any dialogs are open.
|
||||||
|
document.addEventListener('submit', function(ev) {
|
||||||
|
var method = ev.target.getAttribute('method').toLowerCase();
|
||||||
|
if (method != 'dialog') { return; }
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
var dialog = findNearestDialog(ev.target);
|
||||||
|
if (!dialog) { return; }
|
||||||
|
|
||||||
|
// FIXME: The original event doesn't contain the INPUT element used to
|
||||||
|
// submit the form (if any). Look in some possible places.
|
||||||
|
var returnValue;
|
||||||
|
var cands = [document.activeElement, ev.explicitOriginalTarget];
|
||||||
|
cands.some(function(cand) {
|
||||||
|
if (cand && cand.nodeName == 'INPUT' && cand.form == ev.target) {
|
||||||
|
returnValue = cand.value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.close(returnValue);
|
||||||
|
}, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.DialogManager.prototype.createFocusable = function(tabIndex) {
|
||||||
|
var span = document.createElement('span');
|
||||||
|
span.tabIndex = tabIndex || 0;
|
||||||
|
span.style.opacity = 0;
|
||||||
|
span.style.position = 'static';
|
||||||
|
return span;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.DialogManager.prototype.blockDocument = function() {
|
||||||
|
if (!document.body.contains(this.overlay)) {
|
||||||
|
document.body.appendChild(this.overlay);
|
||||||
|
|
||||||
|
// On Safari/Mac (and possibly other browsers), the documentElement is
|
||||||
|
// not focusable. This is required for modal dialogs as it is the first
|
||||||
|
// element to be hit by a tab event, and further tabs are redirected to
|
||||||
|
// the most visible dialog.
|
||||||
|
if (this.needsDocumentElementFocus === undefined) {
|
||||||
|
document.documentElement.focus();
|
||||||
|
this.needsDocumentElementFocus =
|
||||||
|
(document.activeElement != document.documentElement);
|
||||||
|
}
|
||||||
|
if (this.needsDocumentElementFocus) {
|
||||||
|
document.documentElement.tabIndex = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.DialogManager.prototype.unblockDocument = function() {
|
||||||
|
document.body.removeChild(this.overlay);
|
||||||
|
if (this.needsDocumentElementFocus) {
|
||||||
|
// TODO: Restore the previous tabIndex, rather than clearing it.
|
||||||
|
document.documentElement.tabIndex = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.DialogManager.prototype.updateStacking = function() {
|
||||||
|
if (this.pendingDialogStack.length == 0) {
|
||||||
|
this.unblockDocument();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.blockDocument();
|
||||||
|
|
||||||
|
var zIndex = TOP_LAYER_ZINDEX;
|
||||||
|
for (var i = 0; i < this.pendingDialogStack.length; i++) {
|
||||||
|
if (i == this.pendingDialogStack.length - 1)
|
||||||
|
this.overlay.style.zIndex = zIndex++;
|
||||||
|
var dialog = this.pendingDialogStack[i];
|
||||||
|
dialog.dialogPolyfillInfo.backdrop.style.zIndex = zIndex++;
|
||||||
|
dialog.style.zIndex = zIndex++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.DialogManager.prototype.handleKey = function(event) {
|
||||||
|
var dialogCount = this.pendingDialogStack.length;
|
||||||
|
if (dialogCount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var dialog = this.pendingDialogStack[dialogCount - 1];
|
||||||
|
var pfi = dialog.dialogPolyfillInfo;
|
||||||
|
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case 9: /* tab */
|
||||||
|
var activeElement = document.activeElement;
|
||||||
|
var forward = !event.shiftKey;
|
||||||
|
if (forward) {
|
||||||
|
// Tab forward, so look for document or fake last focus element.
|
||||||
|
if (activeElement == document.documentElement ||
|
||||||
|
activeElement == document.body ||
|
||||||
|
activeElement == pfi.backdrop) {
|
||||||
|
pfi.focusFirst.focus();
|
||||||
|
} else if (activeElement == pfi.focusLast) {
|
||||||
|
// TODO: Instead of wrapping to focusFirst, escape to browser chrome.
|
||||||
|
pfi.focusFirst.focus();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Tab backwards, so look for fake first focus element.
|
||||||
|
if (activeElement == pfi.focusFirst) {
|
||||||
|
// TODO: Instead of wrapping to focusLast, escape to browser chrome.
|
||||||
|
pfi.focusLast.focus();
|
||||||
|
} else if (activeElement == this.focusPageLast) {
|
||||||
|
// The focus element is at the end of the page (e.g., shift-tab from
|
||||||
|
// the window chrome): move current focus to the last element in the
|
||||||
|
// dialog instead.
|
||||||
|
pfi.focusLast.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 27: /* esc */
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
var cancelEvent = new supportCustomEvent('cancel', {
|
||||||
|
bubbles: false,
|
||||||
|
cancelable: true
|
||||||
|
});
|
||||||
|
if (dialog.dispatchEvent(cancelEvent)) {
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.DialogManager.prototype.pushDialog = function(dialog) {
|
||||||
|
if (this.pendingDialogStack.length >= MAX_PENDING_DIALOGS) {
|
||||||
|
throw "Too many modal dialogs";
|
||||||
|
}
|
||||||
|
|
||||||
|
var backdrop = document.createElement('div');
|
||||||
|
backdrop.className = 'backdrop';
|
||||||
|
var clickEventListener = function(e) {
|
||||||
|
var redirectedEvent = document.createEvent('MouseEvents');
|
||||||
|
redirectedEvent.initMouseEvent(e.type, e.bubbles, e.cancelable, window,
|
||||||
|
e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey,
|
||||||
|
e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
|
||||||
|
dialog.dispatchEvent(redirectedEvent);
|
||||||
|
};
|
||||||
|
backdrop.addEventListener('click', clickEventListener);
|
||||||
|
dialog.parentNode.insertBefore(backdrop, dialog.nextSibling);
|
||||||
|
dialog.dialogPolyfillInfo.backdrop = backdrop;
|
||||||
|
dialog.dialogPolyfillInfo.clickEventListener = clickEventListener;
|
||||||
|
this.pendingDialogStack.push(dialog);
|
||||||
|
this.updateStacking();
|
||||||
|
|
||||||
|
dialog.dialogPolyfillInfo.focusFirst = this.createFocusable();
|
||||||
|
dialog.dialogPolyfillInfo.focusLast = this.createFocusable();
|
||||||
|
dialog.appendChild(dialog.dialogPolyfillInfo.focusLast);
|
||||||
|
dialog.insertBefore(
|
||||||
|
dialog.dialogPolyfillInfo.focusFirst, dialog.firstChild);
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.DialogManager.prototype.removeDialog = function(dialog) {
|
||||||
|
var index = this.pendingDialogStack.indexOf(dialog);
|
||||||
|
if (index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.pendingDialogStack.splice(index, 1);
|
||||||
|
var backdrop = dialog.dialogPolyfillInfo.backdrop;
|
||||||
|
var clickEventListener = dialog.dialogPolyfillInfo.clickEventListener;
|
||||||
|
backdrop.removeEventListener('click', clickEventListener);
|
||||||
|
backdrop.parentNode.removeChild(backdrop);
|
||||||
|
dialog.dialogPolyfillInfo.backdrop = null;
|
||||||
|
dialog.dialogPolyfillInfo.clickEventListener = null;
|
||||||
|
this.updateStacking();
|
||||||
|
|
||||||
|
dialog.removeChild(dialog.dialogPolyfillInfo.focusFirst);
|
||||||
|
dialog.removeChild(dialog.dialogPolyfillInfo.focusLast);
|
||||||
|
dialog.dialogPolyfillInfo.focusFirst = null;
|
||||||
|
dialog.dialogPolyfillInfo.focusLast = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogPolyfill.dm = new dialogPolyfill.DialogManager();
|
||||||
|
|
||||||
|
document.addEventListener('keydown',
|
||||||
|
dialogPolyfill.dm.handleKey.bind(dialogPolyfill.dm));
|
||||||
|
|
||||||
|
return dialogPolyfill;
|
||||||
|
})();
|
||||||
34
Packages/viewerbase/client/compatibility/dialogPolyfill.styl
Normal file
34
Packages/viewerbase/client/compatibility/dialogPolyfill.styl
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
dialog
|
||||||
|
position: absolute
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
width: -moz-fit-content
|
||||||
|
width: -webkit-fit-content
|
||||||
|
width: fit-content
|
||||||
|
height: -moz-fit-content
|
||||||
|
height: -webkit-fit-content
|
||||||
|
height: fit-content
|
||||||
|
margin: auto
|
||||||
|
border: solid
|
||||||
|
padding: 1em
|
||||||
|
background: white
|
||||||
|
color: black
|
||||||
|
display: none
|
||||||
|
|
||||||
|
dialog[open]
|
||||||
|
display: block
|
||||||
|
|
||||||
|
dialog + .backdrop
|
||||||
|
position: fixed
|
||||||
|
top: 0
|
||||||
|
right: 0
|
||||||
|
bottom: 0
|
||||||
|
left: 0
|
||||||
|
background: rgba(0,0,0,0.1)
|
||||||
|
|
||||||
|
/* for small devices, modal dialogs go full-screen */
|
||||||
|
@media screen and (max-width: 540px)
|
||||||
|
dialog[_polyfill_modal]
|
||||||
|
top: 0
|
||||||
|
width: auto
|
||||||
|
margin: 1em
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,33 @@
|
|||||||
|
<template name="annotationDialogs">
|
||||||
|
<dialog id="annotationDialog"
|
||||||
|
class="annotationDialog noselect">
|
||||||
|
<h5>Enter your annotation</h5>
|
||||||
|
<div class="annotationTextInputOptions">
|
||||||
|
<label for="annotationTextInput">New label</label>
|
||||||
|
<input name="annotationTextInput"
|
||||||
|
class="annotationTextInput"
|
||||||
|
type="text"
|
||||||
|
tabindex="-1"
|
||||||
|
autocomplete="off"/>
|
||||||
|
</div>
|
||||||
|
<a class="annotationDialogConfirm btn btn-sm btn-primary">OK</a>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="relabelAnnotationDialog"
|
||||||
|
class="annotationDialog noselect"
|
||||||
|
oncontextmenu="return false">
|
||||||
|
<h5>Edit your annotation</h5>
|
||||||
|
<div class="annotationTextInputOptions">
|
||||||
|
<label for="annotationTextInput">New label</label>
|
||||||
|
<input name="annotationTextInput"
|
||||||
|
class="annotationTextInput"
|
||||||
|
type="text"
|
||||||
|
tabindex="-1"
|
||||||
|
autocomplete="off"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a class="relabelRemove btn btn-sm btn-secondary">Remove marker</a>
|
||||||
|
<a class="relabelConfirm btn btn-sm btn-primary">OK</a>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
</template>
|
||||||
@ -0,0 +1,126 @@
|
|||||||
|
// ------ 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'];
|
||||||
|
|
||||||
|
dialogIds.forEach(id => {
|
||||||
|
const dialog = instance.$('#' + id);
|
||||||
|
dialog.draggable();
|
||||||
|
dialogPolyfill.registerDialog(dialog.get(0));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
@import "{design}/app.styl"
|
||||||
|
|
||||||
|
.annotationDialog
|
||||||
|
display: none
|
||||||
|
z-index: 1000
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
bottom: 0
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
margin: auto
|
||||||
|
|
||||||
|
overflow: hidden
|
||||||
|
padding: 10px
|
||||||
|
width: 300px
|
||||||
|
height: 140px
|
||||||
|
|
||||||
|
opacity: 0.95
|
||||||
|
border-radius: 8px
|
||||||
|
border: 1px solid $uiBorderColor
|
||||||
|
background: $uiGrayDarkest
|
||||||
|
color: $textSecondaryColor
|
||||||
|
|
||||||
|
h5, label
|
||||||
|
font-weight: 100
|
||||||
|
|
||||||
|
.annotationTextInputOptions
|
||||||
|
padding: 10px 0
|
||||||
|
|
||||||
|
.annotationTextInput
|
||||||
|
margin-left: 5px
|
||||||
|
|
||||||
|
.annotationDialogConfirm
|
||||||
|
float: right
|
||||||
@ -1,7 +1,5 @@
|
|||||||
@import "{design}/app.styl"
|
@import "{design}/app.styl"
|
||||||
|
|
||||||
$borderColor = rgba(77, 99, 110, 0.81)
|
|
||||||
|
|
||||||
#cineDialog
|
#cineDialog
|
||||||
display: none
|
display: none
|
||||||
z-index: 1000
|
z-index: 1000
|
||||||
|
|||||||
@ -564,14 +564,17 @@ Template.imageViewerViewport.onDestroyed(function() {
|
|||||||
|
|
||||||
Template.imageViewerViewport.events({
|
Template.imageViewerViewport.events({
|
||||||
'ActivateViewport .imageViewerViewport': function(e) {
|
'ActivateViewport .imageViewerViewport': function(e) {
|
||||||
|
console.log('activateViewport');
|
||||||
log.info('imageViewerViewport ActivateViewport');
|
log.info('imageViewerViewport ActivateViewport');
|
||||||
setActiveViewport(e.currentTarget);
|
setActiveViewport(e.currentTarget);
|
||||||
},
|
},
|
||||||
'click .imageViewerViewport': function(e) {
|
'click .imageViewerViewport': function(e) {
|
||||||
|
console.log('click imageViewerViewport handler');
|
||||||
var viewportIndex = $('.imageViewerViewport').index(e.currentTarget);
|
var viewportIndex = $('.imageViewerViewport').index(e.currentTarget);
|
||||||
Session.set('activeViewport', viewportIndex);
|
Session.set('activeViewport', viewportIndex);
|
||||||
},
|
},
|
||||||
'dblclick .imageViewerViewport': function(e) {
|
'CornerstoneToolsMouseDoubleClick .imageViewerViewport, CornerstoneToolsDoubleTap .imageViewerViewport': function(e) {
|
||||||
|
console.log('CornerstoneToolsMouseDoubleClick handler');
|
||||||
var viewportIndex = $('.imageViewerViewport').index(e.currentTarget);
|
var viewportIndex = $('.imageViewerViewport').index(e.currentTarget);
|
||||||
layoutManager.toggleEnlargement(viewportIndex);
|
layoutManager.toggleEnlargement(viewportIndex);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ var tools = {};
|
|||||||
|
|
||||||
var toolDefaultStates = {
|
var toolDefaultStates = {
|
||||||
activate: [],
|
activate: [],
|
||||||
deactivate: [],
|
deactivate: ['length', 'angle', 'annotate', 'ellipticalRoi', 'rectangleRoi'],
|
||||||
enable: [],
|
enable: [],
|
||||||
disable: [],
|
disable: [],
|
||||||
disabledToolButtons: []
|
disabledToolButtons: []
|
||||||
@ -35,6 +35,15 @@ function configureTools() {
|
|||||||
|
|
||||||
// Set color for active tools
|
// Set color for active tools
|
||||||
cornerstoneTools.toolColors.setActiveColor('#00ffff'); //rgb(0, 255, 0)'
|
cornerstoneTools.toolColors.setActiveColor('#00ffff'); //rgb(0, 255, 0)'
|
||||||
|
|
||||||
|
// Set the configuration values for the text annotation (Arrow) tool
|
||||||
|
var annotateConfig = {
|
||||||
|
getTextCallback: getAnnotationTextCallback,
|
||||||
|
changeTextCallback: changeAnnotationTextCallback,
|
||||||
|
drawHandles: false,
|
||||||
|
arrowFirst: true
|
||||||
|
};
|
||||||
|
cornerstoneTools.arrowAnnotate.setConfiguration(annotateConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
toolManager = {
|
toolManager = {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ Package.describe({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Package.onUse(function(api) {
|
Package.onUse(function(api) {
|
||||||
api.versionsFrom('1.3.5.1');
|
api.versionsFrom('1.4');
|
||||||
|
|
||||||
api.use('ecmascript');
|
api.use('ecmascript');
|
||||||
api.use('standard-app-packages');
|
api.use('standard-app-packages');
|
||||||
@ -26,7 +26,13 @@ Package.onUse(function(api) {
|
|||||||
api.addAssets('assets/icons.svg', 'client');
|
api.addAssets('assets/icons.svg', 'client');
|
||||||
|
|
||||||
// TODO: Use NPM depends for these
|
// TODO: Use NPM depends for these
|
||||||
api.addFiles('client/compatibility/jquery.hotkeys.js', 'client');
|
api.addFiles('client/compatibility/jquery.hotkeys.js', 'client', {
|
||||||
|
bare: true
|
||||||
|
});
|
||||||
|
api.addFiles('client/compatibility/dialogPolyfill.js', 'client', {
|
||||||
|
bare: true
|
||||||
|
});
|
||||||
|
api.addFiles('client/compatibility/dialogPolyfill.styl', 'client');
|
||||||
|
|
||||||
// ---------- Collections ----------
|
// ---------- Collections ----------
|
||||||
api.addFiles('client/collections.js', 'client');
|
api.addFiles('client/collections.js', 'client');
|
||||||
@ -75,6 +81,10 @@ Package.onUse(function(api) {
|
|||||||
api.addFiles('client/components/viewer/loadingIndicator/loadingIndicator.js', 'client');
|
api.addFiles('client/components/viewer/loadingIndicator/loadingIndicator.js', 'client');
|
||||||
api.addFiles('client/components/viewer/loadingIndicator/loadingIndicator.styl', 'client');
|
api.addFiles('client/components/viewer/loadingIndicator/loadingIndicator.styl', 'client');
|
||||||
|
|
||||||
|
api.addFiles('client/components/viewer/annotationDialogs/annotationDialogs.html', 'client');
|
||||||
|
api.addFiles('client/components/viewer/annotationDialogs/annotationDialogs.js', 'client');
|
||||||
|
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.html', 'client');
|
||||||
api.addFiles('client/components/viewer/viewportOrientationMarkers/viewportOrientationMarkers.js', 'client');
|
api.addFiles('client/components/viewer/viewportOrientationMarkers/viewportOrientationMarkers.js', 'client');
|
||||||
api.addFiles('client/components/viewer/viewportOrientationMarkers/viewportOrientationMarkers.styl', 'client');
|
api.addFiles('client/components/viewer/viewportOrientationMarkers/viewportOrientationMarkers.styl', 'client');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user