Refactor extensions interface (#344)

* Added container class for plugins that care for registration of submodules

* Added extensions manager that cares for adding modules to store

* Make extensions require less knowledge of internal extension management

* Reformatting

* Update generateStaticSite to use ohif-viewer, remove old OHIFViewer-react folder

* Add yarn.lock so Netlify knows to use Yarn

* Fix for copying static site folder

* Try to fix netlify build which is stuck uploading

* Try to fix static site deployment

* Try to fix static site deployment again

* Try to fix static site deployment one more time

* Extracted pdf plugin into external package

* Removed some unused dependencies

* Published pdf extension to npm registry

* Added external microscopy extension (work in progress)

* Extraction of microscopy plugin working now

* Added microscopy extension npm package to package.json

* Increase ohif-core version

* update yarn.lock

* Fix error in readme

* Fix authentication error in pdf plugin

* Remove readme, update packages, add redirect for netlify

* netlify test

* netlify test 2

* netlify test 2, forgot to add new redirects file

* Use public dicom server for demo

* Another netlify try

* Another netlify try 2

* Another netlify try 3

* Another netlify try 4
This commit is contained in:
Sandra 2019-02-02 11:10:56 +00:00 committed by Erik Ziegler
parent af68db0c09
commit b93a677ef3
121 changed files with 14151 additions and 35046 deletions

2
.gitignore vendored
View File

@ -9,5 +9,5 @@ npm-debug.log
Packages/active-entry/helloworld/
LesionTracker/tests/nightwatch/reports/
docs/_book
OHIFViewer-react/build/
.DS_Store
**/.history/*

View File

@ -1,3 +0,0 @@
src/sha.js
src/version.js
yarn-error.log

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,57 +0,0 @@
{
"name": "OHIFViewer",
"version": "0.0.0",
"license": "MIT",
"private": true,
"homepage": "http://localhost:5000/",
"dependencies": {
"cornerstone-core": "2.2.8",
"cornerstone-math": "0.1.8",
"cornerstone-tools": "3.0.0-b.1512",
"cornerstone-wado-image-loader": "2.2.3",
"dicom-parser": "1.8.3",
"dicomweb-client": "^0.4.1",
"hammerjs": "^2.0.8",
"lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8",
"moment": "^2.22.2",
"ohif-core": "^0.1.8",
"ohif-viewer": "0.0.5",
"oidc-client": "^1.6.1",
"prop-types": "^15.6.2",
"query-string": "^6.2.0",
"react": "^16.7.0",
"react-cornerstone-viewport": "^0.1.8",
"react-dom": "^16.4.1",
"react-redux": "^6.0.0",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-scripts": "^2.1.3",
"react-viewerbase": "^0.2.4",
"react-vtkjs-viewport": "^0.0.1",
"redux": "^4.0.1",
"redux-oidc": "^3.1.0"
},
"scripts": {
"copy:webworkers": "cp node_modules/cornerstone-wado-image-loader/dist/*.min.js* public",
"version": "node -p -e \"'export default \\'' + require('./package.json').version + '\\';'\" > src/version.js",
"sha": "echo \"export default '\"$(git rev-parse HEAD)\"';\" > src/sha.js",
"details": "npm run version && npm run sha",
"prep": "npm run details && npm run copy:webworkers",
"start": "npm run prep && react-scripts start",
"build": "npm run prep && react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-class-properties": "^7.2.3",
"husky": "^1.3.1"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,237 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol id="icon-hud" viewBox="0 0 20 22">
<title>HUD</title>
<g id="icon-hud-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
<path id="icon-hud-dashes" d="m14.5,2.75 2,0 a2.5 2.5 0 0 1 2.5 2.5 l0,13.25 a2.5 2.5 0 0 1 -2.5 2.5 l-11.5,0 a2.5 2.5 0 0 1 -2.5 -2.5 l0,-1" stroke-dasharray="1,3" />
<path id="icon-hud-box" d="m0.5,2 0,14 a1.5 1.5 0 0 0 1.5 1.5 l12,0 a1.5 1.5 0 0 0 1.5 -1.5 l0,-14" />
<path id="icon-hud-top" d="m2,2 12,0" stroke-width="4" />
<path id="icon-hud-paralel" d="m4.4,7.5 7.2,0 m0,3 -7.2,0 m0,3 7.2,0" stroke-linecap="square" />
</g>
</symbol>
<symbol id="icon-measurements-additional" viewBox="0 0 16 16">
<title>Additional Measurements</title>
<g id="icon-measurements-additional-group" stroke="none">
<path id="icon-measurements-additional-boxes" d="M11,2 l1.85,-1.85 a1 1 0 0 1 1,0 l2,2 a1 1 0 0 1 0,1 l-1.85,1.85Z M10,3 l-10,10 0,3 3,0 10,-10Z M16,16 l0,-2 -9,0 -2,2Z" />
</g>
</symbol>
<symbol id="icon-measurements-lesions" viewBox="0 0 18 10">
<title>Lesions</title>
<g id="icon-measurements-lesions-group" stroke="none">
<path id="icon-measurements-lesions-boxes" d="M0,0 2,0 2,2 0,2Z M4,0 18,0 18,2 4,2Z M0,4 2,4 2,6 0,6Z M4,4 18,4 18,6 4,6Z M0,8 2,8 2,10 0,10Z M4,8 18,8 18,10 4,10Z" />
</g>
</symbol>
<symbol id="icon-settings" viewBox="0 0 15 15">
<title>Settings</title>
<path d="M12.85 8.25C12.85 8.03 12.93 7.8 12.93 7.5 12.93 7.2 12.93 6.98 12.85 6.75L14.43 5.48C14.58 5.33 14.58 5.18 14.5 5.03L13 2.4C12.93 2.33 12.78 2.25 12.55 2.33L10.68 3.08C10.3 2.78 9.85 2.55 9.4 2.33L9.1 0.38C9.18 0.15 8.95 0 8.8 0L5.8 0C5.65 0 5.43 0.15 5.43 0.3L5.13 2.33C4.68 2.48 4.3 2.78 3.85 3.08L2.05 2.33C1.83 2.25 1.68 2.33 1.53 2.48L0.03 5.1C-0.05 5.18 0.03 5.4 0.18 5.55L1.75 6.75C1.75 6.98 1.68 7.2 1.68 7.5 1.68 7.8 1.68 8.03 1.75 8.25L0.18 9.53C0.03 9.68 0.03 9.82 0.1 9.98L1.6 12.6C1.68 12.68 1.83 12.75 2.05 12.68L3.93 11.93C4.3 12.23 4.75 12.45 5.2 12.68L5.5 14.63C5.5 14.78 5.65 14.93 5.88 14.93L8.88 14.93C9.03 14.93 9.25 14.78 9.25 14.63L9.55 12.68C10 12.45 10.45 12.23 10.83 11.93L12.7 12.68C12.85 12.75 13.08 12.68 13.15 12.53L14.65 9.9C14.73 9.75 14.73 9.53 14.58 9.45L12.85 8.25 12.85 8.25ZM7.3 10.13C5.88 10.13 4.68 8.93 4.68 7.5 4.68 6.08 5.88 4.88 7.3 4.88 8.73 4.88 9.93 6.08 9.93 7.5 9.93 8.93 8.73 10.13 7.3 10.13L7.3 10.13Z" id="Shape"/>
</symbol>
<symbol id="icon-status-complete" viewBox="0 0 27 27">
<title>Complete</title>
<g id="icon-status-complete-group" stroke="none">
<path id="icon-status-complete-circle" d="M13.5,0 a13.5 13.5 0 0 1 0 27 a13.5 13.5 0 0 1 0 -27 M23,8 l-2,-2 -10,10, -5,-5 -2,2 7,7Z" />
</g>
</symbol>
<symbol id="icon-status-lock" viewBox="0 0 16 21">
<title>Locked</title>
<path d="M14 7L13 7 13 5C13 2.2 10.8 0 8 0 5.2 0 3 2.2 3 5L3 7 2 7C0.9 7 0 7.9 0 9L0 19C0 20.1 0.9 21 2 21L14 21C15.1 21 16 20.1 16 19L16 9C16 7.9 15.1 7 14 7L14 7ZM11.1 7L4.9 7 4.9 5C4.9 3.3 6.3 1.9 8 1.9 9.7 1.9 11.1 3.3 11.1 5L11.1 7 11.1 7Z"/>
</symbol>
<symbol id="icon-studies" viewBox="0 0 15 13">
<title>Studies</title>
<g id="icon-studies-group" stroke="none">
<path id="icon-studies-boxes" d="M0,0 7,0 7,6 0,6Z M8,0 15,0 15,6 8,6Z M0,7 7,7 7,13 0,13Z M8,7 15,7 15,13 8,13Z" />
</g>
</symbol>
<symbol id="icon-tools-levels" viewBox="0 0 18 18">
<title>Window / Level</title>
<g id="icon-tools-levels-group">
<path id="icon-tools-levels-path" d="M14.5,3.5 a1 1 0 0 1 -11,11 Z" stroke="none" opacity="0.8" />
<circle id="icon-tools-levels-circle" cx="9" cy="9" r="8" fill="none" stroke-width="2" />
</g>
</symbol>
<symbol id="icon-tools-link" viewBox="0 0 23 15">
<title>Link</title>
<g id="icon-tools-link-group" stroke="none">
<circle id="icon-tools-link-circle-l" opacity="0.8" cx="7.5" cy="7.5" r="7.5" />
<circle id="icon-tools-link-circle-r" opacity="0.6" cx="15.5" cy="7.5" r="7.5" />
</g>
</symbol>
<symbol id="icon-tools-measure-non-target" viewBox="0 0 24 24">
<title>Non-Target Measurement</title>
<g id="icon-tools-measure-non-target-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
<circle id="icon-tools-measure-non-target-circle" cx="6.5" cy="6.5" r="6" />
<path id="icon-tools-measure-non-target-plus" d="M6.5,3 l0,7 M3,6.5 l7,0"></path>
<path id="icon-tools-measure-non-target-arrow" d="M23,7 l-15,15 M7,17 l0,6 6,0" stroke-width="2" />
</g>
</symbol>
<symbol id="icon-tools-measure-target" viewBox="0 0 26 26">
<title>Target Measurement</title>
<g id="icon-tools-measure-target-group" stroke-width="0">
<path id="icon-tools-measure-target-ruler" d="m9.5,26 2,-2 -1.5,-1.5 0.5,-0.5 1.5,1.5 1.5,-1.5 -2.5,-2.5 0.5,-0.5 2.5,2.5 1.5,-1.5 -1.5,-1.5 0.5,-0.5 1.5,1.5 1.5,-1.5 -2.5,-2.5 0.5,-0.5 2.5,2.5 1.5,-1.5 -1.5,-1.5 0.5,-0.5 1.5,1.5 1.5,-1.5 -2.5,-2.5 0.5,-0.5 2.5,2.5 1.5,-1.5 -1.5,-1.5 0.5,-0.5 1.5,1.5 2,-2 -4,-4 a2 2 0 0 0 -2.5 0 l-14 14 a2 2 0 0 0 0 2.5 l4,4 Z" />
<path id="icon-tools-measure-target-plus" d="m6,0 a6 6 0 0 1 0 12 a6 6 0 0 1 0 -12 m0.5,3 a0.5 0.5 0 0 0 -1 0 l0,2.5 -2.5,0 a0.5 0.5 0 0 0 0 1 l2.5,0 0,2.5 a0.5 0.5 0 0 0 1 0 l0,-2.5 2.5,0 a0.5 0.5 0 0 0 0 -1 l-2.5,0 z" />
</g>
</symbol>
<symbol id="icon-tools-measure-target-cr" viewBox="0 0 24 24">
<title>Target CR Measurement</title>
<g id="icon-tools-measure-target-cr-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
<text id="icon-tools-measure-target-cr-text" fill="#000000" x="0" y="8" style="font-size:10px; font-family:sans-serif">CR</text>
<path id="icon-tools-measure-target-cr-arrow" d="M23,7 l-15,15 M7,17 l0,6 6,0" stroke-width="2" />
</g>
</symbol>
<symbol id="icon-tools-measure-target-un" viewBox="0 0 24 24">
<title>Target UN Measurement</title>
<g id="icon-tools-measure-target-un-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
<text id="icon-tools-measure-target-un-text" fill="#000000" x="0" y="8" style="font-size:10px; font-family:sans-serif">UN</text>
<path id="icon-tools-measure-target-un-arrow" d="M23,7 l-15,15 M7,17 l0,6 6,0" stroke-width="2" />
</g>
</symbol>
<symbol id="icon-tools-measure-temp" viewBox="0 0 24 24">
<title>Temporary Measurement</title>
<g id="icon-tools-measure-temp-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
<circle id="icon-tools-measure-temp-circle" cx="6.5" cy="6.5" r="6" />
<path id="icon-tools-measure-temp-plus" d="M6.5,3 l0,7 M3,6.5 l7,0"></path>
<path id="icon-tools-measure-temp-dashes" d="m22.5,6 -16.5,16.5" stroke-width="3" stroke-dasharray="0.6666,5" />
</g>
</symbol>
<symbol id="icon-tools-more" viewBox="0 0 18 18">
<title>More</title>
<g id="icon-tools-more-group" stroke="none">
<path id="icon-tools-more-circle" d="M9,0 a9 9 0 0 1 0 18 a9 9 0 0 1 0 -18 M4.5,8 a1.5 1.5 0 0 0 0 3 a1.5 1.5 0 0 0 0 -3 M9,8 a1.5 1.5 0 0 0 0 3 a1.5 1.5 0 0 0 0 -3 M13.5,8 a1.5 1.5 0 0 0 0 3 a1.5 1.5 0 0 0 0 -3" />
</g>
</symbol>
<symbol id="icon-tools-pan" viewBox="0 0 18 18">
<title>Pan</title>
<g id="icon-tools-pan-group" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path id="icon-tools-pan-line-v" d="M9,1 L9,17"></path>
<path id="icon-tools-pan-line-h" d="M1,9 L17,9"></path>
<polyline id="icon-tools-pan-caret-t" points="7 3 9 1 11 3"></polyline>
<polyline id="icon-tools-pan-caret-r" points="15 11 17 9 15 7"></polyline>
<polyline id="icon-tools-pan-caret-b" points="11 15 9 17 7 15"></polyline>
<polyline id="icon-tools-pan-caret-l" points="3 7 1 9 3 11"></polyline>
</g>
</symbol>
<symbol id="icon-tools-zoom" viewBox="0 0 17 17">
<title>Zoom</title>
<g id="icon-tools-zoom-group" fill="none" stroke-width="2" stroke-linecap="round">
<path id="icon-tools-zoom-path" d="m11.5,11.5 4.5,4.5" />
<circle id="icon-tools-zoom-circle" cx="7" cy="7" r="6" />
</g>
</symbol>
<symbol id="icon-tools-invert" viewBox="0 0 24 28">
<title>Invert</title>
<path d="M12 22.5v-17c-4.688 0-8.5 3.813-8.5 8.5s3.813 8.5 8.5 8.5zM24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12 12 5.375 12 12z"></path>
</symbol>
<symbol id="icon-tools-stack-scroll" viewBox="0 0 24 28">
<title>Stack Scroll</title>
<path d="M24 21v2c0 0.547-0.453 1-1 1h-22c-0.547 0-1-0.453-1-1v-2c0-0.547 0.453-1 1-1h22c0.547 0 1 0.453 1 1zM24 13v2c0 0.547-0.453 1-1 1h-22c-0.547 0-1-0.453-1-1v-2c0-0.547 0.453-1 1-1h22c0.547 0 1 0.453 1 1zM24 5v2c0 0.547-0.453 1-1 1h-22c-0.547 0-1-0.453-1-1v-2c0-0.547 0.453-1 1-1h22c0.547 0 1 0.453 1 1z"></path>
</symbol>
<symbol id="icon-tools-elliptical-roi" viewBox="0 0 24 28">
<title>Elliptical ROI</title>
<path d="M12 5.5c-4.688 0-8.5 3.813-8.5 8.5s3.813 8.5 8.5 8.5 8.5-3.813 8.5-8.5-3.813-8.5-8.5-8.5zM24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12v0c6.625 0 12 5.375 12 12z"></path>
</symbol>
<symbol id="icon-tools-magnify" viewBox="0 0 24 28">
<title>Magnify</title>
<path d="M24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12 12 5.375 12 12z"></path>
</symbol>
<symbol id="icon-tools-reset" viewBox="0 0 24 28">
<title>Reset</title>
<path d="M24 14c0 6.609-5.391 12-12 12-3.578 0-6.953-1.578-9.234-4.328-0.156-0.203-0.141-0.5 0.031-0.672l2.141-2.156c0.109-0.094 0.25-0.141 0.391-0.141 0.141 0.016 0.281 0.078 0.359 0.187 1.531 1.984 3.828 3.109 6.312 3.109 4.406 0 8-3.594 8-8s-3.594-8-8-8c-2.047 0-3.984 0.781-5.437 2.141l2.141 2.156c0.297 0.281 0.375 0.719 0.219 1.078-0.156 0.375-0.516 0.625-0.922 0.625h-7c-0.547 0-1-0.453-1-1v-7c0-0.406 0.25-0.766 0.625-0.922 0.359-0.156 0.797-0.078 1.078 0.219l2.031 2.016c2.203-2.078 5.187-3.313 8.266-3.313 6.609 0 12 5.391 12 12z"></path>
</symbol>
<symbol id="icon-tools-rotate" viewBox="0 0 24 28">
<title>Rotate</title>
<path d="M24 4v7c0 0.547-0.453 1-1 1h-7c-0.406 0-0.766-0.25-0.922-0.625-0.156-0.359-0.078-0.797 0.219-1.078l2.156-2.156c-1.469-1.359-3.406-2.141-5.453-2.141-4.406 0-8 3.594-8 8s3.594 8 8 8c2.484 0 4.781-1.125 6.312-3.109 0.078-0.109 0.219-0.172 0.359-0.187 0.141 0 0.281 0.047 0.391 0.141l2.141 2.156c0.187 0.172 0.187 0.469 0.031 0.672-2.281 2.75-5.656 4.328-9.234 4.328-6.609 0-12-5.391-12-12s5.391-12 12-12c3.078 0 6.062 1.234 8.266 3.313l2.031-2.016c0.281-0.297 0.719-0.375 1.094-0.219 0.359 0.156 0.609 0.516 0.609 0.922z"></path>
</symbol>
<symbol id="icon-tools-rotate-right" viewBox="0 0 24 24">
<title>Rotate Right</title>
<path d="M16.875 15.469c0.563-0.75 0.891-1.594 1.031-2.484h2.016c-0.188 1.406-0.703 2.719-1.594 3.891zM12.984 17.906c0.891-0.141 1.734-0.469 2.484-1.031l1.453 1.453c-1.172 0.891-2.531 1.406-3.938 1.594v-2.016zM19.922 11.016h-2.016c-0.141-0.891-0.469-1.734-1.031-2.484l1.453-1.406c0.891 1.172 1.406 2.484 1.594 3.891zM15.563 5.531l-4.547 4.453v-3.891c-2.859 0.469-5.016 2.953-5.016 5.906s2.156 5.438 5.016 5.906v2.016c-3.938-0.469-7.031-3.844-7.031-7.922s3.094-7.453 7.031-7.922v-3.094z"></path>
</symbol>
<symbol id="icon-tools-cineplay-toggle" viewBox="0 0 28 28">
<title>Cineplay Toggle</title>
<path d="M11.109 17.625l7.562-3.906-7.562-3.953v7.859zM14 4.156c5.891 0 9.797 0.281 9.797 0.281 0.547 0.063 1.75 0.063 2.812 1.188 0 0 0.859 0.844 1.109 2.781 0.297 2.266 0.281 4.531 0.281 4.531v2.125s0.016 2.266-0.281 4.531c-0.25 1.922-1.109 2.781-1.109 2.781-1.062 1.109-2.266 1.109-2.812 1.172 0 0-3.906 0.297-9.797 0.297v0c-7.281-0.063-9.516-0.281-9.516-0.281-0.625-0.109-2.031-0.078-3.094-1.188 0 0-0.859-0.859-1.109-2.781-0.297-2.266-0.281-4.531-0.281-4.531v-2.125s-0.016-2.266 0.281-4.531c0.25-1.937 1.109-2.781 1.109-2.781 1.062-1.125 2.266-1.125 2.812-1.188 0 0 3.906-0.281 9.797-0.281v0z"></path>
</symbol>
<symbol id="icon-tools-flip-vertical" viewBox="0 0 6 28">
<title>Vertical</title>
<path d="M6 19.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5zM6 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5zM6 3.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5z"></path>
</symbol>
<symbol id="icon-tools-flip-horizontal" viewBox="0 0 22 28">
<title>Horizontal</title>
<path d="M6 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5zM14 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5zM22 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5z"></path>
</symbol>
<symbol id="icon-trial-info" viewBox="0 0 20 20">
<title>Trial Information</title>
<g id="icon-trial-info-group" fill="none" stroke-width="2">
<path id="icon-trial-info-circle" d="M10,1 a9 9 0 0 1 0 18 a9 9 0 0 1 0 -18 M10,5 l0,2 m0,2 0,6" />
</g>
</symbol>
<symbol id="icon-ui-accordion-expand" viewBox="0 0 12 8">
<title>Expand</title>
<polygon transform="translate(6.000000, 3.700000) rotate(-270.000000) translate(-6.000000, -3.700000) " points="3.7 -2.3 2.3 -0.9 6.9 3.7 2.3 8.3 3.7 9.7 9.7 3.7"/>
</symbol>
<symbol id="icon-ui-add" viewBox="0 0 12 13">
<title>Add</title>
<path d="M6.5 7L6.5 12C6.5 12.28 6.28 12.5 6 12.5 5.72 12.5 5.5 12.28 5.5 12L5.5 7 0.5 7C0.22 7 0 6.78 0 6.5 0 6.22 0.22 6 0.5 6L5.5 6 5.5 1C5.5 0.72 5.72 0.5 6 0.5 6.28 0.5 6.5 0.72 6.5 1L6.5 6 11.5 6C11.78 6 12 6.22 12 6.5 12 6.78 11.78 7 11.5 7L6.5 7Z" id="Combined-Shape"/>
</symbol>
<symbol id="icon-ui-close" viewBox="0 0 14 14">
<title>Close</title>
<g id="icon-ui-close-group" stroke-width="1.75">
<path id="icon-ui-close-path" d="M1,1 13,13 M1,13 13,1" />
</g>
</symbol>
<symbol id="icon-create-comment" viewBox="0 0 37 34">
<title>Comment</title>
<path fill="#94a8b3" d="M22.32 14L35 14C36.1 14 37 14.9 37 16L37 28C37 29.1 36.1 30 35 30L21 30 17 34 17 20.39C19.45 18.97 21.36 16.71 22.32 14Z" id="Combined-Shape"/>
<path d="M10 0C4.5 0 0 4.5 0 10 0 15.5 4.5 20 10 20 15.5 20 20 15.5 20 10 20 4.5 15.5 0 10 0L10 0ZM15 11L11 11 11 15 9 15 9 11 5 11 5 9 9 9 9 5 11 5 11 9 15 9 15 11 15 11Z" id="Shape"/>
</symbol>
<symbol id="icon-create-screen-capture" viewBox="0 0 37 35">
<title>Capture Screen</title>
<g id="camera-alt" transform="translate(17.000000, 17.000000)" fill="#94a8b3">
<path d="M10 13C11.66 13 13 11.66 13 10 13 8.34 11.66 7 10 7 8.34 7 7 8.34 7 10 7 11.66 8.34 13 10 13Z" id="Oval"/>
<path d="M7 0L5.2 2 2 2C0.9 2 0 2.9 0 4L0 16C0 17.1 0.9 18 2 18L18 18C19.1 18 20 17.1 20 16L20 4C20 2.9 19.1 2 18 2L14.8 2 13 0 7 0 7 0ZM10 15C7.2 15 5 12.8 5 10 5 7.2 7.2 5 10 5 12.8 5 15 7.2 15 10 15 12.8 12.8 15 10 15L10 15Z" id="Shape"/>
</g>
<path d="M10 0C4.5 0 0 4.5 0 10 0 15.5 4.5 20 10 20 15.5 20 20 15.5 20 10 20 4.5 15.5 0 10 0L10 0ZM15 11L11 11 11 15 9 15 9 11 5 11 5 9 9 9 9 5 11 5 11 9 15 9 15 11 15 11Z" id="Shape"/>
</symbol>
<symbol id="icon-ui-warning" viewBox="0 0 22 20">
<title>Warning</title>
<g id="icon-ui-warning-group" stroke="none">
<path d="m11,0 11,20 -22,0Z m1,7 -2,0 0,6 2,0Z m0,8 -2,0 0,2 2,0Z" />
</g>
</symbol>
<symbol id="icon-ohif-logo" viewBox="0 0 47 47">
<title>Open Health Imaging Foundation</title>
<g id="icon-ohif-logo" stroke="#fff" stroke-width="2.5" stroke-miterlimit="10">
<rect x="1.25" y="1.25" width="18.8" height="18.8" rx="1" ry="1"/>
<rect x="26" y="1.25" width="18.8" height="18.8" rx="1" ry="1"/>
<rect x="1.25" y="26" width="18.8" height="18.8" rx="1" ry="1"/>
<rect x="26" y="26" width="18.8" height="18.8" rx="1" ry="1"/>
</g>
</symbol>
<symbol id="icon-viewport-link" viewBox="0 0 32 32">
<title>Viewport Link</title>
<g id="icon-viewport-link-group" stroke="none">
<path d="m5.364 28.917c1.047 1.046 2.422 1.57 3.797 1.57 1.376 0 2.751-0.523 3.798-1.572l7.552-7.554c1.016-1.014 1.574-2.361 1.574-3.797 -0.001-1.432-0.558-2.781-1.574-3.796l-2.297-2.298 -2.009 2.009 2.297 2.298c0.478 0.478 0.741 1.113 0.741 1.788 0 0.674-0.264 1.309-0.741 1.786l-7.553 7.555c-0.987 0.985-2.59 0.985-3.576 0l-2.297-2.298c-0.987-0.985-0.987-2.589 0-3.576l3.775-3.776 -2.009-2.009 -3.776 3.776c-2.094 2.096-2.092 5.502 0 7.595l2.299 2.3zM26.695 2.992"/>
<path d="m26.695 2.992c-1.014-1.016-2.362-1.575-3.797-1.575 -0.001 0-0.001 0-0.002 0 -1.435 0-2.784 0.56-3.798 1.573l-7.551 7.553c-1.017 1.016-1.576 2.363-1.576 3.799 0 1.434 0.558 2.784 1.574 3.797l2.297 2.297 2.01-2.009 -2.298-2.297c-0.477-0.477-0.741-1.113-0.741-1.788 0-0.676 0.265-1.311 0.742-1.788l7.553-7.555c0.477-0.477 1.111-0.74 1.789-0.74 0 0 0 0 0.001 0 0.674 0 1.309 0.264 1.786 0.74l2.297 2.299c0.477 0.477 0.74 1.111 0.74 1.788 0 0.674-0.264 1.311-0.74 1.788l-3.776 3.777 2.009 2.009 3.776-3.777c1.014-1.013 1.573-2.363 1.574-3.797 0-1.435-0.56-2.784-1.574-3.797l-2.295-2.296z"/>
</g>
</symbol>
<symbol id="theme" viewBox="0 0 24 24">
<title>Theme</title>
<path d="M17.484 12c0.844 0 1.5-0.656 1.5-1.5s-0.656-1.5-1.5-1.5-1.5 0.656-1.5 1.5 0.656 1.5 1.5 1.5zM14.484 8.016c0.844 0 1.5-0.656 1.5-1.5s-0.656-1.5-1.5-1.5-1.5 0.656-1.5 1.5 0.656 1.5 1.5 1.5zM9.516 8.016c0.844 0 1.5-0.656 1.5-1.5s-0.656-1.5-1.5-1.5-1.5 0.656-1.5 1.5 0.656 1.5 1.5 1.5zM6.516 12c0.844 0 1.5-0.656 1.5-1.5s-0.656-1.5-1.5-1.5-1.5 0.656-1.5 1.5 0.656 1.5 1.5 1.5zM12 3c4.969 0 9 3.609 9 8.016 0 2.766-2.25 4.969-5.016 4.969h-1.734c-0.844 0-1.5 0.656-1.5 1.5 0 0.375 0.141 0.703 0.375 0.984s0.375 0.656 0.375 1.031c0 0.844-0.656 1.5-1.5 1.5-4.969 0-9-4.031-9-9s4.031-9 9-9z"></path>
</symbol>
<symbol id="log" viewBox="0 0 24 24">
<title>Log</title>
<path d="M9.516 14.016c2.484 0 4.5-2.016 4.5-4.5s-2.016-4.5-4.5-4.5-4.5 2.016-4.5 4.5 2.016 4.5 4.5 4.5zM15.516 14.016l4.969 4.969-1.5 1.5-4.969-4.969v-0.797l-0.281-0.281c-1.125 0.984-2.625 1.547-4.219 1.547-3.609 0-6.516-2.859-6.516-6.469s2.906-6.516 6.516-6.516 6.469 2.906 6.469 6.516c0 1.594-0.563 3.094-1.547 4.219l0.281 0.281h0.797z"></path>
</symbol>
<symbol id="server" viewBox="0 0 24 28">
<title>Server</title>
<path d="M12 12c4.703 0 9.422-0.844 12-2.656v2.656c0 2.203-5.375 4-12 4s-12-1.797-12-4v-2.656c2.578 1.813 7.297 2.656 12 2.656zM12 24c4.703 0 9.422-0.844 12-2.656v2.656c0 2.203-5.375 4-12 4s-12-1.797-12-4v-2.656c2.578 1.813 7.297 2.656 12 2.656zM12 18c4.703 0 9.422-0.844 12-2.656v2.656c0 2.203-5.375 4-12 4s-12-1.797-12-4v-2.656c2.578 1.813 7.297 2.656 12 2.656zM12 0c6.625 0 12 1.797 12 4v2c0 2.203-5.375 4-12 4s-12-1.797-12-4v-2c0-2.203 5.375-4 12-4z"></path>
</symbol>
<symbol id="study-list" viewBox="0 0 24 24">
<title>Study List</title>
<path d="M9 5.016h12v3.984h-12v-3.984zM9 18.984v-3.984h12v3.984h-12zM9 14.016v-4.031h12v4.031h-12zM3.984 9v-3.984h4.031v3.984h-4.031zM3.984 18.984v-3.984h4.031v3.984h-4.031zM3.984 14.016v-4.031h4.031v4.031h-4.031z"></path>
</symbol>
<symbol id="logout" viewBox="0 0 24 28">
<title>Logout</title>
<path d="M24 14c0 6.609-5.391 12-12 12s-12-5.391-12-12c0-3.797 1.75-7.297 4.797-9.578 0.891-0.672 2.141-0.5 2.797 0.391 0.672 0.875 0.484 2.141-0.391 2.797-2.031 1.531-3.203 3.859-3.203 6.391 0 4.406 3.594 8 8 8s8-3.594 8-8c0-2.531-1.172-4.859-3.203-6.391-0.875-0.656-1.062-1.922-0.391-2.797 0.656-0.891 1.922-1.062 2.797-0.391 3.047 2.281 4.797 5.781 4.797 9.578zM14 2v10c0 1.094-0.906 2-2 2s-2-0.906-2-2v-10c0-1.094 0.906-2 2-2s2 0.906 2 2z"></path>
</symbol>
<symbol id="password" viewBox="0 0 18 28">
<title>Password</title>
<path d="M5 12h8v-3c0-2.203-1.797-4-4-4s-4 1.797-4 4v3zM18 13.5v9c0 0.828-0.672 1.5-1.5 1.5h-15c-0.828 0-1.5-0.672-1.5-1.5v-9c0-0.828 0.672-1.5 1.5-1.5h0.5v-3c0-3.844 3.156-7 7-7s7 3.156 7 7v3h0.5c0.828 0 1.5 0.672 1.5 1.5z"></path>
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,30 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<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=Sanchez" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
crossorigin="anonymous">
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>

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>

View File

@ -1,8 +0,0 @@
{
"short_name": "react-cornerstone-viewport",
"name": "react-cornerstone-viewport",
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){"use strict";function e(e){var n=this.constructor;return this.then(function(t){return n.resolve(e()).then(function(){return t})},function(t){return n.resolve(e()).then(function(){return n.reject(t)})})}function n(){}function t(e){if(!(this instanceof t))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=undefined,this._deferreds=[],u(e,this)}function o(e,n){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,t._immediateFn(function(){var t=1===e._state?n.onFulfilled:n.onRejected;if(null!==t){var o;try{o=t(e._value)}catch(f){return void i(n.promise,f)}r(n.promise,o)}else(1===e._state?r:i)(n.promise,e._value)})):e._deferreds.push(n)}function r(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var o=n.then;if(n instanceof t)return e._state=3,e._value=n,void f(e);if("function"==typeof o)return void u(function(e,n){return function(){e.apply(n,arguments)}}(o,n),e)}e._state=1,e._value=n,f(e)}catch(r){i(e,r)}}function i(e,n){e._state=2,e._value=n,f(e)}function f(e){2===e._state&&0===e._deferreds.length&&t._immediateFn(function(){e._handled||t._unhandledRejectionFn(e._value)});for(var n=0,r=e._deferreds.length;r>n;n++)o(e,e._deferreds[n]);e._deferreds=null}function u(e,n){var t=!1;try{e(function(e){t||(t=!0,r(n,e))},function(e){t||(t=!0,i(n,e))})}catch(o){if(t)return;t=!0,i(n,o)}}var c=setTimeout;t.prototype["catch"]=function(e){return this.then(null,e)},t.prototype.then=function(e,t){var r=new this.constructor(n);return o(this,new function(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}(e,t,r)),r},t.prototype["finally"]=e,t.all=function(e){return new t(function(n,t){function o(e,f){try{if(f&&("object"==typeof f||"function"==typeof f)){var u=f.then;if("function"==typeof u)return void u.call(f,function(n){o(e,n)},t)}r[e]=f,0==--i&&n(r)}catch(c){t(c)}}if(!e||"undefined"==typeof e.length)throw new TypeError("Promise.all accepts an array");var r=Array.prototype.slice.call(e);if(0===r.length)return n([]);for(var i=r.length,f=0;r.length>f;f++)o(f,r[f])})},t.resolve=function(e){return e&&"object"==typeof e&&e.constructor===t?e:new t(function(n){n(e)})},t.reject=function(e){return new t(function(n,t){t(e)})},t.race=function(e){return new t(function(n,t){for(var o=0,r=e.length;r>o;o++)e[o].then(n,t)})},t._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){c(e,0)},t._unhandledRejectionFn=function(e){void 0!==console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var l=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if("undefined"!=typeof global)return global;throw Error("unable to locate global object")}();"Promise"in l?l.Promise.prototype["finally"]||(l.Promise.prototype["finally"]=e):l.Promise=t});

View File

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Silent OpenID Connect Token Refresh 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">
new UserManager().signinSilentCallback().catch(console.error);
</script>
</body>
</html>

View File

@ -1,18 +0,0 @@
html,
body,
#root {
background-color: black;
height: 100%;
width: 100%;
margin: 0;
}
body {
box-sizing: border-box;
margin: 0;
padding: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: Roboto, OpenSans, HelveticaNeue-Light, Helvetica Neue Light,
Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif;
}

View File

@ -1,128 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import { Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import { ViewerbaseDragDropContext } from 'react-viewerbase';
import routes from './routes/';
import StudyListRouting from './studylist/StudyListRouting.js';
import CallbackPage from './CallbackPage.js';
import './App.css';
import './variables.css';
import './theme-tide.css';
const { ViewerRouting, StandaloneRouting, IHEInvokeImageDisplay } = routes;
const reload = () => window.location.reload();
function setContext(context) {
/*Rollbar.configure({
payload: {
context
}
});*/
}
class App extends Component {
static propTypes = {
history: PropTypes.object.isRequired,
user: PropTypes.object,
userManager: PropTypes.object,
location: PropTypes.object,
}
componentDidMount() {
this.unlisten = this.props.history.listen((location, action) => {
setContext(window.location.pathname);
});
}
componentWillUnmount() {
this.unlisten();
}
render() {
const { user, userManager } = this.props;
const userNotLoggedIn = userManager && (!user || user.expired);
if (userNotLoggedIn) {
const pathname = this.props.location.pathname;
if (pathname !== '/callback') {
sessionStorage.setItem('ohif-redirect-to', pathname);
}
return (
<Switch>
<Route exact path="/silent-refresh.html" onEnter={reload} />
<Route exact path="/logout-redirect.html" onEnter={reload} />
<Route path="/callback" render={
() => <CallbackPage userManager={userManager} />
} />
<Route component={() => {
userManager.signinRedirect();
return null;
}} />
</Switch>
)
}
return (
<Switch>
<Route exact path="/silent-refresh.html" onEnter={reload} />
<Route exact path="/logout-redirect.html" onEnter={reload} />
<Route
exact
path="/studylist"
component={StudyListRouting}
/>
<Route
exact
path="/"
component={StudyListRouting}
/>
<Route
exact
path="/viewer"
component={StandaloneRouting}
/>
<Route
path="/viewer/:studyInstanceUids"
component={ViewerRouting}
/>
<Route
path="/study/:studyInstanceUid/series/:seriesInstanceUids"
component={ViewerRouting}
/>
<Route
path="/IHEInvokeImageDisplay"
component={IHEInvokeImageDisplay}
/>
<Route path="/silent-refresh.html" onEnter={reload} />
<Route path="/logout-redirect.html" onEnter={reload} />
<Route exact path='/login' component={() => {
userManager.signinRedirect();
}}
/>
<Route path="/callback" component={CallbackPage} />
<Route render={() =>
<div> Sorry, this page does not exist. </div>}
/>
</Switch>
);
}
}
const mapStateToProps = state => {
return {
user: state.oidc.user,
};
};
const ConnectedApp = connect(
mapStateToProps,
null
)(App);
export default ViewerbaseDragDropContext(withRouter(ConnectedApp));

