gramene-search 2.0.5 → 2.0.7

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/index.js CHANGED
@@ -11,6 +11,7 @@ var $gXNCa$gramenebinsclient = require("gramene-bins-client");
11
11
  var $gXNCa$gramenetreesclient = require("gramene-trees-client");
12
12
  var $gXNCa$gramenetaxonomywithgenomes = require("gramene-taxonomy-with-genomes");
13
13
  var $gXNCa$reactga4 = require("react-ga4");
14
+ var $gXNCa$moneyclip = require("money-clip");
14
15
  var $gXNCa$reactbootstrap = require("react-bootstrap");
15
16
  var $gXNCa$reactswitch = require("react-switch");
16
17
  var $gXNCa$reacticonsio5 = require("react-icons/io5");
@@ -1456,6 +1457,12 @@ const $24971af0a229e0e3$var$grameneViews = {
1456
1457
  show: 'off',
1457
1458
  shouldScroll: false
1458
1459
  },
1460
+ {
1461
+ id: 'gsea',
1462
+ name: 'Gene set enrichment',
1463
+ show: 'off',
1464
+ shouldScroll: false
1465
+ },
1459
1466
  {
1460
1467
  id: 'userLists',
1461
1468
  name: 'User Gene Lists',
@@ -1551,7 +1558,9 @@ const $24971af0a229e0e3$var$grameneViews = {
1551
1558
  const resultDependentIds = new Set([
1552
1559
  'taxonomy',
1553
1560
  'list',
1554
- 'export'
1561
+ 'export',
1562
+ 'exprViz',
1563
+ 'gsea'
1555
1564
  ]);
1556
1565
  const autoDisable = numFound === 0 || !hasFilters;
1557
1566
  const hasFirebase = !!(config && config.firebaseConfig);
@@ -1599,6 +1608,16 @@ const $24971af0a229e0e3$var$grameneViews = {
1599
1608
  var $24971af0a229e0e3$export$2e2bcd8739ae039 = $24971af0a229e0e3$var$grameneViews;
1600
1609
 
1601
1610
 
1611
+
1612
+ // Dedicated IndexedDB store for the full pathway corpus, persisted
1613
+ // indefinitely (default maxAge of `Infinity`). The pathway set is small
1614
+ // and stable enough to keep around across sessions, so we bulk-load it
1615
+ // once with `?rows=-1` and reuse it instead of issuing per-id requests.
1616
+ const $671312b287158a8a$var$pathwayCache = (0, $gXNCa$moneyclip.getConfiguredCache)({
1617
+ version: 1,
1618
+ name: 'gramene_pathways'
1619
+ });
1620
+ let $671312b287158a8a$var$pathwaysBulkPromise = null;
1602
1621
  const $671312b287158a8a$var$grameneDocs = {
1603
1622
  name: 'grameneDocs',
1604
1623
  getReducer: ()=>{
@@ -1803,26 +1822,37 @@ const $671312b287158a8a$var$grameneDocs = {
1803
1822
  });
1804
1823
  }
1805
1824
  },
1806
- doRequestGramenePathways: (ids)=>({ dispatch: dispatch, store: store })=>{
1807
- const pathways = store.selectGramenePathways();
1808
- let newIds = ids.filter((id)=>!pathways.hasOwnProperty(id));
1809
- if (newIds) {
1810
- dispatch({
1811
- type: 'GRAMENE_PATHWAYS_REQUESTED',
1812
- payload: newIds
1813
- });
1814
- if (newIds.length === 1) newIds.push(0);
1815
- fetch(`${store.selectGrameneAPI()}/pathways?idList=${newIds.join(',')}`).then((res)=>res.json()).then((res)=>{
1816
- let pathways = {};
1817
- res.forEach((p)=>{
1818
- pathways[p._id] = p;
1825
+ // Signature kept for backward compatibility; the `ids` argument is
1826
+ // ignored. On first call we bulk-load every pathway record from the
1827
+ // dedicated IndexedDB cache (or `${api}/pathways?rows=-1` on miss),
1828
+ // dispatch a single GRAMENE_PATHWAYS_RECEIVED, and short-circuit all
1829
+ // subsequent calls.
1830
+ doRequestGramenePathways: (_ids)=>({ dispatch: dispatch, store: store })=>{
1831
+ if ($671312b287158a8a$var$pathwaysBulkPromise) return $671312b287158a8a$var$pathwaysBulkPromise;
1832
+ $671312b287158a8a$var$pathwaysBulkPromise = $671312b287158a8a$var$pathwayCache.get('all').then((cached)=>{
1833
+ if (cached) {
1834
+ dispatch({
1835
+ type: 'GRAMENE_PATHWAYS_RECEIVED',
1836
+ payload: cached
1837
+ });
1838
+ return;
1839
+ }
1840
+ return fetch(`${store.selectGrameneAPI()}/pathways?rows=-1`).then((res)=>res.json()).then((res)=>{
1841
+ const pathways = {};
1842
+ (res || []).forEach((p)=>{
1843
+ if (p && p._id != null) pathways[p._id] = p;
1819
1844
  });
1820
1845
  dispatch({
1821
1846
  type: 'GRAMENE_PATHWAYS_RECEIVED',
1822
1847
  payload: pathways
1823
1848
  });
1849
+ $671312b287158a8a$var$pathwayCache.set('all', pathways).catch((e)=>console.warn('Failed to cache pathways', e));
1824
1850
  });
1825
- }
1851
+ }).catch((err)=>{
1852
+ console.error('Failed to load pathways', err);
1853
+ $671312b287158a8a$var$pathwaysBulkPromise = null; // allow retry on next call
1854
+ });
1855
+ return $671312b287158a8a$var$pathwaysBulkPromise;
1826
1856
  },
1827
1857
  doRequestExpressionStudies: (id)=>({ dispatch: dispatch, store: store })=>{
1828
1858
  fetch(`${store.selectGrameneAPI()}/experiments?rows=-1`).then((res)=>res.json()).then((res)=>{
@@ -2076,7 +2106,7 @@ var $0d54502f6cafe273$export$2e2bcd8739ae039 = $0d54502f6cafe273$var$grameneGeno
2076
2106
 
2077
2107
 
2078
2108
  var $65709bd8598fce20$exports = {};
2079
- $65709bd8598fce20$exports = JSON.parse('{"groups":{"core":{"label":"Core identifiers","order":0},"location":{"label":"Genomic location","order":1},"structure":{"label":"Gene structure","order":2},"homology":{"label":"Homology","order":3},"expression":{"label":"Gene expression","order":4},"differential":{"label":"Differential expression","order":5},"hierarchical":{"label":"Hierarchical annotations","order":6},"pathways":{"label":"Pathways","order":7},"GO":{"label":"Gene Ontology","order":7},"PO":{"label":"Plant Ontology","order":8},"lof":{"label":"Loss of function alleles","order":9},"MAKER":{"label":"MAKER transcript metrics","order":10},"xrefs":{"label":"External references","order":11},"other":{"label":"Other","order":99}},"patterns":[{"id":"homology","match":"^homology__(.+)$","group":"homology","multiValued":true,"labelTemplate":"Homology: $1"},{"id":"pathways","match":"^pathways__(.+)$","group":"pathways","multiValued":true,"labelTemplate":"Pathway: $1"},{"id":"maker","match":"^MAKER__(.+)__attr_([a-z])$","group":"MAKER","labelTemplate":"MAKER: $1"},{"id":"xrefs","match":"^(.+)__xrefs$","group":"xrefs","multiValued":true,"labelTemplate":"$1 (xrefs)"},{"id":"expr","match":"^(E[-_][A-Za-z0-9_-]+?)_g(\\\\d+)__expr$","group":"expression","expression":true,"labelTemplate":"$1 \xb7 g$2"},{"id":"diffexpr","match":"^(E[-_][A-Za-z0-9_-]+?)_g(\\\\d+)_g(\\\\d+)_(pval|logfc|l2fc)_attr_([a-z])$","group":"differential","diffExpression":true,"labelTemplate":"$1 \xb7 g$2 vs g$3 \xb7 $4"},{"id":"bins","match":"^(fixed|uniform)_([0-9a-zA-Z]+)__bin$","group":"bins","is_hidden":true,"labelTemplate":"$1 bin ($2)"},{"id":"neighbors","match":".*neighbors_[0-9]+$","group":"neighbors","is_hidden":true,"labelTemplate":"neighbors"},{"id":"vep_merged","match":"^VEP__merged__(NAT|EMS)__attr_ss$","group":"lof","multiValued":true,"labelTemplate":"Merged $1 accessions"},{"id":"vep_detail","match":"^VEP__(.+?)__(homo|het)__(.+?)__(\\\\d+)__attr_ss$","group":"lof","multiValued":true,"labelTemplate":"$1 ($2) $3/$4"},{"id":"generic_attr","match":"^(.+)__attr_([a-z])$","group":"other","labelTemplate":"$1"}],"fields":{"id":{"group":"core","label":"Gene ID","order":1},"name":{"group":"core","label":"Name","order":2},"description":{"group":"core","label":"Description","order":4},"summary":{"group":"core","label":"Summary","order":5},"synonyms":{"group":"core","label":"Synonyms","multiValued":true,"order":3},"biotype":{"group":"core","label":"Biotype","order":6},"taxon_id":{"group":"core","label":"Taxon ID","order":8},"system_name":{"group":"core","label":"System name","order":7},"db_type":{"group":"core","label":"DB type","order":9},"closest_rep_id":{"group":"homology","label":"Closest representative ID","order":1},"closest_rep_name":{"group":"homology","label":"Closest representative name","order":2},"closest_rep_identity":{"group":"homology","label":"Closest representative identity","order":3},"closest_rep_taxon_id":{"group":"homology","label":"Closest representative taxon","order":4},"closest_rep_description":{"group":"homology","label":"Closest representative description","order":5},"model_rep_id":{"group":"homology","label":"Model representative ID","order":6},"model_rep_name":{"group":"homology","label":"Model representative name","order":7},"model_rep_identity":{"group":"homology","label":"Model representative identity","order":8},"model_rep_taxon_id":{"group":"homology","label":"Model representative taxon","order":9},"model_rep_description":{"group":"homology","label":"Model representative description","order":10},"gene_tree":{"group":"homology","label":"Gene tree ID","order":11},"pan_tree":{"group":"homology","label":"Pan-gene tree ID","order":14},"capabilities":{"group":"other","label":"Capabilities","multiValued":true},"map":{"group":"location","label":"Map","order":1},"region":{"group":"location","label":"Region","order":2},"start":{"group":"location","label":"Start","order":3},"end":{"group":"location","label":"End","order":4},"strand":{"group":"location","label":"Strand","order":5},"GO__ancestors":{"group":"hierarchical","label":"GO terms","multiValued":true,"order":1},"PO__ancestors":{"group":"hierarchical","label":"PO terms","multiValued":true,"order":2},"TO__ancestors":{"group":"hierarchical","label":"TO terms","multiValued":true,"order":3},"QTL_TO__ancestors":{"group":"hierarchical","label":"QTL traits (TO)","multiValued":true,"order":7},"domains__ancestors":{"group":"hierarchical","label":"Domains","multiValued":true,"order":4},"taxonomy__ancestors":{"group":"hierarchical","label":"Taxonomy","multiValued":true,"order":6},"supertree_attr_s":{"group":"homology","label":"Supertree","order":13},"gene_tree_root_taxon_id":{"group":"homology","label":"Gene tree root taxon","order":12},"protein__length":{"group":"structure","label":"Protein length"},"transcript__count":{"group":"structure","label":"Transcript count"},"transcript__exons":{"group":"structure","label":"Exon count"},"transcript__length":{"group":"structure","label":"Transcript length"},"expressed_in_gxa_attr_ss":{"group":"expression","label":"Expressed in GXA","multiValued":true},"MAKER__AED__attr_f":{"group":"MAKER","label":"AED","description":"Annotation Edit Distance"},"MAKER__QI1__attr_i":{"group":"MAKER","label":"QI1: Length of the 5\' UTR"},"MAKER__QI2__attr_f":{"group":"MAKER","label":"QI2: Fraction of splice sites confirmed by EST"},"MAKER__QI3__attr_f":{"group":"MAKER","label":"QI3: Fraction of exons overlapping an EST"},"MAKER__QI4__attr_f":{"group":"MAKER","label":"QI4: Fraction of exons overlapping EST or protein"},"MAKER__QI5__attr_f":{"group":"MAKER","label":"QI5: Fraction of splice sites confirmed by SNAP"},"MAKER__QI6__attr_f":{"group":"MAKER","label":"QI6: Fraction of exons overlapping a SNAP"},"MAKER__QI7__attr_i":{"group":"MAKER","label":"QI7: Number of exons in the mRNA"},"MAKER__QI8__attr_i":{"group":"MAKER","label":"QI8: Length of the 3\' UTR"},"MAKER__QI9__attr_i":{"group":"MAKER","label":"QI9: Length of the protein sequence"},"homology__all_orthologs":{"group":"homology","label":"All orthologs","multiValued":true,"order":15},"homology__ortholog_one2one":{"group":"homology","label":"1:1 orthologs","multiValued":true,"order":16},"homology__ortholog_one2many":{"group":"homology","label":"1:many orthologs","multiValued":true,"order":17},"homology__ortholog_many2many":{"group":"homology","label":"Many:many orthologs","multiValued":true,"order":18},"homology__syntenic_ortholog_one2one":{"group":"homology","label":"Syntenic 1:1 orthologs","multiValued":true,"order":19},"homology__within_species_paralog":{"group":"homology","label":"Within-species paralogs","multiValued":true,"order":20},"pathways__ancestors":{"group":"hierarchical","label":"Pathways","multiValued":true,"order":5},"canonical_transcript__attr_s":{"group":"structure","label":"Canonical transcript ID"}},"hidden":["_version_","_terms","score","gene_idx","gene_idx_multi","species_idx","compara_idx","compara_idx_multi","_id","annotations","bins","gene_structure","homology","location","xrefs","domain_roots","familyRoot__ancestors","taxonomy__ancestors","capabilities","saved_search"]}');
2109
+ $65709bd8598fce20$exports = JSON.parse('{"groups":{"core":{"label":"Core identifiers","order":0},"location":{"label":"Genomic location","order":1},"structure":{"label":"Gene structure","order":2},"homology":{"label":"Homology","order":3},"expression":{"label":"Gene expression","order":4},"differential":{"label":"Differential expression","order":5},"hierarchical":{"label":"Hierarchical annotations","order":6},"pathways":{"label":"Pathways","order":7},"GO":{"label":"Gene Ontology","order":7},"PO":{"label":"Plant Ontology","order":8},"lof":{"label":"Loss of function alleles","order":9},"MAKER":{"label":"MAKER transcript metrics","order":10},"xrefs":{"label":"External references","order":11},"other":{"label":"Other","order":99}},"patterns":[{"id":"homology","match":"^homology__(.+)$","group":"homology","multiValued":true,"labelTemplate":"Homology: $1"},{"id":"pathways","match":"^pathways__(.+)$","group":"pathways","multiValued":true,"labelTemplate":"Pathway: $1"},{"id":"maker","match":"^MAKER__(.+)__attr_([a-z])$","group":"MAKER","labelTemplate":"MAKER: $1"},{"id":"xrefs","match":"^(.+)__xrefs$","group":"xrefs","multiValued":true,"labelTemplate":"$1 (xrefs)"},{"id":"expr","match":"^(E[-_][A-Za-z0-9_-]+?)_g(\\\\d+)__expr$","group":"expression","expression":true,"labelTemplate":"$1 \xb7 g$2"},{"id":"diffexpr","match":"^(E[-_][A-Za-z0-9_-]+?)_g(\\\\d+)_g(\\\\d+)_(pval|l2fc)_attr_([a-z])$","group":"differential","diffExpression":true,"labelTemplate":"$1 \xb7 g$2 vs g$3 \xb7 $4"},{"id":"bins","match":"^(fixed|uniform)_([0-9a-zA-Z]+)__bin$","group":"bins","is_hidden":true,"labelTemplate":"$1 bin ($2)"},{"id":"neighbors","match":".*neighbors_[0-9]+$","group":"neighbors","is_hidden":true,"labelTemplate":"neighbors"},{"id":"vep_merged","match":"^VEP__merged__(NAT|EMS)__attr_ss$","group":"lof","multiValued":true,"labelTemplate":"Merged $1 accessions"},{"id":"vep_detail","match":"^VEP__(.+?)__(homo|het)__(.+?)__(\\\\d+)__attr_ss$","group":"lof","multiValued":true,"labelTemplate":"$1 ($2) $3/$4"},{"id":"generic_attr","match":"^(.+)__attr_([a-z])$","group":"other","labelTemplate":"$1"}],"fields":{"id":{"group":"core","label":"Gene ID","order":1},"name":{"group":"core","label":"Name","order":2},"description":{"group":"core","label":"Description","order":4},"summary":{"group":"core","label":"Summary","order":5},"synonyms":{"group":"core","label":"Synonyms","multiValued":true,"order":3},"biotype":{"group":"core","label":"Biotype","order":6},"taxon_id":{"group":"core","label":"Taxon ID","order":8},"system_name":{"group":"core","label":"System name","order":7},"db_type":{"group":"core","label":"DB type","order":9},"closest_rep_id":{"group":"homology","label":"Closest representative ID","order":1},"closest_rep_name":{"group":"homology","label":"Closest representative name","order":2},"closest_rep_identity":{"group":"homology","label":"Closest representative identity","order":3},"closest_rep_taxon_id":{"group":"homology","label":"Closest representative taxon","order":4},"closest_rep_description":{"group":"homology","label":"Closest representative description","order":5},"model_rep_id":{"group":"homology","label":"Model representative ID","order":6},"model_rep_name":{"group":"homology","label":"Model representative name","order":7},"model_rep_identity":{"group":"homology","label":"Model representative identity","order":8},"model_rep_taxon_id":{"group":"homology","label":"Model representative taxon","order":9},"model_rep_description":{"group":"homology","label":"Model representative description","order":10},"gene_tree":{"group":"homology","label":"Gene tree ID","order":11},"pan_tree":{"group":"homology","label":"Pan-gene tree ID","order":14},"capabilities":{"group":"other","label":"Capabilities","multiValued":true},"map":{"group":"location","label":"Map","order":1},"region":{"group":"location","label":"Region","order":2},"start":{"group":"location","label":"Start","order":3},"end":{"group":"location","label":"End","order":4},"strand":{"group":"location","label":"Strand","order":5},"GO__ancestors":{"group":"hierarchical","label":"GO terms","multiValued":true,"order":1},"PO__ancestors":{"group":"hierarchical","label":"PO terms","multiValued":true,"order":2},"TO__ancestors":{"group":"hierarchical","label":"TO terms","multiValued":true,"order":3},"QTL_TO__ancestors":{"group":"hierarchical","label":"QTL traits (TO)","multiValued":true,"order":7},"domains__ancestors":{"group":"hierarchical","label":"Domains","multiValued":true,"order":4},"taxonomy__ancestors":{"group":"hierarchical","label":"Taxonomy","multiValued":true,"order":6},"supertree_attr_s":{"group":"homology","label":"Supertree","order":13},"gene_tree_root_taxon_id":{"group":"homology","label":"Gene tree root taxon","order":12},"protein__length":{"group":"structure","label":"Protein length"},"transcript__count":{"group":"structure","label":"Transcript count"},"transcript__exons":{"group":"structure","label":"Exon count"},"transcript__length":{"group":"structure","label":"Transcript length"},"expressed_in_gxa_attr_ss":{"group":"expression","label":"Expressed in GXA","multiValued":true},"MAKER__AED__attr_f":{"group":"MAKER","label":"AED","description":"Annotation Edit Distance"},"MAKER__QI1__attr_i":{"group":"MAKER","label":"QI1: Length of the 5\' UTR"},"MAKER__QI2__attr_f":{"group":"MAKER","label":"QI2: Fraction of splice sites confirmed by EST"},"MAKER__QI3__attr_f":{"group":"MAKER","label":"QI3: Fraction of exons overlapping an EST"},"MAKER__QI4__attr_f":{"group":"MAKER","label":"QI4: Fraction of exons overlapping EST or protein"},"MAKER__QI5__attr_f":{"group":"MAKER","label":"QI5: Fraction of splice sites confirmed by SNAP"},"MAKER__QI6__attr_f":{"group":"MAKER","label":"QI6: Fraction of exons overlapping a SNAP"},"MAKER__QI7__attr_i":{"group":"MAKER","label":"QI7: Number of exons in the mRNA"},"MAKER__QI8__attr_i":{"group":"MAKER","label":"QI8: Length of the 3\' UTR"},"MAKER__QI9__attr_i":{"group":"MAKER","label":"QI9: Length of the protein sequence"},"homology__all_orthologs":{"group":"homology","label":"All orthologs","multiValued":true,"order":15},"homology__ortholog_one2one":{"group":"homology","label":"1:1 orthologs","multiValued":true,"order":16},"homology__ortholog_one2many":{"group":"homology","label":"1:many orthologs","multiValued":true,"order":17},"homology__ortholog_many2many":{"group":"homology","label":"Many:many orthologs","multiValued":true,"order":18},"homology__syntenic_ortholog_one2one":{"group":"homology","label":"Syntenic 1:1 orthologs","multiValued":true,"order":19},"homology__within_species_paralog":{"group":"homology","label":"Within-species paralogs","multiValued":true,"order":20},"pathways__ancestors":{"group":"hierarchical","label":"Pathways","multiValued":true,"order":5},"canonical_transcript__attr_s":{"group":"structure","label":"Canonical transcript ID"}},"hidden":["_version_","_terms","score","gene_idx","gene_idx_multi","species_idx","compara_idx","compara_idx_multi","_id","annotations","bins","gene_structure","homology","location","xrefs","domain_roots","familyRoot__ancestors","taxonomy__ancestors","capabilities","saved_search"]}');
2080
2110
 
2081
2111
 
2082
2112
  const $49d5cbca2ec74b2f$export$428c2f647a2a7545 = {
@@ -2379,14 +2409,13 @@ function $0f839422d0d8c772$var$buildSpeciesNameIndex(grameneMaps) {
2379
2409
  function $0f839422d0d8c772$var$fieldExperimentId(name) {
2380
2410
  let m = name.match(/^(\w+?)_g\d+__expr$/);
2381
2411
  if (m) return m[1].replace(/_/g, '-');
2382
- m = name.match(/^(\w+?)_g\d+_g\d+_(pval|logfc|l2fc)_attr_[a-z]$/);
2412
+ m = name.match(/^(\w+?)_g\d+_g\d+_(pval|l2fc)_attr_[a-z]$/);
2383
2413
  if (m) return m[1].replace(/_/g, '-');
2384
2414
  return null;
2385
2415
  }
2386
2416
  const $0f839422d0d8c772$var$STAT_RANK = {
2387
2417
  l2fc: 0,
2388
- logfc: 1,
2389
- pval: 2
2418
+ pval: 1
2390
2419
  };
2391
2420
  function $0f839422d0d8c772$var$collapseDiffExprInSubgroups(subgroups, fieldsOut, collator) {
2392
2421
  for (const taxGroup of subgroups)for (const expGroup of taxGroup.subgroups || []){
@@ -2423,7 +2452,7 @@ function $0f839422d0d8c772$var$collapseDiffExprInSubgroups(subgroups, fieldsOut,
2423
2452
  });
2424
2453
  const rep = names[0];
2425
2454
  const repEntry = fieldsOut[rep];
2426
- const label = (repEntry.label || rep).replace(/\s+\((?:pval|logfc|l2fc)\)/, '');
2455
+ const label = (repEntry.label || rep).replace(/\s+\((?:pval|l2fc)\)/, '');
2427
2456
  fieldsOut[rep] = {
2428
2457
  ...repEntry,
2429
2458
  label: label,
@@ -2726,15 +2755,12 @@ $0f839422d0d8c772$var$grameneFieldCatalog.selectFieldCatalog = (0, $gXNCa$reduxb
2726
2755
  const present = new Set(fieldNames);
2727
2756
  // Build a synthetic doc from the discovered field names; values carry the
2728
2757
  // right JS type so inferType picks the correct multiValued/type (arrays
2729
- // for multi-valued fields, scalars otherwise). Derive pval/logfc
2730
- // companions from every l2fc field.
2758
+ // for multi-valued fields, scalars otherwise). Derive the pval companion
2759
+ // from every l2fc field.
2731
2760
  const doc = {};
2732
2761
  for (const name of present){
2733
2762
  doc[name] = $0f839422d0d8c772$var$synthesizedValue(name);
2734
- if (/_l2fc_attr_f$/.test(name)) {
2735
- doc[name.replace('_l2fc_', '_pval_')] = 0;
2736
- doc[name.replace('_l2fc_', '_logfc_')] = 0;
2737
- }
2763
+ if (/_l2fc_attr_f$/.test(name)) doc[name.replace('_l2fc_', '_pval_')] = 0;
2738
2764
  }
2739
2765
  const catalog = $0f839422d0d8c772$var$buildCatalog([
2740
2766
  doc
@@ -2749,7 +2775,7 @@ var $0f839422d0d8c772$export$2e2bcd8739ae039 = $0f839422d0d8c772$var$grameneFiel
2749
2775
 
2750
2776
 
2751
2777
  const $6d28e8e62a4d602f$var$EXPR_FIELD_RE = /^(E[-_][A-Za-z0-9_-]+?)_g(\d+)__expr$/;
2752
- const $6d28e8e62a4d602f$var$DIFFEXPR_FIELD_RE = /^(E[-_][A-Za-z0-9_-]+?)_g(\d+)_g(\d+)_(pval|logfc|l2fc)_attr_([a-z])$/;
2778
+ const $6d28e8e62a4d602f$var$DIFFEXPR_FIELD_RE = /^(E[-_][A-Za-z0-9_-]+?)_g(\d+)_g(\d+)_(pval|l2fc)_attr_([a-z])$/;
2753
2779
  const $6d28e8e62a4d602f$export$adbe84e3322ddf23 = [
2754
2780
  'experiment',
2755
2781
  'experiment_name',
@@ -2874,7 +2900,7 @@ function $6d28e8e62a4d602f$export$ba4cd509d0763838(doc, diffExpressionFields, ex
2874
2900
  byContrast.set(key, entry);
2875
2901
  }
2876
2902
  if (parsed.stat === 'pval') entry.pval = val;
2877
- else entry.l2fc = val; // l2fc or logfc
2903
+ else entry.l2fc = val;
2878
2904
  }
2879
2905
  const maxPval = cutoffs && cutoffs.diffMaxPval;
2880
2906
  const maxPvalActive = maxPval !== null && maxPval !== undefined && maxPval !== '' && Number.isFinite(+maxPval);
@@ -3769,13 +3795,21 @@ const $1508f5a42be6e7b5$var$exporter = {
3769
3795
  var $1508f5a42be6e7b5$export$2e2bcd8739ae039 = $1508f5a42be6e7b5$var$exporter;
3770
3796
 
3771
3797
 
3798
+
3772
3799
  const $c921a0d2b34aadb6$var$ONT_KEYS = [
3773
3800
  'GO',
3774
3801
  'PO',
3775
3802
  'TO',
3776
3803
  'domains'
3777
3804
  ];
3778
- const $c921a0d2b34aadb6$var$BATCH_SIZE = 200;
3805
+ // Dedicated IndexedDB store for ontology records. The full term set per
3806
+ // ontology is stable enough to keep around indefinitely (default maxAge of
3807
+ // `Infinity`), and we don't want it to share the short TTL of the app-wide
3808
+ // cache configured in demo.js.
3809
+ const $c921a0d2b34aadb6$var$cache = (0, $gXNCa$moneyclip.getConfiguredCache)({
3810
+ version: 1,
3811
+ name: 'gramene_ontologies'
3812
+ });
3779
3813
  const $c921a0d2b34aadb6$var$inflight = {};
3780
3814
  const $c921a0d2b34aadb6$var$ontologies = {
3781
3815
  name: 'ontologies',
@@ -3784,95 +3818,64 @@ const $c921a0d2b34aadb6$var$ontologies = {
3784
3818
  GO: {},
3785
3819
  PO: {},
3786
3820
  TO: {},
3787
- domains: {}
3821
+ domains: {},
3822
+ loaded: {}
3788
3823
  };
3789
3824
  return (state = initialState, { type: type, payload: payload })=>{
3790
3825
  switch(type){
3791
- case 'ONTOLOGY_RECORDS_REQUESTED':
3792
- {
3793
- const { key: key, ids: ids } = payload;
3794
- const bucket = {
3795
- ...state[key]
3796
- };
3797
- for (const id of ids)if (!bucket.hasOwnProperty(id)) bucket[id] = null;
3798
- return {
3799
- ...state,
3800
- [key]: bucket
3801
- };
3802
- }
3803
- case 'ONTOLOGY_RECORDS_RECEIVED':
3804
- {
3805
- const { key: key, records: records } = payload;
3806
- return {
3807
- ...state,
3808
- [key]: {
3809
- ...state[key],
3810
- ...records
3811
- }
3812
- };
3813
- }
3826
+ case 'ONTOLOGY_BULK_LOADED':
3827
+ return {
3828
+ ...state,
3829
+ [payload.key]: payload.records,
3830
+ loaded: {
3831
+ ...state.loaded,
3832
+ [payload.key]: true
3833
+ }
3834
+ };
3814
3835
  default:
3815
3836
  return state;
3816
3837
  }
3817
3838
  };
3818
3839
  },
3819
- doEnsureOntologyRecords: (key, ids)=>({ dispatch: dispatch, store: store })=>{
3840
+ // Signature kept for backward compatibility the `ids` argument is
3841
+ // ignored. On first call per ontology key we load the full term set
3842
+ // (cache hit if available, otherwise `${api}/${key}?rows=-1`) and
3843
+ // dispatch a single bulk load. Subsequent calls are no-ops.
3844
+ doEnsureOntologyRecords: (key, _ids)=>({ dispatch: dispatch, store: store })=>{
3820
3845
  if (!$c921a0d2b34aadb6$var$ONT_KEYS.includes(key)) return Promise.resolve();
3821
- if (!Array.isArray(ids) || ids.length === 0) return Promise.resolve();
3822
- const existing = store.selectOntologies()[key] || {};
3823
- const missing = [];
3824
- for (const id of ids){
3825
- if (id == null) continue;
3826
- const idNum = +id;
3827
- if (!existing.hasOwnProperty(idNum) && !($c921a0d2b34aadb6$var$inflight[key] && $c921a0d2b34aadb6$var$inflight[key].has(idNum))) missing.push(idNum);
3828
- }
3829
- if (missing.length === 0) return Promise.resolve();
3830
- if (!$c921a0d2b34aadb6$var$inflight[key]) $c921a0d2b34aadb6$var$inflight[key] = new Set();
3831
- for (const id of missing)$c921a0d2b34aadb6$var$inflight[key].add(id);
3832
- dispatch({
3833
- type: 'ONTOLOGY_RECORDS_REQUESTED',
3834
- payload: {
3835
- key: key,
3836
- ids: missing
3837
- }
3838
- });
3839
- const api = store.selectGrameneAPI();
3840
- const batches = [];
3841
- for(let i = 0; i < missing.length; i += $c921a0d2b34aadb6$var$BATCH_SIZE)batches.push(missing.slice(i, i + $c921a0d2b34aadb6$var$BATCH_SIZE));
3842
- const fetchBatch = (batch)=>{
3843
- const idList = batch.length === 1 ? `${batch[0]},0` : batch.join(',');
3844
- return fetch(`${api}/${key}?idList=${idList}&rows=${batch.length + 1}`).then((r)=>r.json()).then((docs)=>{
3845
- const records = {};
3846
- for (const d of docs || [])if (d && d._id != null) records[d._id] = d;
3847
- for (const id of batch)if (!records.hasOwnProperty(id)) records[id] = {
3848
- _id: id,
3849
- missing: true
3850
- };
3846
+ const state = store.selectOntologies();
3847
+ if (state.loaded && state.loaded[key]) return Promise.resolve();
3848
+ if ($c921a0d2b34aadb6$var$inflight[key]) return $c921a0d2b34aadb6$var$inflight[key];
3849
+ $c921a0d2b34aadb6$var$inflight[key] = $c921a0d2b34aadb6$var$cache.get(key).then((cached)=>{
3850
+ if (cached) {
3851
3851
  dispatch({
3852
- type: 'ONTOLOGY_RECORDS_RECEIVED',
3852
+ type: 'ONTOLOGY_BULK_LOADED',
3853
3853
  payload: {
3854
3854
  key: key,
3855
- records: records
3855
+ records: cached
3856
3856
  }
3857
3857
  });
3858
- }).catch(()=>{
3858
+ return;
3859
+ }
3860
+ const api = store.selectGrameneAPI();
3861
+ return fetch(`${api}/${key}?rows=-1`).then((r)=>r.json()).then((docs)=>{
3859
3862
  const records = {};
3860
- for (const id of batch)records[id] = {
3861
- _id: id,
3862
- missing: true
3863
- };
3863
+ for (const d of docs || [])if (d && d._id != null) records[d._id] = d;
3864
3864
  dispatch({
3865
- type: 'ONTOLOGY_RECORDS_RECEIVED',
3865
+ type: 'ONTOLOGY_BULK_LOADED',
3866
3866
  payload: {
3867
3867
  key: key,
3868
3868
  records: records
3869
3869
  }
3870
3870
  });
3871
- }).finally(()=>{
3872
- for (const id of batch)$c921a0d2b34aadb6$var$inflight[key].delete(id);
3871
+ $c921a0d2b34aadb6$var$cache.set(key, records).catch((e)=>console.warn('Failed to cache ontology', key, e));
3873
3872
  });
3874
- };
3875
- return Promise.all(batches.map(fetchBatch));
3873
+ }).catch((err)=>{
3874
+ console.error(`Failed to load ontology ${key}`, err);
3875
+ }).finally(()=>{
3876
+ delete $c921a0d2b34aadb6$var$inflight[key];
3877
+ });
3878
+ return $c921a0d2b34aadb6$var$inflight[key];
3876
3879
  },
3877
3880
  selectOntologies: (state)=>state.ontologies
3878
3881
  };
@@ -4447,232 +4450,910 @@ const $4f15cd8a7d970b18$var$exprViz = {
4447
4450
  var $4f15cd8a7d970b18$export$2e2bcd8739ae039 = $4f15cd8a7d970b18$var$exprViz;
4448
4451
 
4449
4452
 
4450
- var $5df6c55c1bef3469$export$2e2bcd8739ae039 = [
4451
- ...(0, $9d9aeaf9299e61a1$export$2e2bcd8739ae039),
4452
- (0, $671312b287158a8a$export$2e2bcd8739ae039),
4453
- (0, $af4441dd29af05df$export$2e2bcd8739ae039),
4454
- (0, $24971af0a229e0e3$export$2e2bcd8739ae039),
4455
- (0, $0d54502f6cafe273$export$2e2bcd8739ae039),
4456
- (0, $0f839422d0d8c772$export$2e2bcd8739ae039),
4457
- (0, $1508f5a42be6e7b5$export$2e2bcd8739ae039),
4458
- (0, $c921a0d2b34aadb6$export$2e2bcd8739ae039),
4459
- (0, $4f15cd8a7d970b18$export$2e2bcd8739ae039)
4460
- ];
4461
-
4462
-
4463
-
4464
4453
 
4465
-
4466
- const $2fec4872fbf7ebd2$var$Gene = ({ gene: gene })=>{
4467
- return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
4468
- className: "row",
4469
- children: gene.id
4470
- });
4471
- };
4472
- const $2fec4872fbf7ebd2$var$Genes = (results, rows, doChangeQuantity)=>{
4473
- if (results && results.numFound > 0) {
4474
- const moreButton = results.numFound > rows ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("button", {
4475
- onClick: (e)=>doChangeQuantity('Genes', 20),
4476
- children: "more"
4477
- }) : '';
4478
- const fewerButton = rows > 20 ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("button", {
4479
- onClick: (e)=>doChangeQuantity('Genes', -20),
4480
- children: "fewer"
4481
- }) : '';
4482
- const docsToShow = results.response.docs.slice(0, rows);
4483
- return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
4484
- id: "Genes",
4485
- className: "container mb40 anchor",
4486
- children: [
4487
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
4488
- className: "fancy-title mb40",
4489
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
4490
- children: "Genes"
4491
- })
4492
- }),
4493
- docsToShow.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Gene, {
4494
- gene: doc
4495
- }, idx)),
4496
- fewerButton,
4497
- moreButton
4498
- ]
4499
- });
4454
+ // Gene Set Enrichment Analysis bundle.
4455
+ //
4456
+ // For each species tab we run:
4457
+ // foreground: q=<filters>&fq=taxon_id:<tid>&rows=0 + facet on six __ancestors fields
4458
+ // background: q=taxon_id:<tid>&rows=0 + same facets (cached forever per tid)
4459
+ //
4460
+ // Per-ontology denominators (n_ont, N_ont) come from the root term's facet
4461
+ // count, since every annotated gene carries the root in __ancestors. Roots
4462
+ // are identified once ontology records are loaded by picking the ancestor
4463
+ // (or self) with the highest background facet count this works for
4464
+ // multi-rooted GO (BP/MF/CC) and for the rice-rooted pathway tree.
4465
+ //
4466
+ // Enrichment uses the upper-tail Fisher exact (hypergeometric); p-values
4467
+ // are corrected per ontology with Benjamini–Hochberg. The "most-specific"
4468
+ // collapse is applied AFTER BH so a parent term that's significant on its
4469
+ // own is preserved even when none of its children clear the cutoff.
4470
+ const $0736cb5a41609896$var$ONTOLOGIES = [
4471
+ {
4472
+ key: 'GO',
4473
+ field: 'GO__ancestors',
4474
+ label: 'Gene Ontology',
4475
+ bucket: 'GO'
4476
+ },
4477
+ {
4478
+ key: 'PO',
4479
+ field: 'PO__ancestors',
4480
+ label: 'Plant Ontology',
4481
+ bucket: 'PO'
4482
+ },
4483
+ {
4484
+ key: 'TO',
4485
+ field: 'TO__ancestors',
4486
+ label: 'Trait Ontology',
4487
+ bucket: 'TO'
4488
+ },
4489
+ {
4490
+ key: 'QTL_TO',
4491
+ field: 'QTL_TO__ancestors',
4492
+ label: 'QTL Traits (TO)',
4493
+ bucket: 'TO'
4494
+ },
4495
+ {
4496
+ key: 'domains',
4497
+ field: 'domains__ancestors',
4498
+ label: 'InterPro Domains',
4499
+ bucket: 'domains'
4500
+ },
4501
+ {
4502
+ key: 'pathways',
4503
+ field: 'pathways__ancestors',
4504
+ label: 'Pathways',
4505
+ bucket: null
4500
4506
  }
4501
- };
4502
- const $2fec4872fbf7ebd2$var$Pathway = ({ pathway: pathway })=>{
4503
- return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
4504
- className: "row",
4505
- children: pathway.name
4506
- });
4507
- };
4508
- const $2fec4872fbf7ebd2$var$Pathways = (results)=>{
4509
- if (results && results.numFound > 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
4510
- id: "Pathways",
4511
- className: "container mb40 anchor",
4512
- children: [
4513
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
4514
- className: "fancy-title",
4515
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
4516
- children: "Pathways"
4517
- })
4518
- }),
4519
- results.pathways.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Pathway, {
4520
- pathway: doc
4521
- }, idx))
4522
- ]
4523
- });
4524
- };
4525
- const $2fec4872fbf7ebd2$var$Domain = ({ domain: domain })=>{
4526
- return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
4527
- className: "row",
4528
- children: domain.id
4529
- });
4530
- };
4531
- const $2fec4872fbf7ebd2$var$Domains = (results)=>{
4532
- if (results && results.numFound > 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
4533
- id: "Domains",
4534
- className: "container mb40 anchor",
4535
- children: [
4536
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
4537
- className: "fancy-title mb40",
4538
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
4539
- children: "Domains"
4540
- })
4541
- }),
4542
- results.domains.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Domain, {
4543
- domain: doc
4544
- }, idx))
4545
- ]
4546
- });
4547
- };
4548
- const $2fec4872fbf7ebd2$var$Taxon = ({ taxon: taxon })=>{
4549
- return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
4550
- className: "row",
4551
- children: taxon.id
4552
- });
4553
- };
4554
- const $2fec4872fbf7ebd2$var$Species = (results)=>{
4555
- if (results && results.numFound > 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
4556
- id: "Species",
4557
- className: "container mb40 anchor",
4558
- children: [
4559
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
4560
- className: "fancy-title mb40",
4561
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
4562
- children: "Species"
4563
- })
4564
- }),
4565
- results.taxonomy.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Taxon, {
4566
- taxon: doc
4567
- }, idx))
4568
- ]
4569
- });
4570
- };
4571
- const $2fec4872fbf7ebd2$var$ResultList = ({ grameneGenes: grameneGenes, grameneDomains: grameneDomains, gramenePathways: gramenePathways, grameneTaxonomy: grameneTaxonomy, searchUI: searchUI, searchUpdated: searchUpdated, doChangeQuantity: doChangeQuantity })=>{
4572
- if (searchUI.Gramene) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
4573
- id: "gramene",
4574
- className: "row",
4575
- children: [
4576
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
4577
- className: "fancy-title pt50",
4578
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h3", {
4579
- children: "Gramene search results"
4580
- })
4581
- }),
4582
- searchUI.Genes && $2fec4872fbf7ebd2$var$Genes(grameneGenes, searchUI.rows.Genes, doChangeQuantity),
4583
- searchUI.Domains && $2fec4872fbf7ebd2$var$Domains(grameneDomains),
4584
- searchUI.Pathways && $2fec4872fbf7ebd2$var$Pathways(gramenePathways),
4585
- searchUI.Species && $2fec4872fbf7ebd2$var$Species(grameneTaxonomy)
4586
- ]
4587
- });
4588
- else return null;
4589
- };
4590
- var $2fec4872fbf7ebd2$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneGenes', 'selectGrameneDomains', 'selectGramenePathways', 'selectGrameneTaxonomy', 'selectSearchUI', 'selectSearchUpdated', 'doChangeQuantity', $2fec4872fbf7ebd2$var$ResultList);
4591
-
4592
-
4593
-
4594
-
4595
-
4596
- const $27617dbc24e7faf0$var$getStatus = (cat, results, isChecked, toggle)=>{
4597
- const tally = results ? results.numFound : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("img", {
4598
- src: "/static/images/dna_spinner.svg"
4599
- });
4600
- return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
4601
- className: "category-leaf",
4602
- children: [
4603
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("input", {
4604
- type: "checkbox",
4605
- checked: isChecked,
4606
- onChange: (e)=>toggle(cat)
4607
- }),
4608
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("a", {
4609
- "data-scroll": true,
4610
- href: `#${cat}`,
4611
- className: "nav-link active",
4612
- children: [
4613
- cat,
4614
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
4615
- style: {
4616
- float: "right"
4507
+ ];
4508
+ const $0736cb5a41609896$var$FACET_PARAMS = $0736cb5a41609896$var$ONTOLOGIES.map((o)=>`facet.field=${encodeURIComponent(`{!facet.limit=10000 facet.mincount=1 key=${o.key}}${o.field}`)}`).join('&');
4509
+ const $0736cb5a41609896$var$fgPending = {};
4510
+ const $0736cb5a41609896$var$bgPending = {};
4511
+ function $0736cb5a41609896$var$fgSig(q, taxon) {
4512
+ return `${q}|${taxon}`;
4513
+ }
4514
+ function $0736cb5a41609896$var$bgSig(taxon) {
4515
+ return `bg|${taxon}`;
4516
+ }
4517
+ function $0736cb5a41609896$var$parseFacets(json) {
4518
+ const out = {};
4519
+ const ff = json && json.facet_counts && json.facet_counts.facet_fields || {};
4520
+ for (const o of $0736cb5a41609896$var$ONTOLOGIES){
4521
+ const arr = ff[o.key] || [];
4522
+ const map = {};
4523
+ for(let i = 0; i < arr.length; i += 2)map[+arr[i]] = +arr[i + 1];
4524
+ out[o.key] = map;
4525
+ }
4526
+ return out;
4527
+ }
4528
+ const $0736cb5a41609896$var$gsea = {
4529
+ name: 'gsea',
4530
+ // Background facet counts depend only on the species — they're invariant
4531
+ // across filter changes and across sessions, so we persist whenever a bg
4532
+ // fetch completes. Foreground state piggybacks on the same write but is
4533
+ // self-invalidated by the signature check in the reactor.
4534
+ persistActions: [
4535
+ 'GSEA_BG_SUCCEEDED'
4536
+ ],
4537
+ getReducer: ()=>{
4538
+ const initialState = {
4539
+ activeTaxon: null,
4540
+ byTaxon: {},
4541
+ ui: {
4542
+ pAdjCutoff: 0.05,
4543
+ minK: 2,
4544
+ mostSpecific: true,
4545
+ ontology: 'all',
4546
+ search: ''
4547
+ }
4548
+ };
4549
+ function ensureTaxon(state, tid) {
4550
+ if (state.byTaxon[tid]) return state;
4551
+ return {
4552
+ ...state,
4553
+ byTaxon: {
4554
+ ...state.byTaxon,
4555
+ [tid]: {
4556
+ fg: {
4557
+ status: 'idle',
4558
+ signature: null,
4559
+ requestId: 0,
4560
+ terms: null,
4561
+ numFound: 0,
4562
+ error: null
4617
4563
  },
4618
- children: tally
4619
- })
4620
- ]
4621
- })
4622
- ]
4623
- });
4624
- };
4625
- const $27617dbc24e7faf0$var$ResultSummary = ({ grameneGenes: grameneGenes, gramenePathways: gramenePathways, grameneDomains: grameneDomains, grameneTaxonomy: grameneTaxonomy, searchUI: searchUI, searchUpdated: searchUpdated, doToggleCategory: doToggleCategory })=>{
4626
- const status = grameneGenes ? grameneGenes.numFound : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("img", {
4627
- src: "/static/images/dna_spinner.svg"
4628
- });
4629
- if (searchUI.Gramene) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
4630
- className: "active category-expanded",
4631
- children: [
4632
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("a", {
4633
- onClick: (e)=>doToggleCategory('Gramene'),
4634
- children: [
4635
- "Gramene Search",
4636
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
4637
- style: {
4638
- float: "right"
4639
- },
4640
- children: status
4641
- })
4642
- ]
4564
+ bg: {
4565
+ status: 'idle',
4566
+ signature: null,
4567
+ requestId: 0,
4568
+ terms: null,
4569
+ numFound: 0,
4570
+ error: null
4571
+ }
4572
+ }
4573
+ }
4574
+ };
4575
+ }
4576
+ return (state = initialState, { type: type, payload: payload })=>{
4577
+ switch(type){
4578
+ case 'GSEA_ACTIVE_TAXON_SET':
4579
+ return {
4580
+ ...ensureTaxon(state, payload),
4581
+ activeTaxon: payload
4582
+ };
4583
+ case 'GSEA_UI_SET':
4584
+ return {
4585
+ ...state,
4586
+ ui: {
4587
+ ...state.ui,
4588
+ ...payload || {}
4589
+ }
4590
+ };
4591
+ case 'GSEA_FG_STARTED':
4592
+ {
4593
+ const next = ensureTaxon(state, payload.taxon);
4594
+ const t = next.byTaxon[payload.taxon];
4595
+ return {
4596
+ ...next,
4597
+ byTaxon: {
4598
+ ...next.byTaxon,
4599
+ [payload.taxon]: {
4600
+ ...t,
4601
+ fg: {
4602
+ status: 'loading',
4603
+ signature: payload.signature,
4604
+ requestId: payload.requestId,
4605
+ terms: t.fg.terms,
4606
+ numFound: t.fg.numFound,
4607
+ error: null
4608
+ }
4609
+ }
4610
+ }
4611
+ };
4612
+ }
4613
+ case 'GSEA_FG_SUCCEEDED':
4614
+ {
4615
+ const t = state.byTaxon[payload.taxon];
4616
+ if (!t || payload.requestId !== t.fg.requestId) return state;
4617
+ return {
4618
+ ...state,
4619
+ byTaxon: {
4620
+ ...state.byTaxon,
4621
+ [payload.taxon]: {
4622
+ ...t,
4623
+ fg: {
4624
+ ...t.fg,
4625
+ status: 'ready',
4626
+ terms: payload.terms,
4627
+ numFound: payload.numFound
4628
+ }
4629
+ }
4630
+ }
4631
+ };
4632
+ }
4633
+ case 'GSEA_FG_FAILED':
4634
+ {
4635
+ const t = state.byTaxon[payload.taxon];
4636
+ if (!t || payload.requestId !== t.fg.requestId) return state;
4637
+ return {
4638
+ ...state,
4639
+ byTaxon: {
4640
+ ...state.byTaxon,
4641
+ [payload.taxon]: {
4642
+ ...t,
4643
+ fg: {
4644
+ ...t.fg,
4645
+ status: 'error',
4646
+ error: payload.error
4647
+ }
4648
+ }
4649
+ }
4650
+ };
4651
+ }
4652
+ case 'GSEA_BG_STARTED':
4653
+ {
4654
+ const next = ensureTaxon(state, payload.taxon);
4655
+ const t = next.byTaxon[payload.taxon];
4656
+ return {
4657
+ ...next,
4658
+ byTaxon: {
4659
+ ...next.byTaxon,
4660
+ [payload.taxon]: {
4661
+ ...t,
4662
+ bg: {
4663
+ status: 'loading',
4664
+ signature: payload.signature,
4665
+ requestId: payload.requestId,
4666
+ terms: t.bg.terms,
4667
+ numFound: t.bg.numFound,
4668
+ error: null
4669
+ }
4670
+ }
4671
+ }
4672
+ };
4673
+ }
4674
+ case 'GSEA_BG_SUCCEEDED':
4675
+ {
4676
+ const t = state.byTaxon[payload.taxon];
4677
+ if (!t || payload.requestId !== t.bg.requestId) return state;
4678
+ return {
4679
+ ...state,
4680
+ byTaxon: {
4681
+ ...state.byTaxon,
4682
+ [payload.taxon]: {
4683
+ ...t,
4684
+ bg: {
4685
+ ...t.bg,
4686
+ status: 'ready',
4687
+ terms: payload.terms,
4688
+ numFound: payload.numFound
4689
+ }
4690
+ }
4691
+ }
4692
+ };
4693
+ }
4694
+ case 'GSEA_BG_FAILED':
4695
+ {
4696
+ const t = state.byTaxon[payload.taxon];
4697
+ if (!t || payload.requestId !== t.bg.requestId) return state;
4698
+ return {
4699
+ ...state,
4700
+ byTaxon: {
4701
+ ...state.byTaxon,
4702
+ [payload.taxon]: {
4703
+ ...t,
4704
+ bg: {
4705
+ ...t.bg,
4706
+ status: 'error',
4707
+ error: payload.error
4708
+ }
4709
+ }
4710
+ }
4711
+ };
4712
+ }
4713
+ case 'GRAMENE_SEARCH_CLEARED':
4714
+ {
4715
+ // Filters changed — invalidate foreground but keep background
4716
+ // (it depends only on taxon).
4717
+ const newByTaxon = {};
4718
+ for (const tid of Object.keys(state.byTaxon)){
4719
+ const t = state.byTaxon[tid];
4720
+ newByTaxon[tid] = {
4721
+ ...t,
4722
+ fg: {
4723
+ status: 'idle',
4724
+ signature: null,
4725
+ requestId: 0,
4726
+ terms: null,
4727
+ numFound: 0,
4728
+ error: null
4729
+ }
4730
+ };
4731
+ }
4732
+ return {
4733
+ ...state,
4734
+ byTaxon: newByTaxon
4735
+ };
4736
+ }
4737
+ default:
4738
+ return state;
4739
+ }
4740
+ };
4741
+ },
4742
+ doSetGseaActiveTaxon: (tid)=>({ dispatch: dispatch })=>dispatch({
4743
+ type: 'GSEA_ACTIVE_TAXON_SET',
4744
+ payload: tid
4643
4745
  }),
4644
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("ul", {
4645
- className: "list-unstyled",
4646
- children: [
4647
- $27617dbc24e7faf0$var$getStatus('Genes', grameneGenes, searchUI.Genes, doToggleCategory),
4648
- $27617dbc24e7faf0$var$getStatus('Domains', grameneDomains, searchUI.Domains, doToggleCategory),
4649
- $27617dbc24e7faf0$var$getStatus('Pathways', gramenePathways, searchUI.Pathways, doToggleCategory),
4650
- $27617dbc24e7faf0$var$getStatus('Species', grameneTaxonomy, searchUI.Species, doToggleCategory)
4651
- ]
4652
- })
4653
- ]
4654
- });
4655
- else return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("li", {
4656
- className: "active category-collapsed",
4657
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("a", {
4658
- onClick: (e)=>doToggleCategory('Gramene'),
4659
- children: [
4660
- "Gramene Search",
4661
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
4662
- style: {
4663
- float: "right"
4664
- },
4665
- children: status
4666
- })
4746
+ doSetGseaUI: (patch)=>({ dispatch: dispatch })=>dispatch({
4747
+ type: 'GSEA_UI_SET',
4748
+ payload: patch
4749
+ }),
4750
+ doFetchGseaForeground: (taxon)=>({ dispatch: dispatch, store: store })=>{
4751
+ const q = store.selectGrameneFiltersQueryString();
4752
+ const signature = $0736cb5a41609896$var$fgSig(q, taxon);
4753
+ const state = store.selectGsea();
4754
+ const t = state.byTaxon[taxon];
4755
+ if (t && t.fg.signature === signature && (t.fg.status === 'loading' || t.fg.status === 'ready')) return;
4756
+ const requestId = $0736cb5a41609896$var$fgPending[taxon] = ($0736cb5a41609896$var$fgPending[taxon] || 0) + 1;
4757
+ dispatch({
4758
+ type: 'GSEA_FG_STARTED',
4759
+ payload: {
4760
+ taxon: taxon,
4761
+ signature: signature,
4762
+ requestId: requestId
4763
+ }
4764
+ });
4765
+ const api = store.selectGrameneAPI();
4766
+ const url = `${api}/search?q=${q}&fq=taxon_id:${taxon}&rows=0&facet=true&${$0736cb5a41609896$var$FACET_PARAMS}`;
4767
+ fetch(url).then((r)=>r.json()).then((json)=>{
4768
+ if (requestId !== $0736cb5a41609896$var$fgPending[taxon]) return;
4769
+ const terms = $0736cb5a41609896$var$parseFacets(json);
4770
+ const numFound = json && json.response && json.response.numFound || 0;
4771
+ dispatch({
4772
+ type: 'GSEA_FG_SUCCEEDED',
4773
+ payload: {
4774
+ taxon: taxon,
4775
+ requestId: requestId,
4776
+ terms: terms,
4777
+ numFound: numFound
4778
+ }
4779
+ });
4780
+ store.doEnsureGseaTermRecords(taxon);
4781
+ }).catch((err)=>{
4782
+ if (requestId !== $0736cb5a41609896$var$fgPending[taxon]) return;
4783
+ dispatch({
4784
+ type: 'GSEA_FG_FAILED',
4785
+ payload: {
4786
+ taxon: taxon,
4787
+ requestId: requestId,
4788
+ error: String(err)
4789
+ }
4790
+ });
4791
+ });
4792
+ },
4793
+ doFetchGseaBackground: (taxon)=>({ dispatch: dispatch, store: store })=>{
4794
+ const signature = $0736cb5a41609896$var$bgSig(taxon);
4795
+ const state = store.selectGsea();
4796
+ const t = state.byTaxon[taxon];
4797
+ if (t && t.bg.signature === signature && (t.bg.status === 'loading' || t.bg.status === 'ready')) return;
4798
+ const requestId = $0736cb5a41609896$var$bgPending[taxon] = ($0736cb5a41609896$var$bgPending[taxon] || 0) + 1;
4799
+ dispatch({
4800
+ type: 'GSEA_BG_STARTED',
4801
+ payload: {
4802
+ taxon: taxon,
4803
+ signature: signature,
4804
+ requestId: requestId
4805
+ }
4806
+ });
4807
+ const api = store.selectGrameneAPI();
4808
+ const url = `${api}/search?q=taxon_id:${taxon}&rows=0&facet=true&${$0736cb5a41609896$var$FACET_PARAMS}`;
4809
+ fetch(url).then((r)=>r.json()).then((json)=>{
4810
+ if (requestId !== $0736cb5a41609896$var$bgPending[taxon]) return;
4811
+ const terms = $0736cb5a41609896$var$parseFacets(json);
4812
+ const numFound = json && json.response && json.response.numFound || 0;
4813
+ dispatch({
4814
+ type: 'GSEA_BG_SUCCEEDED',
4815
+ payload: {
4816
+ taxon: taxon,
4817
+ requestId: requestId,
4818
+ terms: terms,
4819
+ numFound: numFound
4820
+ }
4821
+ });
4822
+ store.doEnsureGseaTermRecords(taxon);
4823
+ }).catch((err)=>{
4824
+ if (requestId !== $0736cb5a41609896$var$bgPending[taxon]) return;
4825
+ dispatch({
4826
+ type: 'GSEA_BG_FAILED',
4827
+ payload: {
4828
+ taxon: taxon,
4829
+ requestId: requestId,
4830
+ error: String(err)
4831
+ }
4832
+ });
4833
+ });
4834
+ },
4835
+ doEnsureGseaTermRecords: (taxon)=>({ store: store })=>{
4836
+ const state = store.selectGsea();
4837
+ if (!state.byTaxon[taxon]) return;
4838
+ // The ontologies and pathways bundles bulk-load + persist on first
4839
+ // request; we just need to nudge each one once.
4840
+ const buckets = new Set();
4841
+ for (const o of $0736cb5a41609896$var$ONTOLOGIES)if (o.bucket) buckets.add(o.bucket);
4842
+ for (const k of buckets)store.doEnsureOntologyRecords(k);
4843
+ if (store.doRequestGramenePathways) store.doRequestGramenePathways();
4844
+ },
4845
+ reactGseaFetch: (0, $gXNCa$reduxbundler.createSelector)('selectGsea', 'selectGrameneFiltersStatus', 'selectGrameneFiltersQueryString', 'selectGrameneViewsOn', (gs, fStatus, q, viewsOn)=>{
4846
+ if (!viewsOn || !viewsOn.has('gsea')) return;
4847
+ if (fStatus === 'init') return;
4848
+ const tid = gs.activeTaxon;
4849
+ if (!tid) return;
4850
+ const t = gs.byTaxon[tid];
4851
+ if (!t) return;
4852
+ const sig = $0736cb5a41609896$var$fgSig(q, tid);
4853
+ // A 'loading' status from a rehydrated state with no live request is
4854
+ // treated as idle — otherwise we'd deadlock waiting on a fetch that
4855
+ // ended when the previous tab closed.
4856
+ const fgInFlight = t.fg.status === 'loading' && ($0736cb5a41609896$var$fgPending[tid] || 0) === t.fg.requestId && t.fg.requestId > 0;
4857
+ if (t.fg.signature !== sig && !fgInFlight) return {
4858
+ actionCreator: 'doFetchGseaForeground',
4859
+ args: [
4860
+ tid
4667
4861
  ]
4668
- })
4669
- });
4862
+ };
4863
+ const bgInFlight = t.bg.status === 'loading' && ($0736cb5a41609896$var$bgPending[tid] || 0) === t.bg.requestId && t.bg.requestId > 0;
4864
+ if (t.bg.status !== 'ready' && !bgInFlight) return {
4865
+ actionCreator: 'doFetchGseaBackground',
4866
+ args: [
4867
+ tid
4868
+ ]
4869
+ };
4870
+ }),
4871
+ selectGsea: (state)=>state.gsea,
4872
+ selectGseaUI: (state)=>state.gsea.ui,
4873
+ selectGseaOntologyDefs: ()=>$0736cb5a41609896$var$ONTOLOGIES,
4874
+ selectGseaResults: (0, $gXNCa$reduxbundler.createSelector)('selectGsea', 'selectOntologies', 'selectGramenePathways', (gs, ontoBuckets, pathwayDocs)=>{
4875
+ const tid = gs.activeTaxon;
4876
+ if (!tid) return null;
4877
+ const t = gs.byTaxon[tid];
4878
+ if (!t || !t.fg.terms || !t.bg.terms) return null;
4879
+ const ui = gs.ui;
4880
+ const out = {};
4881
+ for (const o of $0736cb5a41609896$var$ONTOLOGIES){
4882
+ const fg = t.fg.terms[o.key] || {};
4883
+ const bg = t.bg.terms[o.key] || {};
4884
+ const recs = o.key === 'pathways' ? pathwayDocs || {} : ontoBuckets && ontoBuckets[o.bucket] || {};
4885
+ // Forest-fallback denominators: when a term is itself a forest root
4886
+ // (no parents in `recs` — common for InterPro), root finding returns
4887
+ // the term and we'd get fold=1 by construction. Use the maximum
4888
+ // counts across the ontology as a proxy for "annotated in this
4889
+ // ontology" instead. For ontologies with a true synthetic root,
4890
+ // these maxima equal the root counts and the answer is unchanged.
4891
+ let maxFg = 0, maxBg = 0;
4892
+ for (const idStr of Object.keys(bg)){
4893
+ const v = bg[idStr];
4894
+ if (v > maxBg) maxBg = v;
4895
+ }
4896
+ for (const idStr of Object.keys(fg)){
4897
+ const v = fg[idStr];
4898
+ if (v > maxFg) maxFg = v;
4899
+ }
4900
+ const rootCache = {};
4901
+ const rootOf = (id)=>{
4902
+ if (rootCache.hasOwnProperty(id)) return rootCache[id];
4903
+ const r = $0736cb5a41609896$var$findRoot(o.key, id, recs, bg);
4904
+ rootCache[id] = r;
4905
+ return r;
4906
+ };
4907
+ const rows = [];
4908
+ for (const idStr of Object.keys(fg)){
4909
+ const id = +idStr;
4910
+ const k = fg[id];
4911
+ const K = bg[id] || 0;
4912
+ if (K < k) continue; // bg should always be >= fg
4913
+ if (k < ui.minK) continue;
4914
+ const termRec = recs && recs[id];
4915
+ if (termRec && (termRec.is_obsolete || termRec.obsolete)) continue;
4916
+ const root = rootOf(id);
4917
+ // If the "root" is the term itself, the term is a forest root in
4918
+ // this ontology — fall back to ontology-wide maxima.
4919
+ const fellBack = +root === id;
4920
+ const n = fellBack ? maxFg : root != null && fg[root] ? fg[root] : k;
4921
+ const N = fellBack ? maxBg : root != null && bg[root] ? bg[root] : K;
4922
+ if (n <= 0 || N <= 0) continue;
4923
+ if (k > n || K > N) continue;
4924
+ const fold = k / n / (K / N);
4925
+ const p = $0736cb5a41609896$var$fisherUpperTail(k, n, K, N);
4926
+ rows.push({
4927
+ ontology: o.key,
4928
+ ontology_label: o.label,
4929
+ term_id: id,
4930
+ field: o.field,
4931
+ k: k,
4932
+ n: n,
4933
+ K: K,
4934
+ N: N,
4935
+ fold: fold,
4936
+ p: p,
4937
+ pAdj: 1,
4938
+ root: root,
4939
+ denomFallback: fellBack
4940
+ });
4941
+ }
4942
+ // GO is split into its three top-level namespaces (BP / MF / CC)
4943
+ // and each is tested as its own ontology — both BH correction and
4944
+ // most-specific collapse run within a namespace. We only split once
4945
+ // ontology records have arrived and root finding has produced
4946
+ // canonical roots; otherwise we'd see a swarm of singleton groups
4947
+ // during the brief loading window between bg landing and records
4948
+ // being fetched.
4949
+ if (o.key === 'GO' && Object.keys(recs).length > 0) {
4950
+ const byRoot = {};
4951
+ for (const r of rows){
4952
+ const k = String(r.root);
4953
+ if (!byRoot[k]) byRoot[k] = [];
4954
+ byRoot[k].push(r);
4955
+ }
4956
+ const rootKeys = Object.keys(byRoot).sort((a, b)=>{
4957
+ const na = $0736cb5a41609896$var$goRootName(recs[+a]) || a;
4958
+ const nb = $0736cb5a41609896$var$goRootName(recs[+b]) || b;
4959
+ return na.localeCompare(nb);
4960
+ });
4961
+ for (const rootKey of rootKeys){
4962
+ const rootRec = recs[+rootKey];
4963
+ const rootName = $0736cb5a41609896$var$goRootName(rootRec);
4964
+ const sectionKey = `GO:${rootKey}`;
4965
+ const sectionLabel = rootName ? `GO: ${$0736cb5a41609896$var$titleCase(rootName)}` : `GO: ${rootKey}`;
4966
+ out[sectionKey] = $0736cb5a41609896$var$finalizeBlock(byRoot[rootKey], o.key, o.field, recs, ui, sectionKey, sectionLabel);
4967
+ }
4968
+ } else out[o.key] = $0736cb5a41609896$var$finalizeBlock(rows, o.key, o.field, recs, ui, o.key, o.label);
4969
+ }
4970
+ return out;
4971
+ })
4670
4972
  };
