Lesion Tracker Redesign so far
This commit is contained in:
parent
b247e89d8b
commit
a9a4f3f82b
@ -33,6 +33,7 @@ fortawesome:fontawesome
|
||||
random
|
||||
reactive-var
|
||||
reactive-dict
|
||||
design
|
||||
lesiontracker
|
||||
practicalmeteor:loglevel
|
||||
momentjs:moment
|
||||
|
||||
@ -38,6 +38,7 @@ ddp-common@1.2.6
|
||||
ddp-rate-limiter@1.0.5
|
||||
ddp-server@1.2.9
|
||||
deps@1.0.12
|
||||
design@0.0.1
|
||||
dicomweb@0.0.1
|
||||
diff-sequence@1.0.6
|
||||
dimseservice@0.0.1
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
body
|
||||
background-color: black
|
||||
|
||||
.tab-content
|
||||
height: calc(100% - 40px)
|
||||
|
||||
/* Mobile devices */
|
||||
@media only screen and (min-device-width : 375px) and (max-device-width : 768px)
|
||||
.logoContainer
|
||||
|
||||
@ -34,7 +34,6 @@ Meteor.startup(function() {
|
||||
touch: cornerstoneTools.exToolTouch
|
||||
});
|
||||
|
||||
|
||||
var states = toolManager.getToolDefaultStates();
|
||||
states.deactivate.push('bidirectional');
|
||||
states.deactivate.push('nonTarget');
|
||||
@ -44,7 +43,5 @@ Meteor.startup(function() {
|
||||
states.deactivate.push('exTool');
|
||||
|
||||
states.activate.push('deleteLesionKeyboardTool');
|
||||
|
||||
states.enable.push('scaleOverlayTool');
|
||||
toolManager.setToolDefaultStates(states);
|
||||
});
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
#viewer
|
||||
width: 100%
|
||||
height: 100%
|
||||
margin: 0
|
||||
padding: 0
|
||||
background-color: black
|
||||
|
||||
#viewportAndLesionTable
|
||||
height: 100%
|
||||
width: 99%
|
||||
background-color: black
|
||||
float: right
|
||||
-o-transition: all 0.3s ease-out;
|
||||
-ms-transition: all 0.3s ease-out;
|
||||
-moz-transition: all 0.3s ease-out;
|
||||
-webkit-transition: all 0.3s ease-out;
|
||||
transition: all 0.3s ease-out;
|
||||
|
||||
#imageViewerViewports
|
||||
.viewportContainer
|
||||
&.active
|
||||
border: 2px solid #777
|
||||
|
||||
&:hover, &:hover.active
|
||||
border: 2px solid white
|
||||
|
||||
#btnCollapse
|
||||
background-color: #333
|
||||
border-color: black
|
||||
|
||||
&.active
|
||||
border-color: #333
|
||||
|
||||
.btnCollapseIcon
|
||||
color: white
|
||||
@ -1,127 +0,0 @@
|
||||
Template.viewerMain.helpers({
|
||||
toolbarOptions: function() {
|
||||
var toolbarOptions = {};
|
||||
|
||||
var buttonData = [];
|
||||
|
||||
var btnGroup = [];
|
||||
|
||||
buttonData.push({
|
||||
id: 'resetViewport',
|
||||
title: 'Reset Viewport',
|
||||
classes: 'imageViewerCommand',
|
||||
iconClasses: 'fa fa-undo'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'wwwc',
|
||||
title: 'WW/WC',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-sun-o'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'zoom',
|
||||
title: 'Zoom',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-search'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'pan',
|
||||
title: 'Pan',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-arrows'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'stackScroll',
|
||||
title: 'Stack Scroll',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-bars'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'length',
|
||||
title: 'Length Measurement',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-arrows-v'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'ellipticalRoi',
|
||||
title: 'Elliptical ROI Measurement',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-circle-o'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'bidirectional',
|
||||
title: 'Target Tool',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-arrows-alt'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'nonTarget',
|
||||
title: 'Non-Target Tool',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-long-arrow-up'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'clearTools',
|
||||
title: 'Clear tools',
|
||||
classes: 'imageViewerCommand',
|
||||
iconClasses: 'fa fa-trash'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'toggleLesionTrackerTools',
|
||||
title: 'Show / hide tools',
|
||||
classes: 'imageViewerCommand',
|
||||
iconClasses: 'fa fa-eye'
|
||||
});
|
||||
|
||||
// CR/UN/EX Tools
|
||||
var crunexToolsBtns = {
|
||||
id: 'crunexTools',
|
||||
tools: [{
|
||||
id: 'crTool',
|
||||
title: 'CR Tool',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-cr'
|
||||
}, {
|
||||
id: 'unTool',
|
||||
title: 'UN Tool',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-un'
|
||||
}, {
|
||||
id: 'exTool',
|
||||
title: 'EX Tool',
|
||||
classes: 'imageViewerTool',
|
||||
iconClasses: 'fa fa-ex'
|
||||
}],
|
||||
title: 'CR/UN/EX',
|
||||
groupIcon: 'fa fa-exchange'
|
||||
};
|
||||
|
||||
btnGroup.push(crunexToolsBtns);
|
||||
|
||||
toolbarOptions.buttonData = buttonData;
|
||||
toolbarOptions.includePlayClipButton = false;
|
||||
toolbarOptions.includeLayoutButton = false;
|
||||
toolbarOptions.includeHangingProtocolButtons = false;
|
||||
toolbarOptions.btnGroup = btnGroup;
|
||||
return toolbarOptions;
|
||||
}
|
||||
});
|
||||
|
||||
Template.viewerMain.onRendered(function() {
|
||||
var parentNode = document.getElementById('layoutManagerTarget');
|
||||
var studies = this.data.studies;
|
||||
layoutManager = new LayoutManager(parentNode, studies);
|
||||
|
||||
ProtocolEngine = new HP.ProtocolEngine(layoutManager, studies);
|
||||
HP.setEngine(ProtocolEngine);
|
||||
});
|
||||
@ -13,5 +13,4 @@
|
||||
<meta name="MobileOptimized" content="320">
|
||||
<meta name="HandheldFriendly" content="True">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<link href='http://fonts.googleapis.com/css?family=Sanchez:400,700&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
@ -36,11 +36,11 @@ var routerOptions = {
|
||||
|
||||
Router.route('/', function() {
|
||||
// Check user is logged in
|
||||
if(Meteor.user() && Meteor.userId()) {
|
||||
if (Meteor.user() && Meteor.userId()) {
|
||||
if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) {
|
||||
this.render('emailVerification', routerOptions);
|
||||
} else {
|
||||
this.render('worklist', routerOptions);
|
||||
this.render('lesionTracker', routerOptions);
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -50,11 +50,11 @@ Router.route('/', function() {
|
||||
|
||||
Router.route('/worklist', function() {
|
||||
// Check user is logged in
|
||||
if(Meteor.user() && Meteor.userId()) {
|
||||
if (Meteor.user() && Meteor.userId()) {
|
||||
if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) {
|
||||
this.render('emailVerification', routerOptions);
|
||||
} else {
|
||||
this.render('worklist', routerOptions);
|
||||
this.render('lesionTracker', routerOptions);
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -77,15 +77,17 @@ Router.route('/viewer/timepoints/:_id', {
|
||||
return;
|
||||
}
|
||||
|
||||
this.render('worklist', routerOptions);
|
||||
this.render('lesionTracker', routerOptions);
|
||||
openNewTabWithTimepoint(timepointId);
|
||||
}
|
||||
});
|
||||
|
||||
Router.onBeforeAction(function () {
|
||||
if (!Meteor.userId() && !Meteor.loggingIn()) {
|
||||
Router.onBeforeAction(function() {
|
||||
if (!Meteor.userId() && !Meteor.loggingIn()) {
|
||||
this.render('entrySignIn');
|
||||
} else {
|
||||
this.next();
|
||||
}
|
||||
},{except: ['entrySignIn', 'entrySignUp', 'forgotPassword', 'resetPassword']});
|
||||
}, {
|
||||
except: ['entrySignIn', 'entrySignUp', 'forgotPassword', 'resetPassword']
|
||||
});
|
||||
|
||||
@ -26,6 +26,7 @@ spacebars
|
||||
check
|
||||
cornerstone
|
||||
dicomweb
|
||||
design
|
||||
viewerbase
|
||||
worklist
|
||||
reactive-var
|
||||
|
||||
@ -24,6 +24,7 @@ ddp-client@1.2.9
|
||||
ddp-common@1.2.6
|
||||
ddp-server@1.2.9
|
||||
deps@1.0.12
|
||||
design@0.0.1
|
||||
dicomweb@0.0.1
|
||||
diff-sequence@1.0.6
|
||||
dimseservice@0.0.1
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>OHIF Viewer</title>
|
||||
<meta name="description" content="Open Health Imaging Foundation DICOM Viewer">
|
||||
<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">
|
||||
<!--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">
|
||||
<link href='http://fonts.googleapis.com/css?family=Sanchez:400,700&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
6
Packages/design/app.styl
Normal file
6
Packages/design/app.styl
Normal file
@ -0,0 +1,6 @@
|
||||
@import "{design}/styles/webfonts"
|
||||
@import "{design}/styles/variables"
|
||||
@import "{design}/styles/mixins"
|
||||
@import "{design}/styles/spacings"
|
||||
|
||||
@import "{design}/styles/global"
|
||||
@ -0,0 +1,7 @@
|
||||
<template name="roundedButtonGroup">
|
||||
<div class="{{class}} viewerRoundedButtonGroup clearfix center-table">
|
||||
{{#each item in this.options}}
|
||||
<a class="viewerRoundedButton noselect {{inlineIf value.get item.key 'active'}}" data-key="{{item.key}}">{{item.text}}</a>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,15 @@
|
||||
Template.roundedButtonGroup.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
const value = instance.data.value;
|
||||
if (!value.get()) {
|
||||
value.set(instance.data.options[0].key);
|
||||
}
|
||||
});
|
||||
|
||||
Template.roundedButtonGroup.events({
|
||||
'click [data-key]'(event, instance) {
|
||||
const $element = $(event.currentTarget);
|
||||
const key = $element.attr('data-key');
|
||||
instance.data.value.set(key);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,46 @@
|
||||
@import "{design}/app"
|
||||
|
||||
$height = 25px
|
||||
$borderColorActive = #3C8074
|
||||
$textColorActive = #2D2D2D
|
||||
|
||||
.viewerRoundedButtonGroup
|
||||
font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif
|
||||
font-weight: 500
|
||||
position: relative
|
||||
|
||||
.viewerRoundedButton
|
||||
background: $darkUiColor
|
||||
border: 1px $uiBorderColorDark solid
|
||||
color: $textSecondaryColor
|
||||
cursor: pointer
|
||||
display: inline-block
|
||||
margin-left: -1px
|
||||
float: left
|
||||
font-size: 11px
|
||||
height: $height
|
||||
line-height: $height
|
||||
padding: 0 15px
|
||||
text-decoration: none
|
||||
text-transform: uppercase
|
||||
transition: $sidebarTransition
|
||||
-webkit-transition: $sidebarTransition
|
||||
-ms-transition: $sidebarTransition
|
||||
-o-transition: $sidebarTransition
|
||||
-moz-transition: $sidebarTransition
|
||||
z-index: 1
|
||||
|
||||
&.active
|
||||
background: $activeColor
|
||||
color: $textColorActive
|
||||
border-color: $borderColorActive
|
||||
z-index: 2
|
||||
|
||||
&:first-child
|
||||
margin-left: 0
|
||||
border-bottom-left-radius: $height
|
||||
border-top-left-radius: $height
|
||||
|
||||
&:last-child
|
||||
border-bottom-right-radius: $height
|
||||
border-top-right-radius: $height
|
||||
34
Packages/design/package.js
Normal file
34
Packages/design/package.js
Normal file
@ -0,0 +1,34 @@
|
||||
Package.describe({
|
||||
name: 'design',
|
||||
summary: 'OHIF Design styles and components',
|
||||
version: '0.0.1'
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
api.versionsFrom('1.2.1');
|
||||
|
||||
api.use('ecmascript');
|
||||
api.use('standard-app-packages');
|
||||
api.use('jquery');
|
||||
api.use('stylus');
|
||||
|
||||
// Importable colors / typography settings
|
||||
api.addFiles([
|
||||
'app.styl',
|
||||
'styles/global.styl',
|
||||
'styles/mixins.styl',
|
||||
'styles/spacings.styl',
|
||||
'styles/variables.styl',
|
||||
'styles/webfonts.styl'
|
||||
], 'client', {
|
||||
isImport: true
|
||||
});
|
||||
|
||||
// Rounded Button Group
|
||||
api.addFiles([
|
||||
'components/roundedButtonGroup/roundedButtonGroup.html',
|
||||
'components/roundedButtonGroup/roundedButtonGroup.styl',
|
||||
'components/roundedButtonGroup/roundedButtonGroup.js'
|
||||
], 'client');
|
||||
|
||||
});
|
||||
10
Packages/design/styles/global.styl
Normal file
10
Packages/design/styles/global.styl
Normal file
@ -0,0 +1,10 @@
|
||||
html *
|
||||
font-family: 'Roboto', sans-serif;
|
||||
|
||||
hr
|
||||
border-top: 1px solid $uiBorderColor;
|
||||
|
||||
.center-table
|
||||
display: table
|
||||
margin-left: auto
|
||||
margin-right: auto
|
||||
13
Packages/design/styles/mixins.styl
Normal file
13
Packages/design/styles/mixins.styl
Normal file
@ -0,0 +1,13 @@
|
||||
transition(t)
|
||||
transition: t
|
||||
-webkit-transition: t
|
||||
-ms-transition: t
|
||||
-o-transition: t
|
||||
-moz-transition: t
|
||||
|
||||
transform(t)
|
||||
transform: t
|
||||
-webkit-transform: t
|
||||
-ms-transform: t
|
||||
-o-transform: t
|
||||
-moz-transform: t
|
||||
39
Packages/design/styles/spacings.styl
Normal file
39
Packages/design/styles/spacings.styl
Normal file
@ -0,0 +1,39 @@
|
||||
$spacer = 1rem
|
||||
|
||||
$spacer-x = $spacer
|
||||
$spacer-y = $spacer
|
||||
|
||||
$spacerAxis = $spacer-x,
|
||||
$spacer-y
|
||||
|
||||
$spacings = 0 0,
|
||||
1 1,
|
||||
2 1.5,
|
||||
3 3g
|
||||
|
||||
$properties = m margin,
|
||||
p padding
|
||||
|
||||
$sides = t top 1,
|
||||
r right 0,
|
||||
b bottom 1,
|
||||
l left 0
|
||||
|
||||
$axes = x left right 0,
|
||||
y top bottom 1
|
||||
|
||||
for $property in $properties
|
||||
for $spacing in $spacings
|
||||
.{$property[0]}-a-{$spacing[0]}
|
||||
{$property[1]} $spacing[1]*$spacer !important
|
||||
for $side in $sides
|
||||
.{$property[0]}-{$side[0]}-{$spacing[0]}
|
||||
{$property[1]}-{$side[1]} $spacing[1]*$spacerAxis[$side[2]] !important
|
||||
for $axis in $axes
|
||||
.{$property[0]}-{$axis[0]}-{$spacing[0]}
|
||||
{$property[1]}-{$axis[1]} $spacing[1]*$spacerAxis[$axis[3]] !important
|
||||
{$property[1]}-{$axis[2]} $spacing[1]*$spacerAxis[$axis[3]] !important
|
||||
|
||||
.m-x-auto
|
||||
margin-left: auto !important
|
||||
margin-right: auto !important
|
||||
34
Packages/design/styles/variables.styl
Normal file
34
Packages/design/styles/variables.styl
Normal file
@ -0,0 +1,34 @@
|
||||
// Interface UI Colors
|
||||
$defaultColor = #94A8B3
|
||||
$hoverColor = #ffffff
|
||||
$activeColor = #40D9B8
|
||||
$uiBorderColor = #436270
|
||||
$uiBorderColorDark = #3C5D80
|
||||
$darkUiColor = #16202B
|
||||
$primaryBackgroundColor = #000000
|
||||
|
||||
// Text Colors
|
||||
$textPrimaryColor = #ffffff
|
||||
$textSecondaryColor = #91B9CD
|
||||
|
||||
// Other Colors
|
||||
$errorColor = #E29E4A
|
||||
$menuBackgroundColor = #F2F2F2
|
||||
|
||||
// Sizes
|
||||
$topBarHeight = 30px
|
||||
$toolbarHeight = 50px
|
||||
|
||||
// Thicknesses
|
||||
$uiBorderThickness = 1px
|
||||
|
||||
// Fonts
|
||||
$logoFontFamily = "Sanchez"
|
||||
$logoFontWeight = 300
|
||||
|
||||
// Transitions
|
||||
$transitionDuration = 0.3s
|
||||
$transitionEffect = ease
|
||||
$sidebarTransition = all $transitionDuration $transitionEffect
|
||||
$studiesSidebarMenuWidth = 307px
|
||||
$lesionsSidebarMenuWidth = 317px
|
||||
2
Packages/design/styles/webfonts.styl
Normal file
2
Packages/design/styles/webfonts.styl
Normal file
@ -0,0 +1,2 @@
|
||||
@import url(http://fonts.googleapis.com/css?family=Roboto:400,100,100italic,300,300italic,400italic,500,500italic,700,700italic,900,900italic&subset=latin,latin-ext)
|
||||
@import url(http://fonts.googleapis.com/css?family=Sanchez:400,700&subset=latin,latin-ext)
|
||||
@ -24,6 +24,10 @@ Array.prototype.move = function(oldIndex, newIndex) {
|
||||
*/
|
||||
function getStageIndex(protocol, id) {
|
||||
var stageIndex;
|
||||
if (!protocol || !protocol.stages) {
|
||||
return;
|
||||
}
|
||||
|
||||
protocol.stages.forEach(function(stage, index) {
|
||||
if (stage.id === id) {
|
||||
stageIndex = index;
|
||||
|
||||
186
Packages/lesiontracker/assets/icons.svg
Normal file
186
Packages/lesiontracker/assets/icons.svg
Normal file
@ -0,0 +1,186 @@
|
||||
<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>
|
||||
<path d="M1 2L1 2 1 16C1 16.55 1.44 17 1.99 17L14.01 17C14.55 17 15 16.55 15 16L15 2C15 1.45 14.56 1 14.01 1L1.99 1C1.45 1 1 1.45 1 2L1 2ZM0 2C0 0.89 0.89 0 1.99 0L14.01 0C15.11 0 16 0.89 16 2L16 16C16 17.11 15.11 18 14.01 18L1.99 18C0.89 18 0 17.11 0 16L0 2 0 2Z"/>
|
||||
<rect x="0" y="0" width="16" height="4" rx="2"/>
|
||||
<path d="M17.69 3.66C17.91 3.78 18.11 3.95 18.25 4.16 18.4 4.39 18.71 4.45 18.94 4.3 19.17 4.14 19.23 3.83 19.08 3.6 18.84 3.25 18.52 2.97 18.14 2.77 17.9 2.65 17.6 2.74 17.47 2.99 17.34 3.24 17.44 3.54 17.69 3.66L17.69 3.66ZM18.5 6.88L18.5 7.88C18.5 8.15 18.72 8.38 19 8.38 19.28 8.38 19.5 8.15 19.5 7.88L19.5 6.88C19.5 6.6 19.28 6.38 19 6.38 18.72 6.38 18.5 6.6 18.5 6.88L18.5 6.88ZM18.5 10.88L18.5 11.88C18.5 12.15 18.72 12.38 19 12.38 19.28 12.38 19.5 12.15 19.5 11.88L19.5 10.88C19.5 10.6 19.28 10.38 19 10.38 18.72 10.38 18.5 10.6 18.5 10.88L18.5 10.88ZM18.5 14.88L18.5 15.88C18.5 16.15 18.72 16.38 19 16.38 19.28 16.38 19.5 16.15 19.5 15.88L19.5 14.88C19.5 14.6 19.28 14.38 19 14.38 18.72 14.38 18.5 14.6 18.5 14.88L18.5 14.88ZM18.5 18.88L18.5 19C18.5 19.23 18.45 19.44 18.36 19.64 18.24 19.89 18.35 20.19 18.6 20.31 18.85 20.43 19.14 20.32 19.26 20.07 19.42 19.74 19.5 19.38 19.5 19L19.5 18.88C19.5 18.6 19.28 18.38 19 18.38 18.72 18.38 18.5 18.6 18.5 18.88L18.5 18.88ZM16.19 20.5L15.19 20.5C14.91 20.5 14.69 20.72 14.69 21 14.69 21.28 14.91 21.5 15.19 21.5L16.19 21.5C16.46 21.5 16.69 21.28 16.69 21 16.69 20.72 16.46 20.5 16.19 20.5L16.19 20.5ZM12.19 20.5L11.19 20.5C10.91 20.5 10.69 20.72 10.69 21 10.69 21.28 10.91 21.5 11.19 21.5L12.19 21.5C12.46 21.5 12.69 21.28 12.69 21 12.69 20.72 12.46 20.5 12.19 20.5L12.19 20.5ZM8.19 20.5L7.19 20.5C6.91 20.5 6.69 20.72 6.69 21 6.69 21.28 6.91 21.5 7.19 21.5L8.19 21.5C8.46 21.5 8.69 21.28 8.69 21 8.69 20.72 8.46 20.5 8.19 20.5L8.19 20.5ZM4.4 20.38C4.17 20.28 3.97 20.12 3.81 19.92 3.64 19.7 3.33 19.66 3.11 19.83 2.89 20 2.85 20.31 3.02 20.53 3.28 20.86 3.62 21.13 4 21.3 4.26 21.41 4.55 21.29 4.66 21.04 4.77 20.78 4.66 20.49 4.4 20.38L4.4 20.38Z"/>
|
||||
<g id="lines" transform="translate(4.000000, 7.000000)" stroke-linecap="square">
|
||||
<path d="M0.4 0.5L7.6 0.5" id="Line"/>
|
||||
<path d="M0.4 3.5L7.6 3.5" id="Line"/>
|
||||
<path d="M0.4 6.5L7.6 6.5" id="Line"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-measurements-additional" viewBox="0 0 14 13">
|
||||
<title>
|
||||
Additional Measurements
|
||||
</title>
|
||||
<path d="M0 10.26L0 13 2.66 13 10.36 4.98 7.7 2.24 0 10.26 0 10.26ZM12.39 2.89C12.67 2.6 12.67 2.17 12.39 1.88L10.78 0.22C10.5-0.07 10.08-0.07 9.8 0.22L8.54 1.52 11.2 4.26 12.39 2.89 12.39 2.89ZM6.3 11.56L4.9 13 14 13 14 11.56 6.3 11.56 6.3 11.56Z" id="icon-additional"/>
|
||||
</symbol>
|
||||
<symbol id="icon-measurements-lesions" viewBox="0 0 18 10">
|
||||
<title>
|
||||
Lesions
|
||||
</title>
|
||||
<path d="M0 6L2 6 2 4 0 4 0 6 0 6ZM0 10L2 10 2 8 0 8 0 10 0 10ZM0 2L2 2 2 0 0 0 0 2 0 2ZM4 6L18 6 18 4 4 4 4 6 4 6ZM4 10L18 10 18 8 4 8 4 10 4 10ZM4 0L4 2 18 2 18 0 4 0 4 0Z" id="icon-lesions"/>
|
||||
</symbol>
|
||||
<symbol id="icon-series-image-count" viewBox="0 0 13 13">
|
||||
<title>
|
||||
Image Count
|
||||
</title>
|
||||
<path d="M10.12 3.94L13 3.94 13 13 3.94 13 3.94 10.12 10.12 10.12 10.12 3.94Z" id="Combined-Shape"/>
|
||||
<rect id="Rectangle-36" x="0" y="0" width="9.06" height="9.06"/>
|
||||
</symbol>
|
||||
<symbol id="icon-series-number" viewBox="0 0 9 9">
|
||||
<title>
|
||||
Series Number
|
||||
</title>
|
||||
<path d="M4.2 6.11C4.2 5.86 4.12 5.66 3.94 5.52 3.76 5.38 3.45 5.24 3 5.09 2.56 4.94 2.19 4.8 1.91 4.66 0.99 4.2 0.53 3.58 0.53 2.79 0.53 2.4 0.64 2.05 0.87 1.75 1.1 1.45 1.43 1.21 1.85 1.04 2.27 0.88 2.74 0.79 3.26 0.79 3.77 0.79 4.23 0.88 4.64 1.07 5.04 1.25 5.36 1.51 5.58 1.85 5.81 2.18 5.92 2.57 5.92 3L4.21 3C4.21 2.71 4.12 2.49 3.94 2.33 3.77 2.17 3.53 2.09 3.22 2.09 2.92 2.09 2.67 2.15 2.5 2.29 2.32 2.42 2.23 2.6 2.23 2.8 2.23 2.99 2.33 3.15 2.52 3.3 2.72 3.45 3.06 3.6 3.55 3.76 4.05 3.92 4.45 4.09 4.77 4.27 5.53 4.71 5.92 5.32 5.92 6.1 5.92 6.72 5.68 7.21 5.21 7.57 4.75 7.92 4.1 8.1 3.29 8.1 2.71 8.1 2.19 7.99 1.72 7.79 1.25 7.58 0.9 7.3 0.67 6.94 0.43 6.58 0.31 6.16 0.31 5.7L2.03 5.7C2.03 6.08 2.13 6.36 2.33 6.54 2.52 6.72 2.84 6.81 3.29 6.81 3.57 6.81 3.79 6.75 3.96 6.63 4.12 6.5 4.2 6.33 4.2 6.11L4.2 6.11ZM7.74 6.4C8 6.4 8.22 6.48 8.39 6.64 8.57 6.79 8.65 6.99 8.65 7.23 8.65 7.47 8.57 7.67 8.39 7.82 8.22 7.98 8 8.05 7.74 8.05 7.48 8.05 7.26 7.98 7.09 7.82 6.92 7.66 6.84 7.47 6.84 7.23 6.84 6.99 6.92 6.79 7.09 6.64 7.26 6.48 7.48 6.4 7.74 6.4L7.74 6.4ZM7.74 2.51C8 2.51 8.22 2.58 8.39 2.74 8.57 2.89 8.65 3.09 8.65 3.33 8.65 3.57 8.57 3.77 8.39 3.93 8.22 4.08 8 4.16 7.74 4.16 7.48 4.16 7.26 4.08 7.09 3.92 6.92 3.77 6.84 3.57 6.84 3.33 6.84 3.09 6.92 2.9 7.09 2.74 7.26 2.58 7.48 2.51 7.74 2.51L7.74 2.51Z" id="S:"/>
|
||||
</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>
|
||||
<path d="M13.5 0C6.08 0 0 6.04 0 13.42 0 20.8 6.08 26.83 13.5 26.83 20.93 26.83 27 20.8 27 13.42 27 6.04 20.93 0 13.5 0L13.5 0ZM10.8 20.13L4.05 13.42 5.94 11.54 10.8 16.37 21.06 6.17 22.95 8.05 10.8 20.13 10.8 20.13Z" id="Shape"/>
|
||||
</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>
|
||||
<path d="M0 13L7 13 7 7 0 7 0 13 0 13ZM0 0L0 6 7 6 7 0 0 0ZM8 13L15 13 15 7 8 7 8 13ZM8 0L8 6 15 6 15 0 8 0Z" id="boxes" stroke="none"/>
|
||||
</symbol>
|
||||
<symbol id="icon-study-modality" viewBox="0 0 64 62">
|
||||
<title>
|
||||
Modality
|
||||
</title>
|
||||
<path d="M48.93 4C47.55 1.61 44.96 0 41.99 0L8.01 0C3.58 0 0 3.58 0 8.01L0 41.99C0 45.4 2.12 48.3 5.11 49.46 5.04 48.98 5 48.49 5 47.99L5 14.01C5 8.48 9.47 4 15.01 4L48.93 4Z" id="Combined-Shape"/>
|
||||
<path d="M55.93 10C54.55 7.61 51.96 6 48.99 6L15.01 6C10.58 6 7 9.58 7 14.01L7 47.99C7 51.4 9.12 54.3 12.11 55.46 12.04 54.98 12 54.49 12 53.99L12 20.01C12 14.48 16.47 10 22.01 10L55.93 10Z" id="Combined-Shape"/>
|
||||
<rect id="Rectangle-132" x="14" y="12" width="50" height="50" rx="8"/>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-levels" viewBox="0 0 18 18">
|
||||
<title>
|
||||
Window / Level
|
||||
</title>
|
||||
<circle id="Oval-2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" cx="8" cy="8" r="8" fill="none"></circle>
|
||||
<path d="M13.9867821,2.69341277 C15.2393779,4.10552346 16,5.96393532 16,8 C16,12.418278 12.418278,16 8,16 C5.96393532,16 4.10552346,15.2393779 2.69341277,13.9867821 L13.9867821,2.69341277 Z" id="Combined-Shape"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-link" viewBox="0 0 23 15">
|
||||
<title>
|
||||
Link
|
||||
</title>
|
||||
<circle id="Oval-4" opacity="0.8" cx="7.5" cy="7.5" r="7.5"/>
|
||||
<circle id="Oval-4" opacity="0.6" cx="15.5" cy="7.5" r="7.5"/>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-measure-non-target" viewBox="0 0 24 25">
|
||||
<title>
|
||||
Non-Target Measurement
|
||||
</title>
|
||||
<path d="M7.46 24.46L23.21 8.71C23.6 8.32 23.6 7.68 23.21 7.29 22.82 6.9 22.18 6.9 21.79 7.29L6.04 23.04C5.65 23.44 5.65 24.07 6.04 24.46 6.43 24.85 7.06 24.85 7.46 24.46L7.46 24.46Z" id="Line"/>
|
||||
<path d="M7.75 23.75L7.75 17.74C7.75 17.19 7.3 16.74 6.75 16.74 6.2 16.74 5.75 17.19 5.75 17.74L5.75 23.75C5.75 24.3 6.2 24.75 6.75 24.75 7.3 24.75 7.75 24.3 7.75 23.75L7.75 23.75Z" id="Line"/>
|
||||
<path d="M6.75 24.75L13 24.75C13.56 24.75 14 24.3 14 23.75 14 23.2 13.56 22.75 13 22.75L6.75 22.75C6.2 22.75 5.75 23.2 5.75 23.75 5.75 24.3 6.2 24.75 6.75 24.75L6.75 24.75Z" id="Line"/>
|
||||
<g id="plus">
|
||||
<path d="M6.5 12.5C9.81 12.5 12.5 9.81 12.5 6.5 12.5 3.19 9.81 0.5 6.5 0.5 3.19 0.5 0.5 3.19 0.5 6.5 0.5 9.81 3.19 12.5 6.5 12.5L6.5 12.5ZM6.5 11.5C3.74 11.5 1.5 9.26 1.5 6.5 1.5 3.74 3.74 1.5 6.5 1.5 9.26 1.5 11.5 3.74 11.5 6.5 11.5 9.26 9.26 11.5 6.5 11.5L6.5 11.5Z" id="Oval-6"/>
|
||||
<path d="M6 7L3.5 7C3.22 7 3 6.78 3 6.5 3 6.22 3.22 6 3.5 6L6 6 6 3.5C6 3.22 6.22 3 6.5 3 6.78 3 7 3.22 7 3.5L7 6 9.58 6C9.86 6 10.08 6.22 10.08 6.5 10.08 6.78 9.86 7 9.58 7L7 7 7 9.58C7 9.86 6.78 10.08 6.5 10.08 6.22 10.08 6 9.86 6 9.58L6 7Z" id="Combined-Shape"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-measure-target" viewBox="0 0 27 26">
|
||||
<title>
|
||||
Target Measurement
|
||||
</title>
|
||||
<path d="M14.25 20.74L15.71 19.28 14.34 17.91 15.05 17.21 16.42 18.57 17.79 17.2 15.31 14.73 16.02 14.02 18.5 16.5 19.96 15.04 18.59 13.67 19.29 12.96 20.66 14.33 22.03 12.96 19.56 10.49 20.26 9.78 22.74 12.25 24.2 10.8 22.83 9.43 23.54 8.72 24.91 10.09 26.99 8 23.07 4.08C22.29 3.3 21.02 3.3 20.24 4.08L5.39 18.92C4.61 19.71 4.61 20.97 5.39 21.76L9.32 25.68 11.47 23.52 10.1 22.16 10.81 21.45 12.18 22.82 13.55 21.45 11.07 18.97 11.78 18.26 14.25 20.74Z" id="Combined-Shape"/>
|
||||
<path d="M5 6L2.5 6C2.22 6 2 5.78 2 5.5 2 5.22 2.22 5 2.5 5L5 5 5 2.5C5 2.22 5.22 2 5.5 2 5.78 2 6 2.22 6 2.5L6 5 8.58 5C8.86 5 9.08 5.22 9.08 5.5 9.08 5.78 8.86 6 8.58 6L6 6 6 8.58C6 8.86 5.78 9.08 5.5 9.08 5.22 9.08 5 8.86 5 8.58L5 6ZM5.5 11C8.54 11 11 8.54 11 5.5 11 2.46 8.54 0 5.5 0 2.46 0 0 2.46 0 5.5 0 8.54 2.46 11 5.5 11Z" id="Combined-Shape"/>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-measure-temp" viewBox="0 0 26 28">
|
||||
<title>
|
||||
Temporary Measurement
|
||||
</title>
|
||||
<path d="M4.81 26.81L5.52 26.11C6.1 25.52 6.1 24.57 5.52 23.98 4.93 23.4 3.98 23.4 3.39 23.98L2.69 24.69C2.1 25.28 2.1 26.23 2.69 26.81 3.27 27.4 4.22 27.4 4.81 26.81L4.81 26.81ZM9.76 21.86L10.47 21.16C11.05 20.57 11.05 19.62 10.47 19.03 9.88 18.45 8.93 18.45 8.34 19.03L7.64 19.74C7.05 20.33 7.05 21.28 7.64 21.86 8.22 22.45 9.17 22.45 9.76 21.86L9.76 21.86ZM14.71 16.91L15.42 16.21C16 15.62 16 14.67 15.42 14.08 14.83 13.5 13.88 13.5 13.29 14.08L12.59 14.79C12 15.38 12 16.33 12.59 16.91 13.17 17.5 14.12 17.5 14.71 16.91L14.71 16.91ZM19.66 11.96L20.37 11.26C20.95 10.67 20.95 9.72 20.37 9.13 19.78 8.55 18.83 8.55 18.24 9.13L17.54 9.84C16.95 10.43 16.95 11.38 17.54 11.96 18.12 12.55 19.07 12.55 19.66 11.96L19.66 11.96ZM24.61 7.01L25.32 6.31C25.9 5.72 25.9 4.77 25.32 4.18 24.73 3.6 23.78 3.6 23.19 4.18L22.49 4.89C21.9 5.48 21.9 6.43 22.49 7.01 23.07 7.6 24.02 7.6 24.61 7.01L24.61 7.01Z" id="Line"/>
|
||||
<g id="plus">
|
||||
<path d="M6.5 12.5C9.81 12.5 12.5 9.81 12.5 6.5 12.5 3.19 9.81 0.5 6.5 0.5 3.19 0.5 0.5 3.19 0.5 6.5 0.5 9.81 3.19 12.5 6.5 12.5L6.5 12.5ZM6.5 11.5C3.74 11.5 1.5 9.26 1.5 6.5 1.5 3.74 3.74 1.5 6.5 1.5 9.26 1.5 11.5 3.74 11.5 6.5 11.5 9.26 9.26 11.5 6.5 11.5L6.5 11.5Z" id="Oval-6"/>
|
||||
<path d="M6 7L3.5 7C3.22 7 3 6.78 3 6.5 3 6.22 3.22 6 3.5 6L6 6 6 3.5C6 3.22 6.22 3 6.5 3 6.78 3 7 3.22 7 3.5L7 6 9.58 6C9.86 6 10.08 6.22 10.08 6.5 10.08 6.78 9.86 7 9.58 7L7 7 7 9.58C7 9.86 6.78 10.08 6.5 10.08 6.22 10.08 6 9.86 6 9.58L6 7Z" id="Combined-Shape"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-more" viewBox="0 0 18 18">
|
||||
<title>
|
||||
More
|
||||
</title>
|
||||
<path d="M9 18C13.97 18 18 13.97 18 9 18 4.03 13.97 0 9 0 4.03 0 0 4.03 0 9 0 13.97 4.03 18 9 18ZM4.5 8C3.68 8 3 8.68 3 9.5 3 10.33 3.68 11 4.5 11 5.33 11 6 10.33 6 9.5 6 8.68 5.33 8 4.5 8ZM13.5 8C12.68 8 12 8.68 12 9.5 12 10.33 12.68 11 13.5 11 14.33 11 15 10.33 15 9.5 15 8.68 14.33 8 13.5 8ZM9 8C8.18 8 7.5 8.68 7.5 9.5 7.5 10.33 8.18 11 9 11 9.82 11 10.5 10.33 10.5 9.5 10.5 8.68 9.82 8 9 8Z" id="Combined-Shape"/>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-pan" viewBox="0 0 18 18">
|
||||
<title>
|
||||
Pan
|
||||
</title>
|
||||
<path d="M8,15 L8,0" id="Line" stroke-width="2px"></path>
|
||||
<path d="M15,8 L1,8" id="Line" stroke-width="2px"></path>
|
||||
<polyline stroke-width="2px" points="6 1.99816663 7.99816663 0 10.0091928 2.01102613"></polyline>
|
||||
<polyline stroke-width="2px" transform="translate(8.004596, 15.005513) rotate(-180.000000) translate(-8.004596, -15.005513) " points="6 15.9981666 7.99816663 14 10.0091928 16.0110261"></polyline>
|
||||
<polyline stroke-width="2px" transform="translate(1.005513, 8.004596) rotate(-90.000000) translate(-1.005513, -8.004596) " points="-0.999083316 8.99724995 0.999083316 6.99908332 3.01010945 9.01010945"></polyline>
|
||||
<polyline stroke-width="2px" transform="translate(15.005513, 8.004596) rotate(-270.000000) translate(-15.005513, -8.004596) " points="13.0009167 8.99724995 14.9990833 6.99908332 17.0101094 9.01010945"></polyline>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-zoom" viewBox="0 0 17 17">
|
||||
<title>
|
||||
Zoom
|
||||
</title>
|
||||
<path d="M10.5 10.5 L15 15"/>
|
||||
<ellipse cx="6" cy="6" rx="6" ry="6" fill="none"/>
|
||||
</symbol>
|
||||
<symbol id="icon-trial-info" viewBox="0 0 20 20">
|
||||
<title>
|
||||
Trial Information
|
||||
</title>
|
||||
<path d="M9 15L11 15 11 9 9 9 9 15 9 15ZM10 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 0ZM10 18C5.6 18 2 14.4 2 10 2 5.6 5.6 2 10 2 14.4 2 18 5.6 18 10 18 14.4 14.4 18 10 18L10 18ZM9 7L11 7 11 5 9 5 9 7 9 7Z" id="Shape"/>
|
||||
</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>
|
||||
<polygon id="Shape" points="14 1.4 12.6 0 7 5.6 1.4 0 0 1.4 5.6 7 0 12.6 1.4 14 7 8.4 12.6 14 14 12.6 8.4 7"/>
|
||||
</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>
|
||||
<path d="M0 20L22 20 11 0 0 20 0 20ZM12 16.84L10 16.84 10 14.74 12 14.74 12 16.84 12 16.84ZM12 12.63L10 12.63 10 8.42 12 8.42 12 12.63 12 12.63Z"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,77 @@
|
||||
<template name="additionalMeasurements">
|
||||
<div id="additionalMeasurements">
|
||||
<div class="scrollArea">
|
||||
<div class="section">
|
||||
<div class="header">
|
||||
Additional Findings: Baseline
|
||||
</div>
|
||||
<div>
|
||||
{{ >radioOptionGroup measurableDiseaseButtonData}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="header">
|
||||
Supplementary Measurements
|
||||
</div>
|
||||
<div class="entrySection">
|
||||
<label>Number of bone lesions</label>
|
||||
<div>
|
||||
<select>
|
||||
<option>0</option>
|
||||
<option>1-2</option>
|
||||
<option selected>2-4</option>
|
||||
<option>>5</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="entrySection">
|
||||
<label>Regions of metastatic disease</label>
|
||||
<select id="regionsOfMetastaticDisease">
|
||||
<option>None</option>
|
||||
<option>Lymph node</option>
|
||||
</select>
|
||||
</div>
|
||||
{{ >radioOptionGroup metastaticDiseaseButtonData}}
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="header">
|
||||
Image Quality
|
||||
</div>
|
||||
{{ >radioOptionGroup imageQualityButtonData}}
|
||||
{{ >radioOptionGroup anatomicalCoverageButtonData}}
|
||||
{{ >radioOptionGroup presenceOfContrastButtonData}}
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="header">
|
||||
Additional Screen Captures
|
||||
</div>
|
||||
<div class="entrySection buttonSection">
|
||||
<div class="svgContainer">
|
||||
<svg>
|
||||
<use xlink:href="/packages/lesiontracker/assets/icons.svg#icon-create-screen-capture"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonLabel noselect">
|
||||
Add new screen capture
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="header">
|
||||
Comments
|
||||
</div>
|
||||
<div class="entrySection buttonSection">
|
||||
<div class="svgContainer">
|
||||
<svg>
|
||||
<use xlink:href="/packages/lesiontracker/assets/icons.svg#icon-create-comment"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonLabel noselect">
|
||||
Add a comment
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,87 @@
|
||||
var yesNoOptions = [{
|
||||
key: 'yes',
|
||||
text: 'Yes'
|
||||
}, {
|
||||
key: 'no',
|
||||
text: 'No'
|
||||
}];
|
||||
|
||||
Template.additionalMeasurements.onCreated(function additionalMeasurementsOnCreated() {
|
||||
const instance = this;
|
||||
|
||||
instance.measurableDisease = new ReactiveVar();
|
||||
instance.numberOfBoneLesions = new ReactiveVar();
|
||||
instance.regionsOfMetastaticDisease = new ReactiveVar();
|
||||
instance.tracerRelatedToMetastaticDisease = new ReactiveVar();
|
||||
instance.acceptableImageQuality = new ReactiveVar();
|
||||
instance.adequateAnatomicalCoverage = new ReactiveVar();
|
||||
instance.presenceOfContrast = new ReactiveVar();
|
||||
instance.phaseOfContrast = new ReactiveVar();
|
||||
|
||||
instance.measurableDisease.set('absent');
|
||||
instance.tracerRelatedToMetastaticDisease.set('no');
|
||||
instance.acceptableImageQuality.set('yes');
|
||||
instance.adequateAnatomicalCoverage.set('yes');
|
||||
instance.presenceOfContrast.set('yes');
|
||||
});
|
||||
|
||||
Template.additionalMeasurements.onRendered(function additionalMeasurementsOnRendered() {
|
||||
const instance = this;
|
||||
|
||||
instance.$("#regionsOfMetastaticDisease").select2({
|
||||
multiple: true
|
||||
});
|
||||
});
|
||||
|
||||
Template.additionalMeasurements.helpers({
|
||||
measurableDiseaseButtonData() {
|
||||
const instance = Template.instance();
|
||||
return {
|
||||
text: 'Measurable disease',
|
||||
value: instance.measurableDisease,
|
||||
options: [{
|
||||
key: 'present',
|
||||
text: 'Present'
|
||||
}, {
|
||||
key: 'absent',
|
||||
text: 'Absent'
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
metastaticDiseaseButtonData() {
|
||||
const instance = Template.instance();
|
||||
return {
|
||||
text: 'Tracer related to metastatic disease',
|
||||
value: instance.tracerRelatedToMetastaticDisease,
|
||||
options: yesNoOptions
|
||||
};
|
||||
},
|
||||
|
||||
imageQualityButtonData() {
|
||||
const instance = Template.instance();
|
||||
return {
|
||||
text: 'Acceptable image quality',
|
||||
value: instance.acceptableImageQuality,
|
||||
options: yesNoOptions
|
||||
};
|
||||
},
|
||||
|
||||
anatomicalCoverageButtonData() {
|
||||
const instance = Template.instance();
|
||||
return {
|
||||
text: 'Adequate anatomical coverage',
|
||||
value: instance.adequateAnatomicalCoverage,
|
||||
options: yesNoOptions
|
||||
};
|
||||
},
|
||||
|
||||
presenceOfContrastButtonData() {
|
||||
const instance = Template.instance();
|
||||
return {
|
||||
text: 'Presence of contrast',
|
||||
value: instance.presenceOfContrast,
|
||||
options: yesNoOptions
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,74 @@
|
||||
@import "{design}/app"
|
||||
|
||||
#additionalMeasurements
|
||||
width: 100%
|
||||
height: 100%
|
||||
background-color: #000000
|
||||
|
||||
.scrollArea
|
||||
height: 100%
|
||||
overflow-x: hidden
|
||||
overflow-y: auto
|
||||
margin-right: -16px
|
||||
padding-bottom: 100px
|
||||
padding-right: 16px
|
||||
|
||||
-webkit-overflow-scrolling: touch
|
||||
|
||||
&::-webkit-scrollbar
|
||||
display: none
|
||||
|
||||
.header
|
||||
height: 33px
|
||||
line-height: 33px
|
||||
font-weight: 400
|
||||
font-size: 14px
|
||||
padding: 0 10px
|
||||
color: $textPrimaryColor
|
||||
background-color: $darkUiColor
|
||||
|
||||
.entrySection
|
||||
#regionsOfMetastaticDisease
|
||||
width: 285px
|
||||
height: 30px
|
||||
border-radius: 2px
|
||||
background-color: #b6b6b6
|
||||
color: #3e3e3e
|
||||
font-size: 14px
|
||||
|
||||
.buttonSection
|
||||
cursor: pointer
|
||||
height: 100px
|
||||
color: $defaultColor
|
||||
|
||||
&:hover
|
||||
color: $hoverColor
|
||||
|
||||
.svgContainer
|
||||
svg
|
||||
fill: $hoverColor
|
||||
|
||||
&:active
|
||||
color: $activeColor
|
||||
|
||||
.svgContainer
|
||||
svg
|
||||
fill: $activeColor
|
||||
|
||||
div
|
||||
float: left
|
||||
display: inline-block
|
||||
height: 80px
|
||||
|
||||
.buttonLabel
|
||||
line-height: 80px
|
||||
padding: 0 30px
|
||||
|
||||
.svgContainer
|
||||
padding: 15px 5px
|
||||
width: 40px
|
||||
|
||||
svg
|
||||
width: 40px
|
||||
height: 40px
|
||||
fill: $textPrimaryColor
|
||||
@ -0,0 +1,16 @@
|
||||
<template name="radioOptionGroup">
|
||||
<div class="{{class}} entrySection viewerRadioOptionGroup">
|
||||
<h5>{{text}}</h5>
|
||||
<div>
|
||||
{{#each item in this.options}}
|
||||
<label>
|
||||
<input type="radio"
|
||||
name="{{item.key}}"
|
||||
data-key="{{item.key}}"
|
||||
{{inlineIf value.get item.key 'selected'}}/>
|
||||
{{item.text}}
|
||||
</label>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,15 @@
|
||||
Template.radioOptionGroup.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
const value = instance.data.value;
|
||||
if (!value.get()) {
|
||||
value.set(instance.data.options[0].key);
|
||||
}
|
||||
});
|
||||
|
||||
Template.radioOptionGroup.events({
|
||||
'click [data-key]'(event, instance) {
|
||||
const $element = $(event.currentTarget);
|
||||
const key = $element.attr('data-key');
|
||||
instance.data.value.set(key);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,20 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.entrySection
|
||||
padding: 10px
|
||||
color: $textPrimaryColor
|
||||
|
||||
&:not(:last-child)
|
||||
border-bottom: $uiBorderThickness solid $uiBorderColorDark
|
||||
|
||||
h5
|
||||
font-weight: 300
|
||||
|
||||
label
|
||||
font-size: 14px
|
||||
font-weight: 100
|
||||
display: inline-block
|
||||
padding: 5px
|
||||
|
||||
input
|
||||
margin: 5px
|
||||
@ -0,0 +1,11 @@
|
||||
<template name="caseProgress">
|
||||
<div class="caseProgress">
|
||||
{{ >radialProgressBar isLocked=isLocked progressPercent=progressPercent progressText=progressText}}
|
||||
{{ #if isLocked }}
|
||||
<h5>Locked</h5>
|
||||
{{ /if }}
|
||||
{{ #if progressComplete }}
|
||||
<button class="btn">Enter response</button>
|
||||
{{ /if }}
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,30 @@
|
||||
Template.caseProgress.onCreated(function caseProgressOnCreated() {
|
||||
const instance = Template.instance();
|
||||
|
||||
instance.progressPercent = new ReactiveVar();
|
||||
instance.progressPercent.set(50);
|
||||
|
||||
instance.progressText = new ReactiveVar();
|
||||
instance.progressText.set(5);
|
||||
|
||||
instance.isLocked = new ReactiveVar(false);
|
||||
});
|
||||
|
||||
Template.caseProgress.helpers({
|
||||
progressPercent() {
|
||||
return Template.instance().progressPercent.get();
|
||||
},
|
||||
|
||||
progressText() {
|
||||
return Template.instance().progressText.get();
|
||||
},
|
||||
|
||||
isLocked() {
|
||||
return Template.instance().isLocked.get();
|
||||
},
|
||||
|
||||
progressComplete() {
|
||||
var progressPercent = Template.instance().progressPercent.get();
|
||||
return progressPercent === 100;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
@import "{design}/app"
|
||||
|
||||
//.caseProgress
|
||||
@ -0,0 +1,20 @@
|
||||
<template name="flexboxLayout">
|
||||
<div class="viewerSection">
|
||||
<div class="sidebarMenu sidebar-left {{ #if studySidebarOpen }}sidebar-open{{/if}}">
|
||||
<div class="timepointButtonContainer p-t-2">
|
||||
{{>roundedButtonGroup buttonGroupData}}
|
||||
</div>
|
||||
<hr class="m-y-0">
|
||||
{{>studyTimepointBrowser timepointViewType=timepointViewType}}
|
||||
</div>
|
||||
<div class="mainContent {{ #if studySidebarOpen }}sidebar-left-open{{/if}} {{ #if rightSidebarOpen }}sidebar-right-open{{/if}}">
|
||||
{{>viewerMain }}
|
||||
</div>
|
||||
<div class="sidebarMenu sidebar-right {{ #if lesionSidebarOpen }}sidebar-open{{/if}}">
|
||||
{{>lesionTable }}
|
||||
</div>
|
||||
<div class="sidebarMenu sidebar-right {{ #if additionalMeasurementsSidebarOpen }}sidebar-open{{/if}}">
|
||||
{{>additionalMeasurements }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,66 @@
|
||||
Template.flexboxLayout.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
instance.state = instance.data.state;
|
||||
instance.timepointViewType = new ReactiveVar();
|
||||
});
|
||||
|
||||
var resizeTimeout;
|
||||
Template.flexboxLayout.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
instance.autorun(function() {
|
||||
Meteor.clearTimeout(resizeTimeout);
|
||||
|
||||
// Trigger a resize any time the layout state changes
|
||||
var studySidebarOpen = instance.state.get('studySidebarOpen');
|
||||
var lesionSidebarOpen = instance.state.get('lesionSidebarOpen');
|
||||
|
||||
var additionalMeasurementsSidebarOpen = instance.state.get('additionalMeasurementsSidebarOpen');
|
||||
|
||||
resizeTimeout = Meteor.setTimeout(function() {
|
||||
handleResize();
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
Template.flexboxLayout.helpers({
|
||||
timepointViewType() {
|
||||
return Template.instance().timepointViewType;
|
||||
},
|
||||
|
||||
buttonGroupData() {
|
||||
const instance = Template.instance();
|
||||
return {
|
||||
value: instance.timepointViewType,
|
||||
options: [{
|
||||
key: 'key',
|
||||
text: 'Key Timepoints'
|
||||
}, {
|
||||
key: 'all',
|
||||
text: 'All Timepoints'
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
studySidebarOpen() {
|
||||
const instance = Template.instance();
|
||||
return instance.state.get('studySidebarOpen');
|
||||
},
|
||||
|
||||
lesionSidebarOpen() {
|
||||
const instance = Template.instance();
|
||||
return instance.state.get('lesionSidebarOpen');
|
||||
},
|
||||
|
||||
additionalMeasurementsSidebarOpen() {
|
||||
const instance = Template.instance();
|
||||
return instance.state.get('additionalMeasurementsSidebarOpen');
|
||||
},
|
||||
|
||||
rightSidebarOpen() {
|
||||
const instance = Template.instance();
|
||||
const lesionSidebarOpen = instance.data.state.get('lesionSidebarOpen');
|
||||
const additionalMeasurementsSidebarOpen = instance.data.state.get('additionalMeasurementsSidebarOpen');
|
||||
return lesionSidebarOpen || additionalMeasurementsSidebarOpen;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,69 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.viewerSection
|
||||
display: inline-flex
|
||||
flex-flow: row nowrap
|
||||
align-items: stretch
|
||||
height: 'calc(100% - %s)' % $toolbarHeight
|
||||
width: 100%
|
||||
|
||||
.sidebarMenu
|
||||
position: absolute
|
||||
height: 100%
|
||||
background: $primaryBackgroundColor
|
||||
-webkit-transition: $sidebarTransition
|
||||
-ms-transition: $sidebarTransition
|
||||
-o-transition: $sidebarTransition
|
||||
-moz-transition: $sidebarTransition
|
||||
transition: $sidebarTransition
|
||||
|
||||
.timepointButtonContainer
|
||||
height: 55px
|
||||
|
||||
.sidebar-left
|
||||
width: $studiesSidebarMenuWidth
|
||||
order: 1
|
||||
flex: 0
|
||||
left: - $studiesSidebarMenuWidth
|
||||
border-right: $uiBorderThickness $uiBorderColor solid
|
||||
|
||||
&.sidebar-open
|
||||
left: 0
|
||||
|
||||
.mainContent
|
||||
order: 2
|
||||
width: 100%
|
||||
height: 100%
|
||||
-webkit-transition: $sidebarTransition
|
||||
-ms-transition: $sidebarTransition
|
||||
-o-transition: $sidebarTransition
|
||||
-moz-transition: $sidebarTransition
|
||||
transition: $sidebarTransition
|
||||
transform: translate3d(0,0,0);
|
||||
|
||||
&.sidebar-left-open.sidebar-right-open
|
||||
width: 'calc(100% - %s)' % ($studiesSidebarMenuWidth + $lesionsSidebarMenuWidth)
|
||||
|
||||
&.sidebar-right-open
|
||||
width: 'calc(100% - %s)' % $lesionsSidebarMenuWidth
|
||||
|
||||
&.sidebar-left-open
|
||||
width: 'calc(100% - %s)' % $studiesSidebarMenuWidth
|
||||
|
||||
&.sidebar-left-open
|
||||
transform: translate3d($studiesSidebarMenuWidth,0,0);
|
||||
|
||||
.sidebar-right
|
||||
width: $lesionsSidebarMenuWidth
|
||||
order: 3
|
||||
flex: 0
|
||||
right: - $lesionsSidebarMenuWidth
|
||||
border-left: $uiBorderThickness $uiBorderColor solid
|
||||
|
||||
&.sidebar-open
|
||||
right: 0
|
||||
|
||||
.studiesListedChanger
|
||||
padding: 20px 10px
|
||||
text-align: center
|
||||
border-bottom: $uiBorderThickness $uiBorderColor solid
|
||||
@ -9,9 +9,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="hidingPanelContent">
|
||||
{{#if studyDateIsShown}}
|
||||
{{> studyDateList}}
|
||||
{{/if}}
|
||||
{{> studyDateList}}
|
||||
<div class="studyBrowserContainer">
|
||||
{{> studyBrowser}}
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
|
||||
function pinPanel() {
|
||||
var hidingPanel = $(".hidingPanel");
|
||||
var hidingPanel = $('.hidingPanel');
|
||||
hidingPanel.css({
|
||||
width: '122px'
|
||||
});
|
||||
@ -12,12 +12,12 @@ function pinPanel() {
|
||||
// Calculate newWidth of viewportAndLesionTable
|
||||
var viewerWidth = $('#viewer').width();
|
||||
var newWidth = 100 - 122 / viewerWidth * 100;
|
||||
$("#viewportAndLesionTable").css('width', newWidth + '%');
|
||||
$('#viewportAndLesionTable').css('width', newWidth + '%');
|
||||
}
|
||||
|
||||
function unpinPanel() {
|
||||
// Calculate newWidth of viewportAndLesionTable
|
||||
$("#viewportAndLesionTable").css('width', '99%');
|
||||
// Calculate newWidth of viewportAndLesionTable
|
||||
$('#viewportAndLesionTable').css('width', '99%');
|
||||
}
|
||||
|
||||
Template.hidingPanel.events({
|
||||
@ -90,9 +90,6 @@ Template.hidingPanel.events({
|
||||
});
|
||||
|
||||
Template.hidingPanel.helpers({
|
||||
studyDateIsShown: function() {
|
||||
return true;
|
||||
},
|
||||
isPinned: function() {
|
||||
return Template.instance().isPinned.get();
|
||||
}
|
||||
@ -117,7 +114,7 @@ Template.hidingPanel.onCreated(function() {
|
||||
if (!isPanelPinned) {
|
||||
isPanelPinned = false;
|
||||
}
|
||||
|
||||
this.isPinned = new ReactiveVar(isPanelPinned);
|
||||
|
||||
|
||||
});
|
||||
|
||||
@ -1,27 +1,30 @@
|
||||
<template name="lesionTable">
|
||||
<div id="lesionTableContainer">
|
||||
<div id="dragbar"></div>
|
||||
<div id="lesionTableWrapper">
|
||||
<table class="table table-striped noselect lesionTable" id="tblLesion">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="thNumber">Lesion #</th>
|
||||
<!--<th id="thLocation">Location</th>-->
|
||||
<th id="thTarget">Target</th>
|
||||
<!--Each time point name as a column-->
|
||||
{{ #each timepoints }}
|
||||
{{ >lesionTableTimepointHeader }}
|
||||
{{ /each }}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!--Each measurement as row-->
|
||||
{{ #each measurement }}
|
||||
{{ >lesionTableRow }}
|
||||
{{ /each }}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="lesionTableLayoutChanger viewerRoundedButtonGroup">
|
||||
{{ >roundedButtonGroup buttonGroupData }}
|
||||
</div>
|
||||
<div class="lesionTableTimepointHeaderRow">
|
||||
<div class="spacer">
|
||||
</div>
|
||||
{{ #each timepoints }}
|
||||
{{ >lesionTableTimepointHeader }}
|
||||
{{ /each }}
|
||||
</div>
|
||||
<div class="lesionTableWrapper">
|
||||
{{ >lesionTableHeaderRow id="target" type="Targets" measurements=targets}}
|
||||
{{ #each targets }}
|
||||
{{ >lesionTableRow }}
|
||||
{{ /each }}
|
||||
|
||||
{{ >lesionTableHeaderRow id="nonTarget" type="Non-targets" measurements=nonTargets}}
|
||||
{{ #each nonTargets }}
|
||||
{{ >lesionTableRow }}
|
||||
{{ /each }}
|
||||
|
||||
{{ >lesionTableHeaderRow id="newLesions" type="New lesions" measurements=newLesions}}
|
||||
{{ #each nonTargets }}
|
||||
{{ >lesionTableRow }}
|
||||
{{ /each }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,9 +1,30 @@
|
||||
Template.lesionTable.helpers({
|
||||
measurement: function() {
|
||||
targets: function() {
|
||||
// All Targets shall be listed first followed by Non-Targets
|
||||
return Measurements.find({}, {
|
||||
return Measurements.find({
|
||||
isTarget: true
|
||||
}, {
|
||||
sort: {
|
||||
lesionNumberAbsolute: 1
|
||||
}
|
||||
});
|
||||
},
|
||||
nonTargets: function() {
|
||||
// All Targets shall be listed first followed by Non-Targets
|
||||
return Measurements.find({
|
||||
isTarget: false
|
||||
}, {
|
||||
sort: {
|
||||
lesionNumberAbsolute: 1
|
||||
}
|
||||
});
|
||||
},
|
||||
newLesions: function() {
|
||||
// All Targets shall be listed first followed by Non-Targets
|
||||
return Measurements.find({
|
||||
saved: false
|
||||
}, {
|
||||
sort: {
|
||||
isTarget: -1,
|
||||
lesionNumberAbsolute: 1
|
||||
}
|
||||
});
|
||||
@ -14,59 +35,43 @@ Template.lesionTable.helpers({
|
||||
latestDate: 1
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
buttonGroupData() {
|
||||
const instance = Template.instance();
|
||||
return {
|
||||
value: instance.lesionTableLayout,
|
||||
options: [{
|
||||
key: 'comparison',
|
||||
text: 'Comparison'
|
||||
}, {
|
||||
key: 'key',
|
||||
text: 'Key Timepoints'
|
||||
}]
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Template.lesionTable.events({
|
||||
// 'click table#tblLesion tbody tr': function(e, template) {
|
||||
// Retrieve the lesion id from the DOM data for this row
|
||||
// var measurementId = $(e.currentTarget).data('measurementid');
|
||||
|
||||
// activateLesion(measurementId, template.data);
|
||||
//},
|
||||
'mousedown div#dragbar': function(e, template) {
|
||||
var pY = e.pageY;
|
||||
var draggableParent = $(e.currentTarget).parent();
|
||||
var startHeight = draggableParent.height();
|
||||
template.dragging.set(true);
|
||||
|
||||
$(document).on('mouseup', function(e) {
|
||||
template.dragging.set(false);
|
||||
$(document).off('mouseup').off('mousemove');
|
||||
});
|
||||
|
||||
$(document).on('mousemove', function(e) {
|
||||
var topPosition = e.pageY - pY;
|
||||
var newHeight = startHeight - topPosition;
|
||||
|
||||
// Min lesion table height = 5px
|
||||
if (newHeight < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
draggableParent.css({
|
||||
top: topPosition,
|
||||
height: newHeight
|
||||
});
|
||||
|
||||
var viewportAndLesionTableHeight = $('#viewportAndLesionTable').height();
|
||||
var newPercentageHeightofLesionTable = (startHeight - topPosition) / viewportAndLesionTableHeight * 100;
|
||||
var newPercentageHeightofViewermain = 100 - newPercentageHeightofLesionTable;
|
||||
$('.viewerMain').height(newPercentageHeightofViewermain + '%');
|
||||
|
||||
// Resize viewport
|
||||
resizeViewportElements();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Template.lesionTable.onCreated(function() {
|
||||
this.dragging = new ReactiveVar(false);
|
||||
/**
|
||||
* Retrieve the lesion id from the DOM data for this row
|
||||
*/
|
||||
/*'click table#tblLesion tbody tr': function(e, template) {
|
||||
var measurementId = $(e.currentTarget).data('measurementid');
|
||||
activateLesion(measurementId, template.data);
|
||||
},*/
|
||||
});
|
||||
|
||||
// Temporary until we have a real window manager with events for series/study changed
|
||||
Session.setDefault('NewSeriesLoaded', false);
|
||||
|
||||
Template.lesionTable.onCreated(function() {
|
||||
var instance = this;
|
||||
|
||||
instance.lesionTableLayout = new ReactiveVar();
|
||||
instance.lesionTableLayout.set('comparisonTimepoints');
|
||||
});
|
||||
|
||||
Template.lesionTable.onRendered(function() {
|
||||
// Find the first measurement by Lesion Number
|
||||
var firstLesion = Measurements.findOne({}, {
|
||||
|
||||
@ -1,93 +1,23 @@
|
||||
@import "{design}/app"
|
||||
|
||||
#lesionTableContainer
|
||||
width: 100%
|
||||
height: 16%
|
||||
background-color: #000000
|
||||
|
||||
#dragbar
|
||||
width: 100%
|
||||
height: 3px
|
||||
top: 0
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
background-color: #666666
|
||||
cursor: row-resize
|
||||
|
||||
#lesionTableWrapper
|
||||
position: relative
|
||||
width: 100%
|
||||
height: 100%
|
||||
margin: 0 auto
|
||||
|
||||
.lesionTable
|
||||
.lesionTableTimepointHeaderRow
|
||||
display: flex
|
||||
|
||||
td, th
|
||||
border: 1px solid #FFFFFF
|
||||
border-collapse: collapse
|
||||
width: 100px
|
||||
.spacer
|
||||
width: 20px
|
||||
|
||||
td.lesionNumber, th#thNumber
|
||||
width: 70px
|
||||
|
||||
td.location, th#thLocation
|
||||
width: 250px
|
||||
|
||||
td.target, th#thTarget
|
||||
width: 70px
|
||||
|
||||
|
||||
thead
|
||||
width: 100%
|
||||
height: 40px
|
||||
display: block
|
||||
|
||||
tr
|
||||
width: 100%
|
||||
height: 40px
|
||||
|
||||
th
|
||||
border-bottom-width: 0
|
||||
font-weight: normal
|
||||
color: #FFFFFF
|
||||
line-height: 15px
|
||||
text-align: center
|
||||
margin: 0
|
||||
padding: 5px
|
||||
background-color: #424242
|
||||
vertical-align: middle
|
||||
|
||||
tbody
|
||||
display: block
|
||||
position: absolute
|
||||
top: 40px
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
overflow: auto
|
||||
width: 100%
|
||||
padding-bottom: 20px
|
||||
|
||||
tr.selectedRow
|
||||
background: #F5F5F5 !important
|
||||
color: #000000 !important
|
||||
|
||||
tr.lesionTableRow
|
||||
width: 100%
|
||||
|
||||
&:nth-child(even)
|
||||
background: #888
|
||||
&:nth-child(odd)
|
||||
background: #BBB
|
||||
&:hover
|
||||
cursor: pointer
|
||||
background-color: #009BD2
|
||||
color: white
|
||||
|
||||
td
|
||||
border-bottom-width: 0
|
||||
margin: 0
|
||||
padding: 5px
|
||||
line-height: 15px
|
||||
|
||||
&.lesionNumber, &.target, &.lesionTableTimepointCell
|
||||
text-align: center
|
||||
.lesionTableLayoutChanger
|
||||
padding: 5px
|
||||
text-align: center
|
||||
margin: 0 auto
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
<template name="lesionTableHeaderRow">
|
||||
<div class="lesionTableHeaderRow">
|
||||
<div class="add">
|
||||
<svg>
|
||||
<use xlink:href=/packages/lesiontracker/assets/icons.svg#icon-ui-add></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="type">
|
||||
{{type}}
|
||||
</div>
|
||||
<div class="max">
|
||||
{{ #if maxNumLesions }}
|
||||
<p class="maxNumLesions">
|
||||
Max {{maxNumLesions}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="numberOfLesions">
|
||||
{{numberOfLesions}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,38 @@
|
||||
Template.lesionTableHeaderRow.helpers({
|
||||
numberOfLesions: function() {
|
||||
var instance = Template.instance();
|
||||
return instance.data.measurements.count();
|
||||
},
|
||||
maxNumLesions: function() {
|
||||
var instance = Template.instance();
|
||||
var lesionType = instance.id;
|
||||
|
||||
// TODO: Check what we are annotating
|
||||
var timepointType = 'baseline';
|
||||
|
||||
// Identify which Trial Conformance Criteria are currently being used
|
||||
// Note that there may be more than one.
|
||||
var criteriaTypes = TrialCriteriaTypes.find({
|
||||
selected: true
|
||||
}).map(function(criteria) {
|
||||
return criteria.id;
|
||||
});
|
||||
|
||||
var currentConstraints = getTrialCriteriaConstraints(criteriaTypes);
|
||||
if (!currentConstraints) {
|
||||
// For testing
|
||||
return 10;
|
||||
}
|
||||
|
||||
var criteria = currentConstraints[timepointType][lesionType];
|
||||
|
||||
return criteria.group.totalNumberOfLesions;
|
||||
}
|
||||
});
|
||||
|
||||
Template.lesionTableHeaderRow.events({
|
||||
'click .add': function() {
|
||||
console.log('Add was clicked');
|
||||
// TODO: Set active tool to new type
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,58 @@
|
||||
@import "{design}/app"
|
||||
|
||||
$headerRowHeight = 63px
|
||||
|
||||
.lesionTableHeaderRow
|
||||
background: $darkUiColor
|
||||
color: $textSecondaryColor
|
||||
fill: $textSecondaryColor
|
||||
width: 100%
|
||||
height: $headerRowHeight
|
||||
display: flex
|
||||
line-height: $headerRowHeight
|
||||
|
||||
div
|
||||
flex: 1
|
||||
justify-content: space-around
|
||||
align-items: stretch
|
||||
text-align: center
|
||||
|
||||
.add
|
||||
padding: 0 10px
|
||||
max-width: 20px
|
||||
cursor: pointer
|
||||
|
||||
svg
|
||||
height: $headerRowHeight
|
||||
max-width: 11px
|
||||
|
||||
.type
|
||||
cursor: pointer
|
||||
font-weight: 200
|
||||
height: $headerRowHeight
|
||||
font-size: 13pt
|
||||
|
||||
.numberOfLesions
|
||||
float: right
|
||||
font-weight: 100
|
||||
font-size: 40px
|
||||
max-width: 30px
|
||||
margin-right: 5px
|
||||
height: $headerRowHeight
|
||||
line-height: $headerRowHeight
|
||||
|
||||
.max
|
||||
height: $headerRowHeight
|
||||
background: $darkUiColor
|
||||
|
||||
.maxNumLesions
|
||||
margin: 21px auto
|
||||
height: 20px
|
||||
line-height: 20px
|
||||
width: 47px
|
||||
font-size: 12px
|
||||
text-transform: uppercase
|
||||
font-weight: 500
|
||||
border-radius: 3px
|
||||
color: black
|
||||
background-color: $textSecondaryColor;
|
||||
@ -1,11 +1,38 @@
|
||||
<template name="lesionTableRow">
|
||||
<tr id="{{lesionNumber}}" class="lesionTableRow" data-measurementid="{{_id}}">
|
||||
<td class='lesionNumber'>{{lesionNumberAbsolute}}</td>
|
||||
<!--<td class='location' tabindex="0">{{location}}</td>-->
|
||||
<td class='target'>{{#if isTarget}} Y {{else}} N {{/if}}</td>
|
||||
<!--Each time point as a column-->
|
||||
{{# each timepoints }}
|
||||
{{> lesionTableTimepointCell}}
|
||||
{{/ each }}
|
||||
</tr>
|
||||
<div class="lesionTableRow" data-measurementid="{{_id}}">
|
||||
<div class='lesionRowSidebar'>
|
||||
<div class="lesionNumber">
|
||||
{{lesionNumberAbsolute}}
|
||||
</div>
|
||||
<div class="lesionNumber">
|
||||
{{ #if descriptionRequired }}
|
||||
<div>
|
||||
<svg>
|
||||
<use xlink:href=/packages/lesiontracker/assets/icons.svg#icon-ui-warning></use>
|
||||
</svg>
|
||||
</div>
|
||||
{{ /if }}
|
||||
{{ #if responseStatus }}
|
||||
<div>
|
||||
CR
|
||||
</div>
|
||||
{{ /if }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="lesionDetails">
|
||||
<div class='location'>
|
||||
{{ #if location }}
|
||||
{{location}}
|
||||
{{ else }}
|
||||
(No description)
|
||||
{{ /if }}
|
||||
</div>
|
||||
<div class="timepointData">
|
||||
<!--Each time point as a column-->
|
||||
{{# each timepoints }}
|
||||
{{> lesionTableTimepointCell}}
|
||||
{{/ each }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,3 +1,39 @@
|
||||
.lesionTableRow .location
|
||||
&:hover
|
||||
font-weight: bold
|
||||
@import "{design}/app"
|
||||
|
||||
.lesionTableRow
|
||||
display: flex
|
||||
width: 100%
|
||||
|
||||
.lesionRowSidebar
|
||||
flex: 1
|
||||
color: $textSecondaryColor
|
||||
background: $darkUiColor
|
||||
max-width: 20px
|
||||
height: 60px
|
||||
|
||||
.lesionNumber
|
||||
padding: 3px
|
||||
|
||||
&.active
|
||||
border-left: 4px $activeColor solid
|
||||
|
||||
.lesionDetails
|
||||
flex: 1
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: stretch
|
||||
|
||||
.location
|
||||
flex: 1
|
||||
text-align: center
|
||||
color: $textSecondaryColor
|
||||
height: 20px
|
||||
width: 100%
|
||||
|
||||
.timepointData
|
||||
display: flex
|
||||
|
||||
div
|
||||
flex: 1
|
||||
text-align: center
|
||||
justify-content: space-around
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
<template name="lesionTableTimepointCell">
|
||||
{{ #if hasDataAtThisTimepoint }}
|
||||
{{ #if isBirectional }}
|
||||
<td class="lesionTableTimepointCell targetCell" tabindex="1">{{displayData}}</td>
|
||||
{{ else }}
|
||||
<td class="lesionTableTimepointCell nonTargetCell" tabindex="1">{{displayData}}</td>
|
||||
{{ /if }}
|
||||
<div class="lesionTableTimepointCell" tabindex="1">
|
||||
{{displayData}}
|
||||
</div>
|
||||
{{ else }}
|
||||
<td class="lesionTableTimepointCell empty"></td>
|
||||
<div class="lesionTableTimepointCell empty">
|
||||
...
|
||||
</div>
|
||||
{{ /if }}
|
||||
</template>
|
||||
@ -1,8 +1,6 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.lesionTableTimepointCell
|
||||
&:hover
|
||||
font-weight: bold
|
||||
.nonTargetCell
|
||||
&:hover
|
||||
color: red
|
||||
|
||||
|
||||
border-left: $uiBorderThickness $uiBorderColor solid
|
||||
color: $textPrimaryColor
|
||||
padding: 0 10px
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
<template name="lesionTableTimepointHeader">
|
||||
<th class="lesionTableTimepointCell {{ #if isLoaded }}loaded{{/if}}" title="Latest study date: {{formatDA latestDate "MM/DD/YYYY"}}"
|
||||
data-toggle="tooltip" data-placement="top">
|
||||
{{ timepointName }}
|
||||
</th>
|
||||
<div class="lesionTableTimepointHeader">
|
||||
<div class="timepointName">
|
||||
{{ timepointName }}
|
||||
</div>
|
||||
<div class="timepointDate">
|
||||
{{ formatDA latestDate "DD-MMM-YY"}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,3 +1,15 @@
|
||||
#lesionTableContainer .lesionTable thead tr th.lesionTableTimepointCell.loaded
|
||||
//font-weight: bold
|
||||
color: #4fbfff
|
||||
@import "{design}/app"
|
||||
|
||||
.lesionTableTimepointHeader
|
||||
flex: 1
|
||||
justify-content: space-around
|
||||
|
||||
div
|
||||
text-align: center
|
||||
|
||||
.timepointName
|
||||
color: $textSecondaryColor
|
||||
|
||||
.timepointDate
|
||||
color: $textPrimaryColor
|
||||
border-left: $uiBorderColor $uiBorderThickness solid
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
<template name="lesionTracker">
|
||||
<div class="topBar">
|
||||
<a class="brandSection pull-left" href="http://ohif.org">
|
||||
<img class="logoImage" src="/images/logo.png">
|
||||
<div class="logoText">
|
||||
Open Health Imaging Foundation
|
||||
</div>
|
||||
</a>
|
||||
{{ #unless onStudyList}}
|
||||
<div class="studyListLinkSection pull-left">
|
||||
<a id="loadStudyList">Study List</a>
|
||||
</div>
|
||||
{{ /unless }}
|
||||
<div class="userAccountSection pull-right">
|
||||
{{>userAccountMenu}}
|
||||
</div>
|
||||
</div>
|
||||
{{>timeoutCountdownDialog}}
|
||||
<div id="worklistTabs" class="tab-content">
|
||||
<div class="tab-pane active" id="worklistTab">
|
||||
<div class="worklistContainer">
|
||||
{{> worklistResult }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="viewerTab">
|
||||
</div>
|
||||
</div>
|
||||
{{ >associationModal }}
|
||||
{{ >optionsModal }}
|
||||
{{ >serverInformationModal }}
|
||||
{{ >confirmRemoveTimepointAssociation }}
|
||||
{{ >lastLoginModal }}
|
||||
{{ >studyContextMenu }}
|
||||
{{ >progressDialog}}
|
||||
</template>
|
||||
@ -0,0 +1,65 @@
|
||||
// Define the ViewerData global object
|
||||
// If there is currently any Session data for this object,
|
||||
// use this to repopulate the variable
|
||||
ViewerData = Session.get('ViewerData') || {};
|
||||
|
||||
Template.lesionTracker.onCreated(function() {
|
||||
// showViewer to go to viewer from audit
|
||||
this.showWorklistMenu = new ReactiveVar(true);
|
||||
// Get url and check worklist
|
||||
var currentRoute = Router.current();
|
||||
if (!currentRoute || !currentRoute.route) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentPath = currentRoute.route.path(this);
|
||||
if (currentPath === '/' || currentPath === '/worklist') {
|
||||
this.showWorklistMenu.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
Template.lesionTracker.onRendered(function() {
|
||||
var templateData = Template.currentData();
|
||||
if (templateData && templateData.studyInstanceUid) {
|
||||
var studyInstanceUid = templateData.studyInstanceUid;
|
||||
openNewTab(studyInstanceUid, studyInstanceUid);
|
||||
} else {
|
||||
// If there is a tab set as active in the Session,
|
||||
// switch to that now.
|
||||
var contentId = Session.get('activeContentId');
|
||||
|
||||
// TODO: FIx this it seems to be forcing two switches
|
||||
switchToTab(contentId);
|
||||
}
|
||||
|
||||
Meteor.subscribe('hangingprotocols');
|
||||
});
|
||||
|
||||
Template.lesionTracker.events({
|
||||
'click #tablist a[data-toggle="tab"]': function(e) {
|
||||
// If this tab is already active, do nothing
|
||||
var tabButton = $(e.currentTarget);
|
||||
var tabTitle = tabButton.parents('.tabTitle');
|
||||
if (tabTitle.hasClass('active')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, switch to the tab
|
||||
var contentId = tabButton.data('target').replace('#', '');
|
||||
switchToTab(contentId);
|
||||
},
|
||||
'click #loadStudyList': function() {
|
||||
switchToTab('worklistTab');
|
||||
}
|
||||
});
|
||||
|
||||
Session.set('defaultSignInMessage', 'Tumor tracking in your browser.');
|
||||
|
||||
Template.lesionTracker.helpers({
|
||||
showWorklistMenu: function() {
|
||||
return Template.instance().showWorklistMenu.get();
|
||||
},
|
||||
onStudyList: function() {
|
||||
return (Session.get('activeContentId') === 'worklistTab');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,47 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.topBar
|
||||
height: $topBarHeight
|
||||
color: $textPrimaryColor
|
||||
background-color: $primaryBackgroundColor
|
||||
|
||||
.brandSection
|
||||
height: 100%
|
||||
display: inline-block
|
||||
|
||||
img.logoImage
|
||||
height: 100%
|
||||
margin: 0 5px
|
||||
|
||||
.logoText
|
||||
display: inline-block
|
||||
font-family: $logoFontFamily
|
||||
font-weight: $logoFontWeight
|
||||
color: $textPrimaryColor
|
||||
|
||||
.studyListLinkSection
|
||||
border-left: $uiBorderThickness solid $uiBorderColor;
|
||||
padding: 5px
|
||||
margin: 0 10px
|
||||
|
||||
a
|
||||
text-decoration: none
|
||||
cursor: pointer
|
||||
line-height: 100%
|
||||
|
||||
.userAccountSection
|
||||
margin: 0 10px
|
||||
|
||||
#worklistTabs
|
||||
height: "calc(100% - %s)" % $topBarHeight
|
||||
|
||||
// Override Viewerbase's borders
|
||||
#imageViewerViewports .viewportContainer
|
||||
border: none !important
|
||||
outline: 0 !important // Prevent blue outline in Chrome
|
||||
|
||||
&:hover
|
||||
&.active
|
||||
&:hover.active
|
||||
border: none !important
|
||||
outline: 0 !important // Prevent blue outline in Chrome
|
||||
@ -1,12 +1,3 @@
|
||||
<template name="lesionTrackerLayout">
|
||||
<div class="logoContainer">
|
||||
{{>userAccountMenu}}
|
||||
<a class="navbar-brand" href="http://ohif.org">
|
||||
<img src="/images/logo.png">
|
||||
<h4 class="name">Open Health Imaging Foundation</h4>
|
||||
</a>
|
||||
</div>
|
||||
{{>timeoutCountdownDialog}}
|
||||
{{>progressDialog}}
|
||||
{{>yield }}
|
||||
</template>
|
||||
@ -1,126 +0,0 @@
|
||||
Session.set('defaultSignInMessage', 'Tumor tracking in your browser.');
|
||||
|
||||
Template.lesionTrackerLayout.helpers({
|
||||
fullName: function() {
|
||||
return Meteor.user().profile.fullName;
|
||||
},
|
||||
|
||||
showWorklistMenu: function() {
|
||||
return Template.instance().showWorklistMenu.get();
|
||||
},
|
||||
|
||||
currentUser: function() {
|
||||
var verifyEmail = Meteor.settings && Meteor.settings.public && Meteor.settings.public.verifyEmail || false;
|
||||
|
||||
if (!Meteor.user() || !Meteor.userId()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!verifyEmail) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Meteor.user().emails[0].verified) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
Template.lesionTrackerLayout.onCreated(function() {
|
||||
// showViewer to go to viewer from audit
|
||||
this.showWorklistMenu = new ReactiveVar(true);
|
||||
// Get url and check worklist
|
||||
var currentRoute = Router.current();
|
||||
if (!currentRoute || !currentRoute.route) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentPath = currentRoute.route.path(this);
|
||||
if (currentPath === '/' || currentPath === '/worklist') {
|
||||
this.showWorklistMenu.set(false);
|
||||
}
|
||||
|
||||
// Show countdown dialog
|
||||
var handle;
|
||||
$(document).on('TriggerOpenTimeoutCountdownDialog', function(e, leftTime) {
|
||||
// Show modal dialog
|
||||
handle = setInterval(function() {
|
||||
leftTime--;
|
||||
// Set countdownDialogLeftTime session
|
||||
Session.set('countdownDialogLeftTime', leftTime);
|
||||
|
||||
// Show dialog
|
||||
var dialog = $('#timeoutCountdownDialog');
|
||||
if (dialog.css('display') === 'none') {
|
||||
dialog.css('display', 'block');
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
});
|
||||
|
||||
$(document).on('TriggerCloseTimeoutCountdownDialog', function(e) {
|
||||
var dialog = $('#timeoutCountdownDialog');
|
||||
dialog.css('display', 'none');
|
||||
if (handle) {
|
||||
clearInterval(handle);
|
||||
// Close the dialog
|
||||
dialog.css('display', 'none');
|
||||
|
||||
// Remove reviewers info for the user
|
||||
Meteor.call('removeUserFromReviewers', Meteor.userId());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Template.lesionTrackerLayout.onRendered(function() {
|
||||
var oldUserId = undefined;
|
||||
var userName;
|
||||
var lastLoginModalInterval;
|
||||
|
||||
Tracker.autorun(function() {
|
||||
// Hook login/logout
|
||||
var newUserId = Meteor.userId();
|
||||
if (oldUserId === null && newUserId) {
|
||||
Session.set('showLastLoginModal', true);
|
||||
userName = Meteor.user().profile.fullName;
|
||||
// Log
|
||||
HipaaLogger.logEvent({
|
||||
eventType: 'init',
|
||||
userId: Meteor.userId(),
|
||||
userName: userName
|
||||
});
|
||||
} else if (newUserId === null && oldUserId) {
|
||||
// Set showLastLoginModal as null
|
||||
Session.set('showLastLoginModal', null);
|
||||
// Destroy interval for last login modal
|
||||
Meteor.clearInterval(lastLoginModalInterval);
|
||||
console.log('The user logged out');
|
||||
|
||||
// Log
|
||||
// TODO: eventype is not defined for logout in hipaa-audit-log
|
||||
/*HipaaLogger.logEvent({
|
||||
eventType: 'logout',
|
||||
userId: oldUserId,
|
||||
userName: userName
|
||||
});*/
|
||||
|
||||
// Remove the user from Reviewers
|
||||
Meteor.call('removeUserFromReviewers', oldUserId);
|
||||
}
|
||||
oldUserId = Meteor.userId();
|
||||
|
||||
// Trigger last login date popup
|
||||
if (Session.get('showLastLoginModal')) {
|
||||
Modal.show('lastLoginModal');
|
||||
lastLoginModalInterval = Meteor.setInterval( function() {
|
||||
Modal.hide('lastLoginModal');
|
||||
Session.set("showLastLoginModal", null);
|
||||
}, 3000);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
.logoContainer
|
||||
position: absolute
|
||||
height: 40px
|
||||
top: 0
|
||||
right: 0
|
||||
padding: 0 10px
|
||||
color: white
|
||||
@media only screen and (min-device-width : 375px) and (max-device-width : 667px) and (orientation : portrait)
|
||||
width: 15%
|
||||
|
||||
.navbar-brand
|
||||
display: inline-block
|
||||
padding: 0
|
||||
margin-right: 5px
|
||||
height: 100%
|
||||
|
||||
img
|
||||
height: 30px
|
||||
float: left
|
||||
margin-right: 10px
|
||||
margin-top: 5px
|
||||
/* Media queries for iPhone6*/
|
||||
@media only screen and (min-device-width : 375px) and (max-device-width : 667px) and (orientation : portrait)
|
||||
height: 20px
|
||||
margin-top: 10px
|
||||
margin-bottom: 10px
|
||||
margin-right: 5px
|
||||
|
||||
h4.name
|
||||
float: left
|
||||
margin: 10px 0
|
||||
font-family: "Sanchez"
|
||||
color: white
|
||||
@media only screen and (min-device-width : 375px) and (max-device-width : 667px) and (orientation : portrait)
|
||||
font-size: 0.3em
|
||||
font-size: 0.3em
|
||||
width: 50%
|
||||
|
||||
@ -1,3 +1,102 @@
|
||||
@import "{design}/app"
|
||||
|
||||
$viewportTagPadding = 20px
|
||||
$imageSliderBorderRadius = 57px
|
||||
$imageSliderColor = #22374d
|
||||
$imageSliderTrackColor = rgba(0,0,0,0)
|
||||
$imageSliderBorder = none
|
||||
|
||||
// Note that these are backwards due to the magic needed to make a cross-browser vertical slider
|
||||
$imageSliderWidth = 12px
|
||||
$imageSliderHeight = 39px
|
||||
$imageSliderCursor = grab
|
||||
|
||||
.imageViewerViewportOverlay
|
||||
.timepointName
|
||||
color: #32BFFF
|
||||
color: $textSecondaryColor
|
||||
|
||||
.dicomTag
|
||||
font-weight: 200
|
||||
|
||||
.topleft
|
||||
top: $viewportTagPadding
|
||||
left: $viewportTagPadding
|
||||
|
||||
.topright
|
||||
top: $viewportTagPadding
|
||||
right: $viewportTagPadding
|
||||
text-align: right
|
||||
|
||||
.bottomleft
|
||||
bottom: $viewportTagPadding
|
||||
left: $viewportTagPadding
|
||||
|
||||
.bottomright
|
||||
bottom: $viewportTagPadding
|
||||
right: $viewportTagPadding
|
||||
text-align: right
|
||||
|
||||
.imageControls
|
||||
#scrollbar
|
||||
#imageSlider
|
||||
-webkit-appearance: none
|
||||
background-color: $imageSliderTrackColor
|
||||
|
||||
// --- Style the range track --- //
|
||||
&::-webkit-slider-runnable-track
|
||||
height: 5px
|
||||
border: none
|
||||
cursor: pointer
|
||||
background-color: $imageSliderTrackColor
|
||||
z-index: 6
|
||||
|
||||
&::-moz-range-track
|
||||
height: 2px
|
||||
border: none
|
||||
cursor: pointer
|
||||
background-color: $imageSliderTrackColor
|
||||
z-index: 6
|
||||
|
||||
&::-ms-track
|
||||
width: 100%
|
||||
height: 2px
|
||||
border: none
|
||||
cursor: pointer
|
||||
animate: 0.2s
|
||||
background: transparent
|
||||
border-width: 15px 0
|
||||
color: $imageSliderTrackColor
|
||||
|
||||
// Hide any fill IE tries to add
|
||||
&::-ms-fill-lower
|
||||
background: $imageSliderTrackColor
|
||||
|
||||
&::-ms-fill-upper
|
||||
background: $imageSliderTrackColor
|
||||
|
||||
// --- Style the range thumb --- //
|
||||
&::-webkit-slider-thumb
|
||||
-webkit-appearance: none !important
|
||||
background-color: $imageSliderColor
|
||||
border-radius: $imageSliderBorderRadius
|
||||
border: $imageSliderBorder
|
||||
height: $imageSliderWidth
|
||||
width: $imageSliderHeight
|
||||
cursor: $imageSliderCursor
|
||||
margin-top: -7px
|
||||
|
||||
&::-moz-range-thumb
|
||||
background-color: $imageSliderColor
|
||||
border-radius: $imageSliderBorderRadius
|
||||
border: $imageSliderBorder
|
||||
height: $imageSliderWidth
|
||||
width: $imageSliderHeight
|
||||
cursor: $imageSliderCursor
|
||||
z-index: 7
|
||||
|
||||
&::-ms-thumb
|
||||
background-color: $imageSliderColor
|
||||
border-radius: $imageSliderBorderRadius
|
||||
border: $imageSliderBorder
|
||||
height: $imageSliderWidth
|
||||
width: $imageSliderHeight
|
||||
cursor: $imageSliderCursor
|
||||
|
||||
@ -45,7 +45,10 @@ Template[defaultTemplate].helpers({
|
||||
return;
|
||||
}
|
||||
|
||||
var timepointReviewers = Reviewers.findOne({timepointId: timepoint.timepointId});
|
||||
var timepointReviewers = Reviewers.findOne({
|
||||
timepointId: timepoint.timepointId
|
||||
});
|
||||
|
||||
if (!timepointReviewers) {
|
||||
return;
|
||||
}
|
||||
@ -59,7 +62,7 @@ function getReviewerTipText(reviewers) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newReviewers = reviewers.filter(function ( reviewer ) {
|
||||
var newReviewers = reviewers.filter(function(reviewer) {
|
||||
return reviewer.userId !== Meteor.userId();
|
||||
});
|
||||
|
||||
@ -76,6 +79,7 @@ function getReviewerTipText(reviewers) {
|
||||
if (index > 0) {
|
||||
tipText += ',';
|
||||
}
|
||||
|
||||
tipText += reviewer.userName;
|
||||
|
||||
});
|
||||
@ -96,4 +100,4 @@ WorklistTabs.find().observe({
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
<template name="radialProgressBar">
|
||||
<div class="radialProgress">
|
||||
<svg id="svg"
|
||||
width="46"
|
||||
height="46"
|
||||
viewPort="0 0 50 50"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<!-- stroke-dasharray is 2 * PI * radius !-->
|
||||
<circle r="13"
|
||||
cx="23"
|
||||
cy="23"
|
||||
fill="transparent"
|
||||
stroke-dasharray="81.64"
|
||||
stroke-dashoffset="0">
|
||||
</circle>
|
||||
<circle id="bar"
|
||||
r="13"
|
||||
cx="23"
|
||||
cy="23"
|
||||
fill="transparent"
|
||||
stroke-dasharray="81.64"
|
||||
style="stroke-dashoffset: {{progressRadius}}px;">
|
||||
</circle>
|
||||
</svg>
|
||||
<div class="progressText">
|
||||
{{ #if isLocked }}
|
||||
<svg>
|
||||
<use xlink:href=/packages/lesiontracker/assets/icons.svg#icon-status-lock></use>
|
||||
</svg>
|
||||
{{ else }}
|
||||
{{ #if progressComplete }}
|
||||
<svg>
|
||||
<use xlink:href=/packages/lesiontracker/assets/icons.svg#icon-status-complete></use>
|
||||
</svg>
|
||||
{{ else }}
|
||||
{{progressText}}
|
||||
{{ /if }}
|
||||
{{ /if }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,13 @@
|
||||
Template.radialProgressBar.helpers({
|
||||
progressComplete() {
|
||||
const instance = Template.instance();
|
||||
return instance.data.progressPercent === 100;
|
||||
},
|
||||
|
||||
progressRadius() {
|
||||
var radius = 13 * 2 * Math.PI;
|
||||
const instance = Template.instance();
|
||||
var percentLeft = (100 - instance.data.progressPercent) / 100;
|
||||
return percentLeft * radius;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,41 @@
|
||||
@import "{design}/app"
|
||||
|
||||
$circleSize = 46px
|
||||
$progressTextColor = #6fbde2
|
||||
|
||||
.radialProgress
|
||||
height: $circleSize
|
||||
width: $circleSize
|
||||
box-shadow: 0 0 1em black
|
||||
border-radius: 100%
|
||||
position: relative
|
||||
|
||||
#svg circle
|
||||
stroke-dashoffset: 0
|
||||
transition: stroke-dashoffset 1s linear
|
||||
stroke: #203244
|
||||
stroke-width: 3px
|
||||
|
||||
#svg #bar
|
||||
stroke: $activeColor
|
||||
-ms-transform: rotate(270deg); /* IE 9 */
|
||||
-ms-transform-origin: center center; /* IE 9 */
|
||||
-webkit-transform: rotate(270deg); /* Chrome, Safari, Opera */
|
||||
-webkit-transform-origin: center center; /* Chrome, Safari, Opera */
|
||||
transform: rotate(270deg);
|
||||
transform-origin: center center;
|
||||
|
||||
.progressText
|
||||
position: absolute;
|
||||
width: $circleSize;
|
||||
height: $circleSize;
|
||||
line-height: $circleSize;
|
||||
font-family: Roboto-Bold;
|
||||
font-size: 17px;
|
||||
text-align: center;
|
||||
color: $progressTextColor;
|
||||
top: 0;
|
||||
|
||||
svg
|
||||
width: 17px
|
||||
height: 17px
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
function parseUrl(url) {
|
||||
var parser = document.createElement('a');
|
||||
parser.href = url;
|
||||
@ -11,14 +10,19 @@ Template.serverInformationModal.helpers({
|
||||
var serviceInfo = Meteor.settings[defaultServiceType];
|
||||
if (defaultServiceType === 'dicomWeb') {
|
||||
var serverInformationDicom = [];
|
||||
var endpoints = serviceInfo["endpoints"];
|
||||
var endpoints = serviceInfo['endpoints'];
|
||||
endpoints.forEach(function(endpoint) {
|
||||
var parsedUrl = parseUrl(endpoint.qidoRoot);
|
||||
serverInformationDicom.push({host: parsedUrl.hostname, port: parsedUrl.port, aeTitle: endpoint.name});
|
||||
serverInformationDicom.push({
|
||||
host: parsedUrl.hostname,
|
||||
port: parsedUrl.port,
|
||||
aeTitle: endpoint.name
|
||||
});
|
||||
});
|
||||
|
||||
return serverInformationDicom;
|
||||
}
|
||||
|
||||
return serviceInfo;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
<template name="studySeriesQuickSwitch">
|
||||
<div class="quickSwitch {{side}}">
|
||||
<div class="switchSection {{#if equals side "left"}}pull-right{{/if}} js-show-studies">
|
||||
<div class="label">Study</div>
|
||||
<div class="studySwitch">
|
||||
</div>
|
||||
</div>
|
||||
<div class="switchSection {{#if equals side "left"}}pull-right{{/if}}">
|
||||
<div class="label">Series</div>
|
||||
<div class="seriesSwitch {{#if equals side "left"}}mirror-x{{/if}} js-show-series">
|
||||
<svg viewBox="0 0 53 53">
|
||||
<rect fill="#22364C" x="0" y="38" width="15" height="15" rx="3"></rect>
|
||||
<rect fill="#22364C" x="19" y="38" width="15" height="15" rx="3"></rect>
|
||||
<rect fill="#22364C" x="38" y="19" width="15" height="15" rx="3"></rect>
|
||||
<rect fill="#22364C" x="19" y="19" width="15" height="15" rx="3"></rect>
|
||||
<rect fill="#40D9B8" x="0" y="19" width="15" height="15" rx="3"></rect>
|
||||
<rect fill="#22364C" x="38" y="0" width="15" height="15" rx="3"></rect>
|
||||
<rect fill="#22364C" x="19" y="0" width="15" height="15" rx="3"></rect>
|
||||
<rect fill="#22364C" x="0" y="0" width="15" height="15" rx="3"></rect>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="studyHover js-hide-studies {{ #unless studiesOpen}}hidden{{/unless}}">
|
||||
</div>
|
||||
<div class="seriesHover js-hide-series {{ #unless seriesOpen }}tiny{{/unless}}">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,34 @@
|
||||
Template.studySeriesQuickSwitch.onCreated(function studySeriesQuickSwitchOnCreated() {
|
||||
const instance = this;
|
||||
|
||||
instance.seriesOpen = new ReactiveVar(false);
|
||||
instance.studiesOpen = new ReactiveVar(false);
|
||||
});
|
||||
|
||||
Template.studySeriesQuickSwitch.events({
|
||||
'mouseenter .js-show-series'(event, instance) {
|
||||
instance.seriesOpen.set(true);
|
||||
},
|
||||
|
||||
'mouseenter .js-show-studies'(event, instance) {
|
||||
instance.studiesOpen.set(true);
|
||||
},
|
||||
|
||||
'mouseleave .js-hide-series'(event, instance) {
|
||||
instance.seriesOpen.set(false);
|
||||
},
|
||||
|
||||
'mouseleave .js-hide-studies'(event, instance) {
|
||||
instance.studiesOpen.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
Template.studySeriesQuickSwitch.helpers({
|
||||
seriesOpen() {
|
||||
return Template.instance().seriesOpen.get();
|
||||
},
|
||||
|
||||
studiesOpen() {
|
||||
return Template.instance().studiesOpen.get();
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,68 @@
|
||||
@import "{design}/app"
|
||||
|
||||
$switchSize = 50px
|
||||
|
||||
.quickSwitch
|
||||
position: absolute
|
||||
width: 140px
|
||||
height: 80px
|
||||
top: 5px
|
||||
|
||||
div.switchSection
|
||||
float: left
|
||||
display: inline-block
|
||||
margin: 0 5px
|
||||
|
||||
.label
|
||||
display: block
|
||||
text-transform: uppercase
|
||||
text-align: center
|
||||
font-size: 12px
|
||||
font-weight: 100
|
||||
color: $textSecondaryColor
|
||||
|
||||
.studySwitch
|
||||
display: block
|
||||
margin: 0 auto
|
||||
width: $switchSize
|
||||
height: $switchSize
|
||||
border-radius: 11px
|
||||
background-color: $darkUiColor
|
||||
border: solid 1px $activeColor
|
||||
|
||||
.seriesSwitch
|
||||
display: block
|
||||
width: $switchSize
|
||||
height: $switchSize
|
||||
|
||||
svg
|
||||
width: $switchSize
|
||||
height: $switchSize
|
||||
|
||||
.mirror-x
|
||||
transform(scaleX(-1));
|
||||
|
||||
.studyHover
|
||||
position: absolute
|
||||
z-index: 10000
|
||||
top: 70px
|
||||
left: -350px
|
||||
width: 400px
|
||||
height: 500px
|
||||
background: red
|
||||
transition(all 0.3s ease)
|
||||
|
||||
.seriesHover
|
||||
position: absolute
|
||||
z-index: 10000
|
||||
top: 70px
|
||||
left: 50px
|
||||
width: 700px
|
||||
height: 900px
|
||||
background: blue
|
||||
transition(all 0.3s ease)
|
||||
|
||||
&.tiny
|
||||
width: 0
|
||||
height: 0
|
||||
opacity: 0
|
||||
@ -0,0 +1,9 @@
|
||||
<template name="studyTimepoint">
|
||||
<div class="studyTimepointWrapper">
|
||||
<div class="studyTimepoint">
|
||||
{{#each study in (studies this.timepoint)}}
|
||||
{{>studyTimepointStudy study=study}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,58 @@
|
||||
// Initialize the timepoint wrapper max-height to enable CSS transition
|
||||
Template.studyTimepoint.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
const $studies = instance.$('.studyTimepoint');
|
||||
const $wrapper = $studies.closest('.studyTimepointWrapper');
|
||||
const $timepoint = $wrapper.closest('.timepointEntry');
|
||||
const studiesVisible = $studies.is(':visible');
|
||||
|
||||
if (!studiesVisible) {
|
||||
$timepoint.addClass('active');
|
||||
}
|
||||
|
||||
// Recalculates the timepoint height to make CSS transition smoother
|
||||
$studies.trigger('displayStateChanged');
|
||||
|
||||
if (!studiesVisible) {
|
||||
$timepoint.removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
Template.studyTimepoint.events({
|
||||
// Changes the selected study
|
||||
'selectionChanged .studyTimepoint'(event, instance, selection) {
|
||||
const $selection = $(selection);
|
||||
const $thumbnails = $selection.find('.studyTimepointThumbnails');
|
||||
const $timepoint = instance.$('.studyTimepoint');
|
||||
|
||||
// Set the max-height to inherit to be able to expand the wrapper on its full height
|
||||
instance.$('.studyTimepointWrapper').css('max-height', 'inherit');
|
||||
|
||||
// Removes selected state from all studies but the triggered study
|
||||
instance.$('.studyTimepointStudy').not(selection).removeClass('active');
|
||||
|
||||
// Toggle selected state for the triggered study
|
||||
$selection.toggleClass('active');
|
||||
|
||||
// Recalculates the timepoint height to make CSS transition smoother
|
||||
$thumbnails.one('transitionend', () => $timepoint.trigger('displayStateChanged'));
|
||||
},
|
||||
// It should be triggered when the timepoint height is changed
|
||||
'displayStateChanged .studyTimepoint'(event, instance) {
|
||||
const $timepoint = $(event.currentTarget);
|
||||
const $wrapper = $timepoint.closest('.studyTimepointWrapper');
|
||||
|
||||
// Set the max-height for the wrapper to make CSS transition smoother
|
||||
$wrapper.css('max-height', $timepoint.height());
|
||||
}
|
||||
});
|
||||
|
||||
Template.studyTimepoint.helpers({
|
||||
studies: function(timepoint) {
|
||||
const query = {
|
||||
selected: true
|
||||
};
|
||||
return ViewerStudies.find(query);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.studyTimepointWrapper
|
||||
overflow: hidden
|
||||
transition($sidebarTransition)
|
||||
|
||||
.timepointEntry:not(.active) .studyTimepointWrapper
|
||||
max-height: 0 !important
|
||||
@ -0,0 +1,29 @@
|
||||
<template name="studyTimepointBrowser">
|
||||
<div class="studyTimepointBrowser">
|
||||
<div class="studyTimepointScrollArea">
|
||||
<div class="p-x-1">
|
||||
{{#each timepoint in timepoints}}
|
||||
{{#if shouldShowTimepoint timepoint @index}}
|
||||
<div class="timepointEntry p-y-2 active">
|
||||
<div class="timepointHeader">
|
||||
<div class="timepointDetails clearfix">
|
||||
<div class="timepointFollowupTitle pull-left">
|
||||
{{timepointTitle timepoint timepoints.count @index}}
|
||||
</div>
|
||||
<div class="expandIcon pull-right">
|
||||
<i class="fa fa-chevron-down"></i>
|
||||
</div>
|
||||
<div class="timepointDate pull-right m-r-1">{{formatDA timepoint.earliestDate 'D-MMM-YYYY'}}</div>
|
||||
</div>
|
||||
<!-- TODO: [design] find out where to get this information -->
|
||||
<div class="timepointModalities">2 MR, 2 CT</div>
|
||||
</div>
|
||||
{{>studyTimepoint timepoint=timepoint index=@index}}
|
||||
</div>
|
||||
<hr class="m-y-1">
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,46 @@
|
||||
Template.studyTimepointBrowser.events({
|
||||
'click .timepointHeader'(event, instance) {
|
||||
const $timepoint = $(event.currentTarget).closest('.timepointEntry');
|
||||
|
||||
// Recalculates the timepoint height to make CSS transition smoother
|
||||
$timepoint.find('.studyTimepoint').trigger('displayStateChanged');
|
||||
|
||||
// Toggle active class to group/ungroup timepoint studies
|
||||
$timepoint.toggleClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
Template.studyTimepointBrowser.helpers({
|
||||
timepoints: function() {
|
||||
const sort = {
|
||||
sort: {
|
||||
earliestDate: -1
|
||||
}
|
||||
};
|
||||
return Timepoints.find({}, sort);
|
||||
},
|
||||
shouldShowTimepoint(timepoint, index) {
|
||||
const instance = Template.instance();
|
||||
if (instance.data.timepointViewType.get() === 'all') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return index < 4 || timepoint.timepointType === 'baseline';
|
||||
},
|
||||
// Build the timepoint title based on its date
|
||||
timepointTitle(timepoint, total, index) {
|
||||
if (timepoint.timepointType === 'baseline') {
|
||||
return 'Baseline';
|
||||
}
|
||||
|
||||
const states = {
|
||||
0: '(Current)',
|
||||
1: '(Prior)',
|
||||
2: '(Nadir)'
|
||||
};
|
||||
// TODO: [design] find out how to define the nadir timepoint
|
||||
const followUp = total - index - 1;
|
||||
const parenthesis = states[index] || '';
|
||||
return `Follow-up ${followUp} ${parenthesis}`;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,68 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.studyTimepointBrowser
|
||||
background-color: $primaryBackgroundColor
|
||||
float: left
|
||||
height: calc(100% - 55px - 50px - 30px)
|
||||
overflow: hidden
|
||||
width: 100%
|
||||
|
||||
.timepointEntry
|
||||
&.active
|
||||
.timepointModalities
|
||||
height: 0
|
||||
.timepointDate
|
||||
display: none
|
||||
.expandIcon i
|
||||
transform: rotateX(180deg)
|
||||
.timepointFollowupTitle
|
||||
font-size: 14px
|
||||
color: $textSecondaryColor
|
||||
text-transform: uppercase
|
||||
|
||||
.timepointHeader
|
||||
cursor: pointer
|
||||
font-size: 13px
|
||||
|
||||
&
|
||||
.expandIcon i
|
||||
.timepointFollowupTitle
|
||||
.timepointDate
|
||||
.timepointModalities
|
||||
color: $textSecondaryColor
|
||||
transition($sidebarTransition)
|
||||
|
||||
&:hover
|
||||
&
|
||||
.timepointFollowupTitle
|
||||
.expandIcon i
|
||||
.timepointDate
|
||||
.timepointModalities
|
||||
color: $textPrimaryColor !important
|
||||
|
||||
.timepointDetails
|
||||
.timepointModalities
|
||||
line-height: 25px
|
||||
|
||||
.timepointModalities
|
||||
height: 25px
|
||||
overflow: hidden
|
||||
|
||||
.expandIcon i
|
||||
color: $defaultColor
|
||||
|
||||
.timepointFollowupTitle
|
||||
color: $textPrimaryColor
|
||||
font-family: Roboto
|
||||
padding-top: 2px
|
||||
|
||||
.studyTimepointScrollArea
|
||||
height: 100%
|
||||
overflow-x: hidden
|
||||
overflow-y: auto
|
||||
margin-right: -16px
|
||||
padding-bottom: 20px
|
||||
padding-right: 16px
|
||||
|
||||
&::-webkit-scrollbar
|
||||
display: none
|
||||
@ -0,0 +1,18 @@
|
||||
<template name="studyTimepointStudy">
|
||||
<div class="studyTimepointStudy">
|
||||
<div class="studyModality">
|
||||
<div class="studyModalityBox">
|
||||
{{this.study.modalities}}
|
||||
</div>
|
||||
<div class="studyModalityText">
|
||||
<div class="studyModalityDate">{{formatDA this.study.studyDate 'D-MMM-YYYY'}}</div>
|
||||
<div class="studyModalityDescription">{{this.study.studyDescription}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="studyTimepointThumbnails">
|
||||
{{#each thumbnail in (thumbnails this.study)}}
|
||||
{{>thumbnailEntry thumbnail}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,40 @@
|
||||
// Initialize the study wrapper max-height to enable CSS transition
|
||||
Template.studyTimepointStudy.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
const $study = instance.$('.studyTimepointStudy');
|
||||
const $thumbnails = instance.$('.studyTimepointThumbnails');
|
||||
$study.addClass('active');
|
||||
$thumbnails.css('max-height', $thumbnails.height());
|
||||
$study.removeClass('active');
|
||||
});
|
||||
|
||||
Template.studyTimepointStudy.events({
|
||||
// Recalculates the timepoint height to make CSS transition smoother
|
||||
'transitionend .studyTimepointThumbnails'(event, instance) {
|
||||
if (event.target === event.currentTarget) {
|
||||
$(event.currentTarget).closest('.studyTimepoint').trigger('displayStateChanged');
|
||||
}
|
||||
},
|
||||
// Changes the current study selection for the clicked study
|
||||
'click .studyModality'(event, instance) {
|
||||
const $study = $(event.currentTarget).closest('.studyTimepointStudy');
|
||||
const $timepoint = $study.closest('.studyTimepoint');
|
||||
const study = $study[0];
|
||||
$timepoint.trigger('selectionChanged', [study]);
|
||||
}
|
||||
});
|
||||
|
||||
Template.studyTimepointStudy.helpers({
|
||||
thumbnails: function(study) {
|
||||
var stacks = createStacks(study);
|
||||
|
||||
var array = [];
|
||||
stacks.forEach(function(stack, index) {
|
||||
array.push({
|
||||
thumbnailIndex: index,
|
||||
stack: stack
|
||||
});
|
||||
});
|
||||
return array;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,99 @@
|
||||
@import "{design}/app"
|
||||
|
||||
$boxBackgroundColor = #3E5975
|
||||
$boxBorderColor = transparent
|
||||
$boxHoverBackgroundColor = #14191E
|
||||
$boxHoverBorderColor = #2d4660
|
||||
$boxActiveBackgroundColor = $activeColor
|
||||
$boxActiveTextColor = $primaryBackgroundColor
|
||||
$boxActiveBorderColor = #131D29
|
||||
$boxWidth = 50px
|
||||
$borderThickness = 2px
|
||||
$boxBorderRadius = 8px
|
||||
$nestingMargin = 6px
|
||||
$spacerX = 7px
|
||||
$spacerY = 12px
|
||||
|
||||
.studyTimepointStudy
|
||||
|
||||
&.active
|
||||
.studyModality
|
||||
box-shadow: inset 0 0 0 3px $activeColor
|
||||
.studyModalityBox
|
||||
color: $boxActiveTextColor
|
||||
&
|
||||
&:before
|
||||
&:after
|
||||
background-color: $boxActiveBackgroundColor
|
||||
border-color: $boxActiveBorderColor
|
||||
|
||||
&:not(.active) .studyTimepointThumbnails
|
||||
max-height: 0 !important
|
||||
|
||||
.studyTimepointThumbnails
|
||||
overflow: hidden
|
||||
transition($sidebarTransition)
|
||||
|
||||
.studyModality
|
||||
border: 1px solid $boxBorderColor
|
||||
border-radius: 12px
|
||||
cursor: pointer
|
||||
font-family: Roboto
|
||||
padding: $spacerY $spacerX ($spacerY - 1)
|
||||
position: relative
|
||||
transition($sidebarTransition)
|
||||
z-index: 1
|
||||
|
||||
&:hover
|
||||
background-color: $boxHoverBackgroundColor
|
||||
border-color: $boxHoverBorderColor
|
||||
|
||||
.studyModalityText
|
||||
font-size: 13px
|
||||
left: ($spacerX * 3) + $boxWidth + ($nestingMargin * 3)
|
||||
line-height: 14px
|
||||
position: absolute
|
||||
right: $spacerX
|
||||
top: $spacerY
|
||||
.studyModalityDate
|
||||
margin-top: 8px
|
||||
color: $textSecondaryColor
|
||||
.studyModalityDescription
|
||||
margin-top: 8px
|
||||
color: $textPrimaryColor
|
||||
|
||||
.studyModalityBox
|
||||
color: $textSecondaryColor
|
||||
font-size: 22px
|
||||
line-height: $boxWidth
|
||||
margin-left: $nestingMargin * 2
|
||||
margin-top: $nestingMargin * 2
|
||||
position: relative
|
||||
text-align: center
|
||||
text-transform: uppercase
|
||||
|
||||
&
|
||||
&:before
|
||||
&:after
|
||||
background-color: $boxBackgroundColor
|
||||
border: $borderThickness solid $primaryBackgroundColor
|
||||
border-radius: $boxBorderRadius
|
||||
height: $boxWidth + ($borderThickness * 2)
|
||||
transition($sidebarTransition)
|
||||
width: $boxWidth + ($borderThickness * 2)
|
||||
|
||||
&:before
|
||||
&:after
|
||||
display: block
|
||||
content: ''
|
||||
position: absolute
|
||||
|
||||
&:before
|
||||
top: -($borderThickness + $nestingMargin)
|
||||
left: -($borderThickness + $nestingMargin)
|
||||
z-index: -1
|
||||
|
||||
&:after
|
||||
top: -($borderThickness + ($nestingMargin * 2))
|
||||
left: -($borderThickness + ($nestingMargin * 2))
|
||||
z-index: -2
|
||||
@ -1,10 +1,10 @@
|
||||
<template name="timeoutCountdownDialog">
|
||||
<div id="timeoutCountdownDialog">
|
||||
<div class="dialogHeader">
|
||||
<h5>Session is about to expire!</h5>
|
||||
<h5>Your session is about to expire.</h5>
|
||||
</div>
|
||||
<div class="dialogContent">
|
||||
<p>You will be log out in {{leftTime}} {{secondsText}}.</p>
|
||||
<p>You will be logged out in {{timeLeft}}.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,9 +1,39 @@
|
||||
|
||||
Template.timeoutCountdownDialog.helpers({
|
||||
leftTime: function(e) {
|
||||
return Session.get("countdownDialogLeftTime");
|
||||
},
|
||||
secondsText: function() {
|
||||
return Session.get("countdownDialogLeftTime") > 1? "seconds": "second";
|
||||
timeLeft: function() {
|
||||
var timeLeft = Session.get('countdownDialogLeftTime');
|
||||
var suffix = timeLeft > 1 ? 'seconds': 'second';
|
||||
return timeLeft + suffix;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Template.timeoutCountdownDialog.onRendered(function() {
|
||||
// Show countdown dialog
|
||||
var timeoutCountdownInterval;
|
||||
$(document).on('TriggerOpenTimeoutCountdownDialog', function(e, leftTime) {
|
||||
// Show modal dialog
|
||||
timeoutCountdownInterval = setInterval(function() {
|
||||
// Set countdownDialogLeftTime session
|
||||
Session.set('countdownDialogLeftTime', --leftTime);
|
||||
|
||||
var dialog = $('#timeoutCountdownDialog');
|
||||
|
||||
// Show dialog
|
||||
dialog.css('display', 'block');
|
||||
}, 1000);
|
||||
|
||||
});
|
||||
|
||||
$(document).on('TriggerCloseTimeoutCountdownDialog', function() {
|
||||
var dialog = $('#timeoutCountdownDialog');
|
||||
|
||||
// Close the dialog
|
||||
dialog.css('display', 'none');
|
||||
|
||||
if (timeoutCountdownInterval) {
|
||||
clearInterval(timeoutCountdownInterval);
|
||||
|
||||
// Remove reviewer info for the user
|
||||
Meteor.call('removeUserFromReviewers', Meteor.userId());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
<template name="toolbarSection">
|
||||
<div class="toolbarSection">
|
||||
<div id="studySidebarToggle" class="pull-left toolbarSectionButton">
|
||||
<div class="capsule svgContainer">
|
||||
<svg>
|
||||
<use xlink:href="/packages/lesiontracker/assets/icons.svg#icon-studies"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
Studies
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
{{ #each toolbarButtons }}
|
||||
{{ >toolbarSectionButton }}
|
||||
{{ /each }}
|
||||
|
||||
<div id="moreTools" class="toolbarSectionButton">
|
||||
<div class="svgContainer">
|
||||
<svg>
|
||||
<use xlink:href="/packages/lesiontracker/assets/icons.svg#icon-tools-more"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
More <i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ >studySeriesQuickSwitch side="left"}}
|
||||
{{ >studySeriesQuickSwitch side="right"}}
|
||||
|
||||
<div class="pull-right toolbarSectionEntry">
|
||||
{{ >caseProgress }}
|
||||
</div>
|
||||
<div class="capsule-group pull-right">
|
||||
<div id="lesionSidebarToggle" class="capsule-entry">
|
||||
<div class="svgContainer capsule">
|
||||
<svg>
|
||||
<use xlink:href="/packages/lesiontracker/assets/icons.svg#icon-measurements-lesions"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
Lesions
|
||||
</div>
|
||||
</div>
|
||||
<div id="additionalMeasurementsSidebarToggle" class="capsule-entry">
|
||||
<div class="svgContainer capsule">
|
||||
<svg>
|
||||
<use xlink:href="/packages/lesiontracker/assets/icons.svg#icon-measurements-additional"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
Additional
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right toolbarSectionButton">
|
||||
<div class="svgContainer">
|
||||
<svg>
|
||||
<use xlink:href="/packages/lesiontracker/assets/icons.svg#icon-hud"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
HUD
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right toolbarSectionButton">
|
||||
<div class="svgContainer">
|
||||
<svg>
|
||||
<use xlink:href="/packages/lesiontracker/assets/icons.svg#icon-trial-info"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
Trial
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,133 @@
|
||||
Template.toolbarSection.onRendered(function() {
|
||||
var instance = this;
|
||||
|
||||
instance.state = instance.data.state;
|
||||
});
|
||||
|
||||
Template.toolbarSection.helpers({
|
||||
toolbarButtons: function() {
|
||||
var buttonData = [];
|
||||
buttonData.push({
|
||||
id: 'zoom',
|
||||
title: 'Zoom',
|
||||
classes: 'imageViewerTool',
|
||||
svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-zoom'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'wwwc',
|
||||
title: 'Levels',
|
||||
classes: 'imageViewerTool',
|
||||
svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-levels'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'pan',
|
||||
title: 'Pan',
|
||||
classes: 'imageViewerTool',
|
||||
svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-pan'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'link',
|
||||
title: 'Link',
|
||||
classes: 'imageViewerCommand toolbarSectionButton',
|
||||
svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-link'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'bidirectional',
|
||||
title: 'Target',
|
||||
classes: 'imageViewerTool',
|
||||
svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-measure-target'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'nonTarget',
|
||||
title: 'Non-Target',
|
||||
classes: 'imageViewerTool toolbarSectionButton',
|
||||
svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-measure-non-target'
|
||||
});
|
||||
|
||||
buttonData.push({
|
||||
id: 'length',
|
||||
title: 'Temp',
|
||||
classes: 'imageViewerTool toolbarSectionButton',
|
||||
svgLink: '/packages/lesiontracker/assets/icons.svg#icon-tools-measure-temp'
|
||||
});
|
||||
|
||||
return buttonData;
|
||||
}
|
||||
});
|
||||
|
||||
Template.toolbarSection.events({
|
||||
'click #studySidebarToggle': function(event, instance) {
|
||||
var isOpen = instance.data.state.get('studySidebarOpen');
|
||||
instance.data.state.set('studySidebarOpen', !isOpen);
|
||||
},
|
||||
'click #lesionSidebarToggle': function(event, instance) {
|
||||
var isOpen = instance.data.state.get('lesionSidebarOpen');
|
||||
instance.data.state.set('lesionSidebarOpen', !isOpen);
|
||||
instance.data.state.set('additionalMeasurementsSidebarOpen', false);
|
||||
},
|
||||
'click #additionalMeasurementsSidebarToggle': function(event, instance) {
|
||||
var isOpen = instance.data.state.get('additionalMeasurementsSidebarOpen');
|
||||
instance.data.state.set('additionalMeasurementsSidebarOpen', !isOpen);
|
||||
instance.data.state.set('lesionSidebarOpen', false);
|
||||
},
|
||||
// TODO: Inherit these from toolbar template somehow
|
||||
'click .imageViewerTool': function(e) {
|
||||
$(e.currentTarget).tooltip('hide');
|
||||
|
||||
var tool = e.currentTarget.id;
|
||||
|
||||
var elements = $('.imageViewerViewport');
|
||||
|
||||
var activeTool = toolManager.getActiveTool();
|
||||
$('.toolbarSectionButton').removeClass('active');
|
||||
if (tool === activeTool) {
|
||||
var defaultTool = toolManager.getDefaultTool();
|
||||
console.log('Setting active tool to: ' + defaultTool);
|
||||
toolManager.setActiveTool(defaultTool, elements);
|
||||
} else {
|
||||
console.log('Setting active tool to: ' + tool);
|
||||
toolManager.setActiveTool(tool, elements);
|
||||
}
|
||||
},
|
||||
'click .imageViewerCommand': function(e) {
|
||||
$(e.currentTarget).tooltip('hide');
|
||||
|
||||
var command = e.currentTarget.id;
|
||||
if (!OHIF.viewer.functionList.hasOwnProperty(command)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var activeViewport = Session.get('activeViewport');
|
||||
var element = $('.imageViewerViewport').get(activeViewport);
|
||||
OHIF.viewer.functionList[command](element);
|
||||
}
|
||||
});
|
||||
|
||||
Template.toolbarSection.onRendered(function() {
|
||||
var tooltipButtons = $('[data-toggle="tooltip"]');
|
||||
tooltipButtons.tooltip(OHIF.viewer.tooltipConfig);
|
||||
|
||||
// Enable tooltips for the layout button
|
||||
var extraTooltipButtons = $('[rel="tooltip"]');
|
||||
extraTooltipButtons.tooltip(OHIF.viewer.tooltipConfig);
|
||||
|
||||
// Set disabled/enabled tool buttons that are set in toolManager
|
||||
var states = toolManager.getToolDefaultStates();
|
||||
var disabledToolButtons = states.disabledToolButtons;
|
||||
var allToolbarButtons = $('#toolbar').find('button');
|
||||
if (disabledToolButtons && disabledToolButtons.length > 0) {
|
||||
for (var i = 0; i < allToolbarButtons.length; i++) {
|
||||
var toolbarButton = allToolbarButtons[i];
|
||||
$(toolbarButton).prop('disabled', false);
|
||||
var index = disabledToolButtons.indexOf($(toolbarButton).attr('id'));
|
||||
if (index !== -1) {
|
||||
$(toolbarButton).prop('disabled', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,151 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.toolbarSection
|
||||
height: $toolbarHeight
|
||||
width: 100%
|
||||
border-bottom: $uiBorderColor $uiBorderThickness solid
|
||||
|
||||
.capsule-group
|
||||
display: inline-block
|
||||
|
||||
div.capsule-entry
|
||||
display: inline-block
|
||||
color: $defaultColor
|
||||
fill: $defaultColor
|
||||
stroke: $defaultColor
|
||||
height: $toolbarHeight
|
||||
min-width: 30px
|
||||
cursor: pointer
|
||||
text-align: center
|
||||
margin: 0
|
||||
|
||||
div.capsule
|
||||
width: 66px
|
||||
height: 26px
|
||||
|
||||
&:first-child
|
||||
.capsule
|
||||
border-radius: 57px
|
||||
background-color: #16202b
|
||||
border: solid 1px #3c5d80
|
||||
border-right: none
|
||||
border-top-right-radius: 0
|
||||
border-bottom-right-radius: 0
|
||||
|
||||
&:last-child
|
||||
.capsule
|
||||
border-radius: 57px
|
||||
background-color: #16202b
|
||||
border: solid 1px #3c5d80
|
||||
border-left: none
|
||||
border-top-left-radius: 0
|
||||
border-bottom-left-radius: 0
|
||||
|
||||
&.disabled
|
||||
opacity: 0.5
|
||||
cursor: not-allowed
|
||||
|
||||
.svgContainer
|
||||
margin: 0 auto
|
||||
text-align: center
|
||||
|
||||
.capsule
|
||||
width: 62px
|
||||
height: 26px
|
||||
border-radius: 100px
|
||||
background-color: #16202b
|
||||
border: solid 1px #3c5d80
|
||||
|
||||
svg
|
||||
background-color: transparent
|
||||
margin: 5px
|
||||
width: 17px
|
||||
height: 17px
|
||||
|
||||
&:active, &.active
|
||||
color: $activeColor
|
||||
|
||||
svg
|
||||
fill: $activeColor
|
||||
stroke: $activeColor
|
||||
|
||||
&:hover
|
||||
color: $hoverColor
|
||||
|
||||
svg
|
||||
fill: $hoverColor
|
||||
stroke: $hoverColor
|
||||
|
||||
|
||||
.toolbarSectionButton
|
||||
display: inline-block
|
||||
color: $defaultColor
|
||||
fill: $defaultColor
|
||||
stroke: $defaultColor
|
||||
padding: 0 10px
|
||||
height: $toolbarHeight
|
||||
min-width: 30px
|
||||
cursor: pointer
|
||||
text-align: center
|
||||
|
||||
&.disabled
|
||||
opacity: 0.5
|
||||
cursor: not-allowed
|
||||
|
||||
.buttonLabel
|
||||
font-size: 12px
|
||||
|
||||
.svgContainer
|
||||
margin: 0 auto
|
||||
text-align: center
|
||||
|
||||
.capsule
|
||||
width: 62px
|
||||
height: 26px
|
||||
border-radius: 100px
|
||||
background-color: #16202b
|
||||
border: solid 1px #3c5d80
|
||||
|
||||
svg
|
||||
background-color: transparent
|
||||
margin: 2px
|
||||
width: 21px
|
||||
height: 21px
|
||||
|
||||
&:active, &.active
|
||||
color: $activeColor
|
||||
|
||||
svg
|
||||
fill: $activeColor
|
||||
stroke: $activeColor
|
||||
|
||||
&:hover
|
||||
color: $hoverColor
|
||||
|
||||
svg
|
||||
fill: $hoverColor
|
||||
stroke: $hoverColor
|
||||
|
||||
.toolbarSectionEntry
|
||||
display: inline-block
|
||||
color: $defaultColor
|
||||
fill: $defaultColor
|
||||
stroke: $defaultColor
|
||||
padding: 0 10px
|
||||
height: $toolbarHeight
|
||||
min-width: 30px
|
||||
cursor: pointer
|
||||
text-align: center
|
||||
|
||||
.quickSwitch
|
||||
position: absolute
|
||||
|
||||
&.middle
|
||||
left: calc(50% - 60px)
|
||||
|
||||
&.left
|
||||
left: calc(50% - 160px)
|
||||
|
||||
&.right
|
||||
left: 50%
|
||||
margin-left: 20px
|
||||
@ -0,0 +1,12 @@
|
||||
<template name="toolbarSectionButton">
|
||||
<div id="{{id}}" class="toolbarSectionButton {{classes}}" title="{{title}}">
|
||||
<div class="svgContainer">
|
||||
<svg>
|
||||
<use xlink:href={{svgLink}}></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="buttonLabel">
|
||||
{{title}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,38 +1,46 @@
|
||||
<template name="userAccountMenu">
|
||||
{{#if currentUser}}
|
||||
<div id="userAccountMenu" class="row-fluid">
|
||||
<div class="list-group panel">
|
||||
<a href="#userMenu" class="list-group-item" id="userName" data-toggle="collapse" data-parent="#userAccountMenu">{{fullName}} {{currentPath}}<span class="caret"></span></a>
|
||||
|
||||
<div class="collapse pos-absolute" id="userMenu">
|
||||
|
||||
{{#if showWorklistMenu}}
|
||||
<a href="worklist" id="worklist" data-toggle="collapse" data-target="#userMenu" class="list-group-item sub-item">
|
||||
<div id="userAccountMenu" class="dropdown">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{name}} {{currentPath}} | <i class="fa fa-cog fa-lg"></i>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
{{#if showWorklistMenu}}
|
||||
<li>
|
||||
<a href="worklist" id="worklist">
|
||||
<i class="fa fa-list-ul"></i> Worklist
|
||||
</a>
|
||||
{{else}}
|
||||
<!--TODO: Move this elsewhere in the Worklist? -->
|
||||
<span data-toggle="collapse" data-target="#userMenu" class="list-group-item sub-item externalItem">{{>optionsButton }}</span>
|
||||
<a href="audit" id="audit" data-toggle="collapse" data-target="#userMenu" class="list-group-item sub-item">
|
||||
</li>
|
||||
{{else}}
|
||||
<!--TODO: Move this elsewhere in the Worklist? -->
|
||||
<li>
|
||||
{{>optionsButton }}
|
||||
</li>
|
||||
<li>
|
||||
<a href="/audit" id="audit">
|
||||
<i class="fa fa-list-ul"></i> View Audit Log
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
<a href="#itemSettings" class="list-group-item sub-item" data-toggle="collapse" data-parent="#itemSettings"><i class="fa fa-cog fa-lg"></i> Settings <span class="caret"></span></a>
|
||||
<div class="collapse list-group-submenu" id="itemSettings">
|
||||
<a href="#" id="serverInformation" class="list-group-item sub-sub-item" data-parent="#itemSettings" data-toggle="collapse" data-target="#itemSettings,#userMenu"><i class="fa fa-server fa-lg"></i> Server Information</a>
|
||||
</div>
|
||||
|
||||
<a href="#itemProfile" class="list-group-item sub-item" data-toggle="collapse" data-parent="#itemProfile" ><i class="fa fa-user fa-lg"></i> Profile <span class="caret"></span></a>
|
||||
<div class="collapse list-group-submenu" id="itemProfile">
|
||||
<a href="changePassword" id="changePassword" class="list-group-item sub-sub-item" data-parent="#itemProfile" data-toggle="collapse" data-target="#itemProfile,#userMenu"><i class="fa fa-lock"></i> Change Password</a>
|
||||
</div>
|
||||
|
||||
<a href="#" id="logoutButton" data-toggle="collapse" data-target="#userMenu" class="list-group-item sub-item">
|
||||
</li>
|
||||
{{/if}}
|
||||
<li role="separator" class="divider"></li>
|
||||
<li>
|
||||
<a id="serverInformation">
|
||||
<i class="fa fa-server fa-lg"></i> Server Information
|
||||
</a>
|
||||
</li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li>
|
||||
<a href="changePassword" id="changePassword">
|
||||
<i class="fa fa-lock"></i> Change Password
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="logoutButton">
|
||||
<i class="fa fa-power-off"></i> Logout
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
@ -1,12 +1,15 @@
|
||||
Template.userAccountMenu.helpers({
|
||||
fullName: function() {
|
||||
return Meteor.user().profile.fullName;
|
||||
name: function() {
|
||||
var nameSplit = Meteor.user().profile.fullName.split(' ');
|
||||
var lastName = nameSplit[nameSplit.length - 1];
|
||||
nameSplit[nameSplit.length - 1] = lastName.substr(0, 1) + '.';
|
||||
return nameSplit.join(' ');
|
||||
},
|
||||
currentUser: function() {
|
||||
var verifyEmail = Meteor.settings && Meteor.settings.public && Meteor.settings.public.verifyEmail || false;
|
||||
|
||||
if (!Meteor.user() || !Meteor.userId()) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!verifyEmail) {
|
||||
@ -17,18 +20,13 @@ Template.userAccountMenu.helpers({
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Meteor.user().emails[0].verified) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return !!Meteor.user().emails[0].verified;
|
||||
},
|
||||
showWorklistMenu: function() {
|
||||
var currentPath = Router.current().route.path(this);
|
||||
if (currentPath === '/' || currentPath === '/worklist') {
|
||||
return false;
|
||||
if (currentPath !== '/' && currentPath !== '/worklist') {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@ -39,8 +37,62 @@ Template.userAccountMenu.events({
|
||||
'click #logoutButton': function() {
|
||||
// Remove reviewers info for the user
|
||||
Meteor.call('removeUserFromReviewers', Meteor.userId());
|
||||
|
||||
Meteor.logout(function() {
|
||||
Router.go('/entrySignIn');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Template.userAccountMenu.onRendered(function() {
|
||||
var oldUserId;
|
||||
var lastLoginModalInterval;
|
||||
|
||||
this.autorun(function() {
|
||||
// Hook login/logout
|
||||
var user = Meteor.user();
|
||||
var newUserId = user._id;
|
||||
|
||||
if (oldUserId === null && newUserId) {
|
||||
Session.set('showLastLoginModal', true);
|
||||
|
||||
// Log
|
||||
HipaaLogger.logEvent({
|
||||
eventType: 'init',
|
||||
userId: user._id,
|
||||
userName: user.fullName
|
||||
});
|
||||
|
||||
} else if (newUserId === null && oldUserId) {
|
||||
// Set showLastLoginModal as null
|
||||
Session.set('showLastLoginModal', null);
|
||||
|
||||
// Destroy interval for last login modal
|
||||
Meteor.clearInterval(lastLoginModalInterval);
|
||||
console.log('The user logged out');
|
||||
|
||||
// Log
|
||||
// TODO: eventType is not defined for logout in hipaa-audit-log
|
||||
/*HipaaLogger.logEvent({
|
||||
eventType: 'logout',
|
||||
userId: oldUserId,
|
||||
userName: userName
|
||||
});*/
|
||||
|
||||
// Remove the user from Reviewers
|
||||
Meteor.call('removeUserFromReviewers', oldUserId);
|
||||
}
|
||||
|
||||
oldUserId = Meteor.userId();
|
||||
|
||||
// Trigger last login date popup
|
||||
if (Session.get('showLastLoginModal')) {
|
||||
Modal.show('lastLoginModal');
|
||||
|
||||
lastLoginModalInterval = Meteor.setInterval(function() {
|
||||
Modal.hide('lastLoginModal');
|
||||
Session.set('showLastLoginModal', null);
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -1,34 +1,15 @@
|
||||
@import "{design}/app"
|
||||
|
||||
#userAccountMenu
|
||||
position: relative
|
||||
float: right
|
||||
padding: 5px
|
||||
height: 100%
|
||||
line-height: 40px
|
||||
z-index: 999
|
||||
|
||||
.list-group
|
||||
background-color: transparent
|
||||
color: white
|
||||
line-height: 35px
|
||||
margin-left: 10px
|
||||
.list-group-item
|
||||
padding: 5px 10px
|
||||
width: 200px
|
||||
a#userName
|
||||
padding: 0
|
||||
background-color: transparent
|
||||
color: white
|
||||
border: solid 1px transparent
|
||||
text-align: right
|
||||
display: inline-block
|
||||
width: auto
|
||||
#userMenu
|
||||
position: absolute
|
||||
right: 0
|
||||
.externalItem
|
||||
a
|
||||
color: #555
|
||||
.dropdown-toggle
|
||||
cursor: pointer
|
||||
text-decoration: none
|
||||
background-color: $primaryBackgroundColor
|
||||
color: $textSecondaryColor
|
||||
|
||||
.list-group-submenu
|
||||
.sub-sub-item
|
||||
padding-left: 20px
|
||||
padding-right: 20px
|
||||
ul.dropdown-menu
|
||||
li
|
||||
cursor: pointer
|
||||
|
||||
@ -5,12 +5,10 @@
|
||||
{{>lesionLocationDialog}}
|
||||
{{>nonTargetLesionDialog}}
|
||||
{{>nonTargetResponseDialog}}
|
||||
{{>conformanceCheckFeedback}}
|
||||
{{>hidingPanel}}
|
||||
<div id="viewportAndLesionTable">
|
||||
{{>viewerMain }}
|
||||
{{>lesionTable }}
|
||||
</div>
|
||||
<!-- >conformanceCheckFeedback -->
|
||||
|
||||
{{ >toolbarSection }}
|
||||
{{ >flexboxLayout }}
|
||||
</div>
|
||||
{{else}}
|
||||
{{>loadingText}}
|
||||
@ -1,4 +1,7 @@
|
||||
Session.setDefault('activeViewport', false);
|
||||
Session.setDefault('studySidebarOpen', false);
|
||||
Session.setDefault('lesionSidebarOpen', false);
|
||||
Session.setDefault('additionalMeasurementsSidebarOpen', false);
|
||||
|
||||
Template.viewer.onCreated(function() {
|
||||
// Attach the Window resize listener
|
||||
@ -6,10 +9,15 @@ Template.viewer.onCreated(function() {
|
||||
|
||||
ValidationErrors.remove({});
|
||||
|
||||
var self = this;
|
||||
var instance = this;
|
||||
instance.data.state = new ReactiveDict();
|
||||
instance.data.state.set('studySidebarOpen', Session.get('studySidebarOpen'));
|
||||
instance.data.state.set('lesionSidebarOpen', Session.get('lesionSidebarOpen'));
|
||||
instance.data.state.set('additionalMeasurementsSidebarOpen', Session.get('additionalMeasurementsSidebarOpen'));
|
||||
|
||||
var contentId = this.data.contentId;
|
||||
|
||||
OHIF = OHIF || {
|
||||
OHIF = OHIF || window.OHIF || {
|
||||
viewer: {}
|
||||
};
|
||||
|
||||
@ -100,7 +108,7 @@ Template.viewer.onCreated(function() {
|
||||
Session.set('activeViewport', ViewerData[contentId].activeViewport || false);
|
||||
|
||||
// Set lesion tool buttons as disabled if pixel spacing is not available for active element
|
||||
self.autorun(pixelSpacingAutorunCheck);
|
||||
instance.autorun(pixelSpacingAutorunCheck);
|
||||
|
||||
// Update the ViewerStudies collection with the loaded studies
|
||||
ViewerStudies.remove({});
|
||||
@ -113,18 +121,17 @@ Template.viewer.onCreated(function() {
|
||||
var patientId = this.data.studies[0].patientId;
|
||||
Session.set('patientId', patientId);
|
||||
|
||||
self.autorun(function() {
|
||||
instance.autorun(function() {
|
||||
var dataContext = Template.currentData();
|
||||
self.subscribe('singlePatientAssociatedStudies', dataContext.studies[0].patientId);
|
||||
self.subscribe('singlePatientTimepoints', dataContext.studies[0].patientId);
|
||||
self.subscribe('singlePatientMeasurements', dataContext.studies[0].patientId);
|
||||
self.subscribe('singlePatientImageMeasurements', dataContext.studies[0].patientId);
|
||||
instance.subscribe('singlePatientAssociatedStudies', dataContext.studies[0].patientId);
|
||||
instance.subscribe('singlePatientTimepoints', dataContext.studies[0].patientId);
|
||||
instance.subscribe('singlePatientMeasurements', dataContext.studies[0].patientId);
|
||||
instance.subscribe('singlePatientImageMeasurements', dataContext.studies[0].patientId);
|
||||
|
||||
var subscriptionsReady = self.subscriptionsReady();
|
||||
var subscriptionsReady = instance.subscriptionsReady();
|
||||
log.info('autorun viewer.js. Ready: ' + subscriptionsReady);
|
||||
|
||||
if (subscriptionsReady) {
|
||||
|
||||
// Set buttons as enabled/disabled when Timepoints collection is ready
|
||||
timepointAutoCheck(dataContext);
|
||||
|
||||
@ -241,7 +248,7 @@ Template.viewer.onCreated(function() {
|
||||
|
||||
// Set reviewer for this timepoint
|
||||
if (data.timepoints[timepointId].studyInstanceUid) {
|
||||
Meteor.call('setReviewer',data.timepoints[timepointId].studyInstanceUid);
|
||||
Meteor.call('setReviewer', data.timepoints[timepointId].studyInstanceUid);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
.viewerMain
|
||||
width: 100%
|
||||
height: 84%
|
||||
@import "{design}/app"
|
||||
|
||||
#viewer
|
||||
width: 100%
|
||||
height: 100%
|
||||
margin: 0
|
||||
padding: 0
|
||||
background-color: black
|
||||
|
||||
#layoutManagerTarget
|
||||
width: 100%
|
||||
height: calc(100% - 30px)
|
||||
|
||||
//font awesome icons
|
||||
.fa-cr:before
|
||||
@ -20,4 +22,14 @@
|
||||
.fa-ex:before
|
||||
content: 'EX'
|
||||
font-weight: bold
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
|
||||
|
||||
#btnCollapse
|
||||
background-color: #333
|
||||
border-color: black
|
||||
|
||||
&.active
|
||||
border-color: #333
|
||||
|
||||
.btnCollapseIcon
|
||||
color: white
|
||||
@ -1,6 +1,5 @@
|
||||
<template name="viewerMain">
|
||||
<div class="viewerMain">
|
||||
{{ >toolbar toolbarOptions=toolbarOptions }}
|
||||
<div id='layoutManagerTarget'>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,10 @@
|
||||
Template.viewerMain.onRendered(function() {
|
||||
var instance = this;
|
||||
var studies = instance.data.studies;
|
||||
|
||||
var parentElement = instance.$("#layoutManagerTarget").get(0);
|
||||
window.layoutManager = new LayoutManager(parentElement, studies);
|
||||
|
||||
ProtocolEngine = new HP.ProtocolEngine(layoutManager, studies);
|
||||
HP.setEngine(ProtocolEngine);
|
||||
});
|
||||
@ -0,0 +1,17 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.viewerMain
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
#layoutManagerTarget
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
transition(all 0.3s ease)
|
||||
|
||||
#imageViewerViewports
|
||||
.viewportContainer:not(:last-child)
|
||||
.removable
|
||||
.imageViewerViewport
|
||||
border-right: $uiBorderThickness solid #436270
|
||||
@ -18,7 +18,7 @@ toggleLesionTrackerTools = function() {
|
||||
activate: [ 'deleteLesionKeyboardTool' ],
|
||||
deactivate: [],
|
||||
enable: [],
|
||||
disable: [ 'bidirectional', 'nonTarget', 'scaleOverlayTool', 'length', 'crTool', 'unTool', 'exTool' ]
|
||||
disable: [ 'bidirectional', 'nonTarget', 'length', 'crTool', 'unTool', 'exTool' ]
|
||||
};
|
||||
|
||||
toolManager.setToolDefaultStates(toolDefaultStates);
|
||||
|
||||
@ -6,12 +6,7 @@
|
||||
* @param title The title to be used for the tab heading
|
||||
*/
|
||||
openNewTabWithTimepoint = function(timepointId, title) {
|
||||
log.info('openNewTabWithTimepoint: ' + timepointId + ' ' + title);
|
||||
|
||||
// Generate a unique ID to represent this tab
|
||||
// We can't just use the Mongo entry ID because
|
||||
// then it will change after hot-reloading.
|
||||
var contentid = uuid.new();
|
||||
var contentid = 'viewerTab';
|
||||
|
||||
var timepoint = Timepoints.findOne({
|
||||
timepointId: timepointId
|
||||
@ -27,13 +22,7 @@ openNewTabWithTimepoint = function(timepointId, title) {
|
||||
throw 'No studies found that are related to this timepoint';
|
||||
}
|
||||
|
||||
// Create a new entry in the WorklistTabs Collection
|
||||
WorklistTabs.insert({
|
||||
title: title,
|
||||
contentid: contentid,
|
||||
active: false,
|
||||
timepointId: timepointId
|
||||
});
|
||||
ViewerData = window.ViewerData || ViewerData;
|
||||
|
||||
// Update the ViewerData global object
|
||||
ViewerData[contentid] = {
|
||||
|
||||
@ -17,7 +17,7 @@ function dblClickOnStudy(data) {
|
||||
// Check if the study has been associated, and if not, just open it on its own
|
||||
if (!study) {
|
||||
// Open a new tab with this study
|
||||
openNewTab(data.studyInstanceUid, title);
|
||||
open(data.studyInstanceUid, title);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ function dblClickOnStudy(data) {
|
||||
});
|
||||
|
||||
if (!timepoint) {
|
||||
openNewTab(data.studyInstanceUid, title);
|
||||
open(data.studyInstanceUid, title);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -37,4 +37,36 @@ function dblClickOnStudy(data) {
|
||||
title += ' ' + getTimepointName(timepoint);
|
||||
|
||||
openNewTabWithTimepoint(timepoint.timepointId, title);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a study
|
||||
*
|
||||
* @param studyInstanceUid The UID of the Study to be opened
|
||||
* @param title The title to be used for the tab heading
|
||||
*/
|
||||
function open(studyInstanceUid, title) {
|
||||
var contentid = 'viewerTab';
|
||||
|
||||
WorklistTabs.remove({});
|
||||
|
||||
// Create a new entry in the WorklistTabs Collection
|
||||
WorklistTabs.insert({
|
||||
title: title,
|
||||
contentid: contentid,
|
||||
studyInstanceUid: studyInstanceUid,
|
||||
active: false
|
||||
});
|
||||
|
||||
ViewerData = window.ViewerData || ViewerData;
|
||||
|
||||
// Update the ViewerData global object
|
||||
ViewerData[contentid] = {
|
||||
title: title,
|
||||
contentid: contentid,
|
||||
studyInstanceUids: [studyInstanceUid]
|
||||
};
|
||||
|
||||
// Switch to the new tab
|
||||
switchToTab(contentid);
|
||||
}
|
||||
|
||||
@ -7,9 +7,11 @@ Package.describe({
|
||||
Package.onUse(function(api) {
|
||||
api.versionsFrom('1.2.0.2');
|
||||
|
||||
api.use('ecmascript');
|
||||
api.use('standard-app-packages');
|
||||
api.use('jquery');
|
||||
api.use('stylus');
|
||||
api.use('design');
|
||||
|
||||
api.use('validatejs');
|
||||
|
||||
@ -33,37 +35,68 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/collections/LocationResponses.js', 'client');
|
||||
api.addFiles('client/collections/subscriptions.js', 'client');
|
||||
|
||||
|
||||
// Additional Custom Cornerstone Tools for Lesion Tracker
|
||||
api.addFiles('client/compatibility/bidirectionalTool.js', 'client', {
|
||||
bare: true
|
||||
});
|
||||
api.addFiles('client/compatibility/nonTargetTool.js', 'client', {
|
||||
bare: true
|
||||
});
|
||||
api.addFiles('client/compatibility/scaleOverlayTool.js', 'client', {
|
||||
bare: true
|
||||
});
|
||||
api.addFiles('client/compatibility/deleteLesionKeyboardTool.js', 'client', {
|
||||
bare: true
|
||||
});
|
||||
api.addFiles('client/compatibility/crunexTool.js', 'client', {
|
||||
bare: true
|
||||
});
|
||||
api.addFiles('client/compatibility/crTool.js', 'client', {
|
||||
bare: true
|
||||
});
|
||||
api.addFiles('client/compatibility/unTool.js', 'client', {
|
||||
bare: true
|
||||
});
|
||||
api.addFiles('client/compatibility/exTool.js', 'client', {
|
||||
api.addFiles([
|
||||
'client/compatibility/bidirectionalTool.js',
|
||||
'client/compatibility/nonTargetTool.js',
|
||||
'client/compatibility/scaleOverlayTool.js',
|
||||
'client/compatibility/deleteLesionKeyboardTool.js',
|
||||
'client/compatibility/crunexTool.js',
|
||||
'client/compatibility/crTool.js',
|
||||
'client/compatibility/unTool.js',
|
||||
'client/compatibility/exTool.js'
|
||||
], 'client', {
|
||||
bare: true
|
||||
});
|
||||
|
||||
api.addAssets('assets/icons.svg', 'client');
|
||||
|
||||
// UI Components
|
||||
api.addFiles('client/components/viewer/viewer.html', 'client');
|
||||
api.addFiles('client/components/viewer/viewer.styl', 'client');
|
||||
api.addFiles('client/components/viewer/viewer.js', 'client');
|
||||
|
||||
api.addFiles('client/components/flexboxLayout/flexboxLayout.html', 'client');
|
||||
api.addFiles('client/components/flexboxLayout/flexboxLayout.styl', 'client');
|
||||
api.addFiles('client/components/flexboxLayout/flexboxLayout.js', 'client');
|
||||
|
||||
api.addFiles('client/components/toolbarSectionButton/toolbarSectionButton.html', 'client');
|
||||
api.addFiles('client/components/toolbarSectionButton/toolbarSectionButton.styl', 'client');
|
||||
api.addFiles('client/components/toolbarSectionButton/toolbarSectionButton.js', 'client');
|
||||
|
||||
api.addFiles('client/components/toolbarSection/toolbarSection.html', 'client');
|
||||
api.addFiles('client/components/toolbarSection/toolbarSection.styl', 'client');
|
||||
api.addFiles('client/components/toolbarSection/toolbarSection.js', 'client');
|
||||
|
||||
api.addFiles('client/components/radialProgressBar/radialProgressBar.html', 'client');
|
||||
api.addFiles('client/components/radialProgressBar/radialProgressBar.styl', 'client');
|
||||
api.addFiles('client/components/radialProgressBar/radialProgressBar.js', 'client');
|
||||
|
||||
api.addFiles('client/components/caseProgress/caseProgress.html', 'client');
|
||||
api.addFiles('client/components/caseProgress/caseProgress.styl', 'client');
|
||||
api.addFiles('client/components/caseProgress/caseProgress.js', 'client');
|
||||
|
||||
api.addFiles('client/components/viewerMain/viewerMain.html', 'client');
|
||||
api.addFiles('client/components/viewerMain/viewerMain.styl', 'client');
|
||||
api.addFiles('client/components/viewerMain/viewerMain.js', 'client');
|
||||
|
||||
api.addFiles('client/components/lesionTracker/lesionTracker.html', 'client');
|
||||
api.addFiles('client/components/lesionTracker/lesionTracker.styl', 'client');
|
||||
api.addFiles('client/components/lesionTracker/lesionTracker.js', 'client');
|
||||
|
||||
api.addFiles('client/components/lesionTrackerLayout/lesionTrackerLayout.html', 'client');
|
||||
api.addFiles('client/components/lesionTrackerLayout/lesionTrackerLayout.styl', 'client');
|
||||
api.addFiles('client/components/lesionTrackerLayout/lesionTrackerLayout.js', 'client');
|
||||
|
||||
api.addFiles('client/components/additionalMeasurements/additionalMeasurements.html', 'client');
|
||||
api.addFiles('client/components/additionalMeasurements/additionalMeasurements.styl', 'client');
|
||||
api.addFiles('client/components/additionalMeasurements/additionalMeasurements.js', 'client');
|
||||
|
||||
api.addFiles('client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.html', 'client');
|
||||
api.addFiles('client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.styl', 'client');
|
||||
api.addFiles('client/components/additionalMeasurements/radioOptionGroup/radioOptionGroup.js', 'client');
|
||||
|
||||
api.addFiles('client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.html', 'client');
|
||||
api.addFiles('client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.styl', 'client');
|
||||
api.addFiles('client/components/studySeriesQuickSwitch/studySeriesQuickSwitch.js', 'client');
|
||||
|
||||
api.addFiles('client/components/associationModal/associationModal.html', 'client');
|
||||
api.addFiles('client/components/associationModal/associationModal.styl', 'client');
|
||||
@ -91,14 +124,19 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/components/lesionLocationDialog/lesionLocationDialog.html', 'client');
|
||||
api.addFiles('client/components/lesionLocationDialog/lesionLocationDialog.js', 'client');
|
||||
api.addFiles('client/components/lesionLocationDialog/lesionLocationDialog.styl', 'client');
|
||||
|
||||
|
||||
api.addFiles('client/components/lesionTable/lesionTable.html', 'client');
|
||||
api.addFiles('client/components/lesionTable/lesionTable.styl', 'client');
|
||||
api.addFiles('client/components/lesionTable/lesionTable.js', 'client');
|
||||
|
||||
api.addFiles('client/components/lesionTableRow/lesionTableRow.html', 'client');
|
||||
api.addFiles('client/components/lesionTableRow/lesionTableRow.styl', 'client');
|
||||
api.addFiles('client/components/lesionTableRow/lesionTableRow.js', 'client');
|
||||
|
||||
api.addFiles('client/components/lesionTableHeaderRow/lesionTableHeaderRow.html', 'client');
|
||||
api.addFiles('client/components/lesionTableHeaderRow/lesionTableHeaderRow.styl', 'client');
|
||||
api.addFiles('client/components/lesionTableHeaderRow/lesionTableHeaderRow.js', 'client');
|
||||
|
||||
api.addFiles('client/components/lesionTableTimepointCell/lesionTableTimepointCell.html', 'client');
|
||||
api.addFiles('client/components/lesionTableTimepointCell/lesionTableTimepointCell.styl', 'client');
|
||||
api.addFiles('client/components/lesionTableTimepointCell/lesionTableTimepointCell.js', 'client');
|
||||
@ -115,10 +153,20 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/components/studyDateList/studyDateList.styl', 'client');
|
||||
api.addFiles('client/components/studyDateList/studyDateList.js', 'client');
|
||||
|
||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.html', 'client');
|
||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.styl', 'client');
|
||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.js', 'client');
|
||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.html', 'client');
|
||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.styl', 'client');
|
||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.js', 'client');
|
||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.html', 'client');
|
||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.styl', 'client');
|
||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.js', 'client');
|
||||
|
||||
api.addFiles('client/components/studyAssociationTable/studyAssociationTable.html', 'client');
|
||||
api.addFiles('client/components/studyAssociationTable/studyAssociationTable.styl', 'client');
|
||||
api.addFiles('client/components/studyAssociationTable/studyAssociationTable.js', 'client');
|
||||
|
||||
|
||||
api.addFiles('client/components/conformanceCheckFeedback/conformanceCheckFeedback.html', 'client');
|
||||
api.addFiles('client/components/conformanceCheckFeedback/conformanceCheckFeedback.styl', 'client');
|
||||
api.addFiles('client/components/conformanceCheckFeedback/conformanceCheckFeedback.js', 'client');
|
||||
@ -236,5 +284,4 @@ Package.onUse(function(api) {
|
||||
api.export('Studies', [ 'client', 'server' ]);
|
||||
api.export('Timepoints', [ 'client', 'server' ]);
|
||||
api.export('Reviewers', [ 'client', 'server' ]);
|
||||
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<template name="imageThumbnail">
|
||||
<div class="imageThumbnail">
|
||||
<div class="imageThumbnailCanvas"></div>
|
||||
<div class="imageThumbnailLoadingIndicator thumbnailLoadingIndicator">
|
||||
<p>Loading {{percentComplete}}</p>
|
||||
</div>
|
||||
@ -7,4 +8,4 @@
|
||||
<p>Error</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
Template.imageThumbnail.onRendered(function() {
|
||||
var instance = this.data.stack.instances[0];
|
||||
var element = this.find('.imageThumbnail');
|
||||
var element = this.find('.imageThumbnailCanvas');
|
||||
|
||||
cornerstone.disable(element);
|
||||
$(element).find('canvas').remove();
|
||||
@ -12,7 +12,7 @@ Template.imageThumbnail.onRendered(function() {
|
||||
var elem = $(element);
|
||||
elem.find('.imageThumbnailLoadingIndicator').css('display', 'block');
|
||||
|
||||
var thumbnailIndex = $('.imageThumbnail').index(element);
|
||||
var thumbnailIndex = $('.imageThumbnailCanvas').index(element);
|
||||
ThumbnailLoading[thumbnailIndex] = imageId;
|
||||
|
||||
this.data.thumbnailIndex = thumbnailIndex;
|
||||
|
||||
@ -1,10 +1,30 @@
|
||||
@import "{design}/app"
|
||||
|
||||
.thumbnailEntry.active .imageThumbnail
|
||||
border-color: $activeColor
|
||||
box-shadow: none
|
||||
transition($sidebarTransition)
|
||||
|
||||
.imageThumbnail
|
||||
width: 90px
|
||||
height: 90px
|
||||
background-color: black
|
||||
background-color: $primaryBackgroundColor
|
||||
border: 5px solid transparent
|
||||
border-radius: 12px
|
||||
box-shadow: inset 0 0 0 1px $uiBorderColorDark
|
||||
height: 135px
|
||||
margin: 0 auto
|
||||
padding 1px 7px
|
||||
position: relative
|
||||
|
||||
transition($sidebarTransition)
|
||||
width: 217px
|
||||
|
||||
&:hover
|
||||
border-color: $hoverColor
|
||||
box-shadow: none
|
||||
|
||||
.imageThumbnailCanvas
|
||||
height: 100%
|
||||
overflow: hidden
|
||||
|
||||
.thumbnailLoadingIndicator
|
||||
display: none
|
||||
pointer-events: none
|
||||
@ -17,7 +37,7 @@
|
||||
bottom: 0
|
||||
margin: auto
|
||||
position: absolute
|
||||
|
||||
|
||||
p
|
||||
text-align: center
|
||||
font-size: 10pt
|
||||
font-size: 10pt
|
||||
|
||||
@ -1,6 +1,22 @@
|
||||
<template name="thumbnailEntry">
|
||||
<div class="thumbnailEntry">
|
||||
{{ >imageThumbnail }}
|
||||
<div class='seriesDescription noselect'>{{seriesDescription}}</div>
|
||||
<div class="thumbnailEntry m-t-1 m-b-2" data-uid="{{this.stack.seriesInstanceUid}}">
|
||||
<div class="p-x-1">
|
||||
{{>imageThumbnail}}
|
||||
</div>
|
||||
<div class="seriesDetails noselect m-a-1">
|
||||
<div class="seriesDescription">
|
||||
{{this.stack.seriesDescription}}
|
||||
</div>
|
||||
<div class="seriesInformation">
|
||||
<div class="seriesInformationIcons">
|
||||
<strong>S:</strong>
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="seriesInformationNumbers">
|
||||
<div>{{this.stack.seriesNumber}}</div>
|
||||
<div>{{this.stack.instances.length}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@ -24,7 +24,7 @@ function thumbnailDragStartHandler(e, data) {
|
||||
// Identify the current study and series index from the thumbnail's DOM position
|
||||
var targetThumbnail = e.currentTarget;
|
||||
var imageThumbnail = $(targetThumbnail);
|
||||
|
||||
|
||||
// Store this data for use during drag and drop
|
||||
OHIF.viewer.dragAndDropData = data;
|
||||
|
||||
@ -32,7 +32,7 @@ function thumbnailDragStartHandler(e, data) {
|
||||
var targetId = targetThumbnail.id + 'DragClone';
|
||||
var clone = cloneElement(targetThumbnail, targetId);
|
||||
clone.classList.add('imageThumbnailClone');
|
||||
|
||||
|
||||
// Set pointerEvents to pass through the clone DOM element
|
||||
// This is necessary in order to identify what is below it
|
||||
// when using document.elementFromPoint
|
||||
@ -40,7 +40,7 @@ function thumbnailDragStartHandler(e, data) {
|
||||
|
||||
// Append the clone to the parent of the target
|
||||
targetThumbnail.parentNode.appendChild(clone);
|
||||
|
||||
|
||||
// Set the cursor x and y positions from the current touch/mouse coordinates
|
||||
// Handle touchStart cases
|
||||
if (e.type === 'touchstart') {
|
||||
@ -61,7 +61,7 @@ function thumbnailDragStartHandler(e, data) {
|
||||
|
||||
// This block gets the current position of the touch/mouse
|
||||
// relative to the thumbnail itself
|
||||
//
|
||||
//
|
||||
// i.e. Where did the user grab it from?
|
||||
var position = imageThumbnail.position();
|
||||
var left = position.left;
|
||||
@ -70,7 +70,7 @@ function thumbnailDragStartHandler(e, data) {
|
||||
// This difference is saved for later so the element movement looks normal
|
||||
diffX = cursorX - left;
|
||||
diffY = cursorY - top;
|
||||
|
||||
|
||||
// This sets the default style properties of the cloned element so it is
|
||||
// ready to be dragged around the page
|
||||
$(clone).css({
|
||||
@ -103,7 +103,7 @@ function thumbnailDragHandler(e, target) {
|
||||
'z-index': 100,
|
||||
visibility: 'visible'
|
||||
});
|
||||
|
||||
|
||||
// Identify the element below the current cursor position
|
||||
var elemBelow = document.elementFromPoint(cursorX + diffX, cursorY + diffY);
|
||||
|
||||
@ -156,7 +156,7 @@ function thumbnailDragEndHandler(e, target) {
|
||||
|
||||
// Remove any current faded effects on viewports
|
||||
$('.imageViewerViewport canvas').removeClass('faded');
|
||||
|
||||
|
||||
// If none exists, stop here
|
||||
if (!elemBelow) {
|
||||
return;
|
||||
@ -186,8 +186,19 @@ function thumbnailDragEndHandler(e, target) {
|
||||
|
||||
var viewportIndex = $('.imageViewerViewport').index(element);
|
||||
|
||||
// Removes active class from previous selected series in viewport
|
||||
const previousUid = layoutManager.viewportData[viewportIndex].seriesInstanceUid;
|
||||
if (previousUid) {
|
||||
$(`.thumbnailEntry[data-uid="${previousUid}"]`).removeClass('active');
|
||||
}
|
||||
|
||||
// Rerender the viewport using the drag and drop data
|
||||
layoutManager.rerenderViewportWithNewSeries(viewportIndex, OHIF.viewer.dragAndDropData);
|
||||
|
||||
// Set active state for dragged thumbnail entry
|
||||
const newUid = OHIF.viewer.dragAndDropData.seriesInstanceUid
|
||||
$(`.thumbnailEntry[data-uid="${newUid}"]`).addClass('active');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -213,9 +224,3 @@ Template.thumbnailEntry.events({
|
||||
thumbnailDragEndHandler(e, e.currentTarget);
|
||||
}
|
||||
});
|
||||
|
||||
Template.thumbnailEntry.helpers({
|
||||
seriesDescription: function() {
|
||||
return this.stack.seriesDescription || '';
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,24 +1,66 @@
|
||||
@import "{design}/app"
|
||||
|
||||
$seriesCountBackgroundColor = #678696
|
||||
|
||||
.thumbnailEntry
|
||||
width: 105px
|
||||
padding: 5px
|
||||
background-color: black
|
||||
border: 1px solid #424242
|
||||
margin: 0 auto
|
||||
cursor: copy
|
||||
cursor: -webkit-grab
|
||||
cursor: -moz-grab
|
||||
|
||||
.seriesDescription
|
||||
text-align: center
|
||||
color: #555
|
||||
display: table
|
||||
margin: 0 auto
|
||||
font-family: Roboto
|
||||
|
||||
.seriesDetails
|
||||
color: $textPrimaryColor
|
||||
font-size: 14px
|
||||
line-height: 1.3em
|
||||
min-height: 36px
|
||||
position: relative
|
||||
word-wrap: break-word
|
||||
|
||||
&:hover
|
||||
background-color: white
|
||||
|
||||
&.active
|
||||
background-color: #424242
|
||||
border-color: #4e4e4e
|
||||
|
||||
.seriesDescription
|
||||
color: white
|
||||
|
||||
.seriesDescription
|
||||
margin-right: 54px
|
||||
|
||||
.seriesInformation
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
|
||||
.seriesInformationNumbers
|
||||
.seriesInformationIcons
|
||||
margin-right: 4px
|
||||
|
||||
.seriesInformationNumbers
|
||||
color: $textSecondaryColor
|
||||
float: left
|
||||
font-size: 12px
|
||||
|
||||
.seriesInformationIcons
|
||||
float: left
|
||||
text-align: right
|
||||
|
||||
strong
|
||||
color: $activeColor
|
||||
font-size: 10px
|
||||
// TODO: [design] try to find out what is the font-weight for Roboto-Black
|
||||
font-weight: 500
|
||||
|
||||
div
|
||||
background-color: $seriesCountBackgroundColor
|
||||
margin-top: 6px
|
||||
position: relative
|
||||
|
||||
&:after
|
||||
background-color: $activeColor
|
||||
box-shadow: 1px 1px rgba(0, 0, 0, .115)
|
||||
left: -5px
|
||||
position: absolute
|
||||
top: -5px
|
||||
|
||||
&
|
||||
&:after
|
||||
border: 1px solid $primaryBackgroundColor
|
||||
content: ''
|
||||
display: block
|
||||
height: 11px
|
||||
width: 11px
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user