View File

@ -1,32 +0,0 @@
import React, { Component } from "react";
import { CallbackComponent } from "redux-oidc";
import { withRouter } from "react-router-dom";
import PropTypes from 'prop-types';
class CallbackPage extends Component {
static propTypes = {
userManager: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
}
render() {
return (
<CallbackComponent
userManager={this.props.userManager}
successCallback={() => {
const pathname = sessionStorage.getItem('ohif-redirect-to');
this.props.history.push(pathname);
}}
errorCallback={error => {
//this.props.history.push("/");
throw new Error(error);
}}
>
<div>Redirecting...</div>
</CallbackComponent>
);
}
}
export default withRouter(CallbackPage);

View File

@ -1,21 +0,0 @@
import { connect } from 'react-redux';
import ExampleViewportPlugin from './ExampleViewportPlugin';
// We don't really need Redux to do this, this is just an example
const mapStateToProps = (state, ownProps) => {
const { displaySet } = ownProps.viewportData;
const { studyInstanceUid, seriesInstanceUid } = displaySet;
return {
studyInstanceUid,
seriesInstanceUid,
displaySet
};
};
const ConnectedExampleViewportPlugin = connect(
mapStateToProps,
null
)(ExampleViewportPlugin);
export default ConnectedExampleViewportPlugin;

