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