Merge OHIFViewer-react into Packages-react\ohif-viewer (#342)

* Added public directory and debug run code in index.js

* Different entry points for development and publish

* Add port to start debug script

* Merge in progress

* Merge in progress

* Merge in progress

* Merged last components
This commit is contained in:
Sandra 2019-01-30 09:34:55 +00:00 committed by Erik Ziegler
parent 80df33f4fd
commit 84c949f2e6
52 changed files with 1350 additions and 230 deletions

View File

@ -13,5 +13,6 @@ body {
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;
font-family: Roboto, OpenSans, HelveticaNeue-Light, Helvetica Neue Light,
Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif;
}

View File

@ -1,4 +1,4 @@
import React, { Component } from "react";
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class ExampleViewportPlugin extends Component {
@ -14,7 +14,8 @@ class ExampleViewportPlugin extends Component {
};
render() {
return (<div className="ExampleViewportPlugin">
return (
<div className="ExampleViewportPlugin">
<p>StudyInstanceUid: {this.props.studyInstanceUid}</p>
<p>SeriesInstanceUid: {this.props.seriesInstanceUid}</p>
{JSON.stringify(this.props.displaySet)}

View File

@ -23,7 +23,7 @@ class ToolbarRow extends Component {
static defaultProps = {
leftSidebarOpen: false,
rightSidebarOpen: false,
rightSidebarOpen: false
};
onLeftSidebarValueChanged = (value) => {

View File

@ -4,7 +4,7 @@ import PropTypes from "prop-types";
import Header from '../components/Header'
import ConnectedFlexboxLayout from './ConnectedFlexboxLayout.js';
import ConnectedToolbarRow from "./ConnectedToolbarRow";
import ConnectedToolbarRow from './ConnectedToolbarRow';
import ConnectedStudyLoadingMonitor from './ConnectedStudyLoadingMonitor.js';
import StudyPrefetcher from '../components/StudyPrefetcher.js';
import './Viewer.css';
@ -46,9 +46,9 @@ OHIF.viewer.functionList = {
};*/
class Viewer extends Component {
static propTypes = {
studies: PropTypes.array,
};
static propTypes = {
studies: PropTypes.array
};
render() {
return (<>

View File

@ -105,7 +105,7 @@
/* Example config without OIDC */
// Try going to:
// http://localhost:5000/viewer/1.3.6.1.4.1.14519.5.2.1.1706.4996.216859690032335293073513900362
/*props.servers = {
/* props.servers = {
"dicomWeb": [
{
"name": "DCM4CHEE",
@ -121,7 +121,7 @@
}
}
]
};*/
}; */
/*

View File

@ -9,6 +9,7 @@
"browser": "dist/index.umd.js",
"module": "dist/index.es.js",
"jsnext:main": "dist/index.es.js",
"homepage": "http://localhost:3000",
"engines": {
"node": ">=8",
"npm": ">=5"
@ -22,7 +23,13 @@
"prepare": "npm run build",
"predeploy": "cd example && npm install && npm run build",
"deploy": "gh-pages -d example/build",
"generateStaticSite": "./generateStaticSite.sh"
"generateStaticSite": "./generateStaticSite.sh",
"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:debug": "npm run prep && cross-env PORT=5000 react-scripts start"
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0",

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

@ -0,0 +1,237 @@
<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>

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,30 @@
<!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

@ -0,0 +1,25 @@
<!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

@ -0,0 +1,8 @@
{
"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

@ -0,0 +1 @@
!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

@ -0,0 +1,16 @@
<!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

@ -12,7 +12,7 @@ import builtins from 'rollup-plugin-node-builtins';
export default {
external: ['react', 'react-dom'],
input: 'src/index.js',
input: 'src/index_publish.js',
output: [
{
file: pkg.main,

View File

@ -0,0 +1,18 @@
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

@ -10,6 +10,8 @@ import ui from './redux/ui.js';
import OHIFStandaloneViewer from './OHIFStandaloneViewer';
import OHIFCornerstoneViewportPlugin from './connectedComponents/OHIFCornerstoneViewportPlugin/OHIFCornerstoneViewportPlugin.js';
import WhiteLabellingContext from './WhiteLabellingContext';
import OHIFDicomPDFViewportPlugin from './connectedComponents/OHIFDicomPDFViewportPlugin/OHIFDicomPDFViewportPlugin.js';
import OHIFDicomPDFSopClassHandlerPlugin from './connectedComponents/OHIFDicomPDFViewportPlugin/OHIFDicomPDFSopClassHandlerPlugin.js';
import {
loadUser,
@ -101,13 +103,31 @@ const buttonsAction = OHIF.redux.actions.setAvailableButtons(defaultButtons);
store.dispatch(buttonsAction);
const pluginAction = OHIF.redux.actions.addPlugin({
const { plugins } = OHIF;
const { PLUGIN_TYPES } = plugins;
const cornerstonePluginAction = OHIF.redux.actions.addPlugin({
id: 'cornerstone',
type: 'viewport',
component: OHIFCornerstoneViewportPlugin
});
store.dispatch(pluginAction);
const pdfPluginAction = OHIF.redux.actions.addPlugin({
id: 'pdf',
type: PLUGIN_TYPES.VIEWPORT,
component: OHIFDicomPDFViewportPlugin
});
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;
@ -168,7 +188,7 @@ function handleWebWorkerInit(basename) {
class App extends Component {
static propTypes = {
servers: PropTypes.array,
servers: PropTypes.object,
oidc: PropTypes.array,
routerBasename: PropTypes.string
};

View File

@ -19,7 +19,7 @@ class CallbackPage extends Component {
this.props.history.push(pathname);
}}
errorCallback={error => {
this.props.history.push('/');
//this.props.history.push("/");
throw new Error(error);
}}
>

View File

@ -5,6 +5,7 @@ import { Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import { ViewerbaseDragDropContext } from 'react-viewerbase';
import ViewerRouting from './routes/ViewerRouting.js';
import StudyListRouting from './studylist/StudyListRouting.js'
import StandaloneRouting from './routes/StandaloneRouting.js';
import IHEInvokeImageDisplay from './routes/IHEInvokeImageDisplay.js';
import CallbackPage from './CallbackPage.js';
@ -67,7 +68,12 @@ class OHIFStandaloneViewer extends Component {
<Switch>
<Route exact path="/silent-refresh.html" onEnter={reload} />
<Route exact path="/logout-redirect.html" onEnter={reload} />
<Route exact path="/" component={StandaloneRouting} />
<Route
exact
path="/studylist"
component={StudyListRouting}
/>
<Route exact path="/" component={StudyListRouting} />
<Route exact path="/viewer" component={StandaloneRouting} />
<Route path="/viewer/:studyInstanceUids" component={ViewerRouting} />
<Route

View File

@ -1,4 +1,5 @@
import OHIFLogo from './components/OHIFLogo/OHIFLogo.js';
import React from 'react';
const defaultContextValues = {
logoComponent: OHIFLogo()

View File

@ -8,7 +8,8 @@ const mapStateToProps = (state, ownProps) => {
return {
studyInstanceUid,
seriesInstanceUid
seriesInstanceUid,
displaySet
};
};

View File

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

View File

@ -4,7 +4,7 @@
}
.entry-header.header-big {
background: rgba(21, 25, 30, 0.7);
background: rgba(21,25,30,0.7);
padding: 10px var(--study-list-padding);
height: auto;
display: inline-block;
@ -20,17 +20,42 @@
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;

View File

@ -19,21 +19,21 @@ function Header({ home, location, children }) {
return (
<div className={`entry-header ${home ? 'header-big' : ''}`}>
<div className="header-left-box">
{/*
{
state && state.studyLink &&
<Link to={state.studyLink} className="header-btn header-viewerLink">
Back to Viewer
</Link>
*/}
}
{children}
{/*!home &&
{!home &&
<Link className='header-btn header-studyListLinkSection' to={{
pathname: "/",
state: { studyLink: location.pathname }
}}>Study list</Link>
*/}
}
</div>
<div className="header-menu">

View File

@ -2,67 +2,72 @@ import dicomParser from 'dicom-parser';
import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import cornerstoneMath from 'cornerstone-math';
import cornerstoneTools from 'cornerstone-tools';
import Hammer from 'hammerjs';
import OHIF from 'ohif-core';
//import sha from './sha.js';
//import version from './version.js';
import sha from './sha.js';
import version from './version.js';
import { homepage } from '../package.json';
window.info = {
//sha,
//version,
sha,
version,
homepage
};
// For debugging
window.cornerstone = cornerstone;
window.cornerstoneTools = cornerstoneTools;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
cornerstoneTools.external.cornerstone = cornerstone;
cornerstoneTools.external.Hammer = Hammer;
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
cornerstoneTools.init();
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.external.cornerstone = cornerstone;
OHIF.external.cornerstoneTools = cornerstoneTools;
OHIF.external.cornerstoneMath = cornerstoneMath;
OHIF.external.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return;
}
// TODO: Is there a better way to guess ROOT_URL?
let ROOT_URL = homepage;
return state.oidc.user.access_token;
// 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.configure({
beforeSend: function(xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
*/
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization);
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);
}
}
}
});
// 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