View File

@ -1,27 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class ExampleViewportPlugin extends Component {
static propTypes = {
studyInstanceUid: PropTypes.string.isRequired,
seriesInstanceUid: PropTypes.string.isRequired,
displaySet: PropTypes.object
};
static defaultProps = {
studyInstanceUid: '',
seriesInstanceUid: ''
};
render() {
return (
<div className="ExampleViewportPlugin">
<p>StudyInstanceUid: {this.props.studyInstanceUid}</p>
<p>SeriesInstanceUid: {this.props.seriesInstanceUid}</p>
{JSON.stringify(this.props.displaySet)}
</div>
);
}
}
export default ExampleViewportPlugin;

View File

@ -1,24 +0,0 @@
import { connect } from 'react-redux';
import Header from './Header.js';
import { setUserPreferencesModalOpen } from '../../redux/actions.js';
const mapStateToProps = state => {
return {
isOpen: state.ui.userPreferencesModalOpen,
};
};
const mapDispatchToProps = dispatch => {
return {
openUserPreferencesModal: () => {
dispatch(setUserPreferencesModalOpen(true))
}
};
};
const ConnectedHeader = connect(
mapStateToProps,
mapDispatchToProps
)(Header);
export default ConnectedHeader;

View File

@ -1,108 +0,0 @@
.entry-header {
padding: 10px 10px 0;
height: var(--top-bar-height);
}
.entry-header.header-big {
background: rgba(21,25,30,0.7);
padding: 10px var(--study-list-padding);
height: auto;
display: inline-block;
width: 100%;
}
.header-left-box {
display: inline-block;
position: relative;
}
.header-viewerLink {
float: left;
}
.header-brand {
height: 30px;
text-decoration: none;
float: left;
clear: both;
}
.entry-header.header-big .header-brand {
height: auto;
padding: 25px 0;
}
.header-logo-image {
fill: transparent;
height: 100%;
margin: 0 8px 0 0;
width: 30px;
float: left;
}
.entry-header.header-big .header-logo-image {
margin: 0 20px 0 0;
width: 50px;
height: 50px;
}
.header-logo-text {
display: inline-block;
font-family: var(--logo-font-family);
font-size: 14px;
font-weight: var(--logo-font-weight);
color: var(--text-primary-color);
line-height: 30px;
}
.entry-header.header-big .header-logo-text {
font-size: 30px;
line-height: 36px;
}
.header-btn {
color: var(--text-secondary-color);
cursor: pointer;
font-size: 13px;
font-weight: 500;
line-height: 26px;
}
.header-btn:hover {
color: var(--hover-color);
}
.header-btn:active {
color: var(--active-color);
}
.header-studyListLinkSection {
border-left: var(--ui-border-thickness) solid var(--ui-border-color);
margin: 3px 0 0 10px;
padding: 0 0 0 10px;
display: inline-block;
}
.header-menu {
color: var(--text-primary-color);
float: right;
padding: 4px 0;
font-size: 13px;
font-weight: 400;
line-height: 18px;
text-decoration: none;
margin-right: 10px;
}
.entry-header.header-big .header-menu {
margin-right: 0;
}
.header-menu .research-use {
float: left;
font-size: 13px;
font-weight: 700;
color: #91b9cd;
margin-right: 1rem;
}

