Mercurial > hg > orthanc-stone
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"> <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 {{ currentFrame }} / {{ framesCount }} | |
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> |