@ -35,7 +35,13 @@ class FlexboxLayout extends Component {
const thumbnails = study.displaySets.map((displaySet) => {
const { displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames } = displaySet;
const imageId = displaySet.images[0].getImageId();
let imageId;
if (displaySet.images && displaySet.images.length) {
imageId = displaySet.images[0].getImageId();
}
return {
imageId,

View File

@ -9,7 +9,7 @@ OHIFComponentPlugin.propTypes = {
id: PropTypes.string.isRequired,
init: PropTypes.func.isRequired,
destroy: PropTypes.func.isRequired,
children: PropTypes.node.isRequired
children: PropTypes.node
};
export default OHIFComponentPlugin;

View File

@ -2,10 +2,23 @@ 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 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,

View File

@ -0,0 +1,31 @@
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

@ -0,0 +1,80 @@
import React, { Component } from "react";
import dicomParser from 'dicom-parser';
import TypedArrayProp from './TypedArrayProp';
// TODO: Should probably use dcmjs for this
const SOP_CLASS_UIDS = {
ENCAPSULATED_PDF: '1.2.840.10008.5.1.4.1.1.104.1'
};
class DicomPDFViewport extends Component {
state = {
fileURL: null,
error: null
};
static propTypes = {
byteArray: TypedArrayProp.uint8,
};
renderPDF = (dataSet) => {
debugger;
const sopClassUid = dataSet.string('x00080016');
if (sopClassUid !== SOP_CLASS_UIDS.ENCAPSULATED_PDF) {
throw new Error('This is not a DICOM-encapsulated PDF');
}
const fileTag = dataSet.elements.x00420011;
const offset = fileTag.dataOffset;
const remainder = offset + fileTag.length;
const pdfByteArray = dataSet.byteArray.slice(offset, remainder);
const PDF = new Blob([pdfByteArray], {type: 'application/pdf'});
const fileURL = URL.createObjectURL(PDF);
this.setState({
fileURL
});
}
parseByteArray = (byteArray) => {
const options = {
untilTag: ''
};
let dataSet;
try {
dataSet = dicomParser.parseDicom(byteArray, options);
} catch(error) {
this.setState({
error
});
}
return dataSet
}
componentDidMount() {
const dataSet = this.parseByteArray(this.props.byteArray);
this.renderPDF(dataSet);
}
render() {
return (
<div className={'DicomPDFViewport'} style={{width: '100%', height: '100%'}}>
{this.state.fileURL &&
<object data={this.state.fileURL} type="application/pdf" width="100%" height="100%">
</object>
}
{this.state.error &&
<h2>{JSON.stringify(this.state.error)}</h2>
}
</div>
);
}
}
export default DicomPDFViewport;

View File

@ -0,0 +1,32 @@
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

@ -0,0 +1,77 @@
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

@ -0,0 +1,137 @@
// https://github.com/facebook/prop-types/issues/69
export const TypedArrayProp = {
any: (props, propName, componentName) => {
let obj = props[propName];
if (!(obj instanceof Float64Array ||
obj instanceof Float32Array ||
obj instanceof Int32Array ||
obj instanceof Int16Array ||
obj instanceof Int8Array ||
obj instanceof Uint32Array ||
obj instanceof Uint16Array ||
obj instanceof Uint8Array ||
obj instanceof Uint8ClampedArray)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected a typed array.'
);
}
},
float64: (props, propName, componentName) => {
if (!(props[propName] instanceof Float64Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected a Float64Array.'
);
}
},
float32: (props, propName, componentName) => {
if (!(props[propName] instanceof Float32Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected a Float32Array.'
);
}
},
float: (props, propName, componentName) => {
if (!(props[propName] instanceof Float64Array ||
props[propName] instanceof Float32Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected a Float32Array or Float64Array.'
);
}
},
int32: (props, propName, componentName) => {
if (!(props[propName] instanceof Int32Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected an Int32Array.'
);
}
},
int16: (props, propName, componentName) => {
if (!(props[propName] instanceof Int16Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected an In16Array.'
);
}
},
int8: (props, propName, componentName) => {
if (!(props[propName] instanceof Int8Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected an Int8Array.'
);
}
},
int: (props, propName, componentName) => {
if (!(props[propName] instanceof Int32Array ||
props[propName] instanceof Int16Array ||
props[propName] instanceof Int8Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected an Int32Array, In16Array, or Int8Array.'
);
}
},
uint32: (props, propName, componentName) => {
if (!(props[propName] instanceof Uint32Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected a Uint32Array.'
);
}
},
uint16: (props, propName, componentName) => {
if (!(props[propName] instanceof Uint16Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected a Uint16Array.'
);
}
},
uint8: (props, propName, componentName) => {
if (!(props[propName] instanceof Uint8Array)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected a Uint8Array.'
);
}
},
uint8clamped: (props, propName, componentName) => {
if (!(props[propName] instanceof Uint8ClampedArray)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected a Uint8ClampedArray.'
);
}
},
uint: (props, propName, componentName) => {
if (!(props[propName] instanceof Uint32Array ||
props[propName] instanceof Uint16Array ||
props[propName] instanceof Uint8Array ||
props[propName] instanceof Uint8ClampedArray)) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Expected a Uint32Array, Uint16Array, Uint8Array, or Uint8ClampedArray.'
);
}
},
};
export { TypedArrayProp as default };