View File

@ -1,85 +0,0 @@
import React, { Component } from "react";
import PropTypes from 'prop-types';
import { Link, withRouter } from 'react-router-dom';
import { Dropdown } from "react-viewerbase";
import './Header.css';
import ConnectedUserPreferencesModal from "../../connectedComponents/ConnectedUserPreferencesModal";
const Icons = '/icons.svg';
class Header extends Component {
static propTypes = {
home: PropTypes.bool.isRequired,
location: PropTypes.object,
openUserPreferencesModal: PropTypes.func,
};
static defaultProps = {
home: true
};
constructor(props) {
super(props);
this.state = {
userPreferencesOpen: false,
};
this.options = [
{
title: 'Preferences ',
icon: 'fa fa-user',
onClick: this.props.openUserPreferencesModal,
},
{
title: 'About',
icon: 'fa fa-info',
link: 'http://ohif.org',
}
];
}
render() {
return (<div className={`entry-header ${this.props.home ? 'header-big' : ''}`}>
<div className='header-left-box'>
{
this.props.location && this.props.location.studyLink &&
<Link to={this.props.location.studyLink} className="header-btn header-viewerLink">
Back to Viewer
</Link>
}
<a target="_blank" rel="noopener noreferrer" className="header-brand" href="http://ohif.org">
<svg className="header-logo-image">
<use xlinkHref={`${Icons}#icon-ohif-logo`} />
</svg>
<div className="header-logo-text">Open Health Imaging Foundation</div>
</a>
{!this.props.home &&
<Link className='header-btn header-studyListLinkSection' to={{
pathname: "/",
state: { studyLink: this.props.location.pathname }
}}>Study list</Link>
}
</div>
<div className="header-menu">
<span className="research-use">
INVESTIGATIONAL USE ONLY
</span>
<Dropdown
title='Options'
list={this.options}
align='right'
/>
<ConnectedUserPreferencesModal />
</div>
</div>
);
}
}
export default withRouter(Header)

View File

@ -1,15 +0,0 @@
import UserPreferences from "../UserPreferences/UserPreferences";
export default [
{
"title": "Preferences",
"icon": "fa fa-user",
"onClick": UserPreferences,
"state": { "modal": true }
},
{
"title": "About",
"icon": "fa fa-info",
"link": "http://ohif.org"
}
];

View File

@ -1,3 +0,0 @@
import Header from './Header'
export default Header

View File

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

View File

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

View File

@ -1,73 +0,0 @@
import dicomParser from 'dicom-parser';
import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import cornerstoneMath from 'cornerstone-math';
import OHIF from 'ohif-core';
import sha from './sha.js';
import version from './version.js';
import { homepage } from '../package.json';
window.info = {
sha,
version,
homepage
};
// For debugging
window.cornerstone = cornerstone;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.external.cornerstone = cornerstone;
OHIF.external.cornerstoneMath = cornerstoneMath;
OHIF.external.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
// TODO: Is there a better way to guess ROOT_URL?
let ROOT_URL = homepage;
// If the page we are on is not a subset of the expected homepage
// provided in the package.json file, we might be doing local development.
// In this case, set the base URL to the current location's origin.
if (homepage.includes(window.location.origin) === false) {
ROOT_URL = window.location.origin;
}
const config = {
maxWebWorkers: Math.max(navigator.hardwareConcurrency - 1, 1),
startWebWorkersOnDemand: true,
webWorkerPath: ROOT_URL + '/cornerstoneWADOImageLoaderWebWorker.min.js',
taskConfiguration: {
decodeTask: {
loadCodecsOnStartup: true,
initializeCodecsOnStartup: false,
codecsPath: ROOT_URL + '/cornerstoneWADOImageLoaderCodecs.min.js',
usePDFJS: false,
strict: false
}
}
};
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return;
}
return state.oidc.user.access_token;
}
cornerstoneWADOImageLoader.configure({
beforeSend: function(xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
if (headers.Authorization) {
xhr.setRequestHeader("Authorization", headers.Authorization);
}
}
});

View File

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

View File

@ -1,16 +0,0 @@
import { connect } from 'react-redux';
import FlexboxLayout from './FlexboxLayout';
const mapStateToProps = state => {
return {
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen,
};
};
const ConnectedFlexboxLayout = connect(
mapStateToProps,
null
)(FlexboxLayout);
export default ConnectedFlexboxLayout;

View File

@ -1,41 +0,0 @@
import { connect } from 'react-redux';
import { LayoutButton } from 'react-viewerbase';
import OHIF from 'ohif-core'
const { setLayout } = OHIF.redux.actions;
const mapStateToProps = state => {
return {
currentLayout: state.viewports.layout,
};
};
const mapDispatchToProps = dispatch => {
return {
// TODO: Change if layout switched becomes more complex
onChange: selectedCell => {
let viewports = [];
const rows = selectedCell.row + 1;
const columns = selectedCell.col + 1;
const numViewports = rows * columns;
for (let i = 0; i < numViewports; i++) {
viewports.push({
height: `${100 / rows}%`,
width: `${100 / columns}%`
});
}
const layout = {
viewports
};
dispatch(setLayout(layout))
}
};
};
const ConnectedLayoutButton = connect(
mapStateToProps,
mapDispatchToProps
)(LayoutButton);
export default ConnectedLayoutButton;

View File

@ -1,34 +0,0 @@
import { connect } from 'react-redux';
import { LayoutManager } from 'react-viewerbase';
import OHIF from 'ohif-core';
const mapStateToProps = state => {
const viewportPluginIds = state.plugins.availablePlugins.filter(plugin => plugin.type === OHIF.plugins.PLUGIN_TYPES.VIEWPORT).map(plugin => plugin.id);
const availablePlugins = {}
viewportPluginIds.forEach(id => {
const plugin = OHIF.plugins.availablePlugins.find(plugin => plugin.id === id);
if (plugin) {
availablePlugins[id] = plugin.component;
}
});
// TODO Use something like state.plugins.defaultPlugin[OHIF.plugins.PLUGIN_TYPES.VIEWPORT]
let defaultPlugin;
if (viewportPluginIds && viewportPluginIds.length) {
defaultPlugin = viewportPluginIds[0];
}
return {
layout: state.viewports.layout,
activeViewportIndex: state.viewports.activeViewportIndex,
availablePlugins,
defaultPlugin
};
};
const ConnectedLayoutManager = connect(
mapStateToProps,
null
)(LayoutManager);
export default ConnectedLayoutManager;

View File

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

View File

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

View File

@ -1,29 +0,0 @@
import { connect } from 'react-redux';
import ToolbarRow from './ToolbarRow';
import { setLeftSidebarOpen, setRightSidebarOpen } from '../redux/actions.js';
const mapStateToProps = state => {
return {
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen
};
};
const mapDispatchToProps = dispatch => {
return {
setLeftSidebarOpen: state => {
dispatch(setLeftSidebarOpen(state))
},
setRightSidebarOpen: state => {
dispatch(setRightSidebarOpen(state))
}
};
};
const ConnectedToolbarRow = connect(
mapStateToProps,
mapDispatchToProps
)(ToolbarRow);
export default ConnectedToolbarRow;

View File

@ -1,29 +0,0 @@
import { connect } from 'react-redux';
import { ToolbarSection } from 'react-viewerbase';
import OHIF from 'ohif-core'
const { setToolActive } = OHIF.redux.actions;
const mapStateToProps = state => {
const activeButton = state.tools.buttons.find(tool => tool.active === true);
return {
buttons: state.tools.buttons,
activeCommand: activeButton && activeButton.command
};
};
const mapDispatchToProps = dispatch => {
return {
setToolActive: tool => {
dispatch(setToolActive(tool.command))
}
};
};
const ConnectedToolbarSection = connect(
mapStateToProps,
mapDispatchToProps
)(ToolbarSection);
export default ConnectedToolbarSection;

View File

@ -1,44 +0,0 @@
import { connect } from 'react-redux';
import { UserPreferencesModal } from 'react-viewerbase';
import OHIF from 'ohif-core';
import { setUserPreferencesModalOpen } from '../redux/actions.js';
import cloneDeep from 'lodash.clonedeep';
const { setUserPreferences } = OHIF.redux.actions;
const mapStateToProps = state => {
const contextName = window.store.getState().commandContext.context;
return {
isOpen: state.ui.userPreferencesModalOpen,
windowLevelData: state.preferences[contextName] ? state.preferences[contextName].windowLevelData : {},
hotKeysData: state.preferences[contextName] ? state.preferences[contextName].hotKeysData : {},
};
};
const mapDispatchToProps = dispatch => {
return {
onCancel: () => {
dispatch(setUserPreferencesModalOpen(false));
},
onSave: data => {
const contextName = window.store.getState().commandContext.context;
const preferences = cloneDeep(window.store.getState().preferences)
preferences[contextName] = data;
dispatch(setUserPreferences(preferences));
dispatch(setUserPreferencesModalOpen(false));
OHIF.hotkeysUtil.setHotkeys(data.hotKeysData);
},
onResetToDefaults: () => {
dispatch(setUserPreferences());
dispatch(setUserPreferencesModalOpen(false));
OHIF.hotkeysUtil.setHotkeys();
},
};
};
const ConnectedUserPreferencesModal = connect(
mapStateToProps,
mapDispatchToProps
)(UserPreferencesModal);
export default ConnectedUserPreferencesModal;

View File

@ -1,19 +0,0 @@
import { connect } from 'react-redux';
import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js';
const isActive = (a) => a.active === true;
const mapStateToProps = state => {
const activeServer = state.servers.servers.find(isActive);
return {
server: activeServer,
};
};
const ConnectedViewerRetrieveStudyData = connect(
mapStateToProps,
null
)(ViewerRetrieveStudyData);
export default ConnectedViewerRetrieveStudyData;

View File

@ -1,47 +0,0 @@
.FlexboxLayout {
display: flex;
flex: 1;
flex-flow: row nowrap;
align-items: stretch;
height: calc(100% - var(--toolbar-height));
width: 100%;
}
.sidebar-menu {
height: 100%;
/* required transformation to make inner fixed elements relative to this one*/
transform: scale(1);
transition: var(--sidebar-transition);
}
.sidebar-left {
border-right: var(--ui-border-thickness) solid var(--ui-border-color);
flex: 1;
margin-left: calc(var(--left-sidebar-menu-width) * -1);
max-width: var(--left-sidebar-menu-width);
order: 1
}
.sidebar-left.sidebar-open {
margin-left: 0
}
.main-content {
flex: 1;
height: 100%;
order: 2;
overflow: hidden;
transition: var(--sidebar-transition);
width: 100%;
}
.sidebar-right {
flex: 1;
margin-right: calc(var(--right-sidebar-menu-width) * -1);
max-width: var(--right-sidebar-menu-width);
order: 3;
}
.sidebar-right.sidebar-open {
margin-right: 0;
}

