Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dicomweb-client

Package Overview
Dependencies
Maintainers
9
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dicomweb-client - npm Package Compare versions

Comparing version 0.4.4 to 0.5.0

10

.eslintrc.js

@@ -5,4 +5,10 @@ module.exports = {

"rules": {
"import/extensions": { "js": "always" } // Better for native ES Module usage
"import/extensions": { "js": "always" }, // Better for native ES Module usage
"no-console": 0, // We can remove this later
"no-underscore-dangle": 0,
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }]
},
"env": {
"browser": 1
}
};
};

1056

build/dicomweb-client.es.js

@@ -40,12 +40,15 @@ function _typeof(obj) {

* @param {Uint8Array} array that should be converted
* @param {Number} offset array offset in case only subset of array items should be extracted (default: 0)
* @param {Number} limit maximum number of array items that should be extracted (defaults to length of array)
* @param {Number} offset array offset in case only subset of array items should
be extracted (default: 0)
* @param {Number} limit maximum number of array items that should be extracted
(defaults to length of array)
* @returns {String}
*/
function uint8ArrayToString(arr, offset, limit) {
offset = offset || 0;
limit = limit || arr.length - offset;
var str = '';
function uint8ArrayToString(arr) {
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var limit = arguments.length > 2 ? arguments[2] : undefined;
var itemLimit = limit || arr.length - offset;
var str = "";
for (var i = offset; i < offset + limit; i++) {
for (var i = offset; i < offset + itemLimit; i++) {
str += String.fromCharCode(arr[i]);

@@ -80,9 +83,11 @@ }

function identifyBoundary(header) {
var parts = header.split('\r\n');
var parts = header.split("\r\n");
for (var i = 0; i < parts.length; i++) {
if (parts[i].substr(0, 2) === '--') {
if (parts[i].substr(0, 2) === "--") {
return parts[i];
}
}
return null;
}

@@ -108,5 +113,7 @@ /**

for (var i = 0; i < token.length; i++) {
if (token[i] !== message[index++]) {
if (token[i] !== message[index]) {
return false;
}
index += 1;
}

@@ -148,2 +155,16 @@

/**
* Create a random GUID
*
* @return {string}
*/
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return "".concat(s4() + s4(), "-").concat(s4(), "-").concat(s4(), "-").concat(s4(), "-").concat(s4()).concat(s4()).concat(s4());
}
/**
* @typedef {Object} MultipartEncodedData

@@ -158,5 +179,10 @@ * @property {ArrayBuffer} data The encoded Multipart Data

*
* @param {ArrayBuffer[]} datasets Array containing each file to be encoded in the multipart body, passed as ArrayBuffers.
* @param {String} [boundary] Optional string to define a boundary between each part of the multipart body. If this is not specified, a random GUID will be generated.
* @return {MultipartEncodedData} The Multipart encoded data returned as an Object. This contains both the data itself, and the boundary string used to divide it.
* @param {ArrayBuffer[]} datasets Array containing each file to be encoded in the
multipart body, passed as ArrayBuffers.
* @param {String} [boundary] Optional string to define a boundary between each part
of the multipart body. If this is not specified, a random
GUID will be generated.
* @return {MultipartEncodedData} The Multipart encoded data returned as an Object. This
contains both the data itself, and the boundary string
used to divide it.
*/

@@ -167,3 +193,3 @@

var boundary = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : guid();
var contentType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'application/dicom';
var contentType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "application/dicom";
var contentTypeString = "Content-Type: ".concat(contentType);

@@ -191,3 +217,2 @@ var header = "\r\n--".concat(boundary, "\r\n").concat(contentTypeString, "\r\n\r\n");

contentArrays.forEach(function (contentArray) {
var contentLength = contentArray.length;
multipartArray.set(headerArray, position);

@@ -210,15 +235,16 @@ multipartArray.set(contentArray, position + headerLength);

function multipartDecode(response) {
var message = new Uint8Array(response);
/* Set a maximum length to search for the header boundaries, otherwise
findToken can run for a long time
*/
findToken can run for a long time
*/
var maxSearchLength = 1000; // First look for the multipart mime header
var separator = stringToUint8Array('\r\n\r\n');
var separator = stringToUint8Array("\r\n\r\n");
var headerIndex = findToken(message, separator, 0, maxSearchLength);
if (headerIndex === -1) {
throw new Error('Response message has no multipart mime header');
throw new Error("Response message has no multipart mime header");
}

@@ -230,3 +256,3 @@

if (!boundaryString) {
throw new Error('Header of response message does not specify boundary');
throw new Error("Header of response message does not specify boundary");
}

@@ -250,9 +276,9 @@

var _headerIndex = findToken(message, separator, offset, maxSearchLength);
var headerTokenIndex = findToken(message, separator, offset, maxSearchLength);
if (_headerIndex === -1) {
throw new Error('Response message part has no mime header');
if (headerTokenIndex === -1) {
throw new Error("Response message part has no mime header");
}
offset = _headerIndex + separator.length; // Extract data from response message, excluding "\r\n"
offset = headerTokenIndex + separator.length; // Extract data from response message, excluding "\r\n"

@@ -270,15 +296,5 @@ var spacingLength = 2;

}
/**
* Create a random GUID
*
* @return {string}
*/
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
function isObject(obj) {
return _typeof(obj) === "object" && obj !== null;
}

@@ -294,12 +310,21 @@

var MIMETYPES = {
DICOM: 'application/dicom',
DICOM_JSON: 'application/dicom+json',
OCTET_STREAM: 'application/octet-stream',
JPEG: 'image/jpeg',
PNG: 'image/png'
var getFirstResultIfLengthGtOne = function getFirstResultIfLengthGtOne(result) {
if (result.length > 1) {
return result;
}
return result[0];
};
var MEDIATYPES = {
DICOM: "application/dicom",
DICOM_JSON: "application/dicom+json",
OCTET_STREAM: "application/octet-stream",
PDF: "application/pdf",
JPEG: "image/jpeg",
PNG: "image/png"
};
/**
* Class for interacting with DICOMweb RESTful services.
*/
* Class for interacting with DICOMweb RESTful services.
*/

@@ -310,5 +335,5 @@ var DICOMwebClient =

/**
* @constructor
* @param {Object} options (choices: "url", "username", "password", "headers")
*/
* @constructor
* @param {Object} options (choices: "url", "username", "password", "headers")
*/
function DICOMwebClient(options) {

@@ -320,10 +345,10 @@ _classCallCheck(this, DICOMwebClient);

if (!this.baseURL) {
console.error('no DICOMweb base url provided - calls will fail');
console.error("no DICOMweb base url provided - calls will fail");
}
if ('username' in options) {
if ("username" in options) {
this.username = options.username;
if (!('password' in options)) {
console.error('no password provided to authenticate with DICOMweb service');
if (!("password" in options)) {
console.error("no password provided to authenticate with DICOMweb service");
}

@@ -334,5 +359,5 @@

if ('qidoURLPrefix' in options) {
if ("qidoURLPrefix" in options) {
console.log("use URL prefix for QIDO-RS: ".concat(options.qidoURLPrefix));
this.qidoURL = this.baseURL + '/' + options.qidoURLPrefix;
this.qidoURL = "".concat(this.baseURL, "/").concat(options.qidoURLPrefix);
} else {

@@ -342,5 +367,5 @@ this.qidoURL = this.baseURL;

if ('wadoURLPrefix' in options) {
if ("wadoURLPrefix" in options) {
console.log("use URL prefix for WADO-RS: ".concat(options.wadoURLPrefix));
this.wadoURL = this.baseURL + '/' + options.wadoURLPrefix;
this.wadoURL = "".concat(this.baseURL, "/").concat(options.wadoURLPrefix);
} else {

@@ -350,5 +375,5 @@ this.wadoURL = this.baseURL;

if ('stowURLPrefix' in options) {
if ("stowURLPrefix" in options) {
console.log("use URL prefix for STOW-RS: ".concat(options.stowURLPrefix));
this.stowURL = this.baseURL + '/' + options.stowURLPrefix;
this.stowURL = "".concat(this.baseURL, "/").concat(options.stowURLPrefix);
} else {

@@ -371,7 +396,7 @@ this.stowURL = this.baseURL;

if ('responseType' in options) {
if ("responseType" in options) {
request.responseType = options.responseType;
}
if (_typeof(headers) === 'object') {
if (_typeof(headers) === "object") {
Object.keys(headers).forEach(function (key) {

@@ -389,11 +414,11 @@ request.setRequestHeader(key, headers[key]);

request.onloadstart = function (event) {//console.log('upload started: ', url)
request.onloadstart = function onloadstart() {// console.log('upload started: ', url)
}; // Event triggered when upload ends
request.onloadend = function (event) {//console.log('upload finished')
request.onloadend = function onloadend() {// console.log('upload finished')
}; // Handle response message
request.onreadystatechange = function (event) {
request.onreadystatechange = function onreadystatechange() {
if (request.readyState === 4) {

@@ -403,10 +428,10 @@ if (request.status === 200) {

} else if (request.status === 202) {
console.warn('some resources already existed: ', request);
console.warn("some resources already existed: ", request);
resolve(request.response);
} else if (request.status === 204) {
console.warn('empty response for request: ', request);
console.warn("empty response for request: ", request);
resolve([]);
} else {
console.error('request failed: ', request);
var error = new Error('request failed');
console.error("request failed: ", request);
var error = new Error("request failed");
error.request = request;

@@ -423,4 +448,4 @@ error.response = request.response;

if ('progressCallback' in options) {
if (typeof options.progressCallback === 'function') {
if ("progressCallback" in options) {
if (typeof options.progressCallback === "function") {
request.onprogress = options.progressCallback;

@@ -441,3 +466,3 @@ }

if ('data' in options) {
if ("data" in options) {
request.send(options.data);

@@ -452,3 +477,3 @@ } else {

value: function _httpGet(url, headers, responseType, progressCallback) {
return this._httpRequest(url, 'get', headers, {
return this._httpRequest(url, "get", headers, {
responseType: responseType,

@@ -463,6 +488,7 @@ progressCallback: progressCallback

var progressCallback = arguments.length > 2 ? arguments[2] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === 'object') {
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
url += DICOMwebClient._parseQueryParameters(params);
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}

@@ -472,16 +498,28 @@ }

var headers = {
'Accept': MIMETYPES.DICOM_JSON
Accept: MEDIATYPES.DICOM_JSON
};
var responseType = 'json';
return this._httpGet(url, headers, responseType, progressCallback);
var responseType = "json";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Performs an HTTP GET request that accepts a message with
"application/pdf" media type.
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGetByMimeType",
value: function _httpGetByMimeType(url, mimeType, params) {
var responseType = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'arraybuffer';
var progressCallback = arguments.length > 4 ? arguments[4] : undefined;
key: "_httpGetApplicationPdf",
value: function _httpGetApplicationPdf(url) {
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var progressCallback = arguments.length > 2 ? arguments[2] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === 'object') {
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
url += DICOMwebClient._parseQueryParameters(params);
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}

@@ -491,10 +529,297 @@ }

var headers = {
'Accept': "multipart/related; type=\"".concat(mimeType, "\"")
Accept: MEDIATYPES.PDF
};
return this._httpGet(url, headers, responseType, progressCallback);
var responseType = "json";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Performs an HTTP GET request that accepts a message with an image
media type.
*
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGetImage",
value: function _httpGetImage(url, mediaTypes) {
var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var progressCallback = arguments.length > 3 ? arguments[3] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
var supportedMediaTypes = ["image/", "image/*", "image/jpeg", "image/jp2", "image/gif", "image/png"];
var acceptHeaderFieldValue = DICOMwebClient._buildAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
var headers = {
Accept: acceptHeaderFieldValue
};
var responseType = "arraybuffer";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Performs an HTTP GET request that accepts a message with a text
media type.
*
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGetText",
value: function _httpGetText(url, mediaTypes) {
var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var progressCallback = arguments.length > 3 ? arguments[3] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
var supportedMediaTypes = ["text/", "text/*", "text/html", "text/plain", "text/rtf", "text/xml"];
var acceptHeaderFieldValue = DICOMwebClient._buildAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
var headers = {
Accept: acceptHeaderFieldValue
};
var responseType = "arraybuffer";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Performs an HTTP GET request that accepts a message with a video
media type.
*
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGetVideo",
value: function _httpGetVideo(url, mediaTypes) {
var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var progressCallback = arguments.length > 3 ? arguments[3] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
var supportedMediaTypes = ["video/", "video/*", "video/mpeg", "video/mp4", "video/H265"];
var acceptHeaderFieldValue = DICOMwebClient._buildAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
var headers = {
Accept: acceptHeaderFieldValue
};
var responseType = "arraybuffer";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Asserts that a given media type is valid.
*
* @params {String} mediaType media type
*/
}, {
key: "_httpGetMultipartImage",
/**
* Performs an HTTP GET request that accepts a multipart message with an image media type.
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Array} byteRange start and end of byte range
* @param {Object} params additional HTTP GET query parameters
* @param {Boolean} rendered whether resource should be requested using rendered media types
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
value: function _httpGetMultipartImage(url, mediaTypes, byteRange, params) {
var rendered = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var progressCallback = arguments.length > 5 ? arguments[5] : undefined;
var headers = {};
var supportedMediaTypes;
if (rendered) {
supportedMediaTypes = ["image/jpeg", "image/gif", "image/png", "image/jp2"];
} else {
supportedMediaTypes = {
"1.2.840.10008.1.2.5": ["image/x-dicom-rle"],
"1.2.840.10008.1.2.4.50": ["image/jpeg"],
"1.2.840.10008.1.2.4.51": ["image/jpeg"],
"1.2.840.10008.1.2.4.57": ["image/jpeg"],
"1.2.840.10008.1.2.4.70": ["image/jpeg"],
"1.2.840.10008.1.2.4.80": ["image/x-jls", "image/jls"],
"1.2.840.10008.1.2.4.81": ["image/x-jls", "image/jls"],
"1.2.840.10008.1.2.4.90": ["image/jp2"],
"1.2.840.10008.1.2.4.91": ["image/jp2"],
"1.2.840.10008.1.2.4.92": ["image/jpx"],
"1.2.840.10008.1.2.4.93": ["image/jpx"]
};
if (byteRange) {
headers.Range = DICOMwebClient._buildRangeHeaderFieldValue(byteRange);
}
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(multipartDecode);
}
/**
* Performs an HTTP GET request that accepts a multipart message with a video media type.
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Array} byteRange start and end of byte range
* @param {Object} params additional HTTP GET query parameters
* @param {Boolean} rendered whether resource should be requested using rendered media types
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
}, {
key: "_httpGetMultipartVideo",
value: function _httpGetMultipartVideo(url, mediaTypes, byteRange, params) {
var rendered = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var progressCallback = arguments.length > 5 ? arguments[5] : undefined;
var headers = {};
var supportedMediaTypes;
if (rendered) {
supportedMediaTypes = ["video/", "video/*", "video/mpeg2", "video/mp4", "video/H265"];
} else {
supportedMediaTypes = {
"1.2.840.10008.1.2.4.100": ["video/mpeg2"],
"1.2.840.10008.1.2.4.101": ["video/mpeg2"],
"1.2.840.10008.1.2.4.102": ["video/mp4"],
"1.2.840.10008.1.2.4.103": ["video/mp4"],
"1.2.840.10008.1.2.4.104": ["video/mp4"],
"1.2.840.10008.1.2.4.105": ["video/mp4"],
"1.2.840.10008.1.2.4.106": ["video/mp4"]
};
if (byteRange) {
headers.Range = DICOMwebClient._buildRangeHeaderFieldValue(byteRange);
}
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(multipartDecode);
}
/**
* Performs a HTTP GET request that accepts a multipart message with "application/dicom" media type
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Object} params additional HTTP GET query parameters
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
}, {
key: "_httpGetMultipartApplicationDicom",
value: function _httpGetMultipartApplicationDicom(url, mediaTypes, params, progressCallback) {
var headers = {};
var defaultMediaType = "application/dicom";
var supportedMediaTypes = {
"1.2.840.10008.1.2.1": [defaultMediaType],
"1.2.840.10008.1.2.5": [defaultMediaType],
"1.2.840.10008.1.2.4.50": [defaultMediaType],
"1.2.840.10008.1.2.4.51": [defaultMediaType],
"1.2.840.10008.1.2.4.57": [defaultMediaType],
"1.2.840.10008.1.2.4.70": [defaultMediaType],
"1.2.840.10008.1.2.4.80": [defaultMediaType],
"1.2.840.10008.1.2.4.81": [defaultMediaType],
"1.2.840.10008.1.2.4.90": [defaultMediaType],
"1.2.840.10008.1.2.4.91": [defaultMediaType],
"1.2.840.10008.1.2.4.92": [defaultMediaType],
"1.2.840.10008.1.2.4.93": [defaultMediaType],
"1.2.840.10008.1.2.4.100": [defaultMediaType],
"1.2.840.10008.1.2.4.101": [defaultMediaType],
"1.2.840.10008.1.2.4.102": [defaultMediaType],
"1.2.840.10008.1.2.4.103": [defaultMediaType],
"1.2.840.10008.1.2.4.104": [defaultMediaType],
"1.2.840.10008.1.2.4.105": [defaultMediaType],
"1.2.840.10008.1.2.4.106": [defaultMediaType]
};
var acceptableMediaTypes = mediaTypes;
if (!mediaTypes) {
acceptableMediaTypes = [{
mediaType: defaultMediaType
}];
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(acceptableMediaTypes, supportedMediaTypes);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(multipartDecode);
}
/**
* Performs a HTTP GET request that accepts a multipart message with "application/octet-stream" media type
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Array} byteRange start and end of byte range
* @param {Object} params additional HTTP GET query parameters
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
}, {
key: "_httpGetMultipartApplicationOctetStream",
value: function _httpGetMultipartApplicationOctetStream(url, mediaTypes, byteRange, params, progressCallback) {
var headers = {};
var defaultMediaType = "application/octet-stream";
var supportedMediaTypes = {
"1.2.840.10008.1.2.1": [defaultMediaType]
};
var acceptableMediaTypes = mediaTypes;
if (!mediaTypes) {
acceptableMediaTypes = [{
mediaType: defaultMediaType
}];
}
if (byteRange) {
headers.Range = DICOMwebClient._buildRangeHeaderFieldValue(byteRange);
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(acceptableMediaTypes, supportedMediaTypes);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(multipartDecode);
}
}, {
key: "_httpPost",
value: function _httpPost(url, headers, data, progressCallback) {
return this._httpRequest(url, 'post', headers, {
return this._httpRequest(url, "post", headers, {
data: data,

@@ -508,3 +833,3 @@ progressCallback: progressCallback

var headers = {
'Content-Type': MIMETYPES.DICOM_JSON
"Content-Type": MEDIATYPES.DICOM_JSON
};

@@ -514,2 +839,12 @@ return this._httpPost(url, headers, data, progressCallback);

/**
* Parses media type and extracts its type and subtype.
*
* @param mediaType e.g. image/jpeg
* @private
*/
}, {
key: "searchForStudies",
/**
* Searches for DICOM studies.

@@ -519,11 +854,8 @@ * @param {Object} options options object

*/
}, {
key: "searchForStudies",
value: function searchForStudies() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
console.log('search for studies');
var url = this.qidoURL + '/studies';
console.log("search for studies");
var url = "".concat(this.qidoURL, "/studies");
if ('queryParams' in options) {
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);

@@ -537,3 +869,4 @@ }

* @param {Object} options options object
* @returns {Array} metadata elements in DICOM JSON format for each instance belonging to the study
* @returns {Array} metadata elements in DICOM JSON format for each instance
belonging to the study
*/

@@ -544,8 +877,8 @@

value: function retrieveStudyMetadata(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of study metadata');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of study metadata");
}
console.log("retrieve metadata of study ".concat(options.studyInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/metadata';
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/metadata");
return this._httpGetApplicationJson(url);

@@ -565,10 +898,10 @@ }

if ('studyInstanceUID' in options) {
if ("studyInstanceUID" in options) {
console.log("search series of study ".concat(options.studyInstanceUID));
url += '/studies/' + options.studyInstanceUID;
url += "/studies/".concat(options.studyInstanceUID);
}
url += '/series';
url += "/series";
if ('queryParams' in options) {
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);

@@ -582,3 +915,4 @@ }

* @param {Object} options options object
* @returns {Array} metadata elements in DICOM JSON format for each instance belonging to the series
* @returns {Array} metadata elements in DICOM JSON format for each instance
belonging to the series
*/

@@ -589,12 +923,12 @@

value: function retrieveSeriesMetadata(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of series metadata');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of series metadata");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of series metadata');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of series metadata");
}
console.log("retrieve metadata of series ".concat(options.seriesInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/metadata';
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/metadata");
return this._httpGetApplicationJson(url);

@@ -614,8 +948,8 @@ }

if ('studyInstanceUID' in options) {
url += '/studies/' + options.studyInstanceUID;
if ("studyInstanceUID" in options) {
url += "/studies/".concat(options.studyInstanceUID);
if ('seriesInstanceUID' in options) {
if ("seriesInstanceUID" in options) {
console.log("search for instances of series ".concat(options.seriesInstanceUID));
url += '/series/' + options.seriesInstanceUID;
url += "/series/".concat(options.seriesInstanceUID);
} else {

@@ -625,8 +959,8 @@ console.log("search for instances of study ".concat(options.studyInstanceUID));

} else {
console.log('search for instances');
console.log("search for instances");
}
url += '/instances';
url += "/instances";
if ('queryParams' in options) {
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);

@@ -645,18 +979,18 @@ }

value: function buildInstanceWadoURIUrl(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required.');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required.");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required.');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required.");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required.');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required.");
}
var contentType = options.contentType || MIMETYPES.DICOM;
var transferSyntax = options.transferSyntax || '*';
var contentType = options.contentType || MEDIATYPES.DICOM;
var transferSyntax = options.transferSyntax || "*";
var params = [];
params.push('requestType=WADO');
params.push("requestType=WADO");
params.push("studyUID=".concat(options.studyInstanceUID));

@@ -667,3 +1001,3 @@ params.push("seriesUID=".concat(options.seriesInstanceUID));

params.push("transferSyntax=".concat(transferSyntax));
var paramString = params.join('&');
var paramString = params.join("&");
return "".concat(this.wadoURL, "?").concat(paramString);

@@ -681,16 +1015,16 @@ }

value: function retrieveInstanceMetadata(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of instance metadata');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of instance metadata");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of instance metadata');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of instance metadata");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required for retrieval of instance metadata');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required for retrieval of instance metadata");
}
console.log("retrieve metadata of instance ".concat(options.sopInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/instances/' + options.sopInstanceUID + '/metadata';
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID, "/metadata");
return this._httpGetApplicationJson(url);

@@ -707,24 +1041,85 @@ }

value: function retrieveInstanceFrames(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of instance frames');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of instance frames");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of instance frames');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of instance frames");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required for retrieval of instance frames');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required for retrieval of instance frames");
}
if (!('frameNumbers' in options)) {
throw new Error('frame numbers are required for retrieval of instance frames');
if (!("frameNumbers" in options)) {
throw new Error("frame numbers are required for retrieval of instance frames");
}
console.log("retrieve frames ".concat(options.frameNumbers.toString(), " of instance ").concat(options.sopInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/instances/' + options.sopInstanceUID + '/frames/' + options.frameNumbers.toString();
var mimeType = options.mimeType ? "".concat(options.mimeType) : MIMETYPES.OCTET_STREAM;
return this._httpGetByMimeType(url, mimeType).then(multipartDecode);
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID, "/frames/").concat(options.frameNumbers.toString());
var mediaTypes = options.mediaTypes;
if (!mediaTypes) {
return this._httpGetMultipartApplicationOctetStream(url);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of frames."));
}
/**
* Retrieves an individual, server-side rendered DICOM instance.
*
* @param {Object} options options object
* @returns {Array} frame items as byte arrays of the pixel data element
*/
}, {
key: "retrieveInstanceRendered",
value: function retrieveInstanceRendered(options) {
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of rendered instance");
}
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of rendered instance");
}
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required for retrieval of rendered instance");
}
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID, "/rendered");
var mediaTypes = options.mediaTypes,
params = options.params;
var headers = {};
if (!mediaTypes) {
var responseType = "arraybuffer";
return this._httpGet(url, headers, responseType);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType.startsWith("image")) {
return this._httpGetImage(url, mediaTypes, params);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetVideo(url, mediaTypes, params);
} else if (commonMediaType.startsWith("text")) {
return this._httpGetText(url, mediaTypes, params);
} else if (commonMediaType === MEDIATYPES.PDF) {
return this._httpGetApplicationPdf(url, params);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of rendered instance."));
}
/**
* Retrieves rendered frames for a DICOM instance.

@@ -738,29 +1133,37 @@ * @param {Object} options options object

value: function retrieveInstanceFramesRendered(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of rendered instance frames');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of rendered instance frames");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of rendered instance frames');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of rendered instance frames");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required for retrieval of rendered instance frames');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required for retrieval of rendered instance frames");
}
if (!('frameNumbers' in options)) {
throw new Error('frame numbers are required for retrieval of rendered instance frames');
if (!("frameNumbers" in options)) {
throw new Error("frame numbers are required for retrieval of rendered instance frames");
}
console.log("retrieve rendered frames ".concat(options.frameNumbers.toString(), " of instance ").concat(options.sopInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/instances/' + options.sopInstanceUID + '/frames/' + options.frameNumbers.toString() + '/rendered';
var headers = {}; // The choice of an acceptable media type depends on a variety of things:
// http://dicom.nema.org/medical/dicom/current/output/chtml/part18/chapter_6.html#table_6.1.1-3
console.debug("retrieve rendered frames ".concat(options.frameNumbers.toString(), " of instance ").concat(options.sopInstanceUID));
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID, "/frames/").concat(options.frameNumbers.toString(), "/rendered");
var mediaTypes = options.mediaTypes;
var headers = {};
if ('mimeType' in options) {
headers['Accept'] = options.mimeType;
if (!mediaTypes) {
var responseType = "arraybuffer";
return this._httpGet(url, headers, responseType);
}
var responseType = 'arraybuffer';
return this._httpGet(url, headers, responseType);
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType.startsWith("image")) {
return this._httpGetImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetVideo(url, mediaTypes);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of rendered frame."));
}

@@ -770,3 +1173,3 @@ /**

* @param {Object} options options object
* @returns {Arraybuffer} DICOM Part 10 file as Arraybuffer
* @returns {ArrayBuffer} DICOM Part 10 file as Arraybuffer
*/

@@ -777,16 +1180,34 @@

value: function retrieveInstance(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required");
}
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/instances/' + options.sopInstanceUID;
return this._httpGetByMimeType(url, MIMETYPES.DICOM).then(multipartDecode).then(getFirstResult);
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID);
var mediaTypes = options.mediaTypes;
if (!mediaTypes) {
return this._httpGetMultipartApplicationDicom(url).then(getFirstResult);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.DICOM) {
return this._httpGetMultipartApplicationDicom(url, mediaTypes).then(getFirstResult);
} else if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes).then(getFirstResult);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes).then(getFirstResultIfLengthGtOne);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes).then(getFirstResultIfLengthGtOne);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of instance."));
}

@@ -796,3 +1217,3 @@ /**

* @param {Object} options options object
* @returns {Arraybuffer[]} Array of DICOM Part 10 files as Arraybuffers
* @returns {ArrayBuffer[]} Array of DICOM Part 10 files as Arraybuffers
*/

@@ -803,12 +1224,30 @@

value: function retrieveSeries(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required");
}
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID;
return this._httpGetByMimeType(url, MIMETYPES.DICOM).then(multipartDecode);
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID);
var mediaTypes = options.mediaTypes;
if (!mediaTypes) {
return this._httpGetMultipartApplicationDicom(url);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.DICOM) {
return this._httpGetMultipartApplicationDicom(url, mediaTypes);
} else if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of series."));
}

@@ -818,3 +1257,3 @@ /**

* @param {Object} options options object
* @returns {Arraybuffer[]} Array of DICOM Part 10 files as Arraybuffers
* @returns {ArrayBuffer[]} Array of DICOM Part 10 files as Arraybuffers
*/

@@ -825,8 +1264,26 @@

value: function retrieveStudy(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required");
}
var url = this.wadoURL + '/studies/' + options.studyInstanceUID;
return this._httpGetByMimeType(url, MIMETYPES.DICOM).then(multipartDecode);
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID);
var mediaTypes = options.mediaTypes;
if (!mediaTypes) {
return this._httpGetMultipartApplicationDicom(url);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.DICOM) {
return this._httpGetMultipartApplicationDicom(url, mediaTypes);
} else if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of study."));
}

@@ -847,7 +1304,23 @@ /**

value: function retrieveBulkData(options) {
if (!('BulkDataURI' in options)) {
throw new Error('BulkDataURI is required.');
if (!("BulkDataURI" in options)) {
throw new Error("BulkDataURI is required.");
}
return this._httpGetByMimeType(options.BulkDataURI, MIMETYPES.OCTET_STREAM).then(multipartDecode).then(getFirstResult);
var url = options.BulkDataURI;
var mediaTypes = options.mediaTypes,
byteRange = options.byteRange;
if (!mediaTypes) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes, byteRange);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes, byteRange);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes, byteRange);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of bulk data."));
}

@@ -863,4 +1336,4 @@ /**

value: function storeInstances(options) {
if (!('datasets' in options)) {
throw new Error('datasets are required for storing');
if (!("datasets" in options)) {
throw new Error("datasets are required for storing");
}

@@ -870,3 +1343,3 @@

if ('studyInstanceUID' in options) {
if ("studyInstanceUID" in options) {
url += "/".concat(options.studyInstanceUID);

@@ -880,3 +1353,3 @@ }

var headers = {
'Content-Type': "multipart/related; type=application/dicom; boundary=".concat(boundary)
"Content-Type": "multipart/related; type=application/dicom; boundary=".concat(boundary)
};

@@ -889,12 +1362,205 @@ return this._httpPost(url, headers, data, options.progressCallback);

var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var queryString = '?';
var queryString = "?";
Object.keys(params).forEach(function (key, index) {
if (index !== 0) {
queryString += '&';
queryString += "&";
}
queryString += key + '=' + encodeURIComponent(params[key]);
queryString += "".concat(key, "=").concat(encodeURIComponent(params[key]));
});
return queryString;
}
}, {
key: "_assertMediaTypeIsValid",
value: function _assertMediaTypeIsValid(mediaType) {
if (!mediaType) {
throw new Error("Not a valid media type: ".concat(mediaType));
}
var sepIndex = mediaType.indexOf("/");
if (sepIndex === -1) {
throw new Error("Not a valid media type: ".concat(mediaType));
}
var mediaTypeType = mediaType.slice(0, sepIndex);
var types = ["application", "image", "text", "video"];
if (!types.includes(mediaTypeType)) {
throw new Error("Not a valid media type: ".concat(mediaType));
}
if (mediaType.slice(sepIndex + 1).includes("/")) {
throw new Error("Not a valid media type: ".concat(mediaType));
}
}
}, {
key: "_parseMediaType",
value: function _parseMediaType(mediaType) {
DICOMwebClient._assertMediaTypeIsValid(mediaType);
return mediaType.split("/");
}
/**
* Builds an accept header field value for HTTP GET request messages.
*
* @param {Object[]} mediaTypes Acceptable media types
* @param {Object[]} supportedMediaTypes Supported media types
* @return {*}
* @private
*/
}, {
key: "_buildAcceptHeaderFieldValue",
value: function _buildAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes) {
if (!Array.isArray(mediaTypes)) {
throw new Error("Acceptable media types must be provided as an Array");
}
var fieldValueParts = mediaTypes.map(function (item) {
var mediaType = item.mediaType;
DICOMwebClient._assertMediaTypeIsValid(mediaType);
if (!supportedMediaTypes.includes(mediaType)) {
throw new Error("Media type ".concat(mediaType, " is not supported for requested resource"));
}
return mediaType;
});
return fieldValueParts.join(", ");
}
/**
* Builds an accept header field value for HTTP GET multipart request
messages.
*
* @param {Object[]} mediaTypes Acceptable media types
* @param {Object[]} supportedMediaTypes Supported media types
* @private
*/
}, {
key: "_buildMultipartAcceptHeaderFieldValue",
value: function _buildMultipartAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes) {
if (!Array.isArray(mediaTypes)) {
throw new Error("Acceptable media types must be provided as an Array");
}
if (!Array.isArray(supportedMediaTypes) && !isObject(supportedMediaTypes)) {
throw new Error("Supported media types must be provided as an Array or an Object");
}
var fieldValueParts = [];
mediaTypes.forEach(function (item) {
var transferSyntaxUID = item.transferSyntaxUID,
mediaType = item.mediaType;
DICOMwebClient._assertMediaTypeIsValid(mediaType);
var fieldValue = "multipart/related; type=\"".concat(mediaType, "\"");
if (isObject(supportedMediaTypes)) {
// SupportedMediaTypes is a lookup table that maps Transfer Syntax UID
// to one or more Media Types
if (!Object.values(supportedMediaTypes).flat(1).includes(mediaType)) {
if (!mediaType.endsWith("/*") || !mediaType.endsWith("/")) {
throw new Error("Media type ".concat(mediaType, " is not supported for requested resource"));
}
}
if (transferSyntaxUID) {
if (transferSyntaxUID !== "*") {
if (!Object.keys(supportedMediaTypes).includes(transferSyntaxUID)) {
throw new Error("Transfer syntax ".concat(transferSyntaxUID, " is not supported for requested resource"));
}
var expectedMediaTypes = supportedMediaTypes[transferSyntaxUID];
if (!expectedMediaTypes.includes(mediaType)) {
var actualType = DICOMwebClient._parseMediaType(mediaType)[0];
expectedMediaTypes.map(function (expectedMediaType) {
var expectedType = DICOMwebClient._parseMediaType(expectedMediaType)[0];
var haveSameType = actualType === expectedType;
if (haveSameType && (mediaType.endsWith("/*") || mediaType.endsWith("/"))) {
return;
}
throw new Error("Transfer syntax ".concat(transferSyntaxUID, " is not supported for requested resource"));
});
}
}
fieldValue += "; transfer-syntax=".concat(transferSyntaxUID);
}
} else if (Array.isArray(supportedMediaTypes) && !supportedMediaTypes.includes(mediaType)) {
throw new Error("Media type ".concat(mediaType, " is not supported for requested resource"));
}
fieldValueParts.push(fieldValue);
});
return fieldValueParts.join(", ");
}
/**
* Builds a range header field value for HTTP GET request messages.
*
* @param {Array} byteRange start and end of byte range
* @returns {String} range header field value
*/
}, {
key: "_buildRangeHeaderFieldValue",
value: function _buildRangeHeaderFieldValue() {
var byteRange = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
if (byteRange.length === 1) {
return "bytes=".concat(byteRange[0], "-");
}
if (byteRange.length === 2) {
return "bytes=".concat(byteRange[0], "-").concat(byteRange[1]);
}
return "bytes=0-";
}
/**
* Gets common type of acceptable media types and asserts that only
one type is specified. For example, ``("image/jpeg", "image/jp2")``
will pass, but ``("image/jpeg", "video/mpeg2")`` will raise an
exception.
* @param {String[]} acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
*
*/
}, {
key: "_getCommonMediaType",
value: function _getCommonMediaType(mediaTypes) {
if (!mediaTypes || !mediaTypes.length) {
throw new Error("No acceptable media types provided");
}
var commonMediaTypes = new Set();
mediaTypes.forEach(function (item) {
var mediaType = item.mediaType;
if (mediaType.startsWith("application")) {
commonMediaTypes.add(mediaType);
} else {
var type = DICOMwebClient._parseMediaType(mediaType)[0];
commonMediaTypes.add("".concat(type, "/"));
}
});
if (commonMediaTypes.size === 0) {
throw new Error("No common acceptable media type could be identified.");
} else if (commonMediaTypes.size > 1) {
throw new Error("Acceptable media types must have the same type.");
}
return Array.from(commonMediaTypes)[0];
}
}]);

@@ -917,5 +1583,5 @@

return null;
} else {
return str.substring(beforeIndex, afterIndex);
}
return str.substring(beforeIndex, afterIndex);
}

@@ -934,3 +1600,3 @@

if (!uid) {
console.debug('Study Instance UID could not be dertermined from URI "' + uri + '"');
console.debug("Study Instance UID could not be dertermined from URI \"".concat(uri, "\""));
}

@@ -949,3 +1615,3 @@

if (!uid) {
console.debug('Series Instance UID could not be dertermined from URI "' + uri + '"');
console.debug("Series Instance UID could not be dertermined from URI \"".concat(uri, "\""));
}

@@ -968,3 +1634,3 @@

if (!uid) {
console.debug('SOP Instance UID could not be dertermined from URI"' + uri + '"');
console.debug("SOP Instance UID could not be dertermined from URI\"".concat(uri, "\""));
}

@@ -983,9 +1649,9 @@

if (numbers === undefined) {
console.debug('Frames Numbers could not be dertermined from URI"' + uri + '"');
console.debug("Frames Numbers could not be dertermined from URI\"".concat(uri, "\""));
}
return numbers.split(',');
return numbers.split(",");
}
var version = '0.3.2';
var version = "0.5.0";

@@ -992,0 +1658,0 @@ var api = {

@@ -46,12 +46,15 @@ (function (global, factory) {

* @param {Uint8Array} array that should be converted
* @param {Number} offset array offset in case only subset of array items should be extracted (default: 0)
* @param {Number} limit maximum number of array items that should be extracted (defaults to length of array)
* @param {Number} offset array offset in case only subset of array items should
be extracted (default: 0)
* @param {Number} limit maximum number of array items that should be extracted
(defaults to length of array)
* @returns {String}
*/
function uint8ArrayToString(arr, offset, limit) {
offset = offset || 0;
limit = limit || arr.length - offset;
var str = '';
function uint8ArrayToString(arr) {
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var limit = arguments.length > 2 ? arguments[2] : undefined;
var itemLimit = limit || arr.length - offset;
var str = "";
for (var i = offset; i < offset + limit; i++) {
for (var i = offset; i < offset + itemLimit; i++) {
str += String.fromCharCode(arr[i]);

@@ -86,9 +89,11 @@ }

function identifyBoundary(header) {
var parts = header.split('\r\n');
var parts = header.split("\r\n");
for (var i = 0; i < parts.length; i++) {
if (parts[i].substr(0, 2) === '--') {
if (parts[i].substr(0, 2) === "--") {
return parts[i];
}
}
return null;
}

@@ -114,5 +119,7 @@ /**

for (var i = 0; i < token.length; i++) {
if (token[i] !== message[index++]) {
if (token[i] !== message[index]) {
return false;
}
index += 1;
}

@@ -154,2 +161,16 @@

/**
* Create a random GUID
*
* @return {string}
*/
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return "".concat(s4() + s4(), "-").concat(s4(), "-").concat(s4(), "-").concat(s4(), "-").concat(s4()).concat(s4()).concat(s4());
}
/**
* @typedef {Object} MultipartEncodedData

@@ -164,5 +185,10 @@ * @property {ArrayBuffer} data The encoded Multipart Data

*
* @param {ArrayBuffer[]} datasets Array containing each file to be encoded in the multipart body, passed as ArrayBuffers.
* @param {String} [boundary] Optional string to define a boundary between each part of the multipart body. If this is not specified, a random GUID will be generated.
* @return {MultipartEncodedData} The Multipart encoded data returned as an Object. This contains both the data itself, and the boundary string used to divide it.
* @param {ArrayBuffer[]} datasets Array containing each file to be encoded in the
multipart body, passed as ArrayBuffers.
* @param {String} [boundary] Optional string to define a boundary between each part
of the multipart body. If this is not specified, a random
GUID will be generated.
* @return {MultipartEncodedData} The Multipart encoded data returned as an Object. This
contains both the data itself, and the boundary string
used to divide it.
*/

@@ -173,3 +199,3 @@

var boundary = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : guid();
var contentType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'application/dicom';
var contentType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "application/dicom";
var contentTypeString = "Content-Type: ".concat(contentType);

@@ -197,3 +223,2 @@ var header = "\r\n--".concat(boundary, "\r\n").concat(contentTypeString, "\r\n\r\n");

contentArrays.forEach(function (contentArray) {
var contentLength = contentArray.length;
multipartArray.set(headerArray, position);

@@ -216,15 +241,16 @@ multipartArray.set(contentArray, position + headerLength);

function multipartDecode(response) {
var message = new Uint8Array(response);
/* Set a maximum length to search for the header boundaries, otherwise
findToken can run for a long time
*/
findToken can run for a long time
*/
var maxSearchLength = 1000; // First look for the multipart mime header
var separator = stringToUint8Array('\r\n\r\n');
var separator = stringToUint8Array("\r\n\r\n");
var headerIndex = findToken(message, separator, 0, maxSearchLength);
if (headerIndex === -1) {
throw new Error('Response message has no multipart mime header');
throw new Error("Response message has no multipart mime header");
}

@@ -236,3 +262,3 @@

if (!boundaryString) {
throw new Error('Header of response message does not specify boundary');
throw new Error("Header of response message does not specify boundary");
}

@@ -256,9 +282,9 @@

var _headerIndex = findToken(message, separator, offset, maxSearchLength);
var headerTokenIndex = findToken(message, separator, offset, maxSearchLength);
if (_headerIndex === -1) {
throw new Error('Response message part has no mime header');
if (headerTokenIndex === -1) {
throw new Error("Response message part has no mime header");
}
offset = _headerIndex + separator.length; // Extract data from response message, excluding "\r\n"
offset = headerTokenIndex + separator.length; // Extract data from response message, excluding "\r\n"

@@ -276,15 +302,5 @@ var spacingLength = 2;

}
/**
* Create a random GUID
*
* @return {string}
*/
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
function isObject(obj) {
return _typeof(obj) === "object" && obj !== null;
}

@@ -300,12 +316,21 @@

var MIMETYPES = {
DICOM: 'application/dicom',
DICOM_JSON: 'application/dicom+json',
OCTET_STREAM: 'application/octet-stream',
JPEG: 'image/jpeg',
PNG: 'image/png'
var getFirstResultIfLengthGtOne = function getFirstResultIfLengthGtOne(result) {
if (result.length > 1) {
return result;
}
return result[0];
};
var MEDIATYPES = {
DICOM: "application/dicom",
DICOM_JSON: "application/dicom+json",
OCTET_STREAM: "application/octet-stream",
PDF: "application/pdf",
JPEG: "image/jpeg",
PNG: "image/png"
};
/**
* Class for interacting with DICOMweb RESTful services.
*/
* Class for interacting with DICOMweb RESTful services.
*/

@@ -316,5 +341,5 @@ var DICOMwebClient =

/**
* @constructor
* @param {Object} options (choices: "url", "username", "password", "headers")
*/
* @constructor
* @param {Object} options (choices: "url", "username", "password", "headers")
*/
function DICOMwebClient(options) {

@@ -326,10 +351,10 @@ _classCallCheck(this, DICOMwebClient);

if (!this.baseURL) {
console.error('no DICOMweb base url provided - calls will fail');
console.error("no DICOMweb base url provided - calls will fail");
}
if ('username' in options) {
if ("username" in options) {
this.username = options.username;
if (!('password' in options)) {
console.error('no password provided to authenticate with DICOMweb service');
if (!("password" in options)) {
console.error("no password provided to authenticate with DICOMweb service");
}

@@ -340,5 +365,5 @@

if ('qidoURLPrefix' in options) {
if ("qidoURLPrefix" in options) {
console.log("use URL prefix for QIDO-RS: ".concat(options.qidoURLPrefix));
this.qidoURL = this.baseURL + '/' + options.qidoURLPrefix;
this.qidoURL = "".concat(this.baseURL, "/").concat(options.qidoURLPrefix);
} else {

@@ -348,5 +373,5 @@ this.qidoURL = this.baseURL;

if ('wadoURLPrefix' in options) {
if ("wadoURLPrefix" in options) {
console.log("use URL prefix for WADO-RS: ".concat(options.wadoURLPrefix));
this.wadoURL = this.baseURL + '/' + options.wadoURLPrefix;
this.wadoURL = "".concat(this.baseURL, "/").concat(options.wadoURLPrefix);
} else {

@@ -356,5 +381,5 @@ this.wadoURL = this.baseURL;

if ('stowURLPrefix' in options) {
if ("stowURLPrefix" in options) {
console.log("use URL prefix for STOW-RS: ".concat(options.stowURLPrefix));
this.stowURL = this.baseURL + '/' + options.stowURLPrefix;
this.stowURL = "".concat(this.baseURL, "/").concat(options.stowURLPrefix);
} else {

@@ -377,7 +402,7 @@ this.stowURL = this.baseURL;

if ('responseType' in options) {
if ("responseType" in options) {
request.responseType = options.responseType;
}
if (_typeof(headers) === 'object') {
if (_typeof(headers) === "object") {
Object.keys(headers).forEach(function (key) {

@@ -395,11 +420,11 @@ request.setRequestHeader(key, headers[key]);

request.onloadstart = function (event) {//console.log('upload started: ', url)
request.onloadstart = function onloadstart() {// console.log('upload started: ', url)
}; // Event triggered when upload ends
request.onloadend = function (event) {//console.log('upload finished')
request.onloadend = function onloadend() {// console.log('upload finished')
}; // Handle response message
request.onreadystatechange = function (event) {
request.onreadystatechange = function onreadystatechange() {
if (request.readyState === 4) {

@@ -409,10 +434,10 @@ if (request.status === 200) {

} else if (request.status === 202) {
console.warn('some resources already existed: ', request);
console.warn("some resources already existed: ", request);
resolve(request.response);
} else if (request.status === 204) {
console.warn('empty response for request: ', request);
console.warn("empty response for request: ", request);
resolve([]);
} else {
console.error('request failed: ', request);
var error = new Error('request failed');
console.error("request failed: ", request);
var error = new Error("request failed");
error.request = request;

@@ -429,4 +454,4 @@ error.response = request.response;

if ('progressCallback' in options) {
if (typeof options.progressCallback === 'function') {
if ("progressCallback" in options) {
if (typeof options.progressCallback === "function") {
request.onprogress = options.progressCallback;

@@ -447,3 +472,3 @@ }

if ('data' in options) {
if ("data" in options) {
request.send(options.data);

@@ -458,3 +483,3 @@ } else {

value: function _httpGet(url, headers, responseType, progressCallback) {
return this._httpRequest(url, 'get', headers, {
return this._httpRequest(url, "get", headers, {
responseType: responseType,

@@ -469,6 +494,7 @@ progressCallback: progressCallback

var progressCallback = arguments.length > 2 ? arguments[2] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === 'object') {
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
url += DICOMwebClient._parseQueryParameters(params);
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}

@@ -478,16 +504,28 @@ }

var headers = {
'Accept': MIMETYPES.DICOM_JSON
Accept: MEDIATYPES.DICOM_JSON
};
var responseType = 'json';
return this._httpGet(url, headers, responseType, progressCallback);
var responseType = "json";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Performs an HTTP GET request that accepts a message with
"application/pdf" media type.
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGetByMimeType",
value: function _httpGetByMimeType(url, mimeType, params) {
var responseType = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'arraybuffer';
var progressCallback = arguments.length > 4 ? arguments[4] : undefined;
key: "_httpGetApplicationPdf",
value: function _httpGetApplicationPdf(url) {
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var progressCallback = arguments.length > 2 ? arguments[2] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === 'object') {
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
url += DICOMwebClient._parseQueryParameters(params);
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}

@@ -497,10 +535,297 @@ }

var headers = {
'Accept': "multipart/related; type=\"".concat(mimeType, "\"")
Accept: MEDIATYPES.PDF
};
return this._httpGet(url, headers, responseType, progressCallback);
var responseType = "json";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Performs an HTTP GET request that accepts a message with an image
media type.
*
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGetImage",
value: function _httpGetImage(url, mediaTypes) {
var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var progressCallback = arguments.length > 3 ? arguments[3] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
var supportedMediaTypes = ["image/", "image/*", "image/jpeg", "image/jp2", "image/gif", "image/png"];
var acceptHeaderFieldValue = DICOMwebClient._buildAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
var headers = {
Accept: acceptHeaderFieldValue
};
var responseType = "arraybuffer";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Performs an HTTP GET request that accepts a message with a text
media type.
*
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGetText",
value: function _httpGetText(url, mediaTypes) {
var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var progressCallback = arguments.length > 3 ? arguments[3] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
var supportedMediaTypes = ["text/", "text/*", "text/html", "text/plain", "text/rtf", "text/xml"];
var acceptHeaderFieldValue = DICOMwebClient._buildAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
var headers = {
Accept: acceptHeaderFieldValue
};
var responseType = "arraybuffer";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Performs an HTTP GET request that accepts a message with a video
media type.
*
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGetVideo",
value: function _httpGetVideo(url, mediaTypes) {
var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var progressCallback = arguments.length > 3 ? arguments[3] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
var supportedMediaTypes = ["video/", "video/*", "video/mpeg", "video/mp4", "video/H265"];
var acceptHeaderFieldValue = DICOMwebClient._buildAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
var headers = {
Accept: acceptHeaderFieldValue
};
var responseType = "arraybuffer";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback);
}
/**
* Asserts that a given media type is valid.
*
* @params {String} mediaType media type
*/
}, {
key: "_httpGetMultipartImage",
/**
* Performs an HTTP GET request that accepts a multipart message with an image media type.
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Array} byteRange start and end of byte range
* @param {Object} params additional HTTP GET query parameters
* @param {Boolean} rendered whether resource should be requested using rendered media types
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
value: function _httpGetMultipartImage(url, mediaTypes, byteRange, params) {
var rendered = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var progressCallback = arguments.length > 5 ? arguments[5] : undefined;
var headers = {};
var supportedMediaTypes;
if (rendered) {
supportedMediaTypes = ["image/jpeg", "image/gif", "image/png", "image/jp2"];
} else {
supportedMediaTypes = {
"1.2.840.10008.1.2.5": ["image/x-dicom-rle"],
"1.2.840.10008.1.2.4.50": ["image/jpeg"],
"1.2.840.10008.1.2.4.51": ["image/jpeg"],
"1.2.840.10008.1.2.4.57": ["image/jpeg"],
"1.2.840.10008.1.2.4.70": ["image/jpeg"],
"1.2.840.10008.1.2.4.80": ["image/x-jls", "image/jls"],
"1.2.840.10008.1.2.4.81": ["image/x-jls", "image/jls"],
"1.2.840.10008.1.2.4.90": ["image/jp2"],
"1.2.840.10008.1.2.4.91": ["image/jp2"],
"1.2.840.10008.1.2.4.92": ["image/jpx"],
"1.2.840.10008.1.2.4.93": ["image/jpx"]
};
if (byteRange) {
headers.Range = DICOMwebClient._buildRangeHeaderFieldValue(byteRange);
}
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(multipartDecode);
}
/**
* Performs an HTTP GET request that accepts a multipart message with a video media type.
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Array} byteRange start and end of byte range
* @param {Object} params additional HTTP GET query parameters
* @param {Boolean} rendered whether resource should be requested using rendered media types
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
}, {
key: "_httpGetMultipartVideo",
value: function _httpGetMultipartVideo(url, mediaTypes, byteRange, params) {
var rendered = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var progressCallback = arguments.length > 5 ? arguments[5] : undefined;
var headers = {};
var supportedMediaTypes;
if (rendered) {
supportedMediaTypes = ["video/", "video/*", "video/mpeg2", "video/mp4", "video/H265"];
} else {
supportedMediaTypes = {
"1.2.840.10008.1.2.4.100": ["video/mpeg2"],
"1.2.840.10008.1.2.4.101": ["video/mpeg2"],
"1.2.840.10008.1.2.4.102": ["video/mp4"],
"1.2.840.10008.1.2.4.103": ["video/mp4"],
"1.2.840.10008.1.2.4.104": ["video/mp4"],
"1.2.840.10008.1.2.4.105": ["video/mp4"],
"1.2.840.10008.1.2.4.106": ["video/mp4"]
};
if (byteRange) {
headers.Range = DICOMwebClient._buildRangeHeaderFieldValue(byteRange);
}
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(multipartDecode);
}
/**
* Performs a HTTP GET request that accepts a multipart message with "application/dicom" media type
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Object} params additional HTTP GET query parameters
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
}, {
key: "_httpGetMultipartApplicationDicom",
value: function _httpGetMultipartApplicationDicom(url, mediaTypes, params, progressCallback) {
var headers = {};
var defaultMediaType = "application/dicom";
var supportedMediaTypes = {
"1.2.840.10008.1.2.1": [defaultMediaType],
"1.2.840.10008.1.2.5": [defaultMediaType],
"1.2.840.10008.1.2.4.50": [defaultMediaType],
"1.2.840.10008.1.2.4.51": [defaultMediaType],
"1.2.840.10008.1.2.4.57": [defaultMediaType],
"1.2.840.10008.1.2.4.70": [defaultMediaType],
"1.2.840.10008.1.2.4.80": [defaultMediaType],
"1.2.840.10008.1.2.4.81": [defaultMediaType],
"1.2.840.10008.1.2.4.90": [defaultMediaType],
"1.2.840.10008.1.2.4.91": [defaultMediaType],
"1.2.840.10008.1.2.4.92": [defaultMediaType],
"1.2.840.10008.1.2.4.93": [defaultMediaType],
"1.2.840.10008.1.2.4.100": [defaultMediaType],
"1.2.840.10008.1.2.4.101": [defaultMediaType],
"1.2.840.10008.1.2.4.102": [defaultMediaType],
"1.2.840.10008.1.2.4.103": [defaultMediaType],
"1.2.840.10008.1.2.4.104": [defaultMediaType],
"1.2.840.10008.1.2.4.105": [defaultMediaType],
"1.2.840.10008.1.2.4.106": [defaultMediaType]
};
var acceptableMediaTypes = mediaTypes;
if (!mediaTypes) {
acceptableMediaTypes = [{
mediaType: defaultMediaType
}];
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(acceptableMediaTypes, supportedMediaTypes);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(multipartDecode);
}
/**
* Performs a HTTP GET request that accepts a multipart message with "application/octet-stream" media type
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Array} byteRange start and end of byte range
* @param {Object} params additional HTTP GET query parameters
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
}, {
key: "_httpGetMultipartApplicationOctetStream",
value: function _httpGetMultipartApplicationOctetStream(url, mediaTypes, byteRange, params, progressCallback) {
var headers = {};
var defaultMediaType = "application/octet-stream";
var supportedMediaTypes = {
"1.2.840.10008.1.2.1": [defaultMediaType]
};
var acceptableMediaTypes = mediaTypes;
if (!mediaTypes) {
acceptableMediaTypes = [{
mediaType: defaultMediaType
}];
}
if (byteRange) {
headers.Range = DICOMwebClient._buildRangeHeaderFieldValue(byteRange);
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(acceptableMediaTypes, supportedMediaTypes);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(multipartDecode);
}
}, {
key: "_httpPost",
value: function _httpPost(url, headers, data, progressCallback) {
return this._httpRequest(url, 'post', headers, {
return this._httpRequest(url, "post", headers, {
data: data,

@@ -514,3 +839,3 @@ progressCallback: progressCallback

var headers = {
'Content-Type': MIMETYPES.DICOM_JSON
"Content-Type": MEDIATYPES.DICOM_JSON
};

@@ -520,2 +845,12 @@ return this._httpPost(url, headers, data, progressCallback);

/**
* Parses media type and extracts its type and subtype.
*
* @param mediaType e.g. image/jpeg
* @private
*/
}, {
key: "searchForStudies",
/**
* Searches for DICOM studies.

@@ -525,11 +860,8 @@ * @param {Object} options options object

*/
}, {
key: "searchForStudies",
value: function searchForStudies() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
console.log('search for studies');
var url = this.qidoURL + '/studies';
console.log("search for studies");
var url = "".concat(this.qidoURL, "/studies");
if ('queryParams' in options) {
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);

@@ -543,3 +875,4 @@ }

* @param {Object} options options object
* @returns {Array} metadata elements in DICOM JSON format for each instance belonging to the study
* @returns {Array} metadata elements in DICOM JSON format for each instance
belonging to the study
*/

@@ -550,8 +883,8 @@

value: function retrieveStudyMetadata(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of study metadata');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of study metadata");
}
console.log("retrieve metadata of study ".concat(options.studyInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/metadata';
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/metadata");
return this._httpGetApplicationJson(url);

@@ -571,10 +904,10 @@ }

if ('studyInstanceUID' in options) {
if ("studyInstanceUID" in options) {
console.log("search series of study ".concat(options.studyInstanceUID));
url += '/studies/' + options.studyInstanceUID;
url += "/studies/".concat(options.studyInstanceUID);
}
url += '/series';
url += "/series";
if ('queryParams' in options) {
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);

@@ -588,3 +921,4 @@ }

* @param {Object} options options object
* @returns {Array} metadata elements in DICOM JSON format for each instance belonging to the series
* @returns {Array} metadata elements in DICOM JSON format for each instance
belonging to the series
*/

@@ -595,12 +929,12 @@

value: function retrieveSeriesMetadata(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of series metadata');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of series metadata");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of series metadata');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of series metadata");
}
console.log("retrieve metadata of series ".concat(options.seriesInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/metadata';
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/metadata");
return this._httpGetApplicationJson(url);

@@ -620,8 +954,8 @@ }

if ('studyInstanceUID' in options) {
url += '/studies/' + options.studyInstanceUID;
if ("studyInstanceUID" in options) {
url += "/studies/".concat(options.studyInstanceUID);
if ('seriesInstanceUID' in options) {
if ("seriesInstanceUID" in options) {
console.log("search for instances of series ".concat(options.seriesInstanceUID));
url += '/series/' + options.seriesInstanceUID;
url += "/series/".concat(options.seriesInstanceUID);
} else {

@@ -631,8 +965,8 @@ console.log("search for instances of study ".concat(options.studyInstanceUID));

} else {
console.log('search for instances');
console.log("search for instances");
}
url += '/instances';
url += "/instances";
if ('queryParams' in options) {
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);

@@ -651,18 +985,18 @@ }

value: function buildInstanceWadoURIUrl(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required.');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required.");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required.');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required.");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required.');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required.");
}
var contentType = options.contentType || MIMETYPES.DICOM;
var transferSyntax = options.transferSyntax || '*';
var contentType = options.contentType || MEDIATYPES.DICOM;
var transferSyntax = options.transferSyntax || "*";
var params = [];
params.push('requestType=WADO');
params.push("requestType=WADO");
params.push("studyUID=".concat(options.studyInstanceUID));

@@ -673,3 +1007,3 @@ params.push("seriesUID=".concat(options.seriesInstanceUID));

params.push("transferSyntax=".concat(transferSyntax));
var paramString = params.join('&');
var paramString = params.join("&");
return "".concat(this.wadoURL, "?").concat(paramString);

@@ -687,16 +1021,16 @@ }

value: function retrieveInstanceMetadata(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of instance metadata');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of instance metadata");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of instance metadata');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of instance metadata");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required for retrieval of instance metadata');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required for retrieval of instance metadata");
}
console.log("retrieve metadata of instance ".concat(options.sopInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/instances/' + options.sopInstanceUID + '/metadata';
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID, "/metadata");
return this._httpGetApplicationJson(url);

@@ -713,24 +1047,85 @@ }

value: function retrieveInstanceFrames(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of instance frames');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of instance frames");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of instance frames');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of instance frames");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required for retrieval of instance frames');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required for retrieval of instance frames");
}
if (!('frameNumbers' in options)) {
throw new Error('frame numbers are required for retrieval of instance frames');
if (!("frameNumbers" in options)) {
throw new Error("frame numbers are required for retrieval of instance frames");
}
console.log("retrieve frames ".concat(options.frameNumbers.toString(), " of instance ").concat(options.sopInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/instances/' + options.sopInstanceUID + '/frames/' + options.frameNumbers.toString();
var mimeType = options.mimeType ? "".concat(options.mimeType) : MIMETYPES.OCTET_STREAM;
return this._httpGetByMimeType(url, mimeType).then(multipartDecode);
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID, "/frames/").concat(options.frameNumbers.toString());
var mediaTypes = options.mediaTypes;
if (!mediaTypes) {
return this._httpGetMultipartApplicationOctetStream(url);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of frames."));
}
/**
* Retrieves an individual, server-side rendered DICOM instance.
*
* @param {Object} options options object
* @returns {Array} frame items as byte arrays of the pixel data element
*/
}, {
key: "retrieveInstanceRendered",
value: function retrieveInstanceRendered(options) {
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of rendered instance");
}
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of rendered instance");
}
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required for retrieval of rendered instance");
}
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID, "/rendered");
var mediaTypes = options.mediaTypes,
params = options.params;
var headers = {};
if (!mediaTypes) {
var responseType = "arraybuffer";
return this._httpGet(url, headers, responseType);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType.startsWith("image")) {
return this._httpGetImage(url, mediaTypes, params);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetVideo(url, mediaTypes, params);
} else if (commonMediaType.startsWith("text")) {
return this._httpGetText(url, mediaTypes, params);
} else if (commonMediaType === MEDIATYPES.PDF) {
return this._httpGetApplicationPdf(url, params);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of rendered instance."));
}
/**
* Retrieves rendered frames for a DICOM instance.

@@ -744,29 +1139,37 @@ * @param {Object} options options object

value: function retrieveInstanceFramesRendered(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of rendered instance frames');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required for retrieval of rendered instance frames");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of rendered instance frames');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required for retrieval of rendered instance frames");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required for retrieval of rendered instance frames');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required for retrieval of rendered instance frames");
}
if (!('frameNumbers' in options)) {
throw new Error('frame numbers are required for retrieval of rendered instance frames');
if (!("frameNumbers" in options)) {
throw new Error("frame numbers are required for retrieval of rendered instance frames");
}
console.log("retrieve rendered frames ".concat(options.frameNumbers.toString(), " of instance ").concat(options.sopInstanceUID));
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/instances/' + options.sopInstanceUID + '/frames/' + options.frameNumbers.toString() + '/rendered';
var headers = {}; // The choice of an acceptable media type depends on a variety of things:
// http://dicom.nema.org/medical/dicom/current/output/chtml/part18/chapter_6.html#table_6.1.1-3
console.debug("retrieve rendered frames ".concat(options.frameNumbers.toString(), " of instance ").concat(options.sopInstanceUID));
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID, "/frames/").concat(options.frameNumbers.toString(), "/rendered");
var mediaTypes = options.mediaTypes;
var headers = {};
if ('mimeType' in options) {
headers['Accept'] = options.mimeType;
if (!mediaTypes) {
var responseType = "arraybuffer";
return this._httpGet(url, headers, responseType);
}
var responseType = 'arraybuffer';
return this._httpGet(url, headers, responseType);
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType.startsWith("image")) {
return this._httpGetImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetVideo(url, mediaTypes);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of rendered frame."));
}

@@ -776,3 +1179,3 @@ /**

* @param {Object} options options object
* @returns {Arraybuffer} DICOM Part 10 file as Arraybuffer
* @returns {ArrayBuffer} DICOM Part 10 file as Arraybuffer
*/

@@ -783,16 +1186,34 @@

value: function retrieveInstance(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required');
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required");
}
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID + '/instances/' + options.sopInstanceUID;
return this._httpGetByMimeType(url, MIMETYPES.DICOM).then(multipartDecode).then(getFirstResult);
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID);
var mediaTypes = options.mediaTypes;
if (!mediaTypes) {
return this._httpGetMultipartApplicationDicom(url).then(getFirstResult);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.DICOM) {
return this._httpGetMultipartApplicationDicom(url, mediaTypes).then(getFirstResult);
} else if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes).then(getFirstResult);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes).then(getFirstResultIfLengthGtOne);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes).then(getFirstResultIfLengthGtOne);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of instance."));
}

@@ -802,3 +1223,3 @@ /**

* @param {Object} options options object
* @returns {Arraybuffer[]} Array of DICOM Part 10 files as Arraybuffers
* @returns {ArrayBuffer[]} Array of DICOM Part 10 files as Arraybuffers
*/

@@ -809,12 +1230,30 @@

value: function retrieveSeries(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required');
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required");
}
var url = this.wadoURL + '/studies/' + options.studyInstanceUID + '/series/' + options.seriesInstanceUID;
return this._httpGetByMimeType(url, MIMETYPES.DICOM).then(multipartDecode);
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID);
var mediaTypes = options.mediaTypes;
if (!mediaTypes) {
return this._httpGetMultipartApplicationDicom(url);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.DICOM) {
return this._httpGetMultipartApplicationDicom(url, mediaTypes);
} else if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of series."));
}

@@ -824,3 +1263,3 @@ /**

* @param {Object} options options object
* @returns {Arraybuffer[]} Array of DICOM Part 10 files as Arraybuffers
* @returns {ArrayBuffer[]} Array of DICOM Part 10 files as Arraybuffers
*/

@@ -831,8 +1270,26 @@

value: function retrieveStudy(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required');
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required");
}
var url = this.wadoURL + '/studies/' + options.studyInstanceUID;
return this._httpGetByMimeType(url, MIMETYPES.DICOM).then(multipartDecode);
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID);
var mediaTypes = options.mediaTypes;
if (!mediaTypes) {
return this._httpGetMultipartApplicationDicom(url);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.DICOM) {
return this._httpGetMultipartApplicationDicom(url, mediaTypes);
} else if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of study."));
}

@@ -853,7 +1310,23 @@ /**

value: function retrieveBulkData(options) {
if (!('BulkDataURI' in options)) {
throw new Error('BulkDataURI is required.');
if (!("BulkDataURI" in options)) {
throw new Error("BulkDataURI is required.");
}
return this._httpGetByMimeType(options.BulkDataURI, MIMETYPES.OCTET_STREAM).then(multipartDecode).then(getFirstResult);
var url = options.BulkDataURI;
var mediaTypes = options.mediaTypes,
byteRange = options.byteRange;
if (!mediaTypes) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes, byteRange);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes, byteRange);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes, byteRange);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported for retrieval of bulk data."));
}

@@ -869,4 +1342,4 @@ /**

value: function storeInstances(options) {
if (!('datasets' in options)) {
throw new Error('datasets are required for storing');
if (!("datasets" in options)) {
throw new Error("datasets are required for storing");
}

@@ -876,3 +1349,3 @@

if ('studyInstanceUID' in options) {
if ("studyInstanceUID" in options) {
url += "/".concat(options.studyInstanceUID);

@@ -886,3 +1359,3 @@ }

var headers = {
'Content-Type': "multipart/related; type=application/dicom; boundary=".concat(boundary)
"Content-Type": "multipart/related; type=application/dicom; boundary=".concat(boundary)
};

@@ -895,12 +1368,205 @@ return this._httpPost(url, headers, data, options.progressCallback);

var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var queryString = '?';
var queryString = "?";
Object.keys(params).forEach(function (key, index) {
if (index !== 0) {
queryString += '&';
queryString += "&";
}
queryString += key + '=' + encodeURIComponent(params[key]);
queryString += "".concat(key, "=").concat(encodeURIComponent(params[key]));
});
return queryString;
}
}, {
key: "_assertMediaTypeIsValid",
value: function _assertMediaTypeIsValid(mediaType) {
if (!mediaType) {
throw new Error("Not a valid media type: ".concat(mediaType));
}
var sepIndex = mediaType.indexOf("/");
if (sepIndex === -1) {
throw new Error("Not a valid media type: ".concat(mediaType));
}
var mediaTypeType = mediaType.slice(0, sepIndex);
var types = ["application", "image", "text", "video"];
if (!types.includes(mediaTypeType)) {
throw new Error("Not a valid media type: ".concat(mediaType));
}
if (mediaType.slice(sepIndex + 1).includes("/")) {
throw new Error("Not a valid media type: ".concat(mediaType));
}
}
}, {
key: "_parseMediaType",
value: function _parseMediaType(mediaType) {
DICOMwebClient._assertMediaTypeIsValid(mediaType);
return mediaType.split("/");
}
/**
* Builds an accept header field value for HTTP GET request messages.
*
* @param {Object[]} mediaTypes Acceptable media types
* @param {Object[]} supportedMediaTypes Supported media types
* @return {*}
* @private
*/
}, {
key: "_buildAcceptHeaderFieldValue",
value: function _buildAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes) {
if (!Array.isArray(mediaTypes)) {
throw new Error("Acceptable media types must be provided as an Array");
}
var fieldValueParts = mediaTypes.map(function (item) {
var mediaType = item.mediaType;
DICOMwebClient._assertMediaTypeIsValid(mediaType);
if (!supportedMediaTypes.includes(mediaType)) {
throw new Error("Media type ".concat(mediaType, " is not supported for requested resource"));
}
return mediaType;
});
return fieldValueParts.join(", ");
}
/**
* Builds an accept header field value for HTTP GET multipart request
messages.
*
* @param {Object[]} mediaTypes Acceptable media types
* @param {Object[]} supportedMediaTypes Supported media types
* @private
*/
}, {
key: "_buildMultipartAcceptHeaderFieldValue",
value: function _buildMultipartAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes) {
if (!Array.isArray(mediaTypes)) {
throw new Error("Acceptable media types must be provided as an Array");
}
if (!Array.isArray(supportedMediaTypes) && !isObject(supportedMediaTypes)) {
throw new Error("Supported media types must be provided as an Array or an Object");
}
var fieldValueParts = [];
mediaTypes.forEach(function (item) {
var transferSyntaxUID = item.transferSyntaxUID,
mediaType = item.mediaType;
DICOMwebClient._assertMediaTypeIsValid(mediaType);
var fieldValue = "multipart/related; type=\"".concat(mediaType, "\"");
if (isObject(supportedMediaTypes)) {
// SupportedMediaTypes is a lookup table that maps Transfer Syntax UID
// to one or more Media Types
if (!Object.values(supportedMediaTypes).flat(1).includes(mediaType)) {
if (!mediaType.endsWith("/*") || !mediaType.endsWith("/")) {
throw new Error("Media type ".concat(mediaType, " is not supported for requested resource"));
}
}
if (transferSyntaxUID) {
if (transferSyntaxUID !== "*") {
if (!Object.keys(supportedMediaTypes).includes(transferSyntaxUID)) {
throw new Error("Transfer syntax ".concat(transferSyntaxUID, " is not supported for requested resource"));
}
var expectedMediaTypes = supportedMediaTypes[transferSyntaxUID];
if (!expectedMediaTypes.includes(mediaType)) {
var actualType = DICOMwebClient._parseMediaType(mediaType)[0];
expectedMediaTypes.map(function (expectedMediaType) {
var expectedType = DICOMwebClient._parseMediaType(expectedMediaType)[0];
var haveSameType = actualType === expectedType;
if (haveSameType && (mediaType.endsWith("/*") || mediaType.endsWith("/"))) {
return;
}
throw new Error("Transfer syntax ".concat(transferSyntaxUID, " is not supported for requested resource"));
});
}
}
fieldValue += "; transfer-syntax=".concat(transferSyntaxUID);
}
} else if (Array.isArray(supportedMediaTypes) && !supportedMediaTypes.includes(mediaType)) {
throw new Error("Media type ".concat(mediaType, " is not supported for requested resource"));
}
fieldValueParts.push(fieldValue);
});
return fieldValueParts.join(", ");
}
/**
* Builds a range header field value for HTTP GET request messages.
*
* @param {Array} byteRange start and end of byte range
* @returns {String} range header field value
*/
}, {
key: "_buildRangeHeaderFieldValue",
value: function _buildRangeHeaderFieldValue() {
var byteRange = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
if (byteRange.length === 1) {
return "bytes=".concat(byteRange[0], "-");
}
if (byteRange.length === 2) {
return "bytes=".concat(byteRange[0], "-").concat(byteRange[1]);
}
return "bytes=0-";
}
/**
* Gets common type of acceptable media types and asserts that only
one type is specified. For example, ``("image/jpeg", "image/jp2")``
will pass, but ``("image/jpeg", "video/mpeg2")`` will raise an
exception.
* @param {String[]} acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
*
*/
}, {
key: "_getCommonMediaType",
value: function _getCommonMediaType(mediaTypes) {
if (!mediaTypes || !mediaTypes.length) {
throw new Error("No acceptable media types provided");
}
var commonMediaTypes = new Set();
mediaTypes.forEach(function (item) {
var mediaType = item.mediaType;
if (mediaType.startsWith("application")) {
commonMediaTypes.add(mediaType);
} else {
var type = DICOMwebClient._parseMediaType(mediaType)[0];
commonMediaTypes.add("".concat(type, "/"));
}
});
if (commonMediaTypes.size === 0) {
throw new Error("No common acceptable media type could be identified.");
} else if (commonMediaTypes.size > 1) {
throw new Error("Acceptable media types must have the same type.");
}
return Array.from(commonMediaTypes)[0];
}
}]);

@@ -923,5 +1589,5 @@

return null;
} else {
return str.substring(beforeIndex, afterIndex);
}
return str.substring(beforeIndex, afterIndex);
}

@@ -940,3 +1606,3 @@

if (!uid) {
console.debug('Study Instance UID could not be dertermined from URI "' + uri + '"');
console.debug("Study Instance UID could not be dertermined from URI \"".concat(uri, "\""));
}

@@ -955,3 +1621,3 @@

if (!uid) {
console.debug('Series Instance UID could not be dertermined from URI "' + uri + '"');
console.debug("Series Instance UID could not be dertermined from URI \"".concat(uri, "\""));
}

@@ -974,3 +1640,3 @@

if (!uid) {
console.debug('SOP Instance UID could not be dertermined from URI"' + uri + '"');
console.debug("SOP Instance UID could not be dertermined from URI\"".concat(uri, "\""));
}

@@ -989,9 +1655,9 @@

if (numbers === undefined) {
console.debug('Frames Numbers could not be dertermined from URI"' + uri + '"');
console.debug("Frames Numbers could not be dertermined from URI\"".concat(uri, "\""));
}
return numbers.split(',');
return numbers.split(",");
}
var version = '0.3.2';
var version = "0.5.0";

@@ -998,0 +1664,0 @@ var api = {

{
"name": "dicomweb-client",
"version": "0.4.4",
"version": "0.5.0",
"description": "Implementation of DICOMweb client code",

@@ -13,3 +13,4 @@ "main": "build/dicomweb-client.js",

"watch": "rollup -c -w",
"version": "node -p -e \"'export default \\'' + require('./package.json').version + '\\';'\" > src/version.js"
"version": "node -p -e \"'export default \\'' + require('./package.json').version + '\\';'\" > src/version.js",
"lint": "eslint -c .eslintrc.js --fix src && prettier --write src/**/*.js"
},

@@ -38,3 +39,6 @@ "repository": {

"chai": "^4.1.2",
"karma": "^3.0.0",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.16.0",
"karma": "^4.2.0",
"karma-chai": "^0.1.0",

@@ -44,3 +48,4 @@ "karma-chrome-launcher": "^2.2.0",

"mocha": "^5.2.0",
"puppeteer": "^1.8.0",
"prettier": "^1.16.4",
"puppeteer": "^1.18.1",
"rollup": "^0.63.2",

@@ -47,0 +52,0 @@ "rollup-plugin-babel": "^4.0.3"

@@ -1,45 +0,49 @@

import {
containsToken,
findToken,
identifyBoundary,
uint8ArrayToString,
stringToUint8Array,
multipartEncode,
multipartDecode
} from './message.js';
import { multipartEncode, multipartDecode } from "./message.js";
function isEmptyObject (obj) {
return Object.keys(obj).length === 0 && obj.constructor === Object;
function isObject(obj) {
return typeof obj === "object" && obj !== null;
}
function isEmptyObject(obj) {
return Object.keys(obj).length === 0 && obj.constructor === Object;
}
const getFirstResult = result => result[0];
const getFirstResultIfLengthGtOne = result => {
if (result.length > 1) {
return result;
}
const MIMETYPES = {
DICOM: 'application/dicom',
DICOM_JSON: 'application/dicom+json',
OCTET_STREAM: 'application/octet-stream',
JPEG: 'image/jpeg',
PNG: 'image/png'
return result[0]
};
const MEDIATYPES = {
DICOM: "application/dicom",
DICOM_JSON: "application/dicom+json",
OCTET_STREAM: "application/octet-stream",
PDF: "application/pdf",
JPEG: "image/jpeg",
PNG: "image/png"
};
/**
* Class for interacting with DICOMweb RESTful services.
*/
* Class for interacting with DICOMweb RESTful services.
*/
class DICOMwebClient {
/**
* @constructor
* @param {Object} options (choices: "url", "username", "password", "headers")
*/
* @constructor
* @param {Object} options (choices: "url", "username", "password", "headers")
*/
constructor(options) {
this.baseURL = options.url;
if (!this.baseURL) {
console.error('no DICOMweb base url provided - calls will fail')
console.error("no DICOMweb base url provided - calls will fail");
}
if ('username' in options) {
if ("username" in options) {
this.username = options.username;
if (!('password' in options)) {
console.error('no password provided to authenticate with DICOMweb service')
if (!("password" in options)) {
console.error(
"no password provided to authenticate with DICOMweb service"
);
}

@@ -49,5 +53,5 @@ this.password = options.password;

if ('qidoURLPrefix' in options) {
if ("qidoURLPrefix" in options) {
console.log(`use URL prefix for QIDO-RS: ${options.qidoURLPrefix}`);
this.qidoURL = this.baseURL + '/' + options.qidoURLPrefix;
this.qidoURL = `${this.baseURL}/${options.qidoURLPrefix}`;
} else {

@@ -57,5 +61,5 @@ this.qidoURL = this.baseURL;

if ('wadoURLPrefix' in options) {
if ("wadoURLPrefix" in options) {
console.log(`use URL prefix for WADO-RS: ${options.wadoURLPrefix}`);
this.wadoURL = this.baseURL + '/' + options.wadoURLPrefix;
this.wadoURL = `${this.baseURL}/${options.wadoURLPrefix}`;
} else {

@@ -65,5 +69,5 @@ this.wadoURL = this.baseURL;

if ('stowURLPrefix' in options) {
if ("stowURLPrefix" in options) {
console.log(`use URL prefix for STOW-RS: ${options.stowURLPrefix}`);
this.stowURL = this.baseURL + '/' + options.stowURLPrefix;
this.stowURL = `${this.baseURL}/${options.stowURLPrefix}`;
} else {

@@ -76,23 +80,23 @@ this.stowURL = this.baseURL;

static _parseQueryParameters(params={}) {
let queryString = '?';
Object.keys(params).forEach(function (key, index) {
static _parseQueryParameters(params = {}) {
let queryString = "?";
Object.keys(params).forEach((key, index) => {
if (index !== 0) {
queryString += '&'
queryString += "&";
}
queryString += key + '=' + encodeURIComponent(params[key]);
queryString += `${key}=${encodeURIComponent(params[key])}`;
});
return queryString
return queryString;
}
_httpRequest(url, method, headers, options={}) {
return new Promise( (resolve, reject) => {
_httpRequest(url, method, headers, options = {}) {
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.open(method, url, true);
if ('responseType' in options) {
if ("responseType" in options) {
request.responseType = options.responseType;
}
if (typeof(headers) === 'object') {
Object.keys(headers).forEach(function (key) {
if (typeof headers === "object") {
Object.keys(headers).forEach(key => {
request.setRequestHeader(key, headers[key]);

@@ -105,3 +109,3 @@ });

const userHeaders = this.headers;
Object.keys(userHeaders).forEach(function (key) {
Object.keys(userHeaders).forEach(key => {
request.setRequestHeader(key, userHeaders[key]);

@@ -111,13 +115,13 @@ });

// Event triggered when upload starts
request.onloadstart = function (event) {
//console.log('upload started: ', url)
request.onloadstart = function onloadstart() {
// console.log('upload started: ', url)
};
// Event triggered when upload ends
request.onloadend = function (event) {
//console.log('upload finished')
request.onloadend = function onloadend() {
// console.log('upload finished')
};
// Handle response message
request.onreadystatechange = function (event) {
request.onreadystatechange = function onreadystatechange() {
if (request.readyState === 4) {

@@ -127,10 +131,10 @@ if (request.status === 200) {

} else if (request.status === 202) {
console.warn('some resources already existed: ', request);
console.warn("some resources already existed: ", request);
resolve(request.response);
} else if (request.status === 204) {
console.warn('empty response for request: ', request);
console.warn("empty response for request: ", request);
resolve([]);
} else {
console.error('request failed: ', request);
const error = new Error('request failed');
console.error("request failed: ", request);
const error = new Error("request failed");
error.request = request;

@@ -148,4 +152,4 @@ error.response = request.response;

// Event triggered while download progresses
if ('progressCallback' in options) {
if (typeof(options.progressCallback) === 'function') {
if ("progressCallback" in options) {
if (typeof options.progressCallback === "function") {
request.onprogress = options.progressCallback;

@@ -167,3 +171,3 @@ }

if ('data' in options) {
if ("data" in options) {
request.send(options.data);

@@ -177,36 +181,429 @@ } else {

_httpGet(url, headers, responseType, progressCallback) {
return this._httpRequest(url, 'get', headers, {responseType, progressCallback});
return this._httpRequest(url, "get", headers, {
responseType,
progressCallback
});
}
_httpGetApplicationJson(url, params={}, progressCallback) {
if (typeof(params) === 'object') {
_httpGetApplicationJson(url, params = {}, progressCallback) {
let urlWithQueryParams = url;
if (typeof params === "object") {
if (!isEmptyObject(params)) {
url += DICOMwebClient._parseQueryParameters(params)
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
const headers = {'Accept': MIMETYPES.DICOM_JSON};
const responseType = 'json';
return this._httpGet(url, headers, responseType, progressCallback);
const headers = { Accept: MEDIATYPES.DICOM_JSON };
const responseType = "json";
return this._httpGet(
urlWithQueryParams,
headers,
responseType,
progressCallback
);
}
_httpGetByMimeType(url, mimeType, params, responseType='arraybuffer', progressCallback) {
if (typeof(params) === 'object') {
/**
* Performs an HTTP GET request that accepts a message with
"application/pdf" media type.
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
_httpGetApplicationPdf(url, params = {}, progressCallback) {
let urlWithQueryParams = url;
if (typeof params === "object") {
if (!isEmptyObject(params)) {
url += DICOMwebClient._parseQueryParameters(params)
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
const headers = { Accept: MEDIATYPES.PDF };
const responseType = "json";
return this._httpGet(
urlWithQueryParams,
headers,
responseType,
progressCallback
);
}
const headers = {
'Accept': `multipart/related; type="${mimeType}"`
/**
* Performs an HTTP GET request that accepts a message with an image
media type.
*
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
_httpGetImage(url, mediaTypes, params = {}, progressCallback) {
let urlWithQueryParams = url;
if (typeof params === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
const supportedMediaTypes = [
"image/",
"image/*",
"image/jpeg",
"image/jp2",
"image/gif",
"image/png"
];
const acceptHeaderFieldValue = DICOMwebClient._buildAcceptHeaderFieldValue(
mediaTypes,
supportedMediaTypes
);
const headers = { Accept: acceptHeaderFieldValue };
const responseType = "arraybuffer";
return this._httpGet(
urlWithQueryParams,
headers,
responseType,
progressCallback
);
}
/**
* Performs an HTTP GET request that accepts a message with a text
media type.
*
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
_httpGetText(url, mediaTypes, params = {}, progressCallback) {
let urlWithQueryParams = url;
if (typeof params === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
const supportedMediaTypes = [
"text/",
"text/*",
"text/html",
"text/plain",
"text/rtf",
"text/xml"
];
const acceptHeaderFieldValue = DICOMwebClient._buildAcceptHeaderFieldValue(
mediaTypes,
supportedMediaTypes
);
const headers = { Accept: acceptHeaderFieldValue };
const responseType = "arraybuffer";
return this._httpGet(
urlWithQueryParams,
headers,
responseType,
progressCallback
);
}
/**
* Performs an HTTP GET request that accepts a message with a video
media type.
*
* @param {String} url
* @param {Object[]} mediaTypes
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
_httpGetVideo(url, mediaTypes, params = {}, progressCallback) {
let urlWithQueryParams = url;
if (typeof params === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
const supportedMediaTypes = [
"video/",
"video/*",
"video/mpeg",
"video/mp4",
"video/H265"
];
const acceptHeaderFieldValue = DICOMwebClient._buildAcceptHeaderFieldValue(
mediaTypes,
supportedMediaTypes
);
const headers = { Accept: acceptHeaderFieldValue };
const responseType = "arraybuffer";
return this._httpGet(
urlWithQueryParams,
headers,
responseType,
progressCallback
);
}
/**
* Asserts that a given media type is valid.
*
* @params {String} mediaType media type
*/
static _assertMediaTypeIsValid(mediaType) {
if (!mediaType) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
const sepIndex = mediaType.indexOf("/");
if (sepIndex === -1) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
const mediaTypeType = mediaType.slice(0, sepIndex);
const types = ["application", "image", "text", "video"];
if (!types.includes(mediaTypeType)) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
if (mediaType.slice(sepIndex + 1).includes("/")) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
}
/**
* Performs an HTTP GET request that accepts a multipart message with an image media type.
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Array} byteRange start and end of byte range
* @param {Object} params additional HTTP GET query parameters
* @param {Boolean} rendered whether resource should be requested using rendered media types
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
_httpGetMultipartImage(
url,
mediaTypes,
byteRange,
params,
rendered = false,
progressCallback
) {
const headers = {};
let supportedMediaTypes;
if (rendered) {
supportedMediaTypes = [
"image/jpeg",
"image/gif",
"image/png",
"image/jp2"
];
} else {
supportedMediaTypes = {
"1.2.840.10008.1.2.5": ["image/x-dicom-rle"],
"1.2.840.10008.1.2.4.50": ["image/jpeg"],
"1.2.840.10008.1.2.4.51": ["image/jpeg"],
"1.2.840.10008.1.2.4.57": ["image/jpeg"],
"1.2.840.10008.1.2.4.70": ["image/jpeg"],
"1.2.840.10008.1.2.4.80": ["image/x-jls", "image/jls"],
"1.2.840.10008.1.2.4.81": ["image/x-jls", "image/jls"],
"1.2.840.10008.1.2.4.90": ["image/jp2"],
"1.2.840.10008.1.2.4.91": ["image/jp2"],
"1.2.840.10008.1.2.4.92": ["image/jpx"],
"1.2.840.10008.1.2.4.93": ["image/jpx"]
};
if (byteRange) {
headers.Range = DICOMwebClient._buildRangeHeaderFieldValue(byteRange);
}
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(
mediaTypes,
supportedMediaTypes
);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(
multipartDecode
);
}
/**
* Performs an HTTP GET request that accepts a multipart message with a video media type.
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Array} byteRange start and end of byte range
* @param {Object} params additional HTTP GET query parameters
* @param {Boolean} rendered whether resource should be requested using rendered media types
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
_httpGetMultipartVideo(
url,
mediaTypes,
byteRange,
params,
rendered = false,
progressCallback
) {
const headers = {};
let supportedMediaTypes;
if (rendered) {
supportedMediaTypes = [
"video/",
"video/*",
"video/mpeg2",
"video/mp4",
"video/H265"
];
} else {
supportedMediaTypes = {
"1.2.840.10008.1.2.4.100": ["video/mpeg2"],
"1.2.840.10008.1.2.4.101": ["video/mpeg2"],
"1.2.840.10008.1.2.4.102": ["video/mp4"],
"1.2.840.10008.1.2.4.103": ["video/mp4"],
"1.2.840.10008.1.2.4.104": ["video/mp4"],
"1.2.840.10008.1.2.4.105": ["video/mp4"],
"1.2.840.10008.1.2.4.106": ["video/mp4"]
};
if (byteRange) {
headers.Range = DICOMwebClient._buildRangeHeaderFieldValue(byteRange);
}
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(
mediaTypes,
supportedMediaTypes
);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(
multipartDecode
);
}
/**
* Performs a HTTP GET request that accepts a multipart message with "application/dicom" media type
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Object} params additional HTTP GET query parameters
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
_httpGetMultipartApplicationDicom(url, mediaTypes, params, progressCallback) {
const headers = {};
const defaultMediaType = "application/dicom";
const supportedMediaTypes = {
"1.2.840.10008.1.2.1": [defaultMediaType],
"1.2.840.10008.1.2.5": [defaultMediaType],
"1.2.840.10008.1.2.4.50": [defaultMediaType],
"1.2.840.10008.1.2.4.51": [defaultMediaType],
"1.2.840.10008.1.2.4.57": [defaultMediaType],
"1.2.840.10008.1.2.4.70": [defaultMediaType],
"1.2.840.10008.1.2.4.80": [defaultMediaType],
"1.2.840.10008.1.2.4.81": [defaultMediaType],
"1.2.840.10008.1.2.4.90": [defaultMediaType],
"1.2.840.10008.1.2.4.91": [defaultMediaType],
"1.2.840.10008.1.2.4.92": [defaultMediaType],
"1.2.840.10008.1.2.4.93": [defaultMediaType],
"1.2.840.10008.1.2.4.100": [defaultMediaType],
"1.2.840.10008.1.2.4.101": [defaultMediaType],
"1.2.840.10008.1.2.4.102": [defaultMediaType],
"1.2.840.10008.1.2.4.103": [defaultMediaType],
"1.2.840.10008.1.2.4.104": [defaultMediaType],
"1.2.840.10008.1.2.4.105": [defaultMediaType],
"1.2.840.10008.1.2.4.106": [defaultMediaType]
};
return this._httpGet(url, headers, responseType, progressCallback);
let acceptableMediaTypes = mediaTypes;
if (!mediaTypes) {
acceptableMediaTypes = [{ mediaType: defaultMediaType }];
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(
acceptableMediaTypes,
supportedMediaTypes
);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(
multipartDecode
);
}
/**
* Performs a HTTP GET request that accepts a multipart message with "application/octet-stream" media type
*
* @param {String} url unique resource locator
* @param {Object[]} mediaTypes acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @param {Array} byteRange start and end of byte range
* @param {Object} params additional HTTP GET query parameters
* @param {Function} progressCallback
* @private
* @returns {Array} content of HTTP message body parts
*/
_httpGetMultipartApplicationOctetStream(
url,
mediaTypes,
byteRange,
params,
progressCallback
) {
const headers = {};
const defaultMediaType = "application/octet-stream";
const supportedMediaTypes = {
"1.2.840.10008.1.2.1": [defaultMediaType]
};
let acceptableMediaTypes = mediaTypes;
if (!mediaTypes) {
acceptableMediaTypes = [{ mediaType: defaultMediaType }];
}
if (byteRange) {
headers.Range = DICOMwebClient._buildRangeHeaderFieldValue(byteRange);
}
headers.Accept = DICOMwebClient._buildMultipartAcceptHeaderFieldValue(
acceptableMediaTypes,
supportedMediaTypes
);
return this._httpGet(url, headers, "arraybuffer", progressCallback).then(
multipartDecode
);
}
_httpPost(url, headers, data, progressCallback) {
return this._httpRequest(url, 'post', headers, {data, progressCallback});
return this._httpRequest(url, "post", headers, {
data,
progressCallback
});
}
_httpPostApplicationJson(url, data, progressCallback) {
const headers = {'Content-Type': MIMETYPES.DICOM_JSON};
const headers = { "Content-Type": MEDIATYPES.DICOM_JSON };
return this._httpPost(url, headers, data, progressCallback);

@@ -216,2 +613,185 @@ }

/**
* Parses media type and extracts its type and subtype.
*
* @param mediaType e.g. image/jpeg
* @private
*/
static _parseMediaType(mediaType) {
DICOMwebClient._assertMediaTypeIsValid(mediaType);
return mediaType.split("/");
}
/**
* Builds an accept header field value for HTTP GET request messages.
*
* @param {Object[]} mediaTypes Acceptable media types
* @param {Object[]} supportedMediaTypes Supported media types
* @return {*}
* @private
*/
static _buildAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes) {
if (!Array.isArray(mediaTypes)) {
throw new Error("Acceptable media types must be provided as an Array");
}
const fieldValueParts = mediaTypes.map(item => {
const { mediaType } = item;
DICOMwebClient._assertMediaTypeIsValid(mediaType);
if (!supportedMediaTypes.includes(mediaType)) {
throw new Error(
`Media type ${mediaType} is not supported for requested resource`
);
}
return mediaType;
});
return fieldValueParts.join(", ");
}
/**
* Builds an accept header field value for HTTP GET multipart request
messages.
*
* @param {Object[]} mediaTypes Acceptable media types
* @param {Object[]} supportedMediaTypes Supported media types
* @private
*/
static _buildMultipartAcceptHeaderFieldValue(
mediaTypes,
supportedMediaTypes
) {
if (!Array.isArray(mediaTypes)) {
throw new Error("Acceptable media types must be provided as an Array");
}
if (!Array.isArray(supportedMediaTypes) && !isObject(supportedMediaTypes)) {
throw new Error(
"Supported media types must be provided as an Array or an Object"
);
}
const fieldValueParts = [];
mediaTypes.forEach(item => {
const { transferSyntaxUID, mediaType } = item;
DICOMwebClient._assertMediaTypeIsValid(mediaType);
let fieldValue = `multipart/related; type="${mediaType}"`;
if (isObject(supportedMediaTypes)) {
// SupportedMediaTypes is a lookup table that maps Transfer Syntax UID
// to one or more Media Types
if (!Object.values(supportedMediaTypes).flat(1).includes(mediaType)) {
if (!mediaType.endsWith("/*") || !mediaType.endsWith("/")) {
throw new Error(
`Media type ${mediaType} is not supported for requested resource`
);
}
}
if (transferSyntaxUID) {
if (transferSyntaxUID !== "*") {
if (!Object.keys(supportedMediaTypes).includes(transferSyntaxUID)) {
throw new Error(
`Transfer syntax ${transferSyntaxUID} is not supported for requested resource`
);
}
const expectedMediaTypes = supportedMediaTypes[transferSyntaxUID];
if (!expectedMediaTypes.includes(mediaType)) {
const actualType = DICOMwebClient._parseMediaType(mediaType)[0];
expectedMediaTypes.map(expectedMediaType => {
const expectedType = DICOMwebClient._parseMediaType(
expectedMediaType
)[0];
const haveSameType = actualType === expectedType;
if (
haveSameType &&
(mediaType.endsWith("/*") || mediaType.endsWith("/"))
) {
return;
}
throw new Error(
`Transfer syntax ${transferSyntaxUID} is not supported for requested resource`
);
})
}
}
fieldValue += `; transfer-syntax=${transferSyntaxUID}`;
}
} else if (
Array.isArray(supportedMediaTypes) &&
!supportedMediaTypes.includes(mediaType)
) {
throw new Error(
`Media type ${mediaType} is not supported for requested resource`
);
}
fieldValueParts.push(fieldValue);
});
return fieldValueParts.join(", ");
}
/**
* Builds a range header field value for HTTP GET request messages.
*
* @param {Array} byteRange start and end of byte range
* @returns {String} range header field value
*/
static _buildRangeHeaderFieldValue(byteRange = []) {
if (byteRange.length === 1) {
return `bytes=${byteRange[0]}-`;
}
if (byteRange.length === 2) {
return `bytes=${byteRange[0]}-${byteRange[1]}`;
}
return "bytes=0-";
}
/**
* Gets common type of acceptable media types and asserts that only
one type is specified. For example, ``("image/jpeg", "image/jp2")``
will pass, but ``("image/jpeg", "video/mpeg2")`` will raise an
exception.
* @param {String[]} acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
*
*/
static _getCommonMediaType(mediaTypes) {
if (!mediaTypes || !mediaTypes.length) {
throw new Error("No acceptable media types provided");
}
const commonMediaTypes = new Set();
mediaTypes.forEach(item => {
const { mediaType } = item;
if (mediaType.startsWith("application")) {
commonMediaTypes.add(mediaType);
} else {
const type = DICOMwebClient._parseMediaType(mediaType)[0];
commonMediaTypes.add(`${type}/`);
}
});
if (commonMediaTypes.size === 0) {
throw new Error("No common acceptable media type could be identified.");
} else if (commonMediaTypes.size > 1) {
throw new Error("Acceptable media types must have the same type.");
}
return Array.from(commonMediaTypes)[0];
}
/**
* Searches for DICOM studies.

@@ -221,10 +801,9 @@ * @param {Object} options options object

*/
searchForStudies(options={}) {
console.log('search for studies');
let url = this.qidoURL +
'/studies';
if ('queryParams' in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);
searchForStudies(options = {}) {
console.log("search for studies");
let url = `${this.qidoURL}/studies`;
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);
}
return(this._httpGetApplicationJson(url));
return this._httpGetApplicationJson(url);
}

@@ -235,13 +814,14 @@

* @param {Object} options options object
* @returns {Array} metadata elements in DICOM JSON format for each instance belonging to the study
* @returns {Array} metadata elements in DICOM JSON format for each instance
belonging to the study
*/
retrieveStudyMetadata(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of study metadata')
if (!("studyInstanceUID" in options)) {
throw new Error(
"Study Instance UID is required for retrieval of study metadata"
);
}
console.log(`retrieve metadata of study ${options.studyInstanceUID}`);
const url = this.wadoURL +
'/studies/' + options.studyInstanceUID +
'/metadata';
return(this._httpGetApplicationJson(url));
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/metadata`;
return this._httpGetApplicationJson(url);
}

@@ -254,13 +834,13 @@

*/
searchForSeries(options={}) {
searchForSeries(options = {}) {
let url = this.qidoURL;
if ('studyInstanceUID' in options) {
if ("studyInstanceUID" in options) {
console.log(`search series of study ${options.studyInstanceUID}`);
url += '/studies/' + options.studyInstanceUID;
url += `/studies/${options.studyInstanceUID}`;
}
url += '/series';
if ('queryParams' in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);
url += "/series";
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);
}
return(this._httpGetApplicationJson(url));
return this._httpGetApplicationJson(url);
}

@@ -271,18 +851,22 @@

* @param {Object} options options object
* @returns {Array} metadata elements in DICOM JSON format for each instance belonging to the series
* @returns {Array} metadata elements in DICOM JSON format for each instance
belonging to the series
*/
retrieveSeriesMetadata(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of series metadata')
if (!("studyInstanceUID" in options)) {
throw new Error(
"Study Instance UID is required for retrieval of series metadata"
);
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of series metadata')
if (!("seriesInstanceUID" in options)) {
throw new Error(
"Series Instance UID is required for retrieval of series metadata"
);
}
console.log(`retrieve metadata of series ${options.seriesInstanceUID}`);
const url = this.wadoURL +
'/studies/' + options.studyInstanceUID +
'/series/' + options.seriesInstanceUID +
'/metadata';
return(this._httpGetApplicationJson(url));
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${
options.seriesInstanceUID
}/metadata`;
return this._httpGetApplicationJson(url);
}

@@ -295,20 +879,24 @@

*/
searchForInstances(options={}) {
searchForInstances(options = {}) {
let url = this.qidoURL;
if ('studyInstanceUID' in options) {
url += '/studies/' + options.studyInstanceUID;
if ('seriesInstanceUID' in options) {
console.log(`search for instances of series ${options.seriesInstanceUID}`);
url += '/series/' + options.seriesInstanceUID;
if ("studyInstanceUID" in options) {
url += `/studies/${options.studyInstanceUID}`;
if ("seriesInstanceUID" in options) {
console.log(
`search for instances of series ${options.seriesInstanceUID}`
);
url += `/series/${options.seriesInstanceUID}`;
} else {
console.log(`search for instances of study ${options.studyInstanceUID}`);
console.log(
`search for instances of study ${options.studyInstanceUID}`
);
}
} else {
console.log('search for instances');
console.log("search for instances");
}
url += '/instances';
if ('queryParams' in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);
url += "/instances";
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);
}
return(this._httpGetApplicationJson(url));
return this._httpGetApplicationJson(url);
}

@@ -321,17 +909,17 @@

buildInstanceWadoURIUrl(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required.')
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required.");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required.')
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required.");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required.')
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required.");
}
const contentType = options.contentType || MIMETYPES.DICOM;
const transferSyntax = options.transferSyntax || '*';
const contentType = options.contentType || MEDIATYPES.DICOM;
const transferSyntax = options.transferSyntax || "*";
const params = [];
params.push('requestType=WADO');
params.push("requestType=WADO");
params.push(`studyUID=${options.studyInstanceUID}`);

@@ -343,3 +931,3 @@ params.push(`seriesUID=${options.seriesInstanceUID}`);

const paramString = params.join('&');
const paramString = params.join("&");

@@ -356,17 +944,21 @@ return `${this.wadoURL}?${paramString}`;

retrieveInstanceMetadata(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of instance metadata')
if (!("studyInstanceUID" in options)) {
throw new Error(
"Study Instance UID is required for retrieval of instance metadata"
);
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of instance metadata')
if (!("seriesInstanceUID" in options)) {
throw new Error(
"Series Instance UID is required for retrieval of instance metadata"
);
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required for retrieval of instance metadata')
if (!("sopInstanceUID" in options)) {
throw new Error(
"SOP Instance UID is required for retrieval of instance metadata"
);
}
console.log(`retrieve metadata of instance ${options.sopInstanceUID}`);
const url = this.wadoURL +
'/studies/' + options.studyInstanceUID +
'/series/' + options.seriesInstanceUID +
'/instances/' + options.sopInstanceUID +
'/metadata';
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${
options.seriesInstanceUID
}/instances/${options.sopInstanceUID}/metadata`;

@@ -382,27 +974,106 @@ return this._httpGetApplicationJson(url);

retrieveInstanceFrames(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of instance frames')
if (!("studyInstanceUID" in options)) {
throw new Error(
"Study Instance UID is required for retrieval of instance frames"
);
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of instance frames')
if (!("seriesInstanceUID" in options)) {
throw new Error(
"Series Instance UID is required for retrieval of instance frames"
);
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required for retrieval of instance frames')
if (!("sopInstanceUID" in options)) {
throw new Error(
"SOP Instance UID is required for retrieval of instance frames"
);
}
if (!('frameNumbers' in options)) {
throw new Error('frame numbers are required for retrieval of instance frames')
if (!("frameNumbers" in options)) {
throw new Error(
"frame numbers are required for retrieval of instance frames"
);
}
console.log(`retrieve frames ${options.frameNumbers.toString()} of instance ${options.sopInstanceUID}`)
const url = this.wadoURL +
'/studies/' + options.studyInstanceUID +
'/series/' + options.seriesInstanceUID +
'/instances/' + options.sopInstanceUID +
'/frames/' + options.frameNumbers.toString();
console.log(
`retrieve frames ${options.frameNumbers.toString()} of instance ${
options.sopInstanceUID
}`
);
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${
options.seriesInstanceUID
}/instances/${
options.sopInstanceUID
}/frames/${options.frameNumbers.toString()}`;
const mimeType = options.mimeType ? `${options.mimeType}` : MIMETYPES.OCTET_STREAM;
const { mediaTypes } = options;
return this._httpGetByMimeType(url, mimeType).then(multipartDecode);
if (!mediaTypes) {
return this._httpGetMultipartApplicationOctetStream(url);
}
const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes);
}
throw new Error(
`Media type ${commonMediaType} is not supported for retrieval of frames.`
);
}
/**
* Retrieves an individual, server-side rendered DICOM instance.
*
* @param {Object} options options object
* @returns {Array} frame items as byte arrays of the pixel data element
*/
retrieveInstanceRendered(options) {
if (!("studyInstanceUID" in options)) {
throw new Error(
"Study Instance UID is required for retrieval of rendered instance"
);
}
if (!("seriesInstanceUID" in options)) {
throw new Error(
"Series Instance UID is required for retrieval of rendered instance"
);
}
if (!("sopInstanceUID" in options)) {
throw new Error(
"SOP Instance UID is required for retrieval of rendered instance"
);
}
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${
options.seriesInstanceUID
}/instances/${options.sopInstanceUID}/rendered`;
const { mediaTypes, params } = options;
const headers = {};
if (!mediaTypes) {
const responseType = "arraybuffer";
return this._httpGet(url, headers, responseType);
}
const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType.startsWith("image")) {
return this._httpGetImage(url, mediaTypes, params);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetVideo(url, mediaTypes, params);
} else if (commonMediaType.startsWith("text")) {
return this._httpGetText(url, mediaTypes, params);
} else if (commonMediaType === MEDIATYPES.PDF) {
return this._httpGetApplicationPdf(url, params);
}
throw new Error(
`Media type ${commonMediaType} is not supported for retrieval of rendered instance.`
);
}
/**
* Retrieves rendered frames for a DICOM instance.

@@ -413,32 +1084,52 @@ * @param {Object} options options object

retrieveInstanceFramesRendered(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required for retrieval of rendered instance frames')
if (!("studyInstanceUID" in options)) {
throw new Error(
"Study Instance UID is required for retrieval of rendered instance frames"
);
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required for retrieval of rendered instance frames')
if (!("seriesInstanceUID" in options)) {
throw new Error(
"Series Instance UID is required for retrieval of rendered instance frames"
);
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required for retrieval of rendered instance frames')
if (!("sopInstanceUID" in options)) {
throw new Error(
"SOP Instance UID is required for retrieval of rendered instance frames"
);
}
if (!('frameNumbers' in options)) {
throw new Error('frame numbers are required for retrieval of rendered instance frames')
if (!("frameNumbers" in options)) {
throw new Error(
"frame numbers are required for retrieval of rendered instance frames"
);
}
console.log(`retrieve rendered frames ${options.frameNumbers.toString()} of instance ${options.sopInstanceUID}`)
const url = this.wadoURL +
'/studies/' + options.studyInstanceUID +
'/series/' + options.seriesInstanceUID +
'/instances/' + options.sopInstanceUID +
'/frames/' + options.frameNumbers.toString() +
'/rendered';
console.debug(
`retrieve rendered frames ${options.frameNumbers.toString()} of instance ${
options.sopInstanceUID
}`
);
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${
options.seriesInstanceUID
}/instances/${
options.sopInstanceUID
}/frames/${options.frameNumbers.toString()}/rendered`;
let headers = {};
// The choice of an acceptable media type depends on a variety of things:
// http://dicom.nema.org/medical/dicom/current/output/chtml/part18/chapter_6.html#table_6.1.1-3
if ('mimeType' in options) {
headers['Accept'] = options.mimeType;
const { mediaTypes } = options;
const headers = {};
if (!mediaTypes) {
const responseType = "arraybuffer";
return this._httpGet(url, headers, responseType);
}
const responseType = 'arraybuffer';
return this._httpGet(url, headers, responseType);
const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType.startsWith("image")) {
return this._httpGetImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetVideo(url, mediaTypes);
}
throw new Error(
`Media type ${commonMediaType} is not supported for retrieval of rendered frame.`
);
}

@@ -449,22 +1140,42 @@

* @param {Object} options options object
* @returns {Arraybuffer} DICOM Part 10 file as Arraybuffer
* @returns {ArrayBuffer} DICOM Part 10 file as Arraybuffer
*/
retrieveInstance(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required')
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required')
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required");
}
if (!('sopInstanceUID' in options)) {
throw new Error('SOP Instance UID is required')
if (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required");
}
const url = this.wadoURL +
'/studies/' + options.studyInstanceUID +
'/series/' + options.seriesInstanceUID +
'/instances/' + options.sopInstanceUID;
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${
options.seriesInstanceUID
}/instances/${options.sopInstanceUID}`;
return this._httpGetByMimeType(url, MIMETYPES.DICOM)
.then(multipartDecode)
.then(getFirstResult);
const { mediaTypes } = options;
if (!mediaTypes) {
return this._httpGetMultipartApplicationDicom(url).then(getFirstResult);
}
const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.DICOM) {
return this._httpGetMultipartApplicationDicom(url, mediaTypes).then(
getFirstResult
);
} else if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes).then(
getFirstResult
);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes).then(getFirstResultIfLengthGtOne);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes).then(getFirstResultIfLengthGtOne);
}
throw new Error(
`Media type ${commonMediaType} is not supported for retrieval of instance.`
);
}

@@ -475,16 +1186,36 @@

* @param {Object} options options object
* @returns {Arraybuffer[]} Array of DICOM Part 10 files as Arraybuffers
* @returns {ArrayBuffer[]} Array of DICOM Part 10 files as Arraybuffers
*/
retrieveSeries(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required')
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required");
}
if (!('seriesInstanceUID' in options)) {
throw new Error('Series Instance UID is required')
if (!("seriesInstanceUID" in options)) {
throw new Error("Series Instance UID is required");
}
const url = this.wadoURL +
'/studies/' + options.studyInstanceUID +
'/series/' + options.seriesInstanceUID;
return this._httpGetByMimeType(url, MIMETYPES.DICOM).then(multipartDecode);
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${
options.seriesInstanceUID
}`;
const { mediaTypes } = options;
if (!mediaTypes) {
return this._httpGetMultipartApplicationDicom(url);
}
const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.DICOM) {
return this._httpGetMultipartApplicationDicom(url, mediaTypes);
} else if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes);
}
throw new Error(
`Media type ${commonMediaType} is not supported for retrieval of series.`
);
}

@@ -495,13 +1226,31 @@

* @param {Object} options options object
* @returns {Arraybuffer[]} Array of DICOM Part 10 files as Arraybuffers
* @returns {ArrayBuffer[]} Array of DICOM Part 10 files as Arraybuffers
*/
retrieveStudy(options) {
if (!('studyInstanceUID' in options)) {
throw new Error('Study Instance UID is required')
if (!("studyInstanceUID" in options)) {
throw new Error("Study Instance UID is required");
}
const url = this.wadoURL +
'/studies/' + options.studyInstanceUID;
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}`;
return this._httpGetByMimeType(url, MIMETYPES.DICOM).then(multipartDecode);
const { mediaTypes } = options;
if (!mediaTypes) {
return this._httpGetMultipartApplicationDicom(url);
}
const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.DICOM) {
return this._httpGetMultipartApplicationDicom(url, mediaTypes);
} else if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes);
}
throw new Error(
`Media type ${commonMediaType} is not supported for retrieval of study.`
);
}

@@ -520,9 +1269,32 @@

retrieveBulkData(options) {
if (!('BulkDataURI' in options)) {
throw new Error('BulkDataURI is required.');
if (!("BulkDataURI" in options)) {
throw new Error("BulkDataURI is required.");
}
return this._httpGetByMimeType(options.BulkDataURI, MIMETYPES.OCTET_STREAM)
.then(multipartDecode)
.then(getFirstResult);
const url = options.BulkDataURI;
const { mediaTypes, byteRange } = options;
if (!mediaTypes) {
return this._httpGetMultipartApplicationOctetStream(
url,
mediaTypes,
byteRange
);
}
const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(
url,
mediaTypes,
byteRange
);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes, byteRange);
}
throw new Error(
`Media type ${commonMediaType} is not supported for retrieval of bulk data.`
);
}

@@ -536,8 +1308,8 @@

storeInstances(options) {
if (!('datasets' in options)) {
throw new Error('datasets are required for storing')
if (!("datasets" in options)) {
throw new Error("datasets are required for storing");
}
let url = `${this.stowURL}/studies`;
if ('studyInstanceUID' in options) {
if ("studyInstanceUID" in options) {
url += `/${options.studyInstanceUID}`;

@@ -548,3 +1320,3 @@ }

const headers = {
'Content-Type': `multipart/related; type=application/dicom; boundary=${boundary}`
"Content-Type": `multipart/related; type=application/dicom; boundary=${boundary}`
};

@@ -557,1 +1329,2 @@

export { DICOMwebClient };
export default DICOMwebClient;

@@ -1,11 +0,13 @@

import { DICOMwebClient } from './api.js';
import {
getStudyInstanceUIDFromUri, getSeriesInstanceUIDFromUri,
getSOPInstanceUIDFromUri, getFrameNumbersFromUri
} from './utils.js';
import { DICOMwebClient } from "./api.js";
import {
getStudyInstanceUIDFromUri,
getSeriesInstanceUIDFromUri,
getSOPInstanceUIDFromUri,
getFrameNumbersFromUri
} from "./utils.js";
let api = {
DICOMwebClient,
const api = {
DICOMwebClient
};
let utils = {
const utils = {
getStudyInstanceUIDFromUri,

@@ -17,4 +19,4 @@ getSeriesInstanceUIDFromUri,

export { default as version } from './version.js';
export { default as version } from "./version.js";
export { api, utils };
/**
* Converts a Uint8Array to a String.
* @param {Uint8Array} array that should be converted
* @param {Number} offset array offset in case only subset of array items should be extracted (default: 0)
* @param {Number} limit maximum number of array items that should be extracted (defaults to length of array)
* @param {Number} offset array offset in case only subset of array items should
be extracted (default: 0)
* @param {Number} limit maximum number of array items that should be extracted
(defaults to length of array)
* @returns {String}
*/
function uint8ArrayToString(arr, offset, limit) {
offset = offset || 0;
limit = limit || arr.length - offset;
let str = '';
for (let i = offset; i < offset + limit; i++) {
function uint8ArrayToString(arr, offset = 0, limit) {
const itemLimit = limit || arr.length - offset;
let str = "";
for (let i = offset; i < offset + itemLimit; i++) {
str += String.fromCharCode(arr[i]);

@@ -18,3 +19,2 @@ }

/**

@@ -33,3 +33,2 @@ * Converts a String to a Uint8Array.

/**

@@ -41,12 +40,13 @@ * Identifies the boundary in a multipart/related message header.

function identifyBoundary(header) {
const parts = header.split('\r\n');
const parts = header.split("\r\n");
for (let i = 0; i < parts.length; i++) {
if (parts[i].substr(0, 2) === '--') {
if (parts[i].substr(0, 2) === "--") {
return parts[i];
}
}
return null;
}
/**

@@ -59,3 +59,3 @@ * Checks whether a given token is contained by a message at a given offset.

*/
function containsToken(message, token, offset=0) {
function containsToken(message, token, offset = 0) {
if (offset + token.length > message.length) {

@@ -67,5 +67,7 @@ return false;

for (let i = 0; i < token.length; i++) {
if (token[i] !== message[index++]) {
if (token[i] !== message[index]) {
return false;
}
index += 1;
}

@@ -75,3 +77,2 @@ return true;

/**

@@ -84,3 +85,3 @@ * Finds a given token in a message at a given offset.

*/
function findToken(message, token, offset=0, maxSearchLength) {
function findToken(message, token, offset = 0, maxSearchLength) {
let searchLength = message.length;

@@ -106,2 +107,16 @@ if (maxSearchLength) {

/**
* Create a random GUID
*
* @return {string}
*/
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
}
/**
* @typedef {Object} MultipartEncodedData

@@ -116,7 +131,16 @@ * @property {ArrayBuffer} data The encoded Multipart Data

*
* @param {ArrayBuffer[]} datasets Array containing each file to be encoded in the multipart body, passed as ArrayBuffers.
* @param {String} [boundary] Optional string to define a boundary between each part of the multipart body. If this is not specified, a random GUID will be generated.
* @return {MultipartEncodedData} The Multipart encoded data returned as an Object. This contains both the data itself, and the boundary string used to divide it.
* @param {ArrayBuffer[]} datasets Array containing each file to be encoded in the
multipart body, passed as ArrayBuffers.
* @param {String} [boundary] Optional string to define a boundary between each part
of the multipart body. If this is not specified, a random
GUID will be generated.
* @return {MultipartEncodedData} The Multipart encoded data returned as an Object. This
contains both the data itself, and the boundary string
used to divide it.
*/
function multipartEncode(datasets, boundary=guid(), contentType='application/dicom') {
function multipartEncode(
datasets,
boundary = guid(),
contentType = "application/dicom"
) {
const contentTypeString = `Content-Type: ${contentType}`;

@@ -140,3 +164,3 @@ const header = `\r\n--${boundary}\r\n${contentTypeString}\r\n\r\n`;

return contentArray;
})
});

@@ -152,4 +176,2 @@ // Allocate the array

contentArrays.forEach(contentArray => {
const contentLength = contentArray.length;
multipartArray.set(headerArray, position);

@@ -167,3 +189,3 @@ multipartArray.set(contentArray, position + headerLength);

};
};
}

@@ -177,77 +199,67 @@ /**

function multipartDecode(response) {
const message = new Uint8Array(response);
const message = new Uint8Array(response);
/* Set a maximum length to search for the header boundaries, otherwise
/* Set a maximum length to search for the header boundaries, otherwise
findToken can run for a long time
*/
const maxSearchLength = 1000;
const maxSearchLength = 1000;
// First look for the multipart mime header
const separator = stringToUint8Array("\r\n\r\n");
const headerIndex = findToken(message, separator, 0, maxSearchLength);
if (headerIndex === -1) {
throw new Error("Response message has no multipart mime header");
}
// First look for the multipart mime header
let separator = stringToUint8Array('\r\n\r\n');
let headerIndex = findToken(message, separator, 0, maxSearchLength);
if (headerIndex === -1) {
throw new Error('Response message has no multipart mime header');
}
const header = uint8ArrayToString(message, 0, headerIndex);
const boundaryString = identifyBoundary(header);
if (!boundaryString) {
throw new Error("Header of response message does not specify boundary");
}
const header = uint8ArrayToString(message, 0, headerIndex);
const boundaryString = identifyBoundary(header);
if (!boundaryString) {
throw new Error('Header of response message does not specify boundary');
}
const boundary = stringToUint8Array(boundaryString);
const boundaryLength = boundary.length;
const components = [];
const boundary = stringToUint8Array(boundaryString);
const boundaryLength = boundary.length;
const components = [];
let offset = boundaryLength;
let offset = boundaryLength;
// Loop until we cannot find any more boundaries
let boundaryIndex;
// Loop until we cannot find any more boundaries
let boundaryIndex;
while (boundaryIndex !== -1) {
// Search for the next boundary in the message, starting
// from the current offset position
boundaryIndex = findToken(message, boundary, offset);
while (boundaryIndex !== -1) {
// Search for the next boundary in the message, starting
// from the current offset position
boundaryIndex = findToken(message, boundary, offset);
// If no further boundaries are found, stop here.
if (boundaryIndex === -1) {
break;
}
// If no further boundaries are found, stop here.
if (boundaryIndex === -1) {
break;
}
let headerIndex = findToken(message, separator, offset, maxSearchLength);
if (headerIndex === -1) {
throw new Error('Response message part has no mime header');
}
offset = headerIndex + separator.length;
const headerTokenIndex = findToken(
message,
separator,
offset,
maxSearchLength
);
if (headerTokenIndex === -1) {
throw new Error("Response message part has no mime header");
}
offset = headerTokenIndex + separator.length;
// Extract data from response message, excluding "\r\n"
const spacingLength = 2;
const data = response.slice(offset, boundaryIndex - spacingLength);
// Extract data from response message, excluding "\r\n"
const spacingLength = 2;
const data = response.slice(offset, boundaryIndex - spacingLength);
// Add the data to the array of results
components.push(data);
// Add the data to the array of results
components.push(data);
// Move the offset to the end of the current section,
// plus the identified boundary
offset = boundaryIndex + boundaryLength;
}
// Move the offset to the end of the current section,
// plus the identified boundary
offset = boundaryIndex + boundaryLength;
}
return components;
return components;
}
/**
* Create a random GUID
*
* @return {string}
*/
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}
export {

@@ -261,3 +273,3 @@ containsToken,

multipartDecode,
guid,
guid
};
function findSubstring(str, before, after) {
const beforeIndex = str.lastIndexOf(before) + before.length;
if (beforeIndex < before.length) {
return(null);
const beforeIndex = str.lastIndexOf(before) + before.length;
if (beforeIndex < before.length) {
return null;
}
if (after !== undefined) {
const afterIndex = str.lastIndexOf(after);
if (afterIndex < 0) {
return null;
}
if (after !== undefined) {
const afterIndex = str.lastIndexOf(after);
if (afterIndex < 0) {
return(null);
} else{
return(str.substring(beforeIndex, afterIndex));
}
}
return(str.substring(beforeIndex));
return str.substring(beforeIndex, afterIndex);
}
return str.substring(beforeIndex);
}
function getStudyInstanceUIDFromUri(uri) {

@@ -24,8 +22,9 @@ let uid = findSubstring(uri, "studies/", "/series");

if (!uid) {
console.debug('Study Instance UID could not be dertermined from URI "' + uri + '"');
console.debug(
`Study Instance UID could not be dertermined from URI "${uri}"`
);
}
return(uid);
return uid;
}
function getSeriesInstanceUIDFromUri(uri) {

@@ -37,8 +36,9 @@ let uid = findSubstring(uri, "series/", "/instances");

if (!uid) {
console.debug('Series Instance UID could not be dertermined from URI "' + uri + '"');
console.debug(
`Series Instance UID could not be dertermined from URI "${uri}"`
);
}
return(uid);
return uid;
}
function getSOPInstanceUIDFromUri(uri) {

@@ -53,8 +53,7 @@ let uid = findSubstring(uri, "/instances/", "/frames");

if (!uid) {
console.debug('SOP Instance UID could not be dertermined from URI"' + uri + '"');
console.debug(`SOP Instance UID could not be dertermined from URI"${uri}"`);
}
return(uid);
return uid;
}
function getFrameNumbersFromUri(uri) {

@@ -66,5 +65,5 @@ let numbers = findSubstring(uri, "/frames/", "/rendered");

if (numbers === undefined) {
console.debug('Frames Numbers could not be dertermined from URI"' + uri + '"');
console.debug(`Frames Numbers could not be dertermined from URI"${uri}"`);
}
return(numbers.split(','));
return numbers.split(",");
}

@@ -76,3 +75,3 @@

getSOPInstanceUIDFromUri,
getFrameNumbersFromUri,
getFrameNumbersFromUri
};

@@ -1,1 +0,1 @@

export default '0.3.2';
export default "0.5.0";

@@ -25,2 +25,3 @@ const { expect } = chai;

url: 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs',
retrieveRendered: false
});

@@ -151,4 +152,6 @@

expect(bulkData).to.be.an('arraybuffer');
expect(bulkData).to.be.an('array');
expect(bulkData).to.to.have.length(1);
expect(bulkData[0]).to.be.an('arraybuffer');
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc