/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium * Copyright (C) 2017-2023 Osimis S.A., Belgium * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **/ // http://stackoverflow.com/questions/1663741/is-there-a-good-jquery-drag-and-drop-file-upload-plugin // Forbid the access to IE if ($.browser.msie) { alert("Please use Mozilla Firefox or Google Chrome. Microsoft Internet Explorer is not supported."); } // http://jquerymobile.com/demos/1.1.0/docs/api/globalconfig.html //$.mobile.ajaxEnabled = false; //$.mobile.page.prototype.options.addBackBtn = true; //$.mobile.defaultPageTransition = 'slide'; var LIMIT_RESOURCES = 100; var currentPage = ''; var currentUuid = ''; var ACQUISITION_NUMBER = '0020,0012'; var IMAGES_IN_ACQUISITION = '0020,1002'; var IMAGE_ORIENTATION_PATIENT = '0020,0037'; var IMAGE_POSITION_PATIENT = '0020,0032'; var INSTANCE_CREATION_DATE = '0008,0012'; var INSTANCE_CREATION_TIME = '0008,0013'; var INSTANCE_NUMBER = '0020,0013'; var MANUFACTURER = '0008,0070'; var OTHER_PATIENT_IDS = '0010,1000'; var PATIENT_BIRTH_DATE = '0010,0030'; var PATIENT_NAME = '0010,0010'; var SERIES_DATE = '0008,0021'; var SERIES_DESCRIPTION = '0008,103e'; var SERIES_INSTANCE_UID = '0020,000e'; var SERIES_TIME = '0008,0031'; var SOP_INSTANCE_UID = '0008,0018'; var STUDY_DATE = '0008,0020'; var STUDY_DESCRIPTION = '0008,1030'; var STUDY_INSTANCE_UID = '0020,000d'; var STUDY_TIME = '0008,0030'; var ANONYMIZED_FROM = 'AnonymizedFrom'; var MODIFIED_FROM = 'ModifiedFrom'; function IsAlphanumeric(s) { return s.match(/^[0-9a-zA-Z]+$/); } function IsValidLabelName(s) { return s.match(/^[0-9a-zA-Z\-_]+$/); } function DeepCopy(obj) { return jQuery.extend(true, {}, obj); } function ChangePage(page, options) { var first = true; var value; if (options) { for (var key in options) { value = options[key]; if (first) { page += '?'; first = false; } else { page += '&'; } page += key + '=' + value; } } window.location.replace('explorer.html#' + page); /*$.mobile.changePage('#' + page, { changeHash: true });*/ } function Refresh() { if (currentPage == 'patient') RefreshPatient(); else if (currentPage == 'study') RefreshStudy(); else if (currentPage == 'series') RefreshSeries(); else if (currentPage == 'instance') RefreshInstance(); } $(document).ready(function() { var trees = [ '#dicom-tree', '#dicom-metaheader' ]; for (var i = 0; i < trees.length; i++) { $(trees[i]).tree({ autoEscape: false }); $(trees[i]).bind( 'tree.click', function(event) { if (event.node.is_open) $(trees[i]).tree('closeNode', event.node, true); else $(trees[i]).tree('openNode', event.node, true); } ); } // Inject the template of the warning about insecure setup as the // first child of each page var insecure = $('#template-insecure').html(); $('[data-role="page"]>[data-role="content"]').prepend(insecure); currentPage = $.mobile.pageData.active; currentUuid = $.mobile.pageData.uuid; if (!(typeof currentPage === 'undefined') && !(typeof currentUuid === 'undefined') && currentPage.length > 0 && currentUuid.length > 0) { Refresh(); } }); function GetAuthorizationTokensFromUrl() { var urlVariables = window.location.search.substring(1).split('&'); var dict = {}; for (var i = 0; i < urlVariables.length; i++) { var split = urlVariables[i].split('='); if (split.length == 2 && (split[0] == "token" || split[0] == "auth-token" || split[0] == "authorization")) { dict[split[0]] = split[1]; } } return dict; }; var authorizationTokens = GetAuthorizationTokensFromUrl(); /* Copy the authoziation toekn from the url search parameters into HTTP headers in every request to the Rest API. Thanks to this behaviour, you may specify a ?token=xxx in your url and this will be passed as the "token" header in every request to the API allowing you to use the authorization plugin */ $.ajaxSetup( { headers : authorizationTokens } ); function ParseDicomDate(s) { y = parseInt(s.substr(0, 4), 10); m = parseInt(s.substr(4, 2), 10) - 1; d = parseInt(s.substr(6, 2), 10); if (y == null || m == null || d == null || !isFinite(y) || !isFinite(m) || !isFinite(d)) { return null; } if (y < 1900 || y > 2100 || m < 0 || m >= 12 || d <= 0 || d >= 32) { return null; } return new Date(y, m, d); } function FormatDicomDate(s) { if (s == undefined) return "No date"; var d = ParseDicomDate(s); if (d == null) return '?'; else return d.toString('dddd, MMMM d, yyyy'); } function FormatFloatSequence(s) { if (s == undefined || s.length == 0) return "-"; if (s.indexOf("\\") == -1) return s; var oldValues = s.split("\\"); var newValues = []; for (var i = 0; i < oldValues.length; i++) { newValues.push(parseFloat(oldValues[i]).toFixed(3)); } return newValues.join("\\"); } function Sort(arr, fieldExtractor, isInteger, reverse) { var defaultValue; if (isInteger) defaultValue = 0; else defaultValue = ''; arr.sort(function(a, b) { var ta = fieldExtractor(a); var tb = fieldExtractor(b); var order; if (ta == undefined) ta = defaultValue; if (tb == undefined) tb = defaultValue; if (isInteger) { ta = parseInt(ta, 10); tb = parseInt(tb, 10); order = ta - tb; } else { if (ta < tb) order = -1; else if (ta > tb) order = 1; else order = 0; } if (reverse) return -order; else return order; }); } function GetMainDicomTag(mainDicomTags, tag) { if (tag in mainDicomTags) { return mainDicomTags[tag].Value; } else { return ''; } } function SortOnDicomTag(arr, tag, isInteger, reverse) { return Sort(arr, function(a) { return GetMainDicomTag(a.MainDicomTags, tag); }, isInteger, reverse); } function GetResource(uri, callback) { $.ajax({ url: '..' + uri, dataType: 'json', async: false, cache: false, success: function(s) { callback(s); } }); } function CompleteFormatting(node, link, isReverse, count) { if (count != null) { node = node.add($('') .addClass('ui-li-count') .text(count)); } if (link != null && link) { node = $('').attr('href', link).append(node); if (isReverse) node.attr('data-direction', 'reverse') } node = $('
  • ').append(node); if (isReverse) node.attr('data-icon', 'back'); return node; } function FormatMainDicomTags(target, tags, tagsToIgnore) { var v; for (var i in tags) { if (tagsToIgnore.indexOf(i) == -1) { v = GetMainDicomTag(tags, i); if (i == PATIENT_BIRTH_DATE || i == STUDY_DATE || i == SERIES_DATE || i == INSTANCE_CREATION_DATE) { v = FormatDicomDate(v); } else if (i == STUDY_INSTANCE_UID || i == SERIES_INSTANCE_UID || i == SOP_INSTANCE_UID) { // Possibly split a long UID // v = '' + s.substr(0, s.length / 2) + '' + s.substr(s.length / 2, s.length - s.length / 2) + ''; } else if (i == IMAGE_POSITION_PATIENT || i == IMAGE_ORIENTATION_PATIENT) { v = FormatFloatSequence(v); } target.append($('

    ') .text(tags[i].Name + ': ') .append($('').text(v))); } } } function FormatPatient(patient, link, isReverse) { var node = $('

    ').append($('

    ').text(GetMainDicomTag(patient.MainDicomTags, PATIENT_NAME))); FormatMainDicomTags(node, patient.MainDicomTags, [ PATIENT_NAME //, OTHER_PATIENT_IDS ]); return CompleteFormatting(node, link, isReverse, patient.Studies.length); } function FormatStudy(study, link, isReverse, includePatient) { var label; var node; if (includePatient) { label = study.Label; } else { label = GetMainDicomTag(study.MainDicomTags, STUDY_DESCRIPTION); } node = $('
    ').append($('

    ').text(label)); if (includePatient) { FormatMainDicomTags(node, study.PatientMainDicomTags, [ PATIENT_NAME ]); } FormatMainDicomTags(node, study.MainDicomTags, [ STUDY_DESCRIPTION, STUDY_TIME ]); return CompleteFormatting(node, link, isReverse, study.Series.length); } function FormatSeries(series, link, isReverse) { var c; var node; if (series.ExpectedNumberOfInstances == null || series.Instances.length == series.ExpectedNumberOfInstances) { c = series.Instances.length; } else { c = series.Instances.length + '/' + series.ExpectedNumberOfInstances; } node = $('
    ') .append($('

    ').text(GetMainDicomTag(series.MainDicomTags, SERIES_DESCRIPTION))) .append($('

    ').append($('') .text('Status: ') .append($('').text(series.Status)))); FormatMainDicomTags(node, series.MainDicomTags, [ SERIES_DESCRIPTION, SERIES_TIME, MANUFACTURER, IMAGES_IN_ACQUISITION, SERIES_DATE, IMAGE_ORIENTATION_PATIENT ]); return CompleteFormatting(node, link, isReverse, c); } function FormatInstance(instance, link, isReverse) { var node = $('

    ').append($('

    ').text('Instance: ' + instance.IndexInSeries)); FormatMainDicomTags(node, instance.MainDicomTags, [ ACQUISITION_NUMBER, INSTANCE_NUMBER, INSTANCE_CREATION_DATE, INSTANCE_CREATION_TIME, ]); return CompleteFormatting(node, link, isReverse); } $('[data-role="page"]').live('pagebeforeshow', function() { $.ajax({ url: '../system', dataType: 'json', async: false, cache: false, success: function(s) { if (s.Name != "") { $('.orthanc-name').empty(); $('.orthanc-name').append($('') .addClass('ui-link') .attr('href', 'explorer.html') .text(s.Name) .append(' » ')); } // New in Orthanc 1.5.8 if ('IsHttpServerSecure' in s && !s.IsHttpServerSecure) { $('.warning-insecure').show(); } else { $('.warning-insecure').hide(); } // New in Orthanc 1.12.0 if ('HasLabels' in s && s.HasLabels) { $('#lookup-study-labels-div').show(); } else { $('#lookup-study-labels-div').hide(); } } }); }); $('#lookup').live('pagebeforeshow', function() { // NB: "GenerateDicomDate()" is defined in "query-retrieve.js" var target = $('#lookup-study-date'); $('option', target).remove(); target.append($('
  • Download attachment "' + key + '"
  • ') } } target.listview('refresh'); }); } function RefreshLabels(nodeLabels, resourceLevel, resourceId) { GetResource('/' + resourceLevel + '/' + resourceId + '/labels', function(labels) { nodeLabels.empty(); if (labels.length > 0) { nodeLabels.css('display', 'block'); for (var i = 0; i < labels.length; i++) { var removeButton = $('