View File

@ -1,90 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
import ViewerMain from './ViewerMain.js';
import './FlexboxLayout.css';
class FlexboxLayout extends Component {
static propTypes = {
studies: PropTypes.array.isRequired,
leftSidebarOpen: PropTypes.bool.isRequired,
rightSidebarOpen: PropTypes.bool.isRequired,
};
state = {
studiesForBrowser: []
};
componentDidMount() {
const studiesForBrowser = this.getStudiesForBrowser();
this.setState({
studiesForBrowser
});
}
getStudiesForBrowser = () => {
const { studies } = this.props;
// TODO[react]:
// - Add sorting of display sets
// - Add useMiddleSeriesInstanceAsThumbnail
// - Add showStackLoadingProgressBar option
return studies.map((study) => {
const { studyInstanceUid } = study;
const thumbnails = study.displaySets.map((displaySet) => {
const { displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames } = displaySet;
let imageId;
if (displaySet.images && displaySet.images.length) {
imageId = displaySet.images[0].getImageId();
}
return {
imageId,
displaySetInstanceUid,
seriesDescription,
seriesNumber,
instanceNumber,
numImageFrames,
};
});
return {
studyInstanceUid,
thumbnails
};
});
}
render() {
let mainContentClassName = "main-content"
if (this.props.leftSidebarOpen) {
mainContentClassName += ' sidebar-left-open';
}
if (this.props.rightSidebarOpen) {
mainContentClassName += ' sidebar-right-open';
}
// TODO[react]: Add measurementLightTable
return (
<div className="FlexboxLayout">
<div className={this.props.leftSidebarOpen ? "sidebar-menu sidebar-left sidebar-open" : "sidebar-menu sidebar-left"}>
<ConnectedStudyBrowser studies={this.state.studiesForBrowser}/>
</div>
<div className={mainContentClassName}>
<ViewerMain studies={this.props.studies}/>
</div>
{/* <div className={this.props.rightSidebarOpen ? "sidebar-menu sidebar-right sidebar-open" : "sidebar-menu sidebar-right"}>
{{> measurementLightTable (clone this)}}
</div> */}
</div>
);
}
}
export default FlexboxLayout;

View File

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

View File

@ -1,85 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import OHIF from 'ohif-core';
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
import OHIFComponentPlugin from '../OHIFComponentPlugin.js';
import cornerstoneTools from "cornerstone-tools";
import cornerstone from "cornerstone-core";
import './config';
const { StackManager } = OHIF.utils;
// Create the synchronizer used to update reference lines
OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer('cornerstonenewimage', cornerstoneTools.updateImageSynchronizer);
// Metadata configuration
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
cornerstone.metaData.addProvider(metadataProvider.provider.bind(metadataProvider));
StackManager.setMetadataProvider(metadataProvider);
class OHIFCornerstoneViewportPlugin extends Component {
static propTypes = {
studies: PropTypes.object,
displaySet: PropTypes.object,
viewportIndex: PropTypes.number,
};
static id = 'CornerstoneViewportPlugin';
static init() {
console.log('CornerstoneViewportPlugin init()');
}
static destroy() {
console.log('CornerstoneViewportPlugin destroy()');
StackManager.clearStacks();
}
static getCornerstoneStack(studies, studyInstanceUid, displaySetInstanceUid) {
// Create shortcut to displaySet
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
const displaySet = study.displaySets.find((set) => {
return set.displaySetInstanceUid === displaySetInstanceUid;
});
// Get stack from Stack Manager
return StackManager.findOrCreateStack(study, displaySet);
}
static getPluginViewportData = (studies, studyInstanceUid, displaySetInstanceUid) => {
const currentStack = OHIFCornerstoneViewportPlugin.getCornerstoneStack(studies, studyInstanceUid, displaySetInstanceUid);
// Clone the stack here so we don't mutate it later
const stack = Object.assign({}, currentStack);
stack.currentImageIdIndex = 0;
return stack;
}
render() {
const { viewportIndex } = this.props;
const { studies, displaySet } = this.props.viewportData;
const { studyInstanceUid, displaySetInstanceUid } = displaySet;
const stack = OHIFCornerstoneViewportPlugin.getPluginViewportData(studies, studyInstanceUid, displaySetInstanceUid);
const viewportData = {
studyInstanceUid,
displaySetInstanceUid,
stack
};
const { id, init, destroy } = OHIFCornerstoneViewportPlugin;
const pluginProps = { id, init, destroy };
return (
<OHIFComponentPlugin {...pluginProps}>
<ConnectedCornerstoneViewport viewportData={viewportData} viewportIndex={viewportIndex}/>
</OHIFComponentPlugin>
);
}
}
export default OHIFCornerstoneViewportPlugin;

View File

@ -1,31 +0,0 @@
import cornerstone from 'cornerstone-core';
import cornerstoneMath from 'cornerstone-math';
import cornerstoneTools from 'cornerstone-tools';
import Hammer from 'hammerjs';
import OHIF from 'ohif-core';
// For debugging
window.cornerstoneTools = cornerstoneTools;
cornerstoneTools.external.cornerstone = cornerstone;
cornerstoneTools.external.Hammer = Hammer;
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
cornerstoneTools.init();
OHIF.external.cornerstoneTools = cornerstoneTools;
// Set the tool font and font size
// context.font = "[style] [variant] [weight] [size]/[line height] [font family]";
const fontFamily =
'Roboto, OpenSans, HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif';
cornerstoneTools.textStyle.setFont(`16px ${fontFamily}`);
// Set the tool width
cornerstoneTools.toolStyle.setToolWidth(2);
// Set color for inactive tools
cornerstoneTools.toolColors.setToolColor('rgb(255, 255, 0)');
// Set color for active tools
cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)');
cornerstoneTools.store.state.touchProximity = 40;

View File

@ -1,32 +0,0 @@
import OHIF from "ohif-core";
const { plugins, utils } = OHIF;
const { PLUGIN_TYPES } = plugins;
// TODO: Should probably use dcmjs for this
const SOP_CLASS_UIDS = {
ENCAPSULATED_PDF: '1.2.840.10008.5.1.4.1.1.104.1'
};
const OHIFDicomPDFSopClassHandlerPlugin = {
id: 'OHIFDicomPDFSopClassHandlerPlugin',
type: PLUGIN_TYPES.SOP_CLASS_HANDLER,
sopClassUids: [
SOP_CLASS_UIDS.ENCAPSULATED_PDF
],
getDisplaySetFromSeries(series, study) {
const instance = series.getFirstInstance();
return {
plugin: 'pdf',
displaySetInstanceUid: utils.guid(),
wadoRoot: study.getData().wadoRoot,
wadoUri: instance.getData().wadouri,
sopInstanceUid: instance.getSOPInstanceUID(),
seriesInstanceUid: series.getSeriesInstanceUID(),
studyInstanceUid: study.getStudyInstanceUID()
};
}
}
export default OHIFDicomPDFSopClassHandlerPlugin;

View File

@ -1,77 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import OHIF from 'ohif-core';
import OHIFComponentPlugin from '../OHIFComponentPlugin.js';
import DicomPDFViewport from './DicomPDFViewport';
const { DICOMWeb } = OHIF;
class OHIFDicomPDFViewportPlugin extends Component {
static propTypes = {
studies: PropTypes.object,
displaySet: PropTypes.object,
viewportIndex: PropTypes.number,
};
state = {
byteArray: null,
error: null
}
static id = 'DicomPDFViewportPlugin';
static init() {
console.log('DicomPDFViewportPlugin init()');
}
static destroy() {
console.log('DicomPDFViewportPlugin destroy()');
}
componentDidMount() {
const { displaySet } = this.props.viewportData;
const { studyInstanceUid, seriesInstanceUid, sopInstanceUid, wadoRoot, wadoUri } = displaySet;
this.retrieveDicomData(studyInstanceUid, seriesInstanceUid, sopInstanceUid, wadoRoot, wadoUri).then(byteArray => {
this.setState({
byteArray
});
}, error => {
this.setState({
error
});
throw new Error(error);
});
}
retrieveDicomData(studyInstanceUid, seriesInstanceUid, sopInstanceUid, wadoRoot, wadoUri) {
// TODO: Passing in a lot of data we aren't using
// TODO: Authorization header depends on the server. If we ever have multiple servers
// we will need to figure out how / when to pass this information in.
return fetch(wadoUri, {
headers: DICOMWeb.getAuthorizationHeader()
}).then(response => response.arrayBuffer()).then(arraybuffer => {
debugger;
return new Uint8Array(arraybuffer);
});
}
render() {
const { id, init, destroy } = OHIFDicomPDFViewportPlugin;
const pluginProps = { id, init, destroy };
return (
<OHIFComponentPlugin {...pluginProps}>
{this.state.byteArray && <DicomPDFViewport byteArray={this.state.byteArray}/>}
{this.state.error &&
<h2>{JSON.stringify(this.state.error)}</h2>
}
</OHIFComponentPlugin>
);
}
}
export default OHIFDicomPDFViewportPlugin;

View File

@ -1,17 +0,0 @@
.ToolbarRow {
border-bottom: var(--ui-border-thickness) solid var(--ui-border-color);
flex: 0 0 auto;
height: var(--toolbar-height);
padding-left: 1rem;
padding-right: 1rem;
position: relative;
transition: height 300ms ease;
width: 100%;
display: flex;
align-items: center;
}
ToolbarRow.expanded {
height: calc(var(--toolbar-height) + var(--toolbar-drawer-height));
}

View File

@ -1,77 +0,0 @@
import React, { Component } from "react";
import ConnectedToolbarSection from './ConnectedToolbarSection';
import ConnectedLayoutButton from './ConnectedLayoutButton';
import ConnectedCineDialog from './ConnectedCineDialog.js';
import PropTypes from 'prop-types';
import { ToolbarButton, RoundedButtonGroup } from 'react-viewerbase';
import './ToolbarRow.css';
//import Icons from "../images/icons.svg"
const Icons = '/icons.svg';
class ToolbarRow extends Component {
state = {
cineDialogOpen: false
};
static propTypes = {
leftSidebarOpen: PropTypes.bool.isRequired,
rightSidebarOpen: PropTypes.bool.isRequired,
setLeftSidebarOpen: PropTypes.func,
setRightSidebarOpen: PropTypes.func
};
static defaultProps = {
leftSidebarOpen: false,
rightSidebarOpen: false
};
onLeftSidebarValueChanged = (value) => {
this.props.setLeftSidebarOpen(!!value);
}
onClickCineToolbarButton = () => {
this.setState({
cineDialogOpen: !this.state.cineDialogOpen
});
}
render() {
const leftSidebarToggle = [{
value: 'studies',
svgLink: `${Icons}#icon-studies`,
svgWidth: 15,
svgHeight: 13,
bottomLabel: 'Series',
}];
const leftSidebarValue = this.props.leftSidebarOpen ? leftSidebarToggle[0].value : null;
const cineDialogContainerStyle = {
display: this.state.cineDialogOpen ? 'inline-block' : 'none'
}
return (<div className="ToolbarRow">
<div className="pull-left m-t-1 p-y-1" style={{ padding: '10px' }}>
<RoundedButtonGroup options={leftSidebarToggle} value={leftSidebarValue} onValueChanged={this.onLeftSidebarValueChanged}/>
</div>
<ConnectedToolbarSection/>
<ConnectedLayoutButton/>
<ToolbarButton
active={this.state.cineDialogOpen}
onClick={this.onClickCineToolbarButton}
text={'CINE'}
iconClasses={'fa fa-youtube'}
/>
<div className='CineDialogContainer' style={cineDialogContainerStyle}>
<ConnectedCineDialog/>
</div>
<div className="pull-right m-t-1 rm-x-1">
{/* > roundedButtonGroup rightSidebarToggleButtonData */}
</div>
</div>
);
}
}
export default ToolbarRow;

View File

@ -1,10 +0,0 @@
#viewer {
background-color: black;
width: 100%;
}
.loadingTextDiv {
color: var(--text-secondary-color);
font-size: 30px;
}

View File