View File

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

View File

@ -3,13 +3,17 @@ import ConnectedToolbarSection from './ConnectedToolbarSection';
import ConnectedLayoutButton from './ConnectedLayoutButton';
import ConnectedCineDialog from './ConnectedCineDialog.js';
import PropTypes from 'prop-types';
import { RoundedButtonGroup } from 'react-viewerbase';
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,
@ -26,6 +30,12 @@ class ToolbarRow extends Component {
this.props.setLeftSidebarOpen(!!value);
}
onClickCineToolbarButton = () => {
this.setState({
cineDialogOpen: !this.state.cineDialogOpen
});
}
render() {
const leftSidebarToggle = [{
value: 'studies',
@ -37,15 +47,24 @@ class ToolbarRow extends Component {
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/>
{/* TODO: Putting cine dialog here for now */}
<div style={{ marginLeft: '100px', display: 'inline-block'}}>
<ConnectedCineDialog/>
<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 */}

View File

@ -12,7 +12,6 @@ import ConnectedToolbarRow from './ConnectedToolbarRow';
import ConnectedStudyLoadingMonitor from './ConnectedStudyLoadingMonitor.js';
import StudyPrefetcher from '../components/StudyPrefetcher.js';
import './Viewer.css';
const { StackManager } = OHIF.utils;
/**
* Inits OHIF Hanging Protocol's onReady.
@ -40,14 +39,6 @@ const { StackManager } = OHIF.utils;
});
};*/
OHIF.viewer.defaultTool = {
left: 'wwwc',
right: 'zoom',
middle: 'pan'
};
OHIF.viewer.refLinesEnabled = true;
/*const viewportUtils = OHIF.viewerbase.viewportUtils;
OHIF.viewer.functionList = {
@ -58,20 +49,6 @@ OHIF.viewer.functionList = {
invert: viewportUtils.invert
};*/
// 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 Viewer extends Component {
static propTypes = {
studies: PropTypes.array
@ -85,7 +62,6 @@ class Viewer extends Component {
<Header home={false}>{whiteLabelling.logoComponent}</Header>
)}
</WhiteLabellingContext.Consumer>
<div className="viewerDialogs">{/*<CineDialog/>*/}</div>
<div id="viewer" className="Viewer">
<ConnectedToolbarRow />
<ConnectedStudyLoadingMonitor studies={this.props.studies} />

View File

@ -1,16 +1,10 @@
import { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import cornerstoneMath from "cornerstone-math";
import { OHIF } from 'ohif-core';
import ConnectedLayoutManager from './ConnectedLayoutManager.js';
import './ViewerMain.css';
// Attempt to fix weird undefined dep issue
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
class ViewerMain extends Component {
state = {
viewportData: [],
@ -20,13 +14,11 @@ class ViewerMain extends Component {
studies: PropTypes.array.isRequired
};
getDisplaySetsWithImages(studies) {
getDisplaySets(studies) {
const displaySets = [];
studies.forEach((study) => {
study.displaySets.forEach(dSet => {
if (dSet.images.length) {
displaySets.push(dSet);
}
displaySets.push(dSet);
});
});
@ -52,7 +44,7 @@ class ViewerMain extends Component {
//window.addEventListener('beforeunload', unloadHandlers.beforeUnload);
// Get all the display sets for the viewer studies
const displaySets = this.getDisplaySetsWithImages(this.props.studies);
const displaySets = this.getDisplaySets(this.props.studies);
this.setState({
viewportData: displaySets
@ -65,14 +57,10 @@ class ViewerMain extends Component {
// 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.
debugger;
console.warn(item);
const updatedViewportData = this.state.viewportData.slice(0);
const displaySet = this.findDisplaySet(this.props.studies, item.studyInstanceUid, item.displaySetInstanceUid);
console.warn(displaySet);
updatedViewportData[viewportIndex] = Object.assign({}, displaySet);
this.setState({

View File

@ -0,0 +1,15 @@
<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,6 +1,126 @@
import App from './App.js';
import routes from './routes/';
/**
* Entry point index.js for development
*/
export {
App
import App from './App.js';
import React from 'react';
import ReactDOM from 'react-dom';
export { App };
var routerBasename = 'http://localhost:5000';
var props = {
routerBasename: routerBasename
};
/*
Example config with OIDC
*/
// Uncomment the next two blocks, comment out the config without OIDC
// Try going to:
// http://localhost:5000/viewer/1.3.6.1.4.1.25403.345050719074.3824.20170126082328.1
// http://ohif-viewer-react.s3-website-us-east-1.amazonaws.com/viewer/1.3.6.1.4.1.25403.345050719074.3824.20170126082328.1
props.servers = {
"dicomWeb": [{
"name": "DCM4CHEE",
"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
}
}
]
};
props.oidc = [{
"authServerUrl": "https://cancer.crowds-cure.org/auth/realms/dcm4che",
"authRedirectUri": routerBasename + '/callback',
"postLogoutRedirectUri": routerBasename + '/logout-redirect.html',
"clientId": "crowds-cure-cancer",
"responseType": "id_token token",
"scope": "email profile openid",
"revokeAccessTokenOnSignout": true,
"extraQueryParams": {
"kc_idp_hint": "crowds-cure-cancer-auth0-oidc",
"client_id": "crowds-cure-cancer"
}
}];
/* Example config without OIDC */
// Try going to:
// http://localhost:5000/viewer/1.3.6.1.4.1.14519.5.2.1.1706.4996.216859690032335293073513900362
/* props.servers = {
dicomWeb: [
{
name: 'DCM4CHEE',
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',
qidoSupportsIncludeField: true,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
requestOptions: {
requestFromBrowser: true,
auth: 'cloud:healthcare'
}
}
]
}; */
/*
UI settings
Plugins
- Custom tools / buttons
- Custom Sidebar module thing
- Custom Viewports
- Custom Sop Class Interpreters
*/
/*"PUBLIC_SETTINGS": {
"ui": {
"studyListFunctionsEnabled": true,
"leftSidebarOpen": false,
"displaySetNavigationLoopOverSeries": false,
"displaySetNavigationMultipleViewports": true,
"autoPositionMeasurementsTextCallOuts": "TRLB"
},
"prefetch": {
"order": "topdown",
"displaySetCount": 1
}
*/
/*
White labelling example
// Note that you can't write JSX here, so you can use the online JSX compiler:
// https://babeljs.io/repl/#?babili=false&browsers=&build=&builtIns=false&spec=false&loose=false&code_lz=GYVwdgxgLglg9mABACwKYBt1wBQEpEDeAUIogE6pQhlIA8AholPWQOaUC8ARAPoBG6emADWXchm5g4cAA6owqMoikVgiimTERBAZx0A5egFtU3NPQAmigLR8yQi2OSqzUKDIBcAei_2LMCHp0GCN6VhgwVgA6CDgjLgA-ElIU1LS02hDWRB0yCA4AcmQ3Tx8_AKCQsIjo2KMvAHcZa1iwKHkoLxAZLEsdLwAmAAYARgB2LxGRrwgyWTkLFrmZBetwMGNURYGomUiCxC8k9JOU2i96BIBuIgBfIiA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=react&prettier=false&targets=&version=6.26.0&envVersion=
*/
function RadicalImagingLogo() {
return React.createElement(
'a',
{
target: '_blank',
rel: 'noopener noreferrer',
className: 'header-brand',
href: 'http://radicalimaging.com'
},
React.createElement('h5', {}, 'RADICAL IMAGING')
);
}
props.whiteLabelling = {
logoComponent: RadicalImagingLogo()
};
// TODO: Why do we need to do this? Something is wrong with our rollup settings
//var Viewer = window.OHIFStandaloneViewer.App;
var app = React.createElement(App, props, null);
ReactDOM.render(app, document.getElementById('root'));

View File

@ -0,0 +1,6 @@
/**
* Entry point index.js for rollup packaging
*/
import App from './App.js';
export { App };

View File

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

View File

@ -1,126 +1,104 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import qs from 'query-string';
import Viewer from '../connectedComponents/Viewer';
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;
function 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();
});
}
class StandaloneRouting extends Component {
state = {
studies: null,
error: null
};
static propTypes = {
location: PropTypes.object,
store: PropTypes.object
};
componentDidMount() {
const search = this.props.location.search;
if (!search) {
console.warn('no search string!');
this.setState({
error: new Error('No query string provided.')
});
return;
state = {
studies: null,
error: null
}
const query = qs.parse(search);
static propTypes = {
location: PropTypes.object,
store: PropTypes.object
};
parseQueryAndFetchStudies(query).then(
studies => {
const updatedStudies = createDisplaySets(studies);
static parseQueryAndFetchStudies(query) {
return new Promise((resolve, reject) => {
const url = query.url;
this.setState({ studies: updatedStudies });
},
error => {
this.setState({ error });
}
);
}
if (!url) {
reject(new Error('No URL was specified. Use ?url=$yourURL'));
}
render() {
if (this.state.error) {
return <div>Error: {JSON.stringify(this.state.error)}</div>;
} else if (!this.state.studies) {
return <div>Loading...</div>;
// 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();
});
}
return <Viewer studies={this.state.studies} />;
}
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

@ -0,0 +1 @@
export default '1242155351531';

View File

@ -0,0 +1,21 @@
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

@ -0,0 +1,24 @@
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

@ -0,0 +1,113 @@
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 Header from "../components/Header";
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 (<>
<Header 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

@ -0,0 +1 @@
export default '0.0.0';