molstar
Version:
A comprehensive macromolecular library.
389 lines • 17.2 kB
JavaScript
"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