@ -1,70 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
//import { CineDialog } from 'react-viewerbase';
import ConnectedFlexboxLayout from './ConnectedFlexboxLayout.js';
import ConnectedToolbarRow from './ConnectedToolbarRow';
import ConnectedStudyLoadingMonitor from './ConnectedStudyLoadingMonitor.js';
import StudyPrefetcher from '../components/StudyPrefetcher.js';
import './Viewer.css';
import ConnectedHeader from "../components/Header/ConnectedHeader";
/**
* Inits OHIF Hanging Protocol's onReady.
* It waits for OHIF Hanging Protocol to be ready to instantiate the ProtocolEngine
* Hanging Protocol will use OHIF LayoutManager to render viewports properly
*/
/*const initHangingProtocol = () => {
// When Hanging Protocol is ready
HP.ProtocolStore.onReady(() => {
// Gets all StudyMetadata objects: necessary for Hanging Protocol to access study metadata
const studyMetadataList = OHIF.viewer.StudyMetadataList.all();
// Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata
const studyMetadataSource = new OHIF.studies.classes.OHIFStudyMetadataSource();
// Get prior studies map
const studyPriorsMap = OHIF.studylist.functions.getStudyPriorsMap(studyMetadataList);
// Creates Protocol Engine object with required arguments
const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, studyPriorsMap, studyMetadataSource);
// Sets up Hanging Protocol engine
HP.setEngine(ProtocolEngine);
});
};*/
/*const viewportUtils = OHIF.viewerbase.viewportUtils;
OHIF.viewer.functionList = {
toggleCineDialog: viewportUtils.toggleCineDialog,
toggleCinePlay: viewportUtils.toggleCinePlay,
clearTools: viewportUtils.clearTools,
resetViewport: viewportUtils.resetViewport,
invert: viewportUtils.invert
};*/
class Viewer extends Component {
static propTypes = {
studies: PropTypes.array
};
render() {
return (<>
<ConnectedHeader home={false} />
<div className='viewerDialogs'>
</div>
<div id="viewer" className='Viewer'>
<ConnectedToolbarRow />
<ConnectedStudyLoadingMonitor studies={this.props.studies} />
<StudyPrefetcher studies={this.props.studies} />
<ConnectedFlexboxLayout studies={this.props.studies} />
</div>
</>
);
}
}
export default Viewer;

View File

@ -1,41 +0,0 @@
#viewer {
height: calc(100% - var(--top-bar-height));
}
#viewer > .loadingTextDiv {
color: var(--text-secondary-color);
font-size: 30px;
height: 100%;
line-height: calc(100% - var(--top-bar-height));
}
.ViewerMain {
width: 100%;
height: 100%;
transition: all 0.3s ease;
}
.ViewerMain>div {
width: 100%;
height: 100%;
}
.ViewerMain #imageViewerViewports .viewportContainer {
/*theme('border', '%s solid $uiBorderColorDark' % $viewportBorderThickness)*/
/*float: left;
position: relative;*/
outline: 0; /* Prevent blue outline in Chrome */
}
.ViewerMain #imageViewerViewports .viewportContainer:hover,
.ViewerMain #imageViewerViewports .viewportContainer:active,
.ViewerMain #imageViewerViewports .viewportContainer:hover.active {
outline: 0; /* Prevent blue outline in Chrome */
}
/* &:hover
//theme('border', '%s solid $uiBorderColor' % $viewportBorderThickness)
&.active, &:hover.active
//theme('border', '%s solid $uiBorderColorActive' % $viewportBorderThickness)
*/

View File

@ -1,107 +0,0 @@
import { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { OHIF } from 'ohif-core';
import ConnectedLayoutManager from './ConnectedLayoutManager.js';
import './ViewerMain.css';
class ViewerMain extends Component {
state = {
viewportData: [],
};
static propTypes = {
studies: PropTypes.array.isRequired
};
constructor(props) {
super(props);
OHIF.hotkeysUtil.setup('viewer');
}
getDisplaySets(studies) {
const displaySets = [];
studies.forEach((study) => {
study.displaySets.forEach(dSet => {
displaySets.push(dSet);
});
});
return displaySets;
}
findDisplaySet(studies, studyInstanceUid, displaySetInstanceUid) {
const study = studies.find(study => {
return study.studyInstanceUid === studyInstanceUid;
});
if (!study) {
return;
}
return study.displaySets.find(displaySet => {
return displaySet.displaySetInstanceUid === displaySetInstanceUid;
});
}
componentDidMount() {
// Add beforeUnload event handler to check for unsaved changes
//window.addEventListener('beforeunload', unloadHandlers.beforeUnload);
// Get all the display sets for the viewer studies
const displaySets = this.getDisplaySets(this.props.studies);
this.setState({
viewportData: displaySets
});
}
setViewportData = ({ viewportIndex, item }) => {
// TODO: Replace this with mapDispatchToProps call
// if we decide to put viewport info into redux
// Note: Use Slice because React does a shallow equality check. Mutating the array
// would not trigger a re-render. We have to create a copy.
const updatedViewportData = this.state.viewportData.slice(0);
const displaySet = this.findDisplaySet(this.props.studies, item.studyInstanceUid, item.displaySetInstanceUid);
updatedViewportData[viewportIndex] = Object.assign({}, displaySet);
this.setState({
viewportData: updatedViewportData
});
}
render() {
return (
<div className="ViewerMain">
<ConnectedLayoutManager
studies={this.props.studies}
viewportData={this.state.viewportData}
setViewportData={this.setViewportData}
/>
</div>
);
}
componentWillUnmount() {
// Remove beforeUnload event handler...
//window.removeEventListener('beforeunload', unloadHandlers.beforeUnload);
// Destroy the synchronizer used to update reference lines
OHIF.viewer.updateImageSynchronizer.destroy();
// TODO: Instruct all plugins to clean up themselves
// @TypeSafeStudies
// Clears OHIF.viewer.Studies collection
//OHIF.viewer.Studies.removeAll();
// @TypeSafeStudies
// Clears OHIF.viewer.StudyMetadataList collection
//OHIF.viewer.StudyMetadataList.removeAll();
}
}
export default ViewerMain;

View File

@ -1,56 +0,0 @@
import React, {Component} from "react";
import PropTypes from "prop-types";
import OHIF from 'ohif-core';
import Viewer from "./Viewer.js";
const { createDisplaySets } = OHIF.utils;
class ViewerRetrieveStudyData extends Component {
static propTypes = {
studyInstanceUids: PropTypes.array.isRequired,
seriesInstanceUids: PropTypes.array,
server: PropTypes.object
};
state = {
studies: null,
error: null
}
componentDidMount() {
// TODO: Avoid using timepoints here
//const params = { studyInstanceUids, seriesInstanceUids, timepointId, timepointsFilter={} };
const { studyInstanceUids, seriesInstanceUids, server } = this.props;
const promise = OHIF.studies.retrieveStudiesMetadata(server, studyInstanceUids, seriesInstanceUids)
// Render the viewer when the data is ready
promise.then(studies => {
const updatedStudies = createDisplaySets(studies);
this.setState({
studies: updatedStudies,
});
}).catch(error => {
this.setState({
error: true,
});
throw new Error(error);
});
}
render() {
if (this.state.error) {
return (<div>Error: {JSON.stringify(this.state.error)}</div>);
} else if (!this.state.studies) {
return (<div>Loading...</div>);
}
return (
<Viewer studies={this.state.studies}/>
);
}
}
export default ViewerRetrieveStudyData;

View File

@ -1,15 +0,0 @@
<head>
<meta charset="utf-8">
<title>OHIF Viewer</title>
<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">
<link rel="shortcut icon" type="image/png" href="/images/logo.png" sizes="16x16 32x32 64x64">
<!--Best display for mobile devices-->
<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">
</head>
<body>
<div id="root"></div>
</body>

View File

@ -1,234 +0,0 @@
import React from "react";
import ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux';
import { createStore, combineReducers } from 'redux';
import { loadUser, reducer as oidcReducer, OidcProvider, createUserManager } from 'redux-oidc';
import OHIF from 'ohif-core';
import './config';
import ui from './redux/ui.js';
import App from './App.js';
import { loadState, saveState } from './redux/localStorageState.js';
import OHIFCornerstoneViewportPlugin from "./connectedComponents/OHIFCornerstoneViewportPlugin/OHIFCornerstoneViewportPlugin.js";
//import ConnectedExampleViewportPlugin from './components/ConnectedExampleViewportPlugin.js';
//import OHIFVTKViewportPlugin from './connectedComponents/OHIFVTKViewportPlugin/OHIFVTKViewportPlugin.js';
import OHIFDicomPDFViewportPlugin from './connectedComponents/OHIFDicomPDFViewportPlugin/OHIFDicomPDFViewportPlugin.js';
import OHIFDicomPDFSopClassHandlerPlugin from './connectedComponents/OHIFDicomPDFViewportPlugin/OHIFDicomPDFSopClassHandlerPlugin.js';
let reducers = OHIF.redux.reducers;
reducers.ui = ui;
reducers.oidc = oidcReducer;
const Icons = '/icons.svg';
const combined = combineReducers(reducers);
const store = createStore(combined, loadState());
// Subscribing store to save state of persistable data
store.subscribe(() => {
saveState({
preferences: store.getState().preferences,
});
});
// Note: Run your build like this:
// REACT_APP_CONFIG=$(cat ../config-react/ccc.json) yarn start
//
// If you change the JSON config, you need to re-run the command!
let config;
if (process.env.REACT_APP_CONFIG) {
config = JSON.parse(process.env.REACT_APP_CONFIG);
}
let userManager;
if (config && config.oidc) {
const oidcClient = config.oidc[0];
const settings = {
authority: oidcClient.authServerUrl,
client_id: oidcClient.clientId,
redirect_uri: oidcClient.authRedirectUri,
silent_redirect_uri: '/silent-refresh.html',
post_logout_redirect_uri: oidcClient.postLogoutRedirectUri,
response_type: oidcClient.responseType,
scope: 'email profile openid', // Note: Request must have scope 'openid' to be considered an OpenID Connect request
automaticSilentRenew: true,
revokeAccessTokenOnSignout: true,
filterProtocolClaims: true,
loadUserInfo: true,
extraQueryParams: oidcClient.extraQueryParams
};
userManager = createUserManager(settings);
loadUser(store, userManager);
}
if (config && config.servers) {
OHIF.utils.addServers(config.servers, store);
}
const defaultButtons = [
{
command: 'StackScroll',
type: 'tool',
text: 'Stack Scroll',
svgUrl: `${Icons}#icon-tools-stack-scroll`,
active: false
},
{
command: 'Zoom',
type: 'tool',
text: 'Zoom',
svgUrl: `${Icons}#icon-tools-zoom`,
active: false
},
{
command: 'Wwwc',
type: 'tool',
text: 'Levels',
svgUrl: `${Icons}#icon-tools-levels`,
active: true
},
{
command: 'Pan',
type: 'tool',
text: 'Pan',
svgUrl: `${Icons}#icon-tools-pan`,
active: false
},
{
command: 'Length',
type: 'tool',
text: 'Length',
svgUrl: `${Icons}#icon-tools-measure-temp`,
active: false
},
/*{
command: 'Annotate',
type: 'tool',
text: 'Annotate',
svgUrl: `${Icons}#icon-tools-measure-non-target`,
active: false
},*/
{
command: 'Angle',
type: 'tool',
text: 'Angle',
iconClasses: 'fa fa-angle-left',
active: false
},
{
command: 'Bidirectional',
type: 'tool',
text: 'Bidirectional',
svgUrl: `${Icons}#icon-tools-measure-target`,
active: false
},
{
command: 'reset',
type: 'command',
text: 'Reset',
svgUrl: `${Icons}#icon-tools-reset`,
active: false
},
];
const buttonsAction = OHIF.redux.actions.setAvailableButtons(defaultButtons);
store.dispatch(buttonsAction);
const { plugins } = OHIF;
const { PLUGIN_TYPES } = plugins;
// Uncomment this and comment the Cornerstone version to see how the
// example plugin works
/*const pluginAction = OHIF.redux.actions.addPlugin({
id: 'example',
type: PLUGIN_TYPES.VIEWPORT,
component: ConnectedExampleViewportPlugin
});*/
const cornerstonePluginAction = OHIF.redux.actions.addPlugin({
id: 'cornerstone',
type: PLUGIN_TYPES.VIEWPORT,
component: OHIFCornerstoneViewportPlugin
});
/*const pluginAction = OHIF.redux.actions.addPlugin({
id: 'vtk',
type: PLUGIN_TYPES.VIEWPORT,
component: OHIFVTKViewportPlugin
});*/
const pdfPluginAction = OHIF.redux.actions.addPlugin({
id: 'pdf',
type: PLUGIN_TYPES.VIEWPORT,
component: OHIFDicomPDFViewportPlugin
});
const servers = {
dicomWeb: [
{
"name": "DCM4CHEE",
//"wadoUriRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/wado",
//"qidoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
//"wadoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
"wadoUriRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/wado",
"qidoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
"wadoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
// "wadoUriRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/wado",
// "qidoRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/rs",
// "wadoRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/rs",
"qidoSupportsIncludeField": true,
"imageRendering": "wadors",
"thumbnailRendering": "wadors",
"requestOptions": {
"requestFromBrowser": true,
"logRequests": true,
"logResponses": false,
"logTiming": true,
//"auth": "admin:admin"
//"auth": "cloud:healthcare"
}
}
]
};
OHIF.utils.addServers(servers, store);
const pdfPluginActionSopClass = OHIF.redux.actions.addPlugin({
id: 'pdf_sopClassHandler',
type: PLUGIN_TYPES.SOP_CLASS_HANDLER,
component: OHIFDicomPDFSopClassHandlerPlugin
});
store.dispatch(cornerstonePluginAction);
store.dispatch(pdfPluginAction);
store.dispatch(pdfPluginActionSopClass);
// TODO[react] Use a provider when the whole tree is React
window.store = store;
if (userManager) {
ReactDOM.render(
<Provider store={store}>
<OidcProvider store={store} userManager={userManager}>
<BrowserRouter>
<App userManager={userManager} />
</BrowserRouter>
</OidcProvider>
</Provider>,
document.getElementById('root')
);
} else {
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
}

View File

@ -1,6 +0,0 @@
import loglevel from 'loglevel';
const log = loglevel.getLogger('OHIFViewer');
log.setLevel('info');
export default log;

View File

@ -1,22 +0,0 @@
export const setLeftSidebarOpen = state => ({
type: 'SET_LEFT_SIDEBAR_OPEN',
state
});
export const setRightSidebarOpen = state => ({
type: 'SET_RIGHT_SIDEBAR_OPEN',
state
});
export const setUserPreferencesModalOpen = state => ({
type: 'SET_USER_PREFERENCES_MODAL_OPEN',
state
});
const actions = {
setLeftSidebarOpen,
setRightSidebarOpen,
setUserPreferencesModalOpen
};
export default actions;

View File

@ -1,21 +0,0 @@
export const loadState = () => {
try {
const serializedState = window.localStorage.getItem('state');
if (!serializedState) {
return undefined;
}
return JSON.parse(serializedState)
} catch (e) {
return undefined;
}
}
export const saveState = (state) => {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem('state', serializedState);
} catch (e) {
console.warn(e);
}
}

