Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc40d84092 | ||
|
|
709f14708e | ||
|
|
5f4c4ab5e7 | ||
|
|
71205618ec | ||
|
|
5014ba864b | ||
|
|
e96205de35 | ||
|
|
273f31ad56 | ||
|
|
2093a00365 | ||
|
|
b1dad6b61b | ||
|
|
3a587f4e4b | ||
|
|
d9fc7bbb0e | ||
|
|
b0cfc45f83 | ||
|
|
450c80b9d5 | ||
|
|
d3eefb50f5 | ||
|
|
29d4955980 | ||
|
|
2412c08595 |
No files matched your search
@@ -126,12 +126,39 @@ globally defined styles/scripts.
|
||||
> We're trying to embed the OHIF Viewer into an existing React App, but seeing
|
||||
> react-dom and react conflicts. What can we do?
|
||||
|
||||
If you are installing OHIF viewer inside another react app, you may use `installViewer` as follows:
|
||||
```
|
||||
import { installViewer } from '@ohif/viewer'
|
||||
|
||||
const ohifViewerConfig = window.config // or set it here
|
||||
const containerId = 'ohif'
|
||||
const componentRenderedOrUpdatedCallback = function() {
|
||||
console.log('OHIF Viewer rendered/updated');
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
installViewer(
|
||||
ohifViewerConfig,
|
||||
containerId,
|
||||
componentRenderedOrUpdatedCallback
|
||||
);
|
||||
}
|
||||
|
||||
render () {
|
||||
...
|
||||
//you can render in any element you wish
|
||||
<AnyTag id={containerId}/>
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
`installViewer` is a convenience method that pulls in some dependencies that may
|
||||
not be compatible with existing `react` apps. `@ohif/viewer` also exports `App`
|
||||
which is a react component that takes the `configuration` outlined above as
|
||||
props. You can use it as a reusable component, and to avoid `react` version
|
||||
conflict issues.
|
||||
|
||||
|
||||
<!--
|
||||
LINKS
|
||||
-->
|
||||
|
||||
@@ -11,7 +11,59 @@ are:
|
||||
```js
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
/**
|
||||
* "White Labeling" is used to change the branding, look, and feel of the OHIF
|
||||
* Viewer. These settings, and the color variables that are used by our components,
|
||||
* are the easiest way to rebrand the application.
|
||||
*
|
||||
* More extensive changes are made possible through swapping out the UI library,
|
||||
* Viewer project, or extensions.
|
||||
*/
|
||||
whiteLabeling: {
|
||||
/* Optional: Should return a React component to be rendered in the "Logo" section of the application's Top Navigation bar */
|
||||
createLogoComponentFn: function(React) {
|
||||
return React.createElement('a', {
|
||||
target: '_self',
|
||||
rel: 'noopener noreferrer',
|
||||
className: 'header-brand',
|
||||
href: '/',
|
||||
style: {
|
||||
display: 'block',
|
||||
textIndent: '-9999px',
|
||||
background: 'url(/svg-file-hosted-at-domain-root.svg)',
|
||||
backgroundSize: 'contain',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
width: '200px',
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Internally, the OHIF Viewer fetches data primarily with the
|
||||
* `cornerstoneWADOImageLoader` and the `DICOMWebClient`. If either of these
|
||||
* receive a non-200 response, this method allows you to handle that error.
|
||||
*
|
||||
* Common use cases include:
|
||||
* - Showing a notification with the UINotificationService
|
||||
* - Redirecting the user
|
||||
* - Refreshing an auth token
|
||||
*
|
||||
* @param {Object} error - JS new Error()
|
||||
* @param {XMLHttpRequest} error.request - The XHR request that's onreadystate change triggered this callback
|
||||
* @param {string} error.response - The XHR's response property
|
||||
* @param {number} error.status - The XHR's status property
|
||||
*/
|
||||
httpErrorHandler: error => {
|
||||
const { request: xhr, response, status } = err;
|
||||
const { responseType, statusText } = xhr;
|
||||
|
||||
// In local files, status is 0 upon success in Firefox
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
console.log(statusText, response, responseType);
|
||||
} else {
|
||||
console.warn('Likely CORS error');
|
||||
}
|
||||
},
|
||||
extensions: [],
|
||||
showStudyList: true,
|
||||
filterQueryParam: false,
|
||||
|
||||
@@ -66,11 +66,11 @@ open to pull requests and discussion issues.
|
||||
|
||||
Current white-labeling options are limited. We expose the ability to replace the
|
||||
"Logo" section of the application with a custom "Logo" component. You can do
|
||||
this by adding a `whiteLabelling` key to your
|
||||
this by adding a `whiteLabeling` key to your
|
||||
[configuration file](./configuration.md).
|
||||
|
||||
```js
|
||||
function RadicalImagingLogo() {
|
||||
function RadicalImagingLogo(React) {
|
||||
return React.createElement(
|
||||
'a',
|
||||
{
|
||||
@@ -83,8 +83,8 @@ function RadicalImagingLogo() {
|
||||
);
|
||||
}
|
||||
|
||||
props.whiteLabelling = {
|
||||
logoComponent: RadicalImagingLogo(),
|
||||
props.whiteLabeling = {
|
||||
createLogoComponentFn: RadicalImagingLogo,
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
+17
-12
@@ -20,10 +20,10 @@ abbrev@~1.0.9:
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
|
||||
integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU=
|
||||
|
||||
agent-base@4, agent-base@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
||||
integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
|
||||
agent-base@4, agent-base@^4.1.0, agent-base@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
|
||||
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
@@ -773,9 +773,9 @@ err-code@^1.0.0:
|
||||
prr "~1.0.1"
|
||||
|
||||
es6-promise@^4.0.3:
|
||||
version "4.2.6"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f"
|
||||
integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
||||
|
||||
es6-promisify@^5.0.0:
|
||||
version "5.0.0"
|
||||
@@ -1289,11 +1289,11 @@ http-signature@~1.2.0:
|
||||
sshpk "^1.7.0"
|
||||
|
||||
https-proxy-agent@^2.1.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
|
||||
integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
||||
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
||||
dependencies:
|
||||
agent-base "^4.1.0"
|
||||
agent-base "^4.3.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
humanize-ms@^1.2.1:
|
||||
@@ -1807,11 +1807,16 @@ ms@2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
ms@^2.0.0, ms@^2.1.1:
|
||||
ms@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
mute-stream@~0.0.4:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.6.0...@ohif/extension-cornerstone@2.6.1) (2020-04-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Multiframe fix ([#1661](https://github.com/OHIF/Viewers/issues/1661)) ([7120561](https://github.com/OHIF/Viewers/commit/71205618ecb8b592247c5acb32284bfe7e18fce5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.5.2...@ohif/extension-cornerstone@2.6.0) (2020-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.5.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.5.1...@ohif/extension-cornerstone@2.5.2) (2020-04-09)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "2.5.2",
|
||||
"version": "2.6.1",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -35,7 +35,7 @@
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "4.12.5",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"cornerstone-wado-image-loader": "^3.1.0",
|
||||
"dcmjs": "^0.10.1",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"hammerjs": "^2.0.8",
|
||||
|
||||
@@ -2,6 +2,9 @@ import Hammer from 'hammerjs';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneMath from 'cornerstone-math';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import OHIF from '@ohif/core';
|
||||
|
||||
const { log } = OHIF;
|
||||
|
||||
export default function(configuration = {}) {
|
||||
// For debugging
|
||||
@@ -12,6 +15,13 @@ export default function(configuration = {}) {
|
||||
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
|
||||
cornerstoneTools.init(configuration);
|
||||
|
||||
cornerstoneTools.loadHandlerManager.setErrorLoadingHandler(
|
||||
(element, imageId, error) => {
|
||||
log.error(imageId);
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
|
||||
// Set the tool font and font size
|
||||
// context.font = "[style] [variant] [weight] [size]/[line height] [font family]";
|
||||
const fontFamily =
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const pkg = require('./../package.json');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './..');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
||||
|
||||
return merge(commonConfig, {
|
||||
devtool: 'source-map',
|
||||
stats: {
|
||||
colors: true,
|
||||
hash: true,
|
||||
timings: true,
|
||||
assets: true,
|
||||
chunks: false,
|
||||
chunkModules: false,
|
||||
modules: false,
|
||||
children: false,
|
||||
warnings: true,
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
sideEffects: true,
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'OHIFExtDicomP10Downloader',
|
||||
libraryTarget: 'umd',
|
||||
libraryExport: 'default',
|
||||
filename: pkg.main,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.0.2...@ohif/extension-dicom-p10-downloader@0.1.0) (2020-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.0.2 (2020-04-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Open Health Imaging Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1 @@
|
||||
# @ohif/extension-dicom-p10-downloader
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-p10-downloader",
|
||||
"version": "0.1.0",
|
||||
"description": "OHIF extension for downloading DICOM P10 files",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
"repository": "OHIF/Viewers",
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^2.6.0",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"dicomweb-client": "^0.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dicomweb-client": "^0.6.0",
|
||||
"file-saver": "^2.0.2",
|
||||
"jszip": "^3.2.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import {
|
||||
save,
|
||||
getDicomWebClientFromContext,
|
||||
getSOPInstanceReferenceFromActiveViewport,
|
||||
getSOPInstanceReferencesFromViewports,
|
||||
} from './utils';
|
||||
import _downloadAndZip from './downloadAndZip';
|
||||
|
||||
const {
|
||||
utils: {
|
||||
Queue,
|
||||
},
|
||||
} = OHIF;
|
||||
|
||||
export function getCommands(context) {
|
||||
const queue = new Queue(1);
|
||||
const actions = {
|
||||
/**
|
||||
* @example Running this command using Commands Manager
|
||||
* commandsManager.runCommand(
|
||||
* 'downloadAndZip',
|
||||
* {
|
||||
* listOfUIDs: [...],
|
||||
* options: {
|
||||
* progress(status) {
|
||||
* console.info('Progress:', (status.progress * 100).toFixed(2) + '%');
|
||||
* }
|
||||
* }
|
||||
* },
|
||||
* 'VIEWER'
|
||||
* );
|
||||
*/
|
||||
downloadAndZip({ servers, dicomWebClient, listOfUIDs, options }) {
|
||||
return save(
|
||||
_downloadAndZip(
|
||||
dicomWebClient || getDicomWebClientFromContext(context, servers),
|
||||
listOfUIDs,
|
||||
options
|
||||
),
|
||||
listOfUIDs
|
||||
);
|
||||
},
|
||||
downloadAndZipSeriesOnViewports({ servers, viewports, progress }) {
|
||||
const dicomWebClient = getDicomWebClientFromContext(context, servers);
|
||||
const listOfUIDs = getSOPInstanceReferencesFromViewports(viewports);
|
||||
return save(
|
||||
_downloadAndZip(dicomWebClient, listOfUIDs, { progress }),
|
||||
listOfUIDs
|
||||
);
|
||||
},
|
||||
downloadAndZipSeriesOnActiveViewport({ servers, viewports, progress }) {
|
||||
const dicomWebClient = getDicomWebClientFromContext(context, servers);
|
||||
const listOfUIDs = getSOPInstanceReferenceFromActiveViewport(viewports);
|
||||
return save(
|
||||
_downloadAndZip(dicomWebClient, listOfUIDs, { progress }),
|
||||
listOfUIDs
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
downloadAndZip: {
|
||||
commandFn: queue.bindSafe(actions.downloadAndZip, error),
|
||||
storeContexts: ['servers'],
|
||||
},
|
||||
downloadAndZipSeriesOnViewports: {
|
||||
commandFn: queue.bindSafe(actions.downloadAndZipSeriesOnViewports, error),
|
||||
storeContexts: ['servers', 'viewports'],
|
||||
options: { progress },
|
||||
},
|
||||
downloadAndZipSeriesOnActiveViewport: {
|
||||
commandFn: queue.bindSafe(
|
||||
actions.downloadAndZipSeriesOnActiveViewport,
|
||||
error
|
||||
),
|
||||
storeContexts: ['servers', 'viewports'],
|
||||
options: { progress },
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
actions,
|
||||
definitions,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
|
||||
function progress(status) {
|
||||
OHIF.log.info(
|
||||
'Download and Zip Progress:',
|
||||
(status.progress * 100.0).toFixed(2) + '%'
|
||||
);
|
||||
}
|
||||
|
||||
function error(e) {
|
||||
if (e.message === 'Queue limit reached') {
|
||||
OHIF.log.warn('A download is already in progress, please wait.');
|
||||
} else {
|
||||
OHIF.log.error(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import { api } from 'dicomweb-client';
|
||||
import dicomParser from 'dicom-parser';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const {
|
||||
utils: {
|
||||
isDicomUid,
|
||||
hierarchicalListUtils,
|
||||
progressTrackingUtils: progressUtils,
|
||||
},
|
||||
} = OHIF;
|
||||
|
||||
/**
|
||||
* Public Methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Download and Zip all DICOM P10 instances from specified DICOM Web Client
|
||||
* based on an hierarchical list of UIDs;
|
||||
*
|
||||
* @param {DICOMwebClient} dicomWebClient A DICOMwebClient instance through
|
||||
* which the referenced instances will be retrieved;
|
||||
* @param {Array} listOfUIDs The hierarchical list of UIDs from the instances
|
||||
* that should be retrieved:
|
||||
* A hierarchical list of UIDs is a regular JS Array where the type of the UID
|
||||
* (study, series, instance) is determined by its nasting lavel. For example:
|
||||
* @ The following list instructs the library to download all the instances
|
||||
* from both studies "A" and "B":
|
||||
*
|
||||
* ['studyUIDFromA', 'studyUIDFromB']
|
||||
*
|
||||
* @ In the previous example both UIDs are treated as STUDY UIDs because both
|
||||
* of them are listed in the same (top) level of the list. If, on the other
|
||||
* hand, only instances from series "I" and "J" from the study "B"
|
||||
* are to be downloaded, the expected hierarchical list would be:
|
||||
*
|
||||
* ['studyUIDFromA', ['studyUIDFromB', ['seriesUIDFromI', 'seriesUIDFromJ']]]
|
||||
*
|
||||
* @ Which, when prettified, reads like this:
|
||||
*
|
||||
* [
|
||||
* 'studyUIDFromA',
|
||||
* ['studyUIDFromB', [
|
||||
* 'seriesUIDFromI',
|
||||
* 'seriesUIDFromJ'
|
||||
* ]]
|
||||
* ]
|
||||
*
|
||||
* @ Furthermore, if only instances "X", "Y" and "Z" from series "J" need to
|
||||
* be downloaded (instead of all the instances from that series), the list
|
||||
* could be changed to:
|
||||
*
|
||||
* [
|
||||
* 'studyUIDFromA',
|
||||
* ['studyUIDFromB', [
|
||||
* 'seriesUIDFromI',
|
||||
* ['seriesUIDFromJ', [
|
||||
* 'instanceUIDFromX',
|
||||
* 'instanceUIDFromY',
|
||||
* 'instanceUIDFromZ'
|
||||
* ]]
|
||||
* ]]
|
||||
* ]
|
||||
*
|
||||
* Please refer to hierarchicalListUtils.js for more information and utilities;
|
||||
*
|
||||
* @param {Object} options A plain object with options;
|
||||
* @param {function} options.progress A callback to retrieve notifications
|
||||
* @returns {Promise} A promise that resolves to an URL from which the ZIP file
|
||||
* can be downloaded;
|
||||
*/
|
||||
|
||||
async function downloadAndZip(dicomWebClient, listOfUIDs, options) {
|
||||
if (dicomWebClient instanceof api.DICOMwebClient) {
|
||||
const settings = buildSettings(listOfUIDs, options);
|
||||
const { compression } = settings.tasks;
|
||||
// Register user-provided progress handler as a task list observer
|
||||
progressUtils.addObserver(settings.taskList, settings.options.progress);
|
||||
const buffers = await downloadAll(dicomWebClient, settings).catch(error => {
|
||||
// Reject promise from compression task on download failure
|
||||
compression.deferred.reject(error);
|
||||
throw error;
|
||||
});
|
||||
compression.deferred.resolve(zipAll(buffers, settings));
|
||||
const url = await compression.deferred.promise;
|
||||
return url;
|
||||
}
|
||||
throw new Error('A valid DICOM Web Client instance is expected');
|
||||
}
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
|
||||
async function zipAll(buffers, settings) {
|
||||
const zip = new JSZip();
|
||||
OHIF.log.info('Adding DICOM P10 files to archive:', buffers.length);
|
||||
buffers.forEach((buffer, i) => {
|
||||
const path = buildPath(buffer) || `${i}.dcm`;
|
||||
zip.file(path, buffer);
|
||||
});
|
||||
// Set compression task progress to 50%
|
||||
progressUtils.update(settings.tasks.compression.task, 0.5);
|
||||
const blob = await zip.generateAsync({ type: 'blob' });
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
function buildSettings(listOfUIDs, options) {
|
||||
const taskList = progressUtils.createList();
|
||||
const compression = progressUtils.addDeferred(taskList);
|
||||
const downloads = [];
|
||||
|
||||
// Build downloads list
|
||||
hierarchicalListUtils.forEach(
|
||||
listOfUIDs,
|
||||
(StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID) => {
|
||||
if (isDicomUid(StudyInstanceUID)) {
|
||||
downloads.push({
|
||||
tracking: progressUtils.addDeferred(taskList),
|
||||
parameters: [StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID],
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Print tree of hierarchical references
|
||||
OHIF.log.info('Downloading DICOM P10 files for references:');
|
||||
OHIF.log.info(hierarchicalListUtils.print(listOfUIDs));
|
||||
|
||||
return {
|
||||
options: Object(options),
|
||||
taskList,
|
||||
tasks: {
|
||||
downloads,
|
||||
compression,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildPath(buffer) {
|
||||
let path;
|
||||
try {
|
||||
const byteArray = new Uint8Array(buffer);
|
||||
const dataSet = dicomParser.parseDicom(byteArray, {
|
||||
// Stop parsing after SeriesInstanceUID is found
|
||||
untilTag: 'x0020000e',
|
||||
});
|
||||
const StudyInstanceUID = dataSet.string('x0020000d');
|
||||
const SeriesInstanceUID = dataSet.string('x0020000e');
|
||||
const SOPInstanceUID = dataSet.string('x00080018');
|
||||
if (StudyInstanceUID && SeriesInstanceUID && SOPInstanceUID) {
|
||||
path = `${StudyInstanceUID}/${SeriesInstanceUID}/${SOPInstanceUID}.dcm`;
|
||||
}
|
||||
} catch (e) {
|
||||
OHIF.log.error('Error parsing downloaded DICOM P10 file...', e);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
async function downloadAll(dicomWebClient, settings) {
|
||||
const { downloads } = settings.tasks;
|
||||
|
||||
// Make sure at least one download was initiated
|
||||
if (downloads.length < 1) {
|
||||
throw new Error('No valid reference to be downloaded');
|
||||
}
|
||||
|
||||
const promises = downloads.map(item => {
|
||||
const {
|
||||
parameters,
|
||||
tracking: { deferred, task },
|
||||
} = item;
|
||||
deferred.resolve(download(task, dicomWebClient, ...parameters));
|
||||
return deferred.promise;
|
||||
});
|
||||
|
||||
// Wait on created download promises
|
||||
return Promise.all(promises).then(results => {
|
||||
const buffers = [];
|
||||
// The "results" array may directly contain buffers (ArrayBuffer instances)
|
||||
// or arrays of buffers, depending on the type of downloads initiated on the
|
||||
// previous step (retrieveStudy, retrieveSeries or retrieveinstance). Ex:
|
||||
// results = [buf1, [buf2, buf3], buf4, [buf5], ...];
|
||||
results.forEach(
|
||||
function select(nesting, result) {
|
||||
if (result instanceof ArrayBuffer) {
|
||||
buffers.push(result);
|
||||
} else if (nesting && Array.isArray(result)) {
|
||||
// "nesting" argument is important to make sure only two levels
|
||||
// of arrays are visited. For example, "bufX" should not be visited:
|
||||
// [buf1, [buf2, buf3, [bufX]], buf4, [buf5], ...];
|
||||
result.forEach(select.bind(null, false));
|
||||
}
|
||||
}.bind(null, true)
|
||||
);
|
||||
return buffers;
|
||||
});
|
||||
}
|
||||
|
||||
async function download(
|
||||
task,
|
||||
dicomWebClient,
|
||||
studyInstanceUID,
|
||||
seriesInstanceUID,
|
||||
sopInstanceUID
|
||||
) {
|
||||
// Strict DICOM-formatted variable names COULDN'T be used here because the
|
||||
// DICOM Web client interface expects them in this specific format.
|
||||
// @TODO: Add support for download progress handler which will use the
|
||||
// currently not use "task" param
|
||||
if (!isDicomUid(studyInstanceUID)) {
|
||||
throw new Error('Download requires at least a "StudyInstanceUID" property');
|
||||
}
|
||||
if (!isDicomUid(seriesInstanceUID)) {
|
||||
// Download entire study
|
||||
return dicomWebClient.retrieveStudy({
|
||||
studyInstanceUID,
|
||||
});
|
||||
}
|
||||
if (!isDicomUid(sopInstanceUID)) {
|
||||
// Download entire series
|
||||
return dicomWebClient.retrieveSeries({
|
||||
studyInstanceUID,
|
||||
seriesInstanceUID,
|
||||
});
|
||||
}
|
||||
// Download specific instance
|
||||
return dicomWebClient.retrieveInstance({
|
||||
studyInstanceUID,
|
||||
seriesInstanceUID,
|
||||
sopInstanceUID,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
export { downloadAndZip as default, downloadAndZip };
|
||||
@@ -0,0 +1,43 @@
|
||||
import { getDicomWebClientFromConfig } from './utils';
|
||||
import { getCommands } from './commandsModule';
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
/**
|
||||
* Globals
|
||||
*/
|
||||
|
||||
const sharedContext = {
|
||||
dicomWebClient: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Extension
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'dicom-p10-downloader',
|
||||
|
||||
/**
|
||||
* LIFECYCLE HOOKS
|
||||
*/
|
||||
|
||||
preRegistration({ appConfig }) {
|
||||
const dicomWebClient = getDicomWebClientFromConfig(appConfig);
|
||||
if (dicomWebClient) {
|
||||
sharedContext.dicomWebClient = dicomWebClient;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* MODULE GETTERS
|
||||
*/
|
||||
|
||||
getCommandsModule() {
|
||||
return getCommands(sharedContext);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,110 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import { api } from 'dicomweb-client';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
const {
|
||||
utils: { isDicomUid, resolveObjectPath, hierarchicalListUtils },
|
||||
DICOMWeb,
|
||||
} = OHIF;
|
||||
|
||||
function validDicomUid(subject) {
|
||||
if (isDicomUid(subject)) {
|
||||
return subject;
|
||||
}
|
||||
}
|
||||
|
||||
function getActiveServerFromServersStore(store) {
|
||||
const servers = resolveObjectPath(store, 'servers');
|
||||
if (Array.isArray(servers) && servers.length > 0) {
|
||||
return servers.find(server => resolveObjectPath(server, 'active') === true);
|
||||
}
|
||||
}
|
||||
|
||||
function getDicomWebClientFromConfig(config) {
|
||||
const servers = resolveObjectPath(config, 'servers.dicomWeb');
|
||||
if (Array.isArray(servers) && servers.length > 0) {
|
||||
const server = servers[0];
|
||||
return new api.DICOMwebClient({
|
||||
url: server.wadoRoot,
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getDicomWebClientFromContext(context, store) {
|
||||
const activeServer = getActiveServerFromServersStore(store);
|
||||
if (activeServer) {
|
||||
return new api.DICOMwebClient({
|
||||
url: activeServer.wadoRoot,
|
||||
headers: DICOMWeb.getAuthorizationHeader(activeServer),
|
||||
});
|
||||
} else if (context.dicomWebClient instanceof api.DICOMwebClient) {
|
||||
return context.dicomWebClient;
|
||||
}
|
||||
}
|
||||
|
||||
function getSOPInstanceReference(viewports, index) {
|
||||
if (index >= 0) {
|
||||
const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = Object(
|
||||
resolveObjectPath(viewports, `viewportSpecificData.${index}`)
|
||||
);
|
||||
return Object.freeze(
|
||||
hierarchicalListUtils.addToList(
|
||||
[],
|
||||
validDicomUid(StudyInstanceUID),
|
||||
validDicomUid(SeriesInstanceUID),
|
||||
validDicomUid(SOPInstanceUID)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getSOPInstanceReferenceFromActiveViewport(viewports) {
|
||||
return getSOPInstanceReference(
|
||||
viewports,
|
||||
resolveObjectPath(viewports, 'activeViewportIndex')
|
||||
);
|
||||
}
|
||||
|
||||
function getSOPInstanceReferencesFromViewports(viewports) {
|
||||
const list = [];
|
||||
const viewportSpecificData = resolveObjectPath(
|
||||
viewports,
|
||||
'viewportSpecificData'
|
||||
);
|
||||
Object.keys(viewportSpecificData).forEach(index => {
|
||||
const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = Object(
|
||||
viewportSpecificData[index]
|
||||
);
|
||||
hierarchicalListUtils.addToList(
|
||||
list,
|
||||
validDicomUid(StudyInstanceUID),
|
||||
validDicomUid(SeriesInstanceUID),
|
||||
validDicomUid(SOPInstanceUID)
|
||||
);
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
function save(promise, listOfUIDs) {
|
||||
return Promise.resolve(promise)
|
||||
.then(url => {
|
||||
OHIF.log.info('Files successfully compressed:', url);
|
||||
const StudyInstanceUID = hierarchicalListUtils.getItem(listOfUIDs, 0);
|
||||
saveAs(url, `${StudyInstanceUID}.zip`);
|
||||
return url;
|
||||
})
|
||||
.catch(error => {
|
||||
OHIF.log.error('Failed to create Zip file...', error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
save,
|
||||
validDicomUid,
|
||||
getDicomWebClientFromConfig,
|
||||
getDicomWebClientFromContext,
|
||||
getSOPInstanceReferenceFromActiveViewport,
|
||||
getSOPInstanceReferencesFromViewports,
|
||||
};
|
||||
@@ -3,6 +3,49 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.4.0...@ohif/extension-vtk@1.4.1) (2020-04-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.4.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.14...@ohif/extension-vtk@1.4.0) (2020-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.13...@ohif/extension-vtk@1.3.14) (2020-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.12...@ohif/extension-vtk@1.3.13) (2020-04-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.11...@ohif/extension-vtk@1.3.12) (2020-04-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.10...@ohif/extension-vtk@1.3.11) (2020-04-09)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "1.3.11",
|
||||
"version": "1.4.1",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -34,7 +34,7 @@
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-tools": "4.12.5",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"cornerstone-wado-image-loader": "^3.1.0",
|
||||
"dcmjs": "^0.10.1",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"i18next": "^17.0.3",
|
||||
@@ -53,10 +53,10 @@
|
||||
"react-vtkjs-viewport": "^0.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^2.6.10",
|
||||
"@ohif/core": "^2.8.1",
|
||||
"@ohif/ui": "^1.4.3",
|
||||
"cornerstone-tools": "4.12.5",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"cornerstone-wado-image-loader": "^3.1.0",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"gh-pages": "^2.0.1",
|
||||
"i18next": "^17.0.3",
|
||||
|
||||
@@ -3,6 +3,58 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.8.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.8.0...@ohif/core@2.8.1) (2020-04-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Multiframe fix ([#1661](https://github.com/OHIF/Viewers/issues/1661)) ([7120561](https://github.com/OHIF/Viewers/commit/71205618ecb8b592247c5acb32284bfe7e18fce5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.8.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.7.1...@ohif/core@2.8.0) (2020-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.7.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.7.0...@ohif/core@2.7.1) (2020-04-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* whiteLabeling should support component creation by passing React to defined fn ([#1659](https://github.com/OHIF/Viewers/issues/1659)) ([2093a00](https://github.com/OHIF/Viewers/commit/2093a0036584b2cc698c8f06fe62b334523b1029))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.7.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.11...@ohif/core@2.7.0) (2020-04-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* set the authorization header for DICOMWeb requests if provided in query string ([#1646](https://github.com/OHIF/Viewers/issues/1646)) ([450c80b](https://github.com/OHIF/Viewers/commit/450c80b9d5f172be8b5713b422370360325a0afc))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.6.11](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.10...@ohif/core@2.6.11) (2020-04-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.6.10](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.9...@ohif/core@2.6.10) (2020-04-09)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/core",
|
||||
"version": "2.6.10",
|
||||
"version": "2.8.1",
|
||||
"description": "Generic business logic for web-based medical imaging applications",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
@@ -33,14 +33,14 @@
|
||||
"peerDependencies": {
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-tools": "4.12.5",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"cornerstone-wado-image-loader": "^3.1.0",
|
||||
"dicom-parser": "^1.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"ajv": "^6.10.0",
|
||||
"dcmjs": "^0.12.0",
|
||||
"dicomweb-client": "^0.5.2",
|
||||
"dicomweb-client": "^0.6.0",
|
||||
"immer": "6.0.2",
|
||||
"isomorphic-base64": "^1.0.2",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
|
||||
@@ -5,6 +5,7 @@ import DICOMWeb from '../DICOMWeb';
|
||||
import parseDicomStructuredReport from './parseDicomStructuredReport';
|
||||
import parseMeasurementsData from './parseMeasurementsData';
|
||||
import getAllDisplaySets from './utils/getAllDisplaySets';
|
||||
import errorHandler from '../errorHandler';
|
||||
|
||||
const VERSION_NAME = 'dcmjs-0.0';
|
||||
const TRANSFER_SYNTAX_UID = '1.2.840.10008.1.2.1';
|
||||
@@ -21,6 +22,7 @@ const retrieveMeasurementFromSR = async (series, studies, serverUrl) => {
|
||||
const config = {
|
||||
url: serverUrl,
|
||||
headers: DICOMWeb.getAuthorizationHeader(),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
@@ -71,6 +73,7 @@ const stowSRFromMeasurements = async (measurements, serverUrl) => {
|
||||
const config = {
|
||||
url: serverUrl,
|
||||
headers: DICOMWeb.getAuthorizationHeader(),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// Commenting this out for now since it looks like Rollup is pulling in the
|
||||
// Node.js version instead of the Browser version of this package
|
||||
//import { btoa } from 'isomorphic-base64';
|
||||
import user from '../user';
|
||||
|
||||
/**
|
||||
@@ -18,6 +15,7 @@ export default function getAuthorizationHeader({ requestOptions } = {}) {
|
||||
// Check for OHIF.user since this can also be run on the server
|
||||
const accessToken = user && user.getAccessToken && user.getAccessToken();
|
||||
|
||||
// Auth for a specific server
|
||||
if (requestOptions && requestOptions.auth) {
|
||||
if (typeof requestOptions.auth === 'function') {
|
||||
// Custom Auth Header
|
||||
@@ -26,7 +24,9 @@ export default function getAuthorizationHeader({ requestOptions } = {}) {
|
||||
// HTTP Basic Auth (user:password)
|
||||
headers.Authorization = `Basic ${btoa(requestOptions.auth)}`;
|
||||
}
|
||||
} else if (accessToken) {
|
||||
}
|
||||
// Auth for the user's default
|
||||
else if (accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ class MetadataProvider {
|
||||
}
|
||||
|
||||
metadata = {
|
||||
overlays
|
||||
overlays,
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
@@ -11,6 +11,7 @@ import { api } from 'dicomweb-client';
|
||||
import { isImage } from '../../utils/isImage';
|
||||
import isDisplaySetReconstructable from '../../utils/isDisplaySetReconstructable';
|
||||
import isLowPriorityModality from '../../utils/isLowPriorityModality';
|
||||
import errorHandler from '../../errorHandler';
|
||||
|
||||
export class StudyMetadata extends Metadata {
|
||||
constructor(data, uid) {
|
||||
@@ -102,10 +103,7 @@ export class StudyMetadata extends Metadata {
|
||||
* @param {SeriesMetadata} series The series metadata object from which the display sets will be created
|
||||
* @returns {Array} The list of display sets created for the given series object
|
||||
*/
|
||||
_createDisplaySetsForSeries(
|
||||
sopClassHandlerModules,
|
||||
series,
|
||||
) {
|
||||
_createDisplaySetsForSeries(sopClassHandlerModules, series) {
|
||||
const study = this;
|
||||
const displaySets = [];
|
||||
|
||||
@@ -293,14 +291,13 @@ export class StudyMetadata extends Metadata {
|
||||
}
|
||||
|
||||
// Loop through the series (SeriesMetadata)
|
||||
this.forEachSeries(
|
||||
series => {
|
||||
const displaySetsForSeries = this._createDisplaySetsForSeries(
|
||||
sopClassHandlerModules,
|
||||
series,
|
||||
);
|
||||
this.forEachSeries(series => {
|
||||
const displaySetsForSeries = this._createDisplaySetsForSeries(
|
||||
sopClassHandlerModules,
|
||||
series
|
||||
);
|
||||
|
||||
displaySets.push(...displaySetsForSeries);
|
||||
displaySets.push(...displaySetsForSeries);
|
||||
});
|
||||
|
||||
return sortDisplaySetList(displaySets);
|
||||
@@ -321,7 +318,10 @@ export class StudyMetadata extends Metadata {
|
||||
return false;
|
||||
}
|
||||
|
||||
const displaySets = this._createDisplaySetsForSeries(sopClassHandlerModules, series)
|
||||
const displaySets = this._createDisplaySetsForSeries(
|
||||
sopClassHandlerModules,
|
||||
series
|
||||
);
|
||||
|
||||
// Note: filtering in place because this._displaySets has writable: false
|
||||
for (let i = this._displaySets.length - 1; i >= 0; i--) {
|
||||
@@ -835,9 +835,11 @@ function _getDisplaySetFromSopClassModule(
|
||||
|
||||
const plugin = handlersForSopClassUID[0];
|
||||
const headers = DICOMWeb.getAuthorizationHeader();
|
||||
const errorInterceptor = errorHandler.getHTTPErrorHandler();
|
||||
const dicomWebClient = new dwc({
|
||||
url: study.getData().wadoRoot,
|
||||
headers,
|
||||
errorInterceptor,
|
||||
});
|
||||
|
||||
let displaySet = plugin.getDisplaySetFromSeries(
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// These should be overridden by the implementation
|
||||
const errorHandler = {
|
||||
getHTTPErrorHandler: () => null,
|
||||
};
|
||||
|
||||
export default errorHandler;
|
||||
@@ -18,6 +18,7 @@ import string from './string.js';
|
||||
import studies from './studies/';
|
||||
import ui from './ui';
|
||||
import user from './user.js';
|
||||
import errorHandler from './errorHandler.js';
|
||||
import utils, { hotkeys } from './utils/';
|
||||
|
||||
import {
|
||||
@@ -46,6 +47,7 @@ const OHIF = {
|
||||
string,
|
||||
ui,
|
||||
user,
|
||||
errorHandler,
|
||||
object,
|
||||
log,
|
||||
DICOMWeb,
|
||||
@@ -79,6 +81,7 @@ export {
|
||||
string,
|
||||
ui,
|
||||
user,
|
||||
errorHandler,
|
||||
object,
|
||||
log,
|
||||
DICOMWeb,
|
||||
|
||||
@@ -24,6 +24,7 @@ describe('Top level exports', () => {
|
||||
'header',
|
||||
'cornerstone',
|
||||
'default', //
|
||||
'errorHandler',
|
||||
'string',
|
||||
'ui',
|
||||
'user',
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import DICOMWeb from '../../../DICOMWeb/';
|
||||
import { api } from 'dicomweb-client';
|
||||
|
||||
import errorHandler from '../../../errorHandler';
|
||||
|
||||
/**
|
||||
* Parses data returned from a QIDO search and transforms it into
|
||||
* an array of series that are present in the study
|
||||
@@ -79,6 +81,7 @@ export default function Instances(server, StudyInstanceUID) {
|
||||
const config = {
|
||||
url: server.qidoRoot,
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
const queryParams = getQIDOQueryParams(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { api } from 'dicomweb-client';
|
||||
import DICOMWeb from '../../../DICOMWeb/';
|
||||
|
||||
import errorHandler from '../../../errorHandler';
|
||||
|
||||
/**
|
||||
* Creates a QIDO date string for a date range query
|
||||
* Assumes the year is positive, at most 4 digits long.
|
||||
@@ -115,6 +117,7 @@ export default function Studies(server, filter) {
|
||||
const config = {
|
||||
url: server.qidoRoot,
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
addInstancesToStudy,
|
||||
} from './studyInstanceHelpers';
|
||||
|
||||
import errorHandler from '../../../errorHandler';
|
||||
|
||||
const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary;
|
||||
|
||||
/**
|
||||
@@ -73,6 +75,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
|
||||
const client = new api.DICOMwebClient({
|
||||
url: server.qidoRoot,
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
@@ -117,7 +120,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
|
||||
|
||||
return {
|
||||
seriesInstanceUIDsMap,
|
||||
seriesData
|
||||
seriesData,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -155,11 +158,14 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
|
||||
SeriesDescription: series.SeriesDescription,
|
||||
SeriesNumber: series.SeriesNumber,
|
||||
Modality: series.Modality,
|
||||
instances: []
|
||||
instances: [],
|
||||
};
|
||||
|
||||
if (study.series[idx]) {
|
||||
study.series[idx] = Object.assign(seriesDataFromQIDO, study.series[idx]);
|
||||
study.series[idx] = Object.assign(
|
||||
seriesDataFromQIDO,
|
||||
study.series[idx]
|
||||
);
|
||||
} else {
|
||||
study.series[idx] = seriesDataFromQIDO;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import DICOMWeb from '../../../DICOMWeb/';
|
||||
import { createStudyFromSOPInstanceList } from './studyInstanceHelpers';
|
||||
import RetrieveMetadataLoader from './retrieveMetadataLoader';
|
||||
|
||||
import errorHandler from '../../../errorHandler';
|
||||
|
||||
/**
|
||||
* Class for sync load of study metadata.
|
||||
* It inherits from RetrieveMetadataLoader
|
||||
@@ -58,6 +60,7 @@ export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
|
||||
const client = new api.DICOMwebClient({
|
||||
url: server.wadoRoot,
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
|
||||
@@ -144,7 +144,7 @@ async function makeSOPInstance(server, study, instance) {
|
||||
wadorsuri,
|
||||
wadoRoot: server.wadoRoot,
|
||||
imageRendering: server.imageRendering,
|
||||
thumbnailRendering: server.thumbnailRendering
|
||||
thumbnailRendering: server.thumbnailRendering,
|
||||
};
|
||||
|
||||
series.instances.push(sopInstance);
|
||||
@@ -160,8 +160,10 @@ async function makeSOPInstance(server, study, instance) {
|
||||
|
||||
const wadoRSMetadata = Object.assign(instance);
|
||||
|
||||
if (sopInstance.NumberOfFrames) {
|
||||
for (let i = 0; i < sopInstance.NumberOfFrames; i++) {
|
||||
const { NumberOfFrames } = sopInstance.metadata;
|
||||
|
||||
if (NumberOfFrames) {
|
||||
for (let i = 0; i < NumberOfFrames; i++) {
|
||||
const wadorsImageId = getWADORSImageId(sopInstance, i);
|
||||
|
||||
cornerstoneWADOImageLoader.wadors.metaDataManager.add(
|
||||
@@ -190,7 +192,7 @@ async function makeSOPInstance(server, study, instance) {
|
||||
*/
|
||||
async function addInstancesToStudy(server, study, sopInstanceList) {
|
||||
return Promise.all(
|
||||
sopInstanceList.map(function (sopInstance) {
|
||||
sopInstanceList.map(function(sopInstance) {
|
||||
return makeSOPInstance(server, study, sopInstance);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
export default class Queue {
|
||||
|
||||
constructor(limit) {
|
||||
this.limit = limit;
|
||||
this.size = 0;
|
||||
this.awaiting = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "proxy" function associated with the current execution queue
|
||||
* instance. When the returned function is invoked, the queue limit is checked
|
||||
* to make sure the limit of scheduled tasks is repected (throwing an
|
||||
* exception when the limit has been reached and before calling the original
|
||||
* function). The original function is only invoked after all the previously
|
||||
* scheduled tasks have finished executing (their returned promises have
|
||||
* resolved/rejected);
|
||||
*
|
||||
* @param {function} task The function whose execution will be associated
|
||||
* with the current Queue instance;
|
||||
* @returns {function} The "proxy" function bound to the current Queue
|
||||
* instance;
|
||||
*/
|
||||
bind(task) {
|
||||
return bind(this, task);
|
||||
}
|
||||
|
||||
bindSafe(task, onError) {
|
||||
const boundTask = bind(this, task);
|
||||
return async function safeTask(...args) {
|
||||
try {
|
||||
return await boundTask(...args);
|
||||
} catch (e) {
|
||||
onError(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
|
||||
function bind(queue, task) {
|
||||
const cleaner = clean.bind(null, queue);
|
||||
return async function boundTask(...args) {
|
||||
if (queue.size >= queue.limit) {
|
||||
throw new Error('Queue limit reached');
|
||||
}
|
||||
const promise = chain(queue.awaiting, task, args);
|
||||
queue.awaiting = promise.then(cleaner, cleaner);
|
||||
queue.size++;
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
||||
function clean(queue) {
|
||||
if (queue.size > 0 && --queue.size === 0) {
|
||||
queue.awaiting = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function chain(prev, task, args) {
|
||||
await prev;
|
||||
return task(...args);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import makeDeferred from './makeDeferred';
|
||||
import Queue from './Queue';
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
|
||||
function timeout(delay) {
|
||||
const { resolve, promise } = makeDeferred();
|
||||
setTimeout(() => void resolve(Date.now()), delay);
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests
|
||||
*/
|
||||
|
||||
describe('Queue', () => {
|
||||
it('should bind functions to the queue', async () => {
|
||||
const queue = new Queue(2);
|
||||
const mockedTimeout = jest.fn(timeout);
|
||||
const timer = queue.bind(mockedTimeout);
|
||||
const start = Date.now();
|
||||
timer(120).then(now => {
|
||||
const elapsed = now - start;
|
||||
expect(elapsed >= 120 && elapsed < 240).toBe(true);
|
||||
});
|
||||
const end = await timer(120);
|
||||
expect(end - start > 240).toBe(true);
|
||||
expect(mockedTimeout).toBeCalledTimes(2);
|
||||
});
|
||||
it('should prevent task execution when queue limit is reached', async () => {
|
||||
const queue = new Queue(1);
|
||||
const mockedTimeout = jest.fn(timeout);
|
||||
const timer = queue.bind(mockedTimeout);
|
||||
const start = Date.now();
|
||||
const promise = timer(120).then(time => time - start);
|
||||
try {
|
||||
await timer(120);
|
||||
} catch (e) {
|
||||
expect(Date.now() - start < 120).toBe(true);
|
||||
expect(e.message).toBe('Queue limit reached');
|
||||
}
|
||||
const elapsed = await promise;
|
||||
expect(elapsed >= 120 && elapsed < 240).toBe(true);
|
||||
expect(mockedTimeout).toBeCalledTimes(1);
|
||||
});
|
||||
it('should safely bind tasks to the queue', async () => {
|
||||
const queue = new Queue(1);
|
||||
const mockedErrorHandler = jest.fn();
|
||||
const mockedTimeout = jest.fn(timeout);
|
||||
const timer = queue.bindSafe(mockedTimeout, mockedErrorHandler);
|
||||
const start = Date.now();
|
||||
const promise = timer(120).then(time => time - start);
|
||||
await timer(120);
|
||||
expect(Date.now() - start < 120).toBe(true);
|
||||
expect(mockedErrorHandler).toBeCalledTimes(1);
|
||||
expect(mockedErrorHandler).nthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({ message: 'Queue limit reached' })
|
||||
);
|
||||
const elapsed = await promise;
|
||||
expect(elapsed >= 120 && elapsed < 240).toBe(true);
|
||||
expect(mockedTimeout).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,8 @@ import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||
import { api } from 'dicomweb-client';
|
||||
import DICOMWeb from '../DICOMWeb';
|
||||
|
||||
import errorHandler from '../errorHandler';
|
||||
|
||||
const getImageId = imageObj => {
|
||||
if (!imageObj) {
|
||||
return;
|
||||
@@ -57,11 +59,13 @@ const wadorsRetriever = (
|
||||
studyInstanceUID,
|
||||
seriesInstanceUID,
|
||||
sopInstanceUID,
|
||||
headers = DICOMWeb.getAuthorizationHeader()
|
||||
headers = DICOMWeb.getAuthorizationHeader(),
|
||||
errorInterceptor = errorHandler.getHTTPErrorHandler()
|
||||
) => {
|
||||
const config = {
|
||||
url,
|
||||
headers,
|
||||
errorInterceptor,
|
||||
};
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const SEPARATOR = '/';
|
||||
|
||||
/**
|
||||
* API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add values to a list hierarchically.
|
||||
* @ For example:
|
||||
* addToList([], 'a', 'b', 'c');
|
||||
* will add the following hierarchy to the list:
|
||||
* a > b > c
|
||||
* resulting in the following array:
|
||||
* [['a', [['b', ['c']]]]]
|
||||
* @param {Array} list The target list;
|
||||
* @param {...string} values The values to be hierarchically added to the list;
|
||||
* @returns {Array} Returns the provided list possibly updated with the given
|
||||
* values or null when a bad list (not an actual array) is provided
|
||||
*/
|
||||
function addToList(list, ...values) {
|
||||
if (Array.isArray(list)) {
|
||||
if (values.length > 0) {
|
||||
addValuesToList(list, values);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the provided hierarchical list executing the callback
|
||||
* once for each leaf-node of the tree. The ancestors of the leaf-node being
|
||||
* visited are passed to the callback function along with the leaf-node in
|
||||
* the exact same order they appear on the tree (from root to leaf);
|
||||
* @ For example, if the hierachy `a > b > c` appears on the tree ("a" being
|
||||
* the root and "c" being the leaf) the callback function will be called as:
|
||||
* callback('a', 'b', 'c');
|
||||
* @param {Array} list The hierarchical list to be iterated
|
||||
* @param {function} callback The callback which will be exected once for
|
||||
* each leaf-node of the hierarchical list;
|
||||
* @returns {Array} Returns the provided list or null for bad arguments;
|
||||
*/
|
||||
function forEach(list, callback) {
|
||||
if (Array.isArray(list)) {
|
||||
if (typeof callback === 'function') {
|
||||
forEachValue(list, callback);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item from the given hierarchical list based on an index (number)
|
||||
* or a path (string).
|
||||
* @ For example:
|
||||
* getItem(list, '1/0/4')
|
||||
* will retrieve the fourth grandchild, from the first child of the second
|
||||
* element of the list;
|
||||
* @param {Array} list The source list;
|
||||
* @param {string|number} indexOrPath The index of the element inside list
|
||||
* (number) or the path to reach the desired element (string). The slash "/"
|
||||
* character is cosidered the path separator;
|
||||
*/
|
||||
function getItem(list, indexOrPath) {
|
||||
if (Array.isArray(list)) {
|
||||
let subpath = null;
|
||||
let index = typeof indexOrPath === 'number' ? indexOrPath : -1;
|
||||
if (typeof indexOrPath === 'string') {
|
||||
const separator = indexOrPath.indexOf(SEPARATOR);
|
||||
if (separator > 0) {
|
||||
index = parseInt(indexOrPath.slice(0, separator), 10);
|
||||
if (separator + 1 < indexOrPath.length) {
|
||||
subpath = indexOrPath.slice(separator + 1, indexOrPath.length);
|
||||
}
|
||||
} else {
|
||||
index = parseInt(indexOrPath, 10);
|
||||
}
|
||||
}
|
||||
if (index >= 0 && index < list.length) {
|
||||
const item = list[index];
|
||||
if (isSublist(item)) {
|
||||
if (subpath !== null) {
|
||||
return getItem(item[1], subpath);
|
||||
}
|
||||
return item[0];
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty-print the provided hierarchical list;
|
||||
* @param {Array} list The source list;
|
||||
* @returns {string} The textual representation of the hierarchical list;
|
||||
*/
|
||||
function print(list) {
|
||||
let text = '';
|
||||
if (Array.isArray(list)) {
|
||||
let prev = [];
|
||||
forEachValue(list, function(...args) {
|
||||
let prevLen = prev.length;
|
||||
for (let i = 0, l = args.length; i < l; ++i) {
|
||||
if (i < prevLen && args[i] === prev[i]) {
|
||||
continue;
|
||||
}
|
||||
text += ' '.repeat(i) + args[i] + '\n';
|
||||
}
|
||||
prev = args;
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
|
||||
function forEachValue(list, callback) {
|
||||
for (let i = 0, l = list.length; i < l; ++i) {
|
||||
let item = list[i];
|
||||
if (isSublist(item)) {
|
||||
if (item[1].length > 0) {
|
||||
forEachValue(item[1], callback.bind(null, item[0]));
|
||||
continue;
|
||||
}
|
||||
item = item[0];
|
||||
}
|
||||
callback(item);
|
||||
}
|
||||
}
|
||||
|
||||
function addValuesToList(list, values) {
|
||||
let value = values.shift();
|
||||
let index = add(list, value);
|
||||
if (index >= 0) {
|
||||
if (values.length > 0) {
|
||||
let sublist = list[index];
|
||||
if (!isSublist(sublist)) {
|
||||
sublist = toSublist(value);
|
||||
list[index] = sublist;
|
||||
}
|
||||
return addValuesToList(sublist[1], values);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function add(list, value) {
|
||||
let index = find(list, value);
|
||||
if (index === -2) {
|
||||
index = list.push(value) - 1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
function find(list, value) {
|
||||
if (typeof value === 'string') {
|
||||
for (let i = 0, l = list.length; i < l; ++i) {
|
||||
let item = list[i];
|
||||
if (item === value || (isSublist(item) && item[0] === value)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function isSublist(subject) {
|
||||
return (
|
||||
Array.isArray(subject) &&
|
||||
subject.length === 2 &&
|
||||
typeof subject[0] === 'string' &&
|
||||
Array.isArray(subject[1])
|
||||
);
|
||||
}
|
||||
|
||||
function toSublist(value) {
|
||||
return [value + '', []];
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
export { addToList, getItem, forEach, print };
|
||||
@@ -0,0 +1,96 @@
|
||||
import { addToList, forEach, getItem, print } from './hierarchicalListUtils';
|
||||
|
||||
describe('hierarchicalListUtils', function() {
|
||||
let sharedList;
|
||||
|
||||
beforeEach(function() {
|
||||
sharedList = [
|
||||
['1.2.3.1', ['1.2.3.1.1', '1.2.3.1.2']],
|
||||
'1.2.3.2',
|
||||
['1.2.3.3', ['1.2.3.3.1', ['1.2.3.3.2', ['1.2.3.3.2.1', '1.2.3.3.2.2']]]],
|
||||
];
|
||||
});
|
||||
|
||||
describe('getItem', function() {
|
||||
it('should retrieve elements from a list by index', function() {
|
||||
expect(getItem(sharedList, 0)).toBe('1.2.3.1');
|
||||
expect(getItem(sharedList, 1)).toBe('1.2.3.2');
|
||||
expect(getItem(sharedList, 2)).toBe('1.2.3.3');
|
||||
expect(getItem(sharedList, 3)).toBeUndefined();
|
||||
});
|
||||
it('should retrieve elements from a list by path', function() {
|
||||
expect(getItem(sharedList, '0')).toBe('1.2.3.1');
|
||||
expect(getItem(sharedList, '0/0')).toBe('1.2.3.1.1');
|
||||
expect(getItem(sharedList, '0/1')).toBe('1.2.3.1.2');
|
||||
expect(getItem(sharedList, '0/2')).toBeUndefined();
|
||||
expect(getItem(sharedList, '1')).toBe('1.2.3.2');
|
||||
expect(getItem(sharedList, '2')).toBe('1.2.3.3');
|
||||
expect(getItem(sharedList, '2/0')).toBe('1.2.3.3.1');
|
||||
expect(getItem(sharedList, '2/1')).toBe('1.2.3.3.2');
|
||||
expect(getItem(sharedList, '2/2')).toBeUndefined();
|
||||
expect(getItem(sharedList, '2/1/0')).toBe('1.2.3.3.2.1');
|
||||
expect(getItem(sharedList, '2/1/1')).toBe('1.2.3.3.2.2');
|
||||
expect(getItem(sharedList, '2/1/2')).toBeUndefined();
|
||||
expect(getItem(sharedList, '3')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('addToList', function() {
|
||||
it('should support adding elements to a list hierarchically', function() {
|
||||
const list = [];
|
||||
addToList(list, '1.2.3.1', '1.2.3.1.1');
|
||||
addToList(list, '1.2.3.1', '1.2.3.1.2');
|
||||
addToList(list, '1.2.3.2');
|
||||
addToList(list, '1.2.3.3', '1.2.3.3.1');
|
||||
addToList(list, '1.2.3.3', '1.2.3.3.2', '1.2.3.3.2.1');
|
||||
addToList(list, '1.2.3.3', '1.2.3.3.2', '1.2.3.3.2.2');
|
||||
expect(list).toStrictEqual(sharedList);
|
||||
});
|
||||
it('should change leaf nodes into non-leaf nodes', function() {
|
||||
const listw = [];
|
||||
const listx = [['x.1', ['x.1.1', 'x.1.2']], 'x.2'];
|
||||
const listy = [
|
||||
['x.1', [['x.1.1', ['x.1.1.1']], 'x.1.2']],
|
||||
['x.2', ['x.2.1']],
|
||||
];
|
||||
addToList(listw, 'x.1');
|
||||
addToList(listw, 'x.1', 'x.1.1');
|
||||
addToList(listw, 'x.1', 'x.1.2');
|
||||
addToList(listw, 'x.2');
|
||||
expect(listw).toStrictEqual(listx);
|
||||
addToList(listw, 'x.2', 'x.2.1');
|
||||
addToList(listw, 'x.1', 'x.1.1', 'x.1.1.1');
|
||||
expect(listw).toStrictEqual(listy);
|
||||
});
|
||||
});
|
||||
|
||||
describe('forEach', function() {
|
||||
it('should iterate through all leaf nodes of the tree', function() {
|
||||
const fn = jest.fn();
|
||||
forEach(sharedList, fn);
|
||||
expect(fn).toHaveBeenCalledTimes(6);
|
||||
expect(fn).nthCalledWith(1, '1.2.3.1', '1.2.3.1.1');
|
||||
expect(fn).nthCalledWith(2, '1.2.3.1', '1.2.3.1.2');
|
||||
expect(fn).nthCalledWith(3, '1.2.3.2');
|
||||
expect(fn).nthCalledWith(4, '1.2.3.3', '1.2.3.3.1');
|
||||
expect(fn).nthCalledWith(5, '1.2.3.3', '1.2.3.3.2', '1.2.3.3.2.1');
|
||||
expect(fn).nthCalledWith(6, '1.2.3.3', '1.2.3.3.2', '1.2.3.3.2.2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('print', function() {
|
||||
it('should pretty-print the hierarchical list', function() {
|
||||
expect(print(sharedList)).toBe(
|
||||
'1.2.3.1\n' +
|
||||
' 1.2.3.1.1\n' +
|
||||
' 1.2.3.1.2\n' +
|
||||
'1.2.3.2\n' +
|
||||
'1.2.3.3\n' +
|
||||
' 1.2.3.3.1\n' +
|
||||
' 1.2.3.3.2\n' +
|
||||
' 1.2.3.3.2.1\n' +
|
||||
' 1.2.3.3.2.2\n'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10,8 +10,14 @@ import DicomLoaderService from './dicomLoaderService.js';
|
||||
import b64toBlob from './b64toBlob.js';
|
||||
import loadAndCacheDerivedDisplaySets from './loadAndCacheDerivedDisplaySets.js';
|
||||
import * as urlUtil from './urlUtil';
|
||||
import makeDeferred from './makeDeferred';
|
||||
import makeCancelable from './makeCancelable';
|
||||
import hotkeys from './hotkeys';
|
||||
import Queue from './Queue';
|
||||
import isDicomUid from './isDicomUid';
|
||||
import resolveObjectPath from './resolveObjectPath';
|
||||
import * as hierarchicalListUtils from './hierarchicalListUtils';
|
||||
import * as progressTrackingUtils from './progressTrackingUtils';
|
||||
|
||||
const utils = {
|
||||
guid,
|
||||
@@ -26,8 +32,14 @@ const utils = {
|
||||
DicomLoaderService,
|
||||
urlUtil,
|
||||
loadAndCacheDerivedDisplaySets,
|
||||
makeDeferred,
|
||||
makeCancelable,
|
||||
hotkeys,
|
||||
Queue,
|
||||
isDicomUid,
|
||||
resolveObjectPath,
|
||||
hierarchicalListUtils,
|
||||
progressTrackingUtils,
|
||||
};
|
||||
|
||||
export {
|
||||
@@ -43,8 +55,14 @@ export {
|
||||
DicomLoaderService,
|
||||
urlUtil,
|
||||
loadAndCacheDerivedDisplaySets,
|
||||
makeDeferred,
|
||||
makeCancelable,
|
||||
hotkeys,
|
||||
Queue,
|
||||
isDicomUid,
|
||||
resolveObjectPath,
|
||||
hierarchicalListUtils,
|
||||
progressTrackingUtils,
|
||||
};
|
||||
|
||||
export default utils;
|
||||
@@ -15,8 +15,14 @@ describe('Top level exports', () => {
|
||||
'loadAndCacheDerivedDisplaySets',
|
||||
'DicomLoaderService',
|
||||
'urlUtil',
|
||||
'makeDeferred',
|
||||
'makeCancelable',
|
||||
'hotkeys',
|
||||
'Queue',
|
||||
'isDicomUid',
|
||||
'resolveObjectPath',
|
||||
'hierarchicalListUtils',
|
||||
'progressTrackingUtils',
|
||||
].sort();
|
||||
|
||||
const exports = Object.keys(utils.default).sort();
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export default function isDicomUid(subject) {
|
||||
const regex = /^\d+(?:\.\d+)*$/;
|
||||
return typeof subject === 'string' && regex.test(subject.trim());
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import isDicomUid from './isDicomUid';
|
||||
|
||||
describe('isDicomUid', function() {
|
||||
it('should return true for valid DICOM UIDs', function() {
|
||||
expect(isDicomUid('1')).toBe(true);
|
||||
expect(isDicomUid('1.2')).toBe(true);
|
||||
expect(isDicomUid('1.2.3')).toBe(true);
|
||||
expect(isDicomUid('1.2.3.4')).toBe(true);
|
||||
});
|
||||
it('should return false for invalid DICOM UIDs', function() {
|
||||
expect(isDicomUid('x')).toBe(false);
|
||||
expect(isDicomUid('1.')).toBe(false);
|
||||
expect(isDicomUid('1. 2')).toBe(false);
|
||||
expect(isDicomUid('1.2.n.4')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export default function makeDeferred() {
|
||||
let reject, resolve, promise = new Promise(function (res, rej) {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
return Object.freeze({ promise, resolve, reject });
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import makeDeferred from './makeDeferred';
|
||||
|
||||
describe('makeDeferred', () => {
|
||||
it('should provide a promise to be resolved externally', () => {
|
||||
const deferred = makeDeferred();
|
||||
setTimeout(() => void deferred.resolve('Yay!'));
|
||||
return deferred.promise.then(result => void expect(result).toBe('Yay!'));
|
||||
});
|
||||
it('should provide a promise to be rejected externally', () => {
|
||||
const deferred = makeDeferred();
|
||||
setTimeout(() => void deferred.reject('Oops...'));
|
||||
return deferred.promise.catch(error => void expect(error).toBe('Oops...'));
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,8 @@ import DICOMWeb from '../../DICOMWeb';
|
||||
import str2ab from '../str2ab';
|
||||
import unpackOverlay from './unpackOverlay';
|
||||
|
||||
import errorHandler from '../../errorHandler';
|
||||
|
||||
export default async function fetchOverlayData(instance, server) {
|
||||
const OverlayDataPromises = [];
|
||||
const OverlayDataTags = [];
|
||||
@@ -21,14 +23,20 @@ export default async function fetchOverlayData(instance, server) {
|
||||
const inlineBinaryData = atob(instance[OverlayDataTag].InlineBinary);
|
||||
const arraybuffer = str2ab(inlineBinaryData);
|
||||
|
||||
instance[OverlayDataTag] = unpackOverlay(arraybuffer)
|
||||
} else if (instance[OverlayDataTag] && instance[OverlayDataTag].BulkDataURI) {
|
||||
instance[OverlayDataTag] = unpackOverlay(arraybuffer);
|
||||
} else if (
|
||||
instance[OverlayDataTag] &&
|
||||
instance[OverlayDataTag].BulkDataURI
|
||||
) {
|
||||
OverlayDataPromises.push(
|
||||
_getOverlayData(instance[OverlayDataTag], server)
|
||||
);
|
||||
OverlayDataTags.push(OverlayDataTag);
|
||||
} else if (instance[OverlayDataTag] && instance[OverlayDataTag] instanceof ArrayBuffer) {
|
||||
instance[OverlayDataTag] = unpackOverlay(instance[OverlayDataTag])
|
||||
} else if (
|
||||
instance[OverlayDataTag] &&
|
||||
instance[OverlayDataTag] instanceof ArrayBuffer
|
||||
) {
|
||||
instance[OverlayDataTag] = unpackOverlay(instance[OverlayDataTag]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +68,7 @@ async function _getOverlayData(tag, server) {
|
||||
const config = {
|
||||
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
const options = {
|
||||
@@ -71,5 +80,3 @@ async function _getOverlayData(tag, server) {
|
||||
.then(result => result[0])
|
||||
.then(unpackOverlay);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import { api } from 'dicomweb-client';
|
||||
import DICOMWeb from '../../DICOMWeb';
|
||||
import str2ab from '../str2ab';
|
||||
|
||||
import errorHandler from '../../errorHandler';
|
||||
|
||||
export default async function fetchPaletteColorLookupTableData(
|
||||
instance,
|
||||
server
|
||||
@@ -146,6 +148,7 @@ function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
|
||||
const config = {
|
||||
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
const options = {
|
||||
@@ -164,6 +167,8 @@ function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
|
||||
resolve(arrayBufferToPaletteColorLUT(arraybuffer));
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(arrayBufferToPaletteColorLUT(paletteColorLookupTableData));
|
||||
return Promise.resolve(
|
||||
arrayBufferToPaletteColorLUT(paletteColorLookupTableData)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
import makeDeferred from './makeDeferred';
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const TYPE = Symbol('Type');
|
||||
const TASK = Symbol('Task');
|
||||
const LIST = Symbol('List');
|
||||
|
||||
/**
|
||||
* Public Methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates an instance of a task list
|
||||
* @returns {Object} A task list object
|
||||
*/
|
||||
function createList() {
|
||||
return objectWithType(LIST, {
|
||||
head: null,
|
||||
named: Object.create(null),
|
||||
observers: [],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given argument is a List instance
|
||||
* @param {any} subject The value to be tested
|
||||
* @returns {boolean} true if a valid List instance is given, false otherwise
|
||||
*/
|
||||
function isList(subject) {
|
||||
return isOfType(LIST, subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of a task
|
||||
* @param {Object} list The List instance related to this task
|
||||
* @param {Object} next The next Task instance to link to
|
||||
* @returns {Object} A task object
|
||||
*/
|
||||
function createTask(list, next) {
|
||||
return objectWithType(TASK, {
|
||||
list: isList(list) ? list : null,
|
||||
next: isTask(next) ? next : null,
|
||||
failed: false,
|
||||
awaiting: null,
|
||||
progress: 0.0,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given argument is a Task instance
|
||||
* @param {any} subject The value to be tested
|
||||
* @returns {boolean} true if a valid Task instance is given, false otherwise
|
||||
*/
|
||||
function isTask(subject) {
|
||||
return isOfType(TASK, subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a new Task to the given List instance and notifies the list observers
|
||||
* @param {Object} list A List instance
|
||||
* @returns {Object} The new Task instance appended to the List or null if the
|
||||
* given List instanc is not valid
|
||||
*/
|
||||
function increaseList(list) {
|
||||
if (isList(list)) {
|
||||
const task = createTask(list, list.head);
|
||||
list.head = task;
|
||||
notify(list, getOverallProgress(list));
|
||||
return task;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the internal progress value of the given Task instance and notifies
|
||||
* the observers of the associated list.
|
||||
* @param {Object} task The Task instance to be updated
|
||||
* @param {number} value A number between 0 (inclusive) and 1 (exclusive)
|
||||
* indicating the progress of the task;
|
||||
* @returns {void} Nothing is returned
|
||||
*/
|
||||
function update(task, value) {
|
||||
if (isTask(task) && isValidProgress(value) && value < 1.0) {
|
||||
if (task.progress !== value) {
|
||||
task.progress = value;
|
||||
if (isList(task.list)) {
|
||||
notify(task.list, getOverallProgress(task.list));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a Task instance as finished (progress = 1.0), freezes it in order to
|
||||
* prevent further modifications and notifies the observers of the associated
|
||||
* list.
|
||||
* @param {Object} task The Task instance to be finalized
|
||||
* @returns {void} Nothing is returned
|
||||
*/
|
||||
function finish(task) {
|
||||
if (isTask(task)) {
|
||||
task.progress = 1.0;
|
||||
task.awaiting = null;
|
||||
Object.freeze(task);
|
||||
if (isList(task.list)) {
|
||||
notify(task.list, getOverallProgress(task.list));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a summarized snapshot of the current status of the given task List
|
||||
* @param {Object} list The List instance to be scanned
|
||||
* @returns {Object} An obeject representing the summarized status of the list
|
||||
*/
|
||||
function getOverallProgress(list) {
|
||||
const status = createStatus();
|
||||
if (isList(list)) {
|
||||
let task = list.head;
|
||||
while (isTask(task)) {
|
||||
status.total++;
|
||||
if (isValidProgress(task.progress)) {
|
||||
status.partial += task.progress;
|
||||
if (task.progress === 1.0 && task.failed) status.failures++;
|
||||
}
|
||||
task = task.next;
|
||||
}
|
||||
}
|
||||
if (status.total > 0) {
|
||||
status.progress = status.partial / status.total;
|
||||
}
|
||||
return Object.freeze(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Task instance to the given list that waits on a given "thenable". When
|
||||
* the thenable resolves the "finish" method is called on the newly created
|
||||
* instance thus notifying the observers of the list.
|
||||
* @param {Object} list The List instance to which the new task will be added
|
||||
* @param {Object|Promise} thenable The thenable to be waited on
|
||||
* @returns {Object} A reference to the newly created Task;
|
||||
*/
|
||||
function waitOn(list, thenable) {
|
||||
const task = increaseList(list);
|
||||
if (isTask(task)) {
|
||||
task.awaiting = Promise.resolve(thenable).then(
|
||||
function() {
|
||||
finish(task);
|
||||
},
|
||||
function() {
|
||||
task.failed = true;
|
||||
finish(task);
|
||||
}
|
||||
);
|
||||
return task;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Task instance to the given list using a deferred (a Promise that can
|
||||
* be externally resolved) notifying the observers of the list.
|
||||
* @param {Object} list The List instance to which the new task will be added
|
||||
* @returns {Object} An object with references to the created deferred and task
|
||||
*/
|
||||
function addDeferred(list) {
|
||||
const deferred = makeDeferred();
|
||||
const task = waitOn(list, deferred.promise);
|
||||
return Object.freeze({
|
||||
deferred,
|
||||
task,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a name to a specific task of the list
|
||||
* @param {Object} list The List instance whose task will be named
|
||||
* @param {Object} task The specified Task instance
|
||||
* @param {string} name The name of the task
|
||||
* @returns {boolean} Returns true on success, false otherwise
|
||||
*/
|
||||
function setTaskName(list, task, name) {
|
||||
if (
|
||||
contains(list, task) &&
|
||||
list.named !== null &&
|
||||
typeof list.named === 'object' &&
|
||||
typeof name === 'string'
|
||||
) {
|
||||
list.named[name] = task;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a task by name
|
||||
* @param {Object} list The List instance whose task will be retrieved
|
||||
* @param {string} name The name of the task to be retrieved
|
||||
* @returns {Object} The Task instance or null if not found
|
||||
*/
|
||||
function getTaskByName(list, name) {
|
||||
if (
|
||||
isList(list) &&
|
||||
list.named !== null &&
|
||||
typeof list.named === 'object' &&
|
||||
typeof name === 'string'
|
||||
) {
|
||||
const task = list.named[name];
|
||||
if (isTask(task)) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an observer (callback function) to a given List instance
|
||||
* @param {Object} list The List instance to which the observer will be appended
|
||||
* @param {Function} observer The observer (function) that will be executed
|
||||
* every time a change happens within the list
|
||||
* @returns {boolean} Returns true on success and false otherewise
|
||||
*/
|
||||
function addObserver(list, observer) {
|
||||
if (
|
||||
isList(list) &&
|
||||
Array.isArray(list.observers) &&
|
||||
typeof observer === 'function'
|
||||
) {
|
||||
list.observers.push(observer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an observer (callback function) from a given List instance
|
||||
* @param {Object} list The instance List from which the observer will removed
|
||||
* @param {Function} observer The observer function to be removed
|
||||
* @returns {boolean} Returns true on success and false otherewise
|
||||
*/
|
||||
function removeObserver(list, observer) {
|
||||
if (
|
||||
isList(list) &&
|
||||
Array.isArray(list.observers) &&
|
||||
list.observers.length > 0
|
||||
) {
|
||||
const index = list.observers.indexOf(observer);
|
||||
if (index >= 0) {
|
||||
list.observers.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
|
||||
function createStatus() {
|
||||
return Object.seal({
|
||||
total: 0,
|
||||
partial: 0.0,
|
||||
progress: 0.0,
|
||||
failures: 0,
|
||||
});
|
||||
}
|
||||
|
||||
function objectWithType(type, object) {
|
||||
return Object.seal(Object.defineProperty(object, TYPE, { value: type }));
|
||||
}
|
||||
|
||||
function isOfType(type, subject) {
|
||||
return (
|
||||
subject !== null && typeof subject === 'object' && subject[TYPE] === type
|
||||
);
|
||||
}
|
||||
|
||||
function isValidProgress(value) {
|
||||
return typeof value === 'number' && value >= 0.0 && value <= 1.0;
|
||||
}
|
||||
|
||||
function contains(list, task) {
|
||||
if (isList(list) && isTask(task)) {
|
||||
let item = list.head;
|
||||
while (isTask(item)) {
|
||||
if (item === task) {
|
||||
return true;
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function notify(list, data) {
|
||||
if (
|
||||
isList(list) &&
|
||||
Array.isArray(list.observers) &&
|
||||
list.observers.length > 0
|
||||
) {
|
||||
list.observers.slice().forEach(function(observer) {
|
||||
if (typeof observer === 'function') {
|
||||
try {
|
||||
observer(data, list);
|
||||
} catch (e) {
|
||||
/* Oops! */
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
export {
|
||||
createList,
|
||||
isList,
|
||||
createTask,
|
||||
isTask,
|
||||
increaseList,
|
||||
update,
|
||||
finish,
|
||||
getOverallProgress,
|
||||
waitOn,
|
||||
addDeferred,
|
||||
setTaskName,
|
||||
getTaskByName,
|
||||
addObserver,
|
||||
removeObserver,
|
||||
};
|
||||
@@ -0,0 +1,175 @@
|
||||
import * as utils from './progressTrackingUtils';
|
||||
|
||||
describe('progressTrackingUtils', () => {
|
||||
describe('Creation of lists of tasks to be tracked', () => {
|
||||
it('should support creation of task lists', () => {
|
||||
expect(utils.createList()).toBeInstanceOf(Object);
|
||||
});
|
||||
it('should support validation of task lists', () => {
|
||||
const list = utils.createList();
|
||||
expect(utils.isList(list)).toBe(true);
|
||||
expect(utils.isList(JSON.parse(JSON.stringify(list)))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Usage of lists of tasks to be tracked', () => {
|
||||
let context;
|
||||
|
||||
// Mock for download
|
||||
function fakeRequest(callback) {
|
||||
return new Promise(resolve => {
|
||||
let progress = 0.0;
|
||||
setTimeout(function step() {
|
||||
if (progress < 1.0) {
|
||||
progress += 1 / 4;
|
||||
callback(progress);
|
||||
setTimeout(step);
|
||||
return;
|
||||
}
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
const list = utils.createList();
|
||||
const observer = jest.fn();
|
||||
utils.addObserver(list, observer);
|
||||
context = { list, observer };
|
||||
});
|
||||
|
||||
it('should call observer twice for each task', () => {
|
||||
const { list, observer } = context;
|
||||
const promises = [
|
||||
Promise.resolve('A'),
|
||||
Promise.resolve('B'),
|
||||
Promise.resolve('C'),
|
||||
];
|
||||
promises.forEach(promise => void utils.waitOn(list, promise));
|
||||
return Promise.all(promises).then(() => {
|
||||
expect(observer).toBeCalledTimes(6);
|
||||
[
|
||||
{
|
||||
failures: 0,
|
||||
partial: 0,
|
||||
progress: 0,
|
||||
total: 1,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 0,
|
||||
progress: 0,
|
||||
total: 2,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 0,
|
||||
progress: 0,
|
||||
total: 3,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 1.0,
|
||||
progress: 1 / 3,
|
||||
total: 3,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 2.0,
|
||||
progress: 2 / 3,
|
||||
total: 3,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 3.0,
|
||||
progress: 1.0,
|
||||
total: 3,
|
||||
},
|
||||
].forEach((item, i) => {
|
||||
const result = expect.objectContaining(item);
|
||||
expect(observer).nthCalledWith(i + 1, result, list);
|
||||
});
|
||||
expect(utils.getOverallProgress(list)).toStrictEqual({
|
||||
failures: 0,
|
||||
partial: 3.0,
|
||||
progress: 1.0,
|
||||
total: 3,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should support tasks with internal progress updates', () => {
|
||||
const { list, observer } = context;
|
||||
const download = utils.addDeferred(list);
|
||||
const processing = download.deferred.promise.then(result => result);
|
||||
const update = jest.fn(p => void utils.update(download.task, p));
|
||||
download.deferred.resolve(fakeRequest(update));
|
||||
utils.waitOn(list, processing);
|
||||
return processing.then(() => {
|
||||
expect(update).toBeCalledTimes(4);
|
||||
[0.25, 0.5, 0.75, 1.0].forEach(
|
||||
(value, i) => void expect(update).nthCalledWith(i + 1, value)
|
||||
);
|
||||
expect(observer).toBeCalledTimes(7);
|
||||
[
|
||||
{
|
||||
failures: 0,
|
||||
partial: 0,
|
||||
progress: 0,
|
||||
total: 1,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 0,
|
||||
progress: 0,
|
||||
total: 2,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 0.25,
|
||||
progress: 0.125,
|
||||
total: 2,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 0.5,
|
||||
progress: 0.25,
|
||||
total: 2,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 0.75,
|
||||
progress: 0.375,
|
||||
total: 2,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 1.0,
|
||||
progress: 0.5,
|
||||
total: 2,
|
||||
},
|
||||
{
|
||||
failures: 0,
|
||||
partial: 2.0,
|
||||
progress: 1.0,
|
||||
total: 2,
|
||||
},
|
||||
].forEach((item, i) => {
|
||||
const result = expect.objectContaining(item);
|
||||
expect(observer).nthCalledWith(i + 1, result, list);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Naming of specific tasks', () => {
|
||||
it('should support naming specific tasks', () => {
|
||||
const list = utils.createList();
|
||||
const tasks = [utils.increaseList(list), utils.increaseList(list)];
|
||||
expect(utils.setTaskName(list, tasks[0], 'firstTask')).toBe(true);
|
||||
expect(utils.setTaskName(list, tasks[1], 'secondTask')).toBe(true);
|
||||
expect(utils.getTaskByName(list, 'secondTask')).toBe(tasks[1]);
|
||||
expect(utils.getTaskByName(list, 'firstTask')).toBe(tasks[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
export default function resolveObjectPath(root, path, defaultValue) {
|
||||
if (root !== null && typeof root === 'object' && typeof path === 'string') {
|
||||
let value,
|
||||
separator = path.indexOf('.');
|
||||
if (separator >= 0) {
|
||||
return resolveObjectPath(
|
||||
root[path.slice(0, separator)],
|
||||
path.slice(separator + 1, path.length),
|
||||
defaultValue
|
||||
);
|
||||
}
|
||||
value = root[path];
|
||||
return value === undefined && defaultValue !== undefined
|
||||
? defaultValue
|
||||
: value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import resolveObjectPath from './resolveObjectPath';
|
||||
|
||||
describe('resolveObjectPath', function() {
|
||||
let config;
|
||||
|
||||
beforeEach(function() {
|
||||
config = {
|
||||
active: {
|
||||
user: {
|
||||
name: {
|
||||
first: 'John',
|
||||
last: 'Doe',
|
||||
},
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
ipv4: '10.0.0.1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('should safely return deeply nested values from an object', function() {
|
||||
expect(resolveObjectPath(config, 'active.user.name.first')).toBe('John');
|
||||
expect(resolveObjectPath(config, 'active.user.name.last')).toBe('Doe');
|
||||
expect(resolveObjectPath(config, 'active.servers.0.ipv4')).toBe('10.0.0.1');
|
||||
});
|
||||
|
||||
it('should silently return undefined when intermediate values are not valid objects', function() {
|
||||
expect(resolveObjectPath(config, 'active.usr.name.first')).toBeUndefined();
|
||||
expect(resolveObjectPath(config, 'active.name.last')).toBeUndefined();
|
||||
expect(resolveObjectPath(config, 'active.servers.7.ipv4')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,77 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.10.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.10.1...@ohif/viewer@3.10.2) (2020-04-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* undefined `errorHandler` in cornerstoneWadoImageLoader configuration ([#1664](https://github.com/OHIF/Viewers/issues/1664)) ([709f147](https://github.com/OHIF/Viewers/commit/709f14708e2b0f912b5ea509114acd87af3149cb))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.10.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.10.0...@ohif/viewer@3.10.1) (2020-04-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.10.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.9.2...@ohif/viewer@3.10.0) (2020-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.9.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.9.1...@ohif/viewer@3.9.2) (2020-04-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* whiteLabeling should support component creation by passing React to defined fn ([#1659](https://github.com/OHIF/Viewers/issues/1659)) ([2093a00](https://github.com/OHIF/Viewers/commit/2093a0036584b2cc698c8f06fe62b334523b1029))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.9.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.9.0...@ohif/viewer@3.9.1) (2020-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* `showStudyList` config ([#1647](https://github.com/OHIF/Viewers/issues/1647)) ([d9fc7bb](https://github.com/OHIF/Viewers/commit/d9fc7bbb0e6d868f507c515f031aaf88a2353e2f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.9.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.21...@ohif/viewer@3.9.0) (2020-04-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* set the authorization header for DICOMWeb requests if provided in query string ([#1646](https://github.com/OHIF/Viewers/issues/1646)) ([450c80b](https://github.com/OHIF/Viewers/commit/450c80b9d5f172be8b5713b422370360325a0afc))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.21](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.20...@ohif/viewer@3.8.21) (2020-04-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.8.20](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.19...@ohif/viewer@3.8.20) (2020-04-09)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/viewer",
|
||||
"version": "3.8.20",
|
||||
"version": "3.10.2",
|
||||
"description": "OHIF Viewer",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
@@ -48,15 +48,16 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@ohif/core": "^2.6.10",
|
||||
"@ohif/extension-cornerstone": "^2.5.2",
|
||||
"@ohif/core": "^2.8.1",
|
||||
"@ohif/extension-cornerstone": "^2.6.1",
|
||||
"@ohif/extension-dicom-html": "^1.1.2",
|
||||
"@ohif/extension-dicom-microscopy": "^0.50.9",
|
||||
"@ohif/extension-dicom-p10-downloader": "^0.1.0",
|
||||
"@ohif/extension-dicom-pdf": "^1.0.3",
|
||||
"@ohif/extension-dicom-rt": "^0.0.2",
|
||||
"@ohif/extension-dicom-segmentation": "^0.1.2",
|
||||
"@ohif/extension-lesion-tracker": "^0.2.0",
|
||||
"@ohif/extension-vtk": "^1.3.11",
|
||||
"@ohif/extension-vtk": "^1.4.1",
|
||||
"@ohif/i18n": "^0.52.8",
|
||||
"@ohif/ui": "^1.4.3",
|
||||
"@tanem/react-nprogress": "^1.1.25",
|
||||
@@ -65,7 +66,7 @@
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "4.12.5",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"cornerstone-wado-image-loader": "^3.1.0",
|
||||
"dcmjs": "^0.12.0",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"dicomweb-client": "^0.4.4",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
window.config = {
|
||||
// default: '/'
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
extensions: [],
|
||||
showStudyList: true,
|
||||
filterQueryParam: false,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
extensions: [],
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
// This is an array, but we'll only use the first entry for now
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
// This is an array, but we'll only use the first entry for now
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
enableGoogleCloudAdapter: true,
|
||||
healthcareApiEndpoint: 'https://healthcare.googleapis.com/v1beta1',
|
||||
servers: {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
window.config = function(props) {
|
||||
var servicesManager = props.servicesManager;
|
||||
|
||||
return {
|
||||
routerBasename: '/',
|
||||
enableGoogleCloudAdapter: true,
|
||||
enableGoogleCloudAdapterUI: false,
|
||||
showStudyList: true,
|
||||
httpErrorHandler: error => {
|
||||
// This is 429 when rejected from the public idc sandbox too often.
|
||||
console.warn(error.status);
|
||||
|
||||
// Could use services manager here to bring up a dialog/modal if needed.
|
||||
console.warn('test, navigate to https://ohif.org/');
|
||||
window.location = 'https://ohif.org/';
|
||||
},
|
||||
healthcareApiEndpoint: 'https://idc-sandbox-002.appspot.com/v1beta1',
|
||||
};
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
window.config = {
|
||||
// default: '/'
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
// default: ''
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
window.config = {
|
||||
routerBasename: '/pwa',
|
||||
whiteLabelling: {},
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
showStudyList: true,
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
|
||||
+15
-10
@@ -31,7 +31,8 @@ import {
|
||||
import i18n from '@ohif/i18n';
|
||||
|
||||
// TODO: This should not be here
|
||||
import './config';
|
||||
//import './config';
|
||||
import { setConfiguration } from './config';
|
||||
|
||||
/** Utils */
|
||||
import {
|
||||
@@ -50,7 +51,7 @@ import { getActiveContexts } from './store/layout/selectors.js';
|
||||
import store from './store';
|
||||
|
||||
/** Contexts */
|
||||
import WhiteLabellingContext from './context/WhiteLabellingContext';
|
||||
import WhiteLabelingContext from './context/WhiteLabelingContext';
|
||||
import UserManagerContext from './context/UserManagerContext';
|
||||
import AppContext from './context/AppContext';
|
||||
|
||||
@@ -77,7 +78,9 @@ class App extends Component {
|
||||
PropTypes.shape({
|
||||
routerBasename: PropTypes.string.isRequired,
|
||||
oidc: PropTypes.array,
|
||||
whiteLabelling: PropTypes.object,
|
||||
whiteLabeling: PropTypes.shape({
|
||||
createLogoComponentFn: PropTypes.func,
|
||||
}),
|
||||
extensions: PropTypes.array,
|
||||
}),
|
||||
]).isRequired,
|
||||
@@ -86,7 +89,7 @@ class App extends Component {
|
||||
|
||||
static defaultProps = {
|
||||
config: {
|
||||
whiteLabelling: {},
|
||||
showStudyList: true,
|
||||
oidc: [],
|
||||
extensions: [],
|
||||
},
|
||||
@@ -102,10 +105,10 @@ class App extends Component {
|
||||
const { config, defaultExtensions } = props;
|
||||
|
||||
const appDefaultConfig = {
|
||||
showStudyList: true,
|
||||
cornerstoneExtensionConfig: {},
|
||||
extensions: [],
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
};
|
||||
|
||||
this._appConfig = {
|
||||
@@ -121,6 +124,8 @@ class App extends Component {
|
||||
oidc,
|
||||
} = this._appConfig;
|
||||
|
||||
setConfiguration(this._appConfig);
|
||||
|
||||
this.initUserManager(oidc);
|
||||
_initServices([
|
||||
UINotificationService,
|
||||
@@ -144,7 +149,7 @@ class App extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { whiteLabelling, routerBasename } = this._appConfig;
|
||||
const { whiteLabeling, routerBasename } = this._appConfig;
|
||||
const {
|
||||
UINotificationService,
|
||||
UIDialogService,
|
||||
@@ -160,7 +165,7 @@ class App extends Component {
|
||||
<OidcProvider store={store} userManager={this._userManager}>
|
||||
<UserManagerContext.Provider value={this._userManager}>
|
||||
<Router basename={routerBasename}>
|
||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||
<WhiteLabelingContext.Provider value={whiteLabeling}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<DialogProvider service={UIDialogService}>
|
||||
<ModalProvider
|
||||
@@ -173,7 +178,7 @@ class App extends Component {
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
</WhiteLabellingContext.Provider>
|
||||
</WhiteLabelingContext.Provider>
|
||||
</Router>
|
||||
</UserManagerContext.Provider>
|
||||
</OidcProvider>
|
||||
@@ -188,7 +193,7 @@ class App extends Component {
|
||||
<Provider store={store}>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Router basename={routerBasename}>
|
||||
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||
<WhiteLabelingContext.Provider value={whiteLabeling}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<DialogProvider service={UIDialogService}>
|
||||
<ModalProvider modal={OHIFModal} service={UIModalService}>
|
||||
@@ -196,7 +201,7 @@ class App extends Component {
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
</WhiteLabellingContext.Provider>
|
||||
</WhiteLabelingContext.Provider>
|
||||
</Router>
|
||||
</I18nextProvider>
|
||||
</Provider>
|
||||
|
||||
@@ -4,8 +4,16 @@
|
||||
height: var(--top-bar-height);
|
||||
}
|
||||
|
||||
.dd-item {
|
||||
width: 100%;
|
||||
.header-left-box {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-brand {
|
||||
text-decoration: none;
|
||||
height: 30px;
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* Home Page */
|
||||
@@ -17,35 +25,11 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-left-box {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-viewerLink {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.header-brand {
|
||||
height: 30px;
|
||||
text-decoration: none;
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.entry-header.header-big .header-brand {
|
||||
height: auto;
|
||||
height: 50px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.header-logo-image {
|
||||
fill: transparent;
|
||||
height: 100%;
|
||||
margin: 0 8px 0 0;
|
||||
width: 30px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.entry-header.header-big .header-logo-image {
|
||||
margin-right: 20px;
|
||||
width: 50px;
|
||||
@@ -57,6 +41,24 @@
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/** OTHER STYLES **/
|
||||
|
||||
.dd-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-viewerLink {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.header-logo-image {
|
||||
fill: transparent;
|
||||
height: 100%;
|
||||
margin: 0 8px 0 0;
|
||||
width: 30px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.header-btn {
|
||||
color: var(--text-secondary-color);
|
||||
cursor: pointer;
|
||||
@@ -130,13 +132,6 @@
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
/* Account for notification bar height */
|
||||
.entry-header.header-big .header-brand {
|
||||
}
|
||||
|
||||
.dd-menu {
|
||||
}
|
||||
|
||||
/* Toggle Notification Bar */
|
||||
.notification-bar {
|
||||
display: block;
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Dropdown, AboutContent, withModal } from '@ohif/ui';
|
||||
|
||||
//
|
||||
import { UserPreferences } from './../UserPreferences';
|
||||
import OHIFLogo from '../OHIFLogo/OHIFLogo.js';
|
||||
import './Header.css';
|
||||
|
||||
// Context
|
||||
import AppContext from './../../context/AppContext';
|
||||
|
||||
function Header(props) {
|
||||
const {
|
||||
t,
|
||||
user,
|
||||
userManager,
|
||||
modal: { show },
|
||||
home,
|
||||
useLargeLogo,
|
||||
linkPath,
|
||||
linkText,
|
||||
location,
|
||||
children,
|
||||
} = props;
|
||||
|
||||
const [options, setOptions] = useState([]);
|
||||
const hasLink = linkText && linkPath;
|
||||
|
||||
useEffect(() => {
|
||||
const optionsValue = [
|
||||
@@ -61,15 +60,12 @@ function Header(props) {
|
||||
setOptions(optionsValue);
|
||||
}, [setOptions, show, t, user, userManager]);
|
||||
|
||||
const { appConfig = {} } = AppContext;
|
||||
const showStudyList =
|
||||
appConfig.showStudyList !== undefined ? appConfig.showStudyList : true;
|
||||
|
||||
// ANTD -- Hamburger, Drawer, Menu
|
||||
return (
|
||||
<>
|
||||
<div className="notification-bar">{t('INVESTIGATIONAL USE ONLY')}</div>
|
||||
<div className={`entry-header ${home ? 'header-big' : ''}`}>
|
||||
<div
|
||||
className={classNames('entry-header', { 'header-big': useLargeLogo })}
|
||||
>
|
||||
<div className="header-left-box">
|
||||
{location && location.studyLink && (
|
||||
<Link
|
||||
@@ -82,15 +78,15 @@ function Header(props) {
|
||||
|
||||
{children}
|
||||
|
||||
{showStudyList && !home && (
|
||||
{hasLink && (
|
||||
<Link
|
||||
className="header-btn header-studyListLinkSection"
|
||||
to={{
|
||||
pathname: '/',
|
||||
pathname: linkPath,
|
||||
state: { studyLink: location.pathname },
|
||||
}}
|
||||
>
|
||||
{t('Study list')}
|
||||
{t(linkText)}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
@@ -105,7 +101,11 @@ function Header(props) {
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
home: PropTypes.bool.isRequired,
|
||||
// Study list, /
|
||||
linkText: PropTypes.string,
|
||||
linkPath: PropTypes.string,
|
||||
useLargeLogo: PropTypes.bool,
|
||||
//
|
||||
location: PropTypes.object.isRequired,
|
||||
children: PropTypes.node,
|
||||
t: PropTypes.func.isRequired,
|
||||
@@ -115,7 +115,7 @@ Header.propTypes = {
|
||||
};
|
||||
|
||||
Header.defaultProps = {
|
||||
home: true,
|
||||
useLargeLogo: false,
|
||||
children: OHIFLogo(),
|
||||
};
|
||||
|
||||
|
||||
@@ -3,43 +3,59 @@ import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||
import dicomParser from 'dicom-parser';
|
||||
import version from './version.js';
|
||||
import AppContext from './context/AppContext';
|
||||
|
||||
let homepage;
|
||||
const { process } = window;
|
||||
if (process && process.env && process.env.PUBLIC_URL) {
|
||||
homepage = process.env.PUBLIC_URL;
|
||||
}
|
||||
|
||||
window.info = {
|
||||
version,
|
||||
homepage,
|
||||
};
|
||||
|
||||
// For debugging
|
||||
//if (process.env.node_env === 'development') {
|
||||
window.cornerstone = cornerstone;
|
||||
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
|
||||
//}
|
||||
|
||||
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
|
||||
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
|
||||
|
||||
OHIF.user.getAccessToken = () => {
|
||||
// TODO: Get the Redux store from somewhere else
|
||||
const state = window.store.getState();
|
||||
if (!state.oidc || !state.oidc.user) {
|
||||
return;
|
||||
export function setConfiguration(appConfig) {
|
||||
let homepage;
|
||||
const { process } = window;
|
||||
if (process && process.env && process.env.PUBLIC_URL) {
|
||||
homepage = process.env.PUBLIC_URL;
|
||||
}
|
||||
|
||||
return state.oidc.user.access_token;
|
||||
};
|
||||
window.info = {
|
||||
version,
|
||||
homepage,
|
||||
};
|
||||
|
||||
cornerstoneWADOImageLoader.configure({
|
||||
beforeSend: function(xhr) {
|
||||
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
|
||||
// For debugging
|
||||
//if (process.env.node_env === 'development') {
|
||||
window.cornerstone = cornerstone;
|
||||
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
|
||||
//}
|
||||
|
||||
if (headers.Authorization) {
|
||||
xhr.setRequestHeader('Authorization', headers.Authorization);
|
||||
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
|
||||
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
|
||||
|
||||
OHIF.user.getAccessToken = () => {
|
||||
// TODO: Get the Redux store from somewhere else
|
||||
const state = window.store.getState();
|
||||
if (!state.oidc || !state.oidc.user) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return state.oidc.user.access_token;
|
||||
};
|
||||
|
||||
OHIF.errorHandler.getHTTPErrorHandler = () => {
|
||||
// const { appConfig = {} } = AppContext;
|
||||
|
||||
return appConfig.httpErrorHandler;
|
||||
};
|
||||
|
||||
cornerstoneWADOImageLoader.configure({
|
||||
beforeSend: function(xhr) {
|
||||
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
|
||||
|
||||
if (headers.Authorization) {
|
||||
xhr.setRequestHeader('Authorization', headers.Authorization);
|
||||
}
|
||||
},
|
||||
errorInterceptor: error => {
|
||||
// const { appConfig = {} } = AppContext;
|
||||
|
||||
if (typeof appConfig.httpErrorHandler === 'function') {
|
||||
appConfig.httpErrorHandler(error);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -14,8 +14,9 @@ import SidePanel from './../components/SidePanel.js';
|
||||
import { extensionManager } from './../App.js';
|
||||
|
||||
// Contexts
|
||||
import WhiteLabellingContext from '../context/WhiteLabellingContext.js';
|
||||
import WhiteLabelingContext from '../context/WhiteLabelingContext.js';
|
||||
import UserManagerContext from '../context/UserManagerContext';
|
||||
import AppContext from '../context/AppContext';
|
||||
|
||||
import './Viewer.css';
|
||||
|
||||
@@ -226,17 +227,33 @@ class Viewer extends Component {
|
||||
return (
|
||||
<>
|
||||
{/* HEADER */}
|
||||
<WhiteLabellingContext.Consumer>
|
||||
{whiteLabelling => (
|
||||
<WhiteLabelingContext.Consumer>
|
||||
{whiteLabeling => (
|
||||
<UserManagerContext.Consumer>
|
||||
{userManager => (
|
||||
<ConnectedHeader home={false} userManager={userManager}>
|
||||
{whiteLabelling.logoComponent}
|
||||
</ConnectedHeader>
|
||||
<AppContext.Consumer>
|
||||
{appContext => (
|
||||
<ConnectedHeader
|
||||
linkText={
|
||||
appContext.appConfig.showStudyList
|
||||
? 'Study List'
|
||||
: undefined
|
||||
}
|
||||
linkPath={
|
||||
appContext.appConfig.showStudyList ? '/' : undefined
|
||||
}
|
||||
userManager={userManager}
|
||||
>
|
||||
{whiteLabeling &&
|
||||
whiteLabeling.createLogoComponentFn &&
|
||||
whiteLabeling.createLogoComponentFn(React)}
|
||||
</ConnectedHeader>
|
||||
)}
|
||||
</AppContext.Consumer>
|
||||
)}
|
||||
</UserManagerContext.Consumer>
|
||||
)}
|
||||
</WhiteLabellingContext.Consumer>
|
||||
</WhiteLabelingContext.Consumer>
|
||||
|
||||
{/* TOOLBAR */}
|
||||
<ConnectedToolbarRow
|
||||
@@ -290,11 +307,11 @@ class Viewer extends Component {
|
||||
activeIndex={this.props.activeViewportIndex}
|
||||
/>
|
||||
) : (
|
||||
<ConnectedStudyBrowser
|
||||
studies={this.state.thumbnails}
|
||||
studyMetadata={this.props.studies}
|
||||
/>
|
||||
)}
|
||||
<ConnectedStudyBrowser
|
||||
studies={this.state.thumbnails}
|
||||
studyMetadata={this.props.studies}
|
||||
/>
|
||||
)}
|
||||
</SidePanel>
|
||||
|
||||
{/* MAIN */}
|
||||
@@ -332,7 +349,7 @@ export default withDialog(Viewer);
|
||||
* @param {Study[]} studies
|
||||
* @param {DisplaySet[]} studies[].displaySets
|
||||
*/
|
||||
const _mapStudiesToThumbnails = function (studies) {
|
||||
const _mapStudiesToThumbnails = function(studies) {
|
||||
return studies.map(study => {
|
||||
const { StudyInstanceUID } = study;
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import OHIFLogo from '../components/OHIFLogo/OHIFLogo.js';
|
||||
import React from 'react';
|
||||
|
||||
const defaultContextValues = {
|
||||
createLogoComponentFn: React => OHIFLogo(),
|
||||
};
|
||||
|
||||
const WhiteLabelingContext = React.createContext(defaultContextValues);
|
||||
|
||||
export default WhiteLabelingContext;
|
||||
@@ -1,10 +0,0 @@
|
||||
import OHIFLogo from '../components/OHIFLogo/OHIFLogo.js';
|
||||
import React from 'react';
|
||||
|
||||
const defaultContextValues = {
|
||||
logoComponent: OHIFLogo(),
|
||||
};
|
||||
|
||||
const WhiteLabellingContext = React.createContext(defaultContextValues);
|
||||
|
||||
export default WhiteLabellingContext;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* hook that builds on useLocation to parse
|
||||
* the query string for you.
|
||||
*
|
||||
* @name useQuery
|
||||
*/
|
||||
export default function() {
|
||||
return new URLSearchParams(useLocation().search);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { httpErrorToStr, checkDicomFile } from '../utils/helpers';
|
||||
import { api } from 'dicomweb-client';
|
||||
import { errorHandler } from '@ohif/core';
|
||||
|
||||
class DicomUploadService {
|
||||
async smartUpload(files, url, uploadCallback, cancellationToken) {
|
||||
@@ -22,8 +23,7 @@ class DicomUploadService {
|
||||
let error = null;
|
||||
try {
|
||||
if (chunk.length > 1) throw new Error('Not implemented');
|
||||
if (chunk.length === 1)
|
||||
await this.simpleUpload(chunk[0], url);
|
||||
if (chunk.length === 1) await this.simpleUpload(chunk[0], url);
|
||||
} catch (err) {
|
||||
// It looks like a stupid bug of Babel that err is not an actual Exception object
|
||||
error = httpErrorToStr(err);
|
||||
@@ -76,11 +76,12 @@ class DicomUploadService {
|
||||
|
||||
getClient(url) {
|
||||
const headers = this.retrieveAuthHeaderFunc();
|
||||
const errorInterceptor = errorHandler.getHTTPErrorHandler();
|
||||
|
||||
// TODO: a bit weird we are creating a new dicomweb client instance for every upload
|
||||
return new api.DICOMwebClient({
|
||||
url,
|
||||
headers
|
||||
headers,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import OHIFDicomSegmentationExtension from '@ohif/extension-dicom-segmentation';
|
||||
import OHIFDicomRtExtension from '@ohif/extension-dicom-rt';
|
||||
import OHIFDicomMicroscopyExtension from '@ohif/extension-dicom-microscopy';
|
||||
import OHIFDicomPDFExtension from '@ohif/extension-dicom-pdf';
|
||||
import OHIFDicomP10DownloaderExtension from '@ohif/extension-dicom-p10-downloader';
|
||||
|
||||
/*
|
||||
* Default Settings
|
||||
@@ -48,6 +49,7 @@ const appProps = {
|
||||
OHIFDicomPDFExtension,
|
||||
OHIFDicomSegmentationExtension,
|
||||
OHIFDicomRtExtension,
|
||||
OHIFDicomP10DownloaderExtension,
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { utils, user } from '@ohif/core';
|
||||
//
|
||||
import ConnectedViewerRetrieveStudyData from '../connectedComponents/ConnectedViewerRetrieveStudyData';
|
||||
import useServer from '../customHooks/useServer';
|
||||
import OHIF from '@ohif/core';
|
||||
const { urlUtil: UrlUtil } = OHIF.utils;
|
||||
import useQuery from '../customHooks/useQuery';
|
||||
const { urlUtil: UrlUtil } = utils;
|
||||
|
||||
/**
|
||||
* Get array of seriesUIDs from param or from queryString
|
||||
@@ -27,8 +29,19 @@ function ViewerRouting({ match: routeMatch, location: routeLocation }) {
|
||||
studyInstanceUIDs,
|
||||
seriesInstanceUIDs,
|
||||
} = routeMatch.params;
|
||||
const server = useServer({ project, location, dataset, dicomStore });
|
||||
|
||||
// Set the user's default authToken for outbound DICOMWeb requests.
|
||||
// Is only applied if target server does not set `requestOptions` property.
|
||||
//
|
||||
// See: `getAuthorizationHeaders.js`
|
||||
let query = useQuery();
|
||||
const authToken = query.get('token');
|
||||
|
||||
if (authToken) {
|
||||
user.getAccessToken = () => authToken;
|
||||
}
|
||||
|
||||
const server = useServer({ project, location, dataset, dicomStore });
|
||||
const studyUIDs = UrlUtil.paramString.parseParam(studyInstanceUIDs);
|
||||
const seriesUIDs = getSeriesInstanceUIDs(seriesInstanceUIDs, routeLocation);
|
||||
|
||||
@@ -55,6 +68,7 @@ ViewerRouting.propTypes = {
|
||||
project: PropTypes.string,
|
||||
}),
|
||||
}),
|
||||
location: PropTypes.any,
|
||||
};
|
||||
|
||||
export default ViewerRouting;
|
||||
@@ -19,7 +19,9 @@ const StudyListRouting = asyncComponent(() =>
|
||||
)
|
||||
);
|
||||
const StandaloneRouting = asyncComponent(() =>
|
||||
import(/* webpackChunkName: "ConnectedStandaloneRouting" */ '../connectedComponents/ConnectedStandaloneRouting.js')
|
||||
import(
|
||||
/* webpackChunkName: "ConnectedStandaloneRouting" */ '../connectedComponents/ConnectedStandaloneRouting.js'
|
||||
)
|
||||
);
|
||||
const ViewerLocalFileData = asyncComponent(() =>
|
||||
import(
|
||||
@@ -43,9 +45,7 @@ const ROUTES_DEF = {
|
||||
path: ['/studylist', '/'],
|
||||
component: StudyListRouting,
|
||||
condition: appConfig => {
|
||||
return appConfig.showStudyList !== undefined
|
||||
? appConfig.showStudyList
|
||||
: true;
|
||||
return appConfig.showStudyList;
|
||||
},
|
||||
},
|
||||
local: {
|
||||
@@ -70,10 +70,7 @@ const ROUTES_DEF = {
|
||||
'/projects/:project/locations/:location/datasets/:dataset/dicomStores/:dicomStore',
|
||||
component: StudyListRouting,
|
||||
condition: appConfig => {
|
||||
const showList =
|
||||
appConfig.showStudyList !== undefined
|
||||
? appConfig.showStudyList
|
||||
: true;
|
||||
const showList = appConfig.showStudyList;
|
||||
|
||||
return showList && !!appConfig.enableGoogleCloudAdapter;
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ import filesToStudies from '../lib/filesToStudies.js';
|
||||
|
||||
// Contexts
|
||||
import UserManagerContext from '../context/UserManagerContext';
|
||||
import WhiteLabellingContext from '../context/WhiteLabellingContext';
|
||||
import WhiteLabelingContext from '../context/WhiteLabelingContext';
|
||||
import AppContext from '../context/AppContext';
|
||||
|
||||
const { urlUtil: UrlUtil } = OHIF.utils;
|
||||
@@ -209,21 +209,23 @@ function StudyListRoute(props) {
|
||||
/>
|
||||
) : null}
|
||||
{healthCareApiWindows}
|
||||
<WhiteLabellingContext.Consumer>
|
||||
{whiteLabelling => (
|
||||
<WhiteLabelingContext.Consumer>
|
||||
{whiteLabeling => (
|
||||
<UserManagerContext.Consumer>
|
||||
{userManager => (
|
||||
<ConnectedHeader
|
||||
home={true}
|
||||
useLargeLogo={true}
|
||||
user={user}
|
||||
userManager={userManager}
|
||||
>
|
||||
{whiteLabelling.logoComponent}
|
||||
{whiteLabeling &&
|
||||
whiteLabeling.createLogoComponentFn &&
|
||||
whiteLabeling.createLogoComponentFn(React)}
|
||||
</ConnectedHeader>
|
||||
)}
|
||||
</UserManagerContext.Consumer>
|
||||
)}
|
||||
</WhiteLabellingContext.Consumer>
|
||||
</WhiteLabelingContext.Consumer>
|
||||
<div className="study-list-header">
|
||||
<div className="header">
|
||||
<h1 style={{ fontWeight: 300, fontSize: '22px' }}>
|
||||
|
||||
@@ -26,14 +26,12 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.0.0"
|
||||
|
||||
"@babel/compat-data@^7.8.6":
|
||||
version "7.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.0.tgz#04815556fc90b0c174abd2c0c1bb966faa036a6c"
|
||||
integrity sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==
|
||||
"@babel/code-frame@^7.8.3":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
|
||||
integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==
|
||||
dependencies:
|
||||
browserslist "^4.9.1"
|
||||
invariant "^2.2.4"
|
||||
semver "^5.5.0"
|
||||
"@babel/highlight" "^7.8.3"
|
||||
|
||||
"@babel/core@7.0.0":
|
||||
version "7.0.0"
|
||||
@@ -155,6 +153,16 @@
|
||||
lodash "^4.17.13"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/generator@^7.9.5":
|
||||
version "7.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9"
|
||||
integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==
|
||||
dependencies:
|
||||
"@babel/types" "^7.9.5"
|
||||
jsesc "^2.5.1"
|
||||
lodash "^4.17.13"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/helper-annotate-as-pure@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32"
|
||||
@@ -194,6 +202,15 @@
|
||||
"@babel/traverse" "^7.4.4"
|
||||
"@babel/types" "^7.4.4"
|
||||
|
||||
"@babel/helper-call-delegate@^7.8.7":
|
||||
version "7.8.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.8.7.tgz#28a279c2e6c622a6233da548127f980751324cab"
|
||||
integrity sha512-doAA5LAKhsFCR0LAFIf+r2RSMmC+m8f/oQ+URnUET/rWeEzC0yTRmAGyWkD4sSu3xwbS7MYQ2u+xlt1V5R56KQ==
|
||||
dependencies:
|
||||
"@babel/helper-hoist-variables" "^7.8.3"
|
||||
"@babel/traverse" "^7.8.3"
|
||||
"@babel/types" "^7.8.7"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.5.5", "@babel/helper-create-class-features-plugin@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.6.0.tgz#769711acca889be371e9bc2eb68641d55218021f"
|
||||
@@ -241,6 +258,15 @@
|
||||
"@babel/template" "^7.1.0"
|
||||
"@babel/types" "^7.0.0"
|
||||
|
||||
"@babel/helper-function-name@^7.9.5":
|
||||
version "7.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c"
|
||||
integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==
|
||||
dependencies:
|
||||
"@babel/helper-get-function-arity" "^7.8.3"
|
||||
"@babel/template" "^7.8.3"
|
||||
"@babel/types" "^7.9.5"
|
||||
|
||||
"@babel/helper-get-function-arity@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
|
||||
@@ -248,6 +274,13 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.0.0"
|
||||
|
||||
"@babel/helper-get-function-arity@^7.8.3":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5"
|
||||
integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==
|
||||
dependencies:
|
||||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/helper-hoist-variables@^7.4.4":
|
||||
version "7.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a"
|
||||
@@ -255,6 +288,13 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.4.4"
|
||||
|
||||
"@babel/helper-hoist-variables@^7.8.3":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134"
|
||||
integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/helper-member-expression-to-functions@^7.0.0", "@babel/helper-member-expression-to-functions@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590"
|
||||
@@ -348,11 +388,23 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.4.4"
|
||||
|
||||
"@babel/helper-split-export-declaration@^7.8.3":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9"
|
||||
integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==
|
||||
dependencies:
|
||||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/helper-validator-identifier@^7.9.0":
|
||||
version "7.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed"
|
||||
integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.9.5":
|
||||
version "7.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80"
|
||||
integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==
|
||||
|
||||
"@babel/helper-wrap-function@^7.1.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa"
|
||||
@@ -381,6 +433,15 @@
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/highlight@^7.8.3":
|
||||
version "7.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
|
||||
integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.9.0"
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.3", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4":
|
||||
version "7.8.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.8.tgz#4c3b7ce36db37e0629be1f0d50a571d2f86f6cd4"
|
||||
@@ -391,6 +452,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81"
|
||||
integrity sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==
|
||||
|
||||
"@babel/parser@^7.8.6", "@babel/parser@^7.9.0":
|
||||
version "7.9.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8"
|
||||
integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==
|
||||
|
||||
"@babel/plugin-proposal-async-generator-functions@^7.0.0", "@babel/plugin-proposal-async-generator-functions@^7.2.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e"
|
||||
@@ -957,14 +1023,13 @@
|
||||
core-js "^2.6.5"
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
|
||||
"@babel/polyfill@^7.8.3":
|
||||
version "7.8.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.8.7.tgz#151ec24c7135481336168c3bd8b8bf0cf91c032f"
|
||||
integrity sha512-LeSfP9bNZH2UOZgcGcZ0PIHUt1ZuHub1L3CVmEyqLxCeDLm4C5Gi8jRH8ZX2PNpDhQCo0z6y/+DIs2JlliXW8w==
|
||||
dependencies:
|
||||
core-js "^2.6.5"
|
||||
regenerator-runtime "^0.13.2"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/preset-env@7.0.0":
|
||||
version "7.0.0"
|
||||
@@ -1182,7 +1247,7 @@
|
||||
pirates "^4.0.0"
|
||||
source-map-support "^0.5.9"
|
||||
|
||||
"@babel/runtime@7.1.2", "@babel/runtime@7.5.5", "@babel/runtime@7.6.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2":
|
||||
"@babel/runtime@7.1.2", "@babel/runtime@7.5.5", "@babel/runtime@7.6.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
|
||||
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
|
||||
@@ -1198,6 +1263,15 @@
|
||||
"@babel/parser" "^7.6.0"
|
||||
"@babel/types" "^7.6.0"
|
||||
|
||||
"@babel/template@^7.8.3":
|
||||
version "7.8.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
|
||||
integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.8.3"
|
||||
"@babel/parser" "^7.8.6"
|
||||
"@babel/types" "^7.8.6"
|
||||
|
||||
"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5", "@babel/traverse@^7.6.0", "@babel/traverse@^7.6.2", "@babel/traverse@^7.6.3":
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.3.tgz#66d7dba146b086703c0fb10dd588b7364cec47f9"
|
||||
@@ -1213,6 +1287,21 @@
|
||||
globals "^11.1.0"
|
||||
lodash "^4.17.13"
|
||||
|
||||
"@babel/traverse@^7.8.3":
|
||||
version "7.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.5.tgz#6e7c56b44e2ac7011a948c21e283ddd9d9db97a2"
|
||||
integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.8.3"
|
||||
"@babel/generator" "^7.9.5"
|
||||
"@babel/helper-function-name" "^7.9.5"
|
||||
"@babel/helper-split-export-declaration" "^7.8.3"
|
||||
"@babel/parser" "^7.9.0"
|
||||
"@babel/types" "^7.9.5"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
lodash "^4.17.13"
|
||||
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5", "@babel/types@^7.6.0", "@babel/types@^7.6.3":
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.3.tgz#3f07d96f854f98e2fbd45c64b0cb942d11e8ba09"
|
||||
@@ -1231,6 +1320,15 @@
|
||||
lodash "^4.17.13"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.8.6", "@babel/types@^7.8.7", "@babel/types@^7.9.5":
|
||||
version "7.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444"
|
||||
integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.9.5"
|
||||
lodash "^4.17.13"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@cnakazawa/watch@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef"
|
||||
@@ -1305,11 +1403,6 @@
|
||||
dependencies:
|
||||
"@emotion/memoize" "0.7.4"
|
||||
|
||||
"@emotion/memoize@0.7.3":
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.3.tgz#5b6b1c11d6a6dddf1f2fc996f74cf3b219644d78"
|
||||
integrity sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow==
|
||||
|
||||
"@emotion/memoize@0.7.4":
|
||||
version "0.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
|
||||
@@ -1430,17 +1523,32 @@
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.2.tgz#1c794cd6dbf2354d1eb1ef10e0303f573e1c7222"
|
||||
integrity sha512-O4QDrx+JoGKZc6aN64L04vqa7e41tIiLU+OvKdcYaEMP97UttL0f9GIi9/0A4WAMx0uBd6SidDIhktZhgOcN8Q==
|
||||
|
||||
"@hapi/address@^2.1.2":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==
|
||||
|
||||
"@hapi/bourne@1.x.x":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a"
|
||||
integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==
|
||||
|
||||
"@hapi/formula@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-1.2.0.tgz#994649c7fea1a90b91a0a1e6d983523f680e10cd"
|
||||
integrity sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA==
|
||||
|
||||
"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0":
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.3.2.tgz#91e7188edebc5d876f0b91a860f555ff06f0782b"
|
||||
integrity sha512-NP5SG4bzix+EtSMtcudp8TvI0lB46mXNo8uFpTDw6tqxGx4z5yx+giIunEFA0Z7oUO4DuWrOJV9xqR2tJVEdyA==
|
||||
|
||||
"@hapi/joi@^15.0.0", "@hapi/joi@^15.0.3", "@hapi/joi@^15.1.0":
|
||||
"@hapi/hoek@^8.2.4":
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06"
|
||||
integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==
|
||||
|
||||
"@hapi/joi@^15.0.0", "@hapi/joi@^15.1.0":
|
||||
version "15.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7"
|
||||
integrity sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==
|
||||
@@ -1450,7 +1558,23 @@
|
||||
"@hapi/hoek" "8.x.x"
|
||||
"@hapi/topo" "3.x.x"
|
||||
|
||||
"@hapi/topo@3.x.x":
|
||||
"@hapi/joi@^16.1.8":
|
||||
version "16.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-16.1.8.tgz#84c1f126269489871ad4e2decc786e0adef06839"
|
||||
integrity sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg==
|
||||
dependencies:
|
||||
"@hapi/address" "^2.1.2"
|
||||
"@hapi/formula" "^1.2.0"
|
||||
"@hapi/hoek" "^8.2.4"
|
||||
"@hapi/pinpoint" "^1.0.2"
|
||||
"@hapi/topo" "^3.1.3"
|
||||
|
||||
"@hapi/pinpoint@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-1.0.2.tgz#025b7a36dbbf4d35bf1acd071c26b20ef41e0d13"
|
||||
integrity sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ==
|
||||
|
||||
"@hapi/topo@3.x.x", "@hapi/topo@^3.1.3":
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29"
|
||||
integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==
|
||||
@@ -3393,7 +3517,7 @@ ajv@6.5.3:
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ajv@^6.0.1, ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.5.5, ajv@^6.9.1:
|
||||
ajv@^6.0.1, ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.9.1:
|
||||
version "6.12.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
|
||||
integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
|
||||
@@ -4481,16 +4605,6 @@ browserslist@^4.0.0, browserslist@^4.1.0, browserslist@^4.6.0, browserslist@^4.6
|
||||
electron-to-chromium "^1.3.295"
|
||||
node-releases "^1.1.38"
|
||||
|
||||
browserslist@^4.9.1:
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.11.0.tgz#aef4357b10a8abda00f97aac7cd587b2082ba1ad"
|
||||
integrity sha512-WqEC7Yr5wUH5sg6ruR++v2SGOQYpyUdYYd4tZoAq1F7y+QXoLoYGXVbxhtaIqWmAJjtNTRjVD3HuJc1OXTel2A==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001035"
|
||||
electron-to-chromium "^1.3.380"
|
||||
node-releases "^1.1.52"
|
||||
pkg-up "^3.1.0"
|
||||
|
||||
bser@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
|
||||
@@ -4824,11 +4938,6 @@ caniuse-lite@^1.0.30001004:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001004.tgz#d879b73981b255488316da946c39327d8c00a586"
|
||||
integrity sha512-3nfOR4O8Wa2RWoYfJkMtwRVOsK96TQ+eq57wd0iKaEWl8dwG4hKZ/g0MVBfCvysFvMLi9fQGR/DvozMdkEPl3g==
|
||||
|
||||
caniuse-lite@^1.0.30001035:
|
||||
version "1.0.30001038"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001038.tgz#44da3cbca2ab6cb6aa83d1be5d324e17f141caff"
|
||||
integrity sha512-zii9quPo96XfOiRD4TrfYGs+QsGZpb2cGiMAzPjtf/hpFgB6zCPZgJb7I1+EATeMw/o+lG8FyRAnI+CWStHcaQ==
|
||||
|
||||
capitalize@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/capitalize/-/capitalize-2.0.0.tgz#61859dd952aba244f03541b23e11470ada097f4b"
|
||||
@@ -5844,10 +5953,10 @@ cornerstone-tools@4.12.5:
|
||||
"@babel/runtime" "7.1.2"
|
||||
cornerstone-math "0.1.7"
|
||||
|
||||
cornerstone-wado-image-loader@^3.0.0:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.0.6.tgz#7b2be91f8f658de3a7474e029aa8314a35725132"
|
||||
integrity sha512-1luWOh1GzkboZ3uxfu4T8vh7K9nlDiiEbQ4dYIY9oobRZVZQbQiLT8wEZPbb4JTUe7UWJISHPCNgtAU8UE9DNw==
|
||||
cornerstone-wado-image-loader@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.1.0.tgz#6007470be896678636da3188f194f5e66ed07858"
|
||||
integrity sha512-n2yadbwK7rCHqJyh5AUmYVPUY5m7boKpheK60sXV41Z0eSZLin9Ja+P9yeAvkg0VGV+/zaBZGPUGY3YyjdWBgg==
|
||||
dependencies:
|
||||
dicom-parser "^1.8.3"
|
||||
pako "^1.0.10"
|
||||
@@ -6749,6 +6858,11 @@ dicomweb-client@^0.5.2:
|
||||
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.5.2.tgz#aa5a4a6a5044b0702bb0a8a2662c86cd16901951"
|
||||
integrity sha512-e11n2+g7HfBuMyopWq76W11eSZJ707g4U1XiO8URA+23aoe0g1kGAtlPB29NKXl7zX7RznTJ7wfRBxFkW6EJDA==
|
||||
|
||||
dicomweb-client@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.6.0.tgz#5e35ada52fe0155af1cc1f0e84f9c7f76477f92d"
|
||||
integrity sha512-VAkBg4W6odIo2XsFxqjN/rptd7bQ8oHpRuKH5d46E9BUIPzRschazE8Dx1xg7/l3N3f1M70jB7yJ339arAXiDQ==
|
||||
|
||||
diff-sequences@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
|
||||
@@ -7301,11 +7415,6 @@ electron-to-chromium@^1.3.295:
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.296.tgz#a1d4322d742317945285d3ba88966561b67f3ac8"
|
||||
integrity sha512-s5hv+TSJSVRsxH190De66YHb50pBGTweT9XGWYu/LMR20KX6TsjFzObo36CjVAzM+PUeeKSBRtm/mISlCzeojQ==
|
||||
|
||||
electron-to-chromium@^1.3.380:
|
||||
version "1.3.386"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.386.tgz#224f97c808da76014096848f80bb9342b6a95cdb"
|
||||
integrity sha512-M7JHfp32Bq6Am59AWgglh2d3nqe6y8Y94Vcb/AXUsO3DGvKUHYI5ML9+U5oNShfdOEfurrrjKSoSgFt2mz7mpw==
|
||||
|
||||
elegant-spinner@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
|
||||
@@ -8149,6 +8258,11 @@ fast-deep-equal@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
|
||||
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
|
||||
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
|
||||
|
||||
fast-diff@^1.1.2:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
|
||||
@@ -8321,6 +8435,11 @@ file-loader@^3.0.1:
|
||||
loader-utils "^1.0.2"
|
||||
schema-utils "^1.0.0"
|
||||
|
||||
file-saver@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.2.tgz#06d6e728a9ea2df2cce2f8d9e84dfcdc338ec17a"
|
||||
integrity sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==
|
||||
|
||||
file-selector@^0.1.11:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.12.tgz#fe726547be219a787a9dcc640575a04a032b1fd0"
|
||||
@@ -9719,6 +9838,11 @@ https-proxy-agent@^2.2.1, https-proxy-agent@^2.2.3:
|
||||
agent-base "^4.3.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
human-signals@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
||||
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
|
||||
|
||||
humanize-ms@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
|
||||
@@ -11308,6 +11432,16 @@ jszip@3.2.2:
|
||||
readable-stream "~2.3.6"
|
||||
set-immediate-shim "~1.0.1"
|
||||
|
||||
jszip@^3.2.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350"
|
||||
integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==
|
||||
dependencies:
|
||||
lie "~3.3.0"
|
||||
pako "~1.0.2"
|
||||
readable-stream "~2.3.6"
|
||||
set-immediate-shim "~1.0.1"
|
||||
|
||||
keygrip@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226"
|
||||
@@ -11584,13 +11718,6 @@ leven@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
|
||||
integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
|
||||
|
||||
levenary@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77"
|
||||
integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==
|
||||
dependencies:
|
||||
leven "^3.1.0"
|
||||
|
||||
levn@^0.3.0, levn@~0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
||||
@@ -13158,7 +13285,7 @@ node-pre-gyp@^0.12.0:
|
||||
semver "^5.3.0"
|
||||
tar "^4"
|
||||
|
||||
node-releases@^1.1.29, node-releases@^1.1.52:
|
||||
node-releases@^1.1.29:
|
||||
version "1.1.52"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9"
|
||||
integrity sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ==
|
||||
@@ -13327,6 +13454,13 @@ npm-run-path@^3.0.0:
|
||||
dependencies:
|
||||
path-key "^3.0.0"
|
||||
|
||||
npm-run-path@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
|
||||
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
|
||||
dependencies:
|
||||
path-key "^3.0.0"
|
||||
|
||||
"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2, npmlog@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
|
||||
@@ -14231,13 +14365,6 @@ pkg-up@2.0.0, pkg-up@^2.0.0:
|
||||
dependencies:
|
||||
find-up "^2.1.0"
|
||||
|
||||
pkg-up@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
||||
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
please-upgrade-node@^3.1.1, please-upgrade-node@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
|
||||
@@ -16263,7 +16390,7 @@ regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||
|
||||
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2:
|
||||
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4:
|
||||
version "0.13.5"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
|
||||
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
|
||||
@@ -16677,6 +16804,13 @@ request-promise-core@1.1.2:
|
||||
dependencies:
|
||||
lodash "^4.17.11"
|
||||
|
||||
request-promise-core@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9"
|
||||
integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==
|
||||
dependencies:
|
||||
lodash "^4.17.15"
|
||||
|
||||
request-promise-native@^1.0.5:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59"
|
||||
@@ -16686,6 +16820,15 @@ request-promise-native@^1.0.5:
|
||||
stealthy-require "^1.1.1"
|
||||
tough-cookie "^2.3.3"
|
||||
|
||||
request-promise-native@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36"
|
||||
integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==
|
||||
dependencies:
|
||||
request-promise-core "1.1.3"
|
||||
stealthy-require "^1.1.1"
|
||||
tough-cookie "^2.3.3"
|
||||
|
||||
request-promise@^4.2.2:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.4.tgz#1c5ed0d71441e38ad58c7ce4ea4ea5b06d54b310"
|
||||
@@ -16961,7 +17104,7 @@ rollup-plugin-terser@^5.1.1:
|
||||
serialize-javascript "^2.1.2"
|
||||
terser "^4.6.2"
|
||||
|
||||
rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.8.1:
|
||||
rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2:
|
||||
version "2.8.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
|
||||
integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
|
||||
@@ -17018,11 +17161,6 @@ rx-lite@*, rx-lite@^4.0.8:
|
||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
|
||||
integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
|
||||
|
||||
rx@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
|
||||
integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=
|
||||
|
||||
rxjs@^5.0.0-beta.11:
|
||||
version "5.5.12"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc"
|
||||
@@ -17037,6 +17175,13 @@ rxjs@^6.3.3, rxjs@^6.4.0:
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
rxjs@^6.5.4:
|
||||
version "6.5.5"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
|
||||
integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
@@ -17232,6 +17377,11 @@ serialize-javascript@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.0.tgz#9310276819efd0eb128258bb341957f6eb2fc570"
|
||||
integrity sha512-a/mxFfU00QT88umAJQsNWOnUKckhNCqOl028N48e7wFmo2/EHpTo9Wso+iJJCMrQnmFvcjto5RJdAHEvVhcyUQ==
|
||||
|
||||
serialize-javascript@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
|
||||
integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==
|
||||
|
||||
serve-handler@6.1.2:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.2.tgz#f05b0421a313fff2d257838cba00cbcc512cd2b6"
|
||||
@@ -18474,7 +18624,7 @@ terser-webpack-plugin@^2.1.0:
|
||||
terser "^4.3.9"
|
||||
webpack-sources "^1.4.3"
|
||||
|
||||
terser@^4.1.0, terser@^4.1.2, terser@^4.3.9:
|
||||
terser@^4.1.2, terser@^4.3.9:
|
||||
version "4.3.9"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-4.3.9.tgz#e4be37f80553d02645668727777687dad26bbca8"
|
||||
integrity sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==
|
||||
@@ -18483,6 +18633,15 @@ terser@^4.1.0, terser@^4.1.2, terser@^4.3.9:
|
||||
source-map "~0.6.1"
|
||||
source-map-support "~0.5.12"
|
||||
|
||||
terser@^4.6.2:
|
||||
version "4.6.11"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.11.tgz#12ff99fdd62a26de2a82f508515407eb6ccd8a9f"
|
||||
integrity sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA==
|
||||
dependencies:
|
||||
commander "^2.20.0"
|
||||
source-map "~0.6.1"
|
||||
source-map-support "~0.5.12"
|
||||
|
||||
test-exclude@^5.2.3:
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0"
|
||||
@@ -19518,16 +19677,17 @@ w3c-hr-time@^1.0.1:
|
||||
dependencies:
|
||||
browser-process-hrtime "^0.1.2"
|
||||
|
||||
wait-on@3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-3.3.0.tgz#9940981d047a72a9544a97b8b5fca45b2170a082"
|
||||
integrity sha512-97dEuUapx4+Y12aknWZn7D25kkjMk16PbWoYzpSdA8bYpVfS6hpl2a2pOWZ3c+Tyt3/i4/pglyZctG3J4V1hWQ==
|
||||
wait-on@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-4.0.0.tgz#4d7e4485ca759968897fd3b0cc50720c0b4ca959"
|
||||
integrity sha512-QrW3J8LzS5ADPfD9Rx5S6KJck66xkqyiFKQs9jmUTkIhiEOmkzU7WRZc+MjsnmkrgjitS2xQ4bb13hnlQnKBUQ==
|
||||
dependencies:
|
||||
"@hapi/joi" "^15.0.3"
|
||||
core-js "^2.6.5"
|
||||
"@hapi/joi" "^16.1.8"
|
||||
lodash "^4.17.15"
|
||||
minimist "^1.2.0"
|
||||
request "^2.88.0"
|
||||
rx "^4.1.0"
|
||||
request-promise-native "^1.0.8"
|
||||
rxjs "^6.5.4"
|
||||
|
||||
walk@^2.3.14:
|
||||
version "2.3.14"
|
||||
|
||||
Reference in new issue
Block a user