4671
- var $27617dbc24e7faf0$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneGenes', 'selectGramenePathways', 'selectGrameneDomains', 'selectGrameneTaxonomy', 'selectSearchUI', 'selectSearchUpdated', 'doToggleCategory', $27617dbc24e7faf0$var$ResultSummary);
4672
-
4673
-
4674
-
4675
-
4973
+ function $0736cb5a41609896$var$goRootName(rec) {
4974
+ if (!rec) return '';
4975
+ return rec.name || rec.display_name || rec.namespace || '';
4976
+ }
4977
+ function $0736cb5a41609896$var$titleCase(s) {
4978
+ return String(s).split('_').map((w)=>w ? w[0].toUpperCase() + w.slice(1) : '').join(' ');
4979
+ }
4980
+ // BH correction + most-specific collapse + metadata + final sort, returning
4981
+ // the block descriptor consumed by the view layer.
4982
+ function $0736cb5a41609896$var$finalizeBlock(rows, ontKey, ontField, recs, ui, sectionKey, sectionLabel) {
4983
+ rows.sort((a, b)=>a.p - b.p);
4984
+ const m = rows.length;
4985
+ let prev = 1;
4986
+ for(let i = m - 1; i >= 0; i--){
4987
+ const adj = Math.min(prev, rows[i].p * m / (i + 1));
4988
+ rows[i].pAdj = adj;
4989
+ prev = adj;
4990
+ }
4991
+ const passing = rows.filter((r)=>r.pAdj <= ui.pAdjCutoff);
4992
+ let display = passing;
4993
+ if (ui.mostSpecific) display = $0736cb5a41609896$var$collapseToMostSpecific(ontKey, passing, recs);
4994
+ for (const r of display){
4995
+ const rec = recs && recs[r.term_id];
4996
+ if (rec) {
4997
+ r.term_name = rec.name || rec.display_name || '';
4998
+ r.term_namespace = rec.namespace || rec.type || '';
4999
+ r.term_display_id = rec.id != null ? String(rec.id) : String(r.term_id);
5000
+ } else {
5001
+ r.term_name = '';
5002
+ r.term_namespace = '';
5003
+ r.term_display_id = String(r.term_id);
5004
+ }
5005
+ }
5006
+ display.sort((a, b)=>a.pAdj - b.pAdj || b.fold - a.fold);
5007
+ return {
5008
+ ontology: sectionKey,
5009
+ label: sectionLabel,
5010
+ field: ontField,
5011
+ tested: rows.length,
5012
+ passing: passing.length,
5013
+ rows: display
5014
+ };
5015
+ }
5016
+ // ---------- math helpers ----------
5017
+ const $0736cb5a41609896$var$LF_CACHE = [
5018
+ 0,
5019
+ 0
5020
+ ];
5021
+ function $0736cb5a41609896$var$logFactorial(n) {
5022
+ if (n < $0736cb5a41609896$var$LF_CACHE.length) return $0736cb5a41609896$var$LF_CACHE[n];
5023
+ let lf = $0736cb5a41609896$var$LF_CACHE[$0736cb5a41609896$var$LF_CACHE.length - 1];
5024
+ for(let i = $0736cb5a41609896$var$LF_CACHE.length; i <= n; i++){
5025
+ lf += Math.log(i);
5026
+ $0736cb5a41609896$var$LF_CACHE.push(lf);
5027
+ }
5028
+ return $0736cb5a41609896$var$LF_CACHE[n];
5029
+ }
5030
+ function $0736cb5a41609896$var$logChoose(n, k) {
5031
+ if (k < 0 || k > n) return -Infinity;
5032
+ return $0736cb5a41609896$var$logFactorial(n) - $0736cb5a41609896$var$logFactorial(k) - $0736cb5a41609896$var$logFactorial(n - k);
5033
+ }
5034
+ function $0736cb5a41609896$var$logHypergeom(x, n, K, N) {
5035
+ return $0736cb5a41609896$var$logChoose(K, x) + $0736cb5a41609896$var$logChoose(N - K, n - x) - $0736cb5a41609896$var$logChoose(N, n);
5036
+ }
5037
+ function $0736cb5a41609896$var$logSumExp(a, b) {
5038
+ if (a === -Infinity) return b;
5039
+ if (b === -Infinity) return a;
5040
+ const m = Math.max(a, b);
5041
+ return m + Math.log(Math.exp(a - m) + Math.exp(b - m));
5042
+ }
5043
+ function $0736cb5a41609896$var$fisherUpperTail(k, n, K, N) {
5044
+ const upper = Math.min(n, K);
5045
+ let logP = -Infinity;
5046
+ for(let x = k; x <= upper; x++)logP = $0736cb5a41609896$var$logSumExp(logP, $0736cb5a41609896$var$logHypergeom(x, n, K, N));
5047
+ const p = Math.exp(logP);
5048
+ if (!isFinite(p)) return 1;
5049
+ return Math.min(1, Math.max(0, p));
5050
+ }
5051
+ // ---------- ontology graph helpers ----------
5052
+ // Pick the "root" for a term as the ancestor (or self) with the highest
5053
+ // background facet count. The true root has every annotated gene under it,
5054
+ // so this selects BP/MF/CC for GO terms automatically and the rice root
5055
+ // for pathways without needing per-ontology hardcoded ids.
5056
+ function $0736cb5a41609896$var$findRoot(ontKey, id, recs, bgCounts) {
5057
+ const rec = recs && recs[id];
5058
+ const candidates = new Set([
5059
+ +id
5060
+ ]);
5061
+ if (rec) {
5062
+ if (ontKey === 'pathways') for (const k of Object.keys(rec)){
5063
+ if (k.startsWith('ancestors_') && Array.isArray(rec[k])) for (const a of rec[k])candidates.add(+a);
5064
+ }
5065
+ else if (Array.isArray(rec.ancestors)) for (const a of rec.ancestors)candidates.add(+a);
5066
+ else if (Array.isArray(rec.is_a)) {
5067
+ const stack = rec.is_a.slice();
5068
+ while(stack.length){
5069
+ const cur = +stack.pop();
5070
+ if (candidates.has(cur)) continue;
5071
+ candidates.add(cur);
5072
+ const r = recs[cur];
5073
+ if (r && Array.isArray(r.is_a)) for (const p of r.is_a)stack.push(p);
5074
+ }
5075
+ }
5076
+ }
5077
+ let best = +id;
5078
+ let bestCount = bgCounts[+id] || 0;
5079
+ for (const c of candidates){
5080
+ const cnt = bgCounts[c] || 0;
5081
+ if (cnt > bestCount) {
5082
+ bestCount = cnt;
5083
+ best = c;
5084
+ }
5085
+ }
5086
+ return best;
5087
+ }
5088
+ function $0736cb5a41609896$var$ancestorsOf(ontKey, id, recs) {
5089
+ const out = new Set();
5090
+ const rec = recs && recs[id];
5091
+ if (!rec) return out;
5092
+ if (ontKey === 'pathways') {
5093
+ for (const k of Object.keys(rec))if (k.startsWith('ancestors_') && Array.isArray(rec[k])) {
5094
+ for (const a of rec[k])if (+a !== +id) out.add(+a);
5095
+ }
5096
+ return out;
5097
+ }
5098
+ if (Array.isArray(rec.ancestors)) {
5099
+ for (const a of rec.ancestors)if (+a !== +id) out.add(+a);
5100
+ return out;
5101
+ }
5102
+ if (Array.isArray(rec.is_a)) {
5103
+ const stack = rec.is_a.slice();
5104
+ while(stack.length){
5105
+ const cur = +stack.pop();
5106
+ if (out.has(cur)) continue;
5107
+ out.add(cur);
5108
+ const r = recs[cur];
5109
+ if (r && Array.isArray(r.is_a)) for (const p of r.is_a)stack.push(p);
5110
+ }
5111
+ }
5112
+ return out;
5113
+ }
5114
+ // Drop any term that is an ancestor of another term in the same passing set.
5115
+ // Applied AFTER BH so a parent term can survive when none of its children
5116
+ // pass the p_adj cutoff.
5117
+ function $0736cb5a41609896$var$collapseToMostSpecific(ontKey, rows, recs) {
5118
+ if (!rows || rows.length <= 1) return rows;
5119
+ const inSet = new Set(rows.map((r)=>+r.term_id));
5120
+ const covered = new Set();
5121
+ for (const r of rows){
5122
+ const ancs = $0736cb5a41609896$var$ancestorsOf(ontKey, +r.term_id, recs);
5123
+ for (const a of ancs)if (inSet.has(a)) covered.add(a);
5124
+ }
5125
+ return rows.filter((r)=>!covered.has(+r.term_id));
5126
+ }
5127
+ var $0736cb5a41609896$export$2e2bcd8739ae039 = $0736cb5a41609896$var$gsea;
5128
+
5129
+
5130
+ var $5df6c55c1bef3469$export$2e2bcd8739ae039 = [
5131
+ ...(0, $9d9aeaf9299e61a1$export$2e2bcd8739ae039),
5132
+ (0, $671312b287158a8a$export$2e2bcd8739ae039),
5133
+ (0, $af4441dd29af05df$export$2e2bcd8739ae039),
5134
+ (0, $24971af0a229e0e3$export$2e2bcd8739ae039),
5135
+ (0, $0d54502f6cafe273$export$2e2bcd8739ae039),
5136
+ (0, $0f839422d0d8c772$export$2e2bcd8739ae039),
5137
+ (0, $1508f5a42be6e7b5$export$2e2bcd8739ae039),
5138
+ (0, $c921a0d2b34aadb6$export$2e2bcd8739ae039),
5139
+ (0, $4f15cd8a7d970b18$export$2e2bcd8739ae039),
5140
+ (0, $0736cb5a41609896$export$2e2bcd8739ae039)
5141
+ ];
5142
+
5143
+
5144
+
5145
+
5146
+
5147
+ const $2fec4872fbf7ebd2$var$Gene = ({ gene: gene })=>{
5148
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
5149
+ className: "row",
5150
+ children: gene.id
5151
+ });
5152
+ };
5153
+ const $2fec4872fbf7ebd2$var$Genes = (results, rows, doChangeQuantity)=>{
5154
+ if (results && results.numFound > 0) {
5155
+ const moreButton = results.numFound > rows ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("button", {
5156
+ onClick: (e)=>doChangeQuantity('Genes', 20),
5157
+ children: "more"
5158
+ }) : '';
5159
+ const fewerButton = rows > 20 ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("button", {
5160
+ onClick: (e)=>doChangeQuantity('Genes', -20),
5161
+ children: "fewer"
5162
+ }) : '';
5163
+ const docsToShow = results.response.docs.slice(0, rows);
5164
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
5165
+ id: "Genes",
5166
+ className: "container mb40 anchor",
5167
+ children: [
5168
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
5169
+ className: "fancy-title mb40",
5170
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
5171
+ children: "Genes"
5172
+ })
5173
+ }),
5174
+ docsToShow.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Gene, {
5175
+ gene: doc
5176
+ }, idx)),
5177
+ fewerButton,
5178
+ moreButton
5179
+ ]
5180
+ });
5181
+ }
5182
+ };
5183
+ const $2fec4872fbf7ebd2$var$Pathway = ({ pathway: pathway })=>{
5184
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
5185
+ className: "row",
5186
+ children: pathway.name
5187
+ });
5188
+ };
5189
+ const $2fec4872fbf7ebd2$var$Pathways = (results)=>{
5190
+ if (results && results.numFound > 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
5191
+ id: "Pathways",
5192
+ className: "container mb40 anchor",
5193
+ children: [
5194
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
5195
+ className: "fancy-title",
5196
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
5197
+ children: "Pathways"
5198
+ })
5199
+ }),
5200
+ results.pathways.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Pathway, {
5201
+ pathway: doc
5202
+ }, idx))
5203
+ ]
5204
+ });
5205
+ };
5206
+ const $2fec4872fbf7ebd2$var$Domain = ({ domain: domain })=>{
5207
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
5208
+ className: "row",
5209
+ children: domain.id
5210
+ });
5211
+ };
5212
+ const $2fec4872fbf7ebd2$var$Domains = (results)=>{
5213
+ if (results && results.numFound > 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
5214
+ id: "Domains",
5215
+ className: "container mb40 anchor",
5216
+ children: [
5217
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
5218
+ className: "fancy-title mb40",
5219
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
5220
+ children: "Domains"
5221
+ })
5222
+ }),
5223
+ results.domains.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Domain, {
5224
+ domain: doc
5225
+ }, idx))
5226
+ ]
5227
+ });
5228
+ };
5229
+ const $2fec4872fbf7ebd2$var$Taxon = ({ taxon: taxon })=>{
5230
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
5231
+ className: "row",
5232
+ children: taxon.id
5233
+ });
5234
+ };
5235
+ const $2fec4872fbf7ebd2$var$Species = (results)=>{
5236
+ if (results && results.numFound > 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
5237
+ id: "Species",
5238
+ className: "container mb40 anchor",
5239
+ children: [
5240
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
5241
+ className: "fancy-title mb40",
5242
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
5243
+ children: "Species"
5244
+ })
5245
+ }),
5246
+ results.taxonomy.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Taxon, {
5247
+ taxon: doc
5248
+ }, idx))
5249
+ ]
5250
+ });
5251
+ };
5252
+ const $2fec4872fbf7ebd2$var$ResultList = ({ grameneGenes: grameneGenes, grameneDomains: grameneDomains, gramenePathways: gramenePathways, grameneTaxonomy: grameneTaxonomy, searchUI: searchUI, searchUpdated: searchUpdated, doChangeQuantity: doChangeQuantity })=>{
5253
+ if (searchUI.Gramene) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
5254
+ id: "gramene",
5255
+ className: "row",
5256
+ children: [
5257
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
5258
+ className: "fancy-title pt50",
5259
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h3", {
5260
+ children: "Gramene search results"
5261
+ })
5262
+ }),
5263
+ searchUI.Genes && $2fec4872fbf7ebd2$var$Genes(grameneGenes, searchUI.rows.Genes, doChangeQuantity),
5264
+ searchUI.Domains && $2fec4872fbf7ebd2$var$Domains(grameneDomains),
5265
+ searchUI.Pathways && $2fec4872fbf7ebd2$var$Pathways(gramenePathways),
5266
+ searchUI.Species && $2fec4872fbf7ebd2$var$Species(grameneTaxonomy)
5267
+ ]
5268
+ });
5269
+ else return null;
5270
+ };
5271
+ var $2fec4872fbf7ebd2$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneGenes', 'selectGrameneDomains', 'selectGramenePathways', 'selectGrameneTaxonomy', 'selectSearchUI', 'selectSearchUpdated', 'doChangeQuantity', $2fec4872fbf7ebd2$var$ResultList);
5272
+
5273
+
5274
+
5275
+
5276
+
5277
+ const $27617dbc24e7faf0$var$getStatus = (cat, results, isChecked, toggle)=>{
5278
+ const tally = results ? results.numFound : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("img", {
5279
+ src: "/static/images/dna_spinner.svg"
5280
+ });
5281
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
5282
+ className: "category-leaf",
5283
+ children: [
5284
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("input", {
5285
+ type: "checkbox",
5286
+ checked: isChecked,
5287
+ onChange: (e)=>toggle(cat)
5288
+ }),
5289
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("a", {
5290
+ "data-scroll": true,
5291
+ href: `#${cat}`,
5292
+ className: "nav-link active",
5293
+ children: [
5294
+ cat,
5295
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
5296
+ style: {
5297
+ float: "right"
5298
+ },
5299
+ children: tally
5300
+ })
5301
+ ]
5302
+ })
5303
+ ]
5304
+ });
5305
+ };
5306
+ const $27617dbc24e7faf0$var$ResultSummary = ({ grameneGenes: grameneGenes, gramenePathways: gramenePathways, grameneDomains: grameneDomains, grameneTaxonomy: grameneTaxonomy, searchUI: searchUI, searchUpdated: searchUpdated, doToggleCategory: doToggleCategory })=>{
5307
+ const status = grameneGenes ? grameneGenes.numFound : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("img", {
5308
+ src: "/static/images/dna_spinner.svg"
5309
+ });
5310
+ if (searchUI.Gramene) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
5311
+ className: "active category-expanded",
5312
+ children: [
5313
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("a", {
5314
+ onClick: (e)=>doToggleCategory('Gramene'),
5315
+ children: [
5316
+ "Gramene Search",
5317
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
5318
+ style: {
5319
+ float: "right"
5320
+ },
5321
+ children: status
5322
+ })
5323
+ ]
5324
+ }),
5325
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("ul", {
5326
+ className: "list-unstyled",
5327
+ children: [
5328
+ $27617dbc24e7faf0$var$getStatus('Genes', grameneGenes, searchUI.Genes, doToggleCategory),
5329
+ $27617dbc24e7faf0$var$getStatus('Domains', grameneDomains, searchUI.Domains, doToggleCategory),
5330
+ $27617dbc24e7faf0$var$getStatus('Pathways', gramenePathways, searchUI.Pathways, doToggleCategory),
5331
+ $27617dbc24e7faf0$var$getStatus('Species', grameneTaxonomy, searchUI.Species, doToggleCategory)
5332
+ ]
5333
+ })
5334
+ ]
5335
+ });
5336
+ else return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("li", {
5337
+ className: "active category-collapsed",
5338
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("a", {
5339
+ onClick: (e)=>doToggleCategory('Gramene'),
5340
+ children: [
5341
+ "Gramene Search",
5342
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
5343
+ style: {
5344
+ float: "right"
5345
+ },
5346
+ children: status
5347
+ })
5348
+ ]
5349
+ })
5350
+ });
5351
+ };
5352
+ var $27617dbc24e7faf0$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneGenes', 'selectGramenePathways', 'selectGrameneDomains', 'selectGrameneTaxonomy', 'selectSearchUI', 'selectSearchUpdated', 'doToggleCategory', $27617dbc24e7faf0$var$ResultSummary);
5353
+
5354
+
5355
+
5356
+
4676
5357
 