View File

@ -1,20 +0,0 @@
const defaultState = {
leftSidebarOpen: true,
rightSidebarOpen: false,
userPreferencesModalOpen: false
}
const ui = (state = defaultState, action) => {
switch (action.type) {
case 'SET_LEFT_SIDEBAR_OPEN':
return Object.assign({}, state, { leftSidebarOpen: action.state });
case 'SET_RIGHT_SIDEBAR_OPEN':
return Object.assign({}, state, { rightSidebarOpen: action.state });
case 'SET_USER_PREFERENCES_MODAL_OPEN':
return Object.assign({}, state, { userPreferencesModalOpen: action.state });
default:
return state;
}
};
export default ui;

View File

@ -1,45 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import ViewerRetrieveStudyData from "../connectedComponents/ViewerRetrieveStudyData.js";
function IHEInvokeImageDisplay({ match }) {
const requestType = match.params.query.requestType;
let studyInstanceUids;
//let patientUids;
let displayStudyList = false;
if (requestType === "STUDY") {
studyInstanceUids = match.params.query.studyUID.split(';');
} else if (requestType === "STUDYBASE64") {
const uids = this.params.query.studyUID;
const decodedData = window.atob(uids);
studyInstanceUids = decodedData.split(';');
} else if (requestType === "PATIENT") {
//patientUidspatientUids = this.params.query.patientID.split(';');
displayStudyList = true
} else {
displayStudyList = true
}
if (displayStudyList) {
return ('')//<StudyList patientUids={patientUids}/>);
}
return (
<ViewerRetrieveStudyData studyInstanceUids={studyInstanceUids}/>
);
}
IHEInvokeImageDisplay.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
query: PropTypes.shape({
requestType: PropTypes.string.isRequired,
studyUID: PropTypes.string,
patientID: PropTypes.string
})
})
})
};
export default IHEInvokeImageDisplay;

View File

@ -1,104 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import qs from 'query-string'
import Viewer from "../connectedComponents/Viewer";
import OHIF from 'ohif-core';
const { createDisplaySets } = OHIF.utils;
class StandaloneRouting extends Component {
state = {
studies: null,
error: null
}
static propTypes = {
location: PropTypes.object,
store: PropTypes.object
};
static parseQueryAndFetchStudies(query) {
return new Promise((resolve, reject) => {
const url = query.url;
if (!url) {
reject(new Error('No URL was specified. Use ?url=$yourURL'));
}
// Define a request to the server to retrieve the study data
// as JSON, given a URL that was in the Route
const oReq = new XMLHttpRequest();
// Add event listeners for request failure
oReq.addEventListener('error', (error) => {
OHIF.log.warn('An error occurred while retrieving the JSON data');
reject(error)
});
// When the JSON has been returned, parse it into a JavaScript Object
// and render the OHIF Viewer with this data
oReq.addEventListener('load', () => {
// Parse the response content
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseText
if (!oReq.responseText) {
OHIF.log.warn('Response was undefined');
reject(new Error('Response was undefined'));
}
OHIF.log.info(JSON.stringify(oReq.responseText, null, 2));
const data = JSON.parse(oReq.responseText);
if (data.servers && query.studyInstanceUids) {
const server = data.servers.dicomWeb[0];
server.type = 'dicomWeb';
const studyInstanceUids = query.studyInstanceUids.split(';');
const seriesInstanceUids = [];
OHIF.studies.retrieveStudiesMetadata(server, studyInstanceUids, seriesInstanceUids).then(studies => {
resolve(studies);
}, error => {
reject(error);
});
} else {
resolve(data.studies);
}
});
// Open the Request to the server for the JSON data
// In this case we have a server-side route called /api/
// which responds to GET requests with the study data
OHIF.log.info(`Sending Request to: ${url}`);
oReq.open('GET', url);
oReq.setRequestHeader('Accept', 'application/json')
// Fire the request to the server
oReq.send();
});
}
componentDidMount() {
const query = qs.parse(this.props.location.search);
StandaloneRouting.parseQueryAndFetchStudies(query).then(studies => {
const updatedStudies = createDisplaySets(studies);
this.setState({ studies: updatedStudies });
}, error => {
this.setState({ error });
});
}
render() {
if (this.state.error) {
return (<div>Error: {JSON.stringify(this.state.error)}</div>);
} else if (!this.state.studies) {
return (<div>Loading...</div>);
}
return (
<Viewer studies={this.state.studies}/>
);
}
}
export default StandaloneRouting;

View File

@ -1,32 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import ConnectedViewerRetrieveStudyData from "../connectedComponents/ConnectedViewerRetrieveStudyData";
function ViewerRouting({ match }) {
const { studyInstanceUids, seriesInstanceUids } = match.params;
let studyUIDs;
let seriesUIDs;
if (studyInstanceUids && !seriesInstanceUids) {
studyUIDs = studyInstanceUids.split(';');
} else if (studyInstanceUids && seriesInstanceUids) {
studyUIDs = [match.params.studyInstanceUid];
seriesUIDs = match.params.seriesInstanceUids.split(';');
}
return (
<ConnectedViewerRetrieveStudyData studyInstanceUids={studyUIDs} seriesInstanceUids={seriesUIDs}/>
);
}
ViewerRouting.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
studyInstanceUids: PropTypes.string.isRequired,
seriesInstanceUids: PropTypes.string
})
})
};
export default ViewerRouting;

View File

@ -1,17 +0,0 @@
import IHEInvokeImageDisplay from './IHEInvokeImageDisplay.js';
import StandaloneRouting from './StandaloneRouting.js';
import ViewerRouting from './ViewerRouting.js';
export {
IHEInvokeImageDisplay,
ViewerRouting,
StandaloneRouting
};
const routes = {
IHEInvokeImageDisplay,
ViewerRouting,
StandaloneRouting
};
export default routes;

View File

@ -1,21 +0,0 @@
import { connect } from 'react-redux';
import StudyListWithData from './StudyListWithData.js';
const isActive = (a) => a.active === true;
const mapStateToProps = state => {
const activeServer = state.servers.servers.find(isActive);
return {
server: activeServer,
user: state.oidc.user,
};
};
const ConnectedStudyList = connect(
mapStateToProps,
null
)(StudyListWithData);
export default ConnectedStudyList;

View File

@ -1,24 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import ConnectedStudyList from "./ConnectedStudyList";
// TODO: Move to react-viewerbase
function StudyListRouting({ match }) {
// TODO: Figure out which filters we want to pass in via a URL
//const { patientId } = match.params;
return (
<ConnectedStudyList/>
);
}
StudyListRouting.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
patientIds: PropTypes.string,
})
})
};
export default StudyListRouting;

View File

@ -1,113 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import OHIF from 'ohif-core';
import { withRouter } from 'react-router-dom';
import { StudyList } from 'react-viewerbase';
import ConnectedHeader from "../components/Header/ConnectedHeader";
class StudyListWithData extends Component {
state = {
searchData: {},
studies: null,
error: null
};
static propTypes = {
patientId: PropTypes.string,
server: PropTypes.object,
user: PropTypes.object,
history: PropTypes.object
};
static rowsPerPage = 25;
static defaultSort = { field: 'patientName', order: 'desc' };
componentDidMount() {
// TODO: Avoid using timepoints here
//const params = { studyInstanceUids, seriesInstanceUids, timepointId, timepointsFilter={} };
this.searchForStudies();
}
searchForStudies = (searchData = {
currentPage: 0,
rowsPerPage: StudyListWithData.rowsPerPage
}) => {
const { server } = this.props;
const filter = {
patientId: searchData.patientId,
patientName: searchData.patientName,
accessionNumber: searchData.accessionNumber,
studyDescription: searchData.studyDescription,
modalitiesInStudy: searchData.modalitiesInStudy,
limit: searchData.currentPage * searchData.rowsPerPage + searchData.rowsPerPage,
offset: searchData.currentPage * searchData.rowsPerPage
};
// TODO: add sorting
const promise = OHIF.studies.searchStudies(server, filter)
// Render the viewer when the data is ready
promise.then(studies => {
if (!studies) {
studies = [];
}
// TODO: Fix casing of this property!
const fixedStudies = studies.map(study => {
const fixedStudy = study;
fixedStudy.studyInstanceUID = study.studyInstanceUid;
return fixedStudy;
})
this.setState({
studies: fixedStudies,
});
}).catch(error => {
this.setState({
error: true,
});
throw new Error(error);
});
}
onImport = () => {
//console.log('onImport');
}
onSelectItem = (studyInstanceUID) => {
this.props.history.push(`/viewer/${studyInstanceUID}`);
}
onSearch = (searchData) => {
// TODO: Update search filters
this.searchForStudies(searchData);
}
render() {
if (this.state.error) {
return (<div>Error: {JSON.stringify(this.state.error)}</div>);
} else if (this.state.studies === null) {
return (<div>Loading...</div>);
}
const studyCount = this.state.studies ? this.state.studies.length : 0;
return (<>
<ConnectedHeader home={true} user={this.props.user} />
<StudyList studies={this.state.studies}
studyCount={studyCount}
studyListFunctionsEnabled={false}
onImport={this.onImport}
onSelectItem={this.onSelectItem}
pageSize={this.rowsPerPage}
defaultSort={StudyListWithData.defaultSort}
onSearch={this.onSearch} />
</>
);
}
}
export default withRouter(StudyListWithData);

View File

@ -1,52 +0,0 @@
/* "Tide" theme */
/* TODO:
- Convert the other themes to CSS Variables
- Allow user to set default CSS theme in JS config
- Export the themes as identical JS objects
- For theme switcher, update root-level CSS properties via JS (https://medium.com/@_bengarrison/accessing-and-modifying-css-variables-with-javascript-2ccb735bbff0)
*/
:root {
/* Interface UI Colors */
--default-color: #9CCEF9;
--hover-color: #ffffff;
--active-color: #20A5D6;
--ui-border-color: #44626F;
--ui-border-color-dark: #3C5D80;
--ui-border-color-active: #00a4d9;
--primary-background-color: #000000;
--box-background-color: #3E5975;
/* Common palette */
--ui-yellow: #E29E4A;
--ui-sky-blue: #6FBDE2;
/* State palette */
--ui-state-error: #FFCCCC;
--ui-state-error-border: #993333;
--ui-state-error-text: #661111;
--ui-gray-light: #516873;
--ui-gray: #263340;
--ui-gray-dark: #16202B;
--ui-gray-darker: #151A1F;
--ui-gray-darkest: #14202A;
/* Text Colors */
--text-color-active: #000000;
--text-primary-color: #ffffff;
--text-secondary-color: #91b9cd;
--large-numbers-color: #6fbde2;
--text-disabled-color: #878787;
--input-background-color: #2c363f;
--input-placeholder-color: #d3d3d3;
--table-hover-color: #2c363f;
--table-text-primary-color: #ffffff;
--table-text-secondary-color: #91b9cd;
--large-numbers-color:#6FBDE2;
--state-error: #FFCCCC;
--state-error-border: #FFCCCC;
--state-error-text: #FFCCCC;
}

View File

@ -1,30 +0,0 @@
/* Sizes */
:root {
--top-bar-height: 40px;
--top-bar-expanded-height: 160px;
--toolbar-height: 78px;
--toolbar-drawer-height: 62px;
--left-sidebar-menu-width: 307px;
--right-sidebar-menu-width: 323px;
--study-list-padding: 8%;
--study-list-padding-medium-screen: 10px;
}
/* Fonts */
:root {
--logo-font-family: "Sanchez";
--logo-font-weight: 300;
}
/* Transitions */
:root {
--transition-duration: 0.3s;
--transition-effect: ease;
--sidebar-transition: all 0.3s ease;
}
/* Thicknesses */
:root {
--viewport-border-thickness: 1px;
--ui-border-thickness: 1px;
}

View File

@ -0,0 +1,14 @@
{
"presets": [
["@babel/preset-env", {
"targets": {
"ie": "11"
}
}],
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-runtime"
]
}

View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -0,0 +1,23 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules
# builds
build
dist
.rpt2_cache
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.idea

View File

@ -0,0 +1,3 @@
{
"singleQuote": true
}

View File

