comparison OrthancExplorer/explorer.js @ 67:9193041c8018

merge
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sun, 16 Sep 2012 09:48:01 +0200
parents 4bc019d2f969
children 6212bf978584
comparison
equal deleted inserted replaced
55:601ee9b7f2c7 67:9193041c8018
1 // http://stackoverflow.com/questions/1663741/is-there-a-good-jquery-drag-and-drop-file-upload-plugin
2
3
4 // Forbid the access to IE
5 if ($.browser.msie)
6 {
7 alert("Please use Mozilla Firefox or Google Chrome. Microsoft Internet Explorer is not supported.");
8 }
9
10 // http://jquerymobile.com/demos/1.1.0/docs/api/globalconfig.html
11 //$.mobile.ajaxEnabled = false;
12 //$.mobile.page.prototype.options.addBackBtn = true;
13 //$.mobile.defaultPageTransition = 'slide';
14
15 // http://stackoverflow.com/a/4673436
16 String.prototype.format = function() {
17 var args = arguments;
18 return this.replace(/{(\d+)}/g, function(match, number) {
19 /*return typeof args[number] != 'undefined'
20 ? args[number]
21 : match;*/
22
23 return args[number];
24 });
25 };
26
27
28 $(document).ready(function() {
29 var $tree = $('#dicom-tree');
30 $tree.tree({
31 autoEscape: false
32 });
33
34 $('#dicom-tree').bind(
35 'tree.click',
36 function(event) {
37 if (event.node.is_open)
38 $tree.tree('closeNode', event.node, true);
39 else
40 $tree.tree('openNode', event.node, true);
41 }
42 );
43 });
44
45
46 function SplitLongUid(s)
47 {
48 return '<span>' + s.substr(0, s.length / 2) + '</span> <span>' + s.substr(s.length / 2, s.length - s.length / 2) + '</span>';
49 }
50
51
52 function ParseDicomDate(s)
53 {
54 y = parseInt(s.substr(0, 4), 10);
55 m = parseInt(s.substr(4, 2), 10) - 1;
56 d = parseInt(s.substr(6, 2), 10);
57
58 if (y == null || m == null || d == null ||
59 !isFinite(y) || !isFinite(m) || !isFinite(d))
60 {
61 return null;
62 }
63
64 if (y < 1900 || y > 2100 ||
65 m < 0 || m >= 12 ||
66 d <= 0 || d >= 32)
67 {
68 return null;
69 }
70
71 return new Date(y, m, d);
72 }
73
74
75 function FormatDicomDate(s)
76 {
77 if (s == undefined)
78 return "No date";
79
80 var d = ParseDicomDate(s);
81 if (d == null)
82 return '?';
83 else
84 return d.toString('dddd, MMMM d, yyyy');
85 }
86
87
88
89 function SortOnDicomTag(arr, tag, isInteger, reverse)
90 {
91 var defaultValue;
92 if (isInteger)
93 defaultValue = 0;
94 else
95 defaultValue = '';
96
97 arr.sort(function(a, b) {
98 var ta = a.MainDicomTags[tag];
99 var tb = b.MainDicomTags[tag];
100 var order;
101
102 if (ta == undefined)
103 ta = defaultValue;
104
105 if (tb == undefined)
106 tb = defaultValue;
107
108 if (isInteger)
109 {
110 ta = parseInt(ta, 10);
111 tb = parseInt(tb, 10);
112 order = ta - tb;
113 }
114 else
115 {
116 if (ta < tb)
117 order = -1;
118 else if (ta > tb)
119 order = 1;
120 else
121 order = 0;
122 }
123
124 if (reverse)
125 return -order;
126 else
127 return order;
128 });
129 }
130
131
132
133 function GetSingleResource(type, uuid, callback)
134 {
135 var resource = null;
136 $.ajax({
137 url: '/' + type + '/' + uuid,
138 dataType: 'json',
139 async: false,
140 success: function(s) {
141 callback(s);
142 }
143 });
144 }
145
146
147 function GetMultipleResources(type, uuids, callback)
148 {
149 if (uuids == null)
150 {
151 $.ajax({
152 url: '/' + type,
153 dataType: 'json',
154 async: false,
155 success: function(s) {
156 uuids = s;
157 }
158 });
159 }
160
161 var resources = [];
162 var ajaxRequests = uuids.map(function(uuid) {
163 return $.ajax({
164 url: '/' + type + '/' + uuid,
165 dataType: 'json',
166 async: true,
167 success: function(s) {
168 resources.push(s);
169 }
170 });
171 });
172
173 // Wait for all the AJAX requests to end
174 $.when.apply($, ajaxRequests).then(function() {
175 callback(resources);
176 });
177 }
178
179
180
181 function CompleteFormatting(s, link, isReverse)
182 {
183 if (link != null)
184 {
185 s = 'href="' + link + '">' + s + '</a>';
186
187 if (isReverse)
188 s = 'data-direction="reverse" '+ s;
189
190 s = '<a ' + s;
191 }
192
193 if (isReverse)
194 return '<li data-icon="back">' + s + '</li>';
195 else
196 return '<li>' + s + '</li>';
197 }
198
199
200 function FormatMainDicomTags(tags, tagsToIgnore)
201 {
202 var s = '';
203
204 for (var i in tags)
205 {
206 if (tagsToIgnore.indexOf(i) == -1)
207 {
208 var v = tags[i];
209
210 if (i == "PatientBirthDate" ||
211 i == "StudyDate" ||
212 i == "SeriesDate")
213 {
214 v = FormatDicomDate(v);
215 }
216 else if (i == "DicomStudyInstanceUID" ||
217 i == "DicomSeriesInstanceUID")
218 {
219 v = SplitLongUid(v);
220 }
221
222
223 s += ('<p>{0}: <strong>{1}</strong></p>').format(i, v);
224 }
225 }
226
227 return s;
228 }
229
230
231 function FormatPatient(patient, link, isReverse)
232 {
233 var s = ('<h3>{0}</h3>{1}' +
234 '<span class="ui-li-count">{2}</span>'
235 ).format
236 (patient.MainDicomTags.PatientName,
237 FormatMainDicomTags(patient.MainDicomTags, [
238 "PatientName",
239 "OtherPatientIDs"
240 ]),
241 patient.Studies.length
242 );
243
244 return CompleteFormatting(s, link, isReverse);
245 }
246
247
248
249 function FormatStudy(study, link, isReverse)
250 {
251 var s = ('<h3>{0}</h3>{1}' +
252 '<span class="ui-li-count">{2}</span>'
253 ).format
254 (study.MainDicomTags.StudyDescription,
255 FormatMainDicomTags(study.MainDicomTags, [
256 "StudyDescription",
257 "StudyTime"
258 ]),
259 study.Series.length
260 );
261
262 return CompleteFormatting(s, link, isReverse);
263 }
264
265
266
267 function FormatSeries(series, link, isReverse)
268 {
269 var s = ('<h3>{0}</h3>{1}' +
270 '<span class="ui-li-count">{2}</span>').format
271 (series.MainDicomTags.SeriesDescription,
272 FormatMainDicomTags(series.MainDicomTags, [
273 "SeriesDescription",
274 "SeriesTime",
275 "Manufacturer",
276 "ImagesInAcquisition",
277 "SeriesDate"
278 ]),
279 series.Instances.length
280 );
281
282 return CompleteFormatting(s, link, isReverse);
283 }
284
285
286 function FormatInstance(instance, link, isReverse)
287 {
288 var s = ('<h3>Instance {0}</h3>{1}').format
289 (instance.MainDicomTags.InstanceNumber,
290 FormatMainDicomTags(instance.MainDicomTags, [
291 "AcquisitionNumber",
292 "InstanceNumber",
293 "InstanceCreationDate",
294 "InstanceCreationTime"
295 ])
296 );
297
298 return CompleteFormatting(s, link, isReverse);
299 }
300
301
302
303
304 $('#find-patients').live('pagebeforeshow', function() {
305 GetMultipleResources('patients', null, function(patients) {
306 var target = $('#all-patients');
307 $('li', target).remove();
308
309 SortOnDicomTag(patients, 'PatientName', false, false);
310
311 for (var i = 0; i < patients.length; i++) {
312 var p = FormatPatient(patients[i], '#patient?uuid=' + patients[i].ID);
313 target.append(p);
314 }
315
316 target.listview('refresh');
317 });
318 });
319
320
321
322 $('#patient').live('pagebeforeshow', function() {
323 if ($.mobile.pageData) {
324 GetSingleResource('patients', $.mobile.pageData.uuid, function(patient) {
325 GetMultipleResources('studies', patient.Studies, function(studies) {
326 SortOnDicomTag(studies, 'StudyDate', false, true);
327
328 $('#patient-info li').remove();
329 $('#patient-info')
330 .append('<li data-role="list-divider">Patient</li>')
331 .append(FormatPatient(patient))
332 .listview('refresh');
333
334 var target = $('#list-studies');
335 $('li', target).remove();
336
337 for (var i = 0; i < studies.length; i++) {
338 if (i == 0 || studies[i].MainDicomTags.StudyDate != studies[i - 1].MainDicomTags.StudyDate)
339 {
340 target.append('<li data-role="list-divider">{0}</li>'.format
341 (FormatDicomDate(studies[i].MainDicomTags.StudyDate)));
342 }
343
344 target.append(FormatStudy(studies[i], '#study?uuid=' + studies[i].ID));
345 }
346
347 target.listview('refresh');
348 });
349 });
350 }
351 });
352
353
354 $('#study').live('pagebeforeshow', function() {
355 if ($.mobile.pageData) {
356 GetSingleResource('studies', $.mobile.pageData.uuid, function(study) {
357 GetSingleResource('patients', study.ParentPatient, function(patient) {
358 GetMultipleResources('series', study.Series, function(series) {
359 SortOnDicomTag(series, 'SeriesDate', false, true);
360
361 $('#study-info li').remove();
362 $('#study-info')
363 .append('<li data-role="list-divider">Patient</li>')
364 .append(FormatPatient(patient, '#patient?uuid=' + patient.ID, true))
365 .append('<li data-role="list-divider">Study</li>')
366 .append(FormatStudy(study))
367 .listview('refresh');
368
369 var target = $('#list-series');
370 $('li', target).remove();
371 for (var i = 0; i < series.length; i++) {
372 if (i == 0 || series[i].MainDicomTags.SeriesDate != series[i - 1].MainDicomTags.SeriesDate)
373 {
374 target.append('<li data-role="list-divider">{0}</li>'.format
375 (FormatDicomDate(series[i].MainDicomTags.SeriesDate)));
376 }
377 target.append(FormatSeries(series[i], '#series?uuid=' + series[i].ID));
378 }
379 target.listview('refresh');
380 });
381 });
382 });
383 }
384 });
385
386
387 $('#series').live('pagebeforeshow', function() {
388 if ($.mobile.pageData) {
389 GetSingleResource('series', $.mobile.pageData.uuid, function(series) {
390 GetSingleResource('studies', series.ParentStudy, function(study) {
391 GetSingleResource('patients', study.ParentPatient, function(patient) {
392 GetMultipleResources('instances', series.Instances, function(instances) {
393 SortOnDicomTag(instances, 'InstanceNumber', true, false);
394
395 $('#series-info li').remove();
396 $('#series-info')
397 .append('<li data-role="list-divider">Patient</li>')
398 .append(FormatPatient(patient, '#patient?uuid=' + patient.ID, true))
399 .append('<li data-role="list-divider">Study</li>')
400 .append(FormatStudy(study, '#study?uuid=' + study.ID, true))
401 .append('<li data-role="list-divider">Series</li>')
402 .append(FormatSeries(series))
403 .listview('refresh');
404
405 var target = $('#list-instances');
406 $('li', target).remove();
407 for (var i = 0; i < instances.length; i++) {
408 target.append(FormatInstance(instances[i], '#instance?uuid=' + instances[i].ID));
409 }
410 target.listview('refresh');
411 });
412 });
413 });
414 });
415 }
416 });
417
418
419
420 function ConvertForTree(dicom)
421 {
422 var result = [];
423
424 for (var i in dicom) {
425 if (dicom[i] != null) {
426 var label = i + '<span class="tag-name"> (<i>' + dicom[i]["Name"] + '</i>)</span>: ';
427
428 if (dicom[i]["Type"] == 'String')
429 {
430 result.push({
431 label: label + '<strong>' + dicom[i]["Value"] + '</strong>',
432 children: []
433 });
434 }
435 else if (dicom[i]["Type"] == 'TooLong')
436 {
437 result.push({
438 label: label + '<i>Too long</i>',
439 children: []
440 });
441 }
442 else if (dicom[i]["Type"] == 'Null')
443 {
444 result.push({
445 label: label + '<i>Null</i>',
446 children: []
447 });
448 }
449 else if (dicom[i]["Type"] == 'Sequence')
450 {
451 var c = [];
452 for (var j = 0; j < dicom[i]["Value"].length; j++) {
453 c.push({
454 label: 'Item ' + j,
455 children: ConvertForTree(dicom[i]["Value"][j])
456 });
457 }
458
459 result.push({
460 label: label + '[]',
461 children: c
462 });
463 }
464 }
465 }
466
467 return result;
468 }
469
470
471 $('#instance').live('pagebeforeshow', function() {
472 if ($.mobile.pageData) {
473 GetSingleResource('instances', $.mobile.pageData.uuid, function(instance) {
474 GetSingleResource('series', instance.ParentSeries, function(series) {
475 GetSingleResource('studies', series.ParentStudy, function(study) {
476 GetSingleResource('patients', study.ParentPatient, function(patient) {
477
478 $('#instance-info li').remove();
479 $('#instance-info')
480 .append('<li data-role="list-divider">Patient</li>')
481 .append(FormatPatient(patient, '#patient?uuid=' + patient.ID, true))
482 .append('<li data-role="list-divider">Study</li>')
483 .append(FormatStudy(study, '#study?uuid=' + study.ID, true))
484 .append('<li data-role="list-divider">Series</li>')
485 .append(FormatSeries(series, '#series?uuid=' + series.ID, true))
486 .append('<li data-role="list-divider">Instance</li>')
487 .append(FormatInstance(instance))
488 .listview('refresh');
489
490 $.ajax({
491 url: '/instances/' + instance.ID + '/tags',
492 dataType: 'json',
493 success: function(s) {
494 $('#dicom-tree').tree('loadData', ConvertForTree(s));
495 }
496 });
497
498 });
499 });
500 });
501 });
502 }
503 });
504
505
506
507 function DeleteResource(path)
508 {
509 $.ajax({
510 url: path,
511 type: 'DELETE',
512 dataType: 'json',
513 async: false,
514 success: function(s) {
515 var ancestor = s.RemainingAncestor;
516 if (ancestor == null)
517 $.mobile.changePage('#find-patients');
518 else
519 $.mobile.changePage('#' + ancestor.Type + '?uuid=' + ancestor.ID);
520 }
521 });
522 }
523
524
525
526 function OpenDeleteResourceDialog(path, title)
527 {
528 $(document).simpledialog2({
529 // http://dev.jtsage.com/jQM-SimpleDialog/demos2/
530 // http://dev.jtsage.com/jQM-SimpleDialog/demos2/options.html
531 mode: 'button',
532 animate: false,
533 headerText: title,
534 headerClose: true,
535 width: '500px',
536 buttons : {
537 'OK': {
538 click: function () {
539 DeleteResource(path);
540 },
541 icon: "delete",
542 theme: "c"
543 },
544 'Cancel': {
545 click: function () {
546 }
547 }
548 }
549 });
550 }
551
552
553
554 $('#instance-delete').live('click', function() {
555 OpenDeleteResourceDialog('/instances/' + $.mobile.pageData.uuid,
556 'Delete this instance?');
557 });
558
559 $('#study-delete').live('click', function() {
560 OpenDeleteResourceDialog('/studies/' + $.mobile.pageData.uuid,
561 'Delete this study?');
562 });
563
564 $('#series-delete').live('click', function() {
565 OpenDeleteResourceDialog('/series/' + $.mobile.pageData.uuid,
566 'Delete this series?');
567 });
568
569 $('#patient-delete').live('click', function() {
570 OpenDeleteResourceDialog('/patients/' + $.mobile.pageData.uuid,
571 'Delete this patient?');
572 });
573
574
575 $('#instance-download-dicom').live('click', function(e) {
576 // http://stackoverflow.com/a/1296101
577 e.preventDefault(); //stop the browser from following
578 window.location.href = '/instances/' + $.mobile.pageData.uuid + '/file';
579 });
580
581 $('#instance-download-json').live('click', function(e) {
582 // http://stackoverflow.com/a/1296101
583 e.preventDefault(); //stop the browser from following
584 window.location.href = '/instances/' + $.mobile.pageData.uuid + '/tags';
585 });
586
587
588 $('#instance-preview').live('click', function(e) {
589 if ($.mobile.pageData) {
590 GetSingleResource('instances', $.mobile.pageData.uuid + '/frames', function(frames) {
591 if (frames.length == 1)
592 {
593 // Viewing a single-frame image
594 jQuery.slimbox('/instances/' + $.mobile.pageData.uuid + '/preview', '', {
595 overlayFadeDuration : 1,
596 resizeDuration : 1,
597 imageFadeDuration : 1
598 });
599 }
600 else
601 {
602 // Viewing a multi-frame image
603
604 var images = [];
605 for (var i = 0; i < frames.length; i++) {
606 images.push([ '/instances/' + $.mobile.pageData.uuid + '/frames/' + i + '/preview' ]);
607 }
608
609 jQuery.slimbox(images, 0, {
610 overlayFadeDuration : 1,
611 resizeDuration : 1,
612 imageFadeDuration : 1,
613 loop : true
614 });
615 }
616 });
617
618 }
619 });
620
621 $('#series-preview').live('click', function(e) {
622 if ($.mobile.pageData) {
623 GetSingleResource('series', $.mobile.pageData.uuid, function(series) {
624 GetMultipleResources('instances', series.Instances, function(instances) {
625 SortOnDicomTag(instances, 'InstanceNumber', true, false);
626
627 var images = [];
628 for (var i = 0; i < instances.length; i++) {
629 images.push([ '/instances/' + instances[i].ID + '/preview',
630 '{0}/{1}'.format(i + 1, instances.length) ])
631 }
632
633 jQuery.slimbox(images, 0, {
634 overlayFadeDuration : 1,
635 resizeDuration : 1,
636 imageFadeDuration : 1,
637 loop : true
638 });
639 })
640 });
641 }
642 });
643
644
645
646
647
648
649 function ChooseDicomModality(callback)
650 {
651 $.ajax({
652 url: '/modalities',
653 type: 'GET',
654 dataType: 'json',
655 async: false,
656 success: function(modalities) {
657 var clickedModality = '';
658 var items = $('<ul>')
659 .attr('data-role', 'listview');
660
661 for (var i = 0; i < modalities.length; i++) {
662 var modality = modalities[i];
663 var item = $('<li>')
664 .html('<a href="#" rel="close">' + modality + '</a>')
665 .attr('modality', modality)
666 .click(function() {
667 clickedModality = $(this).attr('modality');
668 });
669 items.append(item);
670 }
671
672 $('#dialog').simpledialog2({
673 mode: 'blank',
674 animate: false,
675 headerText: 'DICOM modality',
676 headerClose: true,
677 width: '100%',
678 blankContent: items,
679 callbackClose: function() {
680 var timer;
681 function WaitForDialogToClose() {
682 if (!$('#dialog').is(':visible')) {
683 clearInterval(timer);
684 callback(clickedModality);
685 }
686 }
687 timer = setInterval(WaitForDialogToClose, 100);
688 }
689 });
690 }
691 });
692 }
693
694
695 $('#instance-store,#series-store').live('click', function(e) {
696 ChooseDicomModality(function(modality) {
697 if (modality != '') {
698 $.ajax({
699 url: '/modalities/' + modality + '/store',
700 type: 'POST',
701 dataType: 'text',
702 data: $.mobile.pageData.uuid,
703 async: true, // Necessary to block UI
704 beforeSend: function() {
705 $.blockUI({ message: $('#loading') });
706 },
707 complete: function(s) {
708 $.unblockUI();
709 },
710 success: function(s) {
711 console.log('done !');
712 },
713 error: function() {
714 alert('Error during C-Store');
715 }
716 });
717
718 }
719 });
720 });
721
722
723 $('#show-tag-name').live('change', function(e) {
724 var checked = e.currentTarget.checked;
725 if (checked)
726 $('.tag-name').show();
727 else
728 $('.tag-name').hide();
729 });