4677
5358
 
4678
5359
 
@@ -7920,6 +8601,36 @@ const $b36244140732570a$var$examples = [
7920
8601
  ]
7921
8602
  }
7922
8603
  },
8604
+ {
8605
+ subsite: {
8606
+ maize: 1,
8607
+ sorghum: 1,
8608
+ grapevine: 1,
8609
+ rice: 1,
8610
+ main: 1
8611
+ },
8612
+ text: "Which genes are curated in the scientific literature?",
8613
+ filters: {
8614
+ status: 'init',
8615
+ rows: 20,
8616
+ operation: 'AND',
8617
+ negate: false,
8618
+ leftIdx: 0,
8619
+ rightIdx: 3,
8620
+ children: [
8621
+ {
8622
+ fq_field: 'capabilities',
8623
+ fq_value: 'pubs',
8624
+ name: 'publication',
8625
+ category: 'Curated',
8626
+ leftIdx: 1,
8627
+ rightIdx: 2,
8628
+ negate: false,
8629
+ marked: false
8630
+ }
8631
+ ]
8632
+ }
8633
+ },
7923
8634
  {
7924
8635
  subsite: {
7925
8636
  grapevine: 1
@@ -11390,12 +12101,22 @@ const $4ab64e76c1caef59$var$baseColDefs = [
11390
12101
  suppressMovable: true
11391
12102
  }
11392
12103
  ];
12104
+ // Hoisted so the reference is stable across renders. ag-grid otherwise sees a
12105
+ // "new" defaultColDef on every parent re-render (e.g. when hovering a row
12106
+ // triggers setHoveredId in the parent) and re-applies column state, which
12107
+ // snaps any user-resized columns back to their original widths.
12108
+ const $4ab64e76c1caef59$var$DEFAULT_COL_DEF = {
12109
+ resizable: true,
12110
+ sortable: true,
12111
+ filter: false,
12112
+ suppressMenu: true
12113
+ };
11393
12114
  function $4ab64e76c1caef59$var$arraysEqual(a, b) {
11394
12115
  if (a.length !== b.length) return false;
11395
12116
  for(let i = 0; i < a.length; i++)if (a[i] !== b[i]) return false;
11396
12117
  return true;
11397
12118
  }
11398
- function $4ab64e76c1caef59$var$buildFieldInfo(fields, studies, expressionSamples) {
12119
+ function $4ab64e76c1caef59$export$7b242440eb2c300d(fields, studies, expressionSamples) {
11399
12120
  const info = {};
11400
12121
  if (!fields || !studies || !expressionSamples) return info;
11401
12122
  const wanted = new Set(fields);
@@ -11684,7 +12405,7 @@ function $4ab64e76c1caef59$var$buildColumnDefs(fields, fieldInfo, expanded, togg
11684
12405
  ];
11685
12406
  }
11686
12407
  const $4ab64e76c1caef59$var$ExprTable = ({ rows: rows, fields: fields, onReorder: onReorder, studies: studies, expressionSamples: expressionSamples, onHoverRow: onHoverRow })=>{
11687
- const fieldInfo = (0, $gXNCa$react.useMemo)(()=>$4ab64e76c1caef59$var$buildFieldInfo(fields, studies, expressionSamples), [
12408
+ const fieldInfo = (0, $gXNCa$react.useMemo)(()=>$4ab64e76c1caef59$export$7b242440eb2c300d(fields, studies, expressionSamples), [
11688
12409
  fields,
11689
12410
  studies,
11690
12411
  expressionSamples
@@ -11730,12 +12451,7 @@ const $4ab64e76c1caef59$var$ExprTable = ({ rows: rows, fields: fields, onReorder
11730
12451
  children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$aggridreact.AgGridReact), {
11731
12452
  rowData: rows,
11732
12453
  columnDefs: columnDefs,
11733
- defaultColDef: {
11734
- resizable: true,
11735
- sortable: true,
11736
- filter: false,
11737
- suppressMenu: true
11738
- },
12454
+ defaultColDef: $4ab64e76c1caef59$var$DEFAULT_COL_DEF,
11739
12455
  animateRows: false,
11740
12456
  suppressFieldDotNotation: true,
11741
12457
  suppressDragLeaveHidesColumns: true,
@@ -12071,564 +12787,1384 @@ const $caf32827df861c4e$var$ParallelCoordsPlot = ({ rows: rows, fields: fields,
12071
12787
  const target = paths.filter(function() {
12072
12788
  return this.getAttribute('data-id') === String(hoveredId);
12073
12789
  });
12074
- target.classed('exprviz-pc-line-hover', true).raise();
12075
- }, [
12076
- hoveredId,
12077
- rows,
12078
- fields,
12079
- scale
12080
- ]);
12081
- if (!fields || fields.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12082
- className: "exprviz-plot-empty",
12083
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
12084
- children: "Select fields to plot."
12085
- })
12790
+ target.classed('exprviz-pc-line-hover', true).raise();
12791
+ }, [
12792
+ hoveredId,
12793
+ rows,
12794
+ fields,
12795
+ scale
12796
+ ]);
12797
+ if (!fields || fields.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12798
+ className: "exprviz-plot-empty",
12799
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
12800
+ children: "Select fields to plot."
12801
+ })
12802
+ });
12803
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12804
+ ref: containerRef,
12805
+ className: "exprviz-pc-container",
12806
+ children: [
12807
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("svg", {
12808
+ ref: svgRef,
12809
+ width: "100%",
12810
+ height: "100%",
12811
+ preserveAspectRatio: "none"
12812
+ }),
12813
+ tooltip && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($caf32827df861c4e$var$AxisTooltip, {
12814
+ x: tooltip.x,
12815
+ y: tooltip.y,
12816
+ info: tooltip.info
12817
+ })
12818
+ ]
12819
+ });
12820
+ };
12821
+ // Position-fixed so it can escape the plot pane's clipping. Offset slightly
12822
+ // from the cursor and clamped to the viewport so it never spills off-screen.
12823
+ const $caf32827df861c4e$var$AxisTooltip = ({ x: x, y: y, info: info })=>{
12824
+ const ref = (0, $gXNCa$react.useRef)(null);
12825
+ const [pos, setPos] = (0, $gXNCa$react.useState)({
12826
+ left: x + 12,
12827
+ top: y + 12
12828
+ });
12829
+ (0, $gXNCa$react.useEffect)(()=>{
12830
+ const el = ref.current;
12831
+ if (!el) return;
12832
+ const w = el.offsetWidth;
12833
+ const h = el.offsetHeight;
12834
+ const vw = window.innerWidth;
12835
+ const vh = window.innerHeight;
12836
+ let left = x + 12;
12837
+ let top = y + 12;
12838
+ if (left + w > vw - 4) left = Math.max(4, x - 12 - w);
12839
+ if (top + h > vh - 4) top = Math.max(4, y - 12 - h);
12840
+ setPos({
12841
+ left: left,
12842
+ top: top
12843
+ });
12844
+ }, [
12845
+ x,
12846
+ y,
12847
+ info
12848
+ ]);
12849
+ const { studyTitle: studyTitle, group: group, factors: factors, characteristics: characteristics } = info;
12850
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12851
+ ref: ref,
12852
+ className: "exprviz-pc-tooltip",
12853
+ style: pos,
12854
+ children: [
12855
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12856
+ children: [
12857
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
12858
+ className: "exprviz-pc-tip-key",
12859
+ children: "Study:"
12860
+ }),
12861
+ " ",
12862
+ studyTitle,
12863
+ group ? ` (${group})` : ''
12864
+ ]
12865
+ }),
12866
+ factors.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
12867
+ children: [
12868
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12869
+ className: "exprviz-pc-tip-section",
12870
+ children: "Factors"
12871
+ }),
12872
+ factors.map((p, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12873
+ className: "exprviz-pc-tip-row",
12874
+ children: [
12875
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
12876
+ className: "exprviz-pc-tip-key",
12877
+ children: [
12878
+ p.name,
12879
+ ":"
12880
+ ]
12881
+ }),
12882
+ " ",
12883
+ p.value
12884
+ ]
12885
+ }, `f-${i}`))
12886
+ ]
12887
+ }),
12888
+ characteristics.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
12889
+ children: [
12890
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12891
+ className: "exprviz-pc-tip-section",
12892
+ children: "Characteristics"
12893
+ }),
12894
+ characteristics.map((p, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12895
+ className: "exprviz-pc-tip-row",
12896
+ children: [
12897
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
12898
+ className: "exprviz-pc-tip-key",
12899
+ children: [
12900
+ p.name,
12901
+ ":"
12902
+ ]
12903
+ }),
12904
+ " ",
12905
+ p.value
12906
+ ]
12907
+ }, `c-${i}`))
12908
+ ]
12909
+ })
12910
+ ]
12911
+ });
12912
+ };
12913
+ var $caf32827df861c4e$export$2e2bcd8739ae039 = $caf32827df861c4e$var$ParallelCoordsPlot;
12914
+
12915
+
12916
+
12917
+ function $1fd2507769d5bd00$var$speciesTaxonId(tid) {
12918
+ const n = +tid;
12919
+ return n > 1000000 ? Math.floor(n / 1000) : n;
12920
+ }
12921
+ function $1fd2507769d5bd00$var$genomeName(grameneMaps, tid) {
12922
+ if (!grameneMaps) return tid;
12923
+ const direct = grameneMaps[tid];
12924
+ if (direct && direct.display_name) return direct.display_name;
12925
+ const sp = grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(tid)];
12926
+ if (sp && sp.display_name) return sp.display_name;
12927
+ return tid;
12928
+ }
12929
+ const $1fd2507769d5bd00$var$ExprVizViewCmp = (props)=>{
12930
+ const { exprVizPivot: pivot, exprViz: exprViz, exprVizActiveTaxon: activeTaxon, grameneMaps: grameneMaps, expressionStudies: expressionStudies, expressionSamples: expressionSamples, doSetExprVizActiveTaxon: doSetExprVizActiveTaxon, doToggleExprVizFieldsModal: doToggleExprVizFieldsModal, doFetchExprVizData: doFetchExprVizData } = props;
12931
+ const studiesFor = (tid)=>{
12932
+ if (!expressionStudies) return [];
12933
+ return expressionStudies[tid] || expressionStudies[$1fd2507769d5bd00$var$speciesTaxonId(tid)] || [];
12934
+ };
12935
+ const taxa = (0, $gXNCa$react.useMemo)(()=>{
12936
+ const ids = Object.keys(pivot.data || {});
12937
+ if (!grameneMaps) return ids;
12938
+ return ids.sort((a, b)=>{
12939
+ const ma = grameneMaps[a] || grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(a)];
12940
+ const mb = grameneMaps[b] || grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(b)];
12941
+ return (ma && ma.left_index || 0) - (mb && mb.left_index || 0);
12942
+ });
12943
+ }, [
12944
+ pivot.data,
12945
+ grameneMaps
12946
+ ]);
12947
+ (0, $gXNCa$react.useEffect)(()=>{
12948
+ if (taxa.length === 0) return;
12949
+ if (!activeTaxon || !taxa.includes(String(activeTaxon))) doSetExprVizActiveTaxon(taxa[0]);
12950
+ }, [
12951
+ taxa,
12952
+ activeTaxon,
12953
+ doSetExprVizActiveTaxon
12954
+ ]);
12955
+ if (pivot.status === 'loading') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12956
+ className: "exprviz-view",
12957
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
12958
+ children: "Loading studies\u2026"
12959
+ })
12960
+ });
12961
+ if (pivot.status === 'error') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12962
+ className: "exprviz-view",
12963
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("em", {
12964
+ children: [
12965
+ "Error: ",
12966
+ pivot.error
12967
+ ]
12968
+ })
12969
+ });
12970
+ if (taxa.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12971
+ className: "exprviz-view",
12972
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
12973
+ children: "No expression studies for current results."
12974
+ })
12975
+ });
12976
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12977
+ className: "exprviz-view",
12978
+ children: [
12979
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tabs), {
12980
+ activeKey: activeTaxon || taxa[0],
12981
+ onSelect: (k)=>doSetExprVizActiveTaxon(k),
12982
+ className: "exprviz-tabs",
12983
+ children: taxa.map((tid)=>{
12984
+ const studies = studiesFor(tid);
12985
+ const taxName = $1fd2507769d5bd00$var$genomeName(grameneMaps, tid);
12986
+ const geneCount = pivot.data[tid] || 0;
12987
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tab), {
12988
+ eventKey: tid,
12989
+ title: `${taxName} (${studies.length} studies \xb7 ${geneCount} genes)`,
12990
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($1fd2507769d5bd00$var$TaxonPanel, {
12991
+ taxon: tid,
12992
+ studies: studies,
12993
+ expressionSamples: expressionSamples,
12994
+ tabState: exprViz.byTaxon[tid],
12995
+ onOpenFields: ()=>doToggleExprVizFieldsModal(tid, true),
12996
+ onLoad: ()=>doFetchExprVizData(tid),
12997
+ onReorder: (next)=>props.doReorderExprVizFields(tid, next),
12998
+ onAddRangeQuery: props.doAddGrameneRangeQuery
12999
+ })
13000
+ }, tid);
13001
+ })
13002
+ }),
13003
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $4d0c2e01f58b53b1$export$2e2bcd8739ae039), {})
13004
+ ]
13005
+ });
13006
+ };
13007
+ // Compact axis labels for the parallel-coords plot. The raw Solr field name
13008
+ // (e.g. "E_CURD_148_g5__expr") is uninformative; we prefer the assay's
13009
+ // factor labels, falling back to "organism part" then to the group id.
13010
+ // `full` is exposed via an SVG <title> so the user can hover to see study,
13011
+ // group, and every factor/characteristic.
13012
+ const $1fd2507769d5bd00$var$AXIS_LABEL_MAX = 22;
13013
+ function $1fd2507769d5bd00$var$truncateLabel(s, n) {
13014
+ if (!s) return '';
13015
+ return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
13016
+ }
13017
+ function $1fd2507769d5bd00$var$compactAssayLabel(assay, group) {
13018
+ if (!assay) return group || '';
13019
+ const factorVals = (assay.factor || []).map((f)=>f && f.label).filter(Boolean);
13020
+ if (factorVals.length) return factorVals.join('; ');
13021
+ const chars = assay.characteristic || [];
13022
+ const organ = chars.find((c)=>c && c.type === 'organism part');
13023
+ if (organ && organ.label) return organ.label;
13024
+ const firstChar = chars.find((c)=>c && c.label);
13025
+ if (firstChar) return firstChar.label;
13026
+ return group || '';
13027
+ }
13028
+ function $1fd2507769d5bd00$var$assayPairs(list) {
13029
+ return (list || []).filter((x)=>x && x.label).map((x)=>({
13030
+ name: x.type || '',
13031
+ value: x.label
13032
+ }));
13033
+ }
13034
+ function $1fd2507769d5bd00$var$buildAxisLabels(fields, studies, expressionSamples) {
13035
+ const labels = {};
13036
+ if (!fields) return labels;
13037
+ const studyById = {};
13038
+ (studies || []).forEach((s)=>{
13039
+ if (s && s._id) studyById[s._id] = s;
13040
+ });
13041
+ const findAssay = (studyId, group)=>{
13042
+ const arr = expressionSamples && expressionSamples[studyId];
13043
+ return arr ? arr.find((a)=>a.group === group) : null;
13044
+ };
13045
+ for (const f of fields){
13046
+ const m = f.match(/^(.+?)_g(\d+)__expr$/);
13047
+ if (!m) {
13048
+ labels[f] = {
13049
+ short: $1fd2507769d5bd00$var$truncateLabel(f.replace(/__expr$/, ''), $1fd2507769d5bd00$var$AXIS_LABEL_MAX),
13050
+ structured: {
13051
+ studyTitle: f,
13052
+ group: '',
13053
+ factors: [],
13054
+ characteristics: []
13055
+ }
13056
+ };
13057
+ continue;
13058
+ }
13059
+ const expId = m[1].replace(/_/g, '-');
13060
+ const group = 'g' + m[2];
13061
+ const assay = findAssay(expId, group);
13062
+ const study = studyById[expId];
13063
+ const studyName = study && study.description || expId;
13064
+ labels[f] = {
13065
+ short: $1fd2507769d5bd00$var$truncateLabel($1fd2507769d5bd00$var$compactAssayLabel(assay, group), $1fd2507769d5bd00$var$AXIS_LABEL_MAX),
13066
+ structured: {
13067
+ studyTitle: studyName,
13068
+ group: group,
13069
+ factors: $1fd2507769d5bd00$var$assayPairs(assay && assay.factor),
13070
+ characteristics: $1fd2507769d5bd00$var$assayPairs(assay && assay.characteristic)
13071
+ }
13072
+ };
13073
+ }
13074
+ return labels;
13075
+ }
13076
+ function $1fd2507769d5bd00$var$rowMatchesSelections(row, selections) {
13077
+ for (const f of Object.keys(selections)){
13078
+ const v = row[f];
13079
+ if (v == null || Array.isArray(v)) return false;
13080
+ const n = +v;
13081
+ if (!Number.isFinite(n)) return false;
13082
+ const [lo, hi] = selections[f];
13083
+ if (n < lo || n > hi) return false;
13084
+ }
13085
+ return true;
13086
+ }
13087
+ function $1fd2507769d5bd00$var$fmt(n) {
13088
+ if (!Number.isFinite(n)) return String(n);
13089
+ const a = Math.abs(n);
13090
+ if (a !== 0 && (a < 0.001 || a >= 1e6)) return n.toExponential(3);
13091
+ return Number(n.toFixed(4)).toString();
13092
+ }
13093
+ function $1fd2507769d5bd00$var$tsvCell(v) {
13094
+ if (v == null) return '';
13095
+ if (Array.isArray(v)) return v.join(',');
13096
+ const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
13097
+ return s.replace(/[\t\r\n]+/g, ' ');
13098
+ }
13099
+ // Mirror the on-screen table header in the TSV: one row per metadata level
13100
+ // the table is showing (Study, then one row per distinct factor type, then
13101
+ // one row per distinct characteristic type), followed by the leaf header
13102
+ // (Gene ID / Name / per-sample group). The first two columns are repurposed
13103
+ // to carry the row category and the row's specific name, matching the
13104
+ // pinned-column labels in ExprTable.
13105
+ function $1fd2507769d5bd00$var$downloadTsv(filename, rows, fields, studies, expressionSamples) {
13106
+ const cols = [
13107
+ 'id',
13108
+ 'name',
13109
+ ...fields
13110
+ ];
13111
+ const fieldInfo = (0, $4ab64e76c1caef59$export$7b242440eb2c300d)(fields, studies, expressionSamples);
13112
+ const factorTypes = new Set();
13113
+ const charTypes = new Set();
13114
+ for (const f of fields){
13115
+ const info = fieldInfo[f];
13116
+ if (!info) continue;
13117
+ Object.keys(info.factors || {}).forEach((t)=>factorTypes.add(t));
13118
+ Object.keys(info.characteristics || {}).forEach((t)=>charTypes.add(t));
13119
+ }
13120
+ const factorTypeList = Array.from(factorTypes).sort();
13121
+ const charTypeList = Array.from(charTypes).sort();
13122
+ const metaRow = (cat, label, getValue)=>{
13123
+ const cells = [
13124
+ cat,
13125
+ label
13126
+ ];
13127
+ for (const f of fields)cells.push($1fd2507769d5bd00$var$tsvCell(getValue(fieldInfo[f] || {})));
13128
+ return cells.join('\t');
13129
+ };
13130
+ const lines = [];
13131
+ lines.push(metaRow('Study', 'Title', (info)=>info.studyDescription || ''));
13132
+ for (const t of factorTypeList)lines.push(metaRow('Factor', t, (info)=>info.factors && info.factors[t] || ''));
13133
+ for (const t of charTypeList)lines.push(metaRow('Characteristic', t, (info)=>info.characteristics && info.characteristics[t] || ''));
13134
+ // Leaf header — column ids for the data rows.
13135
+ lines.push([
13136
+ 'Gene ID',
13137
+ 'Name',
13138
+ ...fields.map((f)=>{
13139
+ const info = fieldInfo[f];
13140
+ return info && info.group || f.replace(/__expr$/, '');
13141
+ })
13142
+ ].join('\t'));
13143
+ for (const r of rows)lines.push(cols.map((c)=>$1fd2507769d5bd00$var$tsvCell(r[c])).join('\t'));
13144
+ const blob = new Blob([
13145
+ lines.join('\n') + '\n'
13146
+ ], {
13147
+ type: 'text/tab-separated-values'
13148
+ });
13149
+ const url = URL.createObjectURL(blob);
13150
+ const a = document.createElement('a');
13151
+ a.href = url;
13152
+ a.download = filename;
13153
+ document.body.appendChild(a);
13154
+ a.click();
13155
+ document.body.removeChild(a);
13156
+ URL.revokeObjectURL(url);
13157
+ }
13158
+ const $1fd2507769d5bd00$var$TaxonPanel = ({ taxon: taxon, studies: studies, expressionSamples: expressionSamples, tabState: tabState, onOpenFields: onOpenFields, onLoad: onLoad, onReorder: onReorder, onAddRangeQuery: onAddRangeQuery })=>{
13159
+ const selected = tabState && tabState.selectedFields || [];
13160
+ const rows = tabState && tabState.rows || [];
13161
+ const fetchInfo = tabState && tabState.fetch || {
13162
+ status: 'idle',
13163
+ total: 0
13164
+ };
13165
+ const [scale, setScale] = (0, $gXNCa$react.useState)('linear');
13166
+ const [selections, setSelections] = (0, $gXNCa$react.useState)({});
13167
+ const [clearVersion, setClearVersion] = (0, $gXNCa$react.useState)(0);
13168
+ const [hoveredId, setHoveredId] = (0, $gXNCa$react.useState)(null);
13169
+ const [plotHeight, setPlotHeight] = (0, $gXNCa$react.useState)(320);
13170
+ const resizeStateRef = (0, $gXNCa$react.useRef)(null);
13171
+ // Drag the horizontal separator between the plot and the table to retune
13172
+ // their relative sizes. Bounded so neither pane disappears entirely.
13173
+ const startResize = (e)=>{
13174
+ e.preventDefault();
13175
+ resizeStateRef.current = {
13176
+ startY: e.clientY,
13177
+ startHeight: plotHeight
13178
+ };
13179
+ const onMove = (ev)=>{
13180
+ const s = resizeStateRef.current;
13181
+ if (!s) return;
13182
+ const next = Math.max(120, Math.min(1200, s.startHeight + (ev.clientY - s.startY)));
13183
+ setPlotHeight(next);
13184
+ };
13185
+ const onUp = ()=>{
13186
+ resizeStateRef.current = null;
13187
+ document.removeEventListener('mousemove', onMove);
13188
+ document.removeEventListener('mouseup', onUp);
13189
+ document.body.style.cursor = '';
13190
+ document.body.style.userSelect = '';
13191
+ };
13192
+ document.body.style.cursor = 'row-resize';
13193
+ document.body.style.userSelect = 'none';
13194
+ document.addEventListener('mousemove', onMove);
13195
+ document.addEventListener('mouseup', onUp);
13196
+ };
13197
+ const hasBrush = Object.keys(selections).length > 0;
13198
+ const filteredRows = (0, $gXNCa$react.useMemo)(()=>{
13199
+ if (!hasBrush) return rows;
13200
+ return rows.filter((r)=>$1fd2507769d5bd00$var$rowMatchesSelections(r, selections));
13201
+ }, [
13202
+ rows,
13203
+ selections,
13204
+ hasBrush
13205
+ ]);
13206
+ // Drop fields with no numeric data in the loaded rows so empty axes/columns
13207
+ // don't clutter the visualization. Selected-but-empty fields stay in the
13208
+ // underlying selection so a future load can repopulate them.
13209
+ const visibleFields = (0, $gXNCa$react.useMemo)(()=>{
13210
+ if (rows.length === 0 || selected.length === 0) return selected;
13211
+ return selected.filter((f)=>rows.some((r)=>{
13212
+ const v = r[f];
13213
+ return v != null && !Array.isArray(v) && Number.isFinite(+v);
13214
+ }));
13215
+ }, [
13216
+ rows,
13217
+ selected
13218
+ ]);
13219
+ const handleReorder = onReorder ? (newVisibleOrder)=>{
13220
+ const visibleSet = new Set(newVisibleOrder);
13221
+ const hidden = selected.filter((f)=>!visibleSet.has(f));
13222
+ onReorder([
13223
+ ...newVisibleOrder,
13224
+ ...hidden
13225
+ ]);
13226
+ } : undefined;
13227
+ const axisLabels = (0, $gXNCa$react.useMemo)(()=>$1fd2507769d5bd00$var$buildAxisLabels(visibleFields, studies, expressionSamples), [
13228
+ visibleFields,
13229
+ studies,
13230
+ expressionSamples
13231
+ ]);
13232
+ (0, $gXNCa$react.useEffect)(()=>{
13233
+ if (rows.length === 0 && hasBrush) {
13234
+ setSelections({});
13235
+ setClearVersion((v)=>v + 1);
13236
+ }
13237
+ }, [
13238
+ rows.length,
13239
+ hasBrush
13240
+ ]);
13241
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
13242
+ className: "exprviz-tab-panel",
13243
+ children: [
13244
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
13245
+ className: "exprviz-toolbar",
13246
+ children: [
13247
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Button), {
13248
+ size: "sm",
13249
+ onClick: onOpenFields,
13250
+ children: [
13251
+ "Select fields (",
13252
+ selected.length,
13253
+ " selected, ",
13254
+ studies.length,
13255
+ " studies)"
13256
+ ]
13257
+ }),
13258
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
13259
+ size: "sm",
13260
+ variant: "primary",
13261
+ disabled: selected.length === 0 || fetchInfo.status === 'loading',
13262
+ onClick: onLoad,
13263
+ children: fetchInfo.status === 'loading' ? "Loading\u2026" : 'Load data'
13264
+ }),
13265
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.ToggleButtonGroup), {
13266
+ type: "radio",
13267
+ name: `exprviz-scale-${taxon}`,
13268
+ size: "sm",
13269
+ value: scale,
13270
+ onChange: setScale,
13271
+ children: [
13272
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.ToggleButton), {
13273
+ id: `exprviz-scale-${taxon}-lin`,
13274
+ value: "linear",
13275
+ variant: "outline-secondary",
13276
+ children: "Linear"
13277
+ }),
13278
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.ToggleButton), {
13279
+ id: `exprviz-scale-${taxon}-log`,
13280
+ value: "log",
13281
+ variant: "outline-secondary",
13282
+ children: "Log"
13283
+ })
13284
+ ]
13285
+ }),
13286
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
13287
+ size: "sm",
13288
+ variant: "outline-secondary",
13289
+ disabled: !hasBrush,
13290
+ onClick: ()=>{
13291
+ setClearVersion((v)=>v + 1);
13292
+ setSelections({});
13293
+ },
13294
+ children: "Clear brushes"
13295
+ }),
13296
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
13297
+ size: "sm",
13298
+ variant: "outline-secondary",
13299
+ disabled: filteredRows.length === 0 || visibleFields.length === 0,
13300
+ onClick: ()=>$1fd2507769d5bd00$var$downloadTsv(`expression_${taxon}.tsv`, filteredRows, visibleFields, studies, expressionSamples),
13301
+ title: "Download the visible rows and columns as tab-delimited text",
13302
+ children: "Download TSV"
13303
+ }),
13304
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
13305
+ size: "sm",
13306
+ variant: "success",
13307
+ disabled: !hasBrush || !onAddRangeQuery,
13308
+ onClick: ()=>{
13309
+ const terms = Object.keys(selections).map((field)=>{
13310
+ const [lo, hi] = selections[field];
13311
+ return {
13312
+ category: 'Expression',
13313
+ name: `${field}: ${$1fd2507769d5bd00$var$fmt(lo)}\u{2013}${$1fd2507769d5bd00$var$fmt(hi)}`,
13314
+ fq_field: field,
13315
+ fq_value: `[${lo} TO ${hi}]`
13316
+ };
13317
+ });
13318
+ onAddRangeQuery(terms);
13319
+ },
13320
+ title: "Add brush ranges as an AND-conjunction filter on the search",
13321
+ children: "Apply as filter"
13322
+ }),
13323
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
13324
+ className: "exprviz-status",
13325
+ children: [
13326
+ hasBrush ? `${filteredRows.length} of ${rows.length}` : rows.length,
13327
+ fetchInfo.total ? ` / ${fetchInfo.total}` : '',
13328
+ " genes",
13329
+ hasBrush ? ' (brushed)' : ' loaded'
13330
+ ]
13331
+ })
13332
+ ]
13333
+ }),
13334
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
13335
+ className: "exprviz-body",
13336
+ children: [
13337
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13338
+ className: "exprviz-plot",
13339
+ style: {
13340
+ height: plotHeight
13341
+ },
13342
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $caf32827df861c4e$export$2e2bcd8739ae039), {
13343
+ rows: rows,
13344
+ fields: visibleFields,
13345
+ scale: scale,
13346
+ onBrushChange: setSelections,
13347
+ onReorder: handleReorder,
13348
+ clearVersion: clearVersion,
13349
+ hoveredId: hoveredId,
13350
+ axisLabels: axisLabels
13351
+ })
13352
+ }),
13353
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13354
+ className: "exprviz-resizer",
13355
+ role: "separator",
13356
+ "aria-orientation": "horizontal",
13357
+ "aria-label": "Resize plot",
13358
+ onMouseDown: startResize,
13359
+ title: "Drag to resize"
13360
+ }),
13361
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13362
+ className: "exprviz-table",
13363
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $4ab64e76c1caef59$export$2e2bcd8739ae039), {
13364
+ rows: filteredRows,
13365
+ fields: visibleFields,
13366
+ onReorder: handleReorder,
13367
+ studies: studies,
13368
+ expressionSamples: expressionSamples,
13369
+ onHoverRow: setHoveredId
13370
+ })
13371
+ })
13372
+ ]
13373
+ })
13374
+ ]
13375
+ });
13376
+ };
13377
+ var $1fd2507769d5bd00$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectExprViz', 'selectExprVizPivot', 'selectExprVizActiveTaxon', 'selectGrameneMaps', 'selectExpressionStudies', 'selectExpressionSamples', 'doSetExprVizActiveTaxon', 'doToggleExprVizFieldsModal', 'doFetchExprVizData', 'doReorderExprVizFields', 'doAddGrameneRangeQuery', $1fd2507769d5bd00$var$ExprVizViewCmp);
13378
+
13379
+
13380
+
13381
+
13382
+
13383
+
13384
+
13385
+
13386
+ function $f511198465122338$var$speciesTaxonId(tid) {
13387
+ const n = +tid;
13388
+ return n > 1000000 ? Math.floor(n / 1000) : n;
13389
+ }
13390
+ function $f511198465122338$var$genomeName(grameneMaps, tid) {
13391
+ if (!grameneMaps) return String(tid);
13392
+ const direct = grameneMaps[tid];
13393
+ if (direct && direct.display_name) return direct.display_name;
13394
+ const sp = grameneMaps[$f511198465122338$var$speciesTaxonId(tid)];
13395
+ if (sp && sp.display_name) return sp.display_name;
13396
+ return String(tid);
13397
+ }
13398
+ function $f511198465122338$var$fmtP(p) {
13399
+ if (p == null) return '';
13400
+ if (p === 0) return '0';
13401
+ if (p < 1e-4) return p.toExponential(2);
13402
+ return p.toPrecision(3);
13403
+ }
13404
+ function $f511198465122338$var$fmtFold(f) {
13405
+ if (!isFinite(f)) return '';
13406
+ if (f >= 100) return f.toFixed(0);
13407
+ return f.toFixed(2);
13408
+ }
13409
+ // ---------- species tree helpers ----------
13410
+ function $f511198465122338$var$findRoots(tax) {
13411
+ return Object.values(tax).filter((n)=>!n.is_a || n.is_a.length === 0 || !n.is_a.some((p)=>tax[p]));
13412
+ }
13413
+ // Place each facet tid onto a taxonomy node id (fall back to species level
13414
+ // when the exact subspecies tid isn't in grameneTaxonomy).
13415
+ function $f511198465122338$var$placeTaxa(taxonomy, taxa) {
13416
+ const placement = {};
13417
+ const byPlace = {};
13418
+ for (const { tid: tid, count: count } of taxa){
13419
+ const place = taxonomy[tid] ? +tid : $f511198465122338$var$speciesTaxonId(tid);
13420
+ placement[tid] = place;
13421
+ if (!byPlace[place]) byPlace[place] = [];
13422
+ byPlace[place].push({
13423
+ tid: tid,
13424
+ count: count
13425
+ });
13426
+ }
13427
+ return {
13428
+ placement: placement,
13429
+ byPlace: byPlace
13430
+ };
13431
+ }
13432
+ // Set of taxonomy node ids on the path from any placed leaf back to its root.
13433
+ function $f511198465122338$var$relevantNodeIds(taxonomy, byPlace) {
13434
+ const out = new Set();
13435
+ for (const placeId of Object.keys(byPlace)){
13436
+ let cur = taxonomy[placeId];
13437
+ const seen = new Set();
13438
+ while(cur && !seen.has(+cur._id)){
13439
+ seen.add(+cur._id);
13440
+ out.add(+cur._id);
13441
+ const pid = cur.is_a && cur.is_a[0];
13442
+ cur = pid ? taxonomy[pid] : null;
13443
+ }
13444
+ }
13445
+ return out;
13446
+ }
13447
+ function $f511198465122338$var$relevantChildren(node, taxonomy, relevant) {
13448
+ return (node.children || []).map((cid)=>taxonomy[cid]).filter((c)=>c && relevant.has(+c._id));
13449
+ }
13450
+ // Walk down through single-relevant-child internal nodes to a branch / leaf.
13451
+ function $f511198465122338$var$compressChain(node, taxonomy, relevant, byPlace) {
13452
+ const chain = [
13453
+ node
13454
+ ];
13455
+ let cur = node;
13456
+ while(true){
13457
+ if (byPlace[cur._id]) break; // node itself is a placed leaf
13458
+ const kids = $f511198465122338$var$relevantChildren(cur, taxonomy, relevant);
13459
+ if (kids.length !== 1) break;
13460
+ cur = kids[0];
13461
+ chain.push(cur);
13462
+ }
13463
+ return {
13464
+ chain: chain,
13465
+ terminal: cur
13466
+ };
13467
+ }
13468
+ const $f511198465122338$var$CHAIN_DISPLAY_MAX = 3;
13469
+ function $f511198465122338$var$renderChain(chain) {
13470
+ const nameOf = (n)=>n.short_name || n.name || String(n._id);
13471
+ if (chain.length <= $f511198465122338$var$CHAIN_DISPLAY_MAX) return chain.map((n, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, ($parcel$interopDefault($gXNCa$react))).Fragment, {
13472
+ children: [
13473
+ i > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13474
+ className: "tax-sep",
13475
+ children: " \u203A "
13476
+ }),
13477
+ nameOf(n)
13478
+ ]
13479
+ }, n._id));
13480
+ const first = chain[0];
13481
+ const last = chain[chain.length - 1];
13482
+ const middle = chain.slice(1, -1);
13483
+ const fullTitle = chain.map(nameOf).join(" \u203A ");
13484
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
13485
+ children: [
13486
+ nameOf(first),
13487
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13488
+ className: "tax-sep",
13489
+ children: " \u203A "
13490
+ }),
13491
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
13492
+ className: "tax-ellipsis",
13493
+ title: fullTitle,
13494
+ children: [
13495
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13496
+ className: "tax-ellipsis-short",
13497
+ children: "\u2026"
13498
+ }),
13499
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13500
+ className: "tax-ellipsis-full",
13501
+ children: middle.map((n)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, ($parcel$interopDefault($gXNCa$react))).Fragment, {
13502
+ children: [
13503
+ nameOf(n),
13504
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13505
+ className: "tax-sep",
13506
+ children: " \u203A "
13507
+ })
13508
+ ]
13509
+ }, n._id))
13510
+ })
13511
+ ]
13512
+ }),
13513
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13514
+ className: "tax-sep",
13515
+ children: " \u203A "
13516
+ }),
13517
+ nameOf(last)
13518
+ ]
12086
13519
  });