@ -0,0 +1,4 @@
language: node_js
node_js:
- 9
- 8

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Open Health Imaging Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,88 @@
{
"name": "ohif-dicom-microscopy-extension",
"version": "0.0.3",
"description": "OHIF extension for Dicom Microscopy",
"author": "OHIF",
"license": "MIT",
"repository": "OHIF/Viewers",
"main": "dist/index.js",
"module": "dist/index.es.js",
"jsnext:main": "dist/index.es.js",
"engines": {
"node": ">=8",
"npm": ">=5"
},
"scripts": {
"build": "rollup -c",
"start": "rollup -c -w",
"lint": "eslint -c .eslintrc --fix src && prettier --single-quote --write src/**/*.{js,jsx,json,css}"
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-external-helpers": "^7.2.0",
"@babel/plugin-proposal-class-properties": "^7.2.3",
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.2.3",
"@babel/preset-react": "^7.0.0",
"@svgr/rollup": "^4.1.0",
"babel-eslint": "^9.0.0",
"cross-env": "^5.2.0",
"eslint": "5.6.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^8.0.0",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-react": "^7.11.1",
"gh-pages": "^2.0.1",
"husky": "^1.3.1",
"lint-staged": "^8.1.0",
"lodash.clonedeep": "^4.5.0",
"prettier": "^1.15.3",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-scripts": "^2.1.2",
"rollup": "^0.68.2",
"rollup-plugin-babel": "^4.2.0",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-node-resolve": "^4.0.0",
"rollup-plugin-peer-deps-external": "^2.2.0",
"rollup-plugin-postcss": "^1.6.3",
"rollup-plugin-url": "^2.1.0",
"stylelint": "^9.9.0",
"stylelint-config-recommended": "^2.1.0",
"stylus": "^0.54.5"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,jsx,json,css}": [
"prettier --single-quote --write",
"git add"
]
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"files": [
"dist"
],
"dependencies": {
"@babel/runtime": "^7.2.0",
"classnames": "^2.2.6",
"dicom-microscopy-viewer": "^0.2.0",
"dicomweb-client": "^0.4.1",
"lodash.isequal": "^4.5.0",
"ohif-core": "^0.2.3",
"prop-types": "^15.6.2"
}
}

View File

@ -0,0 +1,57 @@
import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import external from 'rollup-plugin-peer-deps-external'
import postcss from 'rollup-plugin-postcss'
import resolve from 'rollup-plugin-node-resolve'
import url from 'rollup-plugin-url'
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';
export default {
input: 'src/index.js',
output: [
{
file: pkg.main,
format: 'umd',
name: 'ohif-dicom-pdf-extension',
sourcemap: true,
exports: 'named',
globals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
},
{
file: pkg.module,
format: 'es',
sourcemap: true
}
],
plugins: [
builtins(),
external(),
postcss({
modules: false
}),
url(),
svgr(),
babel({
exclude: 'node_modules/**',
plugins: [ '@babel/external-helpers' ],
externalHelpers: true,
runtimeHelpers: true
}),
resolve(),
commonjs({
include: 'node_modules/**',
namedExports: {
'node_modules/dicom-microscopy-viewer/build/dicom-microscopy-viewer.js': [
'api'
]
}
})
],
external: ['hammerjs']
}

View File

@ -1,5 +1,4 @@
import OHIF from 'ohif-core';
import { api } from 'dicomweb-client';
const { plugins, utils } = OHIF;
const { PLUGIN_TYPES } = plugins;
@ -8,20 +7,12 @@ const SOP_CLASS_UIDS = {
VL_WHOLE_SLIDE_MICROSCOPY_IMAGE_STORAGE: '1.2.840.10008.5.1.4.1.1.77.1.6'
};
const dwc = api.DICOMwebClient;
const DicomMicrscopySopClassHandlerPlugin = {
const DicomMicrscopySopClassHandler = {
id: 'DicomMicroscopySopClassHandlerPlugin',
type: PLUGIN_TYPES.SOP_CLASS_HANDLER,
sopClassUids: [SOP_CLASS_UIDS.VL_WHOLE_SLIDE_MICROSCOPY_IMAGE_STORAGE],
getDisplaySetFromSeries(series, study) {
getDisplaySetFromSeries(series, study, dicomWebClient) {
const instance = series.getFirstInstance();
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
const dicomWebClient = new dwc({
url: study.getData().wadoRoot,
headers
});
// Note: We are passing the dicomweb client into each viewport!
@ -36,4 +27,4 @@ const DicomMicrscopySopClassHandlerPlugin = {
}
};
export default DicomMicrscopySopClassHandlerPlugin;
export default DicomMicrscopySopClassHandler;

View File

@ -0,0 +1,27 @@
import DicomMicroscopyViewport from './DicomMicroscopyViewport.js';
import DicomMicroscopySopClassHandler from './DicomMicroscopySopClassHandler.js';
export default class OHIFDicomMicroscopyExtension {
/**
* Extension ID is a unique id, might be used for namespacing extension specific redux actions/reducers (?)
*/
getExtensionId() {
return 'microscopy';
}
getViewportModule() {
return DicomMicroscopyViewport;
}
getSopClassHandler() {
return DicomMicroscopySopClassHandler;
}
getPanelModule() {
return null;
}
getToolbarModule() {
return null;
}
}

View File

@ -0,0 +1,7 @@
import OHIFDicomMicroscopyExtension from './OHIFDicomMicroscopyExtension.js';
export {
OHIFDicomMicroscopyExtension
};
export default OHIFDicomMicroscopyExtension;

View File

@ -0,0 +1,14 @@
{
"presets": [
["@babel/preset-env", {
"targets": {
"ie": "11"
}
}],
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-runtime"
]
}

View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -0,0 +1,16 @@
{
"extends": [
"react-app",
"eslint:recommended",
"plugin:react/recommended"
],
"parser": "babel-eslint",
"env": {
"jest": true
},
"settings": {
"react": {
"version": "detect",
},
},
}

View File

@ -0,0 +1,23 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules
# builds
build
dist
.rpt2_cache
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.idea

View File

@ -0,0 +1,3 @@
{
"singleQuote": true
}

View File

@ -0,0 +1,4 @@
language: node_js
node_js:
- 9
- 8

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Open Health Imaging Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,86 @@
{
"name": "ohif-dicom-pdf-extension",
"version": "0.0.3",
"description": "OHIF extension for Dicom PDF",
"author": "OHIF",
"license": "MIT",
"repository": "OHIF/Viewers",
"main": "dist/index.js",
"module": "dist/index.es.js",
"jsnext:main": "dist/index.es.js",
"engines": {
"node": ">=8",
"npm": ">=5"
},
"scripts": {
"build": "rollup -c",
"start": "rollup -c -w",
"lint": "eslint -c .eslintrc --fix src && prettier --single-quote --write src/**/*.{js,jsx,json,css}"
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-external-helpers": "^7.2.0",
"@babel/plugin-proposal-class-properties": "^7.2.3",
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.2.3",
"@babel/preset-react": "^7.0.0",
"@svgr/rollup": "^4.1.0",
"babel-eslint": "^9.0.0",
"cross-env": "^5.2.0",
"eslint": "5.6.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^8.0.0",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-react": "^7.11.1",
"gh-pages": "^2.0.1",
"husky": "^1.3.1",
"lint-staged": "^8.1.0",
"lodash.clonedeep": "^4.5.0",
"prettier": "^1.15.3",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-scripts": "^2.1.2",
"rollup": "^0.68.2",
"rollup-plugin-babel": "^4.2.0",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-node-resolve": "^4.0.0",
"rollup-plugin-peer-deps-external": "^2.2.0",
"rollup-plugin-postcss": "^1.6.3",
"rollup-plugin-url": "^2.1.0",
"stylelint": "^9.9.0",
"stylelint-config-recommended": "^2.1.0",
"stylus": "^0.54.5"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,jsx,json,css}": [
"prettier --single-quote --write",
"git add"
]
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"files": [
"dist"
],
"dependencies": {
"@babel/runtime": "^7.2.0",
"classnames": "^2.2.6",
"lodash.isequal": "^4.5.0",
"ohif-core": "^0.2.3",
"prop-types": "^15.6.2"
}
}

View File

@ -0,0 +1,50 @@
import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import external from 'rollup-plugin-peer-deps-external'
import postcss from 'rollup-plugin-postcss'
import resolve from 'rollup-plugin-node-resolve'
import url from 'rollup-plugin-url'
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';
export default {
input: 'src/index.js',
output: [
{
file: pkg.main,
format: 'umd',
name: 'ohif-dicom-pdf-extension',
sourcemap: true,
exports: 'named',
globals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
},
{
file: pkg.module,
format: 'es',
sourcemap: true
}
],
plugins: [
builtins(),
external(),
postcss({
modules: false
}),
url(),
svgr(),
babel({
exclude: 'node_modules/**',
plugins: [ '@babel/external-helpers' ],
externalHelpers: true,
runtimeHelpers: true
}),
resolve(),
commonjs()
],
external: ['hammerjs']
}

View File

@ -0,0 +1,27 @@
import OHIFDicomPDFViewport from './OHIFDicomPDFViewport.js';
import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js';
export default class OHIFDicomPDFExtension {
/**
* Extension ID is a unique id, might be used for namespacing extension specific redux actions/reducers (?)
*/
getExtensionId() {
return 'pdf';
}
getViewportModule() {
return OHIFDicomPDFViewport;
}
getSopClassHandler() {
return OHIFDicomPDFSopClassHandler;
}
getPanelModuleDefinition() {
return null;
}
getToolbarModuleDefinition() {
return null;
}
}

View File

@ -8,13 +8,14 @@ const SOP_CLASS_UIDS = {
ENCAPSULATED_PDF: '1.2.840.10008.5.1.4.1.1.104.1'
};
const OHIFDicomPDFSopClassHandlerPlugin = {
const OHIFDicomPDFSopClassHandler = {
id: 'OHIFDicomPDFSopClassHandlerPlugin',
type: PLUGIN_TYPES.SOP_CLASS_HANDLER,
sopClassUids: [
SOP_CLASS_UIDS.ENCAPSULATED_PDF
],
getDisplaySetFromSeries(series, study) {
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
debugger;
const instance = series.getFirstInstance();
return {
@ -24,9 +25,10 @@ const OHIFDicomPDFSopClassHandlerPlugin = {
wadoUri: instance.getData().wadouri,
sopInstanceUid: instance.getSOPInstanceUID(),
seriesInstanceUid: series.getSeriesInstanceUID(),
studyInstanceUid: study.getStudyInstanceUID()
studyInstanceUid: study.getStudyInstanceUID(),
authorizationHeaders: authorizationHeaders
};
}
}
export default OHIFDicomPDFSopClassHandlerPlugin;
export default OHIFDicomPDFSopClassHandler;

View File

@ -1,12 +1,12 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import OHIF from 'ohif-core';
import OHIFComponentPlugin from '../OHIFComponentPlugin.js';
import OHIFComponentPlugin from './OHIFComponentPlugin.js';
import DicomPDFViewport from './DicomPDFViewport';
const { DICOMWeb } = OHIF;
class OHIFDicomPDFViewportPlugin extends Component {
class OHIFDicomPDFViewport extends Component {
static propTypes = {
studies: PropTypes.object,
displaySet: PropTypes.object,
@ -18,21 +18,21 @@ class OHIFDicomPDFViewportPlugin extends Component {
error: null
}
static id = 'DicomPDFViewportPlugin';
static id = 'DicomPDFViewportPDF';
static init() {
console.log('DicomPDFViewportPlugin init()');
console.log('DicomPDFViewport init()');
}
static destroy() {
console.log('DicomPDFViewportPlugin destroy()');
console.log('DicomPDFViewport destroy()');
}
componentDidMount() {
const { displaySet } = this.props.viewportData;
const { studyInstanceUid, seriesInstanceUid, sopInstanceUid, wadoRoot, wadoUri } = displaySet;
const { studyInstanceUid, seriesInstanceUid, sopInstanceUid, wadoRoot, wadoUri, authorizationHeaders} = displaySet;
this.retrieveDicomData(studyInstanceUid, seriesInstanceUid, sopInstanceUid, wadoRoot, wadoUri).then(byteArray => {
this.retrieveDicomData(studyInstanceUid, seriesInstanceUid, sopInstanceUid, wadoRoot, wadoUri, authorizationHeaders).then(byteArray => {
this.setState({
byteArray
});
@ -45,21 +45,20 @@ class OHIFDicomPDFViewportPlugin extends Component {
});
}
retrieveDicomData(studyInstanceUid, seriesInstanceUid, sopInstanceUid, wadoRoot, wadoUri) {
retrieveDicomData(studyInstanceUid, seriesInstanceUid, sopInstanceUid, wadoRoot, wadoUri, authorizationHeaders) {
// TODO: Passing in a lot of data we aren't using
// TODO: Authorization header depends on the server. If we ever have multiple servers
// we will need to figure out how / when to pass this information in.
return fetch(wadoUri, {
headers: DICOMWeb.getAuthorizationHeader()
headers: authorizationHeaders
}).then(response => response.arrayBuffer()).then(arraybuffer => {
debugger;
return new Uint8Array(arraybuffer);
});
}
render() {
const { id, init, destroy } = OHIFDicomPDFViewportPlugin;
const { id, init, destroy } = OHIFDicomPDFViewport;
const pluginProps = { id, init, destroy };
return (
@ -73,5 +72,4 @@ class OHIFDicomPDFViewportPlugin extends Component {
}
}
export default OHIFDicomPDFViewportPlugin;
export default OHIFDicomPDFViewport;

View File

@ -0,0 +1,7 @@
import OHIFDicomPDFExtension from './OHIFDicomPDFExtension.js';
export {
OHIFDicomPDFExtension
};
export default OHIFDicomPDFExtension;

Some files were not shown because too many files have changed in this diff Show More