diff --git a/OHIFViewer-react/package.json b/OHIFViewer-react/package.json index 22e8f6690..b9424091f 100644 --- a/OHIFViewer-react/package.json +++ b/OHIFViewer-react/package.json @@ -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" }, diff --git a/OHIFViewer-react/src/ConnectedViewerFromStudyData.js b/OHIFViewer-react/src/ConnectedViewerFromStudyData.js index d4c72d99f..6067dea43 100644 --- a/OHIFViewer-react/src/ConnectedViewerFromStudyData.js +++ b/OHIFViewer-react/src/ConnectedViewerFromStudyData.js @@ -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 }; }; diff --git a/OHIFViewer-react/src/FlexboxLayout/ConnectedCornerstoneViewport.js b/OHIFViewer-react/src/FlexboxLayout/ConnectedCornerstoneViewport.js index a8ee233fb..e4cad0011 100644 --- a/OHIFViewer-react/src/FlexboxLayout/ConnectedCornerstoneViewport.js +++ b/OHIFViewer-react/src/FlexboxLayout/ConnectedCornerstoneViewport.js @@ -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)); } }; }; diff --git a/OHIFViewer-react/src/FlexboxLayout/ConnectedFlexboxLayout.js b/OHIFViewer-react/src/FlexboxLayout/ConnectedFlexboxLayout.js index a06dd85d4..f76393c97 100644 --- a/OHIFViewer-react/src/FlexboxLayout/ConnectedFlexboxLayout.js +++ b/OHIFViewer-react/src/FlexboxLayout/ConnectedFlexboxLayout.js @@ -5,7 +5,6 @@ const mapStateToProps = state => { return { leftSidebarOpen: state.ui.leftSidebarOpen, rightSidebarOpen: state.ui.rightSidebarOpen, - stackLoadingData: state.loading }; }; diff --git a/OHIFViewer-react/src/FlexboxLayout/ConnectedStudyBrowser.js b/OHIFViewer-react/src/FlexboxLayout/ConnectedStudyBrowser.js new file mode 100644 index 000000000..b19ad8815 --- /dev/null +++ b/OHIFViewer-react/src/FlexboxLayout/ConnectedStudyBrowser.js @@ -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; diff --git a/OHIFViewer-react/src/FlexboxLayout/FlexboxLayout.js b/OHIFViewer-react/src/FlexboxLayout/FlexboxLayout.js index 97f545e6a..e3346b299 100644 --- a/OHIFViewer-react/src/FlexboxLayout/FlexboxLayout.js +++ b/OHIFViewer-react/src/FlexboxLayout/FlexboxLayout.js @@ -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 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 (
- +
diff --git a/OHIFViewer-react/src/FlexboxLayout/ToolbarRow.js b/OHIFViewer-react/src/FlexboxLayout/ToolbarRow.js index 3d4bb543a..a669c4fff 100644 --- a/OHIFViewer-react/src/FlexboxLayout/ToolbarRow.js +++ b/OHIFViewer-react/src/FlexboxLayout/ToolbarRow.js @@ -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 {
+ {/* TODO: Putting cine dialog here for now */} +
+ +
{/* > roundedButtonGroup rightSidebarToggleButtonData */}
diff --git a/OHIFViewer-react/src/FlexboxLayout/ViewerMain.js b/OHIFViewer-react/src/FlexboxLayout/ViewerMain.js index 583b9d4f3..919af763a 100644 --- a/OHIFViewer-react/src/FlexboxLayout/ViewerMain.js +++ b/OHIFViewer-react/src/FlexboxLayout/ViewerMain.js @@ -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; diff --git a/OHIFViewer-react/src/Viewer/ConnectedStudyLoadingMonitor.js b/OHIFViewer-react/src/Viewer/ConnectedStudyLoadingMonitor.js new file mode 100644 index 000000000..9681d9ea4 --- /dev/null +++ b/OHIFViewer-react/src/Viewer/ConnectedStudyLoadingMonitor.js @@ -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; diff --git a/OHIFViewer-react/src/Viewer/StudyLoadingMonitor.js b/OHIFViewer-react/src/Viewer/StudyLoadingMonitor.js new file mode 100644 index 000000000..f449a1b28 --- /dev/null +++ b/OHIFViewer-react/src/Viewer/StudyLoadingMonitor.js @@ -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; diff --git a/OHIFViewer-react/src/Viewer/StudyPrefetch.js b/OHIFViewer-react/src/Viewer/StudyPrefetch.js new file mode 100644 index 000000000..b088c1340 --- /dev/null +++ b/OHIFViewer-react/src/Viewer/StudyPrefetch.js @@ -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; diff --git a/OHIFViewer-react/src/Viewer/Viewer.js b/OHIFViewer-react/src/Viewer/Viewer.js index 3efd6f45e..781ac6c72 100644 --- a/OHIFViewer-react/src/Viewer/Viewer.js +++ b/OHIFViewer-react/src/Viewer/Viewer.js @@ -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 (<>
@@ -96,7 +84,9 @@ class Viewer extends Component {
- + + +
); diff --git a/OHIFViewer-react/src/components/ConnectedCineDialog.js b/OHIFViewer-react/src/components/ConnectedCineDialog.js new file mode 100644 index 000000000..94486ddd1 --- /dev/null +++ b/OHIFViewer-react/src/components/ConnectedCineDialog.js @@ -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; diff --git a/OHIFViewer-react/yarn.lock b/OHIFViewer-react/yarn.lock index d76a3b910..6b1580bb9 100644 --- a/OHIFViewer-react/yarn.lock +++ b/OHIFViewer-react/yarn.lock @@ -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"