UNPKG

molstar

Version:

A comprehensive macromolecular library.

389 lines 17.2 kB
"use strict"; /** * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> * * Adapted from LiteMol */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ajaxGetMany = exports.ajaxGet = exports.readFromFile = exports.readUint8ArrayFromFile = exports.readStringFromFile = exports.DataCompressionMethod = void 0; var tslib_1 = require("tslib"); var mol_task_1 = require("../mol-task"); var zip_1 = require("./zip/zip"); var utf8_1 = require("../mol-io/common/utf8"); var assets_1 = require("./assets"); // polyfill XMLHttpRequest in node.js var XHR = typeof document === 'undefined' ? require('xhr2') : XMLHttpRequest; var DataCompressionMethod; (function (DataCompressionMethod) { DataCompressionMethod[DataCompressionMethod["None"] = 0] = "None"; DataCompressionMethod[DataCompressionMethod["Gzip"] = 1] = "Gzip"; DataCompressionMethod[DataCompressionMethod["Zip"] = 2] = "Zip"; })(DataCompressionMethod = exports.DataCompressionMethod || (exports.DataCompressionMethod = {})); function readStringFromFile(file) { return readFromFileInternal(file, 'string'); } exports.readStringFromFile = readStringFromFile; function readUint8ArrayFromFile(file) { return readFromFileInternal(file, 'binary'); } exports.readUint8ArrayFromFile = readUint8ArrayFromFile; function readFromFile(file, type) { return readFromFileInternal(file, type); } exports.readFromFile = readFromFile; function ajaxGet(params) { if (typeof params === 'string') return ajaxGetInternal(params, params, 'string'); return ajaxGetInternal(params.title, params.url, params.type || 'string', params.body, params.headers); } exports.ajaxGet = ajaxGet; function isDone(data) { if (data instanceof FileReader) { return data.readyState === FileReader.DONE; } else if (data instanceof XMLHttpRequest) { return data.readyState === XMLHttpRequest.DONE; } throw new Error('unknown data type'); } function genericError(isDownload) { if (isDownload) return 'Failed to download data. Possible reasons: Resource is not available, or CORS is not allowed on the server.'; return 'Failed to open file.'; } function readData(ctx, action, data) { return new Promise(function (resolve, reject) { // first check if data reading is already done if (isDone(data)) { var error = data.error; if (error !== null && error !== undefined) { reject(error !== null && error !== void 0 ? error : genericError(data instanceof XMLHttpRequest)); } else { resolve(data); } return; } var hasError = false; data.onerror = function (e) { if (hasError) return; var error = e.target.error; reject(error !== null && error !== void 0 ? error : genericError(data instanceof XMLHttpRequest)); }; data.onprogress = function (e) { if (!ctx.shouldUpdate || hasError) return; try { if (e.lengthComputable) { ctx.update({ message: action, isIndeterminate: false, current: e.loaded, max: e.total }); } else { ctx.update({ message: action + " " + (e.loaded / 1024 / 1024).toFixed(2) + " MB", isIndeterminate: true }); } } catch (e) { hasError = true; reject(e); } }; data.onload = function (e) { resolve(data); }; }); } function getCompression(name) { return /\.gz$/i.test(name) ? DataCompressionMethod.Gzip : /\.zip$/i.test(name) ? DataCompressionMethod.Zip : DataCompressionMethod.None; } function decompress(ctx, data, compression) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var _a, parsed, names; return (0, tslib_1.__generator)(this, function (_b) { switch (_b.label) { case 0: _a = compression; switch (_a) { case DataCompressionMethod.None: return [3 /*break*/, 1]; case DataCompressionMethod.Gzip: return [3 /*break*/, 2]; case DataCompressionMethod.Zip: return [3 /*break*/, 3]; } return [3 /*break*/, 5]; case 1: return [2 /*return*/, data]; case 2: return [2 /*return*/, (0, zip_1.ungzip)(ctx, data)]; case 3: return [4 /*yield*/, (0, zip_1.unzip)(ctx, data.buffer)]; case 4: parsed = _b.sent(); names = Object.keys(parsed); if (names.length !== 1) throw new Error('can only decompress zip files with a single entry'); return [2 /*return*/, parsed[names[0]]]; case 5: return [2 /*return*/]; } }); }); } function processFile(ctx, reader, type, compression) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var result, data, decompressed, parser; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: result = reader.result; data = result instanceof ArrayBuffer ? new Uint8Array(result) : result; if (data === null) throw new Error('no data given'); if (!(compression !== DataCompressionMethod.None)) return [3 /*break*/, 4]; if (!(data instanceof Uint8Array)) throw new Error('need Uint8Array for decompression'); return [4 /*yield*/, decompress(ctx, data, compression)]; case 1: decompressed = _a.sent(); if (!(type === 'string')) return [3 /*break*/, 3]; return [4 /*yield*/, ctx.update({ message: 'Decoding text...' })]; case 2: _a.sent(); data = (0, utf8_1.utf8Read)(decompressed, 0, decompressed.length); return [3 /*break*/, 4]; case 3: data = decompressed; _a.label = 4; case 4: if (!(type === 'binary' && data instanceof Uint8Array)) return [3 /*break*/, 5]; return [2 /*return*/, data]; case 5: if (!(type === 'zip' && data instanceof Uint8Array)) return [3 /*break*/, 7]; return [4 /*yield*/, (0, zip_1.unzip)(ctx, data.buffer)]; case 6: return [2 /*return*/, _a.sent()]; case 7: if (type === 'string' && typeof data === 'string') { return [2 /*return*/, data]; } else if (type === 'xml' && typeof data === 'string') { parser = new DOMParser(); return [2 /*return*/, parser.parseFromString(data, 'application/xml')]; } else if (type === 'json' && typeof data === 'string') { return [2 /*return*/, JSON.parse(data)]; } _a.label = 8; case 8: throw new Error("could not get requested response data '" + type + "'"); } }); }); } function readFromFileInternal(file, type) { var _this = this; var reader = void 0; return mol_task_1.Task.create('Read File', function (ctx) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { var compression, fileReader; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, , 5, 6]); reader = new FileReader(); compression = type === 'zip' ? DataCompressionMethod.None : getCompression(file.name); if (type === 'binary' || type === 'zip' || compression !== DataCompressionMethod.None) { reader.readAsArrayBuffer(file); } else { reader.readAsText(file); } return [4 /*yield*/, ctx.update({ message: 'Opening file...', canAbort: true })]; case 1: _a.sent(); return [4 /*yield*/, readData(ctx, 'Reading...', reader)]; case 2: fileReader = _a.sent(); return [4 /*yield*/, ctx.update({ message: 'Processing file...', canAbort: false })]; case 3: _a.sent(); return [4 /*yield*/, processFile(ctx, fileReader, type, compression)]; case 4: return [2 /*return*/, _a.sent()]; case 5: reader = void 0; return [7 /*endfinally*/]; case 6: return [2 /*return*/]; } }); }); }, function () { if (reader) reader.abort(); }); } var RequestPool = /** @class */ (function () { function RequestPool() { } RequestPool.get = function () { if (this.pool.length) { return this.pool.pop(); } return new XHR(); }; RequestPool.emptyFunc = function () { }; RequestPool.deposit = function (req) { if (this.pool.length < this.poolSize) { req.onabort = RequestPool.emptyFunc; req.onerror = RequestPool.emptyFunc; req.onload = RequestPool.emptyFunc; req.onprogress = RequestPool.emptyFunc; this.pool.push(req); } }; RequestPool.pool = []; RequestPool.poolSize = 15; return RequestPool; }()); function processAjax(req, type) { if (req.status >= 200 && req.status < 400) { var response = req.response; RequestPool.deposit(req); if ((type === 'binary' || type === 'zip') && response instanceof ArrayBuffer) { return new Uint8Array(response); } else if (type === 'string' && typeof response === 'string') { return response; } else if (type === 'xml' && response instanceof XMLDocument) { return response; } else if (type === 'json' && typeof response === 'object') { return response; } throw new Error("could not get requested response data '" + type + "'"); } else { RequestPool.deposit(req); throw new Error("Download failed with status code " + req.status); } } function getRequestResponseType(type) { switch (type) { case 'json': return 'json'; case 'xml': return 'document'; case 'string': return 'text'; case 'binary': return 'arraybuffer'; case 'zip': return 'arraybuffer'; } } function ajaxGetInternal(title, url, type, body, headers) { var _this = this; var xhttp = void 0; return mol_task_1.Task.create(title ? title : 'Download', function (ctx) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { var _a, headers_1, _b, name_1, value, req, result; return (0, tslib_1.__generator)(this, function (_c) { switch (_c.label) { case 0: xhttp = RequestPool.get(); xhttp.open(body ? 'post' : 'get', url, true); if (headers) { for (_a = 0, headers_1 = headers; _a < headers_1.length; _a++) { _b = headers_1[_a], name_1 = _b[0], value = _b[1]; xhttp.setRequestHeader(name_1, value); } } xhttp.responseType = getRequestResponseType(type); xhttp.send(body); return [4 /*yield*/, ctx.update({ message: 'Waiting for server...', canAbort: true })]; case 1: _c.sent(); return [4 /*yield*/, readData(ctx, 'Downloading...', xhttp)]; case 2: req = _c.sent(); xhttp = void 0; // guard against reuse, help garbage collector return [4 /*yield*/, ctx.update({ message: 'Parsing response...', canAbort: false })]; case 3: _c.sent(); result = processAjax(req, type); return [2 /*return*/, result]; } }); }); }, function () { if (xhttp) { xhttp.abort(); xhttp = void 0; // guard against reuse, help garbage collector } }); } function ajaxGetMany(ctx, assetManager, sources, maxConcurrency) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var len, slots, promises, promiseKeys, currentSrc, _i, current, done, r, src, idx, current, asset; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: len = sources.length; slots = new Array(sources.length); return [4 /*yield*/, ctx.update({ message: 'Downloading...', current: 0, max: len })]; case 1: _a.sent(); promises = [], promiseKeys = []; currentSrc = 0; for (_i = Math.min(len, maxConcurrency); currentSrc < _i; currentSrc++) { current = sources[currentSrc]; promises.push(wrapPromise(currentSrc, current.id, assetManager.resolve(assets_1.Asset.getUrlAsset(assetManager, current.url), current.isBinary ? 'binary' : 'string').runAsChild(ctx))); promiseKeys.push(currentSrc); } done = 0; _a.label = 2; case 2: if (!(promises.length > 0)) return [3 /*break*/, 6]; return [4 /*yield*/, Promise.race(promises)]; case 3: r = _a.sent(); src = sources[r.index]; idx = promiseKeys.indexOf(r.index); done++; if (r.kind === 'error' && !src.canFail) { // TODO: cancel other downloads throw new Error(src.url + ": " + r.error); } if (!ctx.shouldUpdate) return [3 /*break*/, 5]; return [4 /*yield*/, ctx.update({ message: 'Downloading...', current: done, max: len })]; case 4: _a.sent(); _a.label = 5; case 5: slots[r.index] = r; promises = promises.filter(_filterRemoveIndex, idx); promiseKeys = promiseKeys.filter(_filterRemoveIndex, idx); if (currentSrc < len) { current = sources[currentSrc]; asset = assetManager.resolve(assets_1.Asset.getUrlAsset(assetManager, current.url), current.isBinary ? 'binary' : 'string').runAsChild(ctx); promises.push(wrapPromise(currentSrc, current.id, asset)); promiseKeys.push(currentSrc); currentSrc++; } return [3 /*break*/, 2]; case 6: return [2 /*return*/, slots]; } }); }); } exports.ajaxGetMany = ajaxGetMany; function _filterRemoveIndex(_, i) { return this !== i; } function wrapPromise(index, id, p) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var result, error_1; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, p]; case 1: result = _a.sent(); return [2 /*return*/, { kind: 'ok', result: result, index: index, id: id }]; case 2: error_1 = _a.sent(); return [2 /*return*/, { kind: 'error', error: error_1, index: index, id: id }]; case 3: return [2 /*return*/]; } }); }); } //# sourceMappingURL=data-source.js.map