igv 3.1.3 → 3.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/igv.esm.js +698 -711
- package/dist/igv.esm.min.js +11 -11
- package/dist/igv.esm.min.js.map +1 -1
- package/dist/igv.js +698 -711
- package/dist/igv.min.js +11 -11
- package/dist/igv.min.js.map +1 -1
- package/package.json +1 -1
package/dist/igv.js
CHANGED
|
@@ -19437,7 +19437,7 @@
|
|
|
19437
19437
|
ctx.closePath();
|
|
19438
19438
|
}
|
|
19439
19439
|
|
|
19440
|
-
/*! @license DOMPurify 3.2.
|
|
19440
|
+
/*! @license DOMPurify 3.2.4 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.4/LICENSE */
|
|
19441
19441
|
|
|
19442
19442
|
const {
|
|
19443
19443
|
entries,
|
|
@@ -19476,8 +19476,10 @@
|
|
|
19476
19476
|
};
|
|
19477
19477
|
}
|
|
19478
19478
|
const arrayForEach = unapply(Array.prototype.forEach);
|
|
19479
|
+
const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
|
|
19479
19480
|
const arrayPop = unapply(Array.prototype.pop);
|
|
19480
19481
|
const arrayPush = unapply(Array.prototype.push);
|
|
19482
|
+
const arraySplice = unapply(Array.prototype.splice);
|
|
19481
19483
|
const stringToLowerCase = unapply(String.prototype.toLowerCase);
|
|
19482
19484
|
const stringToString = unapply(String.prototype.toString);
|
|
19483
19485
|
const stringMatch = unapply(String.prototype.match);
|
|
@@ -19633,7 +19635,7 @@
|
|
|
19633
19635
|
// eslint-disable-next-line unicorn/better-regex
|
|
19634
19636
|
const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
|
19635
19637
|
const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
|
19636
|
-
const TMPLIT_EXPR = seal(/\$\{[\w\W]
|
|
19638
|
+
const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
|
|
19637
19639
|
const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
|
|
19638
19640
|
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
19639
19641
|
const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
@@ -19733,9 +19735,9 @@
|
|
|
19733
19735
|
function createDOMPurify() {
|
|
19734
19736
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
19735
19737
|
const DOMPurify = root => createDOMPurify(root);
|
|
19736
|
-
DOMPurify.version = '3.2.
|
|
19738
|
+
DOMPurify.version = '3.2.4';
|
|
19737
19739
|
DOMPurify.removed = [];
|
|
19738
|
-
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
|
|
19740
|
+
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
19739
19741
|
// Not running in a browser, provide a factory function
|
|
19740
19742
|
// so that you can pass your own Window
|
|
19741
19743
|
DOMPurify.isSupported = false;
|
|
@@ -20754,7 +20756,11 @@
|
|
|
20754
20756
|
}
|
|
20755
20757
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
20756
20758
|
};
|
|
20757
|
-
DOMPurify.removeHook = function (entryPoint) {
|
|
20759
|
+
DOMPurify.removeHook = function (entryPoint, hookFunction) {
|
|
20760
|
+
if (hookFunction !== undefined) {
|
|
20761
|
+
const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
|
|
20762
|
+
return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
|
|
20763
|
+
}
|
|
20758
20764
|
return arrayPop(hooks[entryPoint]);
|
|
20759
20765
|
};
|
|
20760
20766
|
DOMPurify.removeHooks = function (entryPoint) {
|
|
@@ -24670,6 +24676,533 @@
|
|
|
24670
24676
|
return value
|
|
24671
24677
|
}
|
|
24672
24678
|
|
|
24679
|
+
/**
|
|
24680
|
+
* Some interpretations of the sequence ontology needed to assemble GFF transcripts.
|
|
24681
|
+
*
|
|
24682
|
+
*/
|
|
24683
|
+
|
|
24684
|
+
const transcriptTypes = new Set(['transcript', 'primary_transcript', 'processed_transcript', 'mRNA', 'mrna',
|
|
24685
|
+
'lnc_RNA', 'miRNA', 'ncRNA', 'rRNA', 'scRNA', 'snRNA', 'snoRNA', 'tRNA']);
|
|
24686
|
+
const cdsTypes = new Set(['CDS', 'cds']);
|
|
24687
|
+
const codonTypes = new Set(['start_codon', 'stop_codon']);
|
|
24688
|
+
const utrTypes = new Set(['5UTR', '3UTR', 'UTR', 'five_prime_UTR', 'three_prime_UTR', "3'-UTR", "5'-UTR"]);
|
|
24689
|
+
const exonTypes = new Set(['exon', 'coding-exon']);
|
|
24690
|
+
|
|
24691
|
+
const transcriptPartTypes = new Set();
|
|
24692
|
+
for (let cltn of [cdsTypes, codonTypes, utrTypes, exonTypes]) {
|
|
24693
|
+
for (let t of cltn) {
|
|
24694
|
+
transcriptPartTypes.add(t);
|
|
24695
|
+
}
|
|
24696
|
+
}
|
|
24697
|
+
|
|
24698
|
+
function isExon(type) {
|
|
24699
|
+
return exonTypes.has(type)
|
|
24700
|
+
}
|
|
24701
|
+
|
|
24702
|
+
function isIntron(type) {
|
|
24703
|
+
return type.includes("intron")
|
|
24704
|
+
}
|
|
24705
|
+
|
|
24706
|
+
function isCoding(type) {
|
|
24707
|
+
return cdsTypes.has(type) || codonTypes.has(type)
|
|
24708
|
+
}
|
|
24709
|
+
|
|
24710
|
+
function isUTR(type) {
|
|
24711
|
+
return utrTypes.has(type)
|
|
24712
|
+
}
|
|
24713
|
+
|
|
24714
|
+
function isTranscript(type) {
|
|
24715
|
+
return transcriptTypes.has(type) || type.endsWith("RNA") || type.endsWith("transcript")
|
|
24716
|
+
}
|
|
24717
|
+
|
|
24718
|
+
function isTranscriptPart(type) {
|
|
24719
|
+
return transcriptPartTypes.has(type) || type.endsWith("RNA") || isIntron(type)
|
|
24720
|
+
}
|
|
24721
|
+
|
|
24722
|
+
const filterPopupProperties = new Set(["id", "parent", "name"]);
|
|
24723
|
+
|
|
24724
|
+
class GFFFeature {
|
|
24725
|
+
|
|
24726
|
+
constructor(properties) {
|
|
24727
|
+
|
|
24728
|
+
Object.assign(this, properties);
|
|
24729
|
+
|
|
24730
|
+
if (properties.phase !== undefined && "." !== properties.phase) {
|
|
24731
|
+
this.readingFrame = (3 - parseInt(properties.phase)) % 3;
|
|
24732
|
+
}
|
|
24733
|
+
|
|
24734
|
+
}
|
|
24735
|
+
|
|
24736
|
+
|
|
24737
|
+
popupData(genomicLocation) {
|
|
24738
|
+
|
|
24739
|
+
const pd = this.geneObject ? this.geneObject.popupData() : [];
|
|
24740
|
+
|
|
24741
|
+
if (this.geneObject) {
|
|
24742
|
+
pd.push('<hr/>');
|
|
24743
|
+
}
|
|
24744
|
+
|
|
24745
|
+
if (this.name) {
|
|
24746
|
+
pd.push({name: 'Name', value: this.name});
|
|
24747
|
+
}
|
|
24748
|
+
|
|
24749
|
+
pd.push({name: 'Type', value: this.type});
|
|
24750
|
+
pd.push({name: 'Source', value: this.source});
|
|
24751
|
+
if (this.score !== undefined) {
|
|
24752
|
+
pd.push({name: 'Score', value: this.score});
|
|
24753
|
+
}
|
|
24754
|
+
pd.push({name: 'Phase', value: this.phase});
|
|
24755
|
+
|
|
24756
|
+
if (this.attributeString) {
|
|
24757
|
+
const atts = parseAttributeString(this.attributeString, this.delim);
|
|
24758
|
+
for (let [key, value] of atts) {
|
|
24759
|
+
if (value !== undefined && value.length > 0 && !filterPopupProperties.has(key.toLowerCase())) {
|
|
24760
|
+
pd.push({name: key + ":", value: value});
|
|
24761
|
+
}
|
|
24762
|
+
}
|
|
24763
|
+
}
|
|
24764
|
+
pd.push({
|
|
24765
|
+
name: 'Location',
|
|
24766
|
+
value: `${this.chr}:${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}`
|
|
24767
|
+
});
|
|
24768
|
+
return pd
|
|
24769
|
+
}
|
|
24770
|
+
|
|
24771
|
+
getAttributeValue(attributeName) {
|
|
24772
|
+
if (this.hasOwnProperty(attributeName)) {
|
|
24773
|
+
return this[attributeName]
|
|
24774
|
+
} else {
|
|
24775
|
+
// TODO -- fetch from attribute string and cache
|
|
24776
|
+
if (!this._attributeCache) {
|
|
24777
|
+
this._attributeCache = new Map();
|
|
24778
|
+
}
|
|
24779
|
+
if (this._attributeCache.has(attributeName)) {
|
|
24780
|
+
return this._attributeCache.get(attributeName)
|
|
24781
|
+
} else {
|
|
24782
|
+
const atts = parseAttributeString(this.attributeString, this.delim);
|
|
24783
|
+
let v;
|
|
24784
|
+
for (let [key, value] of atts) {
|
|
24785
|
+
if (key === attributeName) {
|
|
24786
|
+
v = value;
|
|
24787
|
+
break
|
|
24788
|
+
}
|
|
24789
|
+
}
|
|
24790
|
+
this._attributeCache.set(attributeName, v);
|
|
24791
|
+
return v
|
|
24792
|
+
}
|
|
24793
|
+
}
|
|
24794
|
+
}
|
|
24795
|
+
}
|
|
24796
|
+
|
|
24797
|
+
class GFFTranscript extends GFFFeature {
|
|
24798
|
+
|
|
24799
|
+
constructor(feature) {
|
|
24800
|
+
super(feature);
|
|
24801
|
+
this.exons = [];
|
|
24802
|
+
this.parts = [];
|
|
24803
|
+
}
|
|
24804
|
+
|
|
24805
|
+
addExon(feature) {
|
|
24806
|
+
|
|
24807
|
+
this.exons.push(feature);
|
|
24808
|
+
|
|
24809
|
+
// Expand feature -- for transcripts not explicitly represented in the file (gtf)
|
|
24810
|
+
this.start = Math.min(this.start, feature.start);
|
|
24811
|
+
this.end = Math.max(this.end, feature.end);
|
|
24812
|
+
}
|
|
24813
|
+
|
|
24814
|
+
addPart(feature) {
|
|
24815
|
+
this.parts.push(feature);
|
|
24816
|
+
}
|
|
24817
|
+
|
|
24818
|
+
assembleParts() {
|
|
24819
|
+
|
|
24820
|
+
if (this.parts.length === 0) return
|
|
24821
|
+
|
|
24822
|
+
this.parts.sort(function (a, b) {
|
|
24823
|
+
return a.start - b.start
|
|
24824
|
+
});
|
|
24825
|
+
|
|
24826
|
+
// Create exons, if neccessary
|
|
24827
|
+
let lastStart = this.parts[0].start;
|
|
24828
|
+
let lastEnd = this.parts[0].end;
|
|
24829
|
+
for (let i = 1; i < this.parts.length; i++) {
|
|
24830
|
+
const part = this.parts[i];
|
|
24831
|
+
if (isIntron(part.type)) {
|
|
24832
|
+
continue
|
|
24833
|
+
}
|
|
24834
|
+
if (part.start <= lastEnd) {
|
|
24835
|
+
lastEnd = Math.max(lastEnd, part.end);
|
|
24836
|
+
} else {
|
|
24837
|
+
let exon = this.findExonContaining({start: lastStart, end: lastEnd});
|
|
24838
|
+
if (!exon) {
|
|
24839
|
+
this.exons.push({start: lastStart, end: lastEnd, psuedo: true});
|
|
24840
|
+
}
|
|
24841
|
+
lastStart = part.start;
|
|
24842
|
+
lastEnd = part.end;
|
|
24843
|
+
}
|
|
24844
|
+
}
|
|
24845
|
+
let exon = this.findExonContaining({start: lastStart, end: lastEnd});
|
|
24846
|
+
if (!exon) {
|
|
24847
|
+
this.exons.push({start: lastStart, end: lastEnd, psuedo: true});
|
|
24848
|
+
this.start = Math.min(this.start, lastStart);
|
|
24849
|
+
this.end = Math.max(this.end, lastEnd);
|
|
24850
|
+
}
|
|
24851
|
+
|
|
24852
|
+
|
|
24853
|
+
for (let part of this.parts) {
|
|
24854
|
+
const type = part.type;
|
|
24855
|
+
if (isCoding(type)) {
|
|
24856
|
+
this.addCDS(part);
|
|
24857
|
+
} else if (isUTR(type)) {
|
|
24858
|
+
this.addUTR(part);
|
|
24859
|
+
}
|
|
24860
|
+
}
|
|
24861
|
+
}
|
|
24862
|
+
|
|
24863
|
+
findExonContaining({start, end}) {
|
|
24864
|
+
for (let exon of this.exons) {
|
|
24865
|
+
if (exon.end >= end && exon.start <= start) {
|
|
24866
|
+
return exon
|
|
24867
|
+
}
|
|
24868
|
+
}
|
|
24869
|
+
return undefined
|
|
24870
|
+
}
|
|
24871
|
+
|
|
24872
|
+
addCDS(cds) {
|
|
24873
|
+
|
|
24874
|
+
let exon;
|
|
24875
|
+
const exons = this.exons;
|
|
24876
|
+
|
|
24877
|
+
for (let e of exons) {
|
|
24878
|
+
if (e.start <= cds.start && e.end >= cds.end) {
|
|
24879
|
+
exon = e;
|
|
24880
|
+
break
|
|
24881
|
+
}
|
|
24882
|
+
}
|
|
24883
|
+
|
|
24884
|
+
if (exon) {
|
|
24885
|
+
exon.cdStart = exon.cdStart ? Math.min(cds.start, exon.cdStart) : cds.start;
|
|
24886
|
+
exon.cdEnd = exon.cdEnd ? Math.max(cds.end, exon.cdEnd) : cds.end;
|
|
24887
|
+
exon.readingFrame = cds.readingFrame;
|
|
24888
|
+
// TODO -- merge attributes?
|
|
24889
|
+
} else {
|
|
24890
|
+
// cds.cdStart = cds.start
|
|
24891
|
+
// cds.cdEnd = cds.end
|
|
24892
|
+
// exons.push(cds)
|
|
24893
|
+
console.error("No exon found spanning " + cds.start + "-" + cds.end);
|
|
24894
|
+
}
|
|
24895
|
+
|
|
24896
|
+
// Expand feature -- for transcripts not explicitly represented in the file (gtf files)
|
|
24897
|
+
// this.start = Math.min(this.start, cds.start);
|
|
24898
|
+
// this.end = Math.max(this.end, cds.end);
|
|
24899
|
+
|
|
24900
|
+
this.cdStart = this.cdStart ? Math.min(cds.start, this.cdStart) : cds.start;
|
|
24901
|
+
this.cdEnd = this.cdEnd ? Math.max(cds.end, this.cdEnd) : cds.end;
|
|
24902
|
+
}
|
|
24903
|
+
|
|
24904
|
+
addUTR(utr) {
|
|
24905
|
+
|
|
24906
|
+
let exon;
|
|
24907
|
+
const exons = this.exons;
|
|
24908
|
+
|
|
24909
|
+
// Find exon containing CDS
|
|
24910
|
+
for (let i = 0; i < exons.length; i++) {
|
|
24911
|
+
if (exons[i].start <= utr.start && exons[i].end >= utr.end) {
|
|
24912
|
+
exon = exons[i];
|
|
24913
|
+
break
|
|
24914
|
+
}
|
|
24915
|
+
}
|
|
24916
|
+
|
|
24917
|
+
if (exon) {
|
|
24918
|
+
if (utr.start === exon.start && utr.end === exon.end) {
|
|
24919
|
+
exon.utr = true;
|
|
24920
|
+
} else {
|
|
24921
|
+
if (utr.end < exon.end) {
|
|
24922
|
+
exon.cdStart = utr.end;
|
|
24923
|
+
}
|
|
24924
|
+
if (utr.start > exon.start) {
|
|
24925
|
+
exon.cdEnd = utr.start;
|
|
24926
|
+
}
|
|
24927
|
+
}
|
|
24928
|
+
|
|
24929
|
+
} else {
|
|
24930
|
+
// utr.utr = true
|
|
24931
|
+
// exons.push(utr)
|
|
24932
|
+
console.error("No exon found spanning " + cds.start + "-" + cds.end);
|
|
24933
|
+
}
|
|
24934
|
+
|
|
24935
|
+
// Expand feature -- for transcripts not explicitly represented in the file
|
|
24936
|
+
// this.start = Math.min(this.start, utr.start);
|
|
24937
|
+
// this.end = Math.max(this.end, utr.end);
|
|
24938
|
+
|
|
24939
|
+
}
|
|
24940
|
+
|
|
24941
|
+
finish() {
|
|
24942
|
+
|
|
24943
|
+
this.assembleParts();
|
|
24944
|
+
|
|
24945
|
+
var cdStart = this.cdStart;
|
|
24946
|
+
var cdEnd = this.cdEnd;
|
|
24947
|
+
|
|
24948
|
+
this.exons.sort(function (a, b) {
|
|
24949
|
+
return a.start - b.start
|
|
24950
|
+
});
|
|
24951
|
+
|
|
24952
|
+
// Search for UTR exons that were not explicitly tagged
|
|
24953
|
+
if (cdStart) {
|
|
24954
|
+
this.exons.forEach(function (exon) {
|
|
24955
|
+
if (exon.end < cdStart || exon.start > cdEnd) exon.utr = true;
|
|
24956
|
+
});
|
|
24957
|
+
}
|
|
24958
|
+
}
|
|
24959
|
+
|
|
24960
|
+
popupData(genomicLocation) {
|
|
24961
|
+
|
|
24962
|
+
const pd = super.popupData(genomicLocation);
|
|
24963
|
+
|
|
24964
|
+
// If clicked over an exon add its attributes
|
|
24965
|
+
for (let exon of this.exons) {
|
|
24966
|
+
if (exon.pseudo) continue // An implicit exon
|
|
24967
|
+
if (genomicLocation >= exon.start && genomicLocation < exon.end && typeof exon.popupData === 'function') {
|
|
24968
|
+
pd.push('<hr/>');
|
|
24969
|
+
const exonData = exon.popupData(genomicLocation);
|
|
24970
|
+
for (let att of exonData) {
|
|
24971
|
+
pd.push(att);
|
|
24972
|
+
}
|
|
24973
|
+
}
|
|
24974
|
+
}
|
|
24975
|
+
|
|
24976
|
+
for (let part of this.parts) {
|
|
24977
|
+
if (genomicLocation >= part.start && genomicLocation < part.end && typeof part.popupData === 'function') {
|
|
24978
|
+
pd.push('<hr/>');
|
|
24979
|
+
const partData = part.popupData(genomicLocation);
|
|
24980
|
+
for (let att of partData) {
|
|
24981
|
+
pd.push(att);
|
|
24982
|
+
}
|
|
24983
|
+
}
|
|
24984
|
+
}
|
|
24985
|
+
|
|
24986
|
+
|
|
24987
|
+
return pd
|
|
24988
|
+
}
|
|
24989
|
+
}
|
|
24990
|
+
|
|
24991
|
+
class GFFHelper {
|
|
24992
|
+
|
|
24993
|
+
static gffNameFields = new Set(["Name", "transcript_id", "gene_name", "gene", "gene_id", "alias", "locus", "name" ])
|
|
24994
|
+
constructor(options) {
|
|
24995
|
+
this.format = options.format;
|
|
24996
|
+
this.nameField = options.nameField;
|
|
24997
|
+
this.filterTypes = options.filterTypes === undefined ?
|
|
24998
|
+
new Set(['chromosome']) :
|
|
24999
|
+
new Set(options.filterTypes);
|
|
25000
|
+
}
|
|
25001
|
+
|
|
25002
|
+
combineFeatures(features, genomicInterval) {
|
|
25003
|
+
|
|
25004
|
+
let combinedFeatures;
|
|
25005
|
+
|
|
25006
|
+
const filterTypes = this.filterTypes;
|
|
25007
|
+
features = features.filter(f => filterTypes === undefined || !filterTypes.has(f.type));
|
|
25008
|
+
|
|
25009
|
+
if ("gff3" === this.format) {
|
|
25010
|
+
const tmp = this.combineFeaturesById(features);
|
|
25011
|
+
combinedFeatures = this.combineFeaturesByType(tmp);
|
|
25012
|
+
} else {
|
|
25013
|
+
combinedFeatures = this.combineFeaturesByType(features);
|
|
25014
|
+
}
|
|
25015
|
+
|
|
25016
|
+
this.numberExons(combinedFeatures, genomicInterval);
|
|
25017
|
+
this.nameFeatures(combinedFeatures);
|
|
25018
|
+
return combinedFeatures
|
|
25019
|
+
}
|
|
25020
|
+
|
|
25021
|
+
/**
|
|
25022
|
+
* Combine multiple non-transcript model features with the same ID on the same chromosome into a single feature.
|
|
25023
|
+
* Features that are part of the transcript model (e.g. exon, mRNA, etc) are combined later.
|
|
25024
|
+
*
|
|
25025
|
+
* @param features
|
|
25026
|
+
* @returns {[]}
|
|
25027
|
+
*/
|
|
25028
|
+
combineFeaturesById(features) {
|
|
25029
|
+
|
|
25030
|
+
const chrIdMap = new Map();
|
|
25031
|
+
const combinedFeatures = [];
|
|
25032
|
+
|
|
25033
|
+
for (let f of features) {
|
|
25034
|
+
if (isTranscriptPart(f.type) || isTranscript(f.type) || !f.id) {
|
|
25035
|
+
combinedFeatures.push(f);
|
|
25036
|
+
} else {
|
|
25037
|
+
let idMap = chrIdMap.get(f.chr);
|
|
25038
|
+
if (!idMap) {
|
|
25039
|
+
idMap = new Map();
|
|
25040
|
+
chrIdMap.set(f.chr, idMap);
|
|
25041
|
+
}
|
|
25042
|
+
|
|
25043
|
+
let featureArray = idMap.get(f.id);
|
|
25044
|
+
if (featureArray) {
|
|
25045
|
+
featureArray.push(f);
|
|
25046
|
+
} else {
|
|
25047
|
+
idMap.set(f.id, [f]);
|
|
25048
|
+
}
|
|
25049
|
+
}
|
|
25050
|
+
}
|
|
25051
|
+
|
|
25052
|
+
for (let idMap of chrIdMap.values()) {
|
|
25053
|
+
for (let featureArray of idMap.values()) {
|
|
25054
|
+
if (featureArray.length > 1) {
|
|
25055
|
+
// Use the first feature as prototypical (for column 9 attributes), and adjust start/end
|
|
25056
|
+
// Parts are represented as "exons", as that is how they are presented visually
|
|
25057
|
+
const cf = featureArray[0];
|
|
25058
|
+
cf.exons = [];
|
|
25059
|
+
for (let f of featureArray) {
|
|
25060
|
+
cf.start = Math.min(cf.start, f.start);
|
|
25061
|
+
cf.end = Math.max(cf.end, f.end);
|
|
25062
|
+
cf.exons.push({
|
|
25063
|
+
start: f.start,
|
|
25064
|
+
end: f.end
|
|
25065
|
+
});
|
|
25066
|
+
}
|
|
25067
|
+
combinedFeatures.push(cf);
|
|
25068
|
+
} else {
|
|
25069
|
+
combinedFeatures.push(featureArray[0]);
|
|
25070
|
+
}
|
|
25071
|
+
}
|
|
25072
|
+
}
|
|
25073
|
+
|
|
25074
|
+
return combinedFeatures
|
|
25075
|
+
}
|
|
25076
|
+
|
|
25077
|
+
combineFeaturesByType(features) {
|
|
25078
|
+
|
|
25079
|
+
// Build dictionary of genes
|
|
25080
|
+
const genes = features.filter(f => "gene" === f.type || f.type.endsWith("_gene"));
|
|
25081
|
+
const geneMap = Object.create(null);
|
|
25082
|
+
for (let g of genes) {
|
|
25083
|
+
geneMap[g.id] = g;
|
|
25084
|
+
}
|
|
25085
|
+
|
|
25086
|
+
// 1. Build dictionary of transcripts
|
|
25087
|
+
const transcripts = Object.create(null);
|
|
25088
|
+
const combinedFeatures = [];
|
|
25089
|
+
const consumedFeatures = new Set();
|
|
25090
|
+
const filterTypes = this.filterTypes;
|
|
25091
|
+
|
|
25092
|
+
features = features.filter(f => filterTypes === undefined || !filterTypes.has(f.type));
|
|
25093
|
+
|
|
25094
|
+
for (let f of features) {
|
|
25095
|
+
if (isTranscript(f.type)) {
|
|
25096
|
+
const transcriptId = f.id; // getAttribute(f.attributeString, "transcript_id", /\s+/);
|
|
25097
|
+
if (undefined !== transcriptId) {
|
|
25098
|
+
const gffTranscript = new GFFTranscript(f);
|
|
25099
|
+
transcripts[transcriptId] = gffTranscript;
|
|
25100
|
+
combinedFeatures.push(gffTranscript);
|
|
25101
|
+
consumedFeatures.add(f);
|
|
25102
|
+
const g = geneMap[f.parent];
|
|
25103
|
+
if (g) {
|
|
25104
|
+
gffTranscript.geneObject = g;
|
|
25105
|
+
consumedFeatures.add(g);
|
|
25106
|
+
}
|
|
25107
|
+
}
|
|
25108
|
+
}
|
|
25109
|
+
}
|
|
25110
|
+
|
|
25111
|
+
// Add exons and transcript parts
|
|
25112
|
+
for (let f of features) {
|
|
25113
|
+
if (isTranscriptPart(f.type)) {
|
|
25114
|
+
const parents = getParents(f);
|
|
25115
|
+
if (parents) {
|
|
25116
|
+
for (let id of parents) {
|
|
25117
|
+
|
|
25118
|
+
let transcript = transcripts[id];
|
|
25119
|
+
if (!transcript && this.format === "gtf") {
|
|
25120
|
+
// GTF does not require explicit a transcript or mRNA record, start one with this feature.
|
|
25121
|
+
const psuedoTranscript = Object.assign({}, f);
|
|
25122
|
+
psuedoTranscript.type = "transcript";
|
|
25123
|
+
transcript = new GFFTranscript(psuedoTranscript);
|
|
25124
|
+
transcripts[id] = transcript;
|
|
25125
|
+
combinedFeatures.push(transcript);
|
|
25126
|
+
}
|
|
25127
|
+
if (transcript !== undefined) {
|
|
25128
|
+
|
|
25129
|
+
if (isExon(f.type)) {
|
|
25130
|
+
if (parents.length > 1) {
|
|
25131
|
+
// Multiple parents, this is unusual. Make a copy as exon can be modified
|
|
25132
|
+
// differently by CDS, etc, for each parent
|
|
25133
|
+
const e2 = new GFFFeature(f);
|
|
25134
|
+
transcript.addExon(e2);
|
|
25135
|
+
} else {
|
|
25136
|
+
transcript.addExon(f);
|
|
25137
|
+
}
|
|
25138
|
+
} else {
|
|
25139
|
+
transcript.addPart(f);
|
|
25140
|
+
}
|
|
25141
|
+
consumedFeatures.add(f);
|
|
25142
|
+
}
|
|
25143
|
+
}
|
|
25144
|
+
}
|
|
25145
|
+
}
|
|
25146
|
+
}
|
|
25147
|
+
|
|
25148
|
+
// Finish transcripts
|
|
25149
|
+
combinedFeatures.forEach(function (f) {
|
|
25150
|
+
if (typeof f.finish === "function") {
|
|
25151
|
+
f.finish();
|
|
25152
|
+
}
|
|
25153
|
+
});
|
|
25154
|
+
|
|
25155
|
+
// Add other features
|
|
25156
|
+
const others = features.filter(f => !consumedFeatures.has(f));
|
|
25157
|
+
for (let f of others) {
|
|
25158
|
+
combinedFeatures.push(f);
|
|
25159
|
+
}
|
|
25160
|
+
|
|
25161
|
+
return combinedFeatures
|
|
25162
|
+
|
|
25163
|
+
function getParents(f) {
|
|
25164
|
+
if (f.parent && f.parent.trim() !== "") {
|
|
25165
|
+
return f.parent.trim().split(",")
|
|
25166
|
+
} else {
|
|
25167
|
+
return null
|
|
25168
|
+
}
|
|
25169
|
+
}
|
|
25170
|
+
}
|
|
25171
|
+
|
|
25172
|
+
numberExons(features, genomicInterval) {
|
|
25173
|
+
|
|
25174
|
+
for (let f of features) {
|
|
25175
|
+
if (f.exons &&
|
|
25176
|
+
(!genomicInterval ||
|
|
25177
|
+
(f.end <= genomicInterval.end && f.start > genomicInterval.start))) {
|
|
25178
|
+
for (let i = 0; i < f.exons.length; i++) {
|
|
25179
|
+
const exon = f.exons[i];
|
|
25180
|
+
exon.number = f.strand === "-" ? f.exons.length - i : i + 1;
|
|
25181
|
+
}
|
|
25182
|
+
}
|
|
25183
|
+
}
|
|
25184
|
+
}
|
|
25185
|
+
|
|
25186
|
+
nameFeatures(features) {
|
|
25187
|
+
// Find name (label) property
|
|
25188
|
+
for (let f of features) {
|
|
25189
|
+
if(typeof f.getAttributeValue === 'function') {
|
|
25190
|
+
if (this.nameField) {
|
|
25191
|
+
f.name = f.getAttributeValue(this.nameField);
|
|
25192
|
+
} else {
|
|
25193
|
+
for (let nameField of GFFHelper.gffNameFields) {
|
|
25194
|
+
const v = f.getAttributeValue(nameField);
|
|
25195
|
+
if (v) {
|
|
25196
|
+
f.name = v;
|
|
25197
|
+
break
|
|
25198
|
+
}
|
|
25199
|
+
}
|
|
25200
|
+
}
|
|
25201
|
+
}
|
|
25202
|
+
}
|
|
25203
|
+
}
|
|
25204
|
+
}
|
|
25205
|
+
|
|
24673
25206
|
/**
|
|
24674
25207
|
* Decode the UCSC bed format. Only the first 3 columns (chr, start, end) are required. The remaining columns
|
|
24675
25208
|
* must follow standard bed order, but we will tolerate deviations after column 3.
|
|
@@ -24682,7 +25215,7 @@
|
|
|
24682
25215
|
|
|
24683
25216
|
if (tokens.length < 3) return undefined
|
|
24684
25217
|
|
|
24685
|
-
header && header.gffTags;
|
|
25218
|
+
const gffTags = header && header.gffTags;
|
|
24686
25219
|
|
|
24687
25220
|
const chr = tokens[0];
|
|
24688
25221
|
const start = parseInt(tokens[1]);
|
|
@@ -24702,12 +25235,16 @@
|
|
|
24702
25235
|
feature.attributes = {};
|
|
24703
25236
|
for (let kv of attributeKVs) {
|
|
24704
25237
|
feature.attributes[kv[0]] = kv[1];
|
|
24705
|
-
if
|
|
24706
|
-
|
|
25238
|
+
if(gffTags) {
|
|
25239
|
+
if (header.nameField != undefined && kv[0] === header.nameField) {
|
|
25240
|
+
feature.name = kv[1];
|
|
25241
|
+
} else if (!feature.name && GFFHelper.gffNameFields.has(kv[0])) {
|
|
25242
|
+
feature.name = kv[1];
|
|
25243
|
+
}
|
|
24707
25244
|
}
|
|
24708
25245
|
}
|
|
24709
25246
|
}
|
|
24710
|
-
if (!feature.name) {
|
|
25247
|
+
if (!feature.name && !gffTags) {
|
|
24711
25248
|
feature.name = tokens[3] === '.' ? '' : tokens[3];
|
|
24712
25249
|
}
|
|
24713
25250
|
}
|
|
@@ -25125,506 +25662,194 @@
|
|
|
25125
25662
|
let colorColumn = header.colorColumn;
|
|
25126
25663
|
if (colorColumn && colorColumn < tokens.length) {
|
|
25127
25664
|
feature.color = IGVColor.createColorString(tokens[colorColumn]);
|
|
25128
|
-
}
|
|
25129
|
-
}
|
|
25130
|
-
|
|
25131
|
-
return feature
|
|
25132
|
-
}
|
|
25133
|
-
|
|
25134
|
-
function decodeWig(tokens, header) {
|
|
25135
|
-
|
|
25136
|
-
const wig = header.wig;
|
|
25137
|
-
|
|
25138
|
-
if (wig && wig.format === "fixedStep") {
|
|
25139
|
-
const ss = (wig.index * wig.step) + wig.start;
|
|
25140
|
-
const ee = ss + wig.span;
|
|
25141
|
-
const value = Number(tokens[0]);
|
|
25142
|
-
++(wig.index);
|
|
25143
|
-
return isNaN(value) ? null : {chr: wig.chrom, start: ss, end: ee, value: value}
|
|
25144
|
-
} else if (wig && wig.format === "variableStep") {
|
|
25145
|
-
|
|
25146
|
-
if (tokens.length < 2) return null
|
|
25147
|
-
const ss = parseInt(tokens[0], 10) - 1;
|
|
25148
|
-
const ee = ss + wig.span;
|
|
25149
|
-
const value = Number(tokens[1]);
|
|
25150
|
-
return isNaN(value) ? null : {chr: wig.chrom, start: ss, end: ee, value: value}
|
|
25151
|
-
|
|
25152
|
-
} else {
|
|
25153
|
-
return decodeBedGraph(tokens)
|
|
25154
|
-
}
|
|
25155
|
-
}
|
|
25156
|
-
|
|
25157
|
-
function decodeSNP(tokens, header) {
|
|
25158
|
-
|
|
25159
|
-
if (tokens.length < 6) return undefined
|
|
25160
|
-
|
|
25161
|
-
const autoSql = [
|
|
25162
|
-
'bin',
|
|
25163
|
-
'chr',
|
|
25164
|
-
'start',
|
|
25165
|
-
'end',
|
|
25166
|
-
'name',
|
|
25167
|
-
'score',
|
|
25168
|
-
'strand',
|
|
25169
|
-
'refNCBI',
|
|
25170
|
-
'refUCSC',
|
|
25171
|
-
'observed',
|
|
25172
|
-
'molType',
|
|
25173
|
-
'class',
|
|
25174
|
-
'valid',
|
|
25175
|
-
'avHet',
|
|
25176
|
-
'avHetSE',
|
|
25177
|
-
'func',
|
|
25178
|
-
'locType',
|
|
25179
|
-
'weight',
|
|
25180
|
-
'exceptions',
|
|
25181
|
-
'submitterCount',
|
|
25182
|
-
'submitters',
|
|
25183
|
-
'alleleFreqCount',
|
|
25184
|
-
'alleles',
|
|
25185
|
-
'alleleNs',
|
|
25186
|
-
'alleleFreqs',
|
|
25187
|
-
'bitfields'
|
|
25188
|
-
];
|
|
25189
|
-
|
|
25190
|
-
|
|
25191
|
-
const feature = {
|
|
25192
|
-
chr: tokens[1],
|
|
25193
|
-
start: Number.parseInt(tokens[2]),
|
|
25194
|
-
end: Number.parseInt(tokens[3]),
|
|
25195
|
-
name: tokens[4],
|
|
25196
|
-
score: Number.parseInt(tokens[5])
|
|
25197
|
-
};
|
|
25198
|
-
|
|
25199
|
-
const n = Math.min(tokens.length, autoSql.length);
|
|
25200
|
-
for (let i = 6; i < n; i++) {
|
|
25201
|
-
feature[autoSql[i]] = tokens[i];
|
|
25202
|
-
}
|
|
25203
|
-
return feature
|
|
25204
|
-
|
|
25205
|
-
}
|
|
25206
|
-
|
|
25207
|
-
|
|
25208
|
-
class UCSCBedFeature {
|
|
25209
|
-
|
|
25210
|
-
constructor(properties) {
|
|
25211
|
-
Object.assign(this, properties);
|
|
25212
|
-
}
|
|
25213
|
-
|
|
25214
|
-
getAttributeValue(attributeName) {
|
|
25215
|
-
if (this.hasOwnProperty(attributeName)) {
|
|
25216
|
-
return this[attributeName]
|
|
25217
|
-
} else if (this.attributes) {
|
|
25218
|
-
return this.attributes[attributeName]
|
|
25219
|
-
}
|
|
25220
|
-
}
|
|
25221
|
-
}
|
|
25222
|
-
|
|
25223
|
-
/*
|
|
25224
|
-
* 0 matches - Number of bases that match that aren't repeats
|
|
25225
|
-
* 1 misMatches - Number of bases that don't match
|
|
25226
|
-
* 2 repMatches - Number of bases that match but are part of repeats
|
|
25227
|
-
* 3 nCount - Number of "N" bases
|
|
25228
|
-
* 4 qNumInsert - Number of inserts in query
|
|
25229
|
-
* 5 qBaseInsert - Number of bases inserted in query
|
|
25230
|
-
* 6 tNumInsert - Number of inserts in target
|
|
25231
|
-
* 7 tBaseInsert - Number of bases inserted in target
|
|
25232
|
-
* 8 strand - "+" or "-" for query strand. For translated alignments, second "+"or "-" is for target genomic strand.
|
|
25233
|
-
* 9 qName - Query sequence name
|
|
25234
|
-
* 10 qSize - Query sequence size.
|
|
25235
|
-
* 11 qStart - Alignment start position in query
|
|
25236
|
-
* 12 qEnd - Alignment end position in query
|
|
25237
|
-
* 13 tName - Target sequence name
|
|
25238
|
-
* 14 tSize - Target sequence size
|
|
25239
|
-
* 15 tStart - Alignment start position in target
|
|
25240
|
-
* 16 tEnd - Alignment end position in target
|
|
25241
|
-
* 17 blockCount - Number of blocks in the alignment (a block contains no gaps)
|
|
25242
|
-
* 18 blockSizes - Comma-separated list of sizes of each block. If the query is a protein and the target the genome, blockSizes are in amino acids. See below for more information on protein query PSLs.
|
|
25243
|
-
* 19 qStarts - Comma-separated list of starting positions of each block in query
|
|
25244
|
-
* 20 tStarts - Comma-separated list of starting positions of each block in target
|
|
25245
|
-
*/
|
|
25246
|
-
|
|
25247
|
-
class PSLFeature {
|
|
25248
|
-
|
|
25249
|
-
|
|
25250
|
-
constructor(properties) {
|
|
25251
|
-
Object.assign(this, properties);
|
|
25252
|
-
}
|
|
25253
|
-
|
|
25254
|
-
get score() {
|
|
25255
|
-
const tokens = this.tokens;
|
|
25256
|
-
const match = parseInt(tokens[0]);
|
|
25257
|
-
const repMatch = parseInt(tokens[2]);
|
|
25258
|
-
const misMatch = parseInt(tokens[1]);
|
|
25259
|
-
const qGapCount = parseInt(tokens[4]);
|
|
25260
|
-
const tGapCount = parseInt(tokens[6]);
|
|
25261
|
-
const qSize = parseInt(tokens[10]);
|
|
25262
|
-
return Math.floor((1000 * (match + repMatch - misMatch - qGapCount - tGapCount)) / qSize)
|
|
25263
|
-
}
|
|
25264
|
-
|
|
25265
|
-
get matches() {
|
|
25266
|
-
return this.tokens[0]
|
|
25267
|
-
}
|
|
25268
|
-
|
|
25269
|
-
get misMatches() {
|
|
25270
|
-
return this.tokens[1]
|
|
25271
|
-
}
|
|
25272
|
-
|
|
25273
|
-
get repMatches() {
|
|
25274
|
-
return this.tokens[2]
|
|
25275
|
-
}
|
|
25276
|
-
|
|
25277
|
-
get nCount() {
|
|
25278
|
-
return this.tokens[3]
|
|
25279
|
-
}
|
|
25280
|
-
|
|
25281
|
-
get qNumInsert() {
|
|
25282
|
-
return this.tokens[4]
|
|
25283
|
-
}
|
|
25284
|
-
|
|
25285
|
-
get qBaseInsert() {
|
|
25286
|
-
return this.tokens[5]
|
|
25287
|
-
}
|
|
25288
|
-
|
|
25289
|
-
get tNumInsert() {
|
|
25290
|
-
return this.tokens[6]
|
|
25291
|
-
}
|
|
25292
|
-
|
|
25293
|
-
get tBaseInsert() {
|
|
25294
|
-
return this.tokens[7]
|
|
25295
|
-
|
|
25296
|
-
}
|
|
25297
|
-
|
|
25298
|
-
popupData() {
|
|
25299
|
-
return [
|
|
25300
|
-
{name: 'chr', value: this.chr},
|
|
25301
|
-
{name: 'start', value: this.start + 1},
|
|
25302
|
-
{name: 'end', value: this.end},
|
|
25303
|
-
{name: 'strand', value: this.strand},
|
|
25304
|
-
{name: 'score', value: this.score},
|
|
25305
|
-
{name: 'match', value: this.matches},
|
|
25306
|
-
{name: "mis-match", value: this.misMatches},
|
|
25307
|
-
{name: "rep. match", value: this.repMatches},
|
|
25308
|
-
{name: "N's", value: this.nCount},
|
|
25309
|
-
{name: 'Q gap count', value: this.qNumInsert},
|
|
25310
|
-
{name: 'Q gap bases', value: this.qBaseInsert},
|
|
25311
|
-
{name: 'T gap count', value: this.tNumInsert},
|
|
25312
|
-
{name: 'T gap bases', value: this.tBaseInsert},
|
|
25313
|
-
]
|
|
25314
|
-
}
|
|
25315
|
-
|
|
25316
|
-
}
|
|
25317
|
-
|
|
25318
|
-
/**
|
|
25319
|
-
* Some interpretations of the sequence ontology needed to assemble GFF transcripts.
|
|
25320
|
-
*
|
|
25321
|
-
*/
|
|
25322
|
-
|
|
25323
|
-
const transcriptTypes = new Set(['transcript', 'primary_transcript', 'processed_transcript', 'mRNA', 'mrna',
|
|
25324
|
-
'lnc_RNA', 'miRNA', 'ncRNA', 'rRNA', 'scRNA', 'snRNA', 'snoRNA', 'tRNA']);
|
|
25325
|
-
const cdsTypes = new Set(['CDS', 'cds']);
|
|
25326
|
-
const codonTypes = new Set(['start_codon', 'stop_codon']);
|
|
25327
|
-
const utrTypes = new Set(['5UTR', '3UTR', 'UTR', 'five_prime_UTR', 'three_prime_UTR', "3'-UTR", "5'-UTR"]);
|
|
25328
|
-
const exonTypes = new Set(['exon', 'coding-exon']);
|
|
25329
|
-
|
|
25330
|
-
const transcriptPartTypes = new Set();
|
|
25331
|
-
for (let cltn of [cdsTypes, codonTypes, utrTypes, exonTypes]) {
|
|
25332
|
-
for (let t of cltn) {
|
|
25333
|
-
transcriptPartTypes.add(t);
|
|
25334
|
-
}
|
|
25335
|
-
}
|
|
25336
|
-
|
|
25337
|
-
function isExon(type) {
|
|
25338
|
-
return exonTypes.has(type)
|
|
25339
|
-
}
|
|
25340
|
-
|
|
25341
|
-
function isIntron(type) {
|
|
25342
|
-
return type.includes("intron")
|
|
25343
|
-
}
|
|
25344
|
-
|
|
25345
|
-
function isCoding(type) {
|
|
25346
|
-
return cdsTypes.has(type) || codonTypes.has(type)
|
|
25347
|
-
}
|
|
25348
|
-
|
|
25349
|
-
function isUTR(type) {
|
|
25350
|
-
return utrTypes.has(type)
|
|
25351
|
-
}
|
|
25352
|
-
|
|
25353
|
-
function isTranscript(type) {
|
|
25354
|
-
return transcriptTypes.has(type) || type.endsWith("RNA") || type.endsWith("transcript")
|
|
25355
|
-
}
|
|
25356
|
-
|
|
25357
|
-
function isTranscriptPart(type) {
|
|
25358
|
-
return transcriptPartTypes.has(type) || type.endsWith("RNA") || isIntron(type)
|
|
25359
|
-
}
|
|
25360
|
-
|
|
25361
|
-
const filterPopupProperties = new Set(["id", "parent", "name"]);
|
|
25362
|
-
|
|
25363
|
-
class GFFFeature {
|
|
25364
|
-
|
|
25365
|
-
constructor(properties) {
|
|
25366
|
-
|
|
25367
|
-
Object.assign(this, properties);
|
|
25368
|
-
|
|
25369
|
-
if (properties.phase !== undefined && "." !== properties.phase) {
|
|
25370
|
-
this.readingFrame = (3 - parseInt(properties.phase)) % 3;
|
|
25371
|
-
}
|
|
25372
|
-
|
|
25373
|
-
}
|
|
25374
|
-
|
|
25375
|
-
|
|
25376
|
-
popupData(genomicLocation) {
|
|
25377
|
-
|
|
25378
|
-
const pd = this.geneObject ? this.geneObject.popupData() : [];
|
|
25379
|
-
|
|
25380
|
-
if (this.geneObject) {
|
|
25381
|
-
pd.push('<hr/>');
|
|
25382
|
-
}
|
|
25383
|
-
|
|
25384
|
-
if (this.name) {
|
|
25385
|
-
pd.push({name: 'Name', value: this.name});
|
|
25386
|
-
}
|
|
25387
|
-
|
|
25388
|
-
pd.push({name: 'Type', value: this.type});
|
|
25389
|
-
pd.push({name: 'Source', value: this.source});
|
|
25390
|
-
if (this.score !== undefined) {
|
|
25391
|
-
pd.push({name: 'Score', value: this.score});
|
|
25392
|
-
}
|
|
25393
|
-
pd.push({name: 'Phase', value: this.phase});
|
|
25394
|
-
|
|
25395
|
-
if (this.attributeString) {
|
|
25396
|
-
const atts = parseAttributeString(this.attributeString, this.delim);
|
|
25397
|
-
for (let [key, value] of atts) {
|
|
25398
|
-
if (value !== undefined && value.length > 0 && !filterPopupProperties.has(key.toLowerCase())) {
|
|
25399
|
-
pd.push({name: key + ":", value: value});
|
|
25400
|
-
}
|
|
25401
|
-
}
|
|
25402
|
-
}
|
|
25403
|
-
pd.push({
|
|
25404
|
-
name: 'Location',
|
|
25405
|
-
value: `${this.chr}:${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}`
|
|
25406
|
-
});
|
|
25407
|
-
return pd
|
|
25408
|
-
}
|
|
25409
|
-
|
|
25410
|
-
getAttributeValue(attributeName) {
|
|
25411
|
-
if (this.hasOwnProperty(attributeName)) {
|
|
25412
|
-
return this[attributeName]
|
|
25413
|
-
} else {
|
|
25414
|
-
// TODO -- fetch from attribute string and cache
|
|
25415
|
-
if (!this._attributeCache) {
|
|
25416
|
-
this._attributeCache = new Map();
|
|
25417
|
-
}
|
|
25418
|
-
if (this._attributeCache.has(attributeName)) {
|
|
25419
|
-
return this._attributeCache.get(attributeName)
|
|
25420
|
-
} else {
|
|
25421
|
-
const atts = parseAttributeString(this.attributeString, this.delim);
|
|
25422
|
-
let v;
|
|
25423
|
-
for (let [key, value] of atts) {
|
|
25424
|
-
if (key === attributeName) {
|
|
25425
|
-
v = value;
|
|
25426
|
-
break
|
|
25427
|
-
}
|
|
25428
|
-
}
|
|
25429
|
-
this._attributeCache.set(attributeName, v);
|
|
25430
|
-
return v
|
|
25431
|
-
}
|
|
25432
|
-
}
|
|
25433
|
-
}
|
|
25434
|
-
}
|
|
25435
|
-
|
|
25436
|
-
class GFFTranscript extends GFFFeature {
|
|
25437
|
-
|
|
25438
|
-
constructor(feature) {
|
|
25439
|
-
super(feature);
|
|
25440
|
-
this.exons = [];
|
|
25441
|
-
this.parts = [];
|
|
25442
|
-
}
|
|
25443
|
-
|
|
25444
|
-
addExon(feature) {
|
|
25445
|
-
|
|
25446
|
-
this.exons.push(feature);
|
|
25447
|
-
|
|
25448
|
-
// Expand feature -- for transcripts not explicitly represented in the file (gtf)
|
|
25449
|
-
this.start = Math.min(this.start, feature.start);
|
|
25450
|
-
this.end = Math.max(this.end, feature.end);
|
|
25451
|
-
}
|
|
25452
|
-
|
|
25453
|
-
addPart(feature) {
|
|
25454
|
-
this.parts.push(feature);
|
|
25665
|
+
}
|
|
25455
25666
|
}
|
|
25456
25667
|
|
|
25457
|
-
|
|
25458
|
-
|
|
25459
|
-
if (this.parts.length === 0) return
|
|
25668
|
+
return feature
|
|
25669
|
+
}
|
|
25460
25670
|
|
|
25461
|
-
|
|
25462
|
-
return a.start - b.start
|
|
25463
|
-
});
|
|
25671
|
+
function decodeWig(tokens, header) {
|
|
25464
25672
|
|
|
25465
|
-
|
|
25466
|
-
let lastStart = this.parts[0].start;
|
|
25467
|
-
let lastEnd = this.parts[0].end;
|
|
25468
|
-
for (let i = 1; i < this.parts.length; i++) {
|
|
25469
|
-
const part = this.parts[i];
|
|
25470
|
-
if (isIntron(part.type)) {
|
|
25471
|
-
continue
|
|
25472
|
-
}
|
|
25473
|
-
if (part.start <= lastEnd) {
|
|
25474
|
-
lastEnd = Math.max(lastEnd, part.end);
|
|
25475
|
-
} else {
|
|
25476
|
-
let exon = this.findExonContaining({start: lastStart, end: lastEnd});
|
|
25477
|
-
if (!exon) {
|
|
25478
|
-
this.exons.push({start: lastStart, end: lastEnd, psuedo: true});
|
|
25479
|
-
}
|
|
25480
|
-
lastStart = part.start;
|
|
25481
|
-
lastEnd = part.end;
|
|
25482
|
-
}
|
|
25483
|
-
}
|
|
25484
|
-
let exon = this.findExonContaining({start: lastStart, end: lastEnd});
|
|
25485
|
-
if (!exon) {
|
|
25486
|
-
this.exons.push({start: lastStart, end: lastEnd, psuedo: true});
|
|
25487
|
-
this.start = Math.min(this.start, lastStart);
|
|
25488
|
-
this.end = Math.max(this.end, lastEnd);
|
|
25489
|
-
}
|
|
25673
|
+
const wig = header.wig;
|
|
25490
25674
|
|
|
25675
|
+
if (wig && wig.format === "fixedStep") {
|
|
25676
|
+
const ss = (wig.index * wig.step) + wig.start;
|
|
25677
|
+
const ee = ss + wig.span;
|
|
25678
|
+
const value = Number(tokens[0]);
|
|
25679
|
+
++(wig.index);
|
|
25680
|
+
return isNaN(value) ? null : {chr: wig.chrom, start: ss, end: ee, value: value}
|
|
25681
|
+
} else if (wig && wig.format === "variableStep") {
|
|
25491
25682
|
|
|
25492
|
-
|
|
25493
|
-
|
|
25494
|
-
|
|
25495
|
-
|
|
25496
|
-
|
|
25497
|
-
this.addUTR(part);
|
|
25498
|
-
}
|
|
25499
|
-
}
|
|
25500
|
-
}
|
|
25683
|
+
if (tokens.length < 2) return null
|
|
25684
|
+
const ss = parseInt(tokens[0], 10) - 1;
|
|
25685
|
+
const ee = ss + wig.span;
|
|
25686
|
+
const value = Number(tokens[1]);
|
|
25687
|
+
return isNaN(value) ? null : {chr: wig.chrom, start: ss, end: ee, value: value}
|
|
25501
25688
|
|
|
25502
|
-
|
|
25503
|
-
|
|
25504
|
-
if (exon.end >= end && exon.start <= start) {
|
|
25505
|
-
return exon
|
|
25506
|
-
}
|
|
25507
|
-
}
|
|
25508
|
-
return undefined
|
|
25689
|
+
} else {
|
|
25690
|
+
return decodeBedGraph(tokens)
|
|
25509
25691
|
}
|
|
25692
|
+
}
|
|
25510
25693
|
|
|
25511
|
-
|
|
25694
|
+
function decodeSNP(tokens, header) {
|
|
25512
25695
|
|
|
25513
|
-
|
|
25514
|
-
const exons = this.exons;
|
|
25696
|
+
if (tokens.length < 6) return undefined
|
|
25515
25697
|
|
|
25516
|
-
|
|
25517
|
-
|
|
25518
|
-
|
|
25519
|
-
|
|
25520
|
-
|
|
25521
|
-
|
|
25698
|
+
const autoSql = [
|
|
25699
|
+
'bin',
|
|
25700
|
+
'chr',
|
|
25701
|
+
'start',
|
|
25702
|
+
'end',
|
|
25703
|
+
'name',
|
|
25704
|
+
'score',
|
|
25705
|
+
'strand',
|
|
25706
|
+
'refNCBI',
|
|
25707
|
+
'refUCSC',
|
|
25708
|
+
'observed',
|
|
25709
|
+
'molType',
|
|
25710
|
+
'class',
|
|
25711
|
+
'valid',
|
|
25712
|
+
'avHet',
|
|
25713
|
+
'avHetSE',
|
|
25714
|
+
'func',
|
|
25715
|
+
'locType',
|
|
25716
|
+
'weight',
|
|
25717
|
+
'exceptions',
|
|
25718
|
+
'submitterCount',
|
|
25719
|
+
'submitters',
|
|
25720
|
+
'alleleFreqCount',
|
|
25721
|
+
'alleles',
|
|
25722
|
+
'alleleNs',
|
|
25723
|
+
'alleleFreqs',
|
|
25724
|
+
'bitfields'
|
|
25725
|
+
];
|
|
25522
25726
|
|
|
25523
|
-
if (exon) {
|
|
25524
|
-
exon.cdStart = exon.cdStart ? Math.min(cds.start, exon.cdStart) : cds.start;
|
|
25525
|
-
exon.cdEnd = exon.cdEnd ? Math.max(cds.end, exon.cdEnd) : cds.end;
|
|
25526
|
-
exon.readingFrame = cds.readingFrame;
|
|
25527
|
-
// TODO -- merge attributes?
|
|
25528
|
-
} else {
|
|
25529
|
-
// cds.cdStart = cds.start
|
|
25530
|
-
// cds.cdEnd = cds.end
|
|
25531
|
-
// exons.push(cds)
|
|
25532
|
-
console.error("No exon found spanning " + cds.start + "-" + cds.end);
|
|
25533
|
-
}
|
|
25534
25727
|
|
|
25535
|
-
|
|
25536
|
-
|
|
25537
|
-
|
|
25728
|
+
const feature = {
|
|
25729
|
+
chr: tokens[1],
|
|
25730
|
+
start: Number.parseInt(tokens[2]),
|
|
25731
|
+
end: Number.parseInt(tokens[3]),
|
|
25732
|
+
name: tokens[4],
|
|
25733
|
+
score: Number.parseInt(tokens[5])
|
|
25734
|
+
};
|
|
25538
25735
|
|
|
25539
|
-
|
|
25540
|
-
|
|
25736
|
+
const n = Math.min(tokens.length, autoSql.length);
|
|
25737
|
+
for (let i = 6; i < n; i++) {
|
|
25738
|
+
feature[autoSql[i]] = tokens[i];
|
|
25541
25739
|
}
|
|
25740
|
+
return feature
|
|
25542
25741
|
|
|
25543
|
-
|
|
25742
|
+
}
|
|
25544
25743
|
|
|
25545
|
-
let exon;
|
|
25546
|
-
const exons = this.exons;
|
|
25547
25744
|
|
|
25548
|
-
|
|
25549
|
-
for (let i = 0; i < exons.length; i++) {
|
|
25550
|
-
if (exons[i].start <= utr.start && exons[i].end >= utr.end) {
|
|
25551
|
-
exon = exons[i];
|
|
25552
|
-
break
|
|
25553
|
-
}
|
|
25554
|
-
}
|
|
25745
|
+
class UCSCBedFeature {
|
|
25555
25746
|
|
|
25556
|
-
|
|
25557
|
-
|
|
25558
|
-
|
|
25559
|
-
} else {
|
|
25560
|
-
if (utr.end < exon.end) {
|
|
25561
|
-
exon.cdStart = utr.end;
|
|
25562
|
-
}
|
|
25563
|
-
if (utr.start > exon.start) {
|
|
25564
|
-
exon.cdEnd = utr.start;
|
|
25565
|
-
}
|
|
25566
|
-
}
|
|
25747
|
+
constructor(properties) {
|
|
25748
|
+
Object.assign(this, properties);
|
|
25749
|
+
}
|
|
25567
25750
|
|
|
25568
|
-
|
|
25569
|
-
|
|
25570
|
-
|
|
25571
|
-
|
|
25751
|
+
getAttributeValue(attributeName) {
|
|
25752
|
+
if (this.hasOwnProperty(attributeName)) {
|
|
25753
|
+
return this[attributeName]
|
|
25754
|
+
} else if (this.attributes) {
|
|
25755
|
+
return this.attributes[attributeName]
|
|
25572
25756
|
}
|
|
25757
|
+
}
|
|
25758
|
+
}
|
|
25573
25759
|
|
|
25574
|
-
|
|
25575
|
-
|
|
25576
|
-
|
|
25760
|
+
/*
|
|
25761
|
+
* 0 matches - Number of bases that match that aren't repeats
|
|
25762
|
+
* 1 misMatches - Number of bases that don't match
|
|
25763
|
+
* 2 repMatches - Number of bases that match but are part of repeats
|
|
25764
|
+
* 3 nCount - Number of "N" bases
|
|
25765
|
+
* 4 qNumInsert - Number of inserts in query
|
|
25766
|
+
* 5 qBaseInsert - Number of bases inserted in query
|
|
25767
|
+
* 6 tNumInsert - Number of inserts in target
|
|
25768
|
+
* 7 tBaseInsert - Number of bases inserted in target
|
|
25769
|
+
* 8 strand - "+" or "-" for query strand. For translated alignments, second "+"or "-" is for target genomic strand.
|
|
25770
|
+
* 9 qName - Query sequence name
|
|
25771
|
+
* 10 qSize - Query sequence size.
|
|
25772
|
+
* 11 qStart - Alignment start position in query
|
|
25773
|
+
* 12 qEnd - Alignment end position in query
|
|
25774
|
+
* 13 tName - Target sequence name
|
|
25775
|
+
* 14 tSize - Target sequence size
|
|
25776
|
+
* 15 tStart - Alignment start position in target
|
|
25777
|
+
* 16 tEnd - Alignment end position in target
|
|
25778
|
+
* 17 blockCount - Number of blocks in the alignment (a block contains no gaps)
|
|
25779
|
+
* 18 blockSizes - Comma-separated list of sizes of each block. If the query is a protein and the target the genome, blockSizes are in amino acids. See below for more information on protein query PSLs.
|
|
25780
|
+
* 19 qStarts - Comma-separated list of starting positions of each block in query
|
|
25781
|
+
* 20 tStarts - Comma-separated list of starting positions of each block in target
|
|
25782
|
+
*/
|
|
25577
25783
|
|
|
25784
|
+
class PSLFeature {
|
|
25785
|
+
|
|
25786
|
+
|
|
25787
|
+
constructor(properties) {
|
|
25788
|
+
Object.assign(this, properties);
|
|
25578
25789
|
}
|
|
25579
25790
|
|
|
25580
|
-
|
|
25791
|
+
get score() {
|
|
25792
|
+
const tokens = this.tokens;
|
|
25793
|
+
const match = parseInt(tokens[0]);
|
|
25794
|
+
const repMatch = parseInt(tokens[2]);
|
|
25795
|
+
const misMatch = parseInt(tokens[1]);
|
|
25796
|
+
const qGapCount = parseInt(tokens[4]);
|
|
25797
|
+
const tGapCount = parseInt(tokens[6]);
|
|
25798
|
+
const qSize = parseInt(tokens[10]);
|
|
25799
|
+
return Math.floor((1000 * (match + repMatch - misMatch - qGapCount - tGapCount)) / qSize)
|
|
25800
|
+
}
|
|
25581
25801
|
|
|
25582
|
-
|
|
25802
|
+
get matches() {
|
|
25803
|
+
return this.tokens[0]
|
|
25804
|
+
}
|
|
25583
25805
|
|
|
25584
|
-
|
|
25585
|
-
|
|
25806
|
+
get misMatches() {
|
|
25807
|
+
return this.tokens[1]
|
|
25808
|
+
}
|
|
25586
25809
|
|
|
25587
|
-
|
|
25588
|
-
|
|
25589
|
-
|
|
25810
|
+
get repMatches() {
|
|
25811
|
+
return this.tokens[2]
|
|
25812
|
+
}
|
|
25590
25813
|
|
|
25591
|
-
|
|
25592
|
-
|
|
25593
|
-
this.exons.forEach(function (exon) {
|
|
25594
|
-
if (exon.end < cdStart || exon.start > cdEnd) exon.utr = true;
|
|
25595
|
-
});
|
|
25596
|
-
}
|
|
25814
|
+
get nCount() {
|
|
25815
|
+
return this.tokens[3]
|
|
25597
25816
|
}
|
|
25598
25817
|
|
|
25599
|
-
|
|
25818
|
+
get qNumInsert() {
|
|
25819
|
+
return this.tokens[4]
|
|
25820
|
+
}
|
|
25600
25821
|
|
|
25601
|
-
|
|
25822
|
+
get qBaseInsert() {
|
|
25823
|
+
return this.tokens[5]
|
|
25824
|
+
}
|
|
25602
25825
|
|
|
25603
|
-
|
|
25604
|
-
|
|
25605
|
-
|
|
25606
|
-
if (genomicLocation >= exon.start && genomicLocation < exon.end && typeof exon.popupData === 'function') {
|
|
25607
|
-
pd.push('<hr/>');
|
|
25608
|
-
const exonData = exon.popupData(genomicLocation);
|
|
25609
|
-
for (let att of exonData) {
|
|
25610
|
-
pd.push(att);
|
|
25611
|
-
}
|
|
25612
|
-
}
|
|
25613
|
-
}
|
|
25826
|
+
get tNumInsert() {
|
|
25827
|
+
return this.tokens[6]
|
|
25828
|
+
}
|
|
25614
25829
|
|
|
25615
|
-
|
|
25616
|
-
|
|
25617
|
-
pd.push('<hr/>');
|
|
25618
|
-
const partData = part.popupData(genomicLocation);
|
|
25619
|
-
for (let att of partData) {
|
|
25620
|
-
pd.push(att);
|
|
25621
|
-
}
|
|
25622
|
-
}
|
|
25623
|
-
}
|
|
25830
|
+
get tBaseInsert() {
|
|
25831
|
+
return this.tokens[7]
|
|
25624
25832
|
|
|
25833
|
+
}
|
|
25625
25834
|
|
|
25626
|
-
|
|
25835
|
+
popupData() {
|
|
25836
|
+
return [
|
|
25837
|
+
{name: 'chr', value: this.chr},
|
|
25838
|
+
{name: 'start', value: this.start + 1},
|
|
25839
|
+
{name: 'end', value: this.end},
|
|
25840
|
+
{name: 'strand', value: this.strand},
|
|
25841
|
+
{name: 'score', value: this.score},
|
|
25842
|
+
{name: 'match', value: this.matches},
|
|
25843
|
+
{name: "mis-match", value: this.misMatches},
|
|
25844
|
+
{name: "rep. match", value: this.repMatches},
|
|
25845
|
+
{name: "N's", value: this.nCount},
|
|
25846
|
+
{name: 'Q gap count', value: this.qNumInsert},
|
|
25847
|
+
{name: 'Q gap bases', value: this.qBaseInsert},
|
|
25848
|
+
{name: 'T gap count', value: this.tNumInsert},
|
|
25849
|
+
{name: 'T gap bases', value: this.tBaseInsert},
|
|
25850
|
+
]
|
|
25627
25851
|
}
|
|
25852
|
+
|
|
25628
25853
|
}
|
|
25629
25854
|
|
|
25630
25855
|
function decode(tokens, header) {
|
|
@@ -25926,223 +26151,6 @@
|
|
|
25926
26151
|
return feature
|
|
25927
26152
|
}
|
|
25928
26153
|
|
|
25929
|
-
const gffNameFields = ["Name", "transcript_id", "gene_name", "gene", "gene_id", "alias", "locus", "name" ];
|
|
25930
|
-
|
|
25931
|
-
class GFFHelper {
|
|
25932
|
-
|
|
25933
|
-
constructor(options) {
|
|
25934
|
-
this.format = options.format;
|
|
25935
|
-
this.nameField = options.nameField;
|
|
25936
|
-
this.filterTypes = options.filterTypes === undefined ?
|
|
25937
|
-
new Set(['chromosome']) :
|
|
25938
|
-
new Set(options.filterTypes);
|
|
25939
|
-
}
|
|
25940
|
-
|
|
25941
|
-
combineFeatures(features, genomicInterval) {
|
|
25942
|
-
|
|
25943
|
-
let combinedFeatures;
|
|
25944
|
-
|
|
25945
|
-
const filterTypes = this.filterTypes;
|
|
25946
|
-
features = features.filter(f => filterTypes === undefined || !filterTypes.has(f.type));
|
|
25947
|
-
|
|
25948
|
-
if ("gff3" === this.format) {
|
|
25949
|
-
const tmp = this.combineFeaturesById(features);
|
|
25950
|
-
combinedFeatures = this.combineFeaturesByType(tmp);
|
|
25951
|
-
} else {
|
|
25952
|
-
combinedFeatures = this.combineFeaturesByType(features);
|
|
25953
|
-
}
|
|
25954
|
-
|
|
25955
|
-
this.numberExons(combinedFeatures, genomicInterval);
|
|
25956
|
-
this.nameFeatures(combinedFeatures);
|
|
25957
|
-
return combinedFeatures
|
|
25958
|
-
}
|
|
25959
|
-
|
|
25960
|
-
/**
|
|
25961
|
-
* Combine multiple non-transcript model features with the same ID on the same chromosome into a single feature.
|
|
25962
|
-
* Features that are part of the transcript model (e.g. exon, mRNA, etc) are combined later.
|
|
25963
|
-
*
|
|
25964
|
-
* @param features
|
|
25965
|
-
* @returns {[]}
|
|
25966
|
-
*/
|
|
25967
|
-
combineFeaturesById(features) {
|
|
25968
|
-
|
|
25969
|
-
const chrIdMap = new Map();
|
|
25970
|
-
const combinedFeatures = [];
|
|
25971
|
-
|
|
25972
|
-
for (let f of features) {
|
|
25973
|
-
if (isTranscriptPart(f.type) || isTranscript(f.type) || !f.id) {
|
|
25974
|
-
combinedFeatures.push(f);
|
|
25975
|
-
} else {
|
|
25976
|
-
let idMap = chrIdMap.get(f.chr);
|
|
25977
|
-
if (!idMap) {
|
|
25978
|
-
idMap = new Map();
|
|
25979
|
-
chrIdMap.set(f.chr, idMap);
|
|
25980
|
-
}
|
|
25981
|
-
|
|
25982
|
-
let featureArray = idMap.get(f.id);
|
|
25983
|
-
if (featureArray) {
|
|
25984
|
-
featureArray.push(f);
|
|
25985
|
-
} else {
|
|
25986
|
-
idMap.set(f.id, [f]);
|
|
25987
|
-
}
|
|
25988
|
-
}
|
|
25989
|
-
}
|
|
25990
|
-
|
|
25991
|
-
for (let idMap of chrIdMap.values()) {
|
|
25992
|
-
for (let featureArray of idMap.values()) {
|
|
25993
|
-
if (featureArray.length > 1) {
|
|
25994
|
-
// Use the first feature as prototypical (for column 9 attributes), and adjust start/end
|
|
25995
|
-
// Parts are represented as "exons", as that is how they are presented visually
|
|
25996
|
-
const cf = featureArray[0];
|
|
25997
|
-
cf.exons = [];
|
|
25998
|
-
for (let f of featureArray) {
|
|
25999
|
-
cf.start = Math.min(cf.start, f.start);
|
|
26000
|
-
cf.end = Math.max(cf.end, f.end);
|
|
26001
|
-
cf.exons.push({
|
|
26002
|
-
start: f.start,
|
|
26003
|
-
end: f.end
|
|
26004
|
-
});
|
|
26005
|
-
}
|
|
26006
|
-
combinedFeatures.push(cf);
|
|
26007
|
-
} else {
|
|
26008
|
-
combinedFeatures.push(featureArray[0]);
|
|
26009
|
-
}
|
|
26010
|
-
}
|
|
26011
|
-
}
|
|
26012
|
-
|
|
26013
|
-
return combinedFeatures
|
|
26014
|
-
}
|
|
26015
|
-
|
|
26016
|
-
combineFeaturesByType(features) {
|
|
26017
|
-
|
|
26018
|
-
// Build dictionary of genes
|
|
26019
|
-
const genes = features.filter(f => "gene" === f.type || f.type.endsWith("_gene"));
|
|
26020
|
-
const geneMap = Object.create(null);
|
|
26021
|
-
for (let g of genes) {
|
|
26022
|
-
geneMap[g.id] = g;
|
|
26023
|
-
}
|
|
26024
|
-
|
|
26025
|
-
// 1. Build dictionary of transcripts
|
|
26026
|
-
const transcripts = Object.create(null);
|
|
26027
|
-
const combinedFeatures = [];
|
|
26028
|
-
const consumedFeatures = new Set();
|
|
26029
|
-
const filterTypes = this.filterTypes;
|
|
26030
|
-
|
|
26031
|
-
features = features.filter(f => filterTypes === undefined || !filterTypes.has(f.type));
|
|
26032
|
-
|
|
26033
|
-
for (let f of features) {
|
|
26034
|
-
if (isTranscript(f.type)) {
|
|
26035
|
-
const transcriptId = f.id; // getAttribute(f.attributeString, "transcript_id", /\s+/);
|
|
26036
|
-
if (undefined !== transcriptId) {
|
|
26037
|
-
const gffTranscript = new GFFTranscript(f);
|
|
26038
|
-
transcripts[transcriptId] = gffTranscript;
|
|
26039
|
-
combinedFeatures.push(gffTranscript);
|
|
26040
|
-
consumedFeatures.add(f);
|
|
26041
|
-
const g = geneMap[f.parent];
|
|
26042
|
-
if (g) {
|
|
26043
|
-
gffTranscript.geneObject = g;
|
|
26044
|
-
consumedFeatures.add(g);
|
|
26045
|
-
}
|
|
26046
|
-
}
|
|
26047
|
-
}
|
|
26048
|
-
}
|
|
26049
|
-
|
|
26050
|
-
// Add exons and transcript parts
|
|
26051
|
-
for (let f of features) {
|
|
26052
|
-
if (isTranscriptPart(f.type)) {
|
|
26053
|
-
const parents = getParents(f);
|
|
26054
|
-
if (parents) {
|
|
26055
|
-
for (let id of parents) {
|
|
26056
|
-
|
|
26057
|
-
let transcript = transcripts[id];
|
|
26058
|
-
if (!transcript && this.format === "gtf") {
|
|
26059
|
-
// GTF does not require explicit a transcript or mRNA record, start one with this feature.
|
|
26060
|
-
const psuedoTranscript = Object.assign({}, f);
|
|
26061
|
-
psuedoTranscript.type = "transcript";
|
|
26062
|
-
transcript = new GFFTranscript(psuedoTranscript);
|
|
26063
|
-
transcripts[id] = transcript;
|
|
26064
|
-
combinedFeatures.push(transcript);
|
|
26065
|
-
}
|
|
26066
|
-
if (transcript !== undefined) {
|
|
26067
|
-
|
|
26068
|
-
if (isExon(f.type)) {
|
|
26069
|
-
if (parents.length > 1) {
|
|
26070
|
-
// Multiple parents, this is unusual. Make a copy as exon can be modified
|
|
26071
|
-
// differently by CDS, etc, for each parent
|
|
26072
|
-
const e2 = new GFFFeature(f);
|
|
26073
|
-
transcript.addExon(e2);
|
|
26074
|
-
} else {
|
|
26075
|
-
transcript.addExon(f);
|
|
26076
|
-
}
|
|
26077
|
-
} else {
|
|
26078
|
-
transcript.addPart(f);
|
|
26079
|
-
}
|
|
26080
|
-
consumedFeatures.add(f);
|
|
26081
|
-
}
|
|
26082
|
-
}
|
|
26083
|
-
}
|
|
26084
|
-
}
|
|
26085
|
-
}
|
|
26086
|
-
|
|
26087
|
-
// Finish transcripts
|
|
26088
|
-
combinedFeatures.forEach(function (f) {
|
|
26089
|
-
if (typeof f.finish === "function") {
|
|
26090
|
-
f.finish();
|
|
26091
|
-
}
|
|
26092
|
-
});
|
|
26093
|
-
|
|
26094
|
-
// Add other features
|
|
26095
|
-
const others = features.filter(f => !consumedFeatures.has(f));
|
|
26096
|
-
for (let f of others) {
|
|
26097
|
-
combinedFeatures.push(f);
|
|
26098
|
-
}
|
|
26099
|
-
|
|
26100
|
-
return combinedFeatures
|
|
26101
|
-
|
|
26102
|
-
function getParents(f) {
|
|
26103
|
-
if (f.parent && f.parent.trim() !== "") {
|
|
26104
|
-
return f.parent.trim().split(",")
|
|
26105
|
-
} else {
|
|
26106
|
-
return null
|
|
26107
|
-
}
|
|
26108
|
-
}
|
|
26109
|
-
}
|
|
26110
|
-
|
|
26111
|
-
numberExons(features, genomicInterval) {
|
|
26112
|
-
|
|
26113
|
-
for (let f of features) {
|
|
26114
|
-
if (f.exons &&
|
|
26115
|
-
(!genomicInterval ||
|
|
26116
|
-
(f.end <= genomicInterval.end && f.start > genomicInterval.start))) {
|
|
26117
|
-
for (let i = 0; i < f.exons.length; i++) {
|
|
26118
|
-
const exon = f.exons[i];
|
|
26119
|
-
exon.number = f.strand === "-" ? f.exons.length - i : i + 1;
|
|
26120
|
-
}
|
|
26121
|
-
}
|
|
26122
|
-
}
|
|
26123
|
-
}
|
|
26124
|
-
|
|
26125
|
-
nameFeatures(features) {
|
|
26126
|
-
// Find name (label) property
|
|
26127
|
-
for (let f of features) {
|
|
26128
|
-
if(typeof f.getAttributeValue === 'function') {
|
|
26129
|
-
|
|
26130
|
-
if (this.nameField) {
|
|
26131
|
-
f.name = f.getAttributeValue(this.nameField);
|
|
26132
|
-
} else {
|
|
26133
|
-
for (let nameField of gffNameFields) {
|
|
26134
|
-
const v = f.getAttributeValue(nameField);
|
|
26135
|
-
if (v) {
|
|
26136
|
-
f.name = v;
|
|
26137
|
-
break
|
|
26138
|
-
}
|
|
26139
|
-
}
|
|
26140
|
-
}
|
|
26141
|
-
}
|
|
26142
|
-
}
|
|
26143
|
-
}
|
|
26144
|
-
}
|
|
26145
|
-
|
|
26146
26154
|
/**
|
|
26147
26155
|
* Utilities for creating and registering custom file formats for generic delimited files. A format definition consists
|
|
26148
26156
|
* of an ordered list of fields, and optional delimiter specified.
|
|
@@ -72574,7 +72582,7 @@
|
|
|
72574
72582
|
})
|
|
72575
72583
|
}
|
|
72576
72584
|
|
|
72577
|
-
const _version = "3.1.
|
|
72585
|
+
const _version = "3.1.4";
|
|
72578
72586
|
function version() {
|
|
72579
72587
|
return _version
|
|
72580
72588
|
}
|
|
@@ -76336,15 +76344,6 @@
|
|
|
76336
76344
|
this.root = div({class: 'igv-container'});
|
|
76337
76345
|
shadowRoot.appendChild(this.root);
|
|
76338
76346
|
|
|
76339
|
-
// spinner
|
|
76340
|
-
this.spinner = div({class: 'igv-loading-spinner-container'});
|
|
76341
|
-
this.root.appendChild(this.spinner);
|
|
76342
|
-
|
|
76343
|
-
this.spinner.appendChild(div());
|
|
76344
|
-
this.spinner.style.width = '64px';
|
|
76345
|
-
this.spinner.style.height = '64px';
|
|
76346
|
-
this.stopSpinner();
|
|
76347
|
-
|
|
76348
76347
|
this.alert = new Alert(this.root);
|
|
76349
76348
|
|
|
76350
76349
|
this.columnContainer = div({class: 'igv-column-container'});
|
|
@@ -76420,14 +76419,6 @@
|
|
|
76420
76419
|
return this.roiManager.roiTableIsVisible()
|
|
76421
76420
|
}
|
|
76422
76421
|
|
|
76423
|
-
startSpinner() {
|
|
76424
|
-
this.spinner.style.display = 'flex';
|
|
76425
|
-
}
|
|
76426
|
-
|
|
76427
|
-
stopSpinner() {
|
|
76428
|
-
this.spinner.style.display = 'none';
|
|
76429
|
-
}
|
|
76430
|
-
|
|
76431
76422
|
initialize(config) {
|
|
76432
76423
|
|
|
76433
76424
|
this.flanking = config.flanking;
|
|
@@ -78776,9 +78767,6 @@
|
|
|
78776
78767
|
const browser = new Browser(config, parentDiv);
|
|
78777
78768
|
allBrowsers.push(browser);
|
|
78778
78769
|
|
|
78779
|
-
// Lod initial sessio
|
|
78780
|
-
browser.startSpinner();
|
|
78781
|
-
|
|
78782
78770
|
const sessionURL = config.sessionURL || config.session || config.hubURL;
|
|
78783
78771
|
if (sessionURL) {
|
|
78784
78772
|
await browser.loadSession({
|
|
@@ -78788,7 +78776,6 @@
|
|
|
78788
78776
|
await browser.loadSessionObject(config);
|
|
78789
78777
|
}
|
|
78790
78778
|
|
|
78791
|
-
browser.stopSpinner();
|
|
78792
78779
|
browser.navbar.navbarDidResize();
|
|
78793
78780
|
|
|
78794
78781
|
return browser
|