13520
+ }
13521
+ function $f511198465122338$var$dedupAdjacent(chain) {
13522
+ const out = [];
13523
+ for (const n of chain)if (out.length && out[out.length - 1].name === n.name) out[out.length - 1] = n;
13524
+ else out.push(n);
13525
+ return out;
13526
+ }
13527
+ const $f511198465122338$var$SpeciesTreeNode = ({ node: node, depth: depth, taxonomy: taxonomy, relevant: relevant, byPlace: byPlace, grameneMaps: grameneMaps, expanded: expanded, onToggleExpand: onToggleExpand, activeTaxon: activeTaxon, onSelect: onSelect })=>{
13528
+ const { chain: rawChain, terminal: terminal } = $f511198465122338$var$compressChain(node, taxonomy, relevant, byPlace);
13529
+ const chain = $f511198465122338$var$dedupAdjacent(rawChain);
13530
+ const placed = byPlace[terminal._id] || [];
13531
+ const kids = $f511198465122338$var$relevantChildren(terminal, taxonomy, relevant);
13532
+ const hasKids = kids.length > 0;
13533
+ const isOpen = expanded.has(+terminal._id);
12087
13534
  return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12088
- ref: containerRef,
12089
- className: "exprviz-pc-container",
13535
+ className: "tax-node gsea-tree-node",
12090
13536
  children: [
12091
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("svg", {
12092
- ref: svgRef,
12093
- width: "100%",
12094
- height: "100%",
12095
- preserveAspectRatio: "none"
13537
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
13538
+ className: "tax-row",
13539
+ style: {
13540
+ paddingLeft: depth * 7
13541
+ },
13542
+ children: [
13543
+ hasKids ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13544
+ className: "tax-chevron",
13545
+ onClick: ()=>onToggleExpand(+terminal._id),
13546
+ children: isOpen ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reacticonsbs.BsChevronDown), {}) : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reacticonsbs.BsChevronRight), {})
13547
+ }) : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13548
+ className: "tax-chevron-spacer"
13549
+ }),
13550
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13551
+ className: hasKids ? 'tax-label tax-label-internal' : 'tax-label',
13552
+ children: $f511198465122338$var$renderChain(chain)
13553
+ })
13554
+ ]
12096
13555
  }),
