taxonium-component
Version:
React component for exploring large phylogenetic trees in the browser
316 lines (315 loc) • 10.1 kB
JavaScript
import { L as x } from "./browser-BpRiKmO-.js";
import { s as y, a1 as k } from "./JBrowsePanel-BNE3gNW1.js";
import { B as I } from "./index-CpJXUZUB.js";
import { r as z } from "./rxjs-L4bS73F7.js";
import { S as _ } from "./simpleFeature-DimH8SQV.js";
const S = BigInt(32);
function U(r, e, n) {
const t = +!!n, o = +!n;
return BigInt(r.getInt32(e, n) * o + r.getInt32(e + 4, n) * t) << S | BigInt(r.getUint32(e, n) * t + r.getUint32(e + 4, n) * o);
}
function q(r, e, n) {
const t = r.getUint32(e, n), o = r.getUint32(e + 4, n), s = +!!n, i = +!n;
return BigInt(t * i + o * s) << S | BigInt(t * s + o * i);
}
"getBigInt64" in DataView || (DataView.prototype.getBigInt64 = function(r, e) {
return U(this, r, e);
});
"getBigUint64" in DataView || (DataView.prototype.getBigUint64 = function(r, e) {
return q(this, r, e);
});
const P = 440477507, g = ["T", "C", "A", "G"], m = [];
for (let r = 0; r < 256; r++)
m.push(g[r >> 6 & 3] + g[r >> 4 & 3] + g[r >> 2 & 3] + g[r & 3]);
const O = m.map((r) => r.toLowerCase());
class C {
/**
* @param {object} args
* @param {string} [args.path] filesystem path for the .2bit file to open
* @param {Filehandle} [args.filehandle] node fs.promises-like filehandle for the .2bit file.
* Only needs to support `filehandle.read(buffer, offset, length, position)`
*/
constructor({ filehandle: e, path: n }) {
if (e)
this.filehandle = e;
else if (n)
this.filehandle = new x(n);
else
throw new Error("must supply path or filehandle");
}
async _detectEndianness() {
const e = await this.filehandle.read(8, 0), n = new DataView(e.buffer);
if (n.getInt32(0, !0) === P)
this.version = n.getInt32(0, !0);
else
throw new Error("not a 2bit file");
}
getHeader() {
return this.headerP || (this.headerP = this._getHeader().catch((e) => {
throw this.headerP = void 0, e;
})), this.headerP;
}
async _getHeader() {
await this._detectEndianness();
const e = await this.filehandle.read(16, 0), n = !0, t = new DataView(e.buffer, e.byteOffset, e.length);
let o = 0;
const s = t.getInt32(o, n);
if (o += 4, s !== 440477507)
throw new Error(`Wrong magic number ${s}`);
const i = t.getInt32(o, n);
o += 4;
const c = t.getUint32(o, n);
o += 4;
const a = t.getUint32(o, n);
return {
version: i,
magic: s,
sequenceCount: c,
reserved: a
};
}
getIndex() {
return this.indexP || (this.indexP = this._getIndex().catch((e) => {
throw this.indexP = void 0, e;
})), this.indexP;
}
async _getIndex() {
const e = await this.getHeader(), n = 8 + e.sequenceCount * (257 + (this.version === 1 ? 8 : 4)), t = await this.filehandle.read(n, 8), o = !0, s = new DataView(t.buffer, t.byteOffset, t.length);
let i = 0;
const c = s.getUint32(i, o);
i += 4, i += 4;
const a = [], h = new TextDecoder("utf8");
for (let l = 0; l < c; l++) {
const u = s.getUint8(i);
i += 1;
const f = h.decode(t.subarray(i, i + u));
if (i += u, e.version === 1) {
const d = Number(s.getBigUint64(i, o));
i += 8, a.push({ offset: d, name: f });
} else {
const d = s.getUint32(i, o);
i += 4, a.push({ offset: d, name: f });
}
}
return Object.fromEntries(a.map(({ name: l, offset: u }) => [l, u]));
}
/**
* @returns for an array of string sequence names that are found in the file
*/
async getSequenceNames() {
const e = await this.getIndex();
return Object.keys(e);
}
/**
* @returns object listing the lengths of all sequences like `{seqName:
* length, ...}`.
*
* note: this is a relatively slow operation especially if there are many
* refseqs in the file, if you can get this information from a different file
* e.g. a chrom.sizes file, it will be much faster
*/
async getSequenceSizes() {
const e = await this.getIndex(), n = Object.keys(e), t = await Promise.all(Object.values(e).map((s) => this._getSequenceSize(s))), o = {};
for (const [s, i] of n.entries())
o[i] = t[s];
return o;
}
/**
* @param seqName name of the sequence
*
* @returns sequence length, or undefined if it is not in the file
*/
async getSequenceSize(e) {
const t = (await this.getIndex())[e];
return t ? this._getSequenceSize(t) : void 0;
}
async _getSequenceSize(e) {
return this._record1(e).then((n) => n.dnaSize);
}
async _record1(e, n = 8) {
const t = await this.filehandle.read(n, e), o = !0;
let s = 0;
const i = new DataView(t.buffer, t.byteOffset, t.length), c = i.getUint32(s, o);
s += 4;
const a = i.getUint32(s, o);
return s += 4, { dnaSize: c, nBlockCount: a };
}
async _record2(e, n) {
const t = await this.filehandle.read(n, e), o = !0;
let s = 0;
const i = new DataView(t.buffer, t.byteOffset, t.length), c = i.getUint32(s, o);
s += 4;
const a = [];
for (let u = 0; u < c; u++) {
const f = i.getUint32(s, o);
s += 4, a.push(f);
}
const h = [];
for (let u = 0; u < c; u++) {
const f = i.getUint32(s, o);
s += 4, h.push(f);
}
return {
maskBlockCount: i.getUint32(s, o),
nBlockSizes: h,
nBlockStarts: a
};
}
async _record3(e, n) {
const t = await this.filehandle.read(n, e), o = !0;
let s = 0;
const i = new DataView(t.buffer, t.byteOffset, t.length), c = i.getUint32(s, o);
s += 4;
const a = [];
for (let u = 0; u < c; u++) {
const f = i.getUint32(s, o);
s += 4, a.push(f);
}
const h = [];
for (let u = 0; u < c; u++) {
const f = i.getUint32(s, o);
s += 4, h.push(f);
}
const l = i.getInt32(s, o);
return {
maskBlockCount: c,
maskBlockSizes: h,
maskBlockStarts: a,
reserved: l
};
}
async _getSequenceRecord(e) {
const n = await this._record1(e), t = n.nBlockCount * 8 + 8, o = await this._record2(e + 4, t), s = o.maskBlockCount * 8 + 8, i = await this._record3(e + 4 + t - 4, s);
return {
dnaSize: n.dnaSize,
nBlocks: {
starts: o.nBlockStarts,
sizes: o.nBlockSizes
},
maskBlocks: {
starts: i.maskBlockStarts,
sizes: i.maskBlockSizes
},
dnaPosition: e + 4 + t - 4 + s
};
}
/**
* @param seqName name of the sequence you want
*
* @param [regionStart] optional 0-based half-open start of the sequence
* region to fetch.
*
* @param [regionEnd] optional 0-based half-open end of the sequence region
* to fetch. defaults to end of the sequence
*
* @returns for a string of sequence bases
*/
async getSequence(e, n = 0, t = Number.POSITIVE_INFINITY) {
const s = (await this.getIndex())[e];
if (!s)
return;
const i = await this._getSequenceRecord(s);
if (n < 0)
throw new TypeError("regionStart cannot be less than 0");
t > i.dnaSize && (t = i.dnaSize);
const c = this._getOverlappingBlocks(n, t, i.nBlocks.starts, i.nBlocks.sizes), a = this._getOverlappingBlocks(n, t, i.maskBlocks.starts, i.maskBlocks.sizes), h = Math.ceil((t - n) / 4) + 1, l = Math.floor(n / 4), u = await this.filehandle.read(h, i.dnaPosition + l);
let f = "";
for (let d = n; d < t; d += 1) {
for (; a.length > 0 && a[0].end <= d; )
a.shift();
const p = a[0] && a[0].start <= d && a[0].end > d;
if (c[0] && d >= c[0].start && d < c[0].end) {
const w = c.shift();
for (; d < w.end && d < t; d += 1)
f += p ? "n" : "N";
d -= 1;
} else {
const w = Math.floor(d / 4) - l, B = d % 4, b = u[w];
f += p ? O[b][B] : m[b][B];
}
}
return f;
}
_getOverlappingBlocks(e, n, t, o) {
let s, i;
for (const [a, h] of t.entries()) {
const l = o[a];
if (e >= h + l || n <= h) {
if (s !== void 0) {
i = a;
break;
}
} else s === void 0 && (s = a);
}
if (s === void 0)
return [];
i === void 0 && (i = t.length);
const c = new Array(i - s);
for (let a = s; a < i; a += 1)
c[a - s] = {
start: t[a],
end: t[a] + o[a],
size: o[a]
};
return c;
}
}
class N extends I.BaseSequenceAdapter {
async initChromSizes() {
const e = y.readConfObject(this.config, "chromSizesLocation");
if (e.uri !== "/path/to/default.chrom.sizes" && e.uri !== "") {
const t = await k.openLocation(e, this.pluginManager).readFile("utf8");
return Object.fromEntries(t.split(/\n|\r\n|\r/).filter((o) => !!o.trim()).map((o) => {
const [s, i] = o.split(" ");
return [s, +i];
}));
}
}
async setupPre() {
return {
twobit: new C({
filehandle: k.openLocation(this.getConf("twoBitLocation"), this.pluginManager)
}),
chromSizesData: await this.initChromSizes()
};
}
async setup() {
return this.setupP || (this.setupP = this.setupPre().catch((e) => {
throw this.setupP = void 0, e;
})), this.setupP;
}
async getRefNames() {
const { chromSizesData: e, twobit: n } = await this.setup();
return e ? Object.keys(e) : n.getSequenceNames();
}
async getRegions() {
const { chromSizesData: e, twobit: n } = await this.setup();
if (e)
return Object.keys(e).map((t) => ({
refName: t,
start: 0,
end: e[t]
}));
{
const t = await n.getSequenceSizes();
return Object.keys(t).map((o) => ({
refName: o,
start: 0,
end: t[o]
}));
}
}
getFeatures({ refName: e, start: n, end: t }) {
return z.ObservableCreate(async (o) => {
const { twobit: s } = await this.setup(), i = await s.getSequenceSize(e), c = i !== void 0 ? Math.min(i, t) : t, a = await s.getSequence(e, n, c);
a && o.next(new _({
id: `${e} ${n}-${c}`,
data: { refName: e, start: n, end: c, seq: a }
})), o.complete();
});
}
}
export {
N as default
};
//# sourceMappingURL=TwoBitAdapter-BSeVsbr5.js.map