comparison 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
comparison
equal deleted inserted replaced
1494:5a3ef478deb6 1495:fb74ed5d8c22
1 <!doctype html>
2 <html class="wv-html">
3 <head>
4 <title>Stone Web Viewer</title>
5 <meta charset="utf-8" />
6 <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
7 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
8 <meta name="apple-mobile-web-app-capable" content="yes" />
9 <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
10 <link rel="icon" href="data:;base64,iVBORw0KGgo=">
11 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
12 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.css">
13 <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
14 <link rel="stylesheet" href="app.css">
15
16 <!-- https://stackoverflow.com/a/16863182/881731 -->
17 <style>
18 .tooltip {
19 position: fixed;
20 }
21 </style>
22
23 <!-- Fix if Bootstrap CSS is not used -->
24 <!--style>
25 *,
26 *::before,
27 *::after {
28 box-sizing: border-box;
29 }
30 </style-->
31 </head>
32 <body class="wv-body">
33 <div id="wv">
34 <div class="wvLoadingScreen" v-show="!ready">
35 <span class="wvLoadingSpinner">
36 <div class="bounce1"></div>
37 <div class="bounce2"></div>
38 <div class="bounce3"></div>
39 </span>
40 </div>
41
42 <div class="fluid-height fluid-width" v-show="ready">
43
44 <div class="wvWarning wvPrintExclude" v-show="showWarning">
45 <div class="wvWarning-content clearfix">
46 <span class="wvWarning-text">
47 <h2 class="mb10"><i class="fa fa-exclamation-triangle wvWarning-icon mr5"></i>Warning!</h2>
48 <p class="mn mb10" style="color:#000">
49 You browser is not supported. You might expect
50 inconsistent behaviours and must not use the viewer to
51 produce a diagnostic.
52 </p>
53 </span>
54 </div>
55 <div class="text-right mb10 mr10">
56 <button class="btn btn-primary" @click="showWarning=false">OK</button>
57 </div>
58 </div>
59
60
61 <div class="wvLayoutLeft wvLayoutLeft--closed" v-show="!leftVisible">
62 <div class="wvLayoutLeft__actions--outside" style="z-index:10">
63 <button class="wvLayoutLeft__action button__base wh__25 lh__25 text-center"
64 @click="leftVisible = true">
65 <i class="fa fa-angle-double-right"></i>
66 </button>
67 </div>
68 </div>
69
70
71 <div class="wvLayoutLeft" v-show="leftVisible"
72 v-bind:class="{ 'wvLayoutLeft--small': leftMode == 'small' }"
73 >
74 <div class="wvLayoutLeft__actions" style="z-index:10">
75 <button class="wvLayoutLeft__action button__base wh__25 lh__25 text-center"
76 @click="leftVisible = false">
77 <i class="fa fa-angle-double-left"></i>
78 </button>
79 </div>
80 <div class="wvLayoutLeft__content">
81 <div class="wvLayoutLeft__contentTop">
82 <div class="float__left dropdown" style="max-width: calc(100% - 4.5rem); height:4.5rem !important" v-show="leftMode != 'small'">
83 <button type="button" class="wvButton--border" data-toggle="dropdown">
84 {{ getSelectedStudies }}
85 <span class="caret"></span>
86 </button>
87 <ul class="dropdown-menu checkbox-menu allow-focus">
88 <li v-for="study in studies"
89 v-bind:class="{ active: study.selected }"
90 @click="study.selected = !study.selected">
91 <a>
92 {{ study.tags['0008,1030'] }}
93 <span v-if="study.selected">&nbsp;<i class="fa fa-check"></i></span>
94 </a>
95 </li>
96 </ul>
97 </div>
98
99 <div class="float__right wvButton" v-if="leftMode == 'grid'" @click="leftMode = 'full'">
100 <i class="fa fa-th-list"></i>
101 </div>
102 <div class="float__right wvButton" v-if="leftMode == 'full'" @click="leftMode = 'small'">
103 <i class="fa fa-ellipsis-v"></i>
104 </div>
105 <div class="float__right wvButton" v-if="leftMode == 'small'" @click="leftMode = 'grid'">
106 <i class="fa fa-th"></i>
107 </div>
108
109 <p class="clear disclaimer mbn">For patients, teachers and researchers.</p>
110 </div>
111 <div class="wvLayoutLeft__contentMiddle">
112
113 <div v-for="study in studies">
114 <div v-if="study.selected">
115 <div v-bind:class="'wvStudyIsland--' + study.color">
116 <div v-bind:class="'wvStudyIsland__header--' + study.color">
117 <!-- Actions -->
118 <div class="wvStudyIsland__actions"
119 v-bind:class="{ 'wvStudyIsland__actions--oneCol': leftMode == 'small' }">
120 <a class="wvButton">
121 <!-- download -->
122 <i class="fa fa-download"></i>
123 </a>
124 </div>
125
126 <!-- Title -->
127 {{ study.tags['0008,1030'] }}
128 <br/>
129 <small>{{ study.tags['0008,0020'] }}</small>
130 </div>
131
132 <div class="wvStudyIsland__main">
133 <ul class="wvSerieslist">
134 <li class="wvSerieslist__seriesItem"
135 v-bind:class="{ highlighted : GetActiveSeries().includes(series[seriesIndex].tags['0020,000e']), 'wvSerieslist__seriesItem--list' : leftMode != 'grid', 'wvSerieslist__seriesItem--grid' : leftMode == 'grid' }"
136 v-on:dragstart="SeriesDragStart($event, seriesIndex)"
137 v-on:click="ClickSeries(seriesIndex)"
138 v-for="seriesIndex in study.series">
139 <div class="wvSerieslist__picture" style="z-index:0"
140 draggable="true"
141 v-if="series[seriesIndex].type != stone.ThumbnailType.UNKNOWN"
142 >
143 <div v-if="series[seriesIndex].type == stone.ThumbnailType.LOADING">
144 <img src="img/loading.gif"
145 style="vertical-align:baseline"
146 width="65px" height="65px"
147 />
148 </div>
149
150 <i v-if="series[seriesIndex].type == stone.ThumbnailType.PDF"
151 class="wvSerieslist__placeholderIcon fa fa-file-text"></i>
152
153 <i v-if="series[seriesIndex].type == stone.ThumbnailType.VIDEO"
154 class="wvSerieslist__placeholderIcon fa fa-video-camera"></i>
155
156
157 <div v-if="[stone.ThumbnailType.IMAGE, stone.ThumbnailType.NO_PREVIEW].includes(series[seriesIndex].type)"
158 class="wvSerieslist__placeholderIcon"
159 v-bind:title="leftMode == 'full' ? null : '[' + series[seriesIndex].tags['0008,0060'] + '] ' + series[seriesIndex].tags['0008,103e']">
160 <i v-if="series[seriesIndex].type == stone.ThumbnailType.NO_PREVIEW"
161 class="fa fa-eye-slash"></i>
162
163 <img v-if="series[seriesIndex].type == stone.ThumbnailType.IMAGE"
164 v-bind:src="series[seriesIndex].thumbnail"
165 style="vertical-align:baseline"
166 width="65px" height="65px"
167 v-bind:title="leftMode == 'full' ? null : '[' + series[seriesIndex].tags['0008,0060'] + '] ' + series[seriesIndex].tags['0008,103e']"
168 />
169
170 <div v-bind:class="'wvSerieslist__badge--' + study.color"
171 v-if="'length' in series[seriesIndex]">{{ series[seriesIndex].length }}</div>
172 </div>
173 </div>
174
175 <div v-if="leftMode == 'full'" class="wvSerieslist__information"
176 draggable="true"
177 v-on:dragstart="SeriesDragStart($event, seriesIndex)"
178 v-on:click="ClickSeries(seriesIndex)">
179 <p class="wvSerieslist__label">
180 [{{ series[seriesIndex].tags['0008,0060'] }}]
181 {{ series[seriesIndex].tags['0008,103e'] }}
182 </p>
183 </div>
184 </li>
185 </ul>
186 </div>
187 </div>
188 </div>
189 </div>
190
191 </div>
192 <div class="wvLayoutLeft__contentBottom">
193 </div>
194 </div>
195 </div>
196 <div class="wvLayout__main"
197 v-bind:class="{ 'wvLayout__main--smallleftpadding': leftVisible && leftMode == 'small', 'wvLayout__main--leftpadding': leftVisible && leftMode != 'small' }"
198 >
199
200 <div class="wvToolbar wvToolbar--top">
201 <div class="ng-scope inline-object">
202 <div class="tbGroup">
203 <div class="tbGroup__toggl">
204 <button class="wvButton"
205 v-bind:class="{ 'wvButton--underline' : !viewportLayoutButtonsVisible }"
206 @click="viewportLayoutButtonsVisible = !viewportLayoutButtonsVisible">
207 <i class="fa fa-th"></i>
208 </button>
209 </div>
210
211 <div class="tbGroup__buttons--bottom" v-show="viewportLayoutButtonsVisible">
212 <div class="inline-object">
213 <button class="wvButton" @click="SetViewportLayout('1x1')">
214 <img src="img/grid1x1.png" style="width:1em;height:1em" />
215 </button>
216 </div>
217 <div class="inline-object">
218 <button class="wvButton" @click="SetViewportLayout('2x1')">
219 <img src="img/grid2x1.png" style="width:1em;height:1em" />
220 </button>
221 </div>
222 <div class="inline-object">
223 <button class="wvButton" @click="SetViewportLayout('1x2')">
224 <img src="img/grid1x2.png" style="width:1em;height:1em" />
225 </button>
226 </div>
227 <div class="inline-object">
228 <button class="wvButton" @click="SetViewportLayout('2x2')">
229 <img src="img/grid2x2.png" style="width:1em;height:1em" />
230 </button>
231 </div>
232 </div>
233 </div>
234 </div>
235
236 <!--div class="ng-scope inline-object">
237 <button class="wvButton--underline text-center active">
238 <i class="fa fa-hand-pointer-o"></i>
239 </button>
240 </div>
241
242 <div class="ng-scope inline-object">
243 <button class="wvButton--underline text-center">
244 <i class="fa fa-search"></i>
245 </button>
246 </div>
247
248 <div class="ng-scope inline-object">
249 <button class="wvButton--underline text-center">
250 <i class="fa fa-arrows"></i>
251 </button>
252 </div-->
253
254 <div class="ng-scope inline-object">
255 <button class="wvButton--underline text-center"
256 v-on:click="InvertContrast()">
257 <i class="fa fa-adjust"></i>
258 </button>
259 </div>
260
261 <div class="ng-scope inline-object">
262 <button class="wvButton--underline text-center" id="windowing-popover">
263 <i class="fa fa-sun-o"></i>
264 </button>
265 </div>
266
267 <div class="ng-scope inline-object">
268 <button class="wvButton--underline text-center"
269 v-bind:class="{ 'active' : showInfo }"
270 v-on:click="showInfo = !showInfo">
271 <i class="fa fa-info-circle"></i>
272 </button>
273 </div>
274
275 <div class="ng-scope inline-object">
276 <button class="wvButton--underline text-center"
277 v-bind:class="{ 'active' : showReferenceLines }"
278 v-on:click="showReferenceLines = !showReferenceLines">
279 <i class="fa fa-bars"></i>
280 </button>
281 </div>
282 </div>
283
284
285 <div class="wvLayout__splitpane--toolbarAtTop">
286 <div id="viewport" class="wvSplitpane">
287 <viewport v-on:updated-series="SetViewportSeries(1, $event)"
288 v-on:selected-viewport="activeViewport=1"
289 v-show="viewport1Visible"
290 canvas-id="canvas1"
291 v-bind:series="viewport1Series"
292 v-bind:left="viewport1Left"
293 v-bind:top="viewport1Top"
294 v-bind:width="viewport1Width"
295 v-bind:height="viewport1Height"
296 v-bind:quality="viewport1Quality"
297 v-bind:current-frame="viewport1CurrentFrame"
298 v-bind:frames-count="viewport1FramesCount"
299 v-bind:show-info="showInfo"
300 v-bind:active="activeViewport==1"></viewport>
301 <viewport v-on:updated-series="SetViewportSeries(2, $event)"
302 v-on:selected-viewport="activeViewport=2"
303 v-show="viewport2Visible"
304 canvas-id="canvas2"
305 v-bind:series="viewport2Series"
306 v-bind:left="viewport2Left"
307 v-bind:top="viewport2Top"
308 v-bind:width="viewport2Width"
309 v-bind:height="viewport2Height"
310 v-bind:quality="viewport2Quality"
311 v-bind:current-frame="viewport2CurrentFrame"
312 v-bind:frames-count="viewport2FramesCount"
313 v-bind:show-info="showInfo"
314 v-bind:active="activeViewport==2"></viewport>
315 <viewport v-on:updated-series="SetViewportSeries(3, $event)"
316 v-on:selected-viewport="activeViewport=3"
317 v-show="viewport3Visible"
318 canvas-id="canvas3"
319 v-bind:series="viewport3Series"
320 v-bind:left="viewport3Left"
321 v-bind:top="viewport3Top"
322 v-bind:width="viewport3Width"
323 v-bind:height="viewport3Height"
324 v-bind:quality="viewport3Quality"
325 v-bind:current-frame="viewport3CurrentFrame"
326 v-bind:frames-count="viewport3FramesCount"
327 v-bind:show-info="showInfo"
328 v-bind:active="activeViewport==3"></viewport>
329 <viewport v-on:updated-series="SetViewportSeries(4, $event)"
330 v-on:selected-viewport="activeViewport=4"
331 v-show="viewport4Visible"
332 canvas-id="canvas4"
333 v-bind:series="viewport4Series"
334 v-bind:left="viewport4Left"
335 v-bind:top="viewport4Top"
336 v-bind:width="viewport4Width"
337 v-bind:height="viewport4Height"
338 v-bind:quality="viewport4Quality"
339 v-bind:current-frame="viewport4CurrentFrame"
340 v-bind:frames-count="viewport4FramesCount"
341 v-bind:show-info="showInfo"
342 v-bind:active="activeViewport==4"></viewport>
343 </div>
344 </div>
345
346 </div>
347 </div>
348 </div>
349
350
351
352 <script type="text/x-template" id="windowing-content">
353 <p class="wvToolbar__windowingPresetConfigNotice">
354 Click on the button to toggle the windowing tool or apply a preset to the selected viewport.
355 </p>
356
357 <ul class="wvToolbar__windowingPresetList">
358 <li class="wvToolbar__windowingPresetListItem">
359 <a href="#" onclick="app.SetDefaultWindowing()">
360 Default
361 </a>
362 </li>
363 <li class="wvToolbar__windowingPresetListItem">
364 <a href="#" onclick="app.SetWindowing(-400, 1600)">
365 CT Lung <small>(L -400, W 1,600)</small>
366 </a>
367 </li>
368 <li class="wvToolbar__windowingPresetListItem">
369 <a href="#" onclick="app.SetWindowing(300, 1500)">
370 CT Abdomen <small>(L 300, W 1,500)</small>
371 </a>
372 </li>
373 <li class="wvToolbar__windowingPresetListItem">
374 <a href="#" onclick="app.SetWindowing(40, 80)">
375 CT Bone <small>(L 40, W 80)</small>
376 </a>
377 </li>
378 <li class="wvToolbar__windowingPresetListItem">
379 <a href="#" onclick="app.SetWindowing(40, 400)">
380 CT Brain <small>(L 40, W 400)</small>
381 </a>
382 </li>
383 <li class="wvToolbar__windowingPresetListItem">
384 <a href="#" onclick="app.SetWindowing(-400, 1600)">
385 CT Chest <small>(L -400, W 1,600)</small>
386 </a>
387 </li>
388 <li class="wvToolbar__windowingPresetListItem">
389 <a href="#" onclick="app.SetWindowing(300, 600)">
390 CT Angio <small>(L 300, W 600)</small>
391 </a>
392 </li>
393 </ul>
394 </script>
395
396
397 <script type="text/x-template" id="viewport-template">
398 <div v-bind:style="{ padding:'2px',
399 position:'absolute',
400 left: left,
401 top: top,
402 width: width,
403 height: height }">
404 <div v-bind:class="{ 'wvSplitpane__cellBorder--selected' : active,
405 'wvSplitpane__cellBorder' : series.color == '',
406 'wvSplitpane__cellBorder--blue' : series.color == 'blue',
407 'wvSplitpane__cellBorder--red' : series.color == 'red',
408 'wvSplitpane__cellBorder--green' : series.color == 'green',
409 'wvSplitpane__cellBorder--yellow' : series.color == 'yellow',
410 'wvSplitpane__cellBorder--violet' : series.color == 'violet'
411 }"
412 v-on:dragover="SeriesDragAccept($event)"
413 v-on:drop="SeriesDragDrop($event)"
414 style="width:100%;height:100%">
415 <div class="wvSplitpane__cell"
416 v-on:click="MakeActive()">
417 <div v-show="status == 'ready'"
418 style="position:absolute; left:0; top:0; width:100%; height:100%">
419 <!--div style="width: 100%; height: 100%; background-color: red"></div-->
420 <canvas v-bind:id="canvasId"
421 style="position:absolute; left:0; top:0; width:100%; height:100%"
422 oncontextmenu="return false"></canvas>
423
424 <div v-if="'tags' in series" v-show="showInfo">
425 <div class="wv-overlay">
426 <div class="wv-overlay-topleft">
427 {{ series.tags['0010,0010'] }}<br/>
428 {{ series.tags['0010,0020'] }}
429 </div>
430 <div class="wv-overlay-topright">
431 {{ series.tags['0008,1030'] }}<br/>
432 {{ series.tags['0008,0020'] }}<br/>
433 {{ series.tags['0020,0011'] }} | {{ series.tags['0008,103e'] }}
434 </div>
435 <div class="wv-overlay-bottomleft"
436 v-show="framesCount != 0">
437 <button class="btn btn-primary" @click="DecrementFrame()">
438 <i class="fa fa-chevron-circle-left"></i>
439 </button>
440 &nbsp;&nbsp;{{ currentFrame }} / {{ framesCount }}&nbsp;&nbsp;
441 <button class="btn btn-primary" @click="IncrementFrame()">
442 <i class="fa fa-chevron-circle-right"></i>
443 </button>
444 </div>
445 <div class="wv-overlay-bottomright">
446 <div v-show="quality == stone.DisplayedFrameQuality.NONE"
447 style="display:block;background-color:red;width:1em;height:1em" />
448 <div v-show="quality == stone.DisplayedFrameQuality.LOW"
449 style="display:block;background-color:orange;width:1em;height:1em" />
450 <div v-show="quality == stone.DisplayedFrameQuality.HIGH"
451 style="display:block;background-color:green;width:1em;height:1em" />
452 </div>
453 </div>
454 </div>
455 </div>
456
457 <div v-if="status == 'waiting'" class="wvPaneOverlay">
458 [ drop a series here ]
459 </div>
460
461 <!--div v-if="status == 'video'" class="wvPaneOverlay">
462 <video class="wvVideo" autoplay="" loop="" controls="" preload="auto" type="video/mp4"
463 src="http://viewer-pro.osimis.io/instances/e465dd27-83c96343-96848735-7035a133-1facf1a0/frames/0/raw">
464 </video>
465 </div-->
466
467 <div v-if="status == 'loading'" class="wvPaneOverlay">
468 <span class="wvLoadingSpinner">
469 <div class="bounce1"></div>
470 <div class="bounce2"></div>
471 <div class="bounce3"></div>
472 </span>
473 </div>
474 </div>
475 </div>
476 </div>
477 </script>
478
479
480 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
481 <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.js"></script>
482 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
483 <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.js"></script>
484
485 <script src="stone.js"></script>
486 <script src="app.js"></script>
487 </body>
488 </html>