12097
- tooltip && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($caf32827df861c4e$var$AxisTooltip, {
12098
- x: tooltip.x,
12099
- y: tooltip.y,
12100
- info: tooltip.info
13556
+ placed.map(({ tid: tid, count: count })=>{
13557
+ const isActive = String(activeTaxon) === String(tid);
13558
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
13559
+ className: 'gsea-tree-leaf' + (isActive ? ' gsea-tree-leaf-active' : ''),
13560
+ style: {
13561
+ paddingLeft: (depth + 1) * 7 + 18
13562
+ },
13563
+ onClick: ()=>onSelect(tid),
13564
+ children: [
13565
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("input", {
13566
+ type: "radio",
13567
+ readOnly: true,
13568
+ checked: isActive,
13569
+ onChange: ()=>onSelect(tid)
13570
+ }),
13571
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13572
+ className: "gsea-tree-leaf-name",
13573
+ children: $f511198465122338$var$genomeName(grameneMaps, tid)
13574
+ }),
13575
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13576
+ className: "gsea-tree-leaf-count",
13577
+ children: count.toLocaleString()
13578
+ })
13579
+ ]
13580
+ }, tid);
13581
+ }),
13582
+ hasKids && isOpen && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13583
+ className: "tax-children",
13584
+ children: kids.map((c)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SpeciesTreeNode, {
13585
+ node: c,
13586
+ depth: depth + 1,
13587
+ taxonomy: taxonomy,
13588
+ relevant: relevant,
13589
+ byPlace: byPlace,
13590
+ grameneMaps: grameneMaps,
13591
+ expanded: expanded,
13592
+ onToggleExpand: onToggleExpand,
13593
+ activeTaxon: activeTaxon,
13594
+ onSelect: onSelect
13595
+ }, c._id))
12101
13596
  })
12102
13597
  ]
12103
13598
  });
12104
13599
  };
