Mercurial > hg > orthanc-stone
diff StoneWebViewer/WebApplication/index.html @ 1495:fb74ed5d8c22
initial commit of the Stone Web viewer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 25 Jun 2020 16:51:10 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/StoneWebViewer/WebApplication/index.html Thu Jun 25 16:51:10 2020 +0200 @@ -0,0 +1,488 @@ +<!doctype html> +<html class="wv-html"> + <head> + <title>Stone Web Viewer</title> + <meta charset="utf-8" /> + <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> + <meta name="apple-mobile-web-app-capable" content="yes" /> + <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> + <link rel="icon" href="data:;base64,iVBORw0KGgo="> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.css"> + <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet"> + <link rel="stylesheet" href="app.css"> + + <!-- https://stackoverflow.com/a/16863182/881731 --> + <style> + .tooltip { + position: fixed; + } + </style> + + <!-- Fix if Bootstrap CSS is not used --> + <!--style> + *, + *::before, + *::after { + box-sizing: border-box; + } + </style--> + </head> + <body class="wv-body"> + <div id="wv"> + <div class="wvLoadingScreen" v-show="!ready"> + <span class="wvLoadingSpinner"> + <div class="bounce1"></div> + <div class="bounce2"></div> + <div class="bounce3"></div> + </span> + </div> + + <div class="fluid-height fluid-width" v-show="ready"> + + <div class="wvWarning wvPrintExclude" v-show="showWarning"> + <div class="wvWarning-content clearfix"> + <span class="wvWarning-text"> + <h2 class="mb10"><i class="fa fa-exclamation-triangle wvWarning-icon mr5"></i>Warning!</h2> + <p class="mn mb10" style="color:#000"> + You browser is not supported. You might expect + inconsistent behaviours and must not use the viewer to + produce a diagnostic. + </p> + </span> + </div> + <div class="text-right mb10 mr10"> + <button class="btn btn-primary" @click="showWarning=false">OK</button> + </div> + </div> + + + <div class="wvLayoutLeft wvLayoutLeft--closed" v-show="!leftVisible"> + <div class="wvLayoutLeft__actions--outside" style="z-index:10"> + <button class="wvLayoutLeft__action button__base wh__25 lh__25 text-center" + @click="leftVisible = true"> + <i class="fa fa-angle-double-right"></i> + </button> + </div> + </div> + + + <div class="wvLayoutLeft" v-show="leftVisible" + v-bind:class="{ 'wvLayoutLeft--small': leftMode == 'small' }" + > + <div class="wvLayoutLeft__actions" style="z-index:10"> + <button class="wvLayoutLeft__action button__base wh__25 lh__25 text-center" + @click="leftVisible = false"> + <i class="fa fa-angle-double-left"></i> + </button> + </div> + <div class="wvLayoutLeft__content"> + <div class="wvLayoutLeft__contentTop"> + <div class="float__left dropdown" style="max-width: calc(100% - 4.5rem); height:4.5rem !important" v-show="leftMode != 'small'"> + <button type="button" class="wvButton--border" data-toggle="dropdown"> + {{ getSelectedStudies }} + <span class="caret"></span> + </button> + <ul class="dropdown-menu checkbox-menu allow-focus"> + <li v-for="study in studies" + v-bind:class="{ active: study.selected }" + @click="study.selected = !study.selected"> + <a> + {{ study.tags['0008,1030'] }} + <span v-if="study.selected"> <i class="fa fa-check"></i></span> + </a> + </li> + </ul> + </div> + + <div class="float__right wvButton" v-if="leftMode == 'grid'" @click="leftMode = 'full'"> + <i class="fa fa-th-list"></i> + </div> + <div class="float__right wvButton" v-if="leftMode == 'full'" @click="leftMode = 'small'"> + <i class="fa fa-ellipsis-v"></i> + </div> + <div class="float__right wvButton" v-if="leftMode == 'small'" @click="leftMode = 'grid'"> + <i class="fa fa-th"></i> + </div> + + <p class="clear disclaimer mbn">For patients, teachers and researchers.</p> + </div> + <div class="wvLayoutLeft__contentMiddle"> + + <div v-for="study in studies"> + <div v-if="study.selected"> + <div v-bind:class="'wvStudyIsland--' + study.color"> + <div v-bind:class="'wvStudyIsland__header--' + study.color"> + <!-- Actions --> + <div class="wvStudyIsland__actions" + v-bind:class="{ 'wvStudyIsland__actions--oneCol': leftMode == 'small' }"> + <a class="wvButton"> + <!-- download --> + <i class="fa fa-download"></i> + </a> + </div> + + <!-- Title --> + {{ study.tags['0008,1030'] }} + <br/> + <small>{{ study.tags['0008,0020'] }}</small> + </div> + + <div class="wvStudyIsland__main"> + <ul class="wvSerieslist"> + <li class="wvSerieslist__seriesItem" + v-bind:class="{ highlighted : GetActiveSeries().includes(series[seriesIndex].tags['0020,000e']), 'wvSerieslist__seriesItem--list' : leftMode != 'grid', 'wvSerieslist__seriesItem--grid' : leftMode == 'grid' }" + v-on:dragstart="SeriesDragStart($event, seriesIndex)" + v-on:click="ClickSeries(seriesIndex)" + v-for="seriesIndex in study.series"> + <div class="wvSerieslist__picture" style="z-index:0" + draggable="true" + v-if="series[seriesIndex].type != stone.ThumbnailType.UNKNOWN" + > + <div v-if="series[seriesIndex].type == stone.ThumbnailType.LOADING"> + <img src="img/loading.gif" + style="vertical-align:baseline" + width="65px" height="65px" + /> + </div> + + <i v-if="series[seriesIndex].type == stone.ThumbnailType.PDF" + class="wvSerieslist__placeholderIcon fa fa-file-text"></i> + + <i v-if="series[seriesIndex].type == stone.ThumbnailType.VIDEO" + class="wvSerieslist__placeholderIcon fa fa-video-camera"></i> + + + <div v-if="[stone.ThumbnailType.IMAGE, stone.ThumbnailType.NO_PREVIEW].includes(series[seriesIndex].type)" + class="wvSerieslist__placeholderIcon" + v-bind:title="leftMode == 'full' ? null : '[' + series[seriesIndex].tags['0008,0060'] + '] ' + series[seriesIndex].tags['0008,103e']"> + <i v-if="series[seriesIndex].type == stone.ThumbnailType.NO_PREVIEW" + class="fa fa-eye-slash"></i> + + <img v-if="series[seriesIndex].type == stone.ThumbnailType.IMAGE" + v-bind:src="series[seriesIndex].thumbnail" + style="vertical-align:baseline" + width="65px" height="65px" + v-bind:title="leftMode == 'full' ? null : '[' + series[seriesIndex].tags['0008,0060'] + '] ' + series[seriesIndex].tags['0008,103e']" + /> + + <div v-bind:class="'wvSerieslist__badge--' + study.color" + v-if="'length' in series[seriesIndex]">{{ series[seriesIndex].length }}</div> + </div> + </div> + + <div v-if="leftMode == 'full'" class="wvSerieslist__information" + draggable="true" + v-on:dragstart="SeriesDragStart($event, seriesIndex)" + v-on:click="ClickSeries(seriesIndex)"> + <p class="wvSerieslist__label"> + [{{ series[seriesIndex].tags['0008,0060'] }}] + {{ series[seriesIndex].tags['0008,103e'] }} + </p> + </div> + </li> + </ul> + </div> + </div> + </div> + </div> + + </div> + <div class="wvLayoutLeft__contentBottom"> + </div> + </div> + </div> + <div class="wvLayout__main" + v-bind:class="{ 'wvLayout__main--smallleftpadding': leftVisible && leftMode == 'small', 'wvLayout__main--leftpadding': leftVisible && leftMode != 'small' }" + > + + <div class="wvToolbar wvToolbar--top"> + <div class="ng-scope inline-object"> + <div class="tbGroup"> + <div class="tbGroup__toggl"> + <button class="wvButton" + v-bind:class="{ 'wvButton--underline' : !viewportLayoutButtonsVisible }" + @click="viewportLayoutButtonsVisible = !viewportLayoutButtonsVisible"> + <i class="fa fa-th"></i> + </button> + </div> + + <div class="tbGroup__buttons--bottom" v-show="viewportLayoutButtonsVisible"> + <div class="inline-object"> + <button class="wvButton" @click="SetViewportLayout('1x1')"> + <img src="img/grid1x1.png" style="width:1em;height:1em" /> + </button> + </div> + <div class="inline-object"> + <button class="wvButton" @click="SetViewportLayout('2x1')"> + <img src="img/grid2x1.png" style="width:1em;height:1em" /> + </button> + </div> + <div class="inline-object"> + <button class="wvButton" @click="SetViewportLayout('1x2')"> + <img src="img/grid1x2.png" style="width:1em;height:1em" /> + </button> + </div> + <div class="inline-object"> + <button class="wvButton" @click="SetViewportLayout('2x2')"> + <img src="img/grid2x2.png" style="width:1em;height:1em" /> + </button> + </div> + </div> + </div> + </div> + + <!--div class="ng-scope inline-object"> + <button class="wvButton--underline text-center active"> + <i class="fa fa-hand-pointer-o"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center"> + <i class="fa fa-search"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center"> + <i class="fa fa-arrows"></i> + </button> + </div--> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center" + v-on:click="InvertContrast()"> + <i class="fa fa-adjust"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center" id="windowing-popover"> + <i class="fa fa-sun-o"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center" + v-bind:class="{ 'active' : showInfo }" + v-on:click="showInfo = !showInfo"> + <i class="fa fa-info-circle"></i> + </button> + </div> + + <div class="ng-scope inline-object"> + <button class="wvButton--underline text-center" + v-bind:class="{ 'active' : showReferenceLines }" + v-on:click="showReferenceLines = !showReferenceLines"> + <i class="fa fa-bars"></i> + </button> + </div> + </div> + + + <div class="wvLayout__splitpane--toolbarAtTop"> + <div id="viewport" class="wvSplitpane"> + <viewport v-on:updated-series="SetViewportSeries(1, $event)" + v-on:selected-viewport="activeViewport=1" + v-show="viewport1Visible" + canvas-id="canvas1" + v-bind:series="viewport1Series" + v-bind:left="viewport1Left" + v-bind:top="viewport1Top" + v-bind:width="viewport1Width" + v-bind:height="viewport1Height" + v-bind:quality="viewport1Quality" + v-bind:current-frame="viewport1CurrentFrame" + v-bind:frames-count="viewport1FramesCount" + v-bind:show-info="showInfo" + v-bind:active="activeViewport==1"></viewport> + <viewport v-on:updated-series="SetViewportSeries(2, $event)" + v-on:selected-viewport="activeViewport=2" + v-show="viewport2Visible" + canvas-id="canvas2" + v-bind:series="viewport2Series" + v-bind:left="viewport2Left" + v-bind:top="viewport2Top" + v-bind:width="viewport2Width" + v-bind:height="viewport2Height" + v-bind:quality="viewport2Quality" + v-bind:current-frame="viewport2CurrentFrame" + v-bind:frames-count="viewport2FramesCount" + v-bind:show-info="showInfo" + v-bind:active="activeViewport==2"></viewport> + <viewport v-on:updated-series="SetViewportSeries(3, $event)" + v-on:selected-viewport="activeViewport=3" + v-show="viewport3Visible" + canvas-id="canvas3" + v-bind:series="viewport3Series" + v-bind:left="viewport3Left" + v-bind:top="viewport3Top" + v-bind:width="viewport3Width" + v-bind:height="viewport3Height" + v-bind:quality="viewport3Quality" + v-bind:current-frame="viewport3CurrentFrame" + v-bind:frames-count="viewport3FramesCount" + v-bind:show-info="showInfo" + v-bind:active="activeViewport==3"></viewport> + <viewport v-on:updated-series="SetViewportSeries(4, $event)" + v-on:selected-viewport="activeViewport=4" + v-show="viewport4Visible" + canvas-id="canvas4" + v-bind:series="viewport4Series" + v-bind:left="viewport4Left" + v-bind:top="viewport4Top" + v-bind:width="viewport4Width" + v-bind:height="viewport4Height" + v-bind:quality="viewport4Quality" + v-bind:current-frame="viewport4CurrentFrame" + v-bind:frames-count="viewport4FramesCount" + v-bind:show-info="showInfo" + v-bind:active="activeViewport==4"></viewport> + </div> + </div> + + </div> + </div> + </div> + + + + <script type="text/x-template" id="windowing-content"> + <p class="wvToolbar__windowingPresetConfigNotice"> + Click on the button to toggle the windowing tool or apply a preset to the selected viewport. + </p> + + <ul class="wvToolbar__windowingPresetList"> + <li class="wvToolbar__windowingPresetListItem"> + <a href="#" onclick="app.SetDefaultWindowing()"> + Default + </a> + </li> + <li class="wvToolbar__windowingPresetListItem"> + <a href="#" onclick="app.SetWindowing(-400, 1600)"> + CT Lung <small>(L -400, W 1,600)</small> + </a> + </li> + <li class="wvToolbar__windowingPresetListItem"> + <a href="#" onclick="app.SetWindowing(300, 1500)"> + CT Abdomen <small>(L 300, W 1,500)</small> + </a> + </li> + <li class="wvToolbar__windowingPresetListItem"> + <a href="#" onclick="app.SetWindowing(40, 80)"> + CT Bone <small>(L 40, W 80)</small> + </a> + </li> + <li class="wvToolbar__windowingPresetListItem"> + <a href="#" onclick="app.SetWindowing(40, 400)"> + CT Brain <small>(L 40, W 400)</small> + </a> + </li> + <li class="wvToolbar__windowingPresetListItem"> + <a href="#" onclick="app.SetWindowing(-400, 1600)"> + CT Chest <small>(L -400, W 1,600)</small> + </a> + </li> + <li class="wvToolbar__windowingPresetListItem"> + <a href="#" onclick="app.SetWindowing(300, 600)"> + CT Angio <small>(L 300, W 600)</small> + </a> + </li> + </ul> + </script> + + + <script type="text/x-template" id="viewport-template"> + <div v-bind:style="{ padding:'2px', + position:'absolute', + left: left, + top: top, + width: width, + height: height }"> + <div v-bind:class="{ 'wvSplitpane__cellBorder--selected' : active, + 'wvSplitpane__cellBorder' : series.color == '', + 'wvSplitpane__cellBorder--blue' : series.color == 'blue', + 'wvSplitpane__cellBorder--red' : series.color == 'red', + 'wvSplitpane__cellBorder--green' : series.color == 'green', + 'wvSplitpane__cellBorder--yellow' : series.color == 'yellow', + 'wvSplitpane__cellBorder--violet' : series.color == 'violet' + }" + v-on:dragover="SeriesDragAccept($event)" + v-on:drop="SeriesDragDrop($event)" + style="width:100%;height:100%"> + <div class="wvSplitpane__cell" + v-on:click="MakeActive()"> + <div v-show="status == 'ready'" + style="position:absolute; left:0; top:0; width:100%; height:100%"> + <!--div style="width: 100%; height: 100%; background-color: red"></div--> + <canvas v-bind:id="canvasId" + style="position:absolute; left:0; top:0; width:100%; height:100%" + oncontextmenu="return false"></canvas> + + <div v-if="'tags' in series" v-show="showInfo"> + <div class="wv-overlay"> + <div class="wv-overlay-topleft"> + {{ series.tags['0010,0010'] }}<br/> + {{ series.tags['0010,0020'] }} + </div> + <div class="wv-overlay-topright"> + {{ series.tags['0008,1030'] }}<br/> + {{ series.tags['0008,0020'] }}<br/> + {{ series.tags['0020,0011'] }} | {{ series.tags['0008,103e'] }} + </div> + <div class="wv-overlay-bottomleft" + v-show="framesCount != 0"> + <button class="btn btn-primary" @click="DecrementFrame()"> + <i class="fa fa-chevron-circle-left"></i> + </button> + {{ currentFrame }} / {{ framesCount }} + <button class="btn btn-primary" @click="IncrementFrame()"> + <i class="fa fa-chevron-circle-right"></i> + </button> + </div> + <div class="wv-overlay-bottomright"> + <div v-show="quality == stone.DisplayedFrameQuality.NONE" + style="display:block;background-color:red;width:1em;height:1em" /> + <div v-show="quality == stone.DisplayedFrameQuality.LOW" + style="display:block;background-color:orange;width:1em;height:1em" /> + <div v-show="quality == stone.DisplayedFrameQuality.HIGH" + style="display:block;background-color:green;width:1em;height:1em" /> + </div> + </div> + </div> + </div> + + <div v-if="status == 'waiting'" class="wvPaneOverlay"> + [ drop a series here ] + </div> + + <!--div v-if="status == 'video'" class="wvPaneOverlay"> + <video class="wvVideo" autoplay="" loop="" controls="" preload="auto" type="video/mp4" + src="http://viewer-pro.osimis.io/instances/e465dd27-83c96343-96848735-7035a133-1facf1a0/frames/0/raw"> + </video> + </div--> + + <div v-if="status == 'loading'" class="wvPaneOverlay"> + <span class="wvLoadingSpinner"> + <div class="bounce1"></div> + <div class="bounce2"></div> + <div class="bounce3"></div> + </span> + </div> + </div> + </div> + </div> + </script> + + + <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.js"></script> + + <script src="stone.js"></script> + <script src="app.js"></script> + </body> +</html>