feat(CINE): Add basic CINE dialog support

This commit is contained in:
Erik Ziegler 2019-01-10 13:30:39 +01:00
parent 3954c32441
commit 6e291a18b8
14 changed files with 322 additions and 399 deletions

View File

@ -11,20 +11,21 @@
"cornerstone-wado-image-loader": "2.2.3",
"dicom-parser": "1.8.3",
"hammerjs": "^2.0.8",
"lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8",
"moment": "^2.22.2",
"ohif-core": "^0.1.4",
"ohif-core": "^0.1.5",
"oidc-client": "^1.6.1",
"prop-types": "^15.6.2",
"query-string": "^6.2.0",
"react": "^16.4.1",
"react-cornerstone-viewport": "^0.1.6",
"react-cornerstone-viewport": "^0.1.7",
"react-dom": "^16.4.1",
"react-redux": "^6.0.0",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-scripts": "^2.1.1",
"react-viewerbase": "^0.1.10",
"react-viewerbase": "^0.2.0",
"redux": "^4.0.1",
"redux-oidc": "^3.1.0"
},

View File

@ -4,12 +4,11 @@ import ViewerFromStudyData from './ViewerFromStudyData.js';
const isActive = (a) => a.active === true;
const mapStateToProps = (state, ownProps) => {
const mapStateToProps = state => {
const activeServer = state.servers.servers.find(isActive);
return {
server: activeServer,
...ownProps
};
};

View File

@ -2,26 +2,40 @@ import { connect } from 'react-redux';
import CornerstoneViewport from 'react-cornerstone-viewport';
import OHIF from 'ohif-core'
const { setViewportActive } = OHIF.redux.actions;
const { setViewportActive, setViewportSpecificData, clearViewportSpecificData } = OHIF.redux.actions;
const mapStateToProps = (state, ownProps) => {
const activeButton = state.tools.buttons.find(tool => tool.active === true);
// If this is the active viewport, enable prefetching.
const enableStackPrefetch = ownProps.viewportData.viewportIndex === state.viewports.activeViewportIndex;
const { viewportIndex } = ownProps.viewportData;
const isActive = viewportIndex === state.viewports.activeViewportIndex;
const viewportSpecificData = state.viewports.viewportSpecificData[viewportIndex] || {};
return {
layout: state.viewports.layout,
activeViewportIndex: state.viewports.activeViewportIndex,
isActive,
activeTool: activeButton && activeButton.command,
enableStackPrefetch
enableStackPrefetch: isActive,
//stack: viewportSpecificData.stack,
cineToolData: viewportSpecificData.cine
};
};
const mapDispatchToProps = dispatch => {
const mapDispatchToProps = (dispatch, ownProps) => {
const viewportIndex = ownProps.viewportData.viewportIndex;
return {
setViewportActive: viewportIndex => {
setViewportActive: () => {
dispatch(setViewportActive(viewportIndex))
},
setViewportSpecificData: (data) => {
dispatch(setViewportSpecificData(viewportIndex, data))
},
clearViewportSpecificData: () => {
dispatch(clearViewportSpecificData(viewportIndex));
}
};
};

View File

@ -5,7 +5,6 @@ const mapStateToProps = state => {
return {
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen,
stackLoadingData: state.loading
};
};

View File

@ -0,0 +1,40 @@
import { connect } from 'react-redux';
import { StudyBrowser } from 'react-viewerbase';
import cloneDeep from 'lodash.clonedeep';
// TODO
// - Determine in which display set is active from Redux (activeViewportIndex and layout viewportData)
// - Pass in errors and stack loading progress from Redux
const mapStateToProps = (state, ownProps) => {
// If we know that the stack loading progress details have changed,
// we can try to update the component state so that the thumbnail
// progress bar is updated
const stackLoadingProgressMap = state.loading.progress;
const studiesWithLoadingData = cloneDeep(ownProps.studies);
studiesWithLoadingData.forEach(study => {
study.thumbnails.forEach(data => {
const { displaySetInstanceUid } = data
const stackId = `StackProgress:${displaySetInstanceUid}`;
const stackProgressData = stackLoadingProgressMap[stackId];
let stackPercentComplete = 0;
if (stackProgressData) {
stackPercentComplete = stackProgressData.percentComplete;
}
data.stackPercentComplete = stackPercentComplete;
})
});
return {
studies: studiesWithLoadingData
};
};
const ConnectedStudyBrowser = connect(
mapStateToProps,
null
)(StudyBrowser);
export default ConnectedStudyBrowser;

View File

@ -1,73 +1,14 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import { StudyBrowser } from 'react-viewerbase';
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
import ViewerMain from './ViewerMain.js';
import './FlexboxLayout.css';
// TODO: Where should we put ViewerMain? ohif-core or react-viewerbase?
/**
* Asynchronous wrapper around Cornerstone's renderToCanvas method.
*
* @param {HTMLElement} canvasElement An HTML <canvas> element
* @param {Image} image A Cornerstone Image
*
* @return {Promise} A promise tracking the progress of the rendering. Resolves empty.
*/
function renderAsync(canvasElement, image) {
return new Promise((resolve, reject) => {
try {
cornerstone.renderToCanvas(canvasElement, image);
resolve();
} catch(error) {
reject(error);
}
});
}
// Define a handler for success on image load
const loadSuccess = function(image, studyInstanceUid, displaySetInstanceUid) {
// This is an off-screen canvas. It's used to get dataURL images by using
// cornerstone.renderToCanvas function.
const canvasElement = document.createElement('canvas');
canvasElement.width = 193;
canvasElement.height = 123;
// Render the image to canvas to be able to get its dataURL
renderAsync(canvasElement, image).then(() => {
const studies = this.state.studiesForBrowser;
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
const thumbnail = study.thumbnails.find(t => t.displaySetInstanceUid === displaySetInstanceUid);
thumbnail.imageSrc = canvasElement.toDataURL('image/jpeg', 1);
this.setState({
studiesForBrowser: studies
});
});
};
// Define a handler for error on image load
const loadError = function(error, studyInstanceUid, displaySetInstanceUid) {
const studies = this.state.studiesForBrowser;
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
const thumbnail = study.thumbnails.find(t => t.displaySetInstanceUid === displaySetInstanceUid);
thumbnail.error = true;
//thumbnail.details = error;
this.setState({
studiesForBrowser: studies
});
};
class FlexboxLayout extends Component {
static propTypes = {
studies: PropTypes.array.isRequired,
leftSidebarOpen: PropTypes.bool.isRequired,
rightSidebarOpen: PropTypes.bool.isRequired,
stackLoadingData: PropTypes.object// { lastUpdated, progress }
};
state = {
@ -80,67 +21,13 @@ class FlexboxLayout extends Component {
this.setState({
studiesForBrowser
});
this.getThumbnailsFromImageIds(studiesForBrowser);
}
componentDidUpdate(prevProps) {
const { stackLoadingData } = this.props;
// If we know that the stack loading progress details have changed,
// we can try to update the component state so that the thumbnail
// progress bar is updated
if (stackLoadingData.lastUpdated !== prevProps.stackLoadingData.lastUpdated) {
const stackLoadingProgressMap = stackLoadingData.progress;
const studiesForBrowser = this.state.studiesForBrowser;
studiesForBrowser.forEach(study => {
study.thumbnails.forEach(data => {
const { displaySetInstanceUid } = data
const stackId = `StackProgress:${displaySetInstanceUid}`;
const stackProgressData = stackLoadingProgressMap[stackId];
let stackPercentComplete = 0;
if (stackProgressData) {
stackPercentComplete = stackProgressData.percentComplete;
}
data.stackPercentComplete = stackPercentComplete;
})
})
this.setState({
studiesForBrowser
});
}
}
getThumbnailsFromImageIds = (studiesForBrowser) => {
studiesForBrowser.forEach(function (study) {
const { studyInstanceUid } = study;
study.thumbnails.forEach(function (displaySet) {
const imageId = displaySet.imageId;
const { displaySetInstanceUid } = displaySet;
cornerstone.loadAndCacheImage(imageId).then((image) => {
loadSuccess.call(this, image, studyInstanceUid, displaySetInstanceUid);
}, error => {
loadError.call(this, error, studyInstanceUid, displaySetInstanceUid);
});
}, this);
}, this);
}
getStudiesForBrowser = () => {
const { studies, stackLoadingData } = this.props;
const stackLoadingProgressMap = stackLoadingData.progress;
const { studies } = this.props;
// TODO[react]:
// - Add sorting of display sets
// - Pass in which display set is active from Redux
// - Pass in errors and stack loading progress from Redux
// - Add useMiddleSeriesInstanceAsThumbnail
// - Add showStackLoadingProgressBar option
return studies.map((study) => {
@ -148,27 +35,15 @@ class FlexboxLayout extends Component {
const thumbnails = study.displaySets.map((displaySet) => {
const { displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames } = displaySet;
const active = false;
const stackId = `StackProgress:${displaySetInstanceUid}`;
const stackProgressData = stackLoadingProgressMap[stackId];
let stackPercentComplete = 0;
if (stackProgressData) {
stackPercentComplete = stackProgressData.percentComplete;
}
const imageId = displaySet.images[0].getImageId();
return {
imageSrc: '',
imageId,
displaySetInstanceUid,
seriesDescription,
seriesNumber,
instanceNumber,
numImageFrames,
active,
stackPercentComplete
};
});
@ -193,7 +68,7 @@ class FlexboxLayout extends Component {
return (
<div className="FlexboxLayout">
<div className={this.props.leftSidebarOpen ? "sidebar-menu sidebar-left sidebar-open" : "sidebar-menu sidebar-left"}>
<StudyBrowser studies={this.state.studiesForBrowser}/>
<ConnectedStudyBrowser studies={this.state.studiesForBrowser}/>
</div>
<div className={mainContentClassName}>
<ViewerMain studies={this.props.studies}/>

View File

@ -1,6 +1,7 @@
import React, { Component } from "react";
import ConnectedToolbarSection from './ConnectedToolbarSection';
import ConnectedLayoutButton from './ConnectedLayoutButton';
import ConnectedCineDialog from '../components/ConnectedCineDialog.js';
import PropTypes from 'prop-types';
import { RoundedButtonGroup } from 'react-viewerbase';
import './ToolbarRow.css';
@ -40,6 +41,10 @@ class ToolbarRow extends Component {
</div>
<ConnectedToolbarSection/>
<ConnectedLayoutButton/>
{/* TODO: Putting cine dialog here for now */}
<div style={{ marginLeft: '100px', display: 'inline-block'}}>
<ConnectedCineDialog/>
</div>
<div className="pull-right m-t-1 rm-x-1">
{/* > roundedButtonGroup rightSidebarToggleButtonData */}
</div>

View File

@ -12,7 +12,6 @@ import './ViewerMain.css';
// Attempt to fix weird undefined dep issue
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
const { StudyLoadingListener, StudyPrefetcher } = OHIF.classes;
const { StackManager } = OHIF.utils;
function getCornerstoneStack(studies, viewportData) {
@ -38,19 +37,17 @@ class ViewerMain extends Component {
viewportData: [],
};
static propTypes = {
studies: PropTypes.array.isRequired
};
componentDidMount() {
// Add beforeUnload event handler to check for unsaved changes
//window.addEventListener('beforeunload', unloadHandlers.beforeUnload);
const { studies } = this.props;
this.studyPrefetcher = StudyPrefetcher.getInstance(cornerstone, cornerstoneTools);
this.studyPrefetcher.setStudies(studies);
this.studyLoadingListener = StudyLoadingListener.getInstance(cornerstone);
this.studyLoadingListener.clear();
this.studyLoadingListener.addStudies(studies);
// Get all the display sets for the viewer studies
const displaySets = [];
const studies = this.props.studies;
studies.forEach((study) => {
study.displaySets.forEach(dSet => dSet.images.length && displaySets.push(dSet));
});
@ -119,12 +116,6 @@ class ViewerMain extends Component {
// Destroy the synchronizer used to update reference lines
OHIF.viewer.updateImageSynchronizer.destroy();
// Stop prefetching when we close the viewer
this.studyPrefetcher.destroy();
// Destroy stack loading listeners when we close the viewer
this.studyLoadingListener.clear();
// Clear references to all stacks in the StackManager
StackManager.clearStacks();
@ -138,8 +129,4 @@ class ViewerMain extends Component {
}
}
ViewerMain.propTypes = {
studies: PropTypes.array.isRequired
};
export default ViewerMain;

View File

@ -0,0 +1,23 @@
import { connect } from 'react-redux';
import StudyLoadingMonitor from './StudyLoadingMonitor.js';
import OHIF from 'ohif-core'
const { setStudyLoadingProgress, clearStudyLoadingProgress } = OHIF.redux.actions;
const mapDispatchToProps = dispatch => {
return {
setStudyLoadingProgress: (progressId, progressData) => {
dispatch(setStudyLoadingProgress(progressId, progressData))
},
clearStudyLoadingProgress: progressId => {
dispatch(clearStudyLoadingProgress(progressId))
}
};
};
const ConnectedStudyLoadingMonitor = connect(
null,
mapDispatchToProps
)(StudyLoadingMonitor);
export default ConnectedStudyLoadingMonitor;

View File

@ -0,0 +1,43 @@
import { Component } from "react";
import PropTypes from "prop-types";
import OHIF from 'ohif-core';
class StudyLoadingMonitor extends Component {
static propTypes = {
studies: PropTypes.array.isRequired,
setStudyLoadingProgress: PropTypes.func.isRequired,
clearStudyLoadingProgress: PropTypes.func.isRequired,
};
componentDidMount() {
// TODO: This is pretty ugly. The thing is that the StudyLoadingListener
// needs to update the Redux store, but shouldn't know that it exists.
// I am therefore passing in some functions to update the store instead,
// but this should definitely be cleaned up somehow.
const options = {
_setProgressData: (progressId, progressData) => {
this.props.setStudyLoadingProgress(progressId, progressData)
},
_clearProgressById: (progressId) => {
this.props.clearStudyLoadingProgress(progressId)
}
}
const { StudyLoadingListener } = OHIF.classes;
this.studyLoadingListener = StudyLoadingListener.getInstance(options);
this.studyLoadingListener.clear();
this.studyLoadingListener.addStudies(this.props.studies);
}
render() {
return null;
}
componentWillUnmount() {
// Destroy stack loading listeners when we close the viewer
this.studyLoadingListener.clear();
}
}
export default StudyLoadingMonitor;

View File

@ -0,0 +1,30 @@
import { Component } from "react";
import PropTypes from "prop-types";
import OHIF from 'ohif-core';
const { StudyPrefetcher } = OHIF.classes;
class StudyPrefetch extends Component {
static propTypes = {
studies: PropTypes.array,
};
componentDidMount() {
const { studies } = this.props;
this.studyPrefetcher = StudyPrefetcher.getInstance();
this.studyPrefetcher.setStudies(studies);
}
render() {
return null;
}
componentWillUnmount() {
// Stop prefetching when we close the viewer
this.studyPrefetcher.destroy();
}
}
export default StudyPrefetch;

View File

@ -8,6 +8,8 @@ import OHIF from 'ohif-core';
import Header from '../Header'
import ConnectedFlexboxLayout from '../FlexboxLayout/ConnectedFlexboxLayout.js';
import ConnectedToolbarRow from "../FlexboxLayout/ConnectedToolbarRow";
import ConnectedStudyLoadingMonitor from './ConnectedStudyLoadingMonitor.js';
import StudyPrefetch from './StudyPrefetch.js';
import './Viewer.css';
const { StackManager } = OHIF.utils;
@ -49,10 +51,6 @@ OHIF.viewer.defaultTool = {
};
OHIF.viewer.refLinesEnabled = true;
OHIF.viewer.cine = {
framesPerSecond: 24,
loop: true
};
/*const viewportUtils = OHIF.viewerbase.viewportUtils;
@ -75,19 +73,9 @@ StackManager.setMetadataProvider(metadataProvider);
class Viewer extends Component {
static propTypes = {
studies: PropTypes.array
studies: PropTypes.array,
};
constructor(props) {
super(props);
this.state = {
leftSidebar: 'studies',
rightSidebar: 'measurements',
studies: this.props.studies
};
}
render() {
return (<>
<Header home={false}/>
@ -96,7 +84,9 @@ class Viewer extends Component {
</div>
<div id="viewer" className='Viewer'>
<ConnectedToolbarRow />
<ConnectedFlexboxLayout studies={this.state.studies} />
<ConnectedStudyLoadingMonitor studies={this.props.studies} />
<StudyPrefetch studies={this.props.studies} />
<ConnectedFlexboxLayout studies={this.props.studies} />
</div>
</>
);

View File

@ -0,0 +1,111 @@
import { connect } from 'react-redux';
import { CineDialog } from 'react-viewerbase';
import OHIF from 'ohif-core';
import cloneDeep from 'lodash.clonedeep';
const { setViewportSpecificData } = OHIF.redux.actions;
// TODO: I'm guessing this function will be used in other connect locations
// so we might want to put it somewhere shared
function getActiveViewportSpecificData(state) {
const { viewportSpecificData, activeViewportIndex } = state.viewports;
return viewportSpecificData[activeViewportIndex];
}
const mapStateToProps = state => {
// TODO:
// - Test if including CineDialog in the toolbarRow will prevent it
// from hovering over the rest of the UI when visible.
//
// - Create custom ToolbarButton which just shows Play state
// - Connect this ToolbarButton to Redux
const activeViewportSpecificData = getActiveViewportSpecificData(state);
let stack = {
imageIds: [],
currentImageIdIndex: 0
};
if (activeViewportSpecificData && activeViewportSpecificData.stack) {
stack = activeViewportSpecificData.stack
}
let cine = {
isPlaying: false,
cineFrameRate: 24
};
if (activeViewportSpecificData && activeViewportSpecificData.cine) {
cine = activeViewportSpecificData.cine
}
// TODO: activeViewportStackData won't currently change anything on
// CornerstoneViewport. The updates are too frequent and it's killing
// performance. Need to revisit how we can do this.
return {
activeViewportStackData: stack,
activeViewportCineData: cine,
activeViewportIndex: state.viewports.activeViewportIndex
};
};
const mapDispatchToProps = dispatch => {
return {
dispatchSetViewportSpecificData: (viewportIndex, data) => {
dispatch(setViewportSpecificData(viewportIndex, data));
},
};
};
const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
const { activeViewportStackData, activeViewportCineData, activeViewportIndex } = propsFromState;
return {
cineFrameRate: activeViewportCineData.cineFrameRate,
isPlaying: activeViewportCineData.isPlaying,
onPlayPauseChanged: isPlaying => {
const cine = cloneDeep(activeViewportCineData);
cine.isPlaying = !cine.isPlaying;
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { cine });
},
onFrameRateChanged: frameRate => {
const cine = cloneDeep(activeViewportCineData);
cine.cineFrameRate = frameRate;
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { cine });
},
onClickNextButton: () => {
const stack = cloneDeep(activeViewportStackData);
const largestPossibleIndex = stack.imageIds.length - 1;
stack.currentImageIdIndex = Math.min(stack.currentImageIdIndex + 1, largestPossibleIndex)
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack });
},
onClickBackButton: () => {
const stack = cloneDeep(activeViewportStackData);
stack.currentImageIdIndex = Math.max(stack.currentImageIdIndex - 1, 0);
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack });
},
onClickSkipToStart: () => {
const stack = cloneDeep(activeViewportStackData);
stack.currentImageIdIndex = 0;
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack });
},
onClickSkipToEnd: () => {
const stack = cloneDeep(activeViewportStackData);
stack.currentImageIdIndex = stack.imageIds.length;
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack });
}
};
};
const ConnectedCineDialog = connect(
mapStateToProps,
mapDispatchToProps,
mergeProps
)(CineDialog);
export default ConnectedCineDialog;

View File

@ -2273,11 +2273,6 @@ clone-deep@^2.0.1:
kind-of "^6.0.0"
shallow-clone "^1.0.0"
clone@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@ -3711,7 +3706,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
extend@^3.0.1, extend@~3.0.2:
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
@ -6037,67 +6032,6 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
lodash._basecallback@^3.0.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/lodash._basecallback/-/lodash._basecallback-3.3.1.tgz#b7b2bb43dc2160424a21ccf26c57e443772a8e27"
integrity sha1-t7K7Q9whYEJKIczybFfkQ3cqjic=
dependencies:
lodash._baseisequal "^3.0.0"
lodash._bindcallback "^3.0.0"
lodash.isarray "^3.0.0"
lodash.pairs "^3.0.0"
lodash._baseeach@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash._baseeach/-/lodash._baseeach-3.0.4.tgz#cf8706572ca144e8d9d75227c990da982f932af3"
integrity sha1-z4cGVyyhROjZ11InyZDamC+TKvM=
dependencies:
lodash.keys "^3.0.0"
lodash._basefind@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._basefind/-/lodash._basefind-3.0.0.tgz#b2bba05cc645f972de2cf925fa2bf63a0f60c8ae"
integrity sha1-srugXMZF+XLeLPkl+iv2Og9gyK4=
lodash._basefindindex@^3.0.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/lodash._basefindindex/-/lodash._basefindindex-3.6.0.tgz#f083360a1b022418ed81bc899beb312e21e74a4f"
integrity sha1-8IM2ChsCJBjtgbyJm+sxLiHnSk8=
lodash._baseisequal@^3.0.0:
version "3.0.7"
resolved "https://registry.yarnpkg.com/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz#d8025f76339d29342767dcc887ce5cb95a5b51f1"
integrity sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=
dependencies:
lodash.isarray "^3.0.0"
lodash.istypedarray "^3.0.0"
lodash.keys "^3.0.0"
lodash._baseismatch@^3.0.0:
version "3.1.3"
resolved "https://registry.yarnpkg.com/lodash._baseismatch/-/lodash._baseismatch-3.1.3.tgz#0728fc48efa11699d3d5f2d73049f2ab13c40fd5"
integrity sha1-Byj8SO+hFpnT1fLXMEnyqxPED9U=
dependencies:
lodash._baseisequal "^3.0.0"
lodash._basematches@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/lodash._basematches/-/lodash._basematches-3.2.0.tgz#f47e03f07ec20784ab0968d0cb6cb597e2101158"
integrity sha1-9H4D8H7CB4SrCWjQy2y1l+IQEVg=
dependencies:
lodash._baseismatch "^3.0.0"
lodash.pairs "^3.0.0"
lodash._bindcallback@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
lodash._reinterpolate@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@ -6108,112 +6042,31 @@ lodash.camelcase@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.every@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.every/-/lodash.every-4.6.0.tgz#eb89984bebc4364279bb3aefbbd1ca19bfa6c6a7"
integrity sha1-64mYS+vENkJ5uzrvu9HKGb+mxqc=
lodash.find@^3.0.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-3.2.1.tgz#046e319f3ace912ac6c9246c7f683c5ec07b36ad"
integrity sha1-BG4xnzrOkSrGySRsf2g8XsB7Nq0=
dependencies:
lodash._basecallback "^3.0.0"
lodash._baseeach "^3.0.0"
lodash._basefind "^3.0.0"
lodash._basefindindex "^3.0.0"
lodash.isarray "^3.0.0"
lodash.keys "^3.0.0"
lodash.find@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1"
integrity sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=
lodash.findwhere@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.findwhere/-/lodash.findwhere-3.1.0.tgz#7937d34f3eac818dec7fa94e8ca5e26fdba1cfc1"
integrity sha1-eTfTTz6sgY3sf6lOjKXib9uhz8E=
dependencies:
lodash._basematches "^3.0.0"
lodash.find "^3.0.0"
lodash.foreach@^4.5.0:
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
lodash.isarguments@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=
lodash.isarray@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=
lodash.isempty@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=
lodash.isobject@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d"
integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=
lodash.istypedarray@^3.0.0:
version "3.0.6"
resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8494d283b87c39281cef62"
integrity sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=
lodash.keys@^3.0.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=
dependencies:
lodash._getnative "^3.0.0"
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
lodash.merge@^4.6.0:
lodash.merge@^4.6.1:
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
lodash.omit@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60"
integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=
lodash.pairs@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash.pairs/-/lodash.pairs-3.0.1.tgz#bbe08d5786eeeaa09a15c91ebf0dcb7d2be326a9"
integrity sha1-u+CNV4bu6qCaFckevw3LfSvjJqk=
dependencies:
lodash.keys "^3.0.0"
lodash.pick@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=
lodash.sortby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
@ -6239,21 +6092,11 @@ lodash.templatesettings@^4.0.0:
dependencies:
lodash._reinterpolate "~3.0.0"
lodash.union@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash.without@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac"
integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=
"lodash@>=3.5 <5", lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
@ -6401,14 +6244,6 @@ merge@^1.2.0:
resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145"
integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==
message-box@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/message-box/-/message-box-0.2.0.tgz#f93da217cb11f2a13cb501846fa713f21d6eabb7"
integrity sha512-SPLfVDEM2YcAgV2IB0B5vOGjvqXSSw7ZibEeXcff8HYpxyG1Uj+XjgnGUGyR1C0EQCvPI3MBx3p7opt2CIQ2hw==
dependencies:
lodash.merge "^4.6.0"
lodash.template "^4.4.0"
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@ -6608,16 +6443,6 @@ moment@^2.23.0:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.23.0.tgz#759ea491ac97d54bac5ad776996e2a58cc1bc225"
integrity sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA==
mongo-object@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/mongo-object/-/mongo-object-0.1.3.tgz#56b2a46d3ae4f0bdb1935241928fb444034b92e5"
integrity sha512-m3vs+a1JkvRXELJMe2ieMmBe03NUy6bctGjWicRhReYj8brDi0ojKHLKLmXWr/RupNaFP8Q7/x8xG8GpFtp9wg==
dependencies:
lodash.foreach "^4.5.0"
lodash.isempty "^4.4.0"
lodash.isobject "^3.0.2"
lodash.without "^4.4.0"
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -6967,18 +6792,18 @@ obuf@^1.0.0, obuf@^1.1.1:
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
ohif-core@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/ohif-core/-/ohif-core-0.1.3.tgz#e2c8e6365b3666e24b67c6db120be2aeed4c55e8"
integrity sha512-rLYm/Uduw+XbX4pzhX3oAXT8gU22SzRdmOZ6CYsj0/ZCTJMv3SwT4oxKgOQReoOKzdRR13nhQuy0zn1QKuruCA==
ohif-core@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/ohif-core/-/ohif-core-0.1.5.tgz#c44c489eb54c6062169c030bb452bce6e115a639"
integrity sha512-Ogrg2YRQbRn7SyrhPEeYwitf7e1r24/NqhFmIgmq5p1K3nfe+aVQOxfJKG5nlZJfUAJoqlcXWRv6poNjVHKRzA==
dependencies:
cornerstone-math "^0.1.7"
isomorphic-base64 "^1.0.2"
jquery "^3.3.1"
jquery.hotkeys "^0.1.0"
redux "^4.0.1"
simpl-schema "^1.5.3"
underscore "^1.9.1"
lodash.clonedeep "^4.5.0"
lodash.merge "^4.6.1"
validate.js "^0.12.0"
oidc-client@^1.6.1:
version "1.6.1"
@ -8309,10 +8134,10 @@ react-bootstrap-modal@^4.2.0:
react-overlays "^0.8.0"
react-transition-group "^2.0.0"
react-cornerstone-viewport@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.5.tgz#64809bcba4942710c1cd7dddd63ab14cb682b31f"
integrity sha512-ANSjQvYOc533k055j4/sV+SrOgmLoEI7ssQ6MiHn5j3E3po30ooUYMWc56fsDxEH/lxFt5VtcFyHYgJdI4RHig==
react-cornerstone-viewport@^0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.7.tgz#c09881f073f068dc23095fe65a45f47598a91999"
integrity sha512-IT8kmUycMvBKmNZfBhROPpnUpRHRfsovR2U/iKK2OxqJ+v92qrlfN/CRO6Wd6AOukkQG2uwRrKUawh+WA/a8uw==
dependencies:
lodash.debounce "^4.0.8"
moment "^2.23.0"
@ -8507,12 +8332,13 @@ react-transition-group@^2.0.0, react-transition-group@^2.2.0:
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
react-viewerbase@^0.1.10:
version "0.1.10"
resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.1.10.tgz#7b5d2d9adcea20174a863c0291eca6a728d8da6a"
integrity sha512-I6f6n3wgGD9aHyMTta9CIae3BXWk6TTmeAmqaYrfR+uSTfnjBsARmaj2FKtnusGLc2XCZlsSKUQRHSVL6NH3Kg==
react-viewerbase@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.2.0.tgz#a75a4e6bd9e2113da509e4a42f6eeb65cb6a2516"
integrity sha512-uVe7YYbM0Kzu9Vi9pkOV357vkPL5Mu0BEdivRIqy8aJeRZOFvrwKeQKBVTHLPmCljKhMVp6djhn19rzuLUgJuQ==
dependencies:
classnames "^2.2.6"
lodash.isequal "^4.5.0"
react-bootstrap-modal "^4.2.0"
react-dnd "^7.0.2"
react-dnd-html5-backend "^7.0.2"
@ -9221,26 +9047,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
simpl-schema@^1.5.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/simpl-schema/-/simpl-schema-1.5.3.tgz#c1d12ebddd7ff6dd32ace5e6364824ae506d4a9a"
integrity sha512-J24gSh39gBc3TYRFgaWx81bRf9unNML9u3dTAgxL1KI3F9JOv+Z29yEbosJFqmHfuuZ3K7ET/tTQKX4r4+f8/w==
dependencies:
clone "^2.1.1"
extend "^3.0.1"
lodash.every "^4.6.0"
lodash.find "^4.6.0"
lodash.findwhere "^3.1.0"
lodash.includes "^4.3.0"
lodash.isempty "^4.4.0"
lodash.isobject "^3.0.2"
lodash.omit "^4.5.0"
lodash.pick "^4.4.0"
lodash.union "^4.6.0"
lodash.uniq "^4.5.0"
message-box "^0.2.0"
mongo-object "^0.1.3"
simple-swizzle@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
@ -9990,11 +9796,6 @@ uglifyjs-webpack-plugin@^1.2.4:
webpack-sources "^1.1.0"
worker-farm "^1.5.2"
underscore@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
@ -10177,6 +9978,11 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
validate.js@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/validate.js/-/validate.js-0.12.0.tgz#17f989e37c192ea2f826bbf19bf4e97e6e4be68f"
integrity sha512-/x2RJSvbqEyxKj0RPN4xaRquK+EggjeVXiDDEyrJzsJogjtiZ9ov7lj/svVb4DM5Q5braQF4cooAryQbUwOxlA==
value-equal@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7"