12105
- // Position-fixed so it can escape the plot pane's clipping. Offset slightly
12106
- // from the cursor and clamped to the viewport so it never spills off-screen.
12107
- const $caf32827df861c4e$var$AxisTooltip = ({ x: x, y: y, info: info })=>{
12108
- const ref = (0, $gXNCa$react.useRef)(null);
12109
- const [pos, setPos] = (0, $gXNCa$react.useState)({
12110
- left: x + 12,
12111
- top: y + 12
12112
- });
13600
+ const $f511198465122338$var$SpeciesTree = ({ taxonomy: taxonomy, grameneMaps: grameneMaps, taxa: taxa, activeTaxon: activeTaxon, onSelect: onSelect })=>{
13601
+ const { placement: placement, byPlace: byPlace } = (0, $gXNCa$react.useMemo)(()=>$f511198465122338$var$placeTaxa(taxonomy, taxa), [
13602
+ taxonomy,
13603
+ taxa
13604
+ ]);
13605
+ const relevant = (0, $gXNCa$react.useMemo)(()=>$f511198465122338$var$relevantNodeIds(taxonomy, byPlace), [
13606
+ taxonomy,
13607
+ byPlace
13608
+ ]);
13609
+ const roots = (0, $gXNCa$react.useMemo)(()=>$f511198465122338$var$findRoots(taxonomy).filter((r)=>relevant.has(+r._id)), [
13610
+ taxonomy,
13611
+ relevant
13612
+ ]);
13613
+ // Default: every relevant internal node expanded so the user sees the
13614
+ // full pruned tree on first paint.
13615
+ const [expanded, setExpanded] = (0, $gXNCa$react.useState)(()=>new Set(relevant));
12113
13616
  (0, $gXNCa$react.useEffect)(()=>{
12114
- const el = ref.current;
12115
- if (!el) return;
12116
- const w = el.offsetWidth;
12117
- const h = el.offsetHeight;
12118
- const vw = window.innerWidth;
12119
- const vh = window.innerHeight;
12120
- let left = x + 12;
12121
- let top = y + 12;
12122
- if (left + w > vw - 4) left = Math.max(4, x - 12 - w);
12123
- if (top + h > vh - 4) top = Math.max(4, y - 12 - h);
12124
- setPos({
12125
- left: left,
12126
- top: top
12127
- });
13617
+ setExpanded(new Set(relevant));
12128
13618
  }, [
12129
- x,
12130
- y,
12131
- info
13619
+ relevant
12132
13620
  ]);
12133
- const { studyTitle: studyTitle, group: group, factors: factors, characteristics: characteristics } = info;
12134
- return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12135
- ref: ref,
12136
- className: "exprviz-pc-tooltip",
12137
- style: pos,
13621
+ const handleToggle = (id)=>{
13622
+ setExpanded((prev)=>{
13623
+ const next = new Set(prev);
13624
+ next.has(id) ? next.delete(id) : next.add(id);
13625
+ return next;
13626
+ });
13627
+ };
13628
+ // Skip the root spine: if a root has only one relevant child and isn't
13629
+ // itself a placed leaf, render from its child instead.
13630
+ const topLevel = [];
13631
+ for (const r of roots){
13632
+ const { terminal: terminal } = $f511198465122338$var$compressChain(r, taxonomy, relevant, byPlace);
13633
+ if (byPlace[terminal._id]) topLevel.push(r);
13634
+ else {
13635
+ const kids = $f511198465122338$var$relevantChildren(terminal, taxonomy, relevant);
13636
+ kids.forEach((k)=>topLevel.push(k));
13637
+ }
13638
+ }
13639
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13640
+ className: "gsea-tree",
13641
+ children: topLevel.map((n)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SpeciesTreeNode, {
13642
+ node: n,
13643
+ depth: 0,
13644
+ taxonomy: taxonomy,
13645
+ relevant: relevant,
13646
+ byPlace: byPlace,
13647
+ grameneMaps: grameneMaps,
13648
+ expanded: expanded,
13649
+ onToggleExpand: handleToggle,
13650
+ activeTaxon: activeTaxon,
13651
+ onSelect: onSelect
13652
+ }, n._id))
13653
+ });
13654
+ };
13655
+ // ---------- enrichment panel ----------
13656
+ const $f511198465122338$var$ControlsBar = ({ ui: ui, onChange: onChange })=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
13657
+ className: "gsea-controls",
12138
13658
  children: [
12139
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
13659
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Form).Group, {
13660
+ className: "gsea-control",
12140
13661
  children: [
12141
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
12142
- className: "exprviz-pc-tip-key",
12143
- children: "Study:"
13662
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Label, {
13663
+ children: "p_adj \u2264"
12144
13664
  }),
12145
- " ",
12146
- studyTitle,
12147
- group ? ` (${group})` : ''
13665
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
13666
+ type: "number",
13667
+ step: "0.01",
13668
+ min: "0",
13669
+ max: "1",
13670
+ value: ui.pAdjCutoff,
13671
+ onChange: (e)=>onChange({
13672
+ pAdjCutoff: +e.target.value
13673
+ })
13674
+ })
12148
13675
  ]
12149
13676
  }),
12150
- factors.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
13677
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Form).Group, {
13678
+ className: "gsea-control",
12151
13679
  children: [
12152
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12153
- className: "exprviz-pc-tip-section",
12154
- children: "Factors"
13680
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Label, {
13681
+ children: "min k"
12155
13682
  }),
12156
- factors.map((p, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12157
- className: "exprviz-pc-tip-row",
12158
- children: [
12159
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
12160
- className: "exprviz-pc-tip-key",
12161
- children: [
12162
- p.name,
12163
- ":"
12164
- ]
12165
- }),
12166
- " ",
12167
- p.value
12168
- ]
12169
- }, `f-${i}`))
13683
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
13684
+ type: "number",
13685
+ step: "1",
13686
+ min: "1",
13687
+ value: ui.minK,
13688
+ onChange: (e)=>onChange({
13689
+ minK: Math.max(1, +e.target.value)
13690
+ })
13691
+ })
12170
13692
  ]
12171
13693
  }),
12172
- characteristics.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
13694
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Check, {
13695
+ className: "gsea-control",
13696
+ type: "switch",
13697
+ id: "gsea-most-specific",
13698
+ label: "Most-specific only",
13699
+ checked: !!ui.mostSpecific,
13700
+ onChange: (e)=>onChange({
13701
+ mostSpecific: e.target.checked
13702
+ })
13703
+ }),
13704
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Form).Group, {
13705
+ className: "gsea-control gsea-control-grow",
12173
13706
  children: [
12174
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12175
- className: "exprviz-pc-tip-section",
12176
- children: "Characteristics"
13707
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Label, {
13708
+ children: "Filter terms"
12177
13709
  }),
12178
- characteristics.map((p, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12179
- className: "exprviz-pc-tip-row",
12180
- children: [
12181
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
12182
- className: "exprviz-pc-tip-key",
12183
- children: [
12184
- p.name,
12185
- ":"
12186
- ]
12187
- }),
12188
- " ",
12189
- p.value
12190
- ]
12191
- }, `c-${i}`))
13710
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
13711
+ type: "text",
13712
+ placeholder: "term id or name\u2026",
13713
+ value: ui.search || '',
13714
+ onChange: (e)=>onChange({
13715
+ search: e.target.value
13716
+ })
13717
+ })
13718
+ ]
13719
+ })
13720
+ ]
13721
+ });
13722
+ const $f511198465122338$var$TermRow = ({ row: row, showType: showType, onAddFilter: onAddFilter })=>{
13723
+ const handleClick = ()=>onAddFilter && onAddFilter(row);
13724
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("tr", {
13725
+ onClick: handleClick,
13726
+ title: "Click to add as a filter",
13727
+ children: [
13728
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
13729
+ className: "gsea-term-id",
13730
+ children: row.term_display_id
13731
+ }),
13732
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
13733
+ className: "gsea-term-name",
13734
+ children: row.term_name || /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
13735
+ children: "(loading\u2026)"
13736
+ })
13737
+ }),
13738
+ showType && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
13739
+ className: "gsea-term-type",
13740
+ children: row.term_namespace || ''
13741
+ }),
13742
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("td", {
13743
+ className: "gsea-num",
13744
+ children: [
13745
+ row.k,
13746
+ " / ",
13747
+ row.n
13748
+ ]
13749
+ }),
13750
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("td", {
13751
+ className: "gsea-num",
13752
+ children: [
13753
+ row.K,
13754
+ " / ",
13755
+ row.N
12192
13756
  ]
13757
+ }),
13758
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
13759
+ className: "gsea-num",
13760
+ children: $f511198465122338$var$fmtFold(row.fold)
13761
+ }),
13762
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
13763
+ className: "gsea-num",
13764
+ children: $f511198465122338$var$fmtP(row.p)
13765
+ }),
13766
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
13767
+ className: "gsea-num",
13768
+ children: $f511198465122338$var$fmtP(row.pAdj)
12193
13769
  })
12194
13770
  ]
12195
13771
  });
12196
13772
  };
12197
- var $caf32827df861c4e$export$2e2bcd8739ae039 = $caf32827df861c4e$var$ParallelCoordsPlot;
12198
-
12199
-
12200
-
12201
- function $1fd2507769d5bd00$var$speciesTaxonId(tid) {
12202
- const n = +tid;
12203
- return n > 1000000 ? Math.floor(n / 1000) : n;
12204
- }
12205
- function $1fd2507769d5bd00$var$genomeName(grameneMaps, tid) {
12206
- if (!grameneMaps) return tid;
12207
- const direct = grameneMaps[tid];
12208
- if (direct && direct.display_name) return direct.display_name;
12209
- const sp = grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(tid)];
12210
- if (sp && sp.display_name) return sp.display_name;
12211
- return tid;
12212
- }
12213
- const $1fd2507769d5bd00$var$ExprVizViewCmp = (props)=>{
12214
- const { exprVizPivot: pivot, exprViz: exprViz, exprVizActiveTaxon: activeTaxon, grameneMaps: grameneMaps, expressionStudies: expressionStudies, expressionSamples: expressionSamples, doSetExprVizActiveTaxon: doSetExprVizActiveTaxon, doToggleExprVizFieldsModal: doToggleExprVizFieldsModal, doFetchExprVizData: doFetchExprVizData } = props;
12215
- const studiesFor = (tid)=>{
12216
- if (!expressionStudies) return [];
12217
- return expressionStudies[tid] || expressionStudies[$1fd2507769d5bd00$var$speciesTaxonId(tid)] || [];
12218
- };
12219
- const taxa = (0, $gXNCa$react.useMemo)(()=>{
12220
- const ids = Object.keys(pivot.data || {});
12221
- if (!grameneMaps) return ids;
12222
- return ids.sort((a, b)=>{
12223
- const ma = grameneMaps[a] || grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(a)];
12224
- const mb = grameneMaps[b] || grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(b)];
12225
- return (ma && ma.left_index || 0) - (mb && mb.left_index || 0);
12226
- });
13773
+ // InterPro entry types and pathway types are useful per-row context
13774
+ // (e.g. Domain vs Family vs Repeat for InterPro), and aren't reflected in
13775
+ // the section title the way GO namespaces are. Show a Type column for
13776
+ // those two ontologies only.
13777
+ const $f511198465122338$var$ONTS_WITH_TYPE_COLUMN = new Set([
13778
+ 'domains',
13779
+ 'pathways'
13780
+ ]);
13781
+ const $f511198465122338$var$SORT_ACCESSORS = {
13782
+ term: (r)=>r.term_display_id || '',
13783
+ name: (r)=>(r.term_name || '').toLowerCase(),
13784
+ type: (r)=>(r.term_namespace || '').toLowerCase(),
13785
+ k: (r)=>r.k,
13786
+ K: (r)=>r.K,
13787
+ fold: (r)=>r.fold,
13788
+ p: (r)=>r.p,
13789
+ pAdj: (r)=>r.pAdj
13790
+ };
13791
+ // Defaults chosen so a single click does what the user usually wants:
13792
+ // numeric "more interesting" columns (k, K, fold) descend; p-values and
13793
+ // text columns ascend.
13794
+ const $f511198465122338$var$SORT_DEFAULT_DIR = {
13795
+ term: 'asc',
13796
+ name: 'asc',
13797
+ type: 'asc',
13798
+ k: 'desc',
13799
+ K: 'desc',
13800
+ fold: 'desc',
13801
+ p: 'asc',
13802
+ pAdj: 'asc'
13803
+ };
13804
+ const $f511198465122338$var$SortableTh = ({ label: label, sortKey: sortKey, activeKey: activeKey, activeDir: activeDir, onSort: onSort, numeric: numeric })=>{
13805
+ const active = activeKey === sortKey;
13806
+ const arrow = active ? activeDir === 'asc' ? " \u25B2" : " \u25BC" : '';
13807
+ const cls = 'gsea-sort-th' + (active ? ' gsea-sort-th-active' : '') + (numeric ? ' gsea-num' : '');
13808
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("th", {
13809
+ className: cls,
13810
+ onClick: ()=>onSort(sortKey),
13811
+ children: [
13812
+ label,
13813
+ arrow
13814
+ ]
13815
+ });
13816
+ };
13817
+ const $f511198465122338$var$OntologySection = ({ block: block, search: search, onAddFilter: onAddFilter })=>{
13818
+ const filtered = (0, $gXNCa$react.useMemo)(()=>{
13819
+ if (!search) return block.rows;
13820
+ const needle = search.toLowerCase();
13821
+ return block.rows.filter((r)=>r.term_display_id && r.term_display_id.toLowerCase().includes(needle) || r.term_name && r.term_name.toLowerCase().includes(needle));
12227
13822
  }, [
12228
- pivot.data,
12229
- grameneMaps
13823
+ block.rows,
13824
+ search
12230
13825
  ]);
12231
- (0, $gXNCa$react.useEffect)(()=>{
12232
- if (taxa.length === 0) return;
12233
- if (!activeTaxon || !taxa.includes(String(activeTaxon))) doSetExprVizActiveTaxon(taxa[0]);
13826
+ const showType = $f511198465122338$var$ONTS_WITH_TYPE_COLUMN.has(block.ontology);
13827
+ const [sortKey, setSortKey] = (0, $gXNCa$react.useState)('pAdj');
13828
+ const [sortDir, setSortDir] = (0, $gXNCa$react.useState)('asc');
13829
+ const handleSort = (key)=>{
13830
+ if (key === sortKey) setSortDir((d)=>d === 'asc' ? 'desc' : 'asc');
13831
+ else {
13832
+ setSortKey(key);
13833
+ setSortDir($f511198465122338$var$SORT_DEFAULT_DIR[key] || 'asc');
13834
+ }
13835
+ };
13836
+ const sorted = (0, $gXNCa$react.useMemo)(()=>{
13837
+ const accessor = $f511198465122338$var$SORT_ACCESSORS[sortKey];
13838
+ if (!accessor) return filtered;
13839
+ const sign = sortDir === 'desc' ? -1 : 1;
13840
+ const arr = filtered.slice();
13841
+ arr.sort((a, b)=>{
13842
+ const va = accessor(a);
13843
+ const vb = accessor(b);
13844
+ if (typeof va === 'number' && typeof vb === 'number') {
13845
+ if (va === vb) return 0;
13846
+ return (va - vb) * sign;
13847
+ }
13848
+ return String(va).localeCompare(String(vb)) * sign;
13849
+ });
13850
+ return arr;
12234
13851
  }, [
12235
- taxa,
12236
- activeTaxon,
12237
- doSetExprVizActiveTaxon
13852
+ filtered,
13853
+ sortKey,
13854
+ sortDir
12238
13855
  ]);
12239
- if (pivot.status === 'loading') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12240
- className: "exprviz-view",
13856
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Accordion).Item, {
13857
+ eventKey: block.ontology,
13858
+ children: [
13859
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Accordion).Header, {
13860
+ children: [
13861
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
13862
+ className: "gsea-ont-title",
13863
+ children: block.label
13864
+ }),
13865
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Badge), {
13866
+ bg: "secondary",
13867
+ className: "gsea-ont-badge",
13868
+ children: [
13869
+ block.passing,
13870
+ " significant / ",
13871
+ block.tested,
13872
+ " tested"
13873
+ ]
13874
+ })
13875
+ ]
13876
+ }),
13877
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Accordion).Body, {
13878
+ children: sorted.length === 0 ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
13879
+ children: "No terms pass the current cutoffs."
13880
+ }) : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("table", {
13881
+ className: "gsea-table",
13882
+ children: [
13883
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("thead", {
13884
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("tr", {
13885
+ children: [
13886
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
13887
+ label: "Term",
13888
+ sortKey: "term",
13889
+ activeKey: sortKey,
13890
+ activeDir: sortDir,
13891
+ onSort: handleSort
13892
+ }),
13893
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
13894
+ label: "Name",
13895
+ sortKey: "name",
13896
+ activeKey: sortKey,
13897
+ activeDir: sortDir,
13898
+ onSort: handleSort
13899
+ }),
13900
+ showType && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
13901
+ label: "Type",
13902
+ sortKey: "type",
13903
+ activeKey: sortKey,
13904
+ activeDir: sortDir,
13905
+ onSort: handleSort
13906
+ }),
13907
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
13908
+ label: "k / n",
13909
+ sortKey: "k",
13910
+ activeKey: sortKey,
13911
+ activeDir: sortDir,
13912
+ onSort: handleSort,
13913
+ numeric: true
13914
+ }),
13915
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
13916
+ label: "K / N",
13917
+ sortKey: "K",
13918
+ activeKey: sortKey,
13919
+ activeDir: sortDir,
13920
+ onSort: handleSort,
13921
+ numeric: true
13922
+ }),
13923
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
13924
+ label: "Fold",
13925
+ sortKey: "fold",
13926
+ activeKey: sortKey,
13927
+ activeDir: sortDir,
13928
+ onSort: handleSort,
13929
+ numeric: true
13930
+ }),
13931
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
13932
+ label: "p",
13933
+ sortKey: "p",
13934
+ activeKey: sortKey,
13935
+ activeDir: sortDir,
13936
+ onSort: handleSort,
13937
+ numeric: true
13938
+ }),
13939
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
13940
+ label: "p_adj",
13941
+ sortKey: "pAdj",
13942
+ activeKey: sortKey,
13943
+ activeDir: sortDir,
13944
+ onSort: handleSort,
13945
+ numeric: true
13946
+ })
13947
+ ]
13948
+ })
13949
+ }),
13950
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("tbody", {
13951
+ children: sorted.map((r)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$TermRow, {
13952
+ row: r,
13953
+ showType: showType,
13954
+ onAddFilter: onAddFilter
13955
+ }, `${r.ontology}:${r.term_id}`))
13956
+ })
13957
+ ]
13958
+ })
13959
+ })
13960
+ ]
13961
+ });
13962
+ };
13963
+ const $f511198465122338$var$TaxonPanel = ({ taxon: taxon, gsea: gsea, results: results, ui: ui, onUiChange: onUiChange, onAddFilter: onAddFilter })=>{
13964
+ if (!taxon) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13965
+ className: "gsea-panel",
12241
13966
  children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
12242
- children: "Loading studies\u2026"
13967
+ children: "Select a species on the left."
12243
13968
  })
12244
13969
  });
12245
- if (pivot.status === 'error') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12246
- className: "exprviz-view",
12247
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("em", {
12248
- children: [
12249
- "Error: ",
12250
- pivot.error
12251
- ]
13970
+ const t = gsea.byTaxon[taxon];
13971
+ if (!t) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13972
+ className: "gsea-panel",
13973
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
13974
+ children: "Initializing\u2026"
12252
13975
  })
12253
13976
  });
12254
- if (taxa.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12255
- className: "exprviz-view",
13977
+ if (t.fg.status === 'loading' || t.bg.status === 'loading' || t.fg.status === 'idle' || t.bg.status === 'idle') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13978
+ className: "gsea-panel gsea-loading",
12256
13979
  children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
12257
- children: "No expression studies for current results."
13980
+ children: "Loading enrichment\u2026"
12258
13981
  })
12259
13982
  });
