Compare commits
117
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9138b554e3 | ||
|
|
89079e9d37 | ||
|
|
84cd2237c5 | ||
|
|
496b0a8343 | ||
|
|
d09fb4e71c | ||
|
|
2cf355856b | ||
|
|
e7e2dcc4b6 | ||
|
|
3f7d29dd76 | ||
|
|
d46cccc652 | ||
|
|
d0c0690e0c | ||
|
|
38ea92d2a3 | ||
|
|
98c40bc607 | ||
|
|
51472b2276 | ||
|
|
63a9b3464d | ||
|
|
20d8f0f5db | ||
|
|
7982cf4fb4 | ||
|
|
df6410b9d0 | ||
|
|
a034bdd478 | ||
|
|
cd4282129b | ||
|
|
a54d023c7f | ||
|
|
6d035c1679 | ||
|
|
1ff9beefcc | ||
|
|
be8ab62ab4 | ||
|
|
52dcce875e | ||
|
|
da725a8923 | ||
|
|
4cae576a90 | ||
|
|
b59c8c277a | ||
|
|
2c5ae43341 | ||
|
|
3ba0e8f919 | ||
|
|
58fa26b5ac | ||
|
|
dabc948f4c | ||
|
|
e7ff3a3ac8 | ||
|
|
764b7a1054 | ||
|
|
2f13453bea | ||
|
|
26da6b0013 | ||
|
|
156b093965 | ||
|
|
54dacbb91b | ||
|
|
6d18e7f1fe | ||
|
|
08ff214ea6 | ||
|
|
cac911f08e | ||
|
|
36952c401a | ||
|
|
b3781ac692 | ||
|
|
2174f4f2fd | ||
|
|
d033bd5995 | ||
|
|
c455253c0e | ||
|
|
ec1946e379 | ||
|
|
ad325fd83f | ||
|
|
a4112c46a5 | ||
|
|
e73d009c21 | ||
|
|
f826341631 | ||
|
|
f8b52d0fee | ||
|
|
0e78836d55 | ||
|
|
40bf1a2efd | ||
|
|
02c732aa15 | ||
|
|
c5f532159b | ||
|
|
b1df019c49 | ||
|
|
54b70e5fa4 | ||
|
|
5802cf18eb | ||
|
|
210c2e8d2c | ||
|
|
da318c9c8c | ||
|
|
903c41d26f | ||
|
|
ba261a0d54 | ||
|
|
3398a894a3 | ||
|
|
5a13194203 | ||
|
|
a2cc477a1a | ||
|
|
2e7a59f7f8 | ||
|
|
83d7d59954 | ||
|
|
163fee8941 | ||
|
|
f547fdf832 | ||
|
|
ac347849d0 | ||
|
|
54e0e32e22 | ||
|
|
690156d4b6 | ||
|
|
3b5fdb678a | ||
|
|
e7604c4418 | ||
|
|
71f11a4856 | ||
|
|
ae5d0587db | ||
|
|
fbc06329e2 | ||
|
|
86732c8186 | ||
|
|
a34d47c850 | ||
|
|
f758741950 | ||
|
|
29a470103a | ||
|
|
104343aad5 | ||
|
|
d8b4a4d4c0 | ||
|
|
2fd60f3109 | ||
|
|
08ce30e69c | ||
|
|
546f6bfe68 | ||
|
|
8e1f15e031 | ||
|
|
0d5d804741 | ||
|
|
ca735fa070 | ||
|
|
3d425f54cc | ||
|
|
7bc9b45853 | ||
|
|
5bd3af0cca | ||
|
|
d4aee36542 | ||
|
|
2a281da19f | ||
|
|
4631218dcb | ||
|
|
8b3e5dffb1 | ||
|
|
ef6819cf40 | ||
|
|
2f104b5e91 | ||
|
|
f256ac5a7d | ||
|
|
3a4ee9e346 | ||
|
|
1a9cfea365 | ||
|
|
9055f19fb5 | ||
|
|
bcbc5e4bbf | ||
|
|
408ba36f4f | ||
|
|
091cab49e0 | ||
|
|
4a09f33446 | ||
|
|
4095629310 | ||
|
|
91d2d31846 | ||
|
|
ff2c1cb2f1 | ||
|
|
727c0d8ba6 | ||
|
|
4b3994e788 | ||
|
|
571dd5848f | ||
|
|
2d48d0aa37 | ||
|
|
562d63fa74 | ||
|
|
1a6246f99b | ||
|
|
ce1a62a961 | ||
|
|
dd6acb2d03 |
No files matched your search
+81
-78
@@ -11,10 +11,13 @@ version: 2.1
|
||||
|
||||
orbs:
|
||||
codecov: codecov/codecov@1.0.5
|
||||
cypress: cypress-io/cypress@1.8.0
|
||||
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: circleci/node:10.16.0
|
||||
environment:
|
||||
TERM: xterm # Enable colors in term
|
||||
working_directory: ~/repo
|
||||
|
||||
jobs:
|
||||
@@ -47,12 +50,12 @@ jobs:
|
||||
|
||||
# RUN TESTS
|
||||
- run:
|
||||
name: "JavaScript Test Suite"
|
||||
name: 'JavaScript Test Suite'
|
||||
command: yarn run test:unit:ci
|
||||
|
||||
# PLATFORM/VIEWER
|
||||
- run:
|
||||
name: "VIEWER: Combine report output"
|
||||
name: 'VIEWER: Combine report output'
|
||||
command: |
|
||||
viewerCov="/home/circleci/repo/platform/viewer/coverage"
|
||||
touch "${viewerCov}/reports"
|
||||
@@ -61,12 +64,12 @@ jobs:
|
||||
cat "${viewerCov}/lcov.info" >>"${viewerCov}/reports"
|
||||
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
||||
- codecov/upload:
|
||||
file: "/home/circleci/repo/platform/viewer/coverage/reports"
|
||||
flags: "viewer"
|
||||
file: '/home/circleci/repo/platform/viewer/coverage/reports'
|
||||
flags: 'viewer'
|
||||
|
||||
# PLATFORM/CORE
|
||||
- run:
|
||||
name: "CORE: Combine report output"
|
||||
name: 'CORE: Combine report output'
|
||||
command: |
|
||||
coreCov="/home/circleci/repo/platform/core/coverage"
|
||||
touch "${coreCov}/reports"
|
||||
@@ -75,8 +78,13 @@ jobs:
|
||||
cat "${coreCov}/lcov.info" >> "${coreCov}/reports"
|
||||
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
|
||||
- codecov/upload:
|
||||
file: "/home/circleci/repo/platform/core/coverage/reports"
|
||||
flags: "core"
|
||||
file: '/home/circleci/repo/platform/core/coverage/reports'
|
||||
flags: 'core'
|
||||
|
||||
# Persist :+1:
|
||||
- persist_to_workspace:
|
||||
root: ~/repo
|
||||
paths: .
|
||||
|
||||
MERGE_UNIT_TESTS:
|
||||
<<: *defaults
|
||||
@@ -107,12 +115,12 @@ jobs:
|
||||
|
||||
# RUN TESTS
|
||||
- run:
|
||||
name: "JavaScript Test Suite"
|
||||
name: 'JavaScript Test Suite'
|
||||
command: yarn run test:unit:ci
|
||||
|
||||
# PLATFORM/VIEWER
|
||||
- run:
|
||||
name: "VIEWER: Combine report output"
|
||||
name: 'VIEWER: Combine report output'
|
||||
command: |
|
||||
viewerCov="/home/circleci/repo/platform/viewer/coverage"
|
||||
touch "${viewerCov}/reports"
|
||||
@@ -121,12 +129,12 @@ jobs:
|
||||
cat "${viewerCov}/lcov.info" >>"${viewerCov}/reports"
|
||||
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
|
||||
- codecov/upload:
|
||||
file: "/home/circleci/repo/platform/viewer/coverage/reports"
|
||||
flags: "viewer"
|
||||
file: '/home/circleci/repo/platform/viewer/coverage/reports'
|
||||
flags: 'viewer'
|
||||
|
||||
# PLATFORM/CORE
|
||||
- run:
|
||||
name: "CORE: Combine report output"
|
||||
name: 'CORE: Combine report output'
|
||||
command: |
|
||||
coreCov="/home/circleci/repo/platform/core/coverage"
|
||||
touch "${coreCov}/reports"
|
||||
@@ -135,37 +143,14 @@ jobs:
|
||||
cat "${coreCov}/lcov.info" >> "${coreCov}/reports"
|
||||
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
|
||||
- codecov/upload:
|
||||
file: "/home/circleci/repo/platform/core/coverage/reports"
|
||||
flags: "core"
|
||||
file: '/home/circleci/repo/platform/core/coverage/reports'
|
||||
flags: 'core'
|
||||
|
||||
# Persist :+1:
|
||||
- persist_to_workspace:
|
||||
root: ~/repo
|
||||
paths: .
|
||||
|
||||
e2e_test:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: cypress/base:8
|
||||
environment:
|
||||
## this enables colors in the output
|
||||
TERM: xterm
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- restore_cache:
|
||||
name: Restore Yarn and Cypress Package Cache
|
||||
keys:
|
||||
# when lock file changes, use increasingly general patterns to restore cache
|
||||
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-v1-{{ .Branch }}-
|
||||
- yarn-packages-v1-
|
||||
# - run: $(yarn bin)/cypress run --record
|
||||
# Shouldn't be needed if we're using a package cache that contains Cypress
|
||||
# Could do a check here, if it doesn't exist, install cypress?
|
||||
- run: yarn install
|
||||
- run: yarn run test:e2e:ci
|
||||
|
||||
npm_publish:
|
||||
<<: *defaults
|
||||
steps:
|
||||
@@ -176,11 +161,15 @@ jobs:
|
||||
command:
|
||||
mkdir ~/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking
|
||||
no\n" > ~/.ssh/config
|
||||
# --no-ci argument is not ideal; however, semantic-rlease thinks we're
|
||||
# attempting to run it from a `pr`, which is not the case
|
||||
- run: git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
|
||||
- run: git config --global user.name "ohif-bot"
|
||||
- run:
|
||||
name: Publish using Semantic Release
|
||||
command: npx semantic-release --debug
|
||||
name: Authenticate with registry
|
||||
command:
|
||||
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
|
||||
- run: npx lerna version
|
||||
- run: npx lerna publish from-package
|
||||
|
||||
# Persist :+1:
|
||||
- persist_to_workspace:
|
||||
root: ~/repo
|
||||
@@ -196,8 +185,8 @@ jobs:
|
||||
command:
|
||||
mkdir ~/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking
|
||||
no\n" > ~/.ssh/config
|
||||
- run: git config --global user.email "gh-pages@localhost"
|
||||
- run: git config --global user.name "npm gh-pages"
|
||||
- run: git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
|
||||
- run: git config --global user.name "ohif-bot"
|
||||
- run: yarn global add gh-pages
|
||||
- run:
|
||||
name: Generate Docs
|
||||
@@ -235,29 +224,14 @@ jobs:
|
||||
docker push ohif/$IMAGE_NAME:latest
|
||||
fi
|
||||
|
||||
build_demo_site:
|
||||
deploy_demo_site:
|
||||
<<: *defaults
|
||||
steps:
|
||||
# Download and cache dependencies
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: Restore Yarn Package Cache
|
||||
keys:
|
||||
# when lock file changes, use increasingly general patterns to restore cache
|
||||
- yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-v1-{{ .Branch }}-
|
||||
- yarn-packages-v1-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
# Build & Test
|
||||
- run:
|
||||
name: "Build Demo Site, Upload SourceMaps, Send Deploy Notification"
|
||||
name: 'Build Demo Site, Upload SourceMaps, Send Deploy Notification'
|
||||
command: |
|
||||
yarn build:demo:ci
|
||||
perl -i -pe 's#</head>#`cat .circleci/rollbar.html` #e' build/index.html
|
||||
@@ -302,7 +276,7 @@ workflows:
|
||||
version: 2
|
||||
|
||||
# PULL REQUESTS
|
||||
pull_requests:
|
||||
PR_CHECKS:
|
||||
jobs:
|
||||
- PR_UNIT_TESTS:
|
||||
filters:
|
||||
@@ -311,33 +285,62 @@ workflows:
|
||||
- master
|
||||
- feature/*
|
||||
- hotfix/*
|
||||
# Cypress job added by `cypress-io` orb
|
||||
- cypress/run:
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v' # Use yarn latest
|
||||
yarn: true
|
||||
store_artifacts: true
|
||||
start: yarn run dev
|
||||
wait-on: 'http://localhost:3000'
|
||||
no-workspace: true # Don't persist workspace
|
||||
working_directory: platform/viewer
|
||||
cache-key: 'yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}'
|
||||
post-steps:
|
||||
- store_test_results:
|
||||
path: cypress/results
|
||||
requires:
|
||||
- PR_UNIT_TESTS
|
||||
|
||||
# MERGE TO MASTER
|
||||
cut_release:
|
||||
RELEASE:
|
||||
jobs:
|
||||
- MERGE_UNIT_TESTS:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- e2e_test:
|
||||
# Cypress job added by `cypress-io` orb
|
||||
- cypress/run:
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v' # Use yarn latest
|
||||
yarn: true
|
||||
record: true
|
||||
store_artifacts: true
|
||||
start: yarn run dev
|
||||
wait-on: 'http://localhost:3000'
|
||||
working_directory: platform/viewer
|
||||
cache-key: 'yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}'
|
||||
post-steps:
|
||||
- store_test_results:
|
||||
path: cypress/results
|
||||
requires:
|
||||
- MERGE_UNIT_TESTS
|
||||
# Update NPM
|
||||
- npm_publish:
|
||||
requires:
|
||||
- e2e_test
|
||||
- cypress/run
|
||||
# Update docs.ohif.org
|
||||
- docs_publish:
|
||||
requires:
|
||||
- e2e_test
|
||||
# - docs_publish:
|
||||
# requires:
|
||||
# - cypress/run
|
||||
# Update hub.docker.org
|
||||
- docker_publish:
|
||||
requires:
|
||||
- npm_publish
|
||||
# - docker_publish:
|
||||
# requires:
|
||||
# - npm_publish
|
||||
# Update viewer.ohif.org
|
||||
- build_demo_site:
|
||||
requires:
|
||||
- e2e_test
|
||||
- demo_site_publish:
|
||||
requires:
|
||||
- build_demo_site
|
||||
# - build_demo_site:
|
||||
# requires:
|
||||
# - cypress/run
|
||||
# - demo_site_publish:
|
||||
# requires:
|
||||
# - build_demo_site
|
||||
File renamed without changes.
@@ -4,7 +4,6 @@ about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'Bug Report :bug:'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Before Creating an issue**
|
||||
@@ -15,7 +14,7 @@ assignees: ''
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
*A clear and concise description of what the bug is.*
|
||||
_A clear and concise description of what the bug is._
|
||||
|
||||
**Steps To Reproduce**
|
||||
|
||||
@@ -29,12 +28,12 @@ Please use code blocks to show formatted errors or code snippets
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
*A clear and concise description of what you expected to happen.*
|
||||
|
||||
_A clear and concise description of what you expected to happen._
|
||||
|
||||
**Environment**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
@@ -4,15 +4,16 @@ about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Awesome, do you have an idea? 😍
|
||||
<!-- ## Awesome, do you have an idea? 😍
|
||||
|
||||
If you have a **feature request, improvement or idea**, check [our official roadmap](https://github.com/OHIF/react-viewerbase/projects) to see if it is already planned!
|
||||
If you have a **feature request, improvement or idea**, check [our official roadmap](https://github.com/OHIF/react-viewerbase/projects) to see if it is already planned! -->
|
||||
|
||||
### 👉 [Go to Roadmap](https://github.com/OHIF/react-viewerbase/projects)
|
||||
<!-- ### 👉 [Go to Roadmap](https://github.com/OHIF/react-viewerbase/projects)
|
||||
|
||||
If your feature request isn't there, continue with this issue and we can discuss it 🤟
|
||||
If your feature request isn't there, continue with this issue and we can discuss
|
||||
it 🤟 -->
|
||||
|
||||
Please include the reasons why you think your change should be made, and any supporting evidence that helps us asses its value and priority.
|
||||
Please include the reasons why you think your change should be made, and any
|
||||
supporting evidence that helps us asses its value and priority.
|
||||
@@ -4,12 +4,15 @@ about: "I have a question \U0001F4AC"
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
We are a small team with limited resources. Your question is much more likely to be answered if it is [a good question](https://stackoverflow.com/help/how-to-ask)
|
||||
We are a small team with limited resources. Your question is much more likely to
|
||||
be answered if it is
|
||||
[a good question](https://stackoverflow.com/help/how-to-ask)
|
||||
|
||||
**Description**
|
||||
|
||||
|
||||
Questions can often be answered by our documentation. Unable to find an answer in our docs? We'll try to help. In the meantime, if you answer your own question, please respond with the answer here so that others may benefit as well. Better yet, open a PR to expand our docs ^_^
|
||||
Questions can often be answered by our documentation. Unable to find an answer
|
||||
in our docs? We'll try to help. In the meantime, if you answer your own
|
||||
question, please respond with the answer here so that others may benefit as
|
||||
well. Better yet, open a PR to expand our docs ^\_^
|
||||
@@ -8,6 +8,7 @@ docs/_book
|
||||
src/version.js
|
||||
junit.xml
|
||||
coverage/
|
||||
.docz/
|
||||
|
||||
# YALC (for Erik)
|
||||
.yalc
|
||||
@@ -28,3 +29,6 @@ docker/dcm4che/dcm4che-arc
|
||||
|
||||
# Cypress test results
|
||||
cypress/videos/
|
||||
|
||||
# Locize settings
|
||||
.locize
|
||||
@@ -3,79 +3,28 @@
|
||||
# Set directory to location of this script
|
||||
# https://stackoverflow.com/a/3355423/1867984
|
||||
cd "$(dirname "$0")"
|
||||
cd .. # Up to project root
|
||||
|
||||
# Helpful to verify which versions we're using
|
||||
yarn -v
|
||||
node -v
|
||||
|
||||
# Install GitBook CLI
|
||||
echo 'Installing Gitbook CLI'
|
||||
yarn global add gitbook-cli
|
||||
npm install -g gitbook-cli
|
||||
|
||||
echo 'Running Gitbook installation'
|
||||
# Generate all version's GitBook output
|
||||
# For each directory in /docs ...
|
||||
cd ./../docs/
|
||||
for D in *; do
|
||||
if [ -d "${D}" ]; then
|
||||
|
||||
echo "Generating output for: ${D}"
|
||||
cd "${D}"
|
||||
|
||||
# Clear previous output, generate new
|
||||
rm -rf _book
|
||||
node /opt/buildhome/.nvm/versions/node/v10.16.0/lib/node_modules/gitbook-cli/bin/gitbook.js install
|
||||
node /opt/buildhome/.nvm/versions/node/v10.16.0/lib/node_modules/gitbook-cli/bin/gitbook.js build
|
||||
cd ..
|
||||
|
||||
fi
|
||||
done
|
||||
|
||||
# Move CNAME File into `latest`
|
||||
cp CNAME ./latest/_book/CNAME
|
||||
|
||||
# Create a history folder in our latest version's output
|
||||
mkdir ./latest/_book/history
|
||||
|
||||
# Move each version's files to latest's history folder
|
||||
for D in *; do
|
||||
# If it's a directory
|
||||
if [ -d "${D}" ]; then
|
||||
# If the directory name starts with `v` (v1, v2, etc.)
|
||||
if [ "${D}" == v* ] ; then
|
||||
|
||||
echo "Moving ${D} to the latest version's history folder"
|
||||
|
||||
mkdir "./latest/_book/history/${D}"
|
||||
cp -v -r "./${D}/_book"/* "./latest/_book/history/${D}"
|
||||
|
||||
fi
|
||||
fi
|
||||
done
|
||||
cd ..
|
||||
|
||||
# Build and copy the PWA Viewer into the demo directory
|
||||
mkdir ./docs/latest/_book/demo/
|
||||
|
||||
# Install build deps and all monorepo package dependencies. Yarn Workspaces
|
||||
# should also symlink all projects appropriately
|
||||
yarn install --no-ignore-optional --pure-lockfile
|
||||
|
||||
# Navigate to our Viewer project
|
||||
cd ./platform/viewer/
|
||||
dir
|
||||
ls
|
||||
|
||||
# Create a Versions File
|
||||
# node -p -e '"export default "' + require(\"./../package.json\").version + '";"' > src/version.js
|
||||
yarn run version
|
||||
# Copy over wado-image-loader codecs and worker file
|
||||
cp ./../../node_modules/cornerstone-wado-image-loader/dist/*.min.js* public -v
|
||||
# Build using react-scripts
|
||||
# npx cross-env PUBLIC_URL=/demo REACT_APP_CONFIG=config/netlify.js react-scripts --max_old_space_size=4096 build
|
||||
# Build using WebPack
|
||||
# TODO: consume public/config correctly instead of hardcode
|
||||
npx webpack --config config/webpack.prod.js --mode production --env.production
|
||||
# Copy output to the folder that is our publish target
|
||||
npx cpx "dist/**/*" ./../../docs/latest/_book/demo --verbose
|
||||
# Build && Move PWA Output
|
||||
yarn run build:ci
|
||||
mkdir -p ./.netlify/www/pwa
|
||||
mv platform/viewer/dist/* .netlify/www/pwa -v
|
||||
|
||||
# Build && Move script output
|
||||
# yarn run build:package
|
||||
|
||||
echo 'Nothing left to see here. Go home, folks.'
|
||||
|
||||
# Build using react-scripts
|
||||
# npx cross-env PUBLIC_URL=/demo APP_CONFIG=config/netlify.js react-scripts --max_old_space_size=4096 build
|
||||
@@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>OHIF Viewer: Deploy Preview</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Index of Previews</h1>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/pwa">Progressive Web App</a>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
+74
-37
@@ -1,53 +1,75 @@
|
||||
const path = require("path");
|
||||
const ExtractCssChunks = require("extract-css-chunks-webpack-plugin");
|
||||
|
||||
const SRC_DIR = path.join(__dirname, "../src");
|
||||
const PUBLIC_DIR = path.join(__dirname, "../public");
|
||||
const DIST_DIR = path.join(__dirname, "../dist");
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
// const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
|
||||
|
||||
module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
return {
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`
|
||||
bundle: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
context: SRC_DIR,
|
||||
module: {
|
||||
rules: [
|
||||
/**
|
||||
* JSX
|
||||
*/
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: [/node_modules/, /packages\\extension/],
|
||||
loader: "babel-loader",
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
// Find babel.config.js in monorepo root
|
||||
// https://babeljs.io/docs/en/options#rootmode
|
||||
rootMode: "upward",
|
||||
rootMode: 'upward',
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
'@babel/preset-env',
|
||||
{
|
||||
// Do not transform ES6 modules to another format.
|
||||
// Webpack will take care of that.
|
||||
modules: false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
modules: false,
|
||||
targets: {
|
||||
ie: '11',
|
||||
},
|
||||
// https://babeljs.io/docs/en/babel-preset-env#usebuiltins
|
||||
useBuiltIns: 'usage',
|
||||
// https://babeljs.io/docs/en/babel-preset-env#corejs
|
||||
corejs: 3,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Stylus to CSS
|
||||
* CSS to CommonJS
|
||||
* Style nodes from JS Strings
|
||||
*/
|
||||
{
|
||||
test: /\.styl$/,
|
||||
use: [
|
||||
{ loader: 'style-loader' },
|
||||
{ loader: 'css-loader' },
|
||||
{ loader: 'stylus-loader' },
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
"style-loader",
|
||||
ExtractCssChunks.loader,
|
||||
{ loader: "css-loader", options: { importLoaders: 1 } },
|
||||
'style-loader',
|
||||
// ExtractCssChunks.loader,
|
||||
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
||||
{
|
||||
loader: "postcss-loader",
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
config: {
|
||||
path: "./postcss.config.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
path: './postcss.config.js',
|
||||
},
|
||||
plugins: () => [autoprefixer('last 2 version', 'ie >= 11')],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
/**
|
||||
* This allows us to include web workers in our bundle, and VTK.js
|
||||
@@ -59,10 +81,10 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
include: /vtk\.js[\/\\]Sources/,
|
||||
use: [
|
||||
{
|
||||
loader: "worker-loader",
|
||||
options: { inline: true, fallback: false }
|
||||
}
|
||||
]
|
||||
loader: 'worker-loader',
|
||||
options: { inline: true, fallback: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
/**
|
||||
* This is exclusively used by `vtk.js` to bundle glsl files.
|
||||
@@ -70,21 +92,36 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
{
|
||||
test: /\.glsl$/i,
|
||||
include: /vtk\.js[\/\\]Sources/,
|
||||
loader: "shader-loader"
|
||||
}
|
||||
]
|
||||
loader: 'shader-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
// Which directories to search when resolving modules
|
||||
modules: [
|
||||
path.resolve(__dirname, "../node_modules"),
|
||||
path.resolve(__dirname, "../../../node_modules"),
|
||||
SRC_DIR
|
||||
// Modules specific to this package
|
||||
path.resolve(__dirname, '../node_modules'),
|
||||
// Hoisted Yarn Workspace Modules
|
||||
path.resolve(__dirname, '../../../node_modules'),
|
||||
SRC_DIR,
|
||||
],
|
||||
// Attempt to resolve these extensions in order.
|
||||
extensions: [".js", ".jsx", ".json", "*"],
|
||||
extensions: ['.js', '.jsx', '.json', '*'],
|
||||
// symlinked resources are resolved to their real path, not their symlinked location
|
||||
symlinks: true
|
||||
}
|
||||
symlinks: true,
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
||||
'process.env.APP_CONFIG': JSON.stringify(process.env.APP_CONFIG || ''),
|
||||
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || ''),
|
||||
}),
|
||||
],
|
||||
// Fix: https://github.com/webpack-contrib/css-loader/issues/447#issuecomment-285598881
|
||||
// For issue in cornerstone-wado-image-loader
|
||||
node: {
|
||||
fs: 'empty',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -124,11 +124,12 @@ These commands are available from the root directory. Each project directory als
|
||||
| -------------------- | ------------------------------------------------------------- |
|
||||
| **Develop** | |
|
||||
| `dev` or `start` | Default development experience for Viewer |
|
||||
| `dev:<package-name>` | Replace with `core`, `ui`, `i18n`, `cornerstone`, `vtk`, etc. |
|
||||
| `dev:project <package-name>` | Replace with `core`, `ui`, `i18n`, `cornerstone`, `vtk`, etc. |
|
||||
| `test:unit` | Jest multi-project test runner; overall coverage |
|
||||
| **Deploy** | |
|
||||
| `build`* | Builds production output for our PWA Viewer |
|
||||
| `build:package`* | Builds commonjs bundles for all projects |
|
||||
| `build:package`* | Builds production `commonjs` output for our Viewer |
|
||||
| `build:package-all`* | Builds commonjs bundles for all projects |
|
||||
|
||||
|
||||
\* - For more information on our different builds, check out our [Deploy Docs][deployment-docs]
|
||||
|
||||
+31
-24
@@ -1,29 +1,36 @@
|
||||
const aliases = require("./aliases.config");
|
||||
const path = require("path");
|
||||
const aliases = require('./aliases.config');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
// https://babeljs.io/docs/en/options#babelrcroots
|
||||
// Also specified in .webpack/webpack.common.js
|
||||
// https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
ie: "11"
|
||||
}
|
||||
}
|
||||
ie: '11',
|
||||
},
|
||||
// https://babeljs.io/docs/en/babel-preset-env#usebuiltins
|
||||
useBuiltIns: 'usage',
|
||||
// https://babeljs.io/docs/en/babel-preset-env#corejs
|
||||
corejs: 3,
|
||||
},
|
||||
],
|
||||
"@babel/preset-react"
|
||||
'@babel/preset-react',
|
||||
],
|
||||
babelrcRoots: ["./platform/*", "./extensions/*"],
|
||||
// https://babeljs.io/docs/en/options#babelrcroots
|
||||
babelrcRoots: ['./platform/*', './extensions/*'],
|
||||
plugins: [
|
||||
"inline-react-svg",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-transform-regenerator",
|
||||
"@babel/plugin-transform-runtime",
|
||||
'react-hot-loader/babel',
|
||||
'inline-react-svg',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-regenerator',
|
||||
'@babel/plugin-transform-runtime',
|
||||
[
|
||||
"module-resolver",
|
||||
'module-resolver',
|
||||
{
|
||||
// https://github.com/tleunen/babel-plugin-module-resolver/issues/338
|
||||
// There seem to be a bug with module-resolver with a mono-repo setup:
|
||||
@@ -33,18 +40,18 @@ module.exports = {
|
||||
// This will return undefined if aliases has no key for the sourcePath,
|
||||
// in which case module-resolver will fallback on its default behaviour.
|
||||
return aliases[sourcePath];
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
env: {
|
||||
debug: {
|
||||
sourceMaps: "inline",
|
||||
retainLines: true
|
||||
sourceMaps: 'inline',
|
||||
retainLines: true,
|
||||
},
|
||||
build: {
|
||||
ignore: ["**/*.test.jsx", "**/*.test.js", "__snapshots__", "__tests__"]
|
||||
}
|
||||
}
|
||||
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
|
||||
},
|
||||
},
|
||||
// ignore: ["node_modules"]
|
||||
};
|
||||
@@ -10,7 +10,7 @@
|
||||
<!-- CORNERSTONE.js -->
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.npmjs.com/package/ohif-cornerstone-extension">
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-cornerstone">
|
||||
Cornerstone
|
||||
</a>
|
||||
</td>
|
||||
@@ -22,7 +22,7 @@
|
||||
<!-- VTK.js -->
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.npmjs.com/package/ohif-vtk-extension">
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-vtk">
|
||||
VTK.js
|
||||
</a>
|
||||
</td>
|
||||
@@ -33,30 +33,30 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="">HTML</a>
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-html">DICOM HTML</a>
|
||||
</td>
|
||||
<td>
|
||||
Renders text and HTML content for <a href="https://github.com/OHIF/Viewers/blob/react/extensions/ohif-dicom-html-extension/src/OHIFDicomHtmlSopClassHandler.js#L7-L15">specific SopClassUIDs</a>.
|
||||
Renders text and HTML content for <a href="https://github.com/OHIF/Viewers/blob/master/extensions/dicom-html/src/OHIFDicomHtmlSopClassHandler.js#L4-L12">specific SopClassUIDs</a>.
|
||||
</td>
|
||||
<td>Viewport, SopClassHandler</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.npmjs.com/package/ohif-dicom-pdf-extension">PDF</a>
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-pdf">DICOM PDF</a>
|
||||
</td>
|
||||
<td>
|
||||
Renders PDFs for a <a href="https://github.com/OHIF/Viewers/blob/react/extensions/ohif-dicom-pdf-extension/src/OHIFDicomPDFSopClassHandler.js#L8">specific SopClassUID</a>.
|
||||
Renders PDFs for a <a href="https://github.com/OHIF/Viewers/blob/master/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js#L4-L6">specific SopClassUID</a>.
|
||||
</td>
|
||||
<td>Viewport, SopClassHandler</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.npmjs.com/package/ohif-dicom-microscopy-extension">Microscopy</a>
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-microscopy">DICOM Microscopy</a>
|
||||
</td>
|
||||
<td>
|
||||
Renders Microscopy images for a <a href="https://github.com/OHIF/Viewers/blob/react/extensions/ohif-dicom-microscopy-extension/src/DicomMicroscopySopClassHandler.js#L6">specific SopClassUID</a>.
|
||||
Renders Microscopy images for a <a href="https://github.com/OHIF/Viewers/blob/master/extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js#L5-L7">specific SopClassUID</a>.
|
||||
</td>
|
||||
<td>Viewport, SopClassHandler</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
@@ -152,7 +152,7 @@ is used depends on:
|
||||
<center><i>An example of three Viewports</i></center>
|
||||
|
||||
For a complete example implementation,
|
||||
[check out the OHIFCornerstoneViewport](https://github.com/OHIF/Viewers/blob/react/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js).
|
||||
[check out the OHIFCornerstoneViewport](https://github.com/OHIF/Viewers/blob/master/extensions/cornerstone/src/OHIFCornerstoneViewport.js).
|
||||
|
||||
#### Toolbar
|
||||
|
||||
@@ -168,7 +168,7 @@ store.
|
||||
Toolbar components are rendered in the `ToolbarRow` component.
|
||||
|
||||
For a complete example implementation,
|
||||
[check out the OHIFCornerstoneViewport's Toolbar Module](https://github.com/OHIF/Viewers/blob/react/extensions/ohif-cornerstone-extension/src/ToolbarModule.js).
|
||||
[check out the OHIFCornerstoneViewport's Toolbar Module](https://github.com/OHIF/Viewers/blob/master/extensions/cornerstone/src/toolbarModule.js).
|
||||
|
||||
#### SopClassHandler
|
||||
|
||||
@@ -197,9 +197,9 @@ would like included at startup.
|
||||
_app.js_
|
||||
|
||||
```js
|
||||
import { createStore, combineReducers } from "redux";
|
||||
import OHIF from "@ohif/core";
|
||||
import OHIFCornerstoneExtension from "ohif-cornerstone-extension";
|
||||
import { createStore, combineReducers } from 'redux';
|
||||
import OHIF from '@ohif/core';
|
||||
import OHIFCornerstoneExtension from 'ohif-cornerstone-extension';
|
||||
|
||||
const combined = combineReducers(OHIF.redux.reducers);
|
||||
const store = createStore(combined);
|
||||
@@ -214,8 +214,8 @@ ExtensionManager.registerExtensions(store, extensions);
|
||||
|
||||
A small number of powerful extensions for popular use cases are maintained by
|
||||
OHIF. They're co-located in the
|
||||
[`OHIF/Viewers`](https://github.com/OHIF/Viewers/tree/react/) repository, in the
|
||||
top level [`extensions/`](https://github.com/OHIF/Viewers/tree/react/extensions)
|
||||
[`OHIF/Viewers`](https://github.com/OHIF/Viewers) repository, in the top level
|
||||
[`extensions/`](https://github.com/OHIF/Viewers/tree/master/extensions)
|
||||
directory.
|
||||
|
||||
{% include "./_maintained-extensions-table.md" %}
|
||||
@@ -225,6 +225,6 @@ directory.
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[example-ext-src]: https://github.com/OHIF/Viewers/blob/master/extensions/_ohif-example-extension/src/index.js)
|
||||
[module-types]: https://github.com/OHIF/ohif-core/blob/43c08a29eff3fb646a0e83a03a236ddd84f4a6e8/src/plugins.js#L1-L6
|
||||
[example-ext-src]: https://github.com/OHIF/Viewers/tree/master/extensions/_example/src
|
||||
[module-types]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/extensions/MODULE_TYPES.js
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -1,11 +1,5 @@
|
||||
# Google Cloud Healthcare
|
||||
|
||||
> ATTENTION: The original documentation for this integration lives in the legacy
|
||||
> `version 1` Meteor documentation. You can
|
||||
> [find it here](/history/v1/connecting-to-image-archives/google-cloud-healthcare.html).
|
||||
> These docs will mirror the Meteor documentation until our `React`
|
||||
> implementation has been updated to work with Google Cloud Healthcare.
|
||||
|
||||
> The [Google Cloud Healthcare API](https://cloud.google.com/healthcare/) is a
|
||||
> powerful option for storing medical imaging data in the cloud.
|
||||
|
||||
@@ -16,10 +10,7 @@ store their data in the cloud. It offers an
|
||||
[almost-entirely complete DICOMWeb API](https://cloud.google.com/healthcare/docs/dicom)
|
||||
which requires tokens generated via the
|
||||
[OAuth 2.0 Sign In flow](https://developers.google.com/identity/sign-in/web/sign-in).
|
||||
Images can even be transcoded on the fly if this is desired. The Cloud
|
||||
Healthcare API is a very attractive option because it allows us to avoid
|
||||
deploying the Meteor server entirely. We can just deploy OHIF as a client-only
|
||||
static site application.
|
||||
Images can even be transcoded on the fly if this is desired.
|
||||
|
||||
## Setup a Google Cloud Healthcare Project
|
||||
|
||||
@@ -44,10 +35,10 @@ static site application.
|
||||
- Choose the "Web Application" type
|
||||
- Set up an
|
||||
[OAuth 2.0 Client ID](https://support.google.com/cloud/answer/6158849?hl=en)
|
||||
- Add your domain (e.g. `http://localhost:3000`) to Authorized JavaScript
|
||||
- Add your domain (e.g. `http://localhost:5000`) to Authorized JavaScript
|
||||
origins.
|
||||
- Add your domain, plus `_oauth/google` (e.g.
|
||||
`http://localhost:3000/_oauth/google`) to Authorized Redirect URIs.
|
||||
- Add your domain, plus `callback` (e.g.
|
||||
`http://localhost:5000/callback`) to Authorized Redirect URIs.
|
||||
- Save your Client ID for later.
|
||||
|
||||
- (Optional): Enable Public Datasets that are being hosted by Google:
|
||||
@@ -55,70 +46,26 @@ static site application.
|
||||
|
||||
## Run the viewer with your OAuth Client ID
|
||||
|
||||
1. Open the `config/oidc-googleCloud.json` file and change `YOURCLIENTID` to
|
||||
1. Open the `config/google.js` file and change `YOURCLIENTID` to
|
||||
your Client ID value.
|
||||
1. Run the OHIF Viewer using the oidc-googleCloud.json configuration file
|
||||
1. Run the OHIF Viewer using the config/google.js configuration file
|
||||
|
||||
```bash
|
||||
cd OHIFViewer
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor npm install
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor --settings ../config/oidc-googleCloud.json
|
||||
yarn install
|
||||
APP_CONFIG=config/google.js yarn run dev
|
||||
```
|
||||
|
||||
## Running via Docker
|
||||
|
||||
OHIF is also providing a Docker container which can connect to Google Cloud
|
||||
Healthcare with a Client ID which is provided at runtime. This is a very simple
|
||||
method to get up and running. Internally, the container is running
|
||||
[Nginx](https://nginx.org/) to serve the
|
||||
[Standalone Viewer](../standalone-viewer/usage.md).
|
||||
The OHIF Viewer Docker container can be connected to Google Cloud
|
||||
Healthcare by providing a Client ID at runtime. This is a very simple
|
||||
method to get up and running.
|
||||
|
||||
1. Install Docker (https://www.docker.com/)
|
||||
1. Run the Docker container, providing a Client ID as an environment variable.
|
||||
Client IDs look like `xyz.apps.googleusercontent.com`.
|
||||
|
||||
```bash
|
||||
docker run --env CLIENT_ID=$CLIENT_ID --publish 3000:80 ohif/viewer-google-cloud:latest
|
||||
```
|
||||
|
||||
## Building the ohif/viewer-google-cloud Docker Image
|
||||
|
||||
The
|
||||
[ohif/viewer-google-cloud](https://cloud.docker.com/u/ohif/repository/docker/ohif/viewer-google-cloud)
|
||||
Docker image is built as follows. The Dockerfile and nginx.conf are in the
|
||||
`/dockersupport/viewer-google-cloud` folder.
|
||||
|
||||
1. [Install Meteor](https://www.meteor.com/install)
|
||||
1. Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/OHIF/Viewers.git
|
||||
cd Viewers
|
||||
```
|
||||
|
||||
1. Install meteor-build-client-fixed2 so you can build the Standalone Viewer
|
||||
|
||||
```bash
|
||||
npm install -g meteor-build-client-fixed2
|
||||
```
|
||||
|
||||
1. Build the Standalone client-only OHIF Viewer
|
||||
|
||||
```bash
|
||||
cd OHIFViewer/
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor npm install
|
||||
METEOR_PACKAGE_DIRS="../Packages" meteor-build-client-fixed2 ../dockersupport/viewer-google-cloud/build -s ../config/oidc.json
|
||||
```
|
||||
|
||||
1. Build the Docker image
|
||||
|
||||
```bash
|
||||
cd ../dockersupport/viewer-google-cloud
|
||||
docker build -t ohif/viewer-google-cloud .
|
||||
```
|
||||
|
||||
1. Run the Docker image using an OAuth Client ID
|
||||
|
||||
```bash
|
||||
docker run --env CLIENT_ID={$someID}.apps.googleusercontent.com --publish 3000:80 ohif/viewer-google-cloud
|
||||
docker run --env CLIENT_ID=$CLIENT_ID --publish 5000:80 ohif/viewer:latest
|
||||
```
|
||||
@@ -71,11 +71,11 @@ how to configure the project for your own imaging archive below.
|
||||
#### How it Works
|
||||
|
||||
The configuration for our project is in the `/public/config` directory. Our
|
||||
build process knows which configuration file to use based on the
|
||||
`REACT_APP_CONFIG` environment variable. By default, its value is
|
||||
[`default.js`](https://github.com/OHIF/Viewers/blob/react/public/config/default.js).
|
||||
When we build, the `%REACT_APP_CONFIG%` value in
|
||||
our[`/public/index.html`](https://github.com/OHIF/Viewers/blob/react/public/index.html#L12-L15)
|
||||
build process knows which configuration file to use based on the `APP_CONFIG`
|
||||
environment variable. By default, its value is
|
||||
[`default.js`](https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/default.js).
|
||||
When we build, the `%APP_CONFIG%` value in
|
||||
our[`/public/index.html`](https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/index.html)
|
||||
file is substituted for the correct configuration file's name. This sets
|
||||
the`window.config` equal to our configuration file's value.
|
||||
|
||||
@@ -88,7 +88,6 @@ _/public/config/default.js_
|
||||
```js
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
@@ -105,17 +104,17 @@ window.config = {
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
You can also create a new config file and specify its path relative to the build
|
||||
output's root by setting the `REACT_APP_CONFIG` environment variable. You can
|
||||
set the value of this environment variable a few different ways:
|
||||
output's root by setting the `APP_CONFIG` environment variable. You can set the
|
||||
value of this environment variable a few different ways:
|
||||
|
||||
- [Add a temporary environment variable in your shell](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-temporary-environment-variables-in-your-shell)
|
||||
- [Add environment specific variables in `.env` file(s)](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-development-environment-variables-in-env)
|
||||
- Using the `cross-env` package in an npm script:
|
||||
- `"build": "cross-env REACT_APP_CONFIG=config/my-config.js react-scripts build"`
|
||||
- `"build": "cross-env APP_CONFIG=config/my-config.js react-scripts build"`
|
||||
|
||||
After updating the configuration, `yarn run build:web` to generate updated build
|
||||
output.
|
||||
|
||||
@@ -50,8 +50,6 @@ include tags. Here's how it works:
|
||||
window.config = {
|
||||
// default: '/'
|
||||
routerBasename: '/',
|
||||
// default: ''
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ control.
|
||||
Do not use this recipe to host sensitive medical data on the open web. Depending
|
||||
on your company's policies, this may be an appropriate setup on an internal
|
||||
network when protected with a server's basic authentication. For a more robust
|
||||
setup, check out our [user account control recpie](./user-account-control.md)
|
||||
setup, check out our [user account control recipe](./user-account-control.md)
|
||||
that builds on the lessons learned here.
|
||||
|
||||
## Overview
|
||||
@@ -51,7 +51,7 @@ We can solve this one of two ways:
|
||||
|
||||
This solution uses the first approach, but you can see an example of the second
|
||||
in the `docker-compose` bundled with this project for local development:
|
||||
[HERE](#)
|
||||
[HERE](https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/Nginx-Orthanc/docker-compose.yml)
|
||||
|
||||
You can read more about CORS in this Medium article: [Understanding
|
||||
CORS][understanding-cors]
|
||||
@@ -124,10 +124,10 @@ likely want to update:
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file and made
|
||||
available globally at `window.config`. The configuration we use is set to a
|
||||
specific file when we build the viewer, and determined by the env variable:
|
||||
`REACT_APP_CONFIG`. You can see where we set its value in the `dockerfile` for
|
||||
this solution:
|
||||
`APP_CONFIG`. You can see where we set its value in the `dockerfile` for this
|
||||
solution:
|
||||
|
||||
`ENV REACT_APP_CONFIG=config/docker_openresty-orthanc.js`
|
||||
`ENV APP_CONFIG=config/docker_openresty-orthanc.js`
|
||||
|
||||
You can find the configuration we're using here:
|
||||
`/public/config/docker_openresty-orthanc.js`
|
||||
|
||||
@@ -125,10 +125,10 @@ likely want to update:
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file and made
|
||||
available globally at `window.config`. The configuration we use is set to a
|
||||
specific file when we build the viewer, and determined by the env variable:
|
||||
`REACT_APP_CONFIG`. You can see where we set its value in the `dockerfile` for
|
||||
this solution:
|
||||
`APP_CONFIG`. You can see where we set its value in the `dockerfile` for this
|
||||
solution:
|
||||
|
||||
`ENV REACT_APP_CONFIG=config/docker_openresty-orthanc-keycloak.js`
|
||||
`ENV APP_CONFIG=config/docker_openresty-orthanc-keycloak.js`
|
||||
|
||||
You can find the configuration we're using here:
|
||||
`/public/config/docker_openresty-orthanc-keycloak.js`
|
||||
@@ -281,9 +281,9 @@ community members put together:
|
||||
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
||||
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
||||
<!-- SRC -->
|
||||
[config]: #
|
||||
[dockerfile]: #
|
||||
[config-nginx]: #
|
||||
[config-orthanc]: #
|
||||
[config-keycloak]: #
|
||||
[config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/src/config.js
|
||||
[dockerfile]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/.recipes
|
||||
[config-nginx]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/.recipes
|
||||
[config-orthanc]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/.recipes
|
||||
[config-keycloak]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/.recipes
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -7,11 +7,11 @@
|
||||
## How it Works
|
||||
|
||||
The configuration for our project is in the `/public/config` directory. Our
|
||||
build process knows which configuration file to use based on the
|
||||
`REACT_APP_CONFIG` environment variable. By default, its value is
|
||||
[`default.js`](https://github.com/OHIF/Viewers/blob/react/public/config/default.js).
|
||||
When we build, the `%REACT_APP_CONFIG%` value in
|
||||
our[`/public/index.html`](https://github.com/OHIF/Viewers/blob/react/public/index.html#L12-L15)
|
||||
build process knows which configuration file to use based on the `APP_CONFIG`
|
||||
environment variable. By default, its value is
|
||||
[`default.js`](https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/default.js).
|
||||
When we build, the `%APP_CONFIG%` value in
|
||||
our[`/public/index.html`](https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/index.html)
|
||||
file is substituted for the correct configuration file's name. This sets the
|
||||
`window.config` equal to our configuration file's value.
|
||||
|
||||
@@ -24,7 +24,6 @@ _/public/config/default.js_
|
||||
```js
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
@@ -41,17 +40,17 @@ window.config = {
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
You can also create a new config file and specify its path relative to the build
|
||||
output's root by setting the `REACT_APP_CONFIG` environment variable. You can
|
||||
set the value of this environment variable a few different ways:
|
||||
output's root by setting the `APP_CONFIG` environment variable. You can set the
|
||||
value of this environment variable a few different ways:
|
||||
|
||||
- [Add a temporary environment variable in your shell](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-temporary-environment-variables-in-your-shell)
|
||||
- [Add environment specific variables in `.env` file(s)](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#adding-development-environment-variables-in-env)
|
||||
- Using the `cross-env` package in an npm script:
|
||||
- `"build": "cross-env REACT_APP_CONFIG=config/my-config.js react-scripts build"`
|
||||
- `"build": "cross-env APP_CONFIG=config/my-config.js react-scripts build"`
|
||||
|
||||
After updating the configuration, `yarn run build:web` to generate updated build
|
||||
output.
|
||||
@@ -60,8 +60,8 @@ _Upload your first Study:_
|
||||
#### Orthanc: Learn More
|
||||
|
||||
You can see the `docker-compose.yml` file this command runs at
|
||||
[`<project-root>/docker/Nginx-Docker/`](#), and more on Orthanc for Docker in
|
||||
[Orthanc's documentation][orthanc-docker].
|
||||
[`<project-root>/platform/viewer/.recipes/Nginx-Orthanc/`](https://github.com/OHIF/Viewers/tree/master/platform/viewer/.recipes/Nginx-Orthanc),
|
||||
and more on Orthanc for Docker in [Orthanc's documentation][orthanc-docker].
|
||||
|
||||
### Connecting to Orthanc
|
||||
|
||||
@@ -87,24 +87,22 @@ is running the `dev:orthanc` script in our project's `package.json`. That script
|
||||
is:
|
||||
|
||||
```js
|
||||
cross-env PORT=5000 REACT_APP_CONFIG=config/docker_nginx-orthanc.js react-scripts start
|
||||
cross-env PORT=5000 APP_CONFIG=config/docker_nginx-orthanc.js react-scripts start
|
||||
```
|
||||
|
||||
- `cross-env` sets two environment variables
|
||||
- PORT: 5000
|
||||
- REACT_APP_CONFIG: `config/docker_nginx-orthanc.js`
|
||||
- APP_CONFIG: `config/docker_nginx-orthanc.js`
|
||||
- `react-scripts` runs it's `start` script. This is [the de-facto
|
||||
way][cra-start] to run a "Create React App" in development mode.
|
||||
|
||||
The `REACT_APP_CONFIG` value tells our app which file to load on to
|
||||
`window.config`. By default, our app uses the file at
|
||||
`<project-root>/public/config/default.js`. Here is what that configuration looks
|
||||
like:
|
||||
The `APP_CONFIG` value tells our app which file to load on to `window.config`.
|
||||
By default, our app uses the file at `<project-root>/public/config/default.js`.
|
||||
Here is what that configuration looks like:
|
||||
|
||||
```js
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
relativeWebWorkerScriptsPath: '',
|
||||
servers: {
|
||||
dicomWeb: [
|
||||
{
|
||||
|
||||
@@ -37,8 +37,8 @@ Some tips for filing a new issue:
|
||||
At the moment we are in the process of defining our roadmap and will do our best
|
||||
to communicate this to the community. If your requested feature is on the
|
||||
roadmap, then it will most likely be built at some point. If it is not, you are
|
||||
welcome to build it yourself and [contribute it](../contributing.md). If you
|
||||
have resources and would like to fund the development of a feature, please
|
||||
welcome to build it yourself and [contribute it](contributing.md). If you have
|
||||
resources and would like to fund the development of a feature, please
|
||||
[contact us](http://www.ohif.org).
|
||||
|
||||
### Who should I contact about Academic Collaborations?
|
||||
|
||||
@@ -1074,10 +1074,10 @@ gitbook-plugin-sitemap@^1.2.0:
|
||||
dependencies:
|
||||
sitemap "1.5.0"
|
||||
|
||||
gitbook-plugin-theme-cornerstone@^1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/gitbook-plugin-theme-cornerstone/-/gitbook-plugin-theme-cornerstone-1.1.3.tgz#320e1d688eaf27dbf8d580f285509b55984437fd"
|
||||
integrity sha512-fURj67XGQ5rZPj8LXbwEviMxLfWgN/s08t9Wnp1dQqffBUJwsLe00R/IeecOjsQHJ3Gvb8GjbXJ6qzIDGu5hPA==
|
||||
gitbook-plugin-theme-cornerstone@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/gitbook-plugin-theme-cornerstone/-/gitbook-plugin-theme-cornerstone-1.1.4.tgz#bc21b65acee2ac67e2ddb0986442ec448fe0c7fb"
|
||||
integrity sha512-BADgFyEKhkc0pPSBRwXa5VU2UoYQkd2cRwjrtjHyV6w4h/M7uyeWYFOcsgi2ju3rpHRCwF8lMg/TtDdAV8V1SQ==
|
||||
|
||||
gitbook-plugin-versions@^2.1.4:
|
||||
version "2.1.4"
|
||||
@@ -1608,9 +1608,9 @@ lodash.map@^4.4.0:
|
||||
integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=
|
||||
|
||||
lodash.merge@^4.4.0:
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
|
||||
integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lodash.pad@^4.1.0:
|
||||
version "4.5.1"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
const path = require('path');
|
||||
const merge = require('webpack-merge');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.js');
|
||||
|
||||
//
|
||||
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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-development
|
||||
mode: 'development',
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-production
|
||||
mode: 'production',
|
||||
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: 'ohifExtensionCornerstone',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,87 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.50.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@0.50.0-alpha.10...@ohif/extension-cornerstone@0.50.1) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.50.0-alpha.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@0.0.39-alpha.9...@ohif/extension-cornerstone@0.50.0-alpha.10) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.0.39-alpha.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@0.0.39-alpha.8...@ohif/extension-cornerstone@0.0.39-alpha.9) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.0.39-alpha.8 (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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.39-alpha.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@0.0.39-alpha.6...@ohif/extension-cornerstone@0.0.39-alpha.7) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
# 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.39-alpha.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@0.0.39-alpha.5...@ohif/extension-cornerstone@0.0.39-alpha.6) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
# 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.39-alpha.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@0.0.39-alpha.4...@ohif/extension-cornerstone@0.0.39-alpha.5) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
# 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.39-alpha.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@0.0.39-alpha.3...@ohif/extension-cornerstone@0.0.39-alpha.4) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
# 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.39-alpha.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@0.0.39-alpha.2...@ohif/extension-cornerstone@0.0.39-alpha.3) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
|
||||
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return {
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
context: SRC_DIR,
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.json', '*'],
|
||||
symlinks: true,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: [/node_modules/],
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
// Find babel.config.js in monorepo root
|
||||
// https://babeljs.io/docs/en/options#rootmode
|
||||
rootMode: 'upward',
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
// Do not transform ES6 modules to another format.
|
||||
// Webpack will take care of that.
|
||||
modules: false,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
ExtractCssChunks.loader,
|
||||
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
config: {
|
||||
path: './postcss.config.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin(['NODE_ENV']),
|
||||
new ExtractCssChunks({
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
// hot: true /* only necessary if hot reloading not function*/
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const common = require('./webpack.common');
|
||||
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 = common(env, argv);
|
||||
|
||||
return merge(commonConfig, {
|
||||
mode: 'production',
|
||||
stats: {
|
||||
colors: false,
|
||||
hash: true,
|
||||
timings: true,
|
||||
assets: true,
|
||||
chunks: true,
|
||||
chunkModules: true,
|
||||
modules: true,
|
||||
children: true,
|
||||
warnings: true,
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
sideEffects: true,
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'ohifCornerstoneExtension',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
auxiliaryComment: 'Test Comment',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(gif|jpe?g|png|svg)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 8192,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|woff|woff2)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 50000,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "0.0.39-alpha.2",
|
||||
"version": "0.50.1",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -8,8 +8,9 @@
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -19,19 +20,20 @@
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --config config/webpack.prod.js -d",
|
||||
"dev": "webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:cornerstone": "yarn run dev",
|
||||
"build": "webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"dev": "webpack --config config/webpack.prod.js -w -d",
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.11.1-alpha.0",
|
||||
"@ohif/ui": "^0.2.18-alpha.0",
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "^3.1.0",
|
||||
"cornerstone-wado-image-loader": "^2.2.3",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dcmjs": "^0.3.8",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -43,10 +45,9 @@
|
||||
"redux": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"classnames": "^2.2.6",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"react-cornerstone-viewport": "0.1.30"
|
||||
},
|
||||
"gitHead": "a5baa9228c0eda0df880136bde4420d78e6f8706"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,24 @@
|
||||
import CornerstoneViewport from "react-cornerstone-viewport";
|
||||
import OHIF from "@ohif/core";
|
||||
import { connect } from "react-redux";
|
||||
import throttle from "lodash.throttle";
|
||||
import CornerstoneViewport from 'react-cornerstone-viewport';
|
||||
import OHIF from '@ohif/core';
|
||||
import { connect } from 'react-redux';
|
||||
import throttle from 'lodash.throttle';
|
||||
|
||||
const { setViewportActive, setViewportSpecificData } = OHIF.redux.actions;
|
||||
const {
|
||||
onAdded,
|
||||
onRemoved,
|
||||
onModified,
|
||||
} = OHIF.measurements.MeasurementHandlers;
|
||||
|
||||
// TODO: Transition to enums for the action names so that we can ensure they stay up to date
|
||||
// everywhere they're used.
|
||||
const MEASUREMENT_ACTION_MAP = {
|
||||
added: onAdded,
|
||||
removed: onRemoved,
|
||||
modified: throttle(event => {
|
||||
return onModified(event);
|
||||
}, 300),
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
let dataFromStore;
|
||||
@@ -37,7 +52,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
enableStackPrefetch: isActive,
|
||||
//stack: viewportSpecificData.stack,
|
||||
cineToolData: viewportSpecificData.cine,
|
||||
viewport: viewportSpecificData.viewport
|
||||
viewport: viewportSpecificData.viewport,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -64,28 +79,15 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
||||
dispatch(
|
||||
setViewportSpecificData(viewportIndex, {
|
||||
// TODO: Hack to make sure our plugin info is available from the outset
|
||||
plugin: "cornerstone",
|
||||
dom: enabledElement
|
||||
plugin: 'cornerstone',
|
||||
dom: enabledElement,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
onMeasurementsChanged: (event, action) => {
|
||||
const {
|
||||
onAdded,
|
||||
onRemoved,
|
||||
onModified
|
||||
} = OHIF.measurements.MeasurementHandlers;
|
||||
const actions = {
|
||||
added: onAdded,
|
||||
removed: onRemoved,
|
||||
modified: throttle(event => {
|
||||
return onModified(event);
|
||||
}, 300)
|
||||
};
|
||||
|
||||
return actions[action](event);
|
||||
}
|
||||
return MEASUREMENT_ACTION_MAP[action](event);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -202,6 +202,10 @@ class OHIFCornerstoneViewport extends Component {
|
||||
frameIndex
|
||||
} = displaySet;
|
||||
|
||||
if (!studyInstanceUid || !displaySetInstanceUid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sopClassUids && sopClassUids.length > 1) {
|
||||
console.warn(
|
||||
"More than one SOPClassUid in the same series is not yet supported."
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import OHIF from '@ohif/core';
|
||||
const scroll = cornerstoneTools.import('util/scroll');
|
||||
|
||||
const actions = {
|
||||
rotateViewport: ({ viewports, rotation }) => {
|
||||
@@ -89,15 +91,76 @@ const actions = {
|
||||
// TODO
|
||||
console.warn('updateDisplaySet: ', direction);
|
||||
},
|
||||
clearAnnotations: () => {
|
||||
console.warn('clearAnnotations: not yet implemented');
|
||||
// const toolState =
|
||||
// cornerstoneTools.globalImageIdSpecificToolStateManager.toolState;
|
||||
// if (!toolState) return;
|
||||
// Object.keys(toolState).forEach(imageId => {
|
||||
// if (!cornerstoneImageId || cornerstoneImageId === imageId)
|
||||
// delete toolState[imageId];
|
||||
// });
|
||||
clearAnnotations: ({ viewports }) => {
|
||||
const element = _getActiveViewportEnabledElement(
|
||||
viewports.viewportSpecificData,
|
||||
viewports.activeViewportIndex
|
||||
);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
if (!enabledElement || !enabledElement.image) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
toolState,
|
||||
} = cornerstoneTools.globalImageIdSpecificToolStateManager;
|
||||
if (
|
||||
!toolState ||
|
||||
toolState.hasOwnProperty(enabledElement.image.imageId) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const imageIdToolState = toolState[enabledElement.image.imageId];
|
||||
|
||||
const measurementsToRemove = [];
|
||||
|
||||
Object.keys(imageIdToolState).forEach(toolType => {
|
||||
const { data } = imageIdToolState[toolType];
|
||||
|
||||
data.forEach(measurementData => {
|
||||
const { _id, lesionNamingNumber, measurementNumber } = measurementData;
|
||||
if (!_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
measurementsToRemove.push({
|
||||
toolType,
|
||||
_id,
|
||||
lesionNamingNumber,
|
||||
measurementNumber,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
measurementsToRemove.forEach(measurementData => {
|
||||
OHIF.measurements.MeasurementHandlers.onRemoved({
|
||||
detail: {
|
||||
toolType: measurementData.toolType,
|
||||
measurementData,
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
nextImage: ({ viewports }) => {
|
||||
const enabledElement = _getActiveViewportEnabledElement(
|
||||
viewports.viewportSpecificData,
|
||||
viewports.activeViewportIndex
|
||||
);
|
||||
|
||||
scroll(enabledElement, 1);
|
||||
},
|
||||
previousImage: ({ viewports }) => {
|
||||
const enabledElement = _getActiveViewportEnabledElement(
|
||||
viewports.viewportSpecificData,
|
||||
viewports.activeViewportIndex
|
||||
);
|
||||
|
||||
scroll(enabledElement, -1);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -147,8 +210,21 @@ const definitions = {
|
||||
storeContexts: ['viewports'],
|
||||
options: {},
|
||||
},
|
||||
// TODO: Clear Annotations
|
||||
// TODO: Next/Previous image
|
||||
clearAnnotations: {
|
||||
commandFn: actions.clearAnnotations,
|
||||
storeContexts: ['viewports'],
|
||||
options: {},
|
||||
},
|
||||
nextImage: {
|
||||
commandFn: actions.nextImage,
|
||||
storeContexts: ['viewports'],
|
||||
options: {},
|
||||
},
|
||||
previousImage: {
|
||||
commandFn: actions.previousImage,
|
||||
storeContexts: ['viewports'],
|
||||
options: {},
|
||||
},
|
||||
// TODO: First/Last image
|
||||
// Next/Previous series/DisplaySet
|
||||
nextViewportDisplaySet: {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// -- ACTIVE_ROUTE::VIEWER
|
||||
// -- ACTIVE_VIEWPORT::CORNERSTONE
|
||||
// setToolActive commands should receive the button event that triggered
|
||||
// so we can do the "bind to this butyon" magic
|
||||
// so we can do the "bind to this button" magic
|
||||
|
||||
const TOOLBAR_BUTTON_TYPES = {
|
||||
COMMAND: 'command',
|
||||
@@ -98,6 +98,98 @@ const definitions = [
|
||||
behavior: 'CINE',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'More',
|
||||
label: 'More',
|
||||
icon: 'ellipse-circle',
|
||||
buttons: [
|
||||
{
|
||||
id: 'Magnify',
|
||||
label: 'Magnify',
|
||||
icon: 'circle',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'Magnify' },
|
||||
},
|
||||
{
|
||||
id: 'WwwcRegion',
|
||||
label: 'ROI Window',
|
||||
icon: 'stop',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'WwwcRegion' },
|
||||
},
|
||||
{
|
||||
id: 'DragProbe',
|
||||
label: 'Probe',
|
||||
icon: 'dot-circle',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'DragProbe' },
|
||||
},
|
||||
{
|
||||
id: 'EllipticalRoi',
|
||||
label: 'Ellipse',
|
||||
icon: 'circle-o',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'EllipticalRoi' },
|
||||
},
|
||||
{
|
||||
id: 'RectangleRoi',
|
||||
label: 'Rectangle',
|
||||
icon: 'square-o',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.SET_TOOL_ACTIVE,
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: { toolName: 'RectangleRoi' },
|
||||
},
|
||||
{
|
||||
id: 'Invert',
|
||||
label: 'Invert',
|
||||
icon: 'adjust',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'invertViewport',
|
||||
},
|
||||
{
|
||||
id: 'RotateRight',
|
||||
label: 'Rotate Right',
|
||||
icon: 'rotate-right',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'rotateViewportCW',
|
||||
},
|
||||
{
|
||||
id: 'FlipH',
|
||||
label: 'Flip H',
|
||||
icon: 'ellipse-h',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'flipViewportHorizontal',
|
||||
},
|
||||
{
|
||||
id: 'FlipV',
|
||||
label: 'Flip V',
|
||||
icon: 'ellipse-v',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'flipViewportVertical',
|
||||
},
|
||||
{
|
||||
id: 'Clear',
|
||||
label: 'Clear',
|
||||
icon: 'trash',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'clearAnnotations',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
const path = require('path');
|
||||
const merge = require('webpack-merge');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.js');
|
||||
|
||||
//
|
||||
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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-development
|
||||
mode: 'development',
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-production
|
||||
mode: 'production',
|
||||
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: 'ohifExtensionDicomHtml',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,87 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.50.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@0.50.0-alpha.10...@ohif/extension-dicom-html@0.50.1) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.50.0-alpha.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@0.0.4-alpha.9...@ohif/extension-dicom-html@0.50.0-alpha.10) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.0.4-alpha.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@0.0.4-alpha.8...@ohif/extension-dicom-html@0.0.4-alpha.9) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.0.4-alpha.8 (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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.4-alpha.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@0.0.4-alpha.6...@ohif/extension-dicom-html@0.0.4-alpha.7) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
# 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.4-alpha.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@0.0.4-alpha.5...@ohif/extension-dicom-html@0.0.4-alpha.6) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
# 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.4-alpha.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@0.0.4-alpha.4...@ohif/extension-dicom-html@0.0.4-alpha.5) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
# 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.4-alpha.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@0.0.4-alpha.3...@ohif/extension-dicom-html@0.0.4-alpha.4) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
# 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.4-alpha.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@0.0.4-alpha.2...@ohif/extension-dicom-html@0.0.4-alpha.3) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
|
||||
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return {
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
context: SRC_DIR,
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.json', '*'],
|
||||
symlinks: true,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: [/node_modules/],
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
// Find babel.config.js in monorepo root
|
||||
// https://babeljs.io/docs/en/options#rootmode
|
||||
rootMode: 'upward',
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
// Do not transform ES6 modules to another format.
|
||||
// Webpack will take care of that.
|
||||
modules: false,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
ExtractCssChunks.loader,
|
||||
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
config: {
|
||||
path: './postcss.config.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin(['NODE_ENV']),
|
||||
new ExtractCssChunks({
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
// hot: true /* only necessary if hot reloading not function*/
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const common = require('./webpack.common');
|
||||
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 = common(env, argv);
|
||||
|
||||
return merge(commonConfig, {
|
||||
mode: 'production',
|
||||
stats: {
|
||||
colors: false,
|
||||
hash: true,
|
||||
timings: true,
|
||||
assets: true,
|
||||
chunks: true,
|
||||
chunkModules: true,
|
||||
modules: true,
|
||||
children: true,
|
||||
warnings: true,
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
sideEffects: true,
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'ohifCornerstoneExtension',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
auxiliaryComment: 'Test Comment',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(gif|jpe?g|png|svg)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 8192,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|woff|woff2)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 50000,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-html",
|
||||
"version": "0.0.4-alpha.2",
|
||||
"version": "0.50.1",
|
||||
"description": "OHIF extension for rendering structured reports to HTML",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -8,25 +8,9 @@
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "webpack --config config/webpack.prod.js -w -d",
|
||||
"build": "webpack --config config/webpack.prod.js -d",
|
||||
"build:package": "yarn run build",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.11.1-alpha.0",
|
||||
"dcmjs": "^0.3.3",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^15.0.0 || ^16.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.2.0"
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -35,5 +19,22 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "a5baa9228c0eda0df880136bde4420d78e6f8706"
|
||||
"scripts": {
|
||||
"dev": "webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:dicom-html": "yarn run dev",
|
||||
"build": "webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"dcmjs": "^0.3.3",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^15.0.0 || ^16.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5"
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import { MODULE_TYPES, utils } from "@ohif/core";
|
||||
import { MODULE_TYPES, utils } from '@ohif/core';
|
||||
|
||||
// TODO: Should probably use dcmjs for this
|
||||
const SOP_CLASS_UIDS = {
|
||||
BASIC_TEXT_SR: "1.2.840.10008.5.1.4.1.1.88.11",
|
||||
ENHANCED_SR: "1.2.840.10008.5.1.4.1.1.88.22",
|
||||
COMPREHENSIVE_SR: "1.2.840.10008.5.1.4.1.1.88.33",
|
||||
PROCEDURE_LOG_STORAGE: "1.2.840.10008.5.1.4.1.1.88.40",
|
||||
MAMMOGRAPHY_CAD_SR: "1.2.840.10008.5.1.4.1.1.88.50",
|
||||
CHEST_CAD_SR: "1.2.840.10008.5.1.4.1.1.88.65",
|
||||
X_RAY_RADIATION_DOSE_SR: "1.2.840.10008.5.1.4.1.1.88.67"
|
||||
BASIC_TEXT_SR: '1.2.840.10008.5.1.4.1.1.88.11',
|
||||
ENHANCED_SR: '1.2.840.10008.5.1.4.1.1.88.22',
|
||||
COMPREHENSIVE_SR: '1.2.840.10008.5.1.4.1.1.88.33',
|
||||
PROCEDURE_LOG_STORAGE: '1.2.840.10008.5.1.4.1.1.88.40',
|
||||
MAMMOGRAPHY_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.50',
|
||||
CHEST_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.65',
|
||||
X_RAY_RADIATION_DOSE_SR: '1.2.840.10008.5.1.4.1.1.88.67',
|
||||
};
|
||||
|
||||
const sopClassUids = Object.values(SOP_CLASS_UIDS);
|
||||
@@ -16,23 +16,24 @@ const sopClassUids = Object.values(SOP_CLASS_UIDS);
|
||||
// TODO: Handle the case where there is more than one SOP Class Handler for the
|
||||
// same SOP Class
|
||||
const OHIFDicomHtmlSopClassHandler = {
|
||||
id: "OHIFDicomHtmlSopClassHandler",
|
||||
id: 'OHIFDicomHtmlSopClassHandler',
|
||||
type: MODULE_TYPES.SOP_CLASS_HANDLER,
|
||||
sopClassUids,
|
||||
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
|
||||
const instance = series.getFirstInstance();
|
||||
|
||||
return {
|
||||
plugin: "html",
|
||||
plugin: 'html',
|
||||
modality: 'SR',
|
||||
displaySetInstanceUid: utils.guid(),
|
||||
wadoRoot: study.getData().wadoRoot,
|
||||
wadoUri: instance.getData().wadouri,
|
||||
sopInstanceUid: instance.getSOPInstanceUID(),
|
||||
seriesInstanceUid: series.getSeriesInstanceUID(),
|
||||
studyInstanceUid: study.getStudyInstanceUID(),
|
||||
authorizationHeaders
|
||||
authorizationHeaders,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default OHIFDicomHtmlSopClassHandler;
|
||||
@@ -0,0 +1,16 @@
|
||||
const path = require('path');
|
||||
const merge = require('webpack-merge');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.js');
|
||||
|
||||
//
|
||||
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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-development
|
||||
mode: 'development',
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-production
|
||||
mode: 'production',
|
||||
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: 'ohifExtensionDicomMicroscopy',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,87 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.50.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.0-alpha.10...@ohif/extension-dicom-microscopy@0.50.1) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.50.0-alpha.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.0.9-alpha.9...@ohif/extension-dicom-microscopy@0.50.0-alpha.10) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.0.9-alpha.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.0.9-alpha.8...@ohif/extension-dicom-microscopy@0.0.9-alpha.9) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.0.9-alpha.8 (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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.9-alpha.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.0.9-alpha.6...@ohif/extension-dicom-microscopy@0.0.9-alpha.7) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
# 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.9-alpha.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.0.9-alpha.5...@ohif/extension-dicom-microscopy@0.0.9-alpha.6) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
# 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.9-alpha.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.0.9-alpha.4...@ohif/extension-dicom-microscopy@0.0.9-alpha.5) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
# 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.9-alpha.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.0.9-alpha.3...@ohif/extension-dicom-microscopy@0.0.9-alpha.4) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
# 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.9-alpha.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.0.9-alpha.2...@ohif/extension-dicom-microscopy@0.0.9-alpha.3) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
|
||||
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return {
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
context: SRC_DIR,
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.json', '*'],
|
||||
symlinks: true,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: [/node_modules/],
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
// Find babel.config.js in monorepo root
|
||||
// https://babeljs.io/docs/en/options#rootmode
|
||||
rootMode: 'upward',
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
// Do not transform ES6 modules to another format.
|
||||
// Webpack will take care of that.
|
||||
modules: false,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
ExtractCssChunks.loader,
|
||||
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
config: {
|
||||
path: './postcss.config.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin(['NODE_ENV']),
|
||||
new ExtractCssChunks({
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
// hot: true /* only necessary if hot reloading not function*/
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const common = require('./webpack.common');
|
||||
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 = common(env, argv);
|
||||
|
||||
return merge(commonConfig, {
|
||||
mode: 'production',
|
||||
stats: {
|
||||
colors: false,
|
||||
hash: true,
|
||||
timings: true,
|
||||
assets: true,
|
||||
chunks: true,
|
||||
chunkModules: true,
|
||||
modules: true,
|
||||
children: true,
|
||||
warnings: true,
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
sideEffects: true,
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'ohifCornerstoneExtension',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
auxiliaryComment: 'Test Comment',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(gif|jpe?g|png|svg)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 8192,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|woff|woff2)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 50000,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-microscopy",
|
||||
"version": "0.0.9-alpha.2",
|
||||
"version": "0.50.1",
|
||||
"description": "OHIF extension for Dicom Microscopy",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -10,30 +10,31 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --config config/webpack.prod.js -d",
|
||||
"build:package": "yarn run build",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"dev": "webpack --config config/webpack.prod.js -w -d",
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:dicom-microscopy": "yarn run dev",
|
||||
"build": "webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.11.1-alpha.0",
|
||||
"@ohif/core": "^0.50.0",
|
||||
"react": "^15.0.0 || ^16.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"classnames": "^2.2.6",
|
||||
"dicom-microscopy-viewer": "^0.4.3"
|
||||
},
|
||||
"gitHead": "a5baa9228c0eda0df880136bde4420d78e6f8706"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import OHIF from "@ohif/core";
|
||||
import OHIF from '@ohif/core';
|
||||
|
||||
const { utils } = OHIF;
|
||||
|
||||
const SOP_CLASS_UIDS = {
|
||||
VL_WHOLE_SLIDE_MICROSCOPY_IMAGE_STORAGE: "1.2.840.10008.5.1.4.1.1.77.1.6"
|
||||
VL_WHOLE_SLIDE_MICROSCOPY_IMAGE_STORAGE: '1.2.840.10008.5.1.4.1.1.77.1.6',
|
||||
};
|
||||
|
||||
const DicomMicroscopySopClassHandler = {
|
||||
id: "DicomMicroscopySopClassHandlerPlugin",
|
||||
id: 'DicomMicroscopySopClassHandlerPlugin',
|
||||
sopClassUids: [SOP_CLASS_UIDS.VL_WHOLE_SLIDE_MICROSCOPY_IMAGE_STORAGE],
|
||||
getDisplaySetFromSeries(series, study, dicomWebClient) {
|
||||
const instance = series.getFirstInstance();
|
||||
@@ -15,14 +15,15 @@ const DicomMicroscopySopClassHandler = {
|
||||
// Note: We are passing the dicomweb client into each viewport!
|
||||
|
||||
return {
|
||||
plugin: "microscopy",
|
||||
plugin: 'microscopy',
|
||||
modality: 'SM',
|
||||
displaySetInstanceUid: utils.guid(),
|
||||
dicomWebClient,
|
||||
sopInstanceUid: instance.getSOPInstanceUID(),
|
||||
seriesInstanceUid: series.getSeriesInstanceUID(),
|
||||
studyInstanceUid: study.getStudyInstanceUID()
|
||||
studyInstanceUid: study.getStudyInstanceUID(),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default DicomMicroscopySopClassHandler;
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import ReactResizeDetector from 'react-resize-detector';
|
||||
import { api } from 'dicom-microscopy-viewer';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
const microscopyViewer = api.VLWholeSlideMicroscopyImageViewer;
|
||||
|
||||
@@ -9,10 +10,16 @@ class DicomMicroscopyViewport extends Component {
|
||||
error: null,
|
||||
};
|
||||
|
||||
viewer = null;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.container = React.createRef();
|
||||
|
||||
this.debouncedResize = debounce(() => {
|
||||
if (this.viewer) this.viewer.resize();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// install the microscopy renderer into the web page.
|
||||
@@ -52,17 +59,18 @@ class DicomMicroscopyViewport extends Component {
|
||||
.then(metadata => {
|
||||
metadata = metadata.filter(m => m);
|
||||
|
||||
const viewer = new microscopyViewer({
|
||||
this.viewer = new microscopyViewer({
|
||||
client: dicomWebClient,
|
||||
metadata,
|
||||
retrieveRendered: false
|
||||
});
|
||||
|
||||
viewer.render({ container, retrieveRendered: true });
|
||||
this.viewer.render({ container });
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { studies, displaySet } = this.props.viewportData;
|
||||
const { displaySet } = this.props.viewportData;
|
||||
|
||||
this.installOpenLayersRenderer(this.container.current, displaySet);
|
||||
}
|
||||
@@ -71,6 +79,13 @@ class DicomMicroscopyViewport extends Component {
|
||||
const style = { width: '100%', height: '100%' };
|
||||
return (
|
||||
<div className={'DicomMicroscopyViewer'} style={style}>
|
||||
{ReactResizeDetector && (
|
||||
<ReactResizeDetector
|
||||
handleWidth
|
||||
handleHeight
|
||||
onResize={this.onWindowResize}
|
||||
/>
|
||||
)}
|
||||
{this.state.error ? (
|
||||
<h2>{JSON.stringify(this.state.error)}</h2>
|
||||
) : (
|
||||
@@ -79,6 +94,10 @@ class DicomMicroscopyViewport extends Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onWindowResize = () => {
|
||||
this.debouncedResize();
|
||||
};
|
||||
}
|
||||
|
||||
export default DicomMicroscopyViewport;
|
||||
@@ -0,0 +1,16 @@
|
||||
const path = require('path');
|
||||
const merge = require('webpack-merge');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.js');
|
||||
|
||||
//
|
||||
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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-development
|
||||
mode: 'development',
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-production
|
||||
mode: 'production',
|
||||
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: 'ohifExtensionDicomPdf',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,87 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.50.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@0.50.0-alpha.10...@ohif/extension-dicom-pdf@0.50.1) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.50.0-alpha.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@0.0.8-alpha.9...@ohif/extension-dicom-pdf@0.50.0-alpha.10) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.0.8-alpha.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@0.0.8-alpha.8...@ohif/extension-dicom-pdf@0.0.8-alpha.9) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.0.8-alpha.8 (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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.8-alpha.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@0.0.8-alpha.6...@ohif/extension-dicom-pdf@0.0.8-alpha.7) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
# 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.8-alpha.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@0.0.8-alpha.5...@ohif/extension-dicom-pdf@0.0.8-alpha.6) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
# 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.8-alpha.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@0.0.8-alpha.4...@ohif/extension-dicom-pdf@0.0.8-alpha.5) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
# 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.8-alpha.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@0.0.8-alpha.3...@ohif/extension-dicom-pdf@0.0.8-alpha.4) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
# 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.8-alpha.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@0.0.8-alpha.2...@ohif/extension-dicom-pdf@0.0.8-alpha.3) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
|
||||
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return {
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
context: SRC_DIR,
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.json', '*'],
|
||||
symlinks: true,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: [/node_modules/],
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
// Find babel.config.js in monorepo root
|
||||
// https://babeljs.io/docs/en/options#rootmode
|
||||
rootMode: 'upward',
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
// Do not transform ES6 modules to another format.
|
||||
// Webpack will take care of that.
|
||||
modules: false,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
ExtractCssChunks.loader,
|
||||
{ loader: 'css-loader', options: { importLoaders: 1 } },
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
config: {
|
||||
path: './postcss.config.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin(['NODE_ENV']),
|
||||
new ExtractCssChunks({
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
// hot: true /* only necessary if hot reloading not function*/
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const common = require('./webpack.common');
|
||||
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 = common(env, argv);
|
||||
|
||||
return merge(commonConfig, {
|
||||
mode: 'production',
|
||||
stats: {
|
||||
colors: false,
|
||||
hash: true,
|
||||
timings: true,
|
||||
assets: true,
|
||||
chunks: true,
|
||||
chunkModules: true,
|
||||
modules: true,
|
||||
children: true,
|
||||
warnings: true,
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
sideEffects: true,
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'ohifCornerstoneExtension',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
auxiliaryComment: 'Test Comment',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(gif|jpe?g|png|svg)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 8192,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|woff|woff2)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 50000,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-pdf",
|
||||
"version": "0.0.8-alpha.2",
|
||||
"version": "0.50.1",
|
||||
"description": "OHIF extension for Dicom PDF",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -11,29 +11,31 @@
|
||||
"access": "public"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "webpack --config config/webpack.prod.js -w -d",
|
||||
"build": "webpack --config config/webpack.prod.js -d",
|
||||
"dev": "webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:dicom-pdf": "yarn run dev",
|
||||
"build": "webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.11.1-alpha.0",
|
||||
"@ohif/core": "^0.50.0",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^15.0.0 || ^16.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"classnames": "^2.2.6",
|
||||
"lodash.isequal": "^4.5.0"
|
||||
},
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
import { MODULE_TYPES, utils } from "@ohif/core";
|
||||
import { MODULE_TYPES, utils } from '@ohif/core';
|
||||
|
||||
// TODO: Should probably use dcmjs for this
|
||||
const SOP_CLASS_UIDS = {
|
||||
ENCAPSULATED_PDF: "1.2.840.10008.5.1.4.1.1.104.1"
|
||||
ENCAPSULATED_PDF: '1.2.840.10008.5.1.4.1.1.104.1',
|
||||
};
|
||||
|
||||
const OHIFDicomPDFSopClassHandler = {
|
||||
id: "OHIFDicomPDFSopClassHandlerPlugin",
|
||||
id: 'OHIFDicomPDFSopClassHandlerPlugin',
|
||||
type: MODULE_TYPES.SOP_CLASS_HANDLER,
|
||||
sopClassUids: [SOP_CLASS_UIDS.ENCAPSULATED_PDF],
|
||||
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
|
||||
const instance = series.getFirstInstance();
|
||||
|
||||
return {
|
||||
plugin: "pdf",
|
||||
plugin: 'pdf',
|
||||
modality: 'DOC',
|
||||
displaySetInstanceUid: utils.guid(),
|
||||
wadoRoot: study.getData().wadoRoot,
|
||||
wadoUri: instance.getData().wadouri,
|
||||
sopInstanceUid: instance.getSOPInstanceUID(),
|
||||
seriesInstanceUid: series.getSeriesInstanceUID(),
|
||||
studyInstanceUid: study.getStudyInstanceUID(),
|
||||
authorizationHeaders: authorizationHeaders
|
||||
authorizationHeaders: authorizationHeaders,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default OHIFDicomPDFSopClassHandler;
|
||||
@@ -0,0 +1,16 @@
|
||||
const path = require('path');
|
||||
const merge = require('webpack-merge');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.js');
|
||||
|
||||
//
|
||||
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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-development
|
||||
mode: 'development',
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.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, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-production
|
||||
mode: 'production',
|
||||
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: 'ohifExtensionVtk',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,106 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.50.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.50.2...@ohif/extension-vtk@0.50.3) (2019-08-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.50.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.50.1...@ohif/extension-vtk@0.50.2) (2019-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Update vtk and viewport dependencies ([98c40bc](https://github.com/OHIF/Viewers/commit/98c40bc))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.50.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.50.0-alpha.10...@ohif/extension-vtk@0.50.1) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.50.0-alpha.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.1.4-alpha.9...@ohif/extension-vtk@0.50.0-alpha.10) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.4-alpha.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.1.4-alpha.8...@ohif/extension-vtk@0.1.4-alpha.9) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.1.4-alpha.8 (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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.4-alpha.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.1.4-alpha.6...@ohif/extension-vtk@0.1.4-alpha.7) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
# 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.4-alpha.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.1.4-alpha.5...@ohif/extension-vtk@0.1.4-alpha.6) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
# 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.4-alpha.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.1.4-alpha.4...@ohif/extension-vtk@0.1.4-alpha.5) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
# 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.4-alpha.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.1.4-alpha.3...@ohif/extension-vtk@0.1.4-alpha.4) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
# 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.4-alpha.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@0.1.4-alpha.2...@ohif/extension-vtk@0.1.4-alpha.3) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
|
||||
+23
-22
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "0.1.4-alpha.2",
|
||||
"version": "0.50.3",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -11,23 +11,29 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --progress --colors --mode production",
|
||||
"dev": "webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:dicom-vtk": "yarn run dev",
|
||||
"build": "webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"dev": "webpack --watch --progress --colors --mode development",
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.11.1-alpha.0",
|
||||
"@ohif/i18n": "^0.2.3",
|
||||
"@ohif/ui": "^0.2.18-alpha.0",
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/i18n": "^0.50.0",
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-wado-image-loader": "^2.2.3",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dcmjs": "^0.4.7",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"i18next": "^17.0.3",
|
||||
@@ -41,16 +47,16 @@
|
||||
"redux": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"react-vtkjs-viewport": "0.0.9",
|
||||
"vtk.js": "^8.11.0"
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"react-vtkjs-viewport": "0.0.11",
|
||||
"vtk.js": "^9.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^0.11.1-alpha.2",
|
||||
"@ohif/ui": "^0.2.18-alpha.2",
|
||||
"@ohif/core": "^0.50.2",
|
||||
"@ohif/ui": "^0.50.1",
|
||||
"cornerstone-tools": "^3.13.0",
|
||||
"cornerstone-wado-image-loader": "^2.2.3",
|
||||
"dcmjs": "^0.4.7",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dcmjs": "^0.5.1",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"gh-pages": "^2.0.1",
|
||||
"i18next": "^17.0.3",
|
||||
@@ -58,10 +64,5 @@
|
||||
"react-i18next": "^10.11.0",
|
||||
"react-redux": "^7.1.0",
|
||||
"redux": "^4.0.1"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"gitHead": "a5baa9228c0eda0df880136bde4420d78e6f8706"
|
||||
}
|
||||
}
|
||||
@@ -189,6 +189,18 @@ class OHIFVTKViewport extends Component {
|
||||
volumeActor.setMapper(volumeMapper);
|
||||
volumeMapper.setInputData(data);
|
||||
|
||||
// TODO: Should look into implementing autoAdjustSampleDistance in vtk
|
||||
const sampleDistance =
|
||||
1.2 *
|
||||
Math.sqrt(
|
||||
data
|
||||
.getSpacing()
|
||||
.map((v) => v * v)
|
||||
.reduce((a, b) => a + b, 0)
|
||||
);
|
||||
|
||||
volumeMapper.setSampleDistance(sampleDistance);
|
||||
|
||||
volumeCache[displaySetInstanceUid] = volumeActor;
|
||||
|
||||
return volumeActor;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
vtkInteractorStyleMPRCrosshairs,
|
||||
vtkInteractorStyleMPRSlice,
|
||||
vtkInteractorStyleMPRWindowLevel,
|
||||
vtkInteractorStyleMPRSlice,
|
||||
vtkSVGCrosshairsWidget,
|
||||
vtkSVGWidgetManager,
|
||||
} from 'react-vtkjs-viewport';
|
||||
@@ -11,9 +11,13 @@ import setViewportToVTK from './utils/setViewportToVTK.js';
|
||||
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
|
||||
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
|
||||
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
|
||||
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
|
||||
|
||||
const { BlendMode } = Constants;
|
||||
|
||||
// TODO: Put this somewhere else
|
||||
let apis = {};
|
||||
let currentSlabThickness = 0.1;
|
||||
|
||||
function getCrosshairCallbackForIndex(index) {
|
||||
return ({ worldPos }) => {
|
||||
@@ -96,26 +100,24 @@ function _setView(api, sliceNormal, viewUp) {
|
||||
istyle.setSliceNormal(...sliceNormal);
|
||||
camera.setViewUp(...viewUp);
|
||||
|
||||
api.volumes[0].getMapper().setSampleDistance(5.0);
|
||||
api.volumes[0].getMapper().setMaximumSamplesPerRay(2000);
|
||||
|
||||
renderWindow.render();
|
||||
}
|
||||
|
||||
function switchMPRInteractors(api, istyle) {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const renderer = api.genericRenderWindow.getRenderer();
|
||||
const camera = renderer.getActiveCamera();
|
||||
const currentIStyle = renderWindow.getInteractor().getInteractorStyle();
|
||||
|
||||
let currentNormal;
|
||||
if (istyle.getSliceNormal) {
|
||||
currentNormal = istyle.getSliceNormal();
|
||||
if (currentIStyle.getSliceNormal && istyle.getSliceNormal) {
|
||||
currentNormal = currentIStyle.getSliceNormal();
|
||||
}
|
||||
|
||||
// TODO: This is a hacky workaround because disabling the vtkInteractorStyleMPRSlice is currently
|
||||
// broken. The camera.onModified is never removed. (https://github.com/Kitware/vtk-js/issues/1110)
|
||||
renderWindow
|
||||
.getInteractor()
|
||||
.getInteractorStyle()
|
||||
.setInteractor(null);
|
||||
let currentSlabThickness;
|
||||
if (currentIStyle.getSlabThickness && istyle.getSlabThickness) {
|
||||
currentSlabThickness = currentIStyle.getSlabThickness();
|
||||
}
|
||||
|
||||
renderWindow.getInteractor().setInteractorStyle(istyle);
|
||||
|
||||
@@ -126,6 +128,11 @@ function switchMPRInteractors(api, istyle) {
|
||||
if (currentNormal) {
|
||||
istyle.setSliceNormal(currentNormal);
|
||||
}
|
||||
|
||||
if (currentSlabThickness) {
|
||||
istyle.setSlabThickness(currentSlabThickness);
|
||||
}
|
||||
|
||||
istyle.setVolumeMapper(api.volumes[0]);
|
||||
}
|
||||
}
|
||||
@@ -152,14 +159,14 @@ const actions = {
|
||||
|
||||
_setView(api, [0, 1, 0], [0, 0, 1]);
|
||||
},
|
||||
enableRotateTool: async ({ viewports }) => {
|
||||
enableRotateTool: () => {
|
||||
apis.forEach(api => {
|
||||
const istyle = vtkInteractorStyleMPRSlice.newInstance();
|
||||
|
||||
switchMPRInteractors(api, istyle);
|
||||
});
|
||||
},
|
||||
enableCrosshairsTool: async ({ viewports }) => {
|
||||
enableCrosshairsTool: () => {
|
||||
apis.forEach((api, index) => {
|
||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||
|
||||
@@ -168,13 +175,61 @@ const actions = {
|
||||
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
||||
});
|
||||
},
|
||||
enableLevelTool: async ({ viewports }) => {
|
||||
enableLevelTool: () => {
|
||||
apis.forEach(api => {
|
||||
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
||||
|
||||
switchMPRInteractors(api, istyle);
|
||||
});
|
||||
},
|
||||
setSlabThickness: slabThickness => {
|
||||
currentSlabThickness = slabThickness;
|
||||
|
||||
apis.forEach(api => {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
||||
|
||||
if (istyle.setSlabThickness) {
|
||||
istyle.setSlabThickness(currentSlabThickness);
|
||||
|
||||
// TODO: Do this inside the interactors in a setSlabThickness function instead
|
||||
const renderer = api.genericRenderWindow.getRenderer();
|
||||
const camera = renderer.getActiveCamera();
|
||||
const dist = camera.getDistance();
|
||||
const near = dist - currentSlabThickness / 2;
|
||||
const far = dist + currentSlabThickness / 2;
|
||||
|
||||
camera.setClippingRange(near, far);
|
||||
}
|
||||
|
||||
renderWindow.render();
|
||||
});
|
||||
},
|
||||
changeSlabThickness: ({ change }) => {
|
||||
currentSlabThickness += change;
|
||||
currentSlabThickness = Math.max(currentSlabThickness, 0.1);
|
||||
|
||||
apis.forEach(api => {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
||||
|
||||
if (istyle.setSlabThickness) {
|
||||
istyle.setSlabThickness(currentSlabThickness);
|
||||
}
|
||||
|
||||
renderWindow.render();
|
||||
});
|
||||
},
|
||||
setBlendMode: ({ blendMode }) => {
|
||||
apis.forEach(api => {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
||||
|
||||
api.volumes[0].getMapper().setBlendMode(blendMode);
|
||||
|
||||
renderWindow.render();
|
||||
});
|
||||
},
|
||||
mpr2d: async ({ viewports }) => {
|
||||
const displaySet =
|
||||
viewports.viewportSpecificData[viewports.activeViewportIndex];
|
||||
@@ -199,20 +254,11 @@ const actions = {
|
||||
});
|
||||
});*/
|
||||
|
||||
apis[0].volumes[0].getMapper().setSampleDistance(1.5);
|
||||
|
||||
apiByViewport.forEach((api, index) => {
|
||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||
const renderer = api.genericRenderWindow.getRenderer();
|
||||
const camera = renderer.getActiveCamera();
|
||||
|
||||
// TODO: This is a hacky workaround because disabling the vtkInteractorStyleMPRSlice is currently
|
||||
// broken. The camera.onModified is never removed. (https://github.com/Kitware/vtk-js/issues/1110)
|
||||
renderWindow
|
||||
.getInteractor()
|
||||
.getInteractorStyle()
|
||||
.setInteractor(null);
|
||||
|
||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||
renderWindow.getInteractor().setInteractorStyle(istyle);
|
||||
|
||||
@@ -258,6 +304,8 @@ const actions = {
|
||||
},
|
||||
};
|
||||
|
||||
window.vtkActions = actions;
|
||||
|
||||
const definitions = {
|
||||
axial: {
|
||||
commandFn: actions.axial,
|
||||
@@ -276,19 +324,59 @@ const definitions = {
|
||||
},
|
||||
enableRotateTool: {
|
||||
commandFn: actions.enableRotateTool,
|
||||
storeContexts: ['viewports'],
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
enableCrosshairsTool: {
|
||||
commandFn: actions.enableCrosshairsTool,
|
||||
storeContexts: ['viewports'],
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
enableLevelTool: {
|
||||
commandFn: actions.enableLevelTool,
|
||||
storeContexts: ['viewports'],
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
setBlendModeToComposite: {
|
||||
commandFn: actions.setBlendMode,
|
||||
storeContexts: [],
|
||||
options: { blendMode: BlendMode.COMPOSITE_BLEND },
|
||||
},
|
||||
setBlendModeToMaximumIntensity: {
|
||||
commandFn: actions.setBlendMode,
|
||||
storeContexts: [],
|
||||
options: { blendMode: BlendMode.MAXIMUM_INTENSITY_BLEND },
|
||||
},
|
||||
setBlendModeToMinimumIntensity: {
|
||||
commandFn: actions.setBlendMode,
|
||||
storeContexts: [],
|
||||
options: { blendMode: BlendMode.MINIMUM_INTENSITY_BLEND },
|
||||
},
|
||||
setBlendModeToAverageIntensity: {
|
||||
commandFn: actions.setBlendMode,
|
||||
storeContexts: [],
|
||||
options: { blendMode: BlendMode.AVERAGE_INTENSITY_BLEND },
|
||||
},
|
||||
setSlabThickness: {
|
||||
// TODO: How do we pass in a function argument?
|
||||
commandFn: actions.setSlabThickness,
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
increaseSlabThickness: {
|
||||
commandFn: actions.changeSlabThickness,
|
||||
storeContexts: [],
|
||||
options: {
|
||||
change: 3,
|
||||
},
|
||||
},
|
||||
decreaseSlabThickness: {
|
||||
commandFn: actions.changeSlabThickness,
|
||||
storeContexts: [],
|
||||
options: {
|
||||
change: -3,
|
||||
},
|
||||
},
|
||||
mpr2d: {
|
||||
commandFn: actions.mpr2d,
|
||||
storeContexts: ['viewports'],
|
||||
|
||||
@@ -31,6 +31,43 @@ const definitions = [
|
||||
commandName: 'enableRotateTool',
|
||||
commandOptions: {},
|
||||
},
|
||||
{
|
||||
id: 'setBlendModeToComposite',
|
||||
label: 'Disable MIP',
|
||||
icon: 'times',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'setBlendModeToComposite',
|
||||
commandOptions: {},
|
||||
},
|
||||
{
|
||||
id: 'setBlendModeToMaximumIntensity',
|
||||
label: 'Enable MIP',
|
||||
icon: 'soft-tissue',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'setBlendModeToMaximumIntensity',
|
||||
commandOptions: {},
|
||||
},
|
||||
|
||||
{
|
||||
id: 'increaseSlabThickness',
|
||||
label: 'Increase Slab Thickness',
|
||||
icon: 'caret-up',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'increaseSlabThickness',
|
||||
commandOptions: {},
|
||||
},
|
||||
{
|
||||
id: 'decreaseSlabThickness',
|
||||
label: 'Decrease Slab Thickness',
|
||||
icon: 'caret-down',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'decreaseSlabThickness',
|
||||
commandOptions: {},
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const autoprefixer = require("autoprefixer");
|
||||
// const vtkHtmlRules = require('vtk.js/Utilities/config/dependency.js').webpack
|
||||
// .core.rules;
|
||||
// Optional if you want to load *.css and *.module.css files
|
||||
// var cssRules = require('vtk.js/Utilities/config/dependency.js').webpack.css.rules;
|
||||
const SRC_DIR = path.join(__dirname, "./src");
|
||||
const OUTPUT_DIR = path.join(__dirname, "./dist");
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`
|
||||
},
|
||||
context: SRC_DIR,
|
||||
// ~~~ MODE
|
||||
output: {
|
||||
path: OUTPUT_DIR,
|
||||
library: "ohifVtkExtension",
|
||||
libraryTarget: "umd",
|
||||
filename: "index.umd.js",
|
||||
auxiliaryComment: "Text VTK Extension Comment"
|
||||
},
|
||||
stats: {
|
||||
colors: true,
|
||||
hash: true,
|
||||
timings: true,
|
||||
assets: true,
|
||||
chunks: true,
|
||||
chunkModules: true,
|
||||
modules: true,
|
||||
children: true,
|
||||
warnings: true
|
||||
},
|
||||
optimization: {
|
||||
minimize: false,
|
||||
sideEffects: true
|
||||
},
|
||||
// ~~~ END MODE
|
||||
resolve: {
|
||||
modules: [
|
||||
path.resolve(__dirname, "node_modules"),
|
||||
path.resolve(__dirname, "../../node_modules"),
|
||||
SRC_DIR
|
||||
],
|
||||
extensions: [".js", ".jsx", ".json", "*"],
|
||||
symlinks: true
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: [/node_modules/],
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
// Find babel.config.js in monorepo root
|
||||
// https://babeljs.io/docs/en/options#rootmode
|
||||
rootMode: "upward",
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
// Do not transform ES6 modules to another format.
|
||||
// Webpack will take care of that.
|
||||
modules: false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /\.module\.css$/,
|
||||
use: [
|
||||
"style-loader",
|
||||
"css-loader",
|
||||
{
|
||||
loader: "postcss-loader",
|
||||
options: {
|
||||
plugins: () => [autoprefixer("last 2 version", "ie >= 10")]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
{
|
||||
test: /\.glsl$/i,
|
||||
include: /vtk\.js[\/\\]Sources/,
|
||||
loader: "shader-loader"
|
||||
},
|
||||
/**
|
||||
*
|
||||
*/
|
||||
{
|
||||
test: /\.worker\.js$/,
|
||||
include: /vtk\.js[\/\\]Sources/,
|
||||
use: [
|
||||
{
|
||||
loader: "worker-loader",
|
||||
options: { inline: true, fallback: false }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
// externals: [
|
||||
// {
|
||||
// cornerstone: {
|
||||
// commonjs: 'cornerstone',
|
||||
// commonjs2: 'cornerstone',
|
||||
// amd: 'cornerstone',
|
||||
// root: 'cornerstone',
|
||||
// },
|
||||
// 'cornerstone-math': {
|
||||
// commonjs: 'cornerstone-math',
|
||||
// commonjs2: 'cornerstone-math',
|
||||
// amd: 'cornerstone-math',
|
||||
// root: 'cornerstoneMath',
|
||||
// },
|
||||
// },
|
||||
// '@ohif/i18n',
|
||||
// '@ohif/core',
|
||||
// 'dcmjs',
|
||||
// 'react-viewerbase',
|
||||
// 'react', //: 'React',
|
||||
// 'react-dom', //: 'ReactDOM',
|
||||
// 'react-redux', //: 'ReactRedux',
|
||||
// 'react-resize-detector', //: 'ReactResizeDetector',
|
||||
// 'react-viewerbase', //: 'reactViewerbase',
|
||||
// 'prop-types', //: 'PropTypes'
|
||||
// /*/\b(vtk.js)/*/
|
||||
// ],
|
||||
};
|
||||
+3
-2
@@ -5,14 +5,15 @@
|
||||
"useWorkspaces": true,
|
||||
"command": {
|
||||
"publish": {
|
||||
"ignoreChanges": ["*.md"],
|
||||
"ignoreChanges": ["*.md", "*.yml", "*.spec.js", "*.test.js"],
|
||||
"message": "chore(release): publish"
|
||||
},
|
||||
"version": {
|
||||
"allowBranch": "master",
|
||||
"conventionalCommits": true,
|
||||
"createRelease": "github",
|
||||
"yes": true,
|
||||
"message": "chore(release): publish %s [skip ci]"
|
||||
"message": "chore(release): publish [skip ci]"
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
-3
@@ -13,6 +13,61 @@ yarn add --dev -W package-name
|
||||
|
||||
- Patch status for PRs (we care about threshold only)
|
||||
- Allows us to use multiproject test runner
|
||||
- Flags per project for merged coverage; easier to break down coverage per project
|
||||
- Remove all-contributors bot; install CLI per project and add per project commands
|
||||
-
|
||||
- Flags per project for merged coverage; easier to break down coverage per
|
||||
project
|
||||
- Remove all-contributors bot; install CLI per project and add per project
|
||||
commands
|
||||
- Verify all have "dev:package-name" command
|
||||
- Should we include `package.json` and `src` as output? (build from ESM)
|
||||
- Should we remove `dev:package-name` commands and suggest navigating to
|
||||
package root?
|
||||
- Fix broken peer dependencies?
|
||||
- Lingering core-js resolution issues when building for Viewer PWA (from project
|
||||
directory)
|
||||
|
||||
## ORBS
|
||||
|
||||
- Slack: https://circleci.com/orbs/registry/orb/circleci/slack
|
||||
- GCP: https://circleci.com/orbs/registry/orb/circleci/gcp-cli
|
||||
- Browser Tools: https://circleci.com/orbs/registry/orb/circleci/browser-tools
|
||||
- Multi-Repo: https://circleci.com/orbs/registry/orb/dnephin/multirepo
|
||||
- PR Comment: https://circleci.com/orbs/registry/orb/benjlevesque/pr-comment
|
||||
|
||||
Debug Note: `http://localhost:3000/webpack-dev-server`
|
||||
|
||||
NOTES:
|
||||
|
||||
# bumps pre-release version of all packages if any changes
|
||||
|
||||
- `npx lerna version prerelease`
|
||||
|
||||
# Releases based on package.json differences from NPM
|
||||
|
||||
- `npx lerna publish from-package --dist-tag canary`
|
||||
|
||||
Use Env options to set config:
|
||||
|
||||
- https://webpack.js.org/api/cli/#environment-options
|
||||
|
||||
## Bundling
|
||||
|
||||
- Extensions must bundle all assets into a single file for UMD
|
||||
- The umd build for Viewer must bundle all of it's assets, and baked-in
|
||||
extension assets
|
||||
- The "skinny" umd build for viewer only needs to bundle it's own assets
|
||||
- The PWA build for Viewer can code-split to it's heart's content
|
||||
- Don't load/bundle a font at any layer other than Application/Viewer
|
||||
- It gets to decide if it should be bundled/cached/external
|
||||
|
||||
* For ESM/Module builds
|
||||
* Not dissimilar from VTK.js consumption
|
||||
* jest --watchAll for individual packages?
|
||||
|
||||
WebPack 5: https://github.com/webpack/webpack/issues/6386#issue-291757876
|
||||
|
||||
Flattened ESM: Less efficient, not as great at code splitting? From source? More
|
||||
loaders, added complexity, slower builds, better output
|
||||
|
||||
Notes:
|
||||
|
||||
- Remove preBuild step in viewer after @3 wado image loader
|
||||
+7
-3
@@ -3,6 +3,10 @@
|
||||
# TOML Reference:
|
||||
# https://www.netlify.com/docs/netlify-toml-reference/
|
||||
#
|
||||
# We use Netlify for deploy previews and deploy previews only.
|
||||
# https://viewer.ohif.org is created using a different process that is
|
||||
# managed by CircleCI and deployed to our Google Hosting
|
||||
#
|
||||
|
||||
# Settings in the [build] context are global and are applied to all contexts
|
||||
# unless otherwise overridden by more specific contexts.
|
||||
@@ -33,7 +37,7 @@
|
||||
# inherit these settings.
|
||||
[context.deploy-preview]
|
||||
base = ""
|
||||
publish = "docs/latest/_book/"
|
||||
publish = ".netlify/www/"
|
||||
command = "yarn config set workspaces-experimental true && chmod +x .netlify/build-deploy-preview.sh && .netlify/build-deploy-preview.sh"
|
||||
|
||||
# Redirects and headers are GLOBAL for all builds – they do not get scoped to
|
||||
@@ -51,8 +55,8 @@
|
||||
status = 200
|
||||
|
||||
[[redirects]]
|
||||
from = "/demo/*"
|
||||
to = "/demo/index.html"
|
||||
from = "/pwa/*"
|
||||
to = "/pwa/index.html"
|
||||
status = 200
|
||||
|
||||
[[headers]]
|
||||
|
||||
+31
-24
@@ -6,21 +6,30 @@
|
||||
"extensions/*"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^10.0.0",
|
||||
"npm": "^6.0.0",
|
||||
"yarn": "^1.16.0"
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"scripts": {
|
||||
"cm": "npx git-cz",
|
||||
"dev": ".scripts/dev.sh",
|
||||
"build": "lerna run build:viewer",
|
||||
"build:package": "lerna run build:package --parallel",
|
||||
"dev": "lerna run dev:viewer --stream",
|
||||
"dev:project": ".scripts/dev.sh",
|
||||
"build": "lerna run build:viewer --stream",
|
||||
"build:ci": "lerna run build:viewer:ci --stream",
|
||||
"build:package": "lerna run build:viewer:package --stream",
|
||||
"build:package-all": "lerna run build:package --parallel --stream",
|
||||
"test": "yarn run test:unit && yarn run test:e2e:ci",
|
||||
"test:unit": "jest --collectCoverage",
|
||||
"test:unit:ci": "lerna run test:unit:ci --parallel",
|
||||
"test:unit:ci": "lerna run test:unit:ci --parallel --stream",
|
||||
"test:e2e:ci": "cd platform/viewer && yarn run test:e2e:ci",
|
||||
"see-changed": "lerna changed",
|
||||
"publish": "lerna publish --canary --dist-tag test"
|
||||
"release": "yarn run lerna:version && yarn run lerna:publish",
|
||||
"lerna:version": "npx lerna version prerelease --force-publish",
|
||||
"lerna:publish": "lerna publish from-package --canary --dist-tag canary"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"core-js": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.0",
|
||||
@@ -30,18 +39,23 @@
|
||||
"@babel/plugin-transform-runtime": "^7.5.0",
|
||||
"@babel/preset-env": "^7.5.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"babel-eslint": "^10.0.2",
|
||||
"babel-eslint": "9.x",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-inline-react-svg": "1.1.0",
|
||||
"babel-plugin-module-resolver": "^3.2.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"copy-webpack-plugin": "^5.0.3",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^3.0.0",
|
||||
"eslint": "^6.0.1",
|
||||
"eslint-plugin-import": "^2.18.0",
|
||||
"css-loader": "^3.2.0",
|
||||
"eslint": "5.16.0",
|
||||
"eslint-config-react-app": "^4.0.1",
|
||||
"eslint-plugin-flowtype": "2.x",
|
||||
"eslint-plugin-import": "2.x",
|
||||
"eslint-plugin-jsx-a11y": "6.x",
|
||||
"eslint-plugin-node": "^9.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.14.2",
|
||||
"eslint-plugin-react": "7.x",
|
||||
"eslint-plugin-react-hooks": "1.5.0",
|
||||
"extract-css-chunks-webpack-plugin": "^4.5.4",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"husky": "^3.0.0",
|
||||
@@ -56,9 +70,10 @@
|
||||
"prettier": "^1.18.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "^3.0.1",
|
||||
"react-hot-loader": "^4.12.11",
|
||||
"redux-testkit": "^1.0.6",
|
||||
"shader-loader": "^1.3.1",
|
||||
"style-loader": "^1.0.0",
|
||||
"stylelint": "^10.1.0",
|
||||
"stylelint-config-recommended": "^2.2.0",
|
||||
"stylus": "^0.54.5",
|
||||
@@ -71,16 +86,8 @@
|
||||
"workbox-webpack-plugin": "^4.3.1",
|
||||
"worker-loader": "^2.0.0"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,json,md,yml}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
"resolutions": {
|
||||
"**/@babel/runtime": "7.5.5"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
|
||||
@@ -59,6 +59,34 @@
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "igoroctaviano",
|
||||
"name": "Igor Octaviano",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/13886933?v=4",
|
||||
"profile": "http://igoroctaviano.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dlwire",
|
||||
"name": "David Wire",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/1167291?v=4",
|
||||
"profile": "https://github.com/dlwire",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pavertomato",
|
||||
"name": "Egor Lezhnin",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/878990?v=4",
|
||||
"profile": "http://egor.lezhn.in",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const common = require('./webpack.common');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.common.js');
|
||||
const pkg = require('./../package.json');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './..');
|
||||
@@ -8,19 +8,21 @@ const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
const commonConfig = common(env, argv);
|
||||
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
||||
|
||||
return merge(commonConfig, {
|
||||
// https://webpack.js.org/configuration/mode/#mode-production
|
||||
mode: 'production',
|
||||
devtool: 'source-map',
|
||||
stats: {
|
||||
colors: false,
|
||||
colors: true,
|
||||
hash: true,
|
||||
timings: true,
|
||||
assets: true,
|
||||
chunks: true,
|
||||
chunkModules: true,
|
||||
modules: true,
|
||||
children: true,
|
||||
chunks: false,
|
||||
chunkModules: false,
|
||||
modules: false,
|
||||
children: false,
|
||||
warnings: true,
|
||||
},
|
||||
optimization: {
|
||||
@@ -32,7 +34,6 @@ module.exports = (env, argv) => {
|
||||
library: 'ohifCore',
|
||||
libraryTarget: 'umd',
|
||||
filename: pkg.main,
|
||||
auxiliaryComment: 'Test Comment',
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,95 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.50.2](https://github.com/OHIF/Viewers/compare/@ohif/core@0.50.1...@ohif/core@0.50.2) (2019-08-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.50.1](https://github.com/OHIF/Viewers/compare/@ohif/core@0.50.0-alpha.10...@ohif/core@0.50.1) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.50.0-alpha.10](https://github.com/OHIF/Viewers/compare/@ohif/core@0.11.1-alpha.9...@ohif/core@0.50.0-alpha.10) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.11.1-alpha.9](https://github.com/OHIF/Viewers/compare/@ohif/core@0.11.1-alpha.8...@ohif/core@0.11.1-alpha.9) (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.11.1-alpha.8 (2019-08-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.11.1-alpha.7](https://github.com/OHIF/Viewers/compare/@ohif/core@0.11.1-alpha.6...@ohif/core@0.11.1-alpha.7) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.11.1-alpha.6](https://github.com/OHIF/Viewers/compare/@ohif/core@0.11.1-alpha.5...@ohif/core@0.11.1-alpha.6) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.11.1-alpha.5](https://github.com/OHIF/Viewers/compare/@ohif/core@0.11.1-alpha.4...@ohif/core@0.11.1-alpha.5) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.11.1-alpha.4](https://github.com/OHIF/Viewers/compare/@ohif/core@0.11.1-alpha.3...@ohif/core@0.11.1-alpha.4) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.11.1-alpha.3](https://github.com/OHIF/Viewers/compare/@ohif/core@0.11.1-alpha.2...@ohif/core@0.11.1-alpha.3) (2019-08-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
|
||||
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return {
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
context: SRC_DIR,
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.json', '*'],
|
||||
symlinks: true,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: [/node_modules/],
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
// Find babel.config.js in monorepo root
|
||||
// https://babeljs.io/docs/en/options#rootmode
|
||||
rootMode: 'upward',
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
// Do not transform ES6 modules to another format.
|
||||
// Webpack will take care of that.
|
||||
modules: false,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin(['NODE_ENV']),
|
||||
new ExtractCssChunks({
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
// hot: true /* only necessary if hot reloading not function*/
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
+18
-19
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/core",
|
||||
"version": "0.11.1-alpha.2",
|
||||
"version": "0.50.2",
|
||||
"description": "Generic business logic for web-based medical imaging applications",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
@@ -11,40 +11,39 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "yarn run dev",
|
||||
"dev": "jest --watch",
|
||||
"dev": "jest --watchAll",
|
||||
"dev:core": "yarn run dev",
|
||||
"test:unit": "jest --watch",
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage",
|
||||
"build": "webpack --config config/webpack.prod.js -d",
|
||||
"build": "webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build",
|
||||
"lint": "eslint -c .eslintrc --fix src && prettier --single-quote --write src/**/*.{js,jsx,json,css}",
|
||||
"prepublishOnly": "yarn run build"
|
||||
"prepublishOnly": "yarn run build",
|
||||
"start": "yarn run dev",
|
||||
"test:unit": "jest --watchAll",
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-tools": "^3.9.0",
|
||||
"cornerstone-wado-image-loader": "^2.2.3",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dicom-parser": "^1.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"ajv": "^6.10.0",
|
||||
"dicomweb-client": "^0.4.2",
|
||||
"dicomweb-client": "^0.5.2",
|
||||
"isomorphic-base64": "^1.0.2",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.merge": "^4.6.1",
|
||||
"mousetrap": "^1.6.3",
|
||||
"validate.js": "^0.12.0"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"gitHead": "a5baa9228c0eda0df880136bde4420d78e6f8706"
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ export default function getAttribute(element, defaultValue) {
|
||||
|
||||
function convertToInt(input) {
|
||||
function padFour(input) {
|
||||
var l = input.length;
|
||||
const l = input.length;
|
||||
|
||||
if (l == 0) return '0000';
|
||||
if (l == 1) return '000' + input;
|
||||
@@ -33,9 +33,9 @@ function convertToInt(input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
var output = '';
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
for (var j = 0; j < input[i].length; j++) {
|
||||
let output = '';
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
for (let j = 0; j < input[i].length; j++) {
|
||||
output += padFour(input[i].charCodeAt(j).toString(16));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import getAttribute from './getAttribute';
|
||||
|
||||
describe('getAttribute', () => {
|
||||
it('should return a default value if element is null or undefined', () => {
|
||||
const defaultValue = '0000';
|
||||
const nullElement = null;
|
||||
const undefinedElement = undefined;
|
||||
|
||||
expect(getAttribute(nullElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getAttribute(undefinedElement, defaultValue)).toEqual(defaultValue);
|
||||
});
|
||||
|
||||
it('should return a default value if element.Value is null, undefined or not present', () => {
|
||||
const defaultValue = '0000';
|
||||
const nullElement = {
|
||||
id: 0,
|
||||
Value: null,
|
||||
};
|
||||
const undefinedElement = {
|
||||
id: 0,
|
||||
Value: undefined,
|
||||
};
|
||||
const noValuePresentElement = {
|
||||
id: 0,
|
||||
};
|
||||
|
||||
expect(getAttribute(nullElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getAttribute(undefinedElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getAttribute(noValuePresentElement, defaultValue)).toEqual(
|
||||
defaultValue
|
||||
);
|
||||
});
|
||||
|
||||
it('should return 48 for element with value 0', () => {
|
||||
const returnValue = 48;
|
||||
const element = {
|
||||
Value: '0',
|
||||
};
|
||||
expect(getAttribute(element, null)).toEqual(returnValue);
|
||||
});
|
||||
|
||||
it('should return 3211313 for element with value 11', () => {
|
||||
const returnValue = 3211313;
|
||||
const element = {
|
||||
Value: '11',
|
||||
};
|
||||
expect(getAttribute(element, null)).toEqual(returnValue);
|
||||
});
|
||||
|
||||
it('should return 2.4923405222191973e+35 for element with value 00280009', () => {
|
||||
const returnValue = 2.4923405222191973e35;
|
||||
const element = {
|
||||
id: 0,
|
||||
Value: '00280009',
|
||||
};
|
||||
expect(getAttribute(element, null)).toEqual(returnValue);
|
||||
});
|
||||
|
||||
it('should return 2949169 for element with value -1', () => {
|
||||
const returnValue = 2949169;
|
||||
const element = {
|
||||
id: 0,
|
||||
Value: '-1',
|
||||
};
|
||||
expect(getAttribute(element, null)).toEqual(returnValue);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
import getAuthorizationHeader from './getAuthorizationHeader';
|
||||
import user from './../user';
|
||||
|
||||
jest.mock('./../user.js');
|
||||
|
||||
describe('getAuthorizationHeader', () => {
|
||||
it('should return a HTTP Basic Auth when server contains requestOptions.auth', () => {
|
||||
const validServer = {
|
||||
requestOptions: {
|
||||
auth: {
|
||||
user: 'dummy_user',
|
||||
password: 'dummy_password',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const expectedAuthorizationHeader = {
|
||||
Authorization: `Basic ${btoa(validServer.requestOptions.auth)}`,
|
||||
};
|
||||
|
||||
const authentication = getAuthorizationHeader(validServer);
|
||||
|
||||
expect(authentication).toEqual(expectedAuthorizationHeader);
|
||||
});
|
||||
|
||||
it('should return a HTTP Basic Auth when server contains requestOptions.auth even though there is no password', () => {
|
||||
const validServerWithoutPassword = {
|
||||
requestOptions: {
|
||||
auth: {
|
||||
user: 'dummy_user',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const expectedAuthorizationHeader = {
|
||||
Authorization: `Basic ${btoa(
|
||||
validServerWithoutPassword.requestOptions.auth
|
||||
)}`,
|
||||
};
|
||||
|
||||
const authentication = getAuthorizationHeader(validServerWithoutPassword);
|
||||
|
||||
expect(authentication).toEqual(expectedAuthorizationHeader);
|
||||
});
|
||||
|
||||
it('should return a HTTP Basic Auth when server contains requestOptions.auth even though there is no username', () => {
|
||||
const validServerWithoutPassword = {
|
||||
requestOptions: {
|
||||
auth: {
|
||||
user: 'dummy_user',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const expectedAuthorizationHeader = {
|
||||
Authorization: `Basic ${btoa(
|
||||
validServerWithoutPassword.requestOptions.auth
|
||||
)}`,
|
||||
};
|
||||
|
||||
const authentication = getAuthorizationHeader(validServerWithoutPassword);
|
||||
|
||||
expect(authentication).toEqual(expectedAuthorizationHeader);
|
||||
});
|
||||
|
||||
it('should return an empty object when there is no either server.requestOptions.auth or accessToken', () => {
|
||||
const authentication = getAuthorizationHeader({});
|
||||
|
||||
expect(authentication).toEqual({});
|
||||
});
|
||||
|
||||
it('should return an Authorization with accessToken when server is not defined and there is an accessToken', () => {
|
||||
user.getAccessToken.mockImplementationOnce(() => 'MOCKED_TOKEN');
|
||||
|
||||
const authentication = getAuthorizationHeader({});
|
||||
const exptecteHeaderBasedOnUserAccessToekn = {
|
||||
Authorization: 'Bearer MOCKED_TOKEN',
|
||||
};
|
||||
|
||||
expect(authentication).toEqual(exptecteHeaderBasedOnUserAccessToekn);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
export default function getModalities(modality, modalitiesInStudy) {
|
||||
var modalities = {};
|
||||
let modalities = {};
|
||||
if (modality) {
|
||||
modalities = modality;
|
||||
}
|
||||
@@ -7,8 +7,8 @@ export default function getModalities(modality, modalitiesInStudy) {
|
||||
if (modalitiesInStudy) {
|
||||
// Find vr in modalities
|
||||
if (modalities.vr && modalities.vr === modalitiesInStudy.vr) {
|
||||
for (var i = 0; i < modalitiesInStudy.Value.length; i++) {
|
||||
var value = modalitiesInStudy.Value[i];
|
||||
for (let i = 0; i < modalitiesInStudy.Value.length; i++) {
|
||||
const value = modalitiesInStudy.Value[i];
|
||||
if (modalities.Value.indexOf(value) === -1) {
|
||||
modalities.Value.push(value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import getModalities from './getModalities';
|
||||
|
||||
describe('getModalities', () => {
|
||||
test('should return an empty object when modality and modalitiesInStudy are not present', () => {
|
||||
const modality = null;
|
||||
const modalitiesInStudy = null;
|
||||
|
||||
expect(getModalities(modality, modalitiesInStudy)).toEqual({});
|
||||
});
|
||||
|
||||
test('should return an empty object when modality and modalitiesInStudy are not present', () => {
|
||||
const modality = null;
|
||||
const modalitiesInStudy = null;
|
||||
|
||||
expect(getModalities(modality, modalitiesInStudy)).toEqual({});
|
||||
});
|
||||
|
||||
test('should return modalities in Study when modality is not defined', () => {
|
||||
const modality = null;
|
||||
|
||||
const modalitiesInStudy = {
|
||||
Value: ['MOCKED_VALUE'],
|
||||
vr: 'MOCKED_VALUE',
|
||||
};
|
||||
|
||||
expect(getModalities(modality, modalitiesInStudy)).toEqual(
|
||||
modalitiesInStudy
|
||||
);
|
||||
});
|
||||
|
||||
test('should return only the modalitues that exists in modalitiesInStudy', () => {
|
||||
const modality = {
|
||||
Value: ['DESIRED_VALUE'],
|
||||
vr: 'DESIRED_VR',
|
||||
};
|
||||
|
||||
const modalitiesInStudy = {
|
||||
Value: ['DESIRED_VALUE', 'NOT_DESIRED_VALUE'],
|
||||
vr: 'DESIRED_VR',
|
||||
};
|
||||
|
||||
expect(getModalities(modality, modalitiesInStudy)).toEqual(modality);
|
||||
});
|
||||
|
||||
test('should return the seek modality when the desired modality does not exist in modalitiesInStudy', () => {
|
||||
const modality = {
|
||||
Value: ['DESIRED_VALUE'],
|
||||
vr: 'DESIRED_VR',
|
||||
};
|
||||
|
||||
const modalitiesInStudy = {
|
||||
Value: ['NOT_DESIRED_VALUE'],
|
||||
vr: 'DESIRED_VR',
|
||||
};
|
||||
|
||||
expect(getModalities(modality, modalitiesInStudy)).toEqual(modality);
|
||||
});
|
||||
|
||||
test('should return the seek modality when the desired modality does not exist in modalitiesInStudy VR', () => {
|
||||
const modality = {
|
||||
Value: ['DESIRED_VALUE'],
|
||||
vr: 'DESIRED_VR',
|
||||
};
|
||||
|
||||
const modalitiesInStudy = {
|
||||
Value: ['NOT_DESIRED_VALUE'],
|
||||
vr: 'ANOTHER_VR',
|
||||
};
|
||||
|
||||
expect(getModalities(modality, modalitiesInStudy)).toEqual(
|
||||
modalitiesInStudy
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
import getName from './getName';
|
||||
|
||||
describe('getName', () => {
|
||||
it('should return a default value if element is null or undefined', () => {
|
||||
const defaultValue = 'DEFAULT_NAME';
|
||||
const nullElement = null;
|
||||
const undefinedElement = undefined;
|
||||
|
||||
expect(getName(nullElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getName(undefinedElement, defaultValue)).toEqual(defaultValue);
|
||||
});
|
||||
|
||||
it('should return a default value if element.Value is null, undefined or not present', () => {
|
||||
const defaultValue = 'DEFAULT_NAME';
|
||||
const nullElement = {
|
||||
id: 0,
|
||||
Value: null,
|
||||
};
|
||||
const undefinedElement = {
|
||||
id: 0,
|
||||
Value: undefined,
|
||||
};
|
||||
const noValuePresentElement = {
|
||||
id: 0,
|
||||
};
|
||||
|
||||
expect(getName(nullElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getName(undefinedElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getName(noValuePresentElement, defaultValue)).toEqual(defaultValue);
|
||||
});
|
||||
|
||||
it('should return A for element when Alphabetic is [A, B, C, D]', () => {
|
||||
const returnValue = 'A';
|
||||
const element = {
|
||||
Value: [
|
||||
{ Alphabetic: 'A' },
|
||||
{ Alphabetic: 'B' },
|
||||
{ Alphabetic: 'C' },
|
||||
{ Alphabetic: 'D' },
|
||||
],
|
||||
};
|
||||
expect(getName(element, null)).toEqual(returnValue);
|
||||
});
|
||||
|
||||
it('should return FIRST for element when Alphabetic is [FIRST, SECOND]', () => {
|
||||
const returnValue = 'FIRST';
|
||||
const element = {
|
||||
Value: [{ Alphabetic: 'FIRST' }, { Alphabetic: 'SECOND' }],
|
||||
};
|
||||
expect(getName(element, null)).toEqual(returnValue);
|
||||
});
|
||||
|
||||
it('should return element.value[0] for element with not Alphabetic and when there is at least on element.Value', () => {
|
||||
const returnValue = {
|
||||
anyOtherProperty: 'FIRST',
|
||||
};
|
||||
const element = {
|
||||
Value: [{ anyOtherProperty: 'FIRST' }, { Alphabetic: 'SECOND' }],
|
||||
};
|
||||
expect(getName(element, null)).toEqual(returnValue);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import getNumber from './getNumber';
|
||||
|
||||
describe('getNumber', () => {
|
||||
it('should return a default value if element is null or undefined', () => {
|
||||
const defaultValue = 1.0;
|
||||
const nullElement = null;
|
||||
const undefinedElement = undefined;
|
||||
|
||||
expect(getNumber(nullElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getNumber(undefinedElement, defaultValue)).toEqual(defaultValue);
|
||||
});
|
||||
|
||||
it('should return a default value if element.Value is null, undefined or not present', () => {
|
||||
const defaultValue = 1.0;
|
||||
const nullElement = {
|
||||
id: 0,
|
||||
Value: null,
|
||||
};
|
||||
const undefinedElement = {
|
||||
id: 0,
|
||||
Value: undefined,
|
||||
};
|
||||
const noValuePresentElement = {
|
||||
id: 0,
|
||||
};
|
||||
|
||||
expect(getNumber(nullElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getNumber(undefinedElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getNumber(noValuePresentElement, defaultValue)).toEqual(
|
||||
defaultValue
|
||||
);
|
||||
});
|
||||
|
||||
it('should return 2.0 for element when element.Value[0] = 2', () => {
|
||||
const returnValue = 2.0;
|
||||
const element = {
|
||||
Value: ['2'],
|
||||
};
|
||||
expect(getNumber(element, null)).toEqual(returnValue);
|
||||
});
|
||||
|
||||
it('should return -1.0 for element when element.Value[0] is -1', () => {
|
||||
const returnValue = -1.0;
|
||||
const element = {
|
||||
Value: ['-1'],
|
||||
};
|
||||
expect(getNumber(element, null)).toEqual(returnValue);
|
||||
});
|
||||
|
||||
it('should return -1.0 for element when element.Value is [-1, 2, 5, -10] ', () => {
|
||||
const returnValue = -1.0;
|
||||
const element = {
|
||||
Value: ['-1', '2', '5', '-10'],
|
||||
};
|
||||
expect(getNumber(element, null)).toEqual(returnValue);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import getString from './getString';
|
||||
|
||||
describe('getString', () => {
|
||||
it('should return a default value if element is null or undefined', () => {
|
||||
const defaultValue = ['A', 'B', 'C'].join('\\');
|
||||
const nullElement = null;
|
||||
const undefinedElement = undefined;
|
||||
|
||||
expect(getString(nullElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getString(undefinedElement, defaultValue)).toEqual(defaultValue);
|
||||
});
|
||||
|
||||
it('should return a default value if element.Value is null, undefined or not present', () => {
|
||||
const defaultValue = ['A', 'B', 'C'].join('\\');
|
||||
const nullElement = {
|
||||
id: 0,
|
||||
Value: null,
|
||||
};
|
||||
const undefinedElement = {
|
||||
id: 0,
|
||||
Value: undefined,
|
||||
};
|
||||
const noValuePresentElement = {
|
||||
id: 0,
|
||||
};
|
||||
|
||||
expect(getString(nullElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getString(undefinedElement, defaultValue)).toEqual(defaultValue);
|
||||
expect(getString(noValuePresentElement, defaultValue)).toEqual(
|
||||
defaultValue
|
||||
);
|
||||
});
|
||||
|
||||
it('should return A,B,C,D for element when element.Value[0] = [A, B, C, D]', () => {
|
||||
const returnValue = ['A', 'B', 'C'].join('\\');
|
||||
const element = {
|
||||
Value: ['A', 'B', 'C'],
|
||||
};
|
||||
expect(getString(element, null)).toEqual(returnValue);
|
||||
});
|
||||
|
||||
it('should return 1,4,5,6 for element when element.Value[0] is [1, 4, 5, 6]', () => {
|
||||
const returnValue = [1, 4, 5, 6].join('\\');
|
||||
const element = {
|
||||
Value: [1, 4, 5, 6],
|
||||
};
|
||||
expect(getString(element, null)).toEqual(returnValue);
|
||||
});
|
||||
|
||||
it('should return A,1,3,R,7,-1 for element when element.Value is [-1, 2, 5, -10] ', () => {
|
||||
const returnValue = ['A', '1', '3', 'R', '7', '-1'].join('\\');
|
||||
const element = {
|
||||
Value: ['A', '1', '3', 'R', '7', '-1'],
|
||||
};
|
||||
expect(getString(element, null)).toEqual(returnValue);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import * as DICOMWeb from './index.js';
|
||||
|
||||
describe('Top level exports', () => {
|
||||
test('should export the modules getAttribute, getAuthorizationHeader, getModalities, getName, getNumber, getString', () => {
|
||||
const expectedExports = [
|
||||
'getAttribute',
|
||||
'getAuthorizationHeader',
|
||||
'getModalities',
|
||||
'getName',
|
||||
'getNumber',
|
||||
'getString',
|
||||
].sort();
|
||||
|
||||
const exports = Object.keys(DICOMWeb.default).sort();
|
||||
|
||||
expect(exports).toEqual(expectedExports);
|
||||
});
|
||||
});
|
||||
@@ -18,8 +18,6 @@ import { StudyMetadataSource } from './StudyMetadataSource';
|
||||
import { StudyPrefetcher } from './StudyPrefetcher';
|
||||
import { TypeSafeCollection } from './TypeSafeCollection';
|
||||
|
||||
//import { StudySummary } from './metadata/StudySummary';
|
||||
|
||||
export {
|
||||
OHIFStudyMetadataSource,
|
||||
MetadataProvider,
|
||||
@@ -27,7 +25,7 @@ export {
|
||||
HotkeysManager,
|
||||
ImageSet,
|
||||
StudyPrefetcher,
|
||||
//StudyLoadingListener,
|
||||
StudyLoadingListener,
|
||||
StackLoadingListener,
|
||||
DICOMFileLoadingListener,
|
||||
StudyMetadata,
|
||||
|
||||
@@ -635,7 +635,17 @@ function _getDisplaySetFromSopClassModule(
|
||||
headers,
|
||||
});
|
||||
|
||||
return plugin.getDisplaySetFromSeries(series, study, dicomWebClient, headers);
|
||||
let displaySet = plugin.getDisplaySetFromSeries(
|
||||
series,
|
||||
study,
|
||||
dicomWebClient,
|
||||
headers
|
||||
);
|
||||
if (displaySet && !displaySet.modality) {
|
||||
const instance = series.getFirstInstance();
|
||||
displaySet.modality = instance.getRawValue('x00080060');
|
||||
}
|
||||
return displaySet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,458 +0,0 @@
|
||||
var dialogPolyfill = (function() {
|
||||
var supportCustomEvent = window.CustomEvent;
|
||||
if (!supportCustomEvent || typeof supportCustomEvent == 'object') {
|
||||
supportCustomEvent = function CustomEvent(event, x) {
|
||||
x = x || {};
|
||||
var ev = document.createEvent('CustomEvent');
|
||||
ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);
|
||||
return ev;
|
||||
};
|
||||
supportCustomEvent.prototype = window.Event.prototype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the nearest <dialog> from the passed element.
|
||||
*
|
||||
* @param {Element} el to search from
|
||||
* @param {HTMLDialogElement} dialog found
|
||||
*/
|
||||
function findNearestDialog(el) {
|
||||
while (el) {
|
||||
if (el.nodeName == 'DIALOG') {
|
||||
return el;
|
||||
}
|
||||
el = el.parentElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var dialogPolyfill = {};
|
||||
|
||||
dialogPolyfill.reposition = function(element) {
|
||||
var scrollTop =
|
||||
document.body.scrollTop || document.documentElement.scrollTop;
|
||||
var topValue = scrollTop + (window.innerHeight - element.offsetHeight) / 2;
|
||||
element.style.top = Math.max(0, topValue) + 'px';
|
||||
element.dialogPolyfillInfo.isTopOverridden = true;
|
||||
};
|
||||
|
||||
dialogPolyfill.inNodeList = function(nodeList, node) {
|
||||
for (var i = 0; i < nodeList.length; ++i) {
|
||||
if (nodeList[i] == node) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
dialogPolyfill.isInlinePositionSetByStylesheet = function(element) {
|
||||
for (var i = 0; i < document.styleSheets.length; ++i) {
|
||||
var styleSheet = document.styleSheets[i];
|
||||
var cssRules = null;
|
||||
// Some browsers throw on cssRules.
|
||||
try {
|
||||
cssRules = styleSheet.cssRules;
|
||||
} catch (e) {}
|
||||
if (!cssRules) continue;
|
||||
for (var j = 0; j < cssRules.length; ++j) {
|
||||
var rule = cssRules[j];
|
||||
var selectedNodes = null;
|
||||
// Ignore errors on invalid selector texts.
|
||||
try {
|
||||
selectedNodes = document.querySelectorAll(rule.selectorText);
|
||||
} catch (e) {}
|
||||
if (
|
||||
!selectedNodes ||
|
||||
!dialogPolyfill.inNodeList(selectedNodes, element)
|
||||
)
|
||||
continue;
|
||||
var cssTop = rule.style.getPropertyValue('top');
|
||||
var cssBottom = rule.style.getPropertyValue('bottom');
|
||||
if ((cssTop && cssTop != 'auto') || (cssBottom && cssBottom != 'auto'))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
dialogPolyfill.needsCentering = function(dialog) {
|
||||
var computedStyle = window.getComputedStyle(dialog);
|
||||
if (computedStyle.position != 'absolute') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We must determine whether the top/bottom specified value is non-auto. In
|
||||
// WebKit/Blink, checking computedStyle.top == 'auto' is sufficient, but
|
||||
// Firefox returns the used value. So we do this crazy thing instead: check
|
||||
// the inline style and then go through CSS rules.
|
||||
if (
|
||||
(dialog.style.top != 'auto' && dialog.style.top != '') ||
|
||||
(dialog.style.bottom != 'auto' && dialog.style.bottom != '')
|
||||
)
|
||||
return false;
|
||||
return !dialogPolyfill.isInlinePositionSetByStylesheet(dialog);
|
||||
};
|
||||
|
||||
dialogPolyfill.showDialog = function(isModal) {
|
||||
if (this.open) {
|
||||
throw 'InvalidStateError: showDialog called on open dialog';
|
||||
}
|
||||
this.open = true; // TODO: should be a getter mapped to attribute
|
||||
this.setAttribute('open', 'open');
|
||||
|
||||
if (isModal) {
|
||||
// Find element with `autofocus` attribute or first form control
|
||||
var first_form_ctrl = null;
|
||||
var autofocus = null;
|
||||
var findElementToFocus = function(root) {
|
||||
if (!root.children) {
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < root.children.length; i++) {
|
||||
var elem = root.children[i];
|
||||
if (
|
||||
first_form_ctrl === null &&
|
||||
!elem.disabled &&
|
||||
(elem.nodeName == 'BUTTON' ||
|
||||
elem.nodeName == 'INPUT' ||
|
||||
elem.nodeName == 'KEYGEN' ||
|
||||
elem.nodeName == 'SELECT' ||
|
||||
elem.nodeName == 'TEXTAREA')
|
||||
) {
|
||||
first_form_ctrl = elem;
|
||||
}
|
||||
if (elem.autofocus) {
|
||||
autofocus = elem;
|
||||
return;
|
||||
}
|
||||
findElementToFocus(elem);
|
||||
if (autofocus !== null) return;
|
||||
}
|
||||
};
|
||||
|
||||
findElementToFocus(this);
|
||||
|
||||
if (autofocus !== null) {
|
||||
autofocus.focus();
|
||||
} else if (first_form_ctrl !== null) {
|
||||
first_form_ctrl.focus();
|
||||
}
|
||||
}
|
||||
|
||||
if (dialogPolyfill.needsCentering(this)) dialogPolyfill.reposition(this);
|
||||
if (isModal) {
|
||||
this.dialogPolyfillInfo.modal = true;
|
||||
dialogPolyfill.dm.pushDialog(this);
|
||||
}
|
||||
|
||||
// IE sometimes complains when calling .focus() that it
|
||||
// "Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus."
|
||||
try {
|
||||
if (autofocus !== null) {
|
||||
autofocus.focus();
|
||||
} else if (first_form_ctrl !== null) {
|
||||
first_form_ctrl.focus();
|
||||
}
|
||||
} catch (e) {}
|
||||
this.style.zoom = 1;
|
||||
};
|
||||
|
||||
dialogPolyfill.close = function(retval) {
|
||||
if (!this.open && !window.HTMLDialogElement) {
|
||||
// Native implementations will set .open to false, so ignore this error.
|
||||
throw 'InvalidStateError: close called on closed dialog';
|
||||
}
|
||||
this.open = false;
|
||||
this.removeAttribute('open');
|
||||
|
||||
// Leave returnValue untouched in case it was set directly on the element
|
||||
if (typeof retval != 'undefined') {
|
||||
this.returnValue = retval;
|
||||
}
|
||||
|
||||
// This won't match the native <dialog> exactly because if the user sets top
|
||||
// on a centered polyfill dialog, that top gets thrown away when the dialog is
|
||||
// closed. Not sure it's possible to polyfill this perfectly.
|
||||
if (this.dialogPolyfillInfo.isTopOverridden) {
|
||||
this.style.top = 'auto';
|
||||
}
|
||||
|
||||
if (this.dialogPolyfillInfo.modal) {
|
||||
dialogPolyfill.dm.removeDialog(this);
|
||||
}
|
||||
|
||||
// Triggering "close" event for any attached listeners on the <dialog>
|
||||
var event;
|
||||
if (document.createEvent) {
|
||||
event = document.createEvent('HTMLEvents');
|
||||
event.initEvent('close', true, true);
|
||||
} else {
|
||||
event = new Event('close');
|
||||
}
|
||||
this.dispatchEvent(event);
|
||||
|
||||
return this.returnValue;
|
||||
};
|
||||
|
||||
dialogPolyfill.registerDialog = function(element) {
|
||||
if (element.show) {
|
||||
// console.warn("This browser already supports <dialog>, the polyfill " +
|
||||
// "may not work correctly.");
|
||||
}
|
||||
element.show = dialogPolyfill.showDialog.bind(element, false);
|
||||
element.showModal = dialogPolyfill.showDialog.bind(element, true);
|
||||
element.close = dialogPolyfill.close.bind(element);
|
||||
element.dialogPolyfillInfo = {};
|
||||
element.open = false;
|
||||
};
|
||||
|
||||
// The overlay is used to simulate how a modal dialog blocks the document. The
|
||||
// blocking dialog is positioned on top of the overlay, and the rest of the
|
||||
// dialogs on the pending dialog stack are positioned below it. In the actual
|
||||
// implementation, the modal dialog stacking is controlled by the top layer,
|
||||
// where z-index has no effect.
|
||||
var TOP_LAYER_ZINDEX = 100000;
|
||||
var MAX_PENDING_DIALOGS = 100000;
|
||||
|
||||
dialogPolyfill.DialogManager = function() {
|
||||
this.pendingDialogStack = [];
|
||||
this.overlay = document.createElement('div');
|
||||
this.overlay.style.width = '100%';
|
||||
this.overlay.style.height = '100%';
|
||||
this.overlay.style.position = 'fixed';
|
||||
this.overlay.style.left = '0px';
|
||||
this.overlay.style.top = '0px';
|
||||
this.overlay.style.backgroundColor = 'rgba(0,0,0,0.0)';
|
||||
|
||||
this.focusPageLast = this.createFocusable();
|
||||
this.overlay.appendChild(this.focusPageLast);
|
||||
|
||||
this.overlay.addEventListener('click', function(e) {
|
||||
var redirectedEvent = document.createEvent('MouseEvents');
|
||||
redirectedEvent.initMouseEvent(
|
||||
e.type,
|
||||
e.bubbles,
|
||||
e.cancelable,
|
||||
window,
|
||||
e.detail,
|
||||
e.screenX,
|
||||
e.screenY,
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
e.ctrlKey,
|
||||
e.altKey,
|
||||
e.shiftKey,
|
||||
e.metaKey,
|
||||
e.button,
|
||||
e.relatedTarget
|
||||
);
|
||||
document.body.dispatchEvent(redirectedEvent);
|
||||
});
|
||||
|
||||
// TODO: Only install when any dialogs are open.
|
||||
document.addEventListener(
|
||||
'submit',
|
||||
function(ev) {
|
||||
var method = ev.target.getAttribute('method');
|
||||
method = method ? method.toLowerCase() : '';
|
||||
if (method != 'dialog') {
|
||||
return;
|
||||
}
|
||||
ev.preventDefault();
|
||||
|
||||
var dialog = findNearestDialog(ev.target);
|
||||
if (!dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: The original event doesn't contain the INPUT element used to
|
||||
// submit the form (if any). Look in some possible places.
|
||||
var returnValue;
|
||||
var cands = [document.activeElement, ev.explicitOriginalTarget];
|
||||
cands.some(function(cand) {
|
||||
if (cand && cand.nodeName == 'INPUT' && cand.form == ev.target) {
|
||||
returnValue = cand.value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
dialog.close(returnValue);
|
||||
},
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.createFocusable = function(tabIndex) {
|
||||
var span = document.createElement('span');
|
||||
span.tabIndex = tabIndex || 0;
|
||||
span.style.opacity = 0;
|
||||
span.style.position = 'static';
|
||||
return span;
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.blockDocument = function() {
|
||||
if (!document.body.contains(this.overlay)) {
|
||||
document.body.appendChild(this.overlay);
|
||||
|
||||
// On Safari/Mac (and possibly other browsers), the documentElement is
|
||||
// not focusable. This is required for modal dialogs as it is the first
|
||||
// element to be hit by a tab event, and further tabs are redirected to
|
||||
// the most visible dialog.
|
||||
if (this.needsDocumentElementFocus === undefined) {
|
||||
document.documentElement.focus();
|
||||
this.needsDocumentElementFocus =
|
||||
document.activeElement != document.documentElement;
|
||||
}
|
||||
if (this.needsDocumentElementFocus) {
|
||||
document.documentElement.tabIndex = 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.unblockDocument = function() {
|
||||
document.body.removeChild(this.overlay);
|
||||
if (this.needsDocumentElementFocus) {
|
||||
// TODO: Restore the previous tabIndex, rather than clearing it.
|
||||
document.documentElement.tabIndex = '';
|
||||
}
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.updateStacking = function() {
|
||||
if (this.pendingDialogStack.length == 0) {
|
||||
this.unblockDocument();
|
||||
return;
|
||||
}
|
||||
this.blockDocument();
|
||||
|
||||
var zIndex = TOP_LAYER_ZINDEX;
|
||||
for (var i = 0; i < this.pendingDialogStack.length; i++) {
|
||||
if (i == this.pendingDialogStack.length - 1)
|
||||
this.overlay.style.zIndex = zIndex++;
|
||||
var dialog = this.pendingDialogStack[i];
|
||||
dialog.dialogPolyfillInfo.backdrop.style.zIndex = zIndex++;
|
||||
dialog.style.zIndex = zIndex++;
|
||||
}
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.handleKey = function(event) {
|
||||
var dialogCount = this.pendingDialogStack.length;
|
||||
if (dialogCount == 0) {
|
||||
return;
|
||||
}
|
||||
var dialog = this.pendingDialogStack[dialogCount - 1];
|
||||
var pfi = dialog.dialogPolyfillInfo;
|
||||
|
||||
switch (event.keyCode) {
|
||||
case 9 /* tab */:
|
||||
var activeElement = document.activeElement;
|
||||
var forward = !event.shiftKey;
|
||||
if (forward) {
|
||||
// Tab forward, so look for document or fake last focus element.
|
||||
if (
|
||||
activeElement == document.documentElement ||
|
||||
activeElement == document.body ||
|
||||
activeElement == pfi.backdrop
|
||||
) {
|
||||
pfi.focusFirst.focus();
|
||||
} else if (activeElement == pfi.focusLast) {
|
||||
// TODO: Instead of wrapping to focusFirst, escape to browser chrome.
|
||||
pfi.focusFirst.focus();
|
||||
}
|
||||
} else {
|
||||
// Tab backwards, so look for fake first focus element.
|
||||
if (activeElement == pfi.focusFirst) {
|
||||
// TODO: Instead of wrapping to focusLast, escape to browser chrome.
|
||||
pfi.focusLast.focus();
|
||||
} else if (activeElement == this.focusPageLast) {
|
||||
// The focus element is at the end of the page (e.g., shift-tab from
|
||||
// the window chrome): move current focus to the last element in the
|
||||
// dialog instead.
|
||||
pfi.focusLast.focus();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 27 /* esc */:
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
var cancelEvent = new supportCustomEvent('cancel', {
|
||||
bubbles: false,
|
||||
cancelable: true,
|
||||
});
|
||||
if (dialog.dispatchEvent(cancelEvent)) {
|
||||
dialog.close();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.pushDialog = function(dialog) {
|
||||
if (this.pendingDialogStack.length >= MAX_PENDING_DIALOGS) {
|
||||
throw 'Too many modal dialogs';
|
||||
}
|
||||
|
||||
var backdrop = document.createElement('div');
|
||||
backdrop.className = 'backdrop';
|
||||
var clickEventListener = function(e) {
|
||||
var redirectedEvent = document.createEvent('MouseEvents');
|
||||
redirectedEvent.initMouseEvent(
|
||||
e.type,
|
||||
e.bubbles,
|
||||
e.cancelable,
|
||||
window,
|
||||
e.detail,
|
||||
e.screenX,
|
||||
e.screenY,
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
e.ctrlKey,
|
||||
e.altKey,
|
||||
e.shiftKey,
|
||||
e.metaKey,
|
||||
e.button,
|
||||
e.relatedTarget
|
||||
);
|
||||
dialog.dispatchEvent(redirectedEvent);
|
||||
};
|
||||
backdrop.addEventListener('click', clickEventListener);
|
||||
dialog.parentNode.insertBefore(backdrop, dialog.nextSibling);
|
||||
dialog.dialogPolyfillInfo.backdrop = backdrop;
|
||||
dialog.dialogPolyfillInfo.clickEventListener = clickEventListener;
|
||||
this.pendingDialogStack.push(dialog);
|
||||
this.updateStacking();
|
||||
|
||||
dialog.dialogPolyfillInfo.focusFirst = this.createFocusable();
|
||||
dialog.dialogPolyfillInfo.focusLast = this.createFocusable();
|
||||
dialog.appendChild(dialog.dialogPolyfillInfo.focusLast);
|
||||
dialog.insertBefore(
|
||||
dialog.dialogPolyfillInfo.focusFirst,
|
||||
dialog.firstChild
|
||||
);
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.removeDialog = function(dialog) {
|
||||
var index = this.pendingDialogStack.indexOf(dialog);
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
this.pendingDialogStack.splice(index, 1);
|
||||
var backdrop = dialog.dialogPolyfillInfo.backdrop;
|
||||
var clickEventListener = dialog.dialogPolyfillInfo.clickEventListener;
|
||||
backdrop.removeEventListener('click', clickEventListener);
|
||||
backdrop.parentNode.removeChild(backdrop);
|
||||
dialog.dialogPolyfillInfo.backdrop = null;
|
||||
dialog.dialogPolyfillInfo.clickEventListener = null;
|
||||
this.updateStacking();
|
||||
|
||||
dialog.removeChild(dialog.dialogPolyfillInfo.focusFirst);
|
||||
dialog.removeChild(dialog.dialogPolyfillInfo.focusLast);
|
||||
dialog.dialogPolyfillInfo.focusFirst = null;
|
||||
dialog.dialogPolyfillInfo.focusLast = null;
|
||||
};
|
||||
|
||||
dialogPolyfill.dm = new dialogPolyfill.DialogManager();
|
||||
|
||||
document.addEventListener(
|
||||
'keydown',
|
||||
dialogPolyfill.dm.handleKey.bind(dialogPolyfill.dm)
|
||||
);
|
||||
|
||||
return dialogPolyfill;
|
||||
})();
|
||||
@@ -1,34 +0,0 @@
|
||||
dialog
|
||||
position: absolute
|
||||
left: 0
|
||||
right: 0
|
||||
width: -moz-fit-content
|
||||
width: -webkit-fit-content
|
||||
width: fit-content
|
||||
height: -moz-fit-content
|
||||
height: -webkit-fit-content
|
||||
height: fit-content
|
||||
margin: auto
|
||||
border: solid
|
||||
padding: 1em
|
||||
background: white
|
||||
color: black
|
||||
display: none
|
||||
|
||||
dialog[open]
|
||||
display: block
|
||||
|
||||
dialog + .backdrop
|
||||
position: fixed
|
||||
top: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
left: 0
|
||||
background: rgba(0,0,0,0.1)
|
||||
|
||||
/* for small devices, modal dialogs go full-screen */
|
||||
@media screen and (max-width: 540px)
|
||||
dialog[_polyfill_modal]
|
||||
top: 0
|
||||
width: auto
|
||||
margin: 1em
|
||||
@@ -1 +0,0 @@
|
||||
import './styleProperty.js';
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* https://github.com/swederik/dragula/blob/ccc15d75186f5168e7abadbe3077cf12dab09f8b/styleProperty.js
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const browserProps = {};
|
||||
|
||||
function eachVendor(prop, fn) {
|
||||
const prefixes = ['Webkit', 'Moz', 'ms', 'O'];
|
||||
fn(prop);
|
||||
for (let i = 0; i < prefixes.length; i++) {
|
||||
fn(prefixes[i] + prop.charAt(0).toUpperCase() + prop.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
function check(property, testValue) {
|
||||
const sandbox = document.createElement('iframe');
|
||||
const element = document.createElement('p');
|
||||
|
||||
document.body.appendChild(sandbox);
|
||||
sandbox.contentDocument.body.appendChild(element);
|
||||
const support = set(element, property, testValue);
|
||||
|
||||
// We have to do this because remove() is not supported by IE11 and below
|
||||
sandbox.parentElement.removeChild(sandbox);
|
||||
return support;
|
||||
}
|
||||
|
||||
function checkComputed(el, prop) {
|
||||
const computed = window.getComputedStyle(el).getPropertyValue(prop);
|
||||
return computed !== void 0 && computed.length > 0 && computed !== 'none';
|
||||
}
|
||||
|
||||
function set(el, prop, value) {
|
||||
let match = false;
|
||||
|
||||
if (browserProps[prop] === void 0) {
|
||||
eachVendor(prop, function(vendorProp) {
|
||||
if (el.style[vendorProp] !== void 0 && match === false) {
|
||||
el.style[vendorProp] = value;
|
||||
if (checkComputed(el, vendorProp)) {
|
||||
match = true;
|
||||
browserProps[prop] = vendorProp;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
el.style[browserProps[prop]] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
const styleProperty = {
|
||||
check,
|
||||
set,
|
||||
};
|
||||
|
||||
OHIF.ui.styleProperty = styleProperty;
|
||||
})();
|
||||
|
||||
const { styleProperty } = OHIF.ui;
|
||||
|
||||
export { styleProperty };
|
||||
@@ -1,150 +0,0 @@
|
||||
/*!
|
||||
* transition-to-from-auto 0.5.2
|
||||
* https://github.com/75lb/transition-to-from-auto
|
||||
* Copyright 2015 Lloyd Brookes <75pound@gmail.com>
|
||||
*/
|
||||
|
||||
/**
|
||||
@module
|
||||
@alias transition
|
||||
*/
|
||||
(function(window, document) {
|
||||
'use strict';
|
||||
|
||||
var getComputedStyle = window.getComputedStyle;
|
||||
var isTransition = 'data-ttfaInTransition';
|
||||
|
||||
var elements = [];
|
||||
var data = [];
|
||||
|
||||
// Transition detecting
|
||||
var transitionProp = false;
|
||||
var transitionEnd = false;
|
||||
var testStyle = document.createElement('a').style;
|
||||
var testProp;
|
||||
|
||||
if (testStyle[(testProp = 'webkitTransition')] !== undefined) {
|
||||
transitionProp = testProp;
|
||||
transitionEnd = testProp + 'End';
|
||||
}
|
||||
|
||||
if (testStyle[(testProp = 'transition')] !== undefined) {
|
||||
transitionProp = testProp;
|
||||
transitionEnd = testProp + 'end';
|
||||
}
|
||||
|
||||
function process(options, data) {
|
||||
var el = options.element;
|
||||
var val = options.val;
|
||||
var prop = options.prop;
|
||||
var style = el.style;
|
||||
var startVal;
|
||||
var autoVal;
|
||||
|
||||
if (!transitionProp) {
|
||||
return (style[prop] = val);
|
||||
}
|
||||
|
||||
if (el.hasAttribute(isTransition)) {
|
||||
el.removeEventListener(transitionEnd, data.l);
|
||||
} else {
|
||||
style[transitionProp] = 'none';
|
||||
|
||||
startVal = getComputedStyle(el)[prop];
|
||||
style[prop] = 'auto';
|
||||
autoVal = getComputedStyle(el)[prop];
|
||||
|
||||
// Interrupt
|
||||
if (startVal === val || (val === 'auto' && startVal === autoVal)) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.auto = autoVal;
|
||||
el.setAttribute(isTransition, 1);
|
||||
|
||||
// Transition
|
||||
style[prop] = startVal;
|
||||
el.offsetWidth;
|
||||
style[transitionProp] = options.style;
|
||||
}
|
||||
|
||||
style[prop] = val === 'auto' ? data.auto : val;
|
||||
|
||||
data.l = function(e) {
|
||||
if (e.propertyName === prop) {
|
||||
el.removeAttribute(isTransition);
|
||||
el.removeEventListener(transitionEnd, data.l);
|
||||
if (val === 'auto') {
|
||||
/* avoid transition flashes in Safari */
|
||||
style[transitionProp] = 'none';
|
||||
style[prop] = val;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
el.addEventListener(transitionEnd, data.l);
|
||||
}
|
||||
|
||||
/**
|
||||
@param options {Object}
|
||||
@param options.element {string | element} - The DOM element or selector to transition
|
||||
@param options.val {string} - The value you want to transition to
|
||||
@param [options.prop] {string} - The CSS property to transition, defaults to `"height"`
|
||||
@param [options.style] {string} - The desired value for the `transition` CSS property (e.g. `"height 1s"`). If specified, this value is added inline and will override your CSS. Leave this value blank if you already have it defined in your stylesheet.
|
||||
@alias module:transition-to-from-auto
|
||||
*/
|
||||
function transition(options) {
|
||||
var element = options.element;
|
||||
var datum;
|
||||
var index;
|
||||
|
||||
if (typeof element === 'string') {
|
||||
element = document.querySelector(element);
|
||||
}
|
||||
|
||||
element = options.element = element instanceof Node ? element : false;
|
||||
options.prop = options.prop || 'height';
|
||||
options.style = options.style || '';
|
||||
|
||||
if (element) {
|
||||
index = elements.indexOf(element);
|
||||
if (~index) {
|
||||
datum = data[index];
|
||||
} else {
|
||||
datum = {};
|
||||
elements.push(element);
|
||||
data.push(datum);
|
||||
}
|
||||
|
||||
process(options, datum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The name of the vendor-specific transition CSS property
|
||||
@type {string}
|
||||
@example
|
||||
el.style[transition.prop + 'Duration'] = '1s';
|
||||
*/
|
||||
transition.prop = transitionProp;
|
||||
|
||||
/**
|
||||
* The name of the [transition end event](https://developer.mozilla.org/en-US/docs/Web/Events/transitionend) in the current browser (typically `"transitionend"` or `"webkitTransitionEnd"`)
|
||||
* @type {string}
|
||||
* @example
|
||||
* el.addEventListener(transition.end, function(){
|
||||
* // the transition ended..
|
||||
* });
|
||||
*/
|
||||
transition.end = transitionEnd;
|
||||
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = transition;
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define(function() {
|
||||
return transition;
|
||||
});
|
||||
} else {
|
||||
window.transition = transition;
|
||||
}
|
||||
})(window, document);
|
||||
@@ -13,7 +13,6 @@ import metadata from './classes/metadata/';
|
||||
import object from './object.js';
|
||||
import redux from './redux/';
|
||||
import string from './string.js';
|
||||
//import './schema.js';
|
||||
import studies from './studies/';
|
||||
import ui from './ui';
|
||||
import user from './user.js';
|
||||
|
||||
@@ -6,7 +6,9 @@ import getImageIdForImagePath from '../lib/getImageIdForImagePath';
|
||||
import guid from '../../utils/guid';
|
||||
import studyMetadataManager from '../../utils/studyMetadataManager';
|
||||
|
||||
const configuration = {};
|
||||
const configuration = {
|
||||
measurementTools: [],
|
||||
};
|
||||
|
||||
export default class MeasurementApi {
|
||||
static Instance;
|
||||
|
||||
@@ -32,6 +32,14 @@ export const MaxTargetsSchema = {
|
||||
minItems: 1,
|
||||
uniqueItems: true,
|
||||
},
|
||||
isNodal: {
|
||||
label: 'Filter to evaluate only nodal or extranodal measurements',
|
||||
type: 'boolean'
|
||||
},
|
||||
message: {
|
||||
label: 'Message to be displayed in case of nonconformity',
|
||||
type: 'string',
|
||||
}
|
||||
},
|
||||
required: ['limit'],
|
||||
};
|
||||
@@ -43,6 +51,7 @@ export const MaxTargetsSchema = {
|
||||
* newTarget: Flag to evaluate only new targets (must be evaluated on both)
|
||||
* locationIn: Filter to evaluate only measurements with the specified locations
|
||||
* locationNotIn: Filter to evaluate only measurements without the specified locations
|
||||
* isNodal: Filter to evaluate only nodal or extranodal measurements
|
||||
* message: Message to be displayed in case of nonconformity
|
||||
*/
|
||||
export class MaxTargetsCriterion extends BaseCriterion {
|
||||
@@ -56,16 +65,31 @@ export class MaxTargetsCriterion extends BaseCriterion {
|
||||
const newTargetNumbers = this.getNewTargetNumbers(data);
|
||||
const measurementNumbers = [];
|
||||
data.targets.forEach(target => {
|
||||
const { location, measurementNumber, isSplitLesion } = target.measurement;
|
||||
if (isSplitLesion) return;
|
||||
if (options.newTarget && !newTargetNumbers.has(measurementNumber)) return;
|
||||
const { location, measurementNumber, isSplitLesion, isNodal } = target.measurement;
|
||||
|
||||
if (isSplitLesion)
|
||||
return;
|
||||
|
||||
if (typeof isNodal === 'boolean' && typeof options.isNodal === 'boolean' && options.isNodal !== isNodal)
|
||||
return;
|
||||
|
||||
if (options.newTarget && !newTargetNumbers.has(measurementNumber))
|
||||
return;
|
||||
|
||||
if (options.locationIn && options.locationIn.indexOf(location) === -1)
|
||||
return;
|
||||
|
||||
if (options.locationNotIn && options.locationNotIn.indexOf(location) > -1)
|
||||
return;
|
||||
|
||||
measurementNumbers.push(measurementNumber);
|
||||
});
|
||||
|
||||
let lesionType = '';
|
||||
if (typeof options.isNodal === 'boolean') {
|
||||
lesionType = options.isNodal ? 'nodal ' : 'extranodal ';
|
||||
}
|
||||
|
||||
let message;
|
||||
if (measurementNumbers.length > options.limit) {
|
||||
const increment = options.newTarget ? 'new ' : '';
|
||||
@@ -73,7 +97,7 @@ export class MaxTargetsCriterion extends BaseCriterion {
|
||||
const amount = options.limit === 0 ? '' : `more than ${options.limit}`;
|
||||
message =
|
||||
options.message ||
|
||||
`The study should not have ${amount} ${increment}target${plural}.`;
|
||||
`The study should not have ${amount} ${increment}${lesionType}target${plural}.`;
|
||||
}
|
||||
|
||||
return this.generateResponse(message);
|
||||
|
||||
@@ -12,6 +12,14 @@ export const MaxTargetsPerOrganSchema = {
|
||||
label: 'Flag to evaluate only new targets',
|
||||
type: 'boolean',
|
||||
},
|
||||
isNodal: {
|
||||
label: 'Filter to evaluate only nodal or extranodal measurements',
|
||||
type: 'boolean'
|
||||
},
|
||||
message: {
|
||||
label: 'Message to be displayed in case of nonconformity',
|
||||
type: 'string',
|
||||
}
|
||||
},
|
||||
required: ['limit'],
|
||||
};
|
||||
@@ -22,6 +30,8 @@ export const MaxTargetsPerOrganSchema = {
|
||||
* Options:
|
||||
* limit: Max targets allowed in study
|
||||
* newTarget: Flag to evaluate only new targets (must be evaluated on both)
|
||||
* isNodal: Filter to evaluate only nodal or extranodal measurements
|
||||
* message: Message to be displayed in case of nonconformity
|
||||
*/
|
||||
export class MaxTargetsPerOrganCriterion extends BaseCriterion {
|
||||
constructor(...props) {
|
||||
@@ -36,9 +46,13 @@ export class MaxTargetsPerOrganCriterion extends BaseCriterion {
|
||||
const newTargetNumbers = this.getNewTargetNumbers(data);
|
||||
data.targets.forEach(target => {
|
||||
const { measurement } = target;
|
||||
const { location, measurementNumber, isSplitLesion } = measurement;
|
||||
const { location, measurementNumber, isSplitLesion, isNodal } = measurement;
|
||||
|
||||
if (isSplitLesion) return;
|
||||
if (isSplitLesion)
|
||||
return;
|
||||
|
||||
if (typeof isNodal === 'boolean' && typeof options.isNodal === 'boolean' && options.isNodal !== isNodal)
|
||||
return;
|
||||
|
||||
if (!targetsPerOrgan[location]) {
|
||||
targetsPerOrgan[location] = new Set();
|
||||
@@ -59,7 +73,7 @@ export class MaxTargetsPerOrganCriterion extends BaseCriterion {
|
||||
message =
|
||||
options.message ||
|
||||
`Each organ should not have more than ${
|
||||
options.limit
|
||||
options.limit
|
||||
} ${increment}targets.`;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,10 @@ export const MeasurementsLengthSchema = {
|
||||
minItems: 1,
|
||||
uniqueItems: true,
|
||||
},
|
||||
isNodal: {
|
||||
label: 'Filter to evaluate only nodal or extranodal measurements',
|
||||
type: 'boolean'
|
||||
},
|
||||
message: {
|
||||
label: 'Message to be displayed in case of nonconformity',
|
||||
type: 'string',
|
||||
@@ -89,6 +93,7 @@ export const MeasurementsLengthSchema = {
|
||||
* modalityNotIn: Filter to evaluate only measurements without the specified modalities
|
||||
* locationIn: Filter to evaluate only measurements with the specified locations
|
||||
* locationNotIn: Filter to evaluate only measurements without the specified locations
|
||||
* isNodal: Filter to evaluate only nodal or extranodal measurements
|
||||
* message: Message to be displayed in case of nonconformity
|
||||
*/
|
||||
export class MeasurementsLengthCriterion extends BaseCriterion {
|
||||
@@ -107,7 +112,7 @@ export class MeasurementsLengthCriterion extends BaseCriterion {
|
||||
const { metadata, measurement } = item;
|
||||
const { location } = measurement;
|
||||
|
||||
let { longestDiameter, shortestDiameter } = measurement;
|
||||
let { longestDiameter, shortestDiameter, isNodal } = measurement;
|
||||
if (measurement.childToolsCount) {
|
||||
const child = measurement.bidirectional;
|
||||
longestDiameter = (child && child.longestDiameter) || 0;
|
||||
@@ -118,6 +123,8 @@ export class MeasurementsLengthCriterion extends BaseCriterion {
|
||||
const modality = (metadata.getRawValue('x00080060') || '').toUpperCase();
|
||||
|
||||
// Stop here if the measurement does not match the modality and location filters
|
||||
if (typeof isNodal === 'boolean' && typeof options.isNodal === 'boolean' && options.isNodal !== isNodal)
|
||||
return;
|
||||
if (options.locationIn && options.locationIn.indexOf(location) === -1)
|
||||
return;
|
||||
if (options.modalityIn && options.modalityIn.indexOf(modality) === -1)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
//import SimpleSchema from 'simpl-schema'
|
||||
|
||||
/*
|
||||
Extend the available options on schema definitions:
|
||||
|
||||
* valuesLabels: Used in conjunction with allowedValues to define the text
|
||||
label for each value (used on forms)
|
||||
|
||||
* textOptional: Used to allow empty strings
|
||||
|
||||
*/
|
||||
/*SimpleSchema.extendOptions({
|
||||
valuesLabels: Match.Optional([String]),
|
||||
textOptional: Match.Optional(Boolean)
|
||||
});
|
||||
|
||||
// Add default required validation for empty strings which can be bypassed
|
||||
// using textOptional=true definition
|
||||
SimpleSchema.addValidator(function() {
|
||||
if (
|
||||
this.definition.optional !== true &&
|
||||
this.definition.textOptional !== true &&
|
||||
this.value === ''
|
||||
) {
|
||||
return 'required';
|
||||
}
|
||||
});*/
|
||||
|
||||
// Including [label] for some messages
|
||||
/*SimpleSchema.messages({
|
||||
maxCount: '[label] can not have more than [maxCount] values',
|
||||
minCount: '[label] must have at least [minCount] values',
|
||||
notAllowed: '[label] has an invalid value: "[value]"'
|
||||
});*/
|
||||
@@ -1,320 +0,0 @@
|
||||
/*import SimpleSchema from 'simpl-schema';
|
||||
|
||||
const serverNameDefinitions = {
|
||||
type: String,
|
||||
label: 'Server Name',
|
||||
max: 100
|
||||
};
|
||||
|
||||
const serverTypeDefinitions = {
|
||||
type: String,
|
||||
label: 'Server Type',
|
||||
allowedValues: ['dicomWeb', 'dimse'],
|
||||
valuesLabels: ['DICOM Web', 'DIMSE'],
|
||||
optional: true
|
||||
};
|
||||
|
||||
const wadoUriRootDefinitions = {
|
||||
type: String,
|
||||
label: 'WADO URI root',
|
||||
max: 1000
|
||||
};
|
||||
|
||||
const availableMouseButtonTools = ['wwwc', 'zoom', 'pan', 'stackScroll'];
|
||||
|
||||
export const DICOMWebRequestOptions = new SimpleSchema({
|
||||
auth: {
|
||||
type: String,
|
||||
label: 'Authentication',
|
||||
defaultValue: 'orthanc:orthanc',
|
||||
optional: true
|
||||
},
|
||||
requestFromBrowser: {
|
||||
type: Boolean,
|
||||
label: 'Make DICOMWeb requests from the Browser',
|
||||
defaultValue: false,
|
||||
optional: true
|
||||
},
|
||||
logRequests: {
|
||||
type: Boolean,
|
||||
defaultValue: true,
|
||||
label: 'Requests'
|
||||
},
|
||||
logResponses: {
|
||||
type: Boolean,
|
||||
defaultValue: false,
|
||||
label: 'Responses'
|
||||
},
|
||||
logTiming: {
|
||||
type: Boolean,
|
||||
defaultValue: true,
|
||||
label: 'Timing'
|
||||
}
|
||||
});
|
||||
|
||||
export const DICOMWebServer = new SimpleSchema({
|
||||
name: serverNameDefinitions,
|
||||
type: serverTypeDefinitions,
|
||||
wadoUriRoot: wadoUriRootDefinitions,
|
||||
wadoRoot: {
|
||||
type: String,
|
||||
label: 'WADO root',
|
||||
max: 1000
|
||||
},
|
||||
imageRendering: {
|
||||
type: String,
|
||||
label: 'Image rendering',
|
||||
allowedValues: ['wadouri', 'wadors'],
|
||||
valuesLabels: ['WADO URI', 'WADO RS'],
|
||||
defaultValue: 'wadouri'
|
||||
},
|
||||
thumbnailRendering: {
|
||||
type: String,
|
||||
label: 'Thumbnail rendering',
|
||||
allowedValues: ['wadouri', 'wadors'],
|
||||
valuesLabels: ['WADO URI', 'WADO RS'],
|
||||
defaultValue: 'wadouri'
|
||||
},
|
||||
qidoRoot: {
|
||||
type: String,
|
||||
label: 'QIDO root',
|
||||
max: 1000
|
||||
},
|
||||
qidoSupportsIncludeField: {
|
||||
type: Boolean,
|
||||
label: 'QIDO supports "includefield" query key',
|
||||
defaultValue: false
|
||||
},
|
||||
requestOptions: {
|
||||
type: DICOMWebRequestOptions,
|
||||
label: 'Request Options'
|
||||
}
|
||||
});
|
||||
|
||||
export const DIMSEPeer = new SimpleSchema({
|
||||
aeTitle: {
|
||||
type: String,
|
||||
label: 'AE Title'
|
||||
},
|
||||
hostAE: {
|
||||
type: String,
|
||||
label: 'AE Host',
|
||||
optional: true
|
||||
},
|
||||
host: {
|
||||
type: String,
|
||||
label: 'Host Domain/IP',
|
||||
regEx: SimpleSchema.RegEx.WeakDomain
|
||||
},
|
||||
port: {
|
||||
type: Number,
|
||||
label: 'Port',
|
||||
min: 1,
|
||||
defaultValue: 11112,
|
||||
max: 65535
|
||||
},
|
||||
default: {
|
||||
type: Boolean,
|
||||
label: 'Default',
|
||||
defaultValue: false
|
||||
},
|
||||
server: {
|
||||
type: Boolean,
|
||||
label: 'Server',
|
||||
defaultValue: false
|
||||
},
|
||||
supportsInstanceRetrievalByStudyUid: {
|
||||
type: Boolean,
|
||||
label: 'Supports instance retrieval by StudyUid',
|
||||
defaultValue: true
|
||||
}
|
||||
});
|
||||
|
||||
export const DIMSEServer = new SimpleSchema({
|
||||
name: serverNameDefinitions,
|
||||
type: serverTypeDefinitions,
|
||||
wadoUriRoot: wadoUriRootDefinitions,
|
||||
requestOptions: {
|
||||
type: DICOMWebRequestOptions,
|
||||
label: 'Request Options'
|
||||
},
|
||||
peers: {
|
||||
type: [DIMSEPeer],
|
||||
label: 'Peer List',
|
||||
minCount: 1
|
||||
}
|
||||
});
|
||||
|
||||
export const UISettings = new SimpleSchema({
|
||||
studyListFunctionsEnabled: {
|
||||
type: Boolean,
|
||||
label: 'Study List Functions Enabled?',
|
||||
defaultValue: true
|
||||
},
|
||||
leftSidebarOpen: {
|
||||
type: Boolean,
|
||||
label: 'Left sidebar open by default?',
|
||||
defaultValue: false
|
||||
},
|
||||
leftSidebarDragAndDrop: {
|
||||
type: Boolean,
|
||||
label:
|
||||
'Left sidebar allows thumbnail drag and drop. If false, images will be loaded on single click.',
|
||||
defaultValue: true
|
||||
},
|
||||
displaySetNavigationLoopOverSeries: {
|
||||
type: Boolean,
|
||||
label:
|
||||
'The UP/DOWN display set navigation buttons will start over when reach the last display set in viewport?',
|
||||
defaultValue: true
|
||||
},
|
||||
displaySetNavigationMultipleViewports: {
|
||||
type: Boolean,
|
||||
label:
|
||||
'The UP/DOWN display set navigation buttons will iterate over all the viewports at once?',
|
||||
defaultValue: false
|
||||
},
|
||||
displayEchoUltrasoundWorkflow: {
|
||||
type: Boolean,
|
||||
label: 'Enable cine dialog enhancements for multiframe images.',
|
||||
defaultValue: false
|
||||
},
|
||||
autoPositionMeasurementsTextCallOuts: {
|
||||
type: String,
|
||||
label: 'Auto position text call-outs for measurements when creating them.',
|
||||
defaultValue: 'TRBL'
|
||||
},
|
||||
studyListDateFilterNumDays: {
|
||||
type: Number,
|
||||
label: 'Number of days to be used on Study List date filter',
|
||||
min: 1
|
||||
},
|
||||
showStackLoadingProgressBar: {
|
||||
type: Boolean,
|
||||
label:
|
||||
'Show a progress bar closest to the thumbnail showing how much the stack has loaded',
|
||||
defaultValue: true
|
||||
},
|
||||
cornerstoneRenderer: {
|
||||
type: String,
|
||||
label: 'Cornerstone default image renderer',
|
||||
defaultValue: 'webgl'
|
||||
},
|
||||
sortSeriesByIncomingOrder: {
|
||||
type: Boolean,
|
||||
label:
|
||||
"Define if the series' images shall be sorted by incoming order. Sort by Instance Number by default.",
|
||||
defaultValue: false
|
||||
},
|
||||
useMiddleSeriesInstanceAsThumbnail: {
|
||||
type: Boolean,
|
||||
label:
|
||||
'Define if the middle instance of a series will be used as thumbnail. If not, the first instance will be used.',
|
||||
defaultValue: true
|
||||
}
|
||||
});
|
||||
|
||||
export const PrefetchSchema = new SimpleSchema({
|
||||
order: {
|
||||
type: String,
|
||||
label: 'Prefetch Order',
|
||||
allowedValues: ['topdown', 'downward', 'closest'],
|
||||
optional: false
|
||||
},
|
||||
displaySetCount: {
|
||||
type: Number,
|
||||
label: 'Display Set Count',
|
||||
min: 1,
|
||||
defaultValue: 1
|
||||
}
|
||||
});
|
||||
|
||||
export const MouseButtonToolSchema = new SimpleSchema({
|
||||
left: {
|
||||
type: String,
|
||||
label: 'Left Mouse Button',
|
||||
allowedValues: availableMouseButtonTools,
|
||||
optional: true
|
||||
},
|
||||
right: {
|
||||
type: String,
|
||||
label: 'Right Mouse Button',
|
||||
allowedValues: availableMouseButtonTools,
|
||||
optional: true
|
||||
},
|
||||
middle: {
|
||||
type: String,
|
||||
label: 'Middle Mouse Button',
|
||||
allowedValues: availableMouseButtonTools,
|
||||
optional: true
|
||||
}
|
||||
});
|
||||
|
||||
export const PublicServerConfig = new SimpleSchema({
|
||||
verifyEmail: {
|
||||
type: Boolean,
|
||||
label: 'Verify Email',
|
||||
defaultValue: false
|
||||
},
|
||||
demoUserEnabled: {
|
||||
type: Boolean,
|
||||
label: 'Creates demo user on startup and show TestDrive button',
|
||||
defaultValue: true
|
||||
},
|
||||
userAuthenticationRoutesEnabled: {
|
||||
type: Boolean,
|
||||
label: 'Enables routing to /login page.',
|
||||
defaultValue: false
|
||||
},
|
||||
ui: {
|
||||
type: UISettings,
|
||||
label: 'UI Settings'
|
||||
},
|
||||
prefetch: {
|
||||
type: PrefetchSchema,
|
||||
label: 'Prefetch settings'
|
||||
},
|
||||
defaultMouseButtonTools: {
|
||||
type: MouseButtonToolSchema,
|
||||
label: 'Default Mouse Button Tools'
|
||||
}
|
||||
});
|
||||
|
||||
export const Servers = new SimpleSchema({
|
||||
dicomWeb: {
|
||||
type: [DICOMWebServer],
|
||||
label: 'DICOMWeb Servers',
|
||||
optional: true
|
||||
},
|
||||
dimse: {
|
||||
type: [DIMSEServer],
|
||||
label: 'DIMSE Servers',
|
||||
optional: true
|
||||
}
|
||||
});
|
||||
|
||||
export const ServerConfiguration = new SimpleSchema({
|
||||
servers: {
|
||||
type: Servers,
|
||||
label: 'Servers'
|
||||
},
|
||||
defaultServiceType: {
|
||||
type: String,
|
||||
label: 'Default Service Type',
|
||||
defaultValue: 'dicomWeb'
|
||||
},
|
||||
dropCollections: {
|
||||
type: Boolean,
|
||||
label: 'Drop database collections',
|
||||
defaultValue: false
|
||||
},
|
||||
public: {
|
||||
type: PublicServerConfig,
|
||||
label: 'Public Server Configuration'
|
||||
},
|
||||
origin: {
|
||||
type: String,
|
||||
label: 'Origin',
|
||||
optional: true
|
||||
}
|
||||
});*/
|
||||
@@ -9,7 +9,11 @@ const studySearchPromises = new Map();
|
||||
* @returns {Promise} resolved with an array of studies information or rejected with an error
|
||||
*/
|
||||
export default function searchStudies(server, filter) {
|
||||
const promiseKey = JSON.stringify(filter);
|
||||
const promiseKeyObj = {
|
||||
qidoRoot: server.qidoRoot,
|
||||
filter,
|
||||
};
|
||||
const promiseKey = JSON.stringify(promiseKeyObj);
|
||||
if (studySearchPromises.has(promiseKey)) {
|
||||
return studySearchPromises.get(promiseKey);
|
||||
} else {
|
||||
|
||||
@@ -42,6 +42,7 @@ function getQIDOQueryParams(filter, serverSupportsQIDOIncludeField) {
|
||||
ModalitiesInStudy: filter.modalitiesInStudy,
|
||||
limit: filter.limit,
|
||||
offset: filter.offset,
|
||||
fuzzymatching: filter.fuzzymatching,
|
||||
includefield: serverSupportsQIDOIncludeField ? commaSeparatedFields : 'all',
|
||||
};
|
||||
|
||||
|
||||
@@ -164,9 +164,10 @@ function getPaletteColor(server, instance, tag, lutDescriptor) {
|
||||
return byteArray[position] + byteArray[position + 1] * 256;
|
||||
};
|
||||
|
||||
const arrayBufferToPaletteColorLUT = arraybuffer => {
|
||||
const byteArray = new Uint8Array(arraybuffer);
|
||||
const lut = [];
|
||||
const arrayBufferToPaletteColorLUT = result => {
|
||||
const arraybuffer = result[0]
|
||||
const byteArray = new Uint8Array(arraybuffer)
|
||||
const lut = []
|
||||
|
||||
for (let i = 0; i < numLutEntries; i++) {
|
||||
if (bits === 16) {
|
||||
@@ -390,6 +391,7 @@ async function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
|
||||
DICOMWeb.getString(instance['00181065'])
|
||||
),
|
||||
sliceThickness: DICOMWeb.getNumber(instance['00180050']),
|
||||
spacingBetweenSlices: DICOMWeb.getString(instance['00180088']),
|
||||
lossyImageCompression: DICOMWeb.getString(instance['00282110']),
|
||||
derivationDescription: DICOMWeb.getString(instance['00282111']),
|
||||
lossyImageCompressionRatio: DICOMWeb.getString(instance['00282112']),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export default function absoluteUrl(path) {
|
||||
const absoluteUrl = path => {
|
||||
let absolutePath = '/';
|
||||
|
||||
if (!path) return absolutePath;
|
||||
|
||||
// TODO: Find another way to get root url
|
||||
const absoluteUrl = window.location.origin;
|
||||
const absoluteUrlParts = absoluteUrl.split('/');
|
||||
@@ -13,4 +15,6 @@ export default function absoluteUrl(path) {
|
||||
}
|
||||
|
||||
return absolutePath.replace(/\/\/+/g, '/');
|
||||
}
|
||||
};
|
||||
|
||||
export default absoluteUrl;
|
||||
@@ -0,0 +1,51 @@
|
||||
import absoluteUrl from './absoluteUrl';
|
||||
|
||||
describe('absoluteUrl', () => {
|
||||
test('should return /path_1/path_2/path_3/path_to_destination when the window.location.origin is http://dummy.com/path_1/path_2 and the path is /path_3/path_to_destination', () => {
|
||||
let global = {
|
||||
window: Object.create(window),
|
||||
};
|
||||
const url = 'http://dummy.com/path_1/path_2';
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
origin: url,
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
const absoluteUrlOutput = absoluteUrl('/path_3/path_to_destination');
|
||||
expect(absoluteUrlOutput).toEqual(
|
||||
'/path_1/path_2/path_3/path_to_destination'
|
||||
);
|
||||
});
|
||||
|
||||
test('should return / when the path is not defined', () => {
|
||||
const absoluteUrlOutput = absoluteUrl(undefined);
|
||||
expect(absoluteUrlOutput).toBe('/');
|
||||
});
|
||||
|
||||
test('should return the original path when there path in the window.origin after the domain and port', () => {
|
||||
global.window = Object.create(window);
|
||||
const url = 'http://dummy.com';
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
origin: url,
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
const absoluteUrlOutput = absoluteUrl('path_1/path_2/path_3');
|
||||
expect(absoluteUrlOutput).toEqual('/path_1/path_2/path_3');
|
||||
});
|
||||
|
||||
test('should be able to return the absolute path even when the path contains duplicates', () => {
|
||||
global.window = Object.create(window);
|
||||
const url = 'http://dummy.com';
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
origin: url,
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
const absoluteUrlOutput = absoluteUrl('path_1/path_1/path_1');
|
||||
expect(absoluteUrlOutput).toEqual('/path_1/path_1/path_1');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
import addServers from './addServers';
|
||||
|
||||
describe('addServers', () => {
|
||||
const servers = {
|
||||
dicomWeb: [
|
||||
{
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
qidoSupportsIncludeField: true,
|
||||
imageRendering: 'wadors',
|
||||
thumbnailRendering: 'wadors',
|
||||
requestOptions: {
|
||||
requestFromBrowser: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
oidc: [
|
||||
{
|
||||
authority: 'http://127.0.0.1/auth/realms/ohif',
|
||||
client_id: 'ohif-viewer',
|
||||
redirect_uri: 'http://127.0.0.1/callback',
|
||||
response_type: 'code',
|
||||
scope: 'openid',
|
||||
post_logout_redirect_uri: '/logout-redirect.html',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const store = {
|
||||
dispatch: jest.fn(),
|
||||
};
|
||||
|
||||
test('should be able to add a server and dispatch to the store successfuly', () => {
|
||||
addServers(servers, store);
|
||||
expect(store.dispatch).toBeCalledWith({
|
||||
server: {
|
||||
authority: 'http://127.0.0.1/auth/realms/ohif',
|
||||
client_id: 'ohif-viewer',
|
||||
post_logout_redirect_uri: '/logout-redirect.html',
|
||||
redirect_uri: 'http://127.0.0.1/callback',
|
||||
response_type: 'code',
|
||||
scope: 'openid',
|
||||
type: 'oidc',
|
||||
},
|
||||
type: 'ADD_SERVER',
|
||||
});
|
||||
expect(store.dispatch).toBeCalledWith({
|
||||
server: {
|
||||
imageRendering: 'wadors',
|
||||
name: 'DCM4CHEE',
|
||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
qidoSupportsIncludeField: true,
|
||||
requestOptions: { requestFromBrowser: true },
|
||||
thumbnailRendering: 'wadors',
|
||||
type: 'dicomWeb',
|
||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
},
|
||||
type: 'ADD_SERVER',
|
||||
});
|
||||
});
|
||||
|
||||
test('should throw an error if servers list is not defined', () => {
|
||||
expect(() => addServers(undefined, store)).toThrowError(
|
||||
new Error('The servers and store must be defined')
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw an error if store is not defined', () => {
|
||||
expect(() => addServers(servers, undefined)).toThrowError(
|
||||
new Error('The servers and store must be defined')
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw an error when both server and store are not defined', () => {
|
||||
expect(() => addServers(undefined, undefined)).toThrowError(
|
||||
new Error('The servers and store must be defined')
|
||||
);
|
||||
});
|
||||
});
|
||||
Loaded 100 of 320 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user