Compare commits

...
Author SHA1 Message Date
Erik Ziegler 02c732aa15 fix(config): Stop crashes if window.config is not defined when script-tag viewer is loaded 2019-07-29 14:42:21 +02:00
Erik Ziegler c5f532159b ci(cypress): Remove duplicate React versions in end-to-end tests 2019-07-29 10:45:38 +02:00
Erik Ziegler b1df019c49 fix(package.json): Update @ohif/i18n to 0.2.3 2019-07-29 10:03:48 +02:00
Erik Ziegler 54b70e5fa4 @ohif/i18n: Update i18n locales from Locize source of truth (0.2.3) 2019-07-29 10:03:48 +02:00
Erik Ziegler 5802cf18eb fix(script-tag): Include React and ReactDOM as dependencies and add installViewer function to simplify script tag usage. (#721) 2019-07-29 08:20:07 +02:00
Danny Brown 210c2e8d2c fix: clear annotations toolbar button 2019-07-26 16:38:37 -04:00
Erik Ziegler da318c9c8c feat(DicomUploader): Make DICOM Uploader work with all DICOMWeb servers (#717)
* feat(DicomUploader): Make DICOM Uploader work with all DICOMWeb servers

* Bump viewerbase version
2019-07-26 15:04:28 +02:00
João Felipe de Medeiros Moreira 903c41d26f fix(Bootstrap): Remove bootstrap dependency (#708)
fix #636
2019-07-26 13:41:29 +02:00
Danny Brown ba261a0d54 fix: support for next/previous image hotkeys via cornerstone extension update
* Bump @ohif/extenions-cornerstone react-cornerstone-viewport dep and cut release

* fix: bump @ohif/extension-cornerstone version to latest

* Catching the yarn lock changes

* Cut new release for cornerstone extension and update Viewers to use it

* Add new commands to netlify dmeo

* Flip directions for next/previous image
2019-07-23 15:39:56 -04:00
David Wire 3398a894a3 feat(commandsModule): Add previousImage and nextImage actions and commands (#710)
* feat(commandsModule): Add `previousImage` and `nextImage` actions and commands

This change adds the keybindings for previousImage (down) and nextImage (up) to the default
configuration and adds the command and action necessary to support it. This pull request is
dependent upon https://github.com/cornerstonejs/cornerstoneTools/pull/1025 which exports the scroll
utility it uses.

* style(default.js): Switch tabs to spaces

* fix(commandsModule): import `scroll` through cornerstoneTools.import
2019-07-23 14:52:29 -04:00
Danny Brown 5a13194203 fix(react-cornerstone-viewport): Viewport resizes on stack scroll (#709)
* Bump @ohif/extenions-cornerstone react-cornerstone-viewport dep and cut release

* fix: bump @ohif/extension-cornerstone version to latest

* Catching the yarn lock changes
2019-07-23 13:00:02 -04:00
Erik Ziegler a2cc477a1a fix(package.json): Update @ohif/extension-microscopy to 0.0.12 2019-07-22 15:47:45 +02:00
Erik Ziegler 2e7a59f7f8 @ohif/extension-dicom-microscopy: Update dicom-microscopy-viewer dependency to 0.6.1 2019-07-22 15:47:45 +02:00
Erik Ziegler 83d7d59954 fix(package.json): Update ohif-core and dicomweb-client to fix Palette Color BulkData retrieval (#703) 2019-07-22 15:09:55 +02:00
Erik Ziegler 163fee8941 fix(Viewer): Set thumbnail to use altImageText for SEG images (#699) 2019-07-22 13:29:12 +02:00
Erik Ziegler f547fdf832 fix(OpenIDConnect): Stop storing tokens in sessionStorage. Prefix OIDC routes automatically. Add logout button (#698) 2019-07-21 16:43:42 +02:00
Erik Ziegler ac347849d0 fix(config/google.js): Add metadata endpoints and signingKeys to avoid CORS errors on Google's certificate endpoint (#696) 2019-07-21 10:46:11 +02:00
Erik Ziegler 54e0e32e22 fix(package.json): Bump ohif-core, extension-microscopy, and dicomweb-client (#697) 2019-07-21 10:04:07 +02:00
Erik Ziegler 690156d4b6 @ohif/extension-dicom-microscopy: Update dicom-microscopy-viewer dependency (#695) 2019-07-21 09:34:16 +02:00
Erik Ziegler 3b5fdb678a docs(googleCloud): Fix port in docs to correspond to defaults in config (Thanks @Stormert) 2019-07-21 09:13:23 +02:00
Erik Ziegler e7604c4418 fix(Viewer): Switch to middle of stack for image thumbnail 2019-07-20 16:41:27 +02:00
Erik Ziegler 71f11a4856 fix(package.json): Update microscopy, pdf, and html extensions to fix thumbnail labels 2019-07-19 18:24:43 +02:00
Erik Ziegler ae5d0587db @ohif/extension-dicom-pdf: Return modality from sopClassHandler (0.0.8) 2019-07-19 18:24:43 +02:00
Erik Ziegler fbc06329e2 @ohif/extension-dicom-html: Return modality from sopClassHandler (0.0.4) 2019-07-19 18:24:43 +02:00
Erik Ziegler 86732c8186 @ohif/extension-dicom-microscopy: Return modality from sopClassHandler (0.0.10) 2019-07-19 18:24:43 +02:00
Erik Ziegler a34d47c850 fix(package.json): Bump cornerstone-tools to 3.16.0 (#690) 2019-07-19 17:48:29 +02:00
Erik Ziegler f758741950 fix(package.json): Update @ohif/extension-cornerstone to fix loading issue (#689) 2019-07-19 17:07:35 +02:00
Erik Ziegler 29a470103a @ohif/extension-cornerstone: Bump react-cornerstone-viewport dependency (#688) 2019-07-19 16:50:07 +02:00
Erik Ziegler 104343aad5 fix(package.json): Bump @ohif/vtk-extension usage to fix performance issues, add Slab MIP (#686) 2019-07-18 13:52:06 +02:00
Erik Ziegler d8b4a4d4c0 @ohif/extension-vtk: Add basic MIP and Slab Thickness controls. UI to be updated later (#685) 2019-07-18 13:39:00 +02:00
Erik Ziegler 2fd60f3109 docs(google-cloud): Fix OAuth Callback URL (#683) 2019-07-18 11:18:56 +02:00
Erik Ziegler 08ce30e69c @ohif/extension-vtk: Update VTK extension (#679) 2019-07-18 07:54:53 +02:00
allcontributors[bot] 546f6bfe68 docs: add pavertomato as a contributor (#678)
* docs: update README.md

* docs: update .all-contributorsrc
2019-07-17 11:26:24 -04:00
Egor Lezhnin 8e1f15e031 fix(config): Fix hardcoded default Google Cloud OAuth redirect_uri (#585) (#675) 2019-07-17 17:04:15 +02:00
David Wire 0d5d804741 fix(availableTools): Add the Eraser to the available tools (#666)
* add the Eraser to the available tools

* review response - remove unnecessary config from eraser tool

* remove cargo culted mouseButtonMasks
2019-07-17 09:58:33 -04:00
David Wire ca735fa070 fix(availableTools): add ArrowAnnotate as an available tool (#667)
* add ArrowAnnotate as an available tool

* review response - remove the cargo culted mouseButtonMasks
2019-07-17 09:45:29 -04:00
David Wire 3d425f54cc fix(ToolbarRow): Update ToolbarRow so that an activeButton in a submenu is passed in as an activeCom (#668) 2019-07-17 10:13:59 +02:00
Romulo Bordezani 7bc9b45853 feat(svfCursorsSupport): enable CornerstoneTools SVG Icons on OHIF Viewer (#659) (#672)
- Fixes the issue 659.
2019-07-16 22:41:41 -04:00
João Felipe de Medeiros Moreira 5bd3af0cca fix(Viewer): Removes fixed modality and get the one returned from ohif-core (#664)
* fix(Viewer): Removes fixed modality insert and starting using the information returned from ohif-cor

* fix(Viewer): Includes condition when display set does not have modality information to use UN for un
2019-07-15 10:09:58 -04:00
Erik Ziegler d4aee36542 fix(GoogleCloud): Fix Cloud Healthcare file upload buttons (#658)
fix #654
2019-07-11 23:32:49 +02:00
dependabot[bot] 2a281da19f chore(deps): bump lodash.merge in /extensions/ohif-i18n (#647)
Bumps [lodash.merge](https://github.com/lodash/lodash) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2019-07-11 09:54:49 -07:00
dependabot[bot] 4631218dcb chore(deps): bump lodash.merge in /extensions/ohif-vtk-extension (#646)
Bumps [lodash.merge](https://github.com/lodash/lodash) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2019-07-11 09:45:47 -07:00
Erik Ziegler 8b3e5dffb1 fix(filesToStudies): Improve error handling for local file viewing (#652) 2019-07-11 14:34:29 +02:00
Erik Ziegler ef6819cf40 fix(docker-google-cloud): Update docker build process to allow Google Cloud CLIENT_ID to be passed in at runtime, update docs. (#651) 2019-07-11 14:08:00 +02:00
Erik Ziegler 2f104b5e91 feat(ViewerLocalFileData): Add drag/drop to load DICOM files in the Viewer at /local (#644) 2019-07-11 13:49:29 +02:00
Erik Ziegler f256ac5a7d fix(package.json): Fix issues with babel-core causes undefined _objectSpread2 (#650) 2019-07-11 12:59:33 +02:00
Erik Ziegler 3a4ee9e346 fix(DicomUploadService): Remove require usage (#649) 2019-07-11 12:18:25 +02:00
Biharck Araujo 1a9cfea365 fix(ToolbarRow): Add i18n string for Series switcher (#634) 2019-07-11 11:32:09 +02:00
Erik Ziegler 9055f19fb5 ci(Add e2e test for script tag usage to CI): (#648) 2019-07-11 11:06:32 +02:00
Erik Ziegler bcbc5e4bbf fix(yarn.lock): Blow away lockfile because it seems to be broken for some users (#643) 2019-07-11 10:18:21 +02:00
dependabot[bot] 408ba36f4f chore(deps): bump lodash.merge from 4.6.1 to 4.6.2 in /docs/latest (#645)
Bumps [lodash.merge](https://github.com/lodash/lodash) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2019-07-11 09:01:59 +02:00
Erik Ziegler 091cab49e0 refactor(GoogleCloud): Clean up some of the Healthcare API Adapter components (#641) 2019-07-10 14:54:27 +02:00
Egor Lezhnin 4a09f33446 feat(GoogleCloud): Initial commits to include the Healthcare API Adapter (#584)
* This is a React port of https://github.com/quantumsoftgroup/healthcare-api-adapter - It still needs some UI cleanup.
2019-07-10 09:26:19 +02:00
Erik Ziegler 4095629310 fix(package.json): Bump @ohif/extension-cornerstone to 0.0.40 (#638) 2019-07-09 18:38:31 +02:00
Erik Ziegler 91d2d31846 @ohif/extension-cornerstone: Prevent errors when display set is empty object (0.0.40) (#637) 2019-07-09 18:17:13 +02:00
João Felipe de Medeiros Moreira ff2c1cb2f1 test(E2E): End-to-end tests for script tag (#633)
fix #621
2019-07-09 18:08:18 +02:00
Gustavo André Lelis 727c0d8ba6 fix(labelling): Update naming of configuration to props as cornerstoneTools has also changed (#484) 2019-07-08 21:38:03 +02:00
João Felipe de Medeiros Moreira 4b3994e788 feat(StudyList): Disable study list link/route using 'showStudyList' config property (#597)
fix #574
2019-07-08 21:29:59 +02:00
Erik Ziegler 571dd5848f fix(ViewerRouting): Fix routing failures for /study/x/series/y (#617)
re #614
2019-07-08 18:27:08 +02:00
David Wire 2d48d0aa37 feat(StudyList): Add QIDO style query parameters to set initial filters (#543)
This pull request adds the ability to use query parameters to pass filters into the StudyList from
the url. These parameters are of the same form used by the QIDO requests to apply the filters. This
solution seemed to me to be the most obvious way to manage them. There's one open question however,
around what to do with the parameters after the initial load. For now, the URL parameters are
overridden the moment the user tries to add another filter. Depending on review feedback it seems
like it may make more sense for those query parameter filters to be "permanent" and so any further
filtering would augment those parameters rather than replace them.
2019-07-08 18:15:06 +02:00
Erik Ziegler 562d63fa74 fix(package.json): Update @ohif/extension-dicom-microscopy to fix resizing behaviour
fix #550
2019-07-08 14:31:23 +02:00
biharck 1a6246f99b @ohif/extension-dicom-microscopy: Add resize call when the container has resized (0.0.9) 2019-07-08 14:31:23 +02:00
Erik Ziegler ce1a62a961 chore(gitattributes): Fix gitattributes handling of .fig files 2019-07-08 13:49:56 +02:00
Evren Ozkan dd6acb2d03 feat(extension-cornerstone): Add additional tools: Magnify, ROI Window, Probe, Ellipse, Rectangle, Invert, Rotate Right, Flip H, Flip V, Clear 2019-07-08 13:49:56 +02:00
120 changed files with 3727 additions and 918 deletions

No files matched your search

+9
View File
@@ -87,6 +87,15 @@
"contributions": [
"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
View File
@@ -75,6 +75,7 @@ jobs:
# Could do a check here, if it doesn't exist, install cypress?
- run: yarn install
- run: yarn run test:e2e:ci
- run: yarn run test:e2e:scriptTag
npm_publish:
<<: *defaults
+7
View File
@@ -1,5 +1,6 @@
# Output
dist/
build/
# Dependencies
node_modules/
@@ -7,6 +8,7 @@ node_modules/
# Root
README.md
Dockerfile
dockerfile
# Misc. Config
.git
@@ -14,3 +16,8 @@ Dockerfile
.gitignore
.vscode
.circleci
# Unnecessary things to pull into container
extensions
docs
cypress
+2 -1
View File
@@ -36,4 +36,5 @@
*.pdf binary
*.ez binary
*.bz2 binary
*.swp binary
*.swp binary
*.fig binary
+3
View File
@@ -28,3 +28,6 @@ docker/dcm4che/dcm4che-arc
# Cypress test results
cypress/videos/
# Locize settings
.locize
+16 -4
View File
@@ -27,22 +27,34 @@ FROM node:11.2.0-slim as builder
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
ENV PATH /usr/src/app/node_modules/.bin:$PATH
ENV GENERATE_SOURCEMAP=false
COPY package.json /usr/src/app/package.json
COPY yarn.lock /usr/src/app/yarn.lock
ADD . /usr/src/app/
# Run the install before copying the rest of the files
RUN yarn install
ENV PATH /usr/src/app/node_modules/.bin:$PATH
ENV GENERATE_SOURCEMAP=false
ENV REACT_APP_CONFIG=config/default.js
# White list instead of copying the whole directory
COPY src /usr/src/app/src
COPY public /usr/src/app/public
COPY .babelrc /usr/src/app/.babelrc
COPY .eslintrc /usr/src/app/.eslintrc
RUN yarn run build:web
# Stage 2: Bundle the built application into a Docker container
# which runs Nginx using Alpine Linux
FROM nginx:1.15.5-alpine
RUN apk add --no-cache bash
RUN rm -rf /etc/nginx/conf.d
COPY docker/Viewer-v2.x /etc/nginx/conf.d
COPY docker/Viewer-v2.x/entrypoint.sh /usr/src/
RUN chmod 777 /usr/src/entrypoint.sh
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
EXPOSE 80
EXPOSE 443
ENTRYPOINT ["/usr/src/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
+17 -2
View File
@@ -21,7 +21,7 @@
[![CircleCI][circleci-image]][circleci-url]
[![codecov][codecov-image]][codecov-url]
[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors)
[![code style: prettier][prettier-image]][prettier-url]
[![semantic-release][semantic-image]][semantic-url]
@@ -220,7 +220,22 @@ Thanks goes to these wonderful people
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore -->
<table><tr><td align="center"><a href="https://github.com/swederik"><img src="https://avatars3.githubusercontent.com/u/607793?v=4" width="100px;" alt="Erik Ziegler"/><br /><sub><b>Erik Ziegler</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=swederik" title="Code">💻</a> <a href="#infra-swederik" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td><td align="center"><a href="https://github.com/evren217"><img src="https://avatars1.githubusercontent.com/u/4920551?v=4" width="100px;" alt="Evren Ozkan"/><br /><sub><b>Evren Ozkan</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=evren217" title="Code">💻</a></td><td align="center"><a href="https://github.com/galelis"><img src="https://avatars3.githubusercontent.com/u/2378326?v=4" width="100px;" alt="Gustavo André Lelis"/><br /><sub><b>Gustavo André Lelis</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=galelis" title="Code">💻</a></td><td align="center"><a href="http://dannyrb.com/"><img src="https://avatars1.githubusercontent.com/u/5797588?v=4" width="100px;" alt="Danny Brown"/><br /><sub><b>Danny Brown</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=dannyrb" title="Code">💻</a> <a href="#infra-dannyrb" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td><td align="center"><a href="https://github.com/all-contributors/all-contributors-bot"><img src="https://avatars3.githubusercontent.com/u/46843839?v=4" width="100px;" alt="allcontributors[bot]"/><br /><sub><b>allcontributors[bot]</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=allcontributors" title="Documentation">📖</a></td><td align="center"><a href="https://www.linkedin.com/in/siliconvalleynextgeneration/"><img src="https://avatars0.githubusercontent.com/u/1230575?v=4" width="100px;" alt="Esref Durna"/><br /><sub><b>Esref Durna</b></sub></a><br /><a href="#question-EsrefDurna" title="Answering Questions">💬</a></td><td align="center"><a href="https://github.com/diego0020"><img src="https://avatars3.githubusercontent.com/u/7297450?v=4" width="100px;" alt="diego0020"/><br /><sub><b>diego0020</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=diego0020" title="Code">💻</a></td></tr><tr><td align="center"><a href="https://github.com/dlwire"><img src="https://avatars3.githubusercontent.com/u/1167291?v=4" width="100px;" alt="David Wire"/><br /><sub><b>David Wire</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=dlwire" title="Code">💻</a></td><td align="center"><a href="https://github.com/jfmedeiros1820"><img src="https://avatars1.githubusercontent.com/u/2211708?v=4" width="100px;" alt="João Felipe de Medeiros Moreira"/><br /><sub><b>João Felipe de Medeiros Moreira</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=jfmedeiros1820" title="Tests">⚠️</a></td></tr></table>
<table>
<tr>
<td align="center"><a href="https://github.com/swederik"><img src="https://avatars3.githubusercontent.com/u/607793?v=4" width="100px;" alt="Erik Ziegler"/><br /><sub><b>Erik Ziegler</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=swederik" title="Code">💻</a> <a href="#infra-swederik" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/evren217"><img src="https://avatars1.githubusercontent.com/u/4920551?v=4" width="100px;" alt="Evren Ozkan"/><br /><sub><b>Evren Ozkan</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=evren217" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/galelis"><img src="https://avatars3.githubusercontent.com/u/2378326?v=4" width="100px;" alt="Gustavo André Lelis"/><br /><sub><b>Gustavo André Lelis</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=galelis" title="Code">💻</a></td>
<td align="center"><a href="http://dannyrb.com/"><img src="https://avatars1.githubusercontent.com/u/5797588?v=4" width="100px;" alt="Danny Brown"/><br /><sub><b>Danny Brown</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=dannyrb" title="Code">💻</a> <a href="#infra-dannyrb" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/all-contributors/all-contributors-bot"><img src="https://avatars3.githubusercontent.com/u/46843839?v=4" width="100px;" alt="allcontributors[bot]"/><br /><sub><b>allcontributors[bot]</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=allcontributors" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/siliconvalleynextgeneration/"><img src="https://avatars0.githubusercontent.com/u/1230575?v=4" width="100px;" alt="Esref Durna"/><br /><sub><b>Esref Durna</b></sub></a><br /><a href="#question-EsrefDurna" title="Answering Questions">💬</a></td>
<td align="center"><a href="https://github.com/diego0020"><img src="https://avatars3.githubusercontent.com/u/7297450?v=4" width="100px;" alt="diego0020"/><br /><sub><b>diego0020</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=diego0020" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/dlwire"><img src="https://avatars3.githubusercontent.com/u/1167291?v=4" width="100px;" alt="David Wire"/><br /><sub><b>David Wire</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=dlwire" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jfmedeiros1820"><img src="https://avatars1.githubusercontent.com/u/2211708?v=4" width="100px;" alt="João Felipe de Medeiros Moreira"/><br /><sub><b>João Felipe de Medeiros Moreira</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=jfmedeiros1820" title="Tests">⚠️</a></td>
<td align="center"><a href="http://egor.lezhn.in"><img src="https://avatars0.githubusercontent.com/u/878990?v=4" width="100px;" alt="Egor Lezhnin"/><br /><sub><b>Egor Lezhnin</b></sub></a><br /><a href="https://github.com/OHIF/Viewers/commits?author=pavertomato" title="Code">💻</a></td>
</tr>
</table>
<!-- ALL-CONTRIBUTORS-LIST:END -->
+52
View File
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description" content="Open Health Imaging Foundation DICOM Viewer" />
<meta name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<meta name="theme-color" content="#000000" />
<meta http-equiv="cleartype" content="on" />
<meta name="MobileOptimized" content="320" />
<meta name="HandheldFriendly" content="True" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" />
<title>OHIF Standalone Viewer</title>
</head>
<body>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
<script src="/dist/index.umd.js" crossorigin></script>
<script>
window.config = {
// default: '/'
routerBasename: '/',
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',
},
],
},
}
window.OHIFStandaloneViewer.installViewer(window.config);
</script>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
# If CLIENT_ID is specified, use the google.js configuration with the modified ID
if [ ! -z "$CLIENT_ID" ]
then
echo "Google Cloud Healthcare $CLIENT_ID has been provided: "
echo $CLIENT_ID
echo "Updating config..."
# - Use SED to replace the CLIENT_ID that is currently in public/config/google.js
sed -i -e "s/YOURCLIENTID.apps.googleusercontent.com/$CLIENT_ID/g" /usr/share/nginx/html/config/google.js
# - Copy public/config/google.js to overwrite public/config/default.js
cp /usr/share/nginx/html/config/google.js /usr/share/nginx/html/config/default.js
fi
echo "Starting Nginx to serve the OHIF Viewer..."
exec "$@"
@@ -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
REACT_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
```
+7 -7
View File
@@ -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"
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "0.0.38",
"version": "0.0.43",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -39,7 +39,7 @@
"@babel/runtime": "^7.2.0",
"classnames": "^2.2.6",
"lodash.throttle": "^4.1.1",
"react-cornerstone-viewport": "0.1.30"
"react-cornerstone-viewport": "0.1.32"
},
"devDependencies": {
"@babel/core": "^7.2.2",
@@ -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,16 +91,77 @@ 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);
}
};
const definitions = {
@@ -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 {
@@ -4421,10 +4421,10 @@ randomfill@^1.0.3:
randombytes "^2.0.5"
safe-buffer "^5.1.0"
react-cornerstone-viewport@0.1.30:
version "0.1.30"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.30.tgz#3546a5164feef5f1f3b45ade10fd9a66f82fd101"
integrity sha512-DGxdpS7FJvWi57NhpKhKMdaW3go/Fs/zfWJFtaEzGVHq2zQnEEV5G1pamC4ttRhKMfzy7T7y4h14xUVzE7IqPA==
react-cornerstone-viewport@0.1.32:
version "0.1.32"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.32.tgz#0777d1f3497b27b017aa5eae3d1d4c078383219b"
integrity sha512-NdFsRq4kMgE6zpZo+DEnlQzC2B6OxOgFTkLCTDTYif0IxoXzKhngXP4xujwrUGjud98mTQKtiJOM0ah/fns3og==
dependencies:
lodash.debounce "^4.0.8"
moment "^2.23.0"
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-html",
"version": "0.0.3",
"version": "0.0.4",
"description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF",
"license": "MIT",
@@ -24,6 +24,7 @@ const OHIFDicomHtmlSopClassHandler = {
return {
plugin: 'html',
modality: 'SR',
displaySetInstanceUid: utils.guid(),
wadoRoot: study.getData().wadoRoot,
wadoUri: instance.getData().wadouri,
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-microscopy",
"version": "0.0.8",
"version": "0.0.12",
"description": "OHIF extension for Dicom Microscopy",
"author": "OHIF",
"license": "MIT",
@@ -19,9 +19,10 @@
"lint": "eslint -c .eslintrc --fix src && prettier --single-quote --write src/**/*.{js,jsx,json,css}"
},
"peerDependencies": {
"ohif-core": "^0.6.0",
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0",
"ohif-core": "^0.6.0"
"react-resize-detector": "^4.2.0"
},
"devDependencies": {
"@babel/core": "^7.2.2",
@@ -81,6 +82,7 @@
"dependencies": {
"@babel/runtime": "^7.2.0",
"classnames": "^2.2.6",
"dicom-microscopy-viewer": "^0.4.3"
"dicom-microscopy-viewer": "0.6.1",
"lodash.debounce": "^4.0.8"
}
}
@@ -10,6 +10,7 @@ import builtins from 'rollup-plugin-node-builtins';
const globals = {
react: 'React',
'react-resize-detector': 'ReactResizeDetector',
'react-dom': 'ReactDOM',
'ohif-core': 'OHIF'
};
@@ -52,6 +53,15 @@ export default {
],
'.yalc/dicom-microscopy-viewer/build/dicom-microscopy-viewer.js': [
'api'
],
'node_modules/react-resize-detector/node_modules/prop-types/index.js': [
'bool',
'number',
'string',
'shape',
'func',
'any',
'node'
]
}
})
@@ -16,6 +16,7 @@ const DicomMicroscopySopClassHandler = {
return {
plugin: 'microscopy',
modality: 'SM',
displaySetInstanceUid: utils.guid(),
dicomWebClient,
sopInstanceUid: instance.getSOPInstanceUID(),
@@ -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;
@@ -1617,18 +1617,18 @@ des.js@^1.0.0:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
dicom-microscopy-viewer@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/dicom-microscopy-viewer/-/dicom-microscopy-viewer-0.4.3.tgz#defaa821026765bcd109b97258742df72a9b92bb"
integrity sha512-PjT3qH0hv6dKOQzXFIHw4w/d8QCbiUOJOGZHvAkkRvnjeNRP7/ri8vbFvcSev+nYxKskNb1Tn0xd1+dsDpPGNw==
dicom-microscopy-viewer@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/dicom-microscopy-viewer/-/dicom-microscopy-viewer-0.6.1.tgz#7b593da241d048154a9c1ebcbccf389742c2af18"
integrity sha512-ZQ1QsQswAh7Yw01ba3LZXCIh7WnEYDjhSxJt9vdfRZ5iHlbgW2ghvUHR7GP80UnWJg335scNmIcqJvwxVjlIsg==
dependencies:
dicomweb-client "^0.4.2"
dicomweb-client "^0.5.2"
ol "^5.3.0"
dicomweb-client@^0.4.2:
version "0.4.4"
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.4.4.tgz#0ca0c7706556f3114330a3c40946ee66808148e4"
integrity sha512-5S7wLYxQgHnOyEgR1zlZlt79IjzOk+YDB/Jjzte6ijRZsAoj9RUiIZcsTyxvBD9gLMYs/954n2kYHdbz83Xbtw==
dicomweb-client@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.5.2.tgz#aa5a4a6a5044b0702bb0a8a2662c86cd16901951"
integrity sha512-e11n2+g7HfBuMyopWq76W11eSZJ707g4U1XiO8URA+23aoe0g1kGAtlPB29NKXl7zX7RznTJ7wfRBxFkW6EJDA==
diffie-hellman@^5.0.0:
version "5.0.3"
@@ -3199,6 +3199,11 @@ lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -3238,6 +3243,7 @@ longest-streak@^2.0.1:
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-pdf",
"version": "0.0.7",
"version": "0.0.8",
"description": "OHIF extension for Dicom PDF",
"author": "OHIF",
"license": "MIT",
@@ -14,6 +14,7 @@ const OHIFDicomPDFSopClassHandler = {
return {
plugin: 'pdf',
modality: 'DOC',
displaySetInstanceUid: utils.guid(),
wadoRoot: study.getData().wadoRoot,
wadoUri: instance.getData().wadouri,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/i18n",
"version": "0.2.2",
"version": "0.2.3",
"description": "OHIF extension for internationalization",
"author": "OHIF",
"license": "MIT",
+2
View File
@@ -1,4 +1,6 @@
rm -rf src/locales/
mkdir -p src/locales/
cd src/locales/
npx locize --config-path ../../.locize download --ver latest
cd ../../
node ./writeLocaleIndexFiles.js
@@ -39,4 +39,4 @@
"Stop": "$t(Common:Stop)",
"Themes": "Themes",
"Zoom": "Zoom"
}
}
@@ -5,4 +5,4 @@
"Skip to first image": "Skip to first $t(Common:Image)",
"Skip to last image": "Skip, to last $t(Common:Image)",
"fps": "fps"
}
}
@@ -1,10 +1,15 @@
{
"Image": "Image",
"Layout": "Layout",
"Measurements": "Measurements",
"More": "More",
"Next": "Next",
"Play": "Play",
"Previous": "Previous",
"Reset": "Reset",
"Stop": "Stop"
}
"RowsPerPage": "rows per page",
"Series": "Series",
"Show": "Show",
"Stop": "Stop",
"StudyDate": "Study Date"
}
@@ -5,4 +5,4 @@
"Options": "Options",
"Preferences": "Preferences",
"Study list": "Study list"
}
}
@@ -6,4 +6,4 @@
"NonTargets": "NonTargets",
"Relabel": "Relabel",
"Targets": "Targets"
}
}
@@ -0,0 +1,10 @@
{
"AccessionNumber": "Accession #",
"Empty": "Empty",
"MRN": "MRN",
"Modality": "Modality",
"PatientName": "Patient Name",
"StudyDate": "Study Date",
"StudyDescription": "Description",
"StudyList": "Study List"
}
@@ -3,4 +3,4 @@
"Reset to Defaults": "$t(Buttons:Reset to Defaults)",
"Save": "$t(Buttons:Save)",
"User Preferences": "User Preferences"
}
}
@@ -1,17 +1,19 @@
import Buttons from './Buttons.json';
import CineDialog from './CineDialog.json';
import Common from './Common.json';
import Header from './Header.json';
import MeasurementTable from './MeasurementTable.json';
import UserPreferencesModal from './UserPreferencesModal.json';
import Buttons from "./Buttons.json";
import CineDialog from "./CineDialog.json";
import Common from "./Common.json";
import Header from "./Header.json";
import MeasurementTable from "./MeasurementTable.json";
import StudyList from "./StudyList.json";
import UserPreferencesModal from "./UserPreferencesModal.json";
export default {
export default {
'en-US': {
Buttons,
CineDialog,
Common,
Header,
MeasurementTable,
StudyList,
UserPreferencesModal,
},
};
}
};
@@ -1,27 +1,27 @@
{
"Acquired": "Acquired",
"Acquired": "Adquirido",
"Angle": "Ángulo",
"Axial": "Axial",
"Bidirectional": "Bidirectional",
"Brush": "Escoba",
"Bidirectional": "Bidireccional",
"Brush": "Cepillo",
"CINE": "CINE",
"Cancel": "Cancelar",
"Circle": "Circle",
"Circle": "Círculo",
"Clear": "Limpiar",
"Coronal": "Coronal",
"Crosshairs": "Cruces",
"Crosshairs": "Punto de mira",
"Ellipse": "Elipse",
"Elliptical": "Elliptical",
"Flip H": "Espejo Hor.",
"Flip V": "Espejo Ver.",
"Freehand": "Freehand",
"Invert": "Invertido",
"Elliptical": "Elíptico",
"Flip H": "Espejo H",
"Flip V": "Espejo V",
"Freehand": "Mano alzada",
"Invert": "Invertir",
"Layout": "$t(Common:Layout)",
"Length": "Medición",
"Length": "Longitud",
"Levels": "Niveles",
"Magnify": "Lupa",
"Magnify": "Aumentar",
"Manual": "Manual",
"Measurements": "Medidas",
"Measurements": "Mediciones",
"More": "$t(Common:More)",
"Next": "$t(Common:Next)",
"Pan": "Mover",
@@ -31,12 +31,12 @@
"ROI Window": "Ventana ROI",
"Rectangle": "Rectángulo",
"Reset": "$t(Common:Reset)",
"Reset to Defaults": "$t(Common:reset) por defectos",
"Reset to Defaults": "$t(Common:Reset) por defecto",
"Rotate Right": "Rotar ->",
"Sagittal": "Sagittal",
"Sagittal": "Sagital",
"Save": "Guardar",
"Stack Scroll": "Avance X slice",
"Stack Scroll": "Scroll en pila",
"Stop": "$t(Common:Stop)",
"Themes": "Temas",
"Zoom": "Zoom"
}
}
@@ -1,8 +1,8 @@
{
"Next image": "$t(Common:Play) $t(Common:Image)",
"Play / Stop": "$t(Common:Play) / $t(Common:Stop)",
"Previous image": "$t(Common:Previous) $t(Common:Image)",
"Skip to first image": "Avanza para la primera $t(Common:Image)",
"Skip to last image": "Pular para la ultima $t(Common:Image)",
"fps": "fps"
}
"Next image": " $t(Common:Image) $t(Common:Next)",
"Play / Stop": "$t(Common:Play) / Stop",
"Previous image": "$t(Common:Image) $t(Common:Previous)",
"Skip to first image": "Ir a la primera $t(Common:Image)",
"Skip to last image": "Ir a la última $t(Common:Image)",
"fps": "imágenes/seg."
}
@@ -1,10 +1,15 @@
{
"Image": "Imagen",
"Layout": "Esquema",
"Layout": "Disposición",
"Measurements": "Medidas",
"More": "Más",
"Next": "Próximo",
"Next": "Siguiente",
"Play": "Play",
"Previous": "Anterior",
"Reset": "Reiniciar",
"Stop": "Stop"
}
"Reset": "Resetear",
"RowsPerPage": "filas por página",
"Series": "Secuencia",
"Show": "Mostrar",
"Stop": "Detener",
"StudyDate": "Fecha de estudo"
}
@@ -1,8 +1,8 @@
{
"About": "Sobre",
"Back to Viewer": "Back to Viewer",
"INVESTIGATIONAL USE ONLY": "SOLO USO DE INVESTIGACIÓN",
"About": "Acerca de",
"Back to Viewer": "Volver al visor",
"INVESTIGATIONAL USE ONLY": "SOLO USO PARA INVESTIGACIÓN",
"Options": "Opciones",
"Preferences": "Preferencias",
"Study list": "Lista de estudio"
}
"Study list": "Lista de estudios"
}
@@ -1,9 +1,9 @@
{
"Criteria nonconformities": "Criterios de no conformidades",
"Criteria nonconformities": "Criterios disconformes",
"Delete": "Borrar",
"Description": "Descripción",
"MAX": "máximo",
"NonTargets": "NonObjetivos",
"Relabel": "Reetiquetar",
"MAX": "Máximo",
"NonTargets": "No objetivos",
"Relabel": "Re-etiquetar",
"Targets": "Objetivos"
}
}
@@ -0,0 +1,10 @@
{
"AccessionNumber": "Número de Adhesión",
"Empty": "vacío",
"MRN": "MRN",
"Modality": "Modalidad",
"PatientName": "Nombre del paciente",
"StudyDate": "Fetcha de estudio",
"StudyDescription": "Descripción",
"StudyList": "Lista de Estudio"
}
@@ -2,5 +2,5 @@
"Cancel": "$t(Buttons:Cancel)",
"Reset to Defaults": "$t(Buttons:Reset to Defaults)",
"Save": "$t(Buttons:Save)",
"User Preferences": "Preferencias de usuario"
}
"User Preferences": "Preferencias de Usuario"
}
+12 -10
View File
@@ -1,17 +1,19 @@
import Buttons from './Buttons.json';
import CineDialog from './CineDialog.json';
import Common from './Common.json';
import Header from './Header.json';
import MeasurementTable from './MeasurementTable.json';
import UserPreferencesModal from './UserPreferencesModal.json';
import Buttons from "./Buttons.json";
import CineDialog from "./CineDialog.json";
import Common from "./Common.json";
import Header from "./Header.json";
import MeasurementTable from "./MeasurementTable.json";
import StudyList from "./StudyList.json";
import UserPreferencesModal from "./UserPreferencesModal.json";
export default {
es: {
export default {
'es': {
Buttons,
CineDialog,
Common,
Header,
MeasurementTable,
StudyList,
UserPreferencesModal,
},
};
}
};
+5 -1
View File
@@ -1,9 +1,13 @@
import en_US from './en-US/';
import es from './es/';
import nl from './nl/';
import pt_BR from './pt-BR/';
import zh from './zh/';
export default {
...en_US,
...es,
...nl,
};
...pt_BR,
...zh,
};
@@ -3,4 +3,4 @@
"More": "Meer",
"Pan": "Pan",
"Zoom": "Inzoomen"
}
}
@@ -1,3 +1,3 @@
{
"More": "Meer"
}
}
@@ -4,4 +4,4 @@
"Options": "Opties",
"Preferences": "Voorkeuren",
"Study list": "Studie Overzicht"
}
}
+7 -7
View File
@@ -1,11 +1,11 @@
import Buttons from './Buttons.json';
import Common from './Common.json';
import Header from './Header.json';
import Buttons from "./Buttons.json";
import Common from "./Common.json";
import Header from "./Header.json";
export default {
nl: {
export default {
'nl': {
Buttons,
Common,
Header,
},
};
}
};
@@ -0,0 +1,42 @@
{
"Acquired": "Adquirido",
"Angle": "Ângulo",
"Axial": "Axial",
"Bidirectional": "Bidirecional",
"Brush": "Pincel",
"CINE": "CINE",
"Cancel": "Cancelar",
"Circle": "Círculo",
"Clear": "Limpar",
"Coronal": "Coronal",
"Crosshairs": "Localizador",
"Ellipse": "Elipse",
"Elliptical": "Elíptico",
"Flip H": "Inverter H",
"Flip V": "Inverter V",
"Freehand": "Desenho livre",
"Invert": "Inverter",
"Layout": "Layout",
"Length": "Tamanho",
"Levels": "Níveis",
"Magnify": "Ampliar",
"Manual": "Manual",
"Measurements": "Medidas",
"More": "Mais",
"Next": "Próximo",
"Pan": "Arrastar",
"Play": "Tocar",
"Previous": "Anterior",
"Probe": "Prova",
"ROI Window": "Janela ROI",
"Rectangle": "Retângulo",
"Reset": "Resetar",
"Reset to Defaults": "Restaurar para o Padrão",
"Rotate Right": "Girar à direita",
"Sagittal": "Sagital",
"Save": "Salvar",
"Stack Scroll": "Navegar Stacks",
"Stop": "Parar",
"Themes": "Temas",
"Zoom": "Zoom"
}
@@ -0,0 +1,8 @@
{
"Next image": "Próxima imagem",
"Play / Stop": "Tocar / Parar",
"Previous image": "Imagem Anterior",
"Skip to first image": "Pular para a primeira imagem",
"Skip to last image": "Pular para a última imagem",
"fps": "fps"
}
@@ -0,0 +1,10 @@
{
"Image": "Imagem",
"Layout": "Layout",
"More": "Mais",
"Next": "Próximo",
"Play": "Play",
"Previous": "Anterior",
"Reset": "Reset",
"Stop": "Stop"
}
@@ -0,0 +1,8 @@
{
"About": "Quem somos",
"Back to Viewer": "Voltar para o Viewer",
"INVESTIGATIONAL USE ONLY": "APENAS PARA USO INVESTIGATIVO",
"Options": "Opções",
"Preferences": "Preferências",
"Study list": "Lista de estudos"
}
@@ -0,0 +1,6 @@
{
"Cancel": "Cancelar",
"Reset to Defaults": "Restaurar Default",
"Save": "Salvar",
"User Preferences": "Preferências do Usuário"
}
@@ -0,0 +1,15 @@
import Buttons from "./Buttons.json";
import CineDialog from "./CineDialog.json";
import Common from "./Common.json";
import Header from "./Header.json";
import UserPreferencesModal from "./UserPreferencesModal.json";
export default {
'pt-BR': {
Buttons,
CineDialog,
Common,
Header,
UserPreferencesModal,
}
};
@@ -0,0 +1,13 @@
{
"Angle": "线角测量",
"CINE": "播放",
"Length": "长度测量",
"Levels": "窗宽/窗位",
"Measurements": "测量",
"More": "更多",
"Pan": "平移",
"Play": "播放",
"Reset": "重置",
"Rotate Right": "顺时针",
"Zoom": "缩放"
}
@@ -0,0 +1,3 @@
{
"Play": "播放"
}
@@ -0,0 +1,3 @@
{
"Study list": "检查"
}
@@ -0,0 +1,11 @@
import Buttons from "./Buttons.json";
import Common from "./Common.json";
import Header from "./Header.json";
export default {
'zh': {
Buttons,
Common,
Header,
}
};
+3 -3
View File
@@ -3571,9 +3571,9 @@ lodash.memoize@^4.1.2:
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
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.pick@^4.2.1:
version "4.4.0"
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-vtk",
"version": "0.1.3",
"version": "0.1.5",
"description": "OHIF extension for VTK.js",
"author": "OHIF",
"license": "MIT",
@@ -39,8 +39,8 @@
},
"dependencies": {
"@babel/runtime": "^7.4.5",
"react-vtkjs-viewport": "0.0.9",
"vtk.js": "^8.11.0"
"react-vtkjs-viewport": "0.0.11",
"vtk.js": "^8.13.0"
},
"devDependencies": {
"@babel/core": "^7.4.5",
@@ -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 {
+11 -11
View File
@@ -4051,9 +4051,9 @@ lodash.isequal@4.5.0:
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.merge@^4.6.1:
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
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@^4.1.1, lodash@^4.17.11:
version "4.17.11"
@@ -5519,10 +5519,10 @@ react-viewerbase@^0.15.1:
react-i18next "^10.11.0"
react-with-direction "1.3.0"
react-vtkjs-viewport@0.0.9:
version "0.0.9"
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.0.9.tgz#da79aa66f52dcd731bcc5abd2587cbb120a25331"
integrity sha512-VwRCY2DPxWMhix4v3PAtdENp3U1p+Z9nKf1Ch5+KWRKF6fWeA+B2glYKMYNpPnP4bULzVz9iwV+Zeojt3gEibA==
react-vtkjs-viewport@0.0.11:
version "0.0.11"
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.0.11.tgz#cedd993ca226502bf0417a3748a5b2f806522519"
integrity sha512-ttWw7bU/lL/nJuaaKdtAc8448vMJmlRQzpkpuP/J+3lTzIQbQPCPU40LqgONjr7LvL5++61N2GJ8m+PRgHyfxQ==
dependencies:
moment "^2.24.0"
@@ -6974,10 +6974,10 @@ void-elements@^2.0.1:
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
vtk.js@^8.11.0:
version "8.11.1"
resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-8.11.1.tgz#cdf69705a72a2a3ce76d7442d1b3903e3de770d4"
integrity sha512-Y5ZCFtoMOuuJt44fyj91Gqeg9ZeCQovyoJlEHG1oUHLPehQTvq0oW09WqBcxVSdiv92x3aXYWtgZjm/s4EWw7Q==
vtk.js@^8.13.0:
version "8.13.0"
resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-8.13.0.tgz#d22bbe3403d86f979703506f9be83ab8e5099248"
integrity sha512-uoq2U0Yu2XjuKeeduuGRXlphHR6cdIvtZ5useNMJ8FobK9dpeFTT4ncoyPXx77KvwOMSueT8W+ngm/e9+FGZtA==
dependencies:
base64-js "1.2.1"
blueimp-md5 "2.10.0"
+18 -17
View File
@@ -28,6 +28,7 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --reporters=default --reporters=jest-junit && codecov",
"test:e2e": "",
"test:e2e:ci": "start-server-and-test start http://localhost:5000 cy:run:ci",
"test:e2e:scriptTag": "cross-env START_SERVER=true start-server-and-test build:package http-get://localhost:5000/index.umd.js cy:run",
"staticDeploy": "./generateStaticSite.sh",
"docs:publish": "./publishStaticSite.sh",
"start": "yarn run dev",
@@ -59,6 +60,7 @@
"not op_mini all"
],
"resolutions": {
"@babel/core": "^7.5.4",
"browserslist": "4.6.2",
"caniuse-lite": "1.0.30000974"
},
@@ -70,42 +72,41 @@
"path": "./node_modules/cz-conventional-changelog"
}
},
"peerDependencies": {
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"dependencies": {
"@babel/runtime": "^7.4.5",
"@ohif/extension-cornerstone": "0.0.38",
"@ohif/extension-dicom-html": "0.0.3",
"@ohif/extension-dicom-microscopy": "0.0.8",
"@ohif/extension-dicom-pdf": "0.0.7",
"@ohif/extension-vtk": "0.1.3",
"@ohif/i18n": "0.2.2",
"@ohif/extension-cornerstone": "0.0.43",
"@ohif/extension-dicom-html": "0.0.4",
"@ohif/extension-dicom-microscopy": "0.0.12",
"@ohif/extension-dicom-pdf": "0.0.8",
"@ohif/extension-vtk": "0.1.5",
"@ohif/i18n": "0.2.3",
"@tanem/react-nprogress": "^1.1.25",
"classnames": "^2.2.6",
"cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8",
"cornerstone-tools": "^3.15.1",
"cornerstone-tools": "^3.16.0",
"cornerstone-wado-image-loader": "^2.2.3",
"dcmjs": "^0.4.7",
"dicom-parser": "^1.8.3",
"dicomweb-client": "^0.4.4",
"dicomweb-client": "^0.5.2",
"hammerjs": "^2.0.8",
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
"lodash.isequal": "4.5.0",
"moment": "^2.24.0",
"ohif-core": "0.9.1",
"oidc-client": "1.7.x",
"ohif-core": "0.10.6",
"oidc-client": "1.8.x",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-dropzone": "^10.1.5",
"react-i18next": "^10.11.0",
"react-redux": "^7.1.0",
"react-resize-detector": "^4.2.0",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-transition-group": "^4.1.1",
"react-viewerbase": "0.15.3",
"react-viewerbase": "0.17.6",
"redux": "^4.0.1",
"redux-logger": "^3.0.6",
"redux-oidc": "3.1.x",
@@ -143,8 +144,6 @@
"lodash": "4.17.11",
"lodash.clonedeep": "4.5.0",
"prettier": "1.18.x",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-scripts": "^3.0.1",
"rollup": "^1.15.5",
"rollup-plugin-babel": "^4.3.2",
@@ -154,6 +153,8 @@
"rollup-plugin-node-resolve": "^5.0.2",
"rollup-plugin-peer-deps-external": "^2.2.0",
"rollup-plugin-postcss": "^2.0.3",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-serve": "^1.0.1",
"rollup-plugin-url": "^2.2.2",
"semantic-release": "15.13.x",
"start-server-and-test": "^1.9.1",
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="228" height="80"><defs><style>@font-face{font-family:"Roboto-Bold";src:url(data:application/font-woff;charset=utf-8;base64,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) format("woff"); font-weight:bold;font-style:normal;}</style></defs><path d="M6 0h216a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z" fill="#52ABD3"/><text font-family="Roboto-Bold, Roboto" font-size="14" font-weight="bold" fill="#fff"><tspan x="72.148" y="45">FILE UPLOAD</tspan></text><path d="M58.285 79.648H40.642L36.616 58.63l-24.92 10.66 11.258 5.986-2.325 4.373H6a6 6 0 0 1-6-6V55.073L22.088 13.53a1 1 0 0 1 1.352-.413l36.2 19.248L68.3 60.813 58.285 79.648zm-27.108 0h-.923l.203-.382.72.382zM54.232 34.02l-9.86 18.542 18.542 9.86-8.683-28.4z" fill="#4591B3"/></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="228" height="80"><defs><style>@font-face{font-family:"Roboto-Bold";src:url(data:application/font-woff;charset=utf-8;base64,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) format("woff"); font-weight:bold;font-style:normal;}</style></defs><path d="M6 0h216a6 6 0 0 1 6 6v68a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z" fill="#52ABD3"/><text font-family="Roboto-Bold, Roboto" font-size="14" font-weight="bold" fill="#fff"><tspan x="60" y="45">FOLDER UPLOAD</tspan></text><path d="M51.706 79.74H39.752L38.32 56.483l-25.104 6.482 10.012 7.274-6.904 9.502H6a6 6 0 0 1-6-6V13.585c2.513-1.005 5.48-.74 7.84.975l19.902 14.46L29.6 40.413l26.536 19.28c3.65 2.65 4.485 7.778 1.858 11.393l-6.288 8.655zm-15.4 0H26.52l3.38-4.653 6.404 4.653z" fill="#4591B3"/></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

+3 -2
View File
@@ -3,6 +3,7 @@ window.config = {
routerBasename: '/',
// default: ''
relativeWebWorkerScriptsPath: '',
showStudyList: true,
servers: {
dicomWeb: [
{
@@ -53,8 +54,8 @@ window.config = {
{ commandName: 'fitViewportToWindow', label: 'Zoom to Fit', keys: ['='] },
{ commandName: 'resetViewport', label: 'Reset', keys: ['space'] },
// clearAnnotations
// nextImage
// previousImage
{ commandName: 'nextImage', label: 'Next Image', keys: ['down'] },
{ commandName: 'previousImage', label: 'Previous Image', keys: ['up'] },
// firstImage
// lastImage
{
+1
View File
@@ -1,5 +1,6 @@
window.config = {
routerBasename: '/',
showStudyList: true,
servers: {
dicomWeb: [
{
+1
View File
@@ -1,5 +1,6 @@
window.config = {
routerBasename: '/',
showStudyList: true,
relativeWebWorkerScriptsPath: '',
servers: {
dicomWeb: [
@@ -1,5 +1,6 @@
window.config = {
routerBasename: '/',
showStudyList: true,
relativeWebWorkerScriptsPath: '',
servers: {
// This is an array, but we'll only use the first entry for now
@@ -1,5 +1,6 @@
window.config = {
routerBasename: '/',
showStudyList: true,
relativeWebWorkerScriptsPath: '',
servers: {
// This is an array, but we'll only use the first entry for now
+27
View File
@@ -0,0 +1,27 @@
window.config = {
routerBasename: '/',
relativeWebWorkerScriptsPath: '',
enableGoogleCloudAdapter: true,
servers: {
// This is an array, but we'll only use the first entry for now
dicomWeb: [],
},
// This is an array, but we'll only use the first entry for now
oidc: [
{
// ~ REQUIRED
// Authorization Server URL
authority: 'https://accounts.google.com',
client_id: 'YOURCLIENTID.apps.googleusercontent.com',
redirect_uri: '/callback', // `OHIFStandaloneViewer.js`
response_type: 'id_token token',
scope: 'email profile openid https://www.googleapis.com/auth/cloudplatformprojects.readonly https://www.googleapis.com/auth/cloud-healthcare', // email profile openid
// ~ OPTIONAL
post_logout_redirect_uri: '/logout-redirect.html',
revoke_uri: 'https://accounts.google.com/o/oauth2/revoke?token=',
automaticSilentRenew: true,
revokeAccessTokenOnSignout: true,
},
],
studyListFunctionsEnabled: true,
}
+2
View File
@@ -2,6 +2,7 @@ window.config = {
// default: '/'
routerBasename: '/',
// default: ''
showStudyList: true,
relativeWebWorkerScriptsPath: '',
servers: {
dicomWeb: [
@@ -20,4 +21,5 @@ window.config = {
},
],
},
studyListFunctionsEnabled: true,
}
+3 -2
View File
@@ -1,5 +1,6 @@
window.config = {
routerBasename: '/demo',
showStudyList: true,
servers: {
dicomWeb: [
{
@@ -48,8 +49,8 @@ window.config = {
{ commandName: 'fitViewportToWindow', label: 'Zoom to Fit', keys: ['='] },
{ commandName: 'resetViewport', label: 'Reset', keys: ['space'] },
// clearAnnotations
// nextImage
// previousImage
{ commandName: 'nextImage', label: 'Next Image', keys: ['down'] },
{ commandName: 'previousImage', label: 'Previous Image', keys: ['up'] },
// firstImage
// lastImage
{
+1
View File
@@ -1,5 +1,6 @@
window.config = {
routerBasename: '/',
showStudyList: true,
servers: {
dicomWeb: [
{
-8
View File
@@ -16,14 +16,6 @@
<title>OHIF Viewer</title>
<!-- Latest compiled and minified CSS -->
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous"
/>
<!-- WEB FONTS -->
<link
href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700|Sanchez&display=swap"
-25
View File
@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OpenID Connect Logout Redirect Page</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-shim.min.js"></script>
<script type="text/javascript" src='es6-shim.min.js'></script>
<script type="text/javascript" src='oidc-client.min.js'></script>
<script type="text/javascript" src='polyfill.min.js'></script>
<script type="text/javascript">
// TODO: I think this is actually not necessary anymore
new UserManager().signoutRedirectCallback().then(function() {
// TODO: Keycloak + Auth0 is not properly logging out both sessions?
// This is a hacky hardcoded workaround. This could also be added as a component to the React application rather than a static page.
var path = 'https://www.crowds-cure.org';
var client_id = 'z5cXMPTxeFOdB3i4xRA8JyhTonQmqMKM';
var url = 'https://auth.crowds-cure.org/v2/logout?client_id=' + client_id + '&returnTo=' + path;
window.location.href = url;
});
</script>
</body>
</html>
+46 -8
View File
@@ -9,11 +9,10 @@ import svgr from '@svgr/rollup'
import pkg from './package.json'
// Deal with https://github.com/rollup/rollup-plugin-commonjs/issues/297
import builtins from 'rollup-plugin-node-builtins'
import replace from 'rollup-plugin-replace';
import serve from 'rollup-plugin-serve'
const globals = {
react: 'React',
'react-dom': 'ReactDOM',
}
const startServer = process.env.START_SERVER === 'true';
export default {
input: 'src/index_publish.js',
@@ -25,25 +24,26 @@ export default {
exports: 'named',
name: 'OHIFStandaloneViewer',
esModule: false,
globals,
},
{
file: pkg.module,
format: 'es',
exports: 'named',
sourcemap: true,
globals,
},
],
plugins: [
external(),
replace({
'process.env.NODE_ENV': JSON.stringify('production')
}),
postcss({
modules: false,
}),
url(),
svgr(),
json(),
resolve({preferBuiltins: true}),
resolve({ preferBuiltins: true }),
babel({
exclude: 'node_modules/**',
runtimeHelpers: true,
@@ -51,6 +51,32 @@ export default {
commonjs({
include: 'node_modules/**',
namedExports: {
'node_modules/react/index.js': [
'createContext',
'createElement',
'createRef',
'cloneElement',
'Children',
'Fragment',
'forwardRef',
'useCallback',
'isValidElement',
'memo',
'useContext',
'useState',
'useEffect',
'useLayoutEffect',
'Component',
'PureComponent',
'useRef',
'useReducer',
'useMemo',
],
'node_modules/react-dom/index.js': [
'unstable_batchedUpdates',
'findDOMNode',
'render',
],
'node_modules/react-is/index.js': [
'isValidElementType',
'isContextConsumer',
@@ -58,18 +84,30 @@ export default {
'node_modules/redux-oidc/dist/redux-oidc.js': [
'reducer',
'CallbackComponent',
'SignoutCallbackComponent',
'loadUser',
'OidcProvider',
'createUserManager',
],
'node_modules/oidc-client/lib/oidc-client.min.js': [
'WebStorageStateStore',
'InMemoryWebStorage',
],
'node_modules/cornerstoneTools/dist/cornerstoneTools.min.js': [
'cornerstoneTools',
],
'node_modules/dcmjs/build/dcmjs.js': ['data', 'adapters'],
'node_modules/prop-types/index.js': ['bool', 'number', 'string', 'shape', 'func', 'any', 'node']
'node_modules/prop-types/index.js': ['oneOfType', 'element', 'bool', 'number', 'string', 'shape', 'func', 'any', 'node']
},
}),
builtins(),
startServer && serve({
open: true,
// Multiple folders to serve from
contentBase: ['.', 'dist', 'cypress/support/script-tag', 'public'],
host: 'localhost',
port: 5000,
})
],
external: Object.keys(pkg.peerDependencies || {}),
}
+44 -9
View File
@@ -37,10 +37,12 @@ import { getActiveContexts } from './store/layout/selectors.js';
import i18n from '@ohif/i18n';
import setupTools from './setupTools.js';
import store from './store';
import UserManagerContext from './UserManagerContext';
// ~~~~ APP SETUP
initCornerstoneTools({
globalToolSyncEnabled: true,
showSVGCursors: true,
});
const commandsManagerConfig = {
@@ -70,7 +72,7 @@ extensionManager.registerExtensions([
]);
// Must run after extension commands are registered
if (window.config.hotkeys) {
if (window.config && window.config.hotkeys) {
hotkeysManager.setHotkeys(window.config.hotkeys, true);
}
@@ -83,6 +85,23 @@ function handleServers(servers) {
}
}
function isAbsoluteUrl(url) {
return url.includes('http://') || url.includes('https://');
}
function makeAbsoluteIfNecessary(url, base_url) {
if (isAbsoluteUrl(url)) {
return url;
}
// Make sure base_url and url are not duplicating slashes
if (base_url[base_url.length - 1] === "/") {
base_url = base_url.slice(0, base_url.length - 1);
}
return base_url + url;
}
class App extends Component {
static propTypes = {
routerBasename: PropTypes.string.isRequired,
@@ -103,9 +122,23 @@ class App extends Component {
if (this.props.oidc.length) {
const firstOpenIdClient = this.props.oidc[0];
const { protocol, host } = window.location;
const { routerBasename } = this.props;
const baseUri = `${protocol}//${host}${routerBasename}`;
const redirect_uri = firstOpenIdClient.redirect_uri || '/callback';
const silent_redirect_uri = firstOpenIdClient.silent_redirect_uri || '/silent-refresh.html';
const post_logout_redirect_uri = firstOpenIdClient.post_logout_redirect_uri || '/';
const openIdConnectConfiguration = Object.assign({}, firstOpenIdClient, {
redirect_uri: makeAbsoluteIfNecessary(redirect_uri, baseUri),
silent_redirect_uri: makeAbsoluteIfNecessary(silent_redirect_uri, baseUri),
post_logout_redirect_uri: makeAbsoluteIfNecessary(post_logout_redirect_uri, baseUri),
});
this.userManager = getUserManagerForOpenIdConnectClient(
store,
firstOpenIdClient
openIdConnectConfiguration,
);
}
handleServers(this.props.servers);
@@ -123,13 +156,15 @@ class App extends Component {
<Provider store={store}>
<I18nextProvider i18n={i18n}>
<OidcProvider store={store} userManager={userManager}>
<Router basename={this.props.routerBasename}>
<WhiteLabellingContext.Provider
value={this.props.whiteLabelling}
>
<OHIFStandaloneViewer userManager={userManager} />
</WhiteLabellingContext.Provider>
</Router>
<UserManagerContext.Provider value={userManager}>
<Router basename={this.props.routerBasename}>
<WhiteLabellingContext.Provider
value={this.props.whiteLabelling}
>
<OHIFStandaloneViewer userManager={userManager} />
</WhiteLabellingContext.Provider>
</Router>
</UserManagerContext.Provider>
</OidcProvider>
</I18nextProvider>
</Provider>
+5
View File
@@ -13,6 +13,11 @@ body,
Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif;
}
body {
font-size: 14px;
line-height: 1.42857143;
}
#root .fade-enter {
opacity: 0;
}
+40 -9
View File
@@ -5,10 +5,12 @@ import { Route, Switch } from 'react-router-dom';
import { NProgress } from '@tanem/react-nprogress';
import { CSSTransition } from 'react-transition-group';
import { connect } from 'react-redux';
import { SignoutCallbackComponent } from 'redux-oidc';
import { ViewerbaseDragDropContext } from 'react-viewerbase';
// import asyncComponent from './components/AsyncComponent.js'
import IHEInvokeImageDisplay from './routes/IHEInvokeImageDisplay.js';
import ViewerRouting from './routes/ViewerRouting.js';
import ViewerLocalFileData from './connectedComponents/ViewerLocalFileData.js';
import StudyListRouting from './studylist/StudyListRouting.js';
import StandaloneRouting from './routes/StandaloneRouting.js';
import CallbackPage from './routes/CallbackPage.js';
@@ -42,6 +44,9 @@ class OHIFStandaloneViewer extends Component {
static propTypes = {
history: PropTypes.object.isRequired,
user: PropTypes.object,
setContext: PropTypes.func,
userManager: PropTypes.object,
location: PropTypes.object,
};
componentDidMount() {
@@ -70,14 +75,29 @@ class OHIFStandaloneViewer extends Component {
return (
<Switch>
<Route exact path="/silent-refresh.html" onEnter={reload} />
<Route exact path="/logout-redirect.html" onEnter={reload} />
<Route exact path="/logout-redirect" render={() =>
<SignoutCallbackComponent
userManager={userManager}
successCallback={() => console.log('Signout successful')}
errorCallback={(error) => {
console.warn(error);
console.warn('Signout failed');
}}
/>
}/>
<Route
path="/callback"
render={() => <CallbackPage userManager={userManager} />}
/>
<Route
component={() => {
userManager.signinRedirect();
userManager.getUser().then(user => {
if (user) {
userManager.signinSilent();
} else {
userManager.signinRedirect();
}
});
return null;
}}
@@ -94,12 +114,8 @@ class OHIFStandaloneViewer extends Component {
*/
const routes = [
{
path: '/',
Component: StudyListRouting,
},
{
path: '/studylist',
Component: StudyListRouting,
path: '/local',
Component: ViewerLocalFileData,
},
{
path: '/viewer',
@@ -110,7 +126,7 @@ class OHIFStandaloneViewer extends Component {
Component: ViewerRouting,
},
{
path: '/study/:studyInstanceUid/series/:seriesInstanceUids',
path: '/study/:studyInstanceUids/series/:seriesInstanceUids',
Component: ViewerRouting,
},
{
@@ -119,6 +135,21 @@ class OHIFStandaloneViewer extends Component {
},
];
const showStudyList =
window.config && window.config.showStudyList !== undefined
? window.config.showStudyList
: true;
if (showStudyList) {
routes.push({
path: '/studylist',
Component: StudyListRouting,
});
routes.push({
path: '/',
Component: StudyListRouting,
});
}
const currentPath = this.props.location.pathname;
const noMatchingRoutes = !routes.find(r =>
matchPath(currentPath, {
+5
View File
@@ -0,0 +1,5 @@
import React from 'react';
const UserManagerContext = React.createContext();
export default UserManagerContext;
@@ -140,6 +140,7 @@ function convertTimepointsToTableData(timepoints) {
return [
{
label: 'Study date:',
key: 'StudyDate',
date: moment(timepoints[0].latestDate).format('DD-MMM-YY'),
},
];
+16 -1
View File
@@ -17,6 +17,7 @@ class Header extends Component {
location: PropTypes.object.isRequired,
children: PropTypes.node,
t: PropTypes.func.isRequired,
userManager: PropTypes.object
};
static defaultProps = {
@@ -59,6 +60,16 @@ class Header extends Component {
},
];
if (this.props.user && this.props.userManager) {
this.options.push({
title: t('Logout'),
icon: { name: 'power-off' },
onClick: () => {
this.props.userManager.signoutRedirect();
},
});
}
this.hotKeysData = hotkeysManager.hotkeyDefinitions;
}
@@ -71,6 +82,10 @@ class Header extends Component {
render() {
const { t } = this.props;
const showStudyList =
window.config.showStudyList !== undefined
? window.config.showStudyList
: true;
return (
<div className={`entry-header ${this.props.home ? 'header-big' : ''}`}>
<div className="header-left-box">
@@ -85,7 +100,7 @@ class Header extends Component {
{this.props.children}
{!this.props.home && (
{showStudyList && !this.props.home && (
<Link
className="header-btn header-studyListLinkSection"
to={{
+7 -1
View File
@@ -4,9 +4,15 @@ import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import dicomParser from 'dicom-parser';
import version from './version.js';
let homepage;
const { process } = window;
if (process && process.env && process.env.PUBLIC_URL) {
homepage = process.env.PUBLIC_URL;
}
window.info = {
version,
homepage: `${process.env.PUBLIC_URL}`,
homepage,
};
// For debugging
@@ -3,6 +3,7 @@ import { connect } from 'react-redux';
const mapStateToProps = state => {
return {
user: state.oidc && state.oidc.user,
isOpen: state.ui.userPreferencesModalOpen,
};
};
+1 -1
View File
@@ -13,7 +13,7 @@ class PluginSwitch extends Component {
render() {
return (
<div className="PluginSwitch">
<ExpandableToolMenu buttons={this.props.buttons} />
<ExpandableToolMenu buttons={this.props.buttons} text={'View'} />
</div>
);
}
+13 -5
View File
@@ -2,9 +2,9 @@ import './ToolbarRow.css';
import React, { Component } from 'react';
import {
ExpandableToolMenu,
RoundedButtonGroup,
ToolbarButton,
ExpandableToolMenu,
} from 'react-viewerbase';
import { commandsManager, extensionManager } from './../App.js';
@@ -13,6 +13,7 @@ import ConnectedLayoutButton from './ConnectedLayoutButton';
import ConnectedPluginSwitch from './ConnectedPluginSwitch.js';
import { MODULE_TYPES } from 'ohif-core';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
class ToolbarRow extends Component {
// TODO: Simplify these? isOpen can be computed if we say "any" value for selected,
@@ -53,7 +54,7 @@ class ToolbarRow extends Component {
{
value: 'studies',
icon: 'th-large',
bottomLabel: 'Series',
bottomLabel: this.props.t('Series'),
},
],
right: [],
@@ -155,10 +156,17 @@ class ToolbarRow extends Component {
*/
function _getButtonComponents(toolbarButtons, activeButtons) {
return toolbarButtons.map((button, index) => {
if (button.buttons) {
let activeCommand = undefined;
if (button.buttons && button.buttons.length) {
// Iterate over button definitions and update `onClick` behavior
const childButtons = button.buttons.map(childButton => {
childButton.onClick = _handleToolbarButtonClick.bind(this, childButton);
if (activeButtons.indexOf(childButton.id) > -1) {
activeCommand = childButton.id;
}
return childButton;
});
@@ -168,10 +176,10 @@ function _getButtonComponents(toolbarButtons, activeButtons) {
text={button.label}
icon={button.icon}
buttons={childButtons}
activeCommand={activeCommand}
/>
);
}
return (
<ToolbarButton
key={button.id}
@@ -239,4 +247,4 @@ function _handleBuiltIn({ behavior } = {}) {
}
}
export default ToolbarRow;
export default withTranslation('Common')(ToolbarRow);
+20 -8
View File
@@ -13,6 +13,7 @@ import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
import ConnectedViewerMain from './ConnectedViewerMain.js';
import SidePanel from './../components/SidePanel.js';
import { extensionManager } from './../App.js';
import UserManagerContext from '../UserManagerContext';
import './Viewer.css';
/**
* Inits OHIF Hanging Protocol's onReady.
@@ -230,9 +231,14 @@ class Viewer extends Component {
{/* HEADER */}
<WhiteLabellingContext.Consumer>
{whiteLabelling => (
<ConnectedHeader home={false}>
{whiteLabelling.logoComponent}
</ConnectedHeader>
<UserManagerContext.Consumer>
{ userManager => (
<ConnectedHeader home={false} userManager={userManager}>
{whiteLabelling.logoComponent}
</ConnectedHeader>
)
}
</UserManagerContext.Consumer>
)}
</WhiteLabellingContext.Consumer>
@@ -319,7 +325,6 @@ export default Viewer;
*
* TODO[react]:
* - Add sorting of display sets
* - Add useMiddleSeriesInstanceAsThumbnail
* - Add showStackLoadingProgressBar option
*
* @param {Study[]} studies
@@ -339,12 +344,19 @@ const _mapStudiesToThumbnails = function(studies) {
} = displaySet;
let imageId;
let altImageText = ' '; // modality
let altImageText;
if (displaySet.images && displaySet.images.length) {
imageId = displaySet.images[0].getImageId();
if (displaySet.modality && displaySet.modality === 'SEG') {
// TODO: We want to replace this with a thumbnail showing
// the segmentation map on the image, but this is easier
// and better than what we have right now.
altImageText = 'SEG';
} else if (displaySet.images && displaySet.images.length) {
const imageIndex = Math.floor(displaySet.images.length / 2);
imageId = displaySet.images[imageIndex].getImageId();
} else {
altImageText = 'SR';
altImageText = displaySet.modality ? displaySet.modality : 'UN';
}
return {
@@ -0,0 +1,15 @@
.drag-drop-instructions {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.drag-drop-instructions h3 {
color: var(--active-color);
}
.drag-drop-instructions h4 {
color: var(--text-secondary-color);
}
@@ -0,0 +1,117 @@
import React, { Component } from 'react';
import { metadata, utils } from 'ohif-core';
import ConnectedViewer from './ConnectedViewer.js';
import PropTypes from 'prop-types';
import { extensionManager } from './../App.js';
import Dropzone from 'react-dropzone';
import filesToStudies from '../lib/filesToStudies';
import './ViewerLocalFileData.css';
import { withTranslation } from 'react-i18next';
const { OHIFStudyMetadata } = metadata;
const { studyMetadataManager, updateMetaDataManager } = utils;
class ViewerLocalFileData extends Component {
static propTypes = {
studies: PropTypes.array,
};
state = {
studies: null,
loading: false,
error: null,
};
updateStudies = studies => {
// Render the viewer when the data is ready
studyMetadataManager.purge();
// Map studies to new format, update metadata manager?
const updatedStudies = studies.map(study => {
const studyMetadata = new OHIFStudyMetadata(
study,
study.studyInstanceUid
);
const sopClassHandlerModules =
extensionManager.modules['sopClassHandlerModule'];
study.displaySets =
study.displaySets ||
studyMetadata.createDisplaySets(sopClassHandlerModules);
studyMetadata.setDisplaySets(study.displaySets);
// Updates WADO-RS metaDataManager
updateMetaDataManager(study);
studyMetadataManager.add(studyMetadata);
return study;
});
this.setState({
studies: updatedStudies,
});
};
render() {
const onDrop = async acceptedFiles => {
this.setState({ loading: true });
const studies = await filesToStudies(acceptedFiles);
const updatedStudies = this.updateStudies(studies);
if (!updatedStudies) {
return;
}
this.setState({ studies: updatedStudies, loading: false });
};
if (this.state.error) {
return <div>Error: {JSON.stringify(this.state.error)}</div>;
}
return (
<Dropzone onDrop={onDrop}>
{({ getRootProps, getInputProps }) => (
<div {...getRootProps()} style={{ width: '100%', height: '100%' }}>
{this.state.studies ? (
<ConnectedViewer
studies={this.state.studies}
studyInstanceUids={
this.state.studies &&
this.state.studies.map(a => a.studyInstanceUid)
}
/>
) : (
<div className={'drag-drop-instructions'}>
<div className={'drag-drop-contents'}>
{this.state.loading ? (
<h3>{this.props.t('Loading...')}</h3>
) : (
<>
<h3>
{this.props.t(
'Drag and Drop DICOM files here to load them in the Viewer'
)}
</h3>
<h4>
{this.props.t(
"Or click to load the browser's file selector"
)}
</h4>
</>
)}
</div>
<input {...getInputProps()} style={{ display: 'none' }} />
</div>
)}
</div>
)}
</Dropzone>
);
}
}
export default withTranslation('Common')(ViewerLocalFileData);
@@ -0,0 +1,25 @@
import { connect } from 'react-redux';
import DicomFileUploaderModal from './DicomFileUploaderModal.js';
import OHIF from 'ohif-core';
const isActive = a => a.active === true;
const mapStateToProps = state => {
const activeServer = state.servers.servers.find(isActive);
// TODO: Not sure I like this approach since it means we are recreating
// this function every time redux changes
const retrieveAuthHeaderFunction = () => OHIF.DICOMWeb.getAuthorizationHeader(activeServer);
return {
retrieveAuthHeaderFunction,
url: activeServer && activeServer.qidoRoot,
};
};
const ConnectedDicomFileUploader = connect(
mapStateToProps,
null
)(DicomFileUploaderModal);
export default ConnectedDicomFileUploader;
@@ -0,0 +1,32 @@
import { connect } from 'react-redux';
import DicomStorePickerModal from './DicomStorePickerModal.js';
const isActive = a => a.active === true;
const mapStateToProps = state => {
const activeServer = state.servers.servers.find(isActive);
return {
user: state.oidc && state.oidc.user,
url: activeServer && activeServer.qidoRoot,
};
};
const mapDispatchToProps = dispatch => {
return {
setServers: servers => {
const action = {
type: 'SET_SERVERS',
servers,
};
dispatch(action);
},
};
};
const ConnectedDicomStorePicker = connect(
mapStateToProps,
mapDispatchToProps
)(DicomStorePickerModal);
export default ConnectedDicomStorePicker;
+55
View File
@@ -0,0 +1,55 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import api from './api/GoogleCloudApi';
import DatasetsList from './DatasetsList';
import './googleCloud.css';
export default class DatasetPicker extends Component {
state = {
error: null,
loading: true,
datasets: [],
};
static propTypes = {
project: PropTypes.object,
location: PropTypes.object,
onSelect: PropTypes.func,
accessToken: PropTypes.string,
};
async componentDidMount() {
api.setAccessToken(this.props.accessToken);
const response = await api.loadDatasets(
this.props.project.projectId,
this.props.location.locationId
);
if (response.isError) {
this.setState({
error: response.message,
});
return;
}
this.setState({
datasets: response.data.datasets || [],
loading: false,
});
}
render() {
const { datasets, loading, error } = this.state;
const { onSelect } = this.props;
return (
<DatasetsList
datasets={datasets}
loading={loading}
error={error}
onSelect={onSelect}
/>
);
}
}
+158
View File
@@ -0,0 +1,158 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import DicomStorePicker from './DicomStorePicker';
import DatasetPicker from './DatasetPicker';
import ProjectPicker from './ProjectPicker';
import LocationPicker from './LocationPicker';
import GoogleCloudApi from './api/GoogleCloudApi';
import './googleCloud.css';
class DatasetSelector extends Component {
state = {
project: null,
location: null,
dataset: null,
unloading: false,
};
static propTypes = {
id: PropTypes.string,
event: PropTypes.string,
user: PropTypes.object,
canClose: PropTypes.string,
setServers: PropTypes.func.isRequired,
};
onProjectSelect = project => {
this.setState({
project,
});
};
onLocationSelect = location => {
this.setState({
location,
});
};
onDatasetSelect = dataset => {
this.setState({
dataset,
});
};
onProjectClick = () => {
this.setState({
dataset: null,
location: null,
project: null,
});
};
onLocationClick = () => {
this.setState({
dataset: null,
location: null,
});
};
onDatasetClick = () => {
this.setState({
dataset: null,
});
};
onDicomStoreSelect = dicomStoreJson => {
const dicomStore = dicomStoreJson.name;
const parts = dicomStore.split('/');
const result = {
wadoUriRoot: GoogleCloudApi.urlBase + `/${dicomStore}/dicomWeb`,
qidoRoot: GoogleCloudApi.urlBase + `/${dicomStore}/dicomWeb`,
wadoRoot: GoogleCloudApi.urlBase + `/${dicomStore}/dicomWeb`,
project: parts[1],
location: parts[3],
dataset: parts[5],
dicomStore: parts[7],
};
this.props.setServers(result);
};
render() {
const accessToken = this.props.user.access_token;
const { project, location, dataset } = this.state;
const {
onProjectClick,
onLocationClick,
onDatasetClick,
onProjectSelect,
onLocationSelect,
onDatasetSelect,
onDicomStoreSelect,
} = this;
let projectBreadcrumbs = (
<div className="gcp-picker--path">
<span>{this.props.t('Select a Project')}</span>
</div>
);
if (project) {
projectBreadcrumbs = (
<div className="gcp-picker--path">
<span onClick={onProjectClick}>{project.name}</span>
{project && location && (
<span onClick={onLocationClick}>
{' '}
-> {location.name.split('/')[3]}
</span>
)}
{project && location && dataset && (
<span onClick={onDatasetClick}>
{' '}
-> {dataset.name.split('/')[5]}
</span>
)}
</div>
);
}
return (
<>
{projectBreadcrumbs}
{!project && (
<ProjectPicker
accessToken={accessToken}
onSelect={onProjectSelect}
/>
)}
{project && !location && (
<LocationPicker
accessToken={accessToken}
project={project}
onSelect={onLocationSelect}
/>
)}
{project && location && !dataset && (
<DatasetPicker
accessToken={accessToken}
project={project}
location={location}
onSelect={onDatasetSelect}
/>
)}
{project && location && dataset && (
<DicomStorePicker
accessToken={accessToken}
dataset={dataset}
onSelect={onDicomStoreSelect}
/>
)}
</>
);
}
}
export default withTranslation('Common')(DatasetSelector);
+80
View File
@@ -0,0 +1,80 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import './googleCloud.css';
import { withTranslation } from 'react-i18next';
import { Icon } from 'react-viewerbase';
class DatasetsList extends Component {
state = {
search: '',
};
static propTypes = {
datasets: PropTypes.array,
loading: PropTypes.bool,
error: PropTypes.string,
onSelect: PropTypes.func,
};
static defaultProps = {
loading: true,
};
renderTableRow = dataset => {
return (
<tr
key={dataset.name}
className={
this.state.highlightedItem === dataset.name
? 'noselect active'
: 'noselect'
}
onMouseEnter={() => {
this.onHighlightItem(dataset.name);
}}
onClick={() => {
this.props.onSelect(dataset);
}}
>
<td>{dataset.name.split('/')[5]}</td>
</tr>
);
};
onHighlightItem(dataset) {
this.setState({ highlightedItem: dataset });
}
render() {
if (this.props.error) {
return <p>{this.props.error}</p>;
}
const loadingIcon = (
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
);
if (this.props.loading) {
return loadingIcon;
}
const body = (
<tbody id="DatasetList">
{this.props.datasets.map(this.renderTableRow)}
</tbody>
);
return (
<table id="tblDatasetList" className="gcp-table table noselect">
<thead>
<tr>
<th>{this.props.t('Dataset')}</th>
</tr>
</thead>
{this.props.datasets && body}
</table>
);
}
}
export default withTranslation('Common')(DatasetsList);
+49
View File
@@ -0,0 +1,49 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Modal from 'react-bootstrap-modal';
import DicomUploader from './DicomUploader';
import { withTranslation } from 'react-i18next';
class DicomFileUploaderModal extends Component {
static propTypes = {
url: PropTypes.string,
retrieveAuthHeaderFunction: PropTypes.func,
onClose: PropTypes.func,
};
state = {
uploaded: false,
};
render() {
if (!this.props.url) {
return null;
}
return (
<Modal
show={this.props.isOpen}
onHide={this.props.onClose}
aria-labelledby="ModalHeader"
className="modal fade themed in"
backdrop={false}
size={'md'}
keyboard={true}
>
<Modal.Header closeButton>
<Modal.Title>
{this.props.t('Upload DICOM Files')}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<DicomUploader
url={this.props.url}
retrieveAuthHeaderFunction={this.props.retrieveAuthHeaderFunction}
/>
</Modal.Body>
</Modal>
);
}
}
export default withTranslation('Common')(DicomFileUploaderModal);
+78
View File
@@ -0,0 +1,78 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import './googleCloud.css';
import { withTranslation } from 'react-i18next';
import { Icon } from 'react-viewerbase';
class DicomStoreList extends Component {
state = {
search: '',
};
static propTypes = {
stores: PropTypes.array,
loading: PropTypes.bool.isRequired,
error: PropTypes.string,
onSelect: PropTypes.func,
};
static defaultProps = {
loading: true,
};
renderTableRow = store => {
return (
<tr
key={store.name}
className={
this.state.highlightedItem === store.name
? 'noselect active'
: 'noselect'
}
onMouseEnter={() => {
this.onHighlightItem(store.name);
}}
onClick={() => {
this.props.onSelect(store);
}}
>
<td className="project">{store.name.split('/')[7]}</td>
</tr>
);
};
onHighlightItem(store) {
this.setState({ highlightedItem: store });
}
render() {
if (this.props.error) {
return <p>{this.props.error}</p>;
}
const loadingIcon = (
<Icon name="circle-notch" className="loading-icon-spin loading-icon" />
);
if (this.props.loading) {
return loadingIcon;
}
const body = (
<tbody id="StoreList">{this.props.stores.map(this.renderTableRow)}</tbody>
);
return (
<table id="tblStoreList" className="gcp-table table noselect">
<thead>
<tr>
<th>{this.props.t('DICOM Store')}</th>
</tr>
</thead>
{this.props.stores && body}
</table>
);
}
}
export default withTranslation('Common')(DicomStoreList);
+53
View File
@@ -0,0 +1,53 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import api from './api/GoogleCloudApi';
import DicomStoreList from './DicomStoreList';
import './googleCloud.css';
export default class DicomStorePicker extends Component {
state = {
error: null,
loading: true,
stores: [],
locations: [],
};
static propTypes = {
dataset: PropTypes.object,
onSelect: PropTypes.func,
accessToken: PropTypes.string.isRequired
};
async componentDidMount() {
api.setAccessToken(this.props.accessToken);
const response = await api.loadDicomStores(this.props.dataset.name);
if (response.isError) {
this.setState({
error: response.message,
});
return;
}
this.setState({
stores: response.data.dicomStores || [],
loading: false,
});
}
render() {
const { stores, loading, error } = this.state;
const { onSelect } = this.props;
return (
<DicomStoreList
stores={stores}
loading={loading}
error={error}
onSelect={onSelect}
/>
);
}
}
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff. Show more