12260
- return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12261
- className: "exprviz-view",
12262
- children: [
12263
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tabs), {
12264
- activeKey: activeTaxon || taxa[0],
12265
- onSelect: (k)=>doSetExprVizActiveTaxon(k),
12266
- className: "exprviz-tabs",
12267
- children: taxa.map((tid)=>{
12268
- const studies = studiesFor(tid);
12269
- const taxName = $1fd2507769d5bd00$var$genomeName(grameneMaps, tid);
12270
- const geneCount = pivot.data[tid] || 0;
12271
- return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tab), {
12272
- eventKey: tid,
12273
- title: `${taxName} (${studies.length} studies \xb7 ${geneCount} genes)`,
12274
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($1fd2507769d5bd00$var$TaxonPanel, {
12275
- taxon: tid,
12276
- studies: studies,
12277
- expressionSamples: expressionSamples,
12278
- tabState: exprViz.byTaxon[tid],
12279
- onOpenFields: ()=>doToggleExprVizFieldsModal(tid, true),
12280
- onLoad: ()=>doFetchExprVizData(tid),
12281
- onReorder: (next)=>props.doReorderExprVizFields(tid, next),
12282
- onAddRangeQuery: props.doAddGrameneRangeQuery
12283
- })
12284
- }, tid);
12285
- })
12286
- }),
12287
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $4d0c2e01f58b53b1$export$2e2bcd8739ae039), {})
12288
- ]
12289
- });
12290
- };
12291
- // Compact axis labels for the parallel-coords plot. The raw Solr field name
12292
- // (e.g. "E_CURD_148_g5__expr") is uninformative; we prefer the assay's
12293
- // factor labels, falling back to "organism part" then to the group id.
12294
- // `full` is exposed via an SVG <title> so the user can hover to see study,
12295
- // group, and every factor/characteristic.
12296
- const $1fd2507769d5bd00$var$AXIS_LABEL_MAX = 22;
12297
- function $1fd2507769d5bd00$var$truncateLabel(s, n) {
12298
- if (!s) return '';
12299
- return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
12300
- }
12301
- function $1fd2507769d5bd00$var$compactAssayLabel(assay, group) {
12302
- if (!assay) return group || '';
12303
- const factorVals = (assay.factor || []).map((f)=>f && f.label).filter(Boolean);
12304
- if (factorVals.length) return factorVals.join('; ');
12305
- const chars = assay.characteristic || [];
12306
- const organ = chars.find((c)=>c && c.type === 'organism part');
12307
- if (organ && organ.label) return organ.label;
12308
- const firstChar = chars.find((c)=>c && c.label);
12309
- if (firstChar) return firstChar.label;
12310
- return group || '';
12311
- }
12312
- function $1fd2507769d5bd00$var$assayPairs(list) {
12313
- return (list || []).filter((x)=>x && x.label).map((x)=>({
12314
- name: x.type || '',
12315
- value: x.label
12316
- }));
12317
- }
12318
- function $1fd2507769d5bd00$var$buildAxisLabels(fields, studies, expressionSamples) {
12319
- const labels = {};
12320
- if (!fields) return labels;
12321
- const studyById = {};
12322
- (studies || []).forEach((s)=>{
12323
- if (s && s._id) studyById[s._id] = s;
13983
+ if (t.fg.status === 'error') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13984
+ className: "gsea-panel",
13985
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("em", {
13986
+ children: [
13987
+ "Foreground error: ",
13988
+ t.fg.error
13989
+ ]
13990
+ })
12324
13991
  });
12325
- const findAssay = (studyId, group)=>{
12326
- const arr = expressionSamples && expressionSamples[studyId];
12327
- return arr ? arr.find((a)=>a.group === group) : null;
12328
- };
12329
- for (const f of fields){
12330
- const m = f.match(/^(.+?)_g(\d+)__expr$/);
12331
- if (!m) {
12332
- labels[f] = {
12333
- short: $1fd2507769d5bd00$var$truncateLabel(f.replace(/__expr$/, ''), $1fd2507769d5bd00$var$AXIS_LABEL_MAX),
12334
- structured: {
12335
- studyTitle: f,
12336
- group: '',
12337
- factors: [],
12338
- characteristics: []
12339
- }
12340
- };
12341
- continue;
12342
- }
12343
- const expId = m[1].replace(/_/g, '-');
12344
- const group = 'g' + m[2];
12345
- const assay = findAssay(expId, group);
12346
- const study = studyById[expId];
12347
- const studyName = study && study.description || expId;
12348
- labels[f] = {
12349
- short: $1fd2507769d5bd00$var$truncateLabel($1fd2507769d5bd00$var$compactAssayLabel(assay, group), $1fd2507769d5bd00$var$AXIS_LABEL_MAX),
12350
- structured: {
12351
- studyTitle: studyName,
12352
- group: group,
12353
- factors: $1fd2507769d5bd00$var$assayPairs(assay && assay.factor),
12354
- characteristics: $1fd2507769d5bd00$var$assayPairs(assay && assay.characteristic)
12355
- }
12356
- };
12357
- }
12358
- return labels;
12359
- }
12360
- function $1fd2507769d5bd00$var$rowMatchesSelections(row, selections) {
12361
- for (const f of Object.keys(selections)){
12362
- const v = row[f];
12363
- if (v == null || Array.isArray(v)) return false;
12364
- const n = +v;
12365
- if (!Number.isFinite(n)) return false;
12366
- const [lo, hi] = selections[f];
12367
- if (n < lo || n > hi) return false;
12368
- }
12369
- return true;
12370
- }
12371
- function $1fd2507769d5bd00$var$fmt(n) {
12372
- if (!Number.isFinite(n)) return String(n);
12373
- const a = Math.abs(n);
12374
- if (a !== 0 && (a < 0.001 || a >= 1e6)) return n.toExponential(3);
12375
- return Number(n.toFixed(4)).toString();
12376
- }
12377
- function $1fd2507769d5bd00$var$tsvCell(v) {
12378
- if (v == null) return '';
12379
- if (Array.isArray(v)) return v.join(',');
12380
- const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
12381
- return s.replace(/[\t\r\n]+/g, ' ');
12382
- }
12383
- function $1fd2507769d5bd00$var$downloadTsv(filename, rows, fields) {
12384
- const cols = [
12385
- 'id',
12386
- 'name',
12387
- ...fields
12388
- ];
12389
- const headerLabels = [
12390
- 'id',
12391
- 'name',
12392
- ...fields.map((f)=>f.replace(/__expr$/, ''))
12393
- ];
12394
- const lines = [
12395
- headerLabels.join('\t')
12396
- ];
12397
- for (const r of rows)lines.push(cols.map((c)=>$1fd2507769d5bd00$var$tsvCell(r[c])).join('\t'));
12398
- const blob = new Blob([
12399
- lines.join('\n') + '\n'
12400
- ], {
12401
- type: 'text/tab-separated-values'
13992
+ if (t.bg.status === 'error') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
13993
+ className: "gsea-panel",
13994
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("em", {
13995
+ children: [
13996
+ "Background error: ",
13997
+ t.bg.error
13998
+ ]
13999
+ })
12402
14000
  });
12403
- const url = URL.createObjectURL(blob);
12404
- const a = document.createElement('a');
12405
- a.href = url;
12406
- a.download = filename;
12407
- document.body.appendChild(a);
12408
- a.click();
12409
- document.body.removeChild(a);
12410
- URL.revokeObjectURL(url);
12411
- }
12412
- const $1fd2507769d5bd00$var$TaxonPanel = ({ taxon: taxon, studies: studies, expressionSamples: expressionSamples, tabState: tabState, onOpenFields: onOpenFields, onLoad: onLoad, onReorder: onReorder, onAddRangeQuery: onAddRangeQuery })=>{
12413
- const selected = tabState && tabState.selectedFields || [];
12414
- const rows = tabState && tabState.rows || [];
12415
- const fetchInfo = tabState && tabState.fetch || {
12416
- status: 'idle',
12417
- total: 0
12418
- };
12419
- const [scale, setScale] = (0, $gXNCa$react.useState)('linear');
12420
- const [selections, setSelections] = (0, $gXNCa$react.useState)({});
12421
- const [clearVersion, setClearVersion] = (0, $gXNCa$react.useState)(0);
12422
- const [hoveredId, setHoveredId] = (0, $gXNCa$react.useState)(null);
12423
- const [plotHeight, setPlotHeight] = (0, $gXNCa$react.useState)(320);
12424
- const resizeStateRef = (0, $gXNCa$react.useRef)(null);
12425
- // Drag the horizontal separator between the plot and the table to retune
12426
- // their relative sizes. Bounded so neither pane disappears entirely.
12427
- const startResize = (e)=>{
14001
+ if (!results) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
14002
+ className: "gsea-panel",
14003
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
14004
+ children: "No data."
14005
+ })
14006
+ });
14007
+ const allBlocks = ui.ontology === 'all' ? Object.values(results) : results[ui.ontology] ? [
14008
+ results[ui.ontology]
14009
+ ] : [];
14010
+ // Hide ontologies that aren't used in this species at all.
14011
+ const blocks = allBlocks.filter((b)=>b.tested > 0);
14012
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
14013
+ className: "gsea-panel",
14014
+ children: [
14015
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
14016
+ className: "gsea-summary",
14017
+ children: [
14018
+ "Foreground: ",
14019
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("b", {
14020
+ children: t.fg.numFound.toLocaleString()
14021
+ }),
14022
+ " genes \xb7 Background: ",
14023
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("b", {
14024
+ children: t.bg.numFound.toLocaleString()
14025
+ }),
14026
+ " genes"
14027
+ ]
14028
+ }),
14029
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$ControlsBar, {
14030
+ ui: ui,
14031
+ onChange: onUiChange
14032
+ }),
14033
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Accordion), {
14034
+ alwaysOpen: true,
14035
+ defaultActiveKey: blocks.length === 1 ? blocks[0].ontology : undefined,
14036
+ children: blocks.map((b)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$OntologySection, {
14037
+ block: b,
14038
+ search: ui.search,
14039
+ onAddFilter: onAddFilter
14040
+ }, b.ontology))
14041
+ })
14042
+ ]
14043
+ });
14044
+ };
14045
+ const $f511198465122338$var$GseaViewCmp = (props)=>{
14046
+ const { grameneSearch: grameneSearch, grameneMaps: grameneMaps, grameneTaxonomy: grameneTaxonomy, gsea: gsea, gseaUI: ui, gseaResults: results, doSetGseaActiveTaxon: doSetGseaActiveTaxon, doSetGseaUI: doSetGseaUI, doFetchGseaForeground: doFetchGseaForeground, doFetchGseaBackground: doFetchGseaBackground, doAcceptGrameneSuggestion: doAcceptGrameneSuggestion } = props;
14047
+ const taxa = (0, $gXNCa$react.useMemo)(()=>{
14048
+ if (!grameneSearch || !grameneSearch.facet_counts) return [];
14049
+ const arr = grameneSearch.facet_counts.facet_fields.taxon_id || [];
14050
+ const ids = [];
14051
+ const counts = {};
14052
+ for(let i = 0; i < arr.length; i += 2){
14053
+ ids.push(arr[i]);
14054
+ counts[arr[i]] = +arr[i + 1];
14055
+ }
14056
+ if (grameneMaps) ids.sort((a, b)=>{
14057
+ const ma = grameneMaps[a] || grameneMaps[$f511198465122338$var$speciesTaxonId(a)];
14058
+ const mb = grameneMaps[b] || grameneMaps[$f511198465122338$var$speciesTaxonId(b)];
14059
+ return (ma && ma.left_index || 0) - (mb && mb.left_index || 0);
14060
+ });
14061
+ return ids.map((tid)=>({
14062
+ tid: tid,
14063
+ count: counts[tid]
14064
+ }));
14065
+ }, [
14066
+ grameneSearch,
14067
+ grameneMaps
14068
+ ]);
14069
+ const activeTaxon = gsea.activeTaxon;
14070
+ const [treeWidth, setTreeWidth] = (0, $gXNCa$react.useState)(280);
14071
+ const beginResize = (e)=>{
12428
14072
  e.preventDefault();
12429
- resizeStateRef.current = {
12430
- startY: e.clientY,
12431
- startHeight: plotHeight
12432
- };
14073
+ const startX = e.clientX;
14074
+ const startWidth = treeWidth;
12433
14075
  const onMove = (ev)=>{
12434
- const s = resizeStateRef.current;
12435
- if (!s) return;
12436
- const next = Math.max(120, Math.min(1200, s.startHeight + (ev.clientY - s.startY)));
12437
- setPlotHeight(next);
14076
+ const next = Math.max(150, Math.min(800, startWidth + (ev.clientX - startX)));
14077
+ setTreeWidth(next);
12438
14078
  };
12439
14079
  const onUp = ()=>{
12440
- resizeStateRef.current = null;
12441
14080
  document.removeEventListener('mousemove', onMove);
12442
14081
  document.removeEventListener('mouseup', onUp);
12443
- document.body.style.cursor = '';
12444
14082
  document.body.style.userSelect = '';
14083
+ document.body.style.cursor = '';
12445
14084
  };
12446
- document.body.style.cursor = 'row-resize';
12447
14085
  document.body.style.userSelect = 'none';
14086
+ document.body.style.cursor = 'col-resize';
12448
14087
  document.addEventListener('mousemove', onMove);
12449
14088
  document.addEventListener('mouseup', onUp);
12450
14089
  };
12451
- const hasBrush = Object.keys(selections).length > 0;
12452
- const filteredRows = (0, $gXNCa$react.useMemo)(()=>{
12453
- if (!hasBrush) return rows;
12454
- return rows.filter((r)=>$1fd2507769d5bd00$var$rowMatchesSelections(r, selections));
12455
- }, [
12456
- rows,
12457
- selections,
12458
- hasBrush
12459
- ]);
12460
- // Drop fields with no numeric data in the loaded rows so empty axes/columns
12461
- // don't clutter the visualization. Selected-but-empty fields stay in the
12462
- // underlying selection so a future load can repopulate them.
12463
- const visibleFields = (0, $gXNCa$react.useMemo)(()=>{
12464
- if (rows.length === 0 || selected.length === 0) return selected;
12465
- return selected.filter((f)=>rows.some((r)=>{
12466
- const v = r[f];
12467
- return v != null && !Array.isArray(v) && Number.isFinite(+v);
12468
- }));
14090
+ (0, $gXNCa$react.useEffect)(()=>{
14091
+ if (taxa.length === 0) return;
14092
+ if (!activeTaxon || !taxa.find((t)=>String(t.tid) === String(activeTaxon))) doSetGseaActiveTaxon(taxa[0].tid);
12469
14093
  }, [
12470
- rows,
12471
- selected
12472
- ]);
12473
- const handleReorder = onReorder ? (newVisibleOrder)=>{
12474
- const visibleSet = new Set(newVisibleOrder);
12475
- const hidden = selected.filter((f)=>!visibleSet.has(f));
12476
- onReorder([
12477
- ...newVisibleOrder,
12478
- ...hidden
12479
- ]);
12480
- } : undefined;
12481
- const axisLabels = (0, $gXNCa$react.useMemo)(()=>$1fd2507769d5bd00$var$buildAxisLabels(visibleFields, studies, expressionSamples), [
12482
- visibleFields,
12483
- studies,
12484
- expressionSamples
14094
+ taxa,
14095
+ activeTaxon,
14096
+ doSetGseaActiveTaxon
12485
14097
  ]);
12486
14098
  (0, $gXNCa$react.useEffect)(()=>{
12487
- if (rows.length === 0 && hasBrush) {
12488
- setSelections({});
12489
- setClearVersion((v)=>v + 1);
12490
- }
14099
+ if (!activeTaxon) return;
14100
+ const t = gsea.byTaxon[activeTaxon];
14101
+ if (!t) return;
14102
+ if (t.fg.status === 'idle') doFetchGseaForeground(activeTaxon);
14103
+ if (t.bg.status === 'idle') doFetchGseaBackground(activeTaxon);
12491
14104
  }, [
12492
- rows.length,
12493
- hasBrush
14105
+ activeTaxon,
14106
+ gsea,
14107
+ doFetchGseaForeground,
14108
+ doFetchGseaBackground
12494
14109
  ]);
14110
+ if (taxa.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
14111
+ className: "gsea-view",
14112
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
14113
+ children: "No species in the current results."
14114
+ })
14115
+ });
14116
+ if (!grameneTaxonomy) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
14117
+ className: "gsea-view",
14118
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
14119
+ children: "Loading taxonomy\u2026"
14120
+ })
14121
+ });
14122
+ const handleAddFilter = (row)=>{
14123
+ if (!doAcceptGrameneSuggestion) return;
14124
+ const label = row.term_name ? `${row.term_display_id} ${row.term_name}` : row.term_display_id;
14125
+ doAcceptGrameneSuggestion({
14126
+ fq_field: row.field,
14127
+ fq_value: String(row.term_id),
14128
+ name: label,
14129
+ category: row.ontology_label
14130
+ });
14131
+ };
12495
14132
  return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12496
- className: "exprviz-tab-panel",
14133
+ className: "gsea-view gsea-layout",
12497
14134
  children: [
12498
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12499
- className: "exprviz-toolbar",
12500
- children: [
12501
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Button), {
12502
- size: "sm",
12503
- onClick: onOpenFields,
12504
- children: [
12505
- "Select fields (",
12506
- selected.length,
12507
- " selected, ",
12508
- studies.length,
12509
- " studies)"
12510
- ]
12511
- }),
12512
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
12513
- size: "sm",
12514
- variant: "primary",
12515
- disabled: selected.length === 0 || fetchInfo.status === 'loading',
12516
- onClick: onLoad,
12517
- children: fetchInfo.status === 'loading' ? "Loading\u2026" : 'Load data'
12518
- }),
12519
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.ToggleButtonGroup), {
12520
- type: "radio",
12521
- name: `exprviz-scale-${taxon}`,
12522
- size: "sm",
12523
- value: scale,
12524
- onChange: setScale,
12525
- children: [
12526
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.ToggleButton), {
12527
- id: `exprviz-scale-${taxon}-lin`,
12528
- value: "linear",
12529
- variant: "outline-secondary",
12530
- children: "Linear"
12531
- }),
12532
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.ToggleButton), {
12533
- id: `exprviz-scale-${taxon}-log`,
12534
- value: "log",
12535
- variant: "outline-secondary",
12536
- children: "Log"
12537
- })
12538
- ]
12539
- }),
12540
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
12541
- size: "sm",
12542
- variant: "outline-secondary",
12543
- disabled: !hasBrush,
12544
- onClick: ()=>{
12545
- setClearVersion((v)=>v + 1);
12546
- setSelections({});
12547
- },
12548
- children: "Clear brushes"
12549
- }),
12550
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
12551
- size: "sm",
12552
- variant: "outline-secondary",
12553
- disabled: filteredRows.length === 0 || visibleFields.length === 0,
12554
- onClick: ()=>$1fd2507769d5bd00$var$downloadTsv(`expression_${taxon}.tsv`, filteredRows, visibleFields),
12555
- title: "Download the visible rows and columns as tab-delimited text",
12556
- children: "Download TSV"
12557
- }),
12558
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
12559
- size: "sm",
12560
- variant: "success",
12561
- disabled: !hasBrush || !onAddRangeQuery,
12562
- onClick: ()=>{
12563
- const terms = Object.keys(selections).map((field)=>{
12564
- const [lo, hi] = selections[field];
12565
- return {
12566
- category: 'Expression',
12567
- name: `${field}: ${$1fd2507769d5bd00$var$fmt(lo)}\u{2013}${$1fd2507769d5bd00$var$fmt(hi)}`,
12568
- fq_field: field,
12569
- fq_value: `[${lo} TO ${hi}]`
12570
- };
12571
- });
12572
- onAddRangeQuery(terms);
12573
- },
12574
- title: "Add brush ranges as an AND-conjunction filter on the search",
12575
- children: "Apply as filter"
12576
- }),
12577
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
12578
- className: "exprviz-status",
12579
- children: [
12580
- hasBrush ? `${filteredRows.length} of ${rows.length}` : rows.length,
12581
- fetchInfo.total ? ` / ${fetchInfo.total}` : '',
12582
- " genes",
12583
- hasBrush ? ' (brushed)' : ' loaded'
12584
- ]
12585
- })
12586
- ]
14135
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
14136
+ className: "gsea-layout-tree",
14137
+ style: {
14138
+ flex: `0 0 ${treeWidth}px`
14139
+ },
14140
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SpeciesTree, {
14141
+ taxonomy: grameneTaxonomy,
14142
+ grameneMaps: grameneMaps,
14143
+ taxa: taxa,
14144
+ activeTaxon: activeTaxon,
14145
+ onSelect: (tid)=>doSetGseaActiveTaxon(String(tid))
14146
+ })
12587
14147
  }),
12588
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
12589
- className: "exprviz-body",
12590
- children: [
12591
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12592
- className: "exprviz-plot",
12593
- style: {
12594
- height: plotHeight
12595
- },
12596
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $caf32827df861c4e$export$2e2bcd8739ae039), {
12597
- rows: rows,
12598
- fields: visibleFields,
12599
- scale: scale,
12600
- onBrushChange: setSelections,
12601
- onReorder: handleReorder,
12602
- clearVersion: clearVersion,
12603
- hoveredId: hoveredId,
12604
- axisLabels: axisLabels
12605
- })
12606
- }),
12607
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12608
- className: "exprviz-resizer",
12609
- role: "separator",
12610
- "aria-orientation": "horizontal",
12611
- "aria-label": "Resize plot",
12612
- onMouseDown: startResize,
12613
- title: "Drag to resize"
12614
- }),
12615
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
12616
- className: "exprviz-table",
12617
- children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $4ab64e76c1caef59$export$2e2bcd8739ae039), {
12618
- rows: filteredRows,
12619
- fields: visibleFields,
12620
- onReorder: handleReorder,
12621
- studies: studies,
12622
- expressionSamples: expressionSamples,
12623
- onHoverRow: setHoveredId
12624
- })
12625
- })
12626
- ]
14148
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
14149
+ className: "gsea-splitter",
14150
+ onMouseDown: beginResize,
14151
+ title: "Drag to resize"
14152
+ }),
14153
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
14154
+ className: "gsea-layout-panel",
14155
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$TaxonPanel, {
14156
+ taxon: activeTaxon ? String(activeTaxon) : null,
14157
+ gsea: gsea,
14158
+ results: results,
14159
+ ui: ui,
14160
+ onUiChange: doSetGseaUI,
14161
+ onAddFilter: handleAddFilter
14162
+ })
12627
14163
  })
12628
14164
  ]
12629
14165
  });
12630
14166
  };
12631
- var $1fd2507769d5bd00$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectExprViz', 'selectExprVizPivot', 'selectExprVizActiveTaxon', 'selectGrameneMaps', 'selectExpressionStudies', 'selectExpressionSamples', 'doSetExprVizActiveTaxon', 'doToggleExprVizFieldsModal', 'doFetchExprVizData', 'doReorderExprVizFields', 'doAddGrameneRangeQuery', $1fd2507769d5bd00$var$ExprVizViewCmp);
14167
+ var $f511198465122338$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneSearch', 'selectGrameneMaps', 'selectGrameneTaxonomy', 'selectGsea', 'selectGseaUI', 'selectGseaResults', 'doSetGseaActiveTaxon', 'doSetGseaUI', 'doFetchGseaForeground', 'doFetchGseaBackground', 'doAcceptGrameneSuggestion', $f511198465122338$var$GseaViewCmp);
12632
14168
 
12633
14169
 
12634
14170
 
@@ -12749,6 +14285,7 @@ const $693dd8c7a5607c3a$var$inventory = {
12749
14285
  attribs: (0, $67bf5a43401bffdc$export$2e2bcd8739ae039),
12750
14286
  expression: (0, $261baeb81c4d4d8a$export$2e2bcd8739ae039),
12751
14287
  exprViz: (0, $1fd2507769d5bd00$export$2e2bcd8739ae039),
14288
+ gsea: (0, $f511198465122338$export$2e2bcd8739ae039),
12752
14289
  userLists: (0, $0f50f369018a42ef$export$2e2bcd8739ae039),
12753
14290
  export: (0, $37b3bb0145d266b0$export$2e2bcd8739ae039)
12754
14291
  };