dicom-microscopy-viewer-changed
Version:
Interactive web-based viewer for DICOM Microscopy Images
1,404 lines (1,189 loc) • 74.5 kB
JavaScript
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _readOnlyError(name) {
throw new Error("\"" + name + "\" is read-only");
}
/**
* 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)
* @returns {String}
*/
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 + itemLimit; i++) {
str += String.fromCharCode(arr[i]);
}
return str;
}
/**
* Converts a String to a Uint8Array.
* @param {String} str string that should be converted
* @returns {Uint8Array}
*/
function stringToUint8Array(str) {
var arr = new Uint8Array(str.length);
for (var i = 0, j = str.length; i < j; i++) {
arr[i] = str.charCodeAt(i);
}
return arr;
}
/**
* Identifies the boundary in a multipart/related message header.
* @param {String} header message header
* @returns {String} boundary
*/
function identifyBoundary(header) {
var parts = header.split("\r\n");
for (var i = 0; i < parts.length; i++) {
if (parts[i].substr(0, 2) === "--") {
return parts[i];
}
}
return null;
}
/**
* Checks whether a given token is contained by a message at a given offset.
* @param {Uint8Array} message message content
* @param {Uint8Array} token substring that should be present
* @param {Number} offset offset in message content from where search should start
* @returns {Boolean} whether message contains token at offset
*/
function containsToken(message, token) {
var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
if (offset + token.length > message.length) {
return false;
}
var index = offset;
for (var i = 0; i < token.length; i++) {
if (token[i] !== message[index]) {
return false;
}
index += 1;
}
return true;
}
/**
* Finds a given token in a message at a given offset.
* @param {Uint8Array} message message content
* @param {Uint8Array} token substring that should be found
* @param {String} offset message body offset from where search should start
* @returns {Boolean} whether message has a part at given offset or not
*/
function findToken(message, token) {
var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
var maxSearchLength = arguments.length > 3 ? arguments[3] : undefined;
var searchLength = message.length;
if (maxSearchLength) {
searchLength = Math.min(offset + maxSearchLength, message.length);
}
for (var i = offset; i < searchLength; i++) {
// If the first value of the message matches
// the first value of the token, check if
// this is the full token.
if (message[i] === token[0]) {
if (containsToken(message, token, i)) {
return i;
}
}
}
return -1;
}
/**
* 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
* @property {ArrayBuffer} data The encoded Multipart Data
* @property {String} boundary The boundary used to divide pieces of the encoded data
*/
/**
* Encode one or more DICOM datasets into a single body so it can be
* sent using the Multipart Content-Type.
*
* @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) {
var boundary = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : guid();
var contentType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "application/dicom";
var contentTypeString = "Content-Type: ".concat(contentType);
var header = "\r\n--".concat(boundary, "\r\n").concat(contentTypeString, "\r\n\r\n");
var footer = "\r\n--".concat(boundary, "--");
var headerArray = stringToUint8Array(header);
var footerArray = stringToUint8Array(footer);
var headerLength = headerArray.length;
var footerLength = footerArray.length;
var length = 0; // Calculate the total length for the final array
var contentArrays = datasets.map(function (datasetBuffer) {
var contentArray = new Uint8Array(datasetBuffer);
var contentLength = contentArray.length;
length += headerLength + contentLength + footerLength;
return contentArray;
}); // Allocate the array
var multipartArray = new Uint8Array(length); // Set the initial header
multipartArray.set(headerArray, 0); // Write each dataset into the multipart array
var position = 0;
contentArrays.forEach(function (contentArray) {
multipartArray.set(headerArray, position);
multipartArray.set(contentArray, position + headerLength);
position += headerLength + contentArray.length;
});
multipartArray.set(footerArray, position);
return {
data: multipartArray.buffer,
boundary: boundary
};
}
/**
* Decode a Multipart encoded ArrayBuffer and return the components as an Array.
*
* @param {ArrayBuffer} response Data encoded as a 'multipart/related' message
* @returns {Array} The content
*/
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
*/
var maxSearchLength = 1000; // First look for the multipart mime header
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");
}
var header = uint8ArrayToString(message, 0, headerIndex);
var boundaryString = identifyBoundary(header);
if (!boundaryString) {
throw new Error("Header of response message does not specify boundary");
}
var boundary = stringToUint8Array(boundaryString);
var boundaryLength = boundary.length;
var components = [];
var offset = boundaryLength; // Loop until we cannot find any more boundaries
var boundaryIndex;
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;
}
var 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"
var spacingLength = 2;
var data = response.slice(offset, boundaryIndex - spacingLength); // 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;
}
return components;
}
function isObject(obj) {
return _typeof(obj) === "object" && obj !== null;
}
function isEmptyObject(obj) {
return Object.keys(obj).length === 0 && obj.constructor === Object;
}
function areValidRequestHooks(requestHooks) {
var isValid = Array.isArray(requestHooks) && requestHooks.every(function (requestHook) {
return typeof requestHook === 'function' && requestHook.length === 2;
});
if (!isValid) {
console.warn('Request hooks should have the following signature: ' + 'function requestHook(request, metadata) { return request; }');
}
return isValid;
}
var getFirstResult = function getFirstResult(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"
};
/**
* A callback with the request instance and metadata information
* of the currently request being executed that should necessarily
* return the given request optionally modified.
* @typedef {function} RequestHook
* @param {XMLHttpRequest} request - The original XMLHttpRequest instance.
* @param {object} metadata - The metadata used by the request.
*/
/**
* Class for interacting with DICOMweb RESTful services.
*/
var DICOMwebClient =
/*#__PURE__*/
function () {
/**
* @constructor
* @param {Object} options
* @param {String} options.url - URL of the DICOMweb RESTful Service endpoint
* @param {String} options.qidoURLPrefix - URL path prefix for QIDO-RS
* @param {String} options.wadoURLPrefix - URL path prefix for WADO-RS
* @param {String} options.stowURLPrefix - URL path prefix for STOW-RS
* @param {String} options.username - Username
* @param {String} options.password - Password
* @param {Object} options.headers - HTTP headers
* @param {Array.<RequestHook>} options.requestHooks - Request hooks.
* @param {Object} options.verbose - print to console request warnings and errors, default true
*/
function DICOMwebClient(options) {
_classCallCheck(this, DICOMwebClient);
this.baseURL = options.url;
if (!this.baseURL) {
console.error("no DICOMweb base url provided - calls will fail");
}
if ("username" in options) {
this.username = options.username;
if (!("password" in options)) {
console.error("no password provided to authenticate with DICOMweb service");
}
this.password = options.password;
}
if ("qidoURLPrefix" in options) {
console.log("use URL prefix for QIDO-RS: ".concat(options.qidoURLPrefix));
this.qidoURL = "".concat(this.baseURL, "/").concat(options.qidoURLPrefix);
} else {
this.qidoURL = this.baseURL;
}
if ("wadoURLPrefix" in options) {
console.log("use URL prefix for WADO-RS: ".concat(options.wadoURLPrefix));
this.wadoURL = "".concat(this.baseURL, "/").concat(options.wadoURLPrefix);
} else {
this.wadoURL = this.baseURL;
}
if ("stowURLPrefix" in options) {
console.log("use URL prefix for STOW-RS: ".concat(options.stowURLPrefix));
this.stowURL = "".concat(this.baseURL, "/").concat(options.stowURLPrefix);
} else {
this.stowURL = this.baseURL;
}
if ("requestHooks" in options) {
this.requestHooks = options.requestHooks;
} // Headers to pass to requests.
this.headers = options.headers || {}; // Optional error interceptor callback to handle any failed request.
this.errorInterceptor = options.errorInterceptor || function () {}; // Verbose - print to console request warnings and errors, default true
this.verbose = options.verbose === false ? false : true;
}
/**
* Sets verbose flag.
*
* @param {Boolean} verbose
*/
_createClass(DICOMwebClient, [{
key: "setVerbose",
value: function setVerbose(verbose) {
this.verbose = verbose;
}
/**
* Gets verbose flag.
*
* @return {Boolean} verbose
*/
}, {
key: "getVerbose",
value: function getVerbose() {
return this.verbose;
}
}, {
key: "_httpRequest",
/**
* Performs an HTTP request.
*
* @param {String} url
* @param {String} method
* @param {Object} headers
* @param {Object} options
* @param {Array.<RequestHook>} options.requestHooks - Request hooks.
* @return {*}
* @private
*/
value: function _httpRequest(url, method) {
var _this = this;
var headers = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
var errorInterceptor = this.errorInterceptor,
requestHooks = this.requestHooks;
return new Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
request.open(method, url, true);
if ("responseType" in options) {
request.responseType = options.responseType;
}
if (_typeof(headers) === "object") {
Object.keys(headers).forEach(function (key) {
request.setRequestHeader(key, headers[key]);
});
} // now add custom headers from the user
// (e.g. access tokens)
var userHeaders = _this.headers;
Object.keys(userHeaders).forEach(function (key) {
request.setRequestHeader(key, userHeaders[key]);
}); // Event triggered when upload starts
request.onloadstart = function onloadstart() {// console.log('upload started: ', url)
}; // Event triggered when upload ends
request.onloadend = function onloadend() {// console.log('upload finished')
}; // Handle response message
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
resolve(request.response);
} else if (request.status === 202) {
if (_this.verbose) {
console.warn("some resources already existed: ", request);
}
resolve(request.response);
} else if (request.status === 204) {
if (_this.verbose) {
console.warn("empty response for request: ", request);
}
resolve([]);
} else {
var error = new Error("request failed");
error.request = request;
error.response = request.response;
error.status = request.status;
if (_this.verbose) {
console.error("request failed: ", request);
console.error(error);
console.error(error.response);
}
errorInterceptor(error);
reject(error);
}
}
}; // Event triggered while download progresses
if ("progressCallback" in options) {
if (typeof options.progressCallback === "function") {
request.onprogress = options.progressCallback;
}
}
if (requestHooks && areValidRequestHooks(requestHooks)) {
var _headers = Object.assign({}, _headers, _this.headers);
var metadata = {
method: method,
url: url,
headers: _headers
};
var pipeRequestHooks = function pipeRequestHooks(functions) {
return function (args) {
return functions.reduce(function (args, fn) {
return fn(args, metadata);
}, args);
};
};
var pipedRequest = pipeRequestHooks(requestHooks);
request = pipedRequest(request);
} // Add withCredentials to request if needed
if ("withCredentials" in options) {
if (options.withCredentials) {
request.withCredentials = true;
}
}
if ("data" in options) {
request.send(options.data);
} else {
request.send();
}
});
}
/**
* Performs an HTTP GET request.
*
* @param {String} url
* @param {Object} headers
* @param {Object} responseType
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGet",
value: function _httpGet(url, headers, responseType, progressCallback, withCredentials) {
return this._httpRequest(url, "get", headers, {
responseType: responseType,
progressCallback: progressCallback,
withCredentials: withCredentials
});
}
/**
* Performs an HTTP GET request that accepts a message with application/json
* media type.
*
* @param {String} url
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
key: "_httpGetApplicationJson",
value: function _httpGetApplicationJson(url) {
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var progressCallback = arguments.length > 2 ? arguments[2] : undefined;
var withCredentials = arguments.length > 3 ? arguments[3] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
var headers = {
Accept: MEDIATYPES.DICOM_JSON
};
var responseType = "json";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback, withCredentials);
}
/**
* Performs an HTTP GET request that accepts a message with application/pdf
* media type.
*
* @param {String} url
* @param {Object} params
* @param {Function} progressCallback
* @return {*}
* @private
*/
}, {
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 withCredentials = arguments.length > 3 ? arguments[3] : undefined;
var urlWithQueryParams = url;
if (_typeof(params) === "object") {
if (!isEmptyObject(params)) {
urlWithQueryParams += DICOMwebClient._parseQueryParameters(params);
}
}
var headers = {
Accept: MEDIATYPES.PDF
};
var responseType = "json";
return this._httpGet(urlWithQueryParams, headers, responseType, progressCallback, withCredentials);
}
/**
* 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 withCredentials = arguments.length > 4 ? arguments[4] : 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, withCredentials);
}
/**
* 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 withCredentials = arguments.length > 4 ? arguments[4] : 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, withCredentials);
}
/**
* 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 withCredentials = arguments.length > 4 ? arguments[4] : 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, withCredentials);
}
/**
* 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 {Promise<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 withCredentials = arguments.length > 6 ? arguments[6] : 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, withCredentials).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 {Promise<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 withCredentials = arguments.length > 6 ? arguments[6] : 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, withCredentials).then(multipartDecode);
}
/**
* Performs an HTTP GET request that accepts a multipart message with a 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 {Promise<Array>} Content of HTTP message body parts
*/
}, {
key: "_httpGetMultipartApplicationDicom",
value: function _httpGetMultipartApplicationDicom(url, mediaTypes, params, progressCallback, withCredentials) {
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, withCredentials).then(multipartDecode);
}
/**
* Performs an HTTP GET request that accepts a multipart message with a 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 {Promise<Array>} Content of HTTP message body parts
*/
}, {
key: "_httpGetMultipartApplicationOctetStream",
value: function _httpGetMultipartApplicationOctetStream(url, mediaTypes, byteRange, params, progressCallback, withCredentials) {
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, withCredentials).then(multipartDecode);
}
/**
* Performs an HTTP POST request.
*
* @param {String} url - Unique resource locator
* @param {Object} headers - HTTP header fields
* @param {Array} data - Data that should be stored
* @param {Function} progressCallback
* @private
* @returns {Promise} Response
*/
}, {
key: "_httpPost",
value: function _httpPost(url, headers, data, progressCallback, withCredentials) {
return this._httpRequest(url, "post", headers, {
data: data,
progressCallback: progressCallback,
withCredentials: withCredentials
});
}
/**
* Performs an HTTP POST request with content-type application/dicom+json.
*
* @param {String} url - Unique resource locator
* @param {Object} headers - HTTP header fields
* @param {Array} data - Data that should be stored
* @param {Function} progressCallback
* @private
* @returns {Promise} Response
*/
}, {
key: "_httpPostApplicationJson",
value: function _httpPostApplicationJson(url, data, progressCallback, withCredentials) {
var headers = {
"Content-Type": MEDIATYPES.DICOM_JSON
};
return this._httpPost(url, headers, data, progressCallback, withCredentials);
}
/**
* Parses media type and extracts its type and subtype.
*
* @param {String} mediaType - HTTP media type (e.g. image/jpeg)
* @private
* @returns {String[]} Media type and subtype
*/
}, {
key: "searchForStudies",
/**
* Searches for DICOM studies.
*
* @param {Object} options
* @param {Object} [options.queryParams] - HTTP query parameters
* @return {Object[]} Study representations (http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_6.7.html#table_6.7.1-2)
*/
value: function searchForStudies() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
console.log("search for studies");
var withCredentials = false;
var url = "".concat(this.qidoURL, "/studies");
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);
}
if ("withCredentials" in options) {
if (options.withCredentials) {
withCredentials = options.withCredentials;
}
}
return this._httpGetApplicationJson(url, {}, false, withCredentials);
}
/**
* Retrieves metadata for a DICOM study.
*
* @param {Object} options
* @param {Object} studyInstanceUID - Study Instance UID
* @returns {Object[]} Metadata elements in DICOM JSON format for each instance
belonging to the study
*/
}, {
key: "retrieveStudyMetadata",
value: function retrieveStudyMetadata(options) {
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 = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/metadata");
var withCredentials = false;
if ("withCredentials" in options) {
if (options.withCredentials) {
withCredentials = options.withCredentials;
}
}
return this._httpGetApplicationJson(url, {}, false, withCredentials);
}
/**
* Searches for DICOM series.
*
* @param {Object} options
* @param {Object} [options.studyInstanceUID] - Study Instance UID
* @param {Object} [options.queryParams] - HTTP query parameters
* @returns {Object[]} Series representations (http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_6.7.html#table_6.7.1-2a)
*/
}, {
key: "searchForSeries",
value: function searchForSeries() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var url = this.qidoURL;
if ("studyInstanceUID" in options) {
console.log("search series of study ".concat(options.studyInstanceUID));
url += "/studies/".concat(options.studyInstanceUID);
}
url += "/series";
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);
}
var withCredentials = false;
if ("withCredentials" in options) {
if (options.withCredentials) {
withCredentials = options.withCredentials;
}
}
return this._httpGetApplicationJson(url, {}, false, withCredentials);
}
/**
* Retrieves metadata for a DICOM series.
*
* @param {Object} options
* @param {Object} options.studyInstanceUID - Study Instance UID
* @param {Object} options.seriesInstanceUID - Series Instance UID
* @returns {Object[]} Metadata elements in DICOM JSON format for each instance
belonging to the series
*/
}, {
key: "retrieveSeriesMetadata",
value: function retrieveSeriesMetadata(options) {
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");
}
console.log("retrieve metadata of series ".concat(options.seriesInstanceUID));
var url = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/metadata");
var withCredentials = false;
if ("withCredentials" in options) {
if (options.withCredentials) {
withCredentials = options.withCredentials;
}
}
return this._httpGetApplicationJson(url, {}, false, withCredentials);
}
/**
* Searches for DICOM Instances.
*
* @param {Object} options
* @param {Object} [options.studyInstanceUID] - Study Instance UID
* @param {Object} [options.seriesInstanceUID] - Series Instance UID
* @param {Object} [options.queryParams] - HTTP query parameters
* @returns {Object[]} Instance representations (http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_6.7.html#table_6.7.1-2b)
*/
}, {
key: "searchForInstances",
value: function searchForInstances() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var url = this.qidoURL;
var withCredentials = false;
if ("studyInstanceUID" in options) {
url += "/studies/".concat(options.studyInstanceUID);
if ("seriesInstanceUID" in options) {
console.log("search for instances of series ".concat(options.seriesInstanceUID));
url += "/series/".concat(options.seriesInstanceUID);
} else {
console.log("search for instances of study ".concat(options.studyInstanceUID));
}
} else {
console.log("search for instances");
}
url += "/instances";
if ("queryParams" in options) {
url += DICOMwebClient._parseQueryParameters(options.queryParams);
}
if ("withCredentials" in options) {
if (options.withCredentials) {
withCredentials = options.withCredentials;
}
}
return this._httpGetApplicationJson(url, {}, false, withCredentials);
}
/** Returns a WADO-URI URL for an instance
*
* @param {Object} options
* @param {Object} options.studyInstanceUID - Study Instance UID
* @param {Object} options.seriesInstanceUID - Series Instance UID
* @param {Object} options.sopInstanceUID - SOP Instance UID
* @returns {String} WADO-URI URL
*/
}, {
key: "buildInstanceWadoURIUrl",
value: function buildInstanceWadoURIUrl(options) {
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 (!("sopInstanceUID" in options)) {
throw new Error("SOP Instance UID is required.");
}
var contentType = options.contentType || MEDIATYPES.DICOM;
var transferSyntax = options.transferSyntax || "*";
var params = [];
params.push("requestType=WADO");
params.push("studyUID=".concat(options.studyInstanceUID));
params.push("seriesUID=".concat(options.seriesInstanceUID));
params.push("objectUID=".concat(options.sopInstanceUID));
params.push("contentType=".concat(contentType));
params.push("transferSyntax=".concat(transferSyntax));
var paramString = params.join("&");
return "".concat(this.wadoURL, "?").concat(paramString);
}
/**
* Retrieves metadata for a DICOM Instance.
*
* @param {Object} options object
* @param {String} options.studyInstanceUID - Study Instance UID
* @param {String} options.seriesInstanceUID - Series Instance UID
* @param {String} options.sopInstanceUID - SOP Instance UID
* @returns {Object} metadata elements in DICOM JSON format
*/
}, {
key: "retrieveInstanceMetadata",
value: function retrieveInstanceMetadata(options) {
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 (!("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 = "".concat(this.wadoURL, "/studies/").concat(options.studyInstanceUID, "/series/").concat(options.seriesInstanceUID, "/instances/").concat(options.sopInstanceUID, "/metadata");
var withCredentials = false;
if ("withCredentials" in options) {
if (options.withCredentials) {
withCredentials = options.withCredentials;
}
}
return this._httpGetApplicationJson(url, {}, false, withCredentials);
}
/**
* Retrieves frames for a DICOM Instance.
* @param {Object} options options object
* @param {String} options.studyInstanceUID - Study Instance UID
* @param {String} options.seriesInstanceUID - Series Instance UID
* @param {String} options.sopInstanceUID - SOP Instance UID
* @param {String} options.frameNumbers - One-based indices of Frame Items
* @returns {Array} frame items as byte arrays of the pixel data element
*/
}, {
key: "retrieveInstanceFrames",
value: function retrieveInstanceFrames(options) {
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 (!("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");
}
console.log("retrieve 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());
var mediaTypes = options.mediaTypes;
var withCredentials = false;
if ("withCredentials" in options) {
if (options.withCredentials) {
withCredentials = options.withCredentials;
}
}
var progressCallback = false;
if ("progressCallback" in options) {
progressCallback = options.progressCallback;
}
if (!mediaTypes) {
return this._httpGetMultipartApplicationOctetStream(url, false, false, false, progressCallback, withCredentials);
}
var sharedMediaTypes = DICOMwebClient._getSharedMediaTypes(mediaTypes);
if (sharedMediaTypes.length > 1) {
/**
* Enable request of frames that are stored either compressed
* (image/* media type) or uncompressed (application/octet-stream
* media type).
*/
var supportedMediaTypes = {
"1.2.840.10008.1.2.1": ["application/octet-stream"],
"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"]
};
var headers = {
Accept: DICOMwebClient._buildMultipartAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes)
};
return this._httpGet(url, headers, "arraybuffer", progressCallback, withCredentials).then(multipartDecode);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType.startsWith("application")) {
return this._httpGetMultipartApplicationOctetStream(url, mediaTypes, false, false, progressCallback, withCredentials);
} else if (commonMediaType.startsWith("image")) {
return this._httpGetMultipartImage(url, mediaTypes, false, false, false, progressCallback, withCredentials);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetMultipartVideo(url, mediaTypes, false, false, false, progressCallback, withCredentials);
}
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
* @param {String} options.studyInstanceUID - Study Instance UID
* @param {String} options.seriesInstanceUID - Series Instance UID
* @param {String} options.sopInstanceUID - SOP Instance UID
* @param {String[]} [options.mediaType] - Acceptable HTTP media types
* @param {Object} [options.queryParams] - HTTP query parameters
* @returns {ArrayBuffer} Rendered DICOM Instance
*/
}, {
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,
queryParams = options.queryParams;
var headers = {};
var withCredentials = false;
if ("withCredentials" in options) {
if (options.withCredentials) {
withCredentials = options.withCredentials;
}
}
var progressCallback = false;
if ("progressCallback" in options) {
progressCallback = options.progressCallback;
}
if (!mediaTypes) {
var responseType = "arraybuffer";
if (queryParams) {
url += (_readOnlyError("url"), DICOMwebClient._parseQueryParameters(queryParams));
}
return this._httpGet(url, headers, responseType, progressCallback, withCredentials);
}
var commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);
if (commonMediaType.startsWith("image")) {
return this._httpGetImage(url, mediaTypes, queryParams, progressCallback, withCredentials);
} else if (commonMediaType.startsWith("video")) {
return this._httpGetVideo(url, mediaTypes, queryParams, progressCallback, withCredentials);
} else if (commonMediaType.startsWith("text")) {
return this._httpGetText(url, mediaTypes, queryParams, progressCallback, withCredentials);
} else if (commonMediaType === MEDIATYPES.PDF) {
return this._httpGetApplicationPdf(url, queryParams, progressCallback, withCredentials);
}
throw new Error("Media type ".concat(commonMediaType, " is not supported ") + 'for retrieval of rendered instance.');
}
/**
* Retrieves a thumbnail of an DICOM Instance.
*
* @param {Object} options
* @param {String} options.studyInstanceUID - Study Instance UID
* @param {String} options.seriesInstanceUID - Series Instance UID
* @param {String} options.sopInstanceUID - SOP Instance UID
* @param {String[]} [options.mediaType] - Acceptable HTTP media types
* @param {Object} [options.queryParams] - HTTP query parameters
* @returns {ArrayBuffer} Thumbnail
*/
}, {
key: "retrieveInstanceThumbnail",
value: function retrieveInstanceThumbnail(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, "/thumbnail");
var mediaTypes = options.mediaTypes,
queryParams = options.queryParams;
v