From b9de70f157164c69272b2b4667009d2fb83370e3 Mon Sep 17 00:00:00 2001 From: Colin Diesh Date: Fri, 28 Oct 2022 15:01:42 -0600 Subject: [PATCH 001/183] Upgrade deps (#3283) --- package.json | 18 +- .../__snapshots__/index.test.js.snap | 26 +- .../__snapshots__/index.test.js.snap | 108 +- .../ConfigurationEditor.test.tsx.snap | 178 +- .../AddConnectionWidget.test.js.snap | 10 +- .../HierarchicalTrackSelector.test.tsx.snap | 14 +- .../PluginStoreWidget.test.js.snap | 70 +- .../components/LinearGenomeViewSvg.tsx | 39 +- .../LinearGenomeView.test.tsx.snap | 408 +-- .../src/LinearGenomeView/index.tsx | 2 + .../__snapshots__/AboutWidget.test.js.snap | 6 +- .../__snapshots__/HelpWidget.test.js.snap | 4 +- .../__snapshots__/SessionManager.test.js.snap | 4 +- .../VariantFeatureWidget.test.js.snap | 50 +- products/jbrowse-desktop/package.json | 6 +- products/jbrowse-img/package.json | 2 + products/jbrowse-img/src/renderRegion.js | 18 +- .../test/svg_configtracks_local.svg | 2 +- .../test/svg_from_volvox_fasta_and_bam.svg | 2 +- ..._from_volvox_fasta_and_bam_norasterize.svg | 2 +- .../package.json | 2 +- .../JBrowseCircularGenomeView.test.tsx.snap | 32 +- .../package.json | 2 +- .../JBrowseLinearGenomeView.test.tsx.snap | 332 +- products/jbrowse-web/package.json | 4 +- .../tests/__image_snapshots__/snapshot.svg | 2 +- .../__snapshots__/ExportSvg.test.tsx.snap | 2 +- yarn.lock | 2785 ++++++++--------- 28 files changed, 2024 insertions(+), 2106 deletions(-) diff --git a/package.json b/package.json index f0f452f9c3..6e48c7298c 100644 --- a/package.json +++ b/package.json @@ -44,13 +44,13 @@ "@mui/material": "^5.0.0", "@oclif/dev-cli": "^1.26.9", "@oclif/test": "^1.2.7", - "@storybook/addon-actions": "^6.5.0", - "@storybook/addon-docs": "^6.5.0", - "@storybook/addon-links": "^6.5.0", - "@storybook/addons": "^6.5.0", - "@storybook/builder-webpack5": "^6.5.0", - "@storybook/manager-webpack5": "^6.5.0", - "@storybook/react": "^6.5.0", + "@storybook/addon-actions": "^6.5.13", + "@storybook/addon-docs": "^6.5.13", + "@storybook/addon-links": "^6.5.13", + "@storybook/addons": "^6.5.13", + "@storybook/builder-webpack5": "^6.5.13", + "@storybook/manager-webpack5": "^6.5.13", + "@storybook/react": "^6.5.13", "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.0.0", "@types/base64-js": "^1.2.5", @@ -105,7 +105,7 @@ "cross-env": "^7.0.2", "cross-spawn": "^7.0.1", "dependency-graph": "^0.11.0", - "electron": "20.1.4", + "electron": "21.2.0", "electron-builder": "^23.0.3", "electron-mock-ipc": "^0.3.8", "electron-notarize": "^1.0.0", @@ -131,7 +131,7 @@ "mobx-react": "^7.0.0", "mobx-state-tree": "^5.0.0", "nock": "^13.2.1", - "node-polyfill-webpack-plugin": "^1.1.4", + "node-polyfill-webpack-plugin": "^2.0.1", "npm-run-all": "^4.1.5", "prettier": "^2.5.1", "prop-types": "^15.0.0", diff --git a/packages/core/BaseFeatureWidget/__snapshots__/index.test.js.snap b/packages/core/BaseFeatureWidget/__snapshots__/index.test.js.snap index e9d88c7ceb..be087e7d75 100644 --- a/packages/core/BaseFeatureWidget/__snapshots__/index.test.js.snap +++ b/packages/core/BaseFeatureWidget/__snapshots__/index.test.js.snap @@ -25,7 +25,7 @@ exports[`open up a widget 1`] = ` >

Position
ctgA:3..102 (+) @@ -75,15 +75,15 @@ exports[`open up a widget 1`] = `
Length
100 @@ -99,16 +99,16 @@ exports[`open up a widget 1`] = ` Attributes

score
37 @@ -116,7 +116,7 @@ exports[`open up a widget 1`] = `
MQ
37 @@ -213,16 +213,16 @@ exports[`open up a widget 1`] = `
CIGAR
100M @@ -230,16 +230,16 @@ exports[`open up a widget 1`] = `
length_on_ref
100 @@ -247,16 +247,16 @@ exports[`open up a widget 1`] = `
template_length
0 @@ -264,16 +264,16 @@ exports[`open up a widget 1`] = `
seq_length
100 @@ -281,7 +281,7 @@ exports[`open up a widget 1`] = `
+

+ The code for this app is available at + https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-view-vanillajs +

diff --git a/demos/jbrowse-react-linear-genome-view-vanillajs/package.json b/embedded_demos/jbrowse-react-linear-genome-view-vanillajs/package.json similarity index 91% rename from demos/jbrowse-react-linear-genome-view-vanillajs/package.json rename to embedded_demos/jbrowse-react-linear-genome-view-vanillajs/package.json index b26b4e6c42..ea3fe9c080 100644 --- a/demos/jbrowse-react-linear-genome-view-vanillajs/package.json +++ b/embedded_demos/jbrowse-react-linear-genome-view-vanillajs/package.json @@ -1,4 +1,5 @@ { + "license": "MIT", "scripts": { "deploy": "aws s3 sync --delete . s3://jbrowse.org/demos/lgv-vanillajs/", "postdeploy": "aws cloudfront create-invalidation --distribution-id E13LGELJOT4GQO --paths \"/demos/lgv-vanillajs/*\"" diff --git a/embedded_demos/jbrowse-react-linear-genome-view-vanillajs/tracks.js b/embedded_demos/jbrowse-react-linear-genome-view-vanillajs/tracks.js new file mode 100644 index 0000000000..fb6422b271 --- /dev/null +++ b/embedded_demos/jbrowse-react-linear-genome-view-vanillajs/tracks.js @@ -0,0 +1,113 @@ +const tracks = [ + { + type: 'FeatureTrack', + trackId: 'genes', + name: 'NCBI RefSeq Genes', + assemblyNames: ['GRCh38'], + category: ['Genes'], + adapter: { + type: 'Gff3TabixAdapter', + gffGzLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz', + }, + index: { + location: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.tbi', + }, + }, + }, + textSearching: { + textSearchAdapter: { + type: 'TrixTextSearchAdapter', + textSearchAdapterId: 'gff3tabix_genes-index', + ixFilePath: { + uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/trix/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.ix', + }, + ixxFilePath: { + uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/trix/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.ixx', + }, + metaFilePath: { + uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/trix/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz_meta.json', + }, + assemblyNames: ['GRCh38'], + }, + }, + }, + { + type: 'FeatureTrack', + trackId: 'repeats_hg38', + name: 'Repeats', + assemblyNames: ['hg38'], + category: ['Annotation'], + adapter: { + type: 'BigBedAdapter', + bigBedLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/repeats.bb', + locationType: 'UriLocation', + }, + }, + }, + { + type: 'AlignmentsTrack', + trackId: 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome', + name: 'NA12878 Exome', + assemblyNames: ['GRCh38'], + category: ['1000 Genomes', 'Alignments'], + adapter: { + type: 'CramAdapter', + cramLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/alignments/NA12878/NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome.cram', + }, + craiLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/alignments/NA12878/NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome.cram.crai', + }, + sequenceAdapter: { + type: 'BgzipFastaAdapter', + fastaLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz', + }, + faiLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz.fai', + }, + gziLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz.gzi', + }, + }, + }, + }, + { + type: 'VariantTrack', + trackId: + 'ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf', + name: '1000 Genomes Variant Calls', + assemblyNames: ['GRCh38'], + category: ['1000 Genomes', 'Variants'], + adapter: { + type: 'VcfTabixAdapter', + vcfGzLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/variants/ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf.gz', + }, + index: { + location: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/variants/ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf.gz.tbi', + }, + }, + }, + }, + { + type: 'QuantitativeTrack', + trackId: 'hg38.100way.phyloP100way', + name: 'hg38.100way.phyloP100way', + category: ['Conservation'], + assemblyNames: ['hg38'], + adapter: { + type: 'BigWigAdapter', + bigWigLocation: { + uri: 'https://hgdownload.cse.ucsc.edu/goldenpath/hg38/phyloP100way/hg38.phyloP100way.bw', + locationType: 'UriLocation', + }, + }, + }, +] + +export default tracks diff --git a/demos/jbrowse-react-linear-genome-view-vanillajs/yarn.lock b/embedded_demos/jbrowse-react-linear-genome-view-vanillajs/yarn.lock similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vanillajs/yarn.lock rename to embedded_demos/jbrowse-react-linear-genome-view-vanillajs/yarn.lock diff --git a/demos/jbrowse-react-linear-genome-view-vite/.gitignore b/embedded_demos/jbrowse-react-linear-genome-view-vite/.gitignore similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/.gitignore rename to embedded_demos/jbrowse-react-linear-genome-view-vite/.gitignore diff --git a/demos/jbrowse-react-linear-genome-view-vite/README.md b/embedded_demos/jbrowse-react-linear-genome-view-vite/README.md similarity index 89% rename from demos/jbrowse-react-linear-genome-view-vite/README.md rename to embedded_demos/jbrowse-react-linear-genome-view-vite/README.md index 32dd04c4ff..f22011de90 100644 --- a/demos/jbrowse-react-linear-genome-view-vite/README.md +++ b/embedded_demos/jbrowse-react-linear-genome-view-vite/README.md @@ -17,7 +17,7 @@ including the Buffer polyfill See this app running at https://jbrowse.org/demos/lgv-vite/. Download this directory from the monorepo using -https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-linear-genome-view-vite +https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-vite ## Usage diff --git a/demos/jbrowse-react-linear-genome-view-vite/index.html b/embedded_demos/jbrowse-react-linear-genome-view-vite/index.html similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/index.html rename to embedded_demos/jbrowse-react-linear-genome-view-vite/index.html diff --git a/demos/jbrowse-react-linear-genome-view-vite/package.json b/embedded_demos/jbrowse-react-linear-genome-view-vite/package.json similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/package.json rename to embedded_demos/jbrowse-react-linear-genome-view-vite/package.json diff --git a/demos/jbrowse-react-linear-genome-view-vite/src/App.tsx b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/App.tsx similarity index 58% rename from demos/jbrowse-react-linear-genome-view-vite/src/App.tsx rename to embedded_demos/jbrowse-react-linear-genome-view-vite/src/App.tsx index efeb8996f0..11cd54243e 100644 --- a/demos/jbrowse-react-linear-genome-view-vite/src/App.tsx +++ b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/App.tsx @@ -7,68 +7,12 @@ import { import assembly from './assembly' import tracks from './tracks' +import defaultSession from './defaultSession' -const defaultSession = { - name: 'this session', - view: { - id: 'linearGenomeView', - type: 'LinearGenomeView', - tracks: [ - { - id: '7PWx6ki1_', - type: 'ReferenceSequenceTrack', - configuration: 'GRCh38-ReferenceSequenceTrack', - displays: [ - { - id: 'pa_7lx6FDh', - type: 'LinearReferenceSequenceDisplay', - height: 210, - configuration: - 'GRCh38-ReferenceSequenceTrack-LinearReferenceSequenceDisplay', - }, - ], - }, - { - id: 'KHwe41KXk', - type: 'AlignmentsTrack', - configuration: 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome', - displays: [ - { - id: '_-kwYVczT8', - type: 'LinearAlignmentsDisplay', - PileupDisplay: { - id: '1HTk32IDZJ', - type: 'LinearPileupDisplay', - height: 100, - configuration: { - type: 'LinearPileupDisplay', - displayId: - 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay_pileup_xyz', - }, - }, - SNPCoverageDisplay: { - id: 'ZBXRXmuDrc', - type: 'LinearSNPCoverageDisplay', - height: 45, - configuration: { - type: 'LinearSNPCoverageDisplay', - displayId: - 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay_snpcoverage_xyz', - }, - }, - configuration: - 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay', - height: 250, - }, - ], - }, - ], - }, -} +type ViewModel = ReturnType function View() { - const [viewState, setViewState] = - useState>() + const [viewState, setViewState] = useState() const [patches, setPatches] = useState('') const [stateSnapshot, setStateSnapshot] = useState('') @@ -76,11 +20,10 @@ function View() { const state = createViewState({ assembly, tracks, - location: '10:29,838,655..29,838,737', + defaultSession, onChange: patch => { setPatches(previous => previous + JSON.stringify(patch) + '\n') }, - defaultSession, }) setViewState(state) }, []) @@ -97,11 +40,11 @@ function View() {

The code for this app is available at{' '} - https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-linear-genome-view-vite + https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-vite .

diff --git a/demos/jbrowse-react-linear-genome-view-nextjs/utils/assembly.ts b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/assembly.ts similarity index 75% rename from demos/jbrowse-react-linear-genome-view-nextjs/utils/assembly.ts rename to embedded_demos/jbrowse-react-linear-genome-view-vite/src/assembly.ts index 5c26ea9164..af9f6755d9 100644 --- a/demos/jbrowse-react-linear-genome-view-nextjs/utils/assembly.ts +++ b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/assembly.ts @@ -16,16 +16,6 @@ const assembly = { uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz.gzi', }, }, - displays: [ - { - type: 'LinearReferenceSequenceDisplay', - displayId: - 'GRCh38-ReferenceSequenceTrack-LinearReferenceSequenceDisplay', - renderer: { - type: 'DivSequenceRenderer', - }, - }, - ], }, refNameAliases: { adapter: { diff --git a/embedded_demos/jbrowse-react-linear-genome-view-vite/src/defaultSession.ts b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/defaultSession.ts new file mode 100644 index 0000000000..d9e0281ccc --- /dev/null +++ b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/defaultSession.ts @@ -0,0 +1,130 @@ +export default { + name: 'this session', + margin: 0, + view: { + id: 'linearGenomeView', + minimized: false, + type: 'LinearGenomeView', + offsetPx: 191980240, + bpPerPx: 0.1554251851851852, + displayedRegions: [ + { + refName: '10', + start: 0, + end: 133797422, + reversed: false, + assemblyName: 'GRCh38', + }, + ], + tracks: [ + { + id: '4aZAiE-A3', + type: 'ReferenceSequenceTrack', + configuration: 'GRCh38-ReferenceSequenceTrack', + minimized: false, + displays: [ + { + id: 'AD3gqvG0_6', + type: 'LinearReferenceSequenceDisplay', + height: 180, + configuration: + 'GRCh38-ReferenceSequenceTrack-LinearReferenceSequenceDisplay', + showForward: true, + showReverse: true, + showTranslation: true, + }, + ], + }, + { + id: 'T6uhrtY40O', + type: 'AlignmentsTrack', + configuration: 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome', + minimized: false, + displays: [ + { + id: 'FinKswChSr', + type: 'LinearAlignmentsDisplay', + PileupDisplay: { + id: 'YAAaF494z', + type: 'LinearPileupDisplay', + height: 134, + configuration: { + type: 'LinearPileupDisplay', + displayId: + 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay_LinearPileupDisplay_xyz', + }, + showSoftClipping: false, + filterBy: { + flagInclude: 0, + flagExclude: 1540, + }, + }, + SNPCoverageDisplay: { + id: 'VTQ_VGbAVJ', + type: 'LinearSNPCoverageDisplay', + height: 45, + configuration: { + type: 'LinearSNPCoverageDisplay', + displayId: + 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay_snpcoverage_xyz', + }, + selectedRendering: '', + resolution: 1, + constraints: {}, + filterBy: { + flagInclude: 0, + flagExclude: 1540, + }, + }, + snpCovHeight: 45, + configuration: + 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay', + height: 179, + lowerPanelType: 'LinearPileupDisplay', + }, + ], + }, + { + id: 'EUnTnpVI6', + type: 'QuantitativeTrack', + configuration: 'hg38.100way.phyloP100way', + minimized: false, + displays: [ + { + id: 'mrlawr9Wtg', + type: 'LinearWiggleDisplay', + height: 100, + configuration: 'hg38.100way.phyloP100way-LinearWiggleDisplay', + selectedRendering: '', + resolution: 1, + constraints: {}, + }, + ], + }, + { + id: 'Cbnwl72EX', + type: 'VariantTrack', + configuration: + 'ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf', + minimized: false, + displays: [ + { + id: 'dvXz01Wf6w', + type: 'LinearVariantDisplay', + height: 100, + configuration: + 'ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf-LinearVariantDisplay', + }, + ], + }, + ], + hideHeader: false, + hideHeaderOverview: false, + hideNoTracksActive: false, + trackSelectorType: 'hierarchical', + trackLabels: 'overlapping', + showCenterLine: false, + showCytobandsSetting: true, + showGridlines: true, + }, +} diff --git a/demos/jbrowse-react-linear-genome-view-vite/src/favicon.svg b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/favicon.svg similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/src/favicon.svg rename to embedded_demos/jbrowse-react-linear-genome-view-vite/src/favicon.svg diff --git a/demos/jbrowse-react-linear-genome-view-vite/src/index.css b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/index.css similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/src/index.css rename to embedded_demos/jbrowse-react-linear-genome-view-vite/src/index.css diff --git a/demos/jbrowse-react-linear-genome-view-vite/src/logo.svg b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/logo.svg similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/src/logo.svg rename to embedded_demos/jbrowse-react-linear-genome-view-vite/src/logo.svg diff --git a/demos/jbrowse-react-linear-genome-view-vite/src/main.tsx b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/main.tsx similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/src/main.tsx rename to embedded_demos/jbrowse-react-linear-genome-view-vite/src/main.tsx diff --git a/demos/jbrowse-react-linear-genome-view-vite/src/tracks.ts b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/tracks.ts similarity index 60% rename from demos/jbrowse-react-linear-genome-view-vite/src/tracks.ts rename to embedded_demos/jbrowse-react-linear-genome-view-vite/src/tracks.ts index c3489ea36a..fb6422b271 100644 --- a/demos/jbrowse-react-linear-genome-view-vite/src/tracks.ts +++ b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/tracks.ts @@ -1,8 +1,7 @@ const tracks = [ { - type: 'BasicTrack', - trackId: - 'GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff', + type: 'FeatureTrack', + trackId: 'genes', name: 'NCBI RefSeq Genes', assemblyNames: ['GRCh38'], category: ['Genes'], @@ -17,6 +16,36 @@ const tracks = [ }, }, }, + textSearching: { + textSearchAdapter: { + type: 'TrixTextSearchAdapter', + textSearchAdapterId: 'gff3tabix_genes-index', + ixFilePath: { + uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/trix/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.ix', + }, + ixxFilePath: { + uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/trix/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.ixx', + }, + metaFilePath: { + uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/trix/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz_meta.json', + }, + assemblyNames: ['GRCh38'], + }, + }, + }, + { + type: 'FeatureTrack', + trackId: 'repeats_hg38', + name: 'Repeats', + assemblyNames: ['hg38'], + category: ['Annotation'], + adapter: { + type: 'BigBedAdapter', + bigBedLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/repeats.bb', + locationType: 'UriLocation', + }, + }, }, { type: 'AlignmentsTrack', @@ -65,6 +94,20 @@ const tracks = [ }, }, }, + { + type: 'QuantitativeTrack', + trackId: 'hg38.100way.phyloP100way', + name: 'hg38.100way.phyloP100way', + category: ['Conservation'], + assemblyNames: ['hg38'], + adapter: { + type: 'BigWigAdapter', + bigWigLocation: { + uri: 'https://hgdownload.cse.ucsc.edu/goldenpath/hg38/phyloP100way/hg38.phyloP100way.bw', + locationType: 'UriLocation', + }, + }, + }, ] export default tracks diff --git a/demos/jbrowse-react-linear-genome-view-vite/src/vite-env.d.ts b/embedded_demos/jbrowse-react-linear-genome-view-vite/src/vite-env.d.ts similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/src/vite-env.d.ts rename to embedded_demos/jbrowse-react-linear-genome-view-vite/src/vite-env.d.ts diff --git a/demos/jbrowse-react-linear-genome-view-vite/tsconfig.json b/embedded_demos/jbrowse-react-linear-genome-view-vite/tsconfig.json similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/tsconfig.json rename to embedded_demos/jbrowse-react-linear-genome-view-vite/tsconfig.json diff --git a/demos/jbrowse-react-linear-genome-view-vite/vite.config.ts b/embedded_demos/jbrowse-react-linear-genome-view-vite/vite.config.ts similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/vite.config.ts rename to embedded_demos/jbrowse-react-linear-genome-view-vite/vite.config.ts diff --git a/demos/jbrowse-react-linear-genome-view-vite/yarn.lock b/embedded_demos/jbrowse-react-linear-genome-view-vite/yarn.lock similarity index 100% rename from demos/jbrowse-react-linear-genome-view-vite/yarn.lock rename to embedded_demos/jbrowse-react-linear-genome-view-vite/yarn.lock diff --git a/demos/jbrowse-react-linear-genome-view/.env b/embedded_demos/jbrowse-react-linear-genome-view/.env similarity index 100% rename from demos/jbrowse-react-linear-genome-view/.env rename to embedded_demos/jbrowse-react-linear-genome-view/.env diff --git a/demos/jbrowse-react-linear-genome-view/.gitignore b/embedded_demos/jbrowse-react-linear-genome-view/.gitignore similarity index 100% rename from demos/jbrowse-react-linear-genome-view/.gitignore rename to embedded_demos/jbrowse-react-linear-genome-view/.gitignore diff --git a/demos/jbrowse-react-linear-genome-view/README.md b/embedded_demos/jbrowse-react-linear-genome-view/README.md similarity index 85% rename from demos/jbrowse-react-linear-genome-view/README.md rename to embedded_demos/jbrowse-react-linear-genome-view/README.md index 09eed45f14..b3c0abee73 100644 --- a/demos/jbrowse-react-linear-genome-view/README.md +++ b/embedded_demos/jbrowse-react-linear-genome-view/README.md @@ -4,7 +4,7 @@ See this app running at https://jbrowse.org/demos/lgv/. This demo is originally by [@garrettjstevens](https://github.com/garrettjstevens/). -Download this directory from the monorepo using https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-linear-genome-view +Download this directory from the monorepo using https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view ## Usage diff --git a/demos/jbrowse-react-linear-genome-view/package.json b/embedded_demos/jbrowse-react-linear-genome-view/package.json similarity index 98% rename from demos/jbrowse-react-linear-genome-view/package.json rename to embedded_demos/jbrowse-react-linear-genome-view/package.json index 427d9207e8..caadf8d5d5 100644 --- a/demos/jbrowse-react-linear-genome-view/package.json +++ b/embedded_demos/jbrowse-react-linear-genome-view/package.json @@ -1,6 +1,7 @@ { "name": "jbrowse-react-linear-genome-view-demo-site", "version": "0.1.0", + "license": "MIT", "private": true, "dependencies": { "@fontsource/roboto": "^4.5.3", diff --git a/demos/jbrowse-react-linear-genome-view/public/favicon.ico b/embedded_demos/jbrowse-react-linear-genome-view/public/favicon.ico similarity index 100% rename from demos/jbrowse-react-linear-genome-view/public/favicon.ico rename to embedded_demos/jbrowse-react-linear-genome-view/public/favicon.ico diff --git a/demos/jbrowse-react-linear-genome-view/public/index.html b/embedded_demos/jbrowse-react-linear-genome-view/public/index.html similarity index 100% rename from demos/jbrowse-react-linear-genome-view/public/index.html rename to embedded_demos/jbrowse-react-linear-genome-view/public/index.html diff --git a/demos/jbrowse-react-linear-genome-view/public/logo192.png b/embedded_demos/jbrowse-react-linear-genome-view/public/logo192.png similarity index 100% rename from demos/jbrowse-react-linear-genome-view/public/logo192.png rename to embedded_demos/jbrowse-react-linear-genome-view/public/logo192.png diff --git a/demos/jbrowse-react-linear-genome-view/public/logo512.png b/embedded_demos/jbrowse-react-linear-genome-view/public/logo512.png similarity index 100% rename from demos/jbrowse-react-linear-genome-view/public/logo512.png rename to embedded_demos/jbrowse-react-linear-genome-view/public/logo512.png diff --git a/demos/jbrowse-react-linear-genome-view/public/manifest.json b/embedded_demos/jbrowse-react-linear-genome-view/public/manifest.json similarity index 100% rename from demos/jbrowse-react-linear-genome-view/public/manifest.json rename to embedded_demos/jbrowse-react-linear-genome-view/public/manifest.json diff --git a/demos/jbrowse-react-linear-genome-view/public/robots.txt b/embedded_demos/jbrowse-react-linear-genome-view/public/robots.txt similarity index 100% rename from demos/jbrowse-react-linear-genome-view/public/robots.txt rename to embedded_demos/jbrowse-react-linear-genome-view/public/robots.txt diff --git a/demos/jbrowse-react-linear-genome-view/src/App.tsx b/embedded_demos/jbrowse-react-linear-genome-view/src/App.tsx similarity index 60% rename from demos/jbrowse-react-linear-genome-view/src/App.tsx rename to embedded_demos/jbrowse-react-linear-genome-view/src/App.tsx index 21e8bbce8a..7ed29782b5 100644 --- a/demos/jbrowse-react-linear-genome-view/src/App.tsx +++ b/embedded_demos/jbrowse-react-linear-genome-view/src/App.tsx @@ -7,64 +7,7 @@ import { import assembly from './assembly' import tracks from './tracks' - -const defaultSession = { - name: 'this session', - view: { - id: 'linearGenomeView', - type: 'LinearGenomeView', - tracks: [ - { - id: '7PWx6ki1_', - type: 'ReferenceSequenceTrack', - configuration: 'GRCh38-ReferenceSequenceTrack', - displays: [ - { - id: 'pa_7lx6FDh', - type: 'LinearReferenceSequenceDisplay', - height: 210, - configuration: - 'GRCh38-ReferenceSequenceTrack-LinearReferenceSequenceDisplay', - }, - ], - }, - { - id: 'KHwe41KXk', - type: 'AlignmentsTrack', - configuration: 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome', - displays: [ - { - id: '_-kwYVczT8', - type: 'LinearAlignmentsDisplay', - PileupDisplay: { - id: '1HTk32IDZJ', - type: 'LinearPileupDisplay', - height: 100, - configuration: { - type: 'LinearPileupDisplay', - displayId: - 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay_pileup_xyz', - }, - }, - SNPCoverageDisplay: { - id: 'ZBXRXmuDrc', - type: 'LinearSNPCoverageDisplay', - height: 45, - configuration: { - type: 'LinearSNPCoverageDisplay', - displayId: - 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay_snpcoverage_xyz', - }, - }, - configuration: - 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay', - height: 250, - }, - ], - }, - ], - }, -} +import defaultSession from './defaultSession' type ViewModel = ReturnType @@ -77,7 +20,6 @@ function View() { const state = createViewState({ assembly, tracks, - location: '10:29,838,655..29,838,737', onChange: (patch: any) => { setPatches((previous) => previous + JSON.stringify(patch) + '\n') }, @@ -100,11 +42,11 @@ function View() {

The code for this app is available at{' '} - https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-linear-genome-view + https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view .

diff --git a/embedded_demos/jbrowse-react-linear-genome-view/src/assembly.ts b/embedded_demos/jbrowse-react-linear-genome-view/src/assembly.ts new file mode 100644 index 0000000000..af9f6755d9 --- /dev/null +++ b/embedded_demos/jbrowse-react-linear-genome-view/src/assembly.ts @@ -0,0 +1,30 @@ +const assembly = { + name: 'GRCh38', + aliases: ['hg38'], + sequence: { + type: 'ReferenceSequenceTrack', + trackId: 'GRCh38-ReferenceSequenceTrack', + adapter: { + type: 'BgzipFastaAdapter', + fastaLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz', + }, + faiLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz.fai', + }, + gziLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz.gzi', + }, + }, + }, + refNameAliases: { + adapter: { + type: 'RefNameAliasAdapter', + location: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/hg38_aliases.txt', + }, + }, + }, +} + +export default assembly diff --git a/demos/jbrowse-react-linear-genome-view/src/declare.d.ts b/embedded_demos/jbrowse-react-linear-genome-view/src/declare.d.ts similarity index 100% rename from demos/jbrowse-react-linear-genome-view/src/declare.d.ts rename to embedded_demos/jbrowse-react-linear-genome-view/src/declare.d.ts diff --git a/embedded_demos/jbrowse-react-linear-genome-view/src/defaultSession.ts b/embedded_demos/jbrowse-react-linear-genome-view/src/defaultSession.ts new file mode 100644 index 0000000000..d9e0281ccc --- /dev/null +++ b/embedded_demos/jbrowse-react-linear-genome-view/src/defaultSession.ts @@ -0,0 +1,130 @@ +export default { + name: 'this session', + margin: 0, + view: { + id: 'linearGenomeView', + minimized: false, + type: 'LinearGenomeView', + offsetPx: 191980240, + bpPerPx: 0.1554251851851852, + displayedRegions: [ + { + refName: '10', + start: 0, + end: 133797422, + reversed: false, + assemblyName: 'GRCh38', + }, + ], + tracks: [ + { + id: '4aZAiE-A3', + type: 'ReferenceSequenceTrack', + configuration: 'GRCh38-ReferenceSequenceTrack', + minimized: false, + displays: [ + { + id: 'AD3gqvG0_6', + type: 'LinearReferenceSequenceDisplay', + height: 180, + configuration: + 'GRCh38-ReferenceSequenceTrack-LinearReferenceSequenceDisplay', + showForward: true, + showReverse: true, + showTranslation: true, + }, + ], + }, + { + id: 'T6uhrtY40O', + type: 'AlignmentsTrack', + configuration: 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome', + minimized: false, + displays: [ + { + id: 'FinKswChSr', + type: 'LinearAlignmentsDisplay', + PileupDisplay: { + id: 'YAAaF494z', + type: 'LinearPileupDisplay', + height: 134, + configuration: { + type: 'LinearPileupDisplay', + displayId: + 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay_LinearPileupDisplay_xyz', + }, + showSoftClipping: false, + filterBy: { + flagInclude: 0, + flagExclude: 1540, + }, + }, + SNPCoverageDisplay: { + id: 'VTQ_VGbAVJ', + type: 'LinearSNPCoverageDisplay', + height: 45, + configuration: { + type: 'LinearSNPCoverageDisplay', + displayId: + 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay_snpcoverage_xyz', + }, + selectedRendering: '', + resolution: 1, + constraints: {}, + filterBy: { + flagInclude: 0, + flagExclude: 1540, + }, + }, + snpCovHeight: 45, + configuration: + 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome-LinearAlignmentsDisplay', + height: 179, + lowerPanelType: 'LinearPileupDisplay', + }, + ], + }, + { + id: 'EUnTnpVI6', + type: 'QuantitativeTrack', + configuration: 'hg38.100way.phyloP100way', + minimized: false, + displays: [ + { + id: 'mrlawr9Wtg', + type: 'LinearWiggleDisplay', + height: 100, + configuration: 'hg38.100way.phyloP100way-LinearWiggleDisplay', + selectedRendering: '', + resolution: 1, + constraints: {}, + }, + ], + }, + { + id: 'Cbnwl72EX', + type: 'VariantTrack', + configuration: + 'ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf', + minimized: false, + displays: [ + { + id: 'dvXz01Wf6w', + type: 'LinearVariantDisplay', + height: 100, + configuration: + 'ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf-LinearVariantDisplay', + }, + ], + }, + ], + hideHeader: false, + hideHeaderOverview: false, + hideNoTracksActive: false, + trackSelectorType: 'hierarchical', + trackLabels: 'overlapping', + showCenterLine: false, + showCytobandsSetting: true, + showGridlines: true, + }, +} diff --git a/demos/jbrowse-react-linear-genome-view/src/index.css b/embedded_demos/jbrowse-react-linear-genome-view/src/index.css similarity index 100% rename from demos/jbrowse-react-linear-genome-view/src/index.css rename to embedded_demos/jbrowse-react-linear-genome-view/src/index.css diff --git a/embedded_demos/jbrowse-react-linear-genome-view/src/index.tsx b/embedded_demos/jbrowse-react-linear-genome-view/src/index.tsx new file mode 100644 index 0000000000..3992ef9741 --- /dev/null +++ b/embedded_demos/jbrowse-react-linear-genome-view/src/index.tsx @@ -0,0 +1,11 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import './index.css' +import App from './App' + +ReactDOM.render( + + + , + document.getElementById('root'), +) diff --git a/demos/jbrowse-react-linear-genome-view/src/react-app-env.d.ts b/embedded_demos/jbrowse-react-linear-genome-view/src/react-app-env.d.ts similarity index 100% rename from demos/jbrowse-react-linear-genome-view/src/react-app-env.d.ts rename to embedded_demos/jbrowse-react-linear-genome-view/src/react-app-env.d.ts diff --git a/embedded_demos/jbrowse-react-linear-genome-view/src/tracks.ts b/embedded_demos/jbrowse-react-linear-genome-view/src/tracks.ts new file mode 100644 index 0000000000..fb6422b271 --- /dev/null +++ b/embedded_demos/jbrowse-react-linear-genome-view/src/tracks.ts @@ -0,0 +1,113 @@ +const tracks = [ + { + type: 'FeatureTrack', + trackId: 'genes', + name: 'NCBI RefSeq Genes', + assemblyNames: ['GRCh38'], + category: ['Genes'], + adapter: { + type: 'Gff3TabixAdapter', + gffGzLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz', + }, + index: { + location: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.tbi', + }, + }, + }, + textSearching: { + textSearchAdapter: { + type: 'TrixTextSearchAdapter', + textSearchAdapterId: 'gff3tabix_genes-index', + ixFilePath: { + uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/trix/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.ix', + }, + ixxFilePath: { + uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/trix/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.ixx', + }, + metaFilePath: { + uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/trix/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz_meta.json', + }, + assemblyNames: ['GRCh38'], + }, + }, + }, + { + type: 'FeatureTrack', + trackId: 'repeats_hg38', + name: 'Repeats', + assemblyNames: ['hg38'], + category: ['Annotation'], + adapter: { + type: 'BigBedAdapter', + bigBedLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/repeats.bb', + locationType: 'UriLocation', + }, + }, + }, + { + type: 'AlignmentsTrack', + trackId: 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome', + name: 'NA12878 Exome', + assemblyNames: ['GRCh38'], + category: ['1000 Genomes', 'Alignments'], + adapter: { + type: 'CramAdapter', + cramLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/alignments/NA12878/NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome.cram', + }, + craiLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/alignments/NA12878/NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome.cram.crai', + }, + sequenceAdapter: { + type: 'BgzipFastaAdapter', + fastaLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz', + }, + faiLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz.fai', + }, + gziLocation: { + uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz.gzi', + }, + }, + }, + }, + { + type: 'VariantTrack', + trackId: + 'ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf', + name: '1000 Genomes Variant Calls', + assemblyNames: ['GRCh38'], + category: ['1000 Genomes', 'Variants'], + adapter: { + type: 'VcfTabixAdapter', + vcfGzLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/variants/ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf.gz', + }, + index: { + location: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/variants/ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf.gz.tbi', + }, + }, + }, + }, + { + type: 'QuantitativeTrack', + trackId: 'hg38.100way.phyloP100way', + name: 'hg38.100way.phyloP100way', + category: ['Conservation'], + assemblyNames: ['hg38'], + adapter: { + type: 'BigWigAdapter', + bigWigLocation: { + uri: 'https://hgdownload.cse.ucsc.edu/goldenpath/hg38/phyloP100way/hg38.phyloP100way.bw', + locationType: 'UriLocation', + }, + }, + }, +] + +export default tracks diff --git a/demos/jbrowse-react-linear-genome-view/tsconfig.json b/embedded_demos/jbrowse-react-linear-genome-view/tsconfig.json similarity index 100% rename from demos/jbrowse-react-linear-genome-view/tsconfig.json rename to embedded_demos/jbrowse-react-linear-genome-view/tsconfig.json diff --git a/demos/jbrowse-react-linear-genome-view/yarn.lock b/embedded_demos/jbrowse-react-linear-genome-view/yarn.lock similarity index 100% rename from demos/jbrowse-react-linear-genome-view/yarn.lock rename to embedded_demos/jbrowse-react-linear-genome-view/yarn.lock diff --git a/demos/update_demos.sh b/embedded_demos/update_demos.sh similarity index 100% rename from demos/update_demos.sh rename to embedded_demos/update_demos.sh diff --git a/website/docs/embedded_components.md b/website/docs/embedded_components.md index 5a9e4c572b..9046d37e2d 100644 --- a/website/docs/embedded_components.md +++ b/website/docs/embedded_components.md @@ -19,13 +19,13 @@ This component consists of a single JBrowse 2 linear view. Here is a table of different usages of the `@jbrowse/react-linear-genome-view` using different bundlers -| Bundler | Demo | Source code | Note | -| ------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| create-react-app v4 | [demo](https://jbrowse.org/demos/lgv/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-linear-genome-view) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-linear-genome-view)) | no polyfills needed in create-react-app v4. on newer versions of node, you can need to use `export NODE_OPTIONS=--openssl-legacy-provider` before building cra4 apps | -| create-react-app v5 | [demo](https://jbrowse.org/demos/lgv-cra5/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-linear-genome-view-cra5) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-linear-genome-view-cra5)) | for create-react-app v5, we use craco to update the webpack config to polyfill some node modules. This demo also uses webworkers, which is a unique ability with webpack 5. See https://jbrowse.org/storybook/lgv/main/?path=/story/linear-view--with-web-worker for details | -| vite | [demo](https://jbrowse.org/demos/lgv-vite) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-linear-genome-view-vite) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-linear-genome-view-vite)) | for vite, we use rollup to polyfill some node polyfills similar to craco in create-react-app v5. note, may not work with newly released vite 3.x, works in dev but fails in production so this example uses vite 2.x | -| next.js | [demo](https://jbrowse.org/demos/lgv-nextjs) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-linear-genome-view-nextjs) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-linear-genome-view-nextjs)) | uses next.js 12. Also see next.config.js to update basePath as needed | -| vanilla js | [demo](https://jbrowse.org/demos/lgv-vanillajs) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-linear-genome-view-vanillajs) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-linear-genome-view-vanillajs)) | uses a script tag to include a UMD bundle, and doesn't require any transpilation or bundling. see also dev tutorial here https://jbrowse.org/jb2/docs/tutorials/embed_linear_genome_view/01_introduction/ | +| Bundler | Demo | Source code | Note | +| ------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| create-react-app v4 | [demo](https://jbrowse.org/demos/lgv/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view)) | no polyfills needed in create-react-app v4. on newer versions of node, you can need to use `export NODE_OPTIONS=--openssl-legacy-provider` before building cra4 apps | +| create-react-app v5 | [demo](https://jbrowse.org/demos/lgv-cra5/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-cra5) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-cra5)) | for create-react-app v5, we use craco to update the webpack config to polyfill some node modules. This demo also uses webworkers, which is a unique ability with webpack 5. See https://jbrowse.org/storybook/lgv/main/?path=/story/linear-view--with-web-worker for details | +| vite | [demo](https://jbrowse.org/demos/lgv-vite) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-vite) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-vite)) | for vite, we use rollup to polyfill some node polyfills similar to craco in create-react-app v5. note, may not work with newly released vite 3.x, works in dev but fails in production so this example uses vite 2.x | +| next.js | [demo](https://jbrowse.org/demos/lgv-nextjs) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-nextjs) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-nextjs)) | uses next.js 13. Currently is hardcoded to use /demos/lgv-nextjs/ as sub-uri, update next.config.js to customize as needed | +| vanilla js | [demo](https://jbrowse.org/demos/lgv-vanillajs) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-vanillajs) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-vanillajs)) | uses a script tag to include a UMD bundle, and doesn't require any transpilation or bundling. see also dev tutorial here https://jbrowse.org/jb2/docs/tutorials/embed_linear_genome_view/01_introduction/ | ## JBrowse React Circular Genome View @@ -39,8 +39,8 @@ This component consists of a single JBrowse 2 circular view. Here is a table of different usages of the `@jbrowse/react-circular-genome-view` using different bundlers -| Syntax | Demo | Source code | Note | -| ------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| create-react-app v4 | [demo](https://jbrowse.org/demos/cgv/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-circular-genome-view) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-circular-genome-view)) | no polyfills needed in create-react-app v4. on newer versions of node, you can need to use `export NODE_OPTIONS=--openssl-legacy-provider` before building cra4 apps | -| create-react-app v5 | [demo](https://jbrowse.org/demos/cgv-cra5/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-circular-genome-view-cra5) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-circular-genome-view-cra5)) | for create-react-app v5, we use craco to update the webpack config to polyfill some node modules | -| vanilla js | [demo](https://jbrowse.org/demos/cgv-vanillajs) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/demos/jbrowse-react-circular-genome-view-vanillajs) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fdemos%2Fjbrowse-react-circular-genome-view-vanillajs)) | uses a script tag to include a UMD bundle, and doesn't require any transpilation or bundling | +| Syntax | Demo | Source code | Note | +| ------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| create-react-app v4 | [demo](https://jbrowse.org/demos/cgv/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-circular-genome-view) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-circular-genome-view)) | no polyfills needed in create-react-app v4. on newer versions of node, you can need to use `export NODE_OPTIONS=--openssl-legacy-provider` before building cra4 apps | +| create-react-app v5 | [demo](https://jbrowse.org/demos/cgv-cra5/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-circular-genome-view-cra5) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-circular-genome-view-cra5)) | for create-react-app v5, we use craco to update the webpack config to polyfill some node modules | +| vanilla js | [demo](https://jbrowse.org/demos/cgv-vanillajs) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-circular-genome-view-vanillajs) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-circular-genome-view-vanillajs)) | uses a script tag to include a UMD bundle, and doesn't require any transpilation or bundling | From 79f0ac1d91809382c4b478e1da0dc47597bc9c8d Mon Sep 17 00:00:00 2001 From: Colin Date: Fri, 16 Dec 2022 16:11:47 -0700 Subject: [PATCH 154/183] [update docs] Update embedded_demo yarn.locks --- .../yarn.lock | 40 +++++++++---------- .../yarn.lock | 18 ++++----- .../yarn.lock | 34 ++++++++-------- .../yarn.lock | 16 ++++---- .../yarn.lock | 18 ++++----- .../yarn.lock | 18 ++++----- 6 files changed, 72 insertions(+), 72 deletions(-) diff --git a/embedded_demos/jbrowse-react-circular-genome-view-cra5/yarn.lock b/embedded_demos/jbrowse-react-circular-genome-view-cra5/yarn.lock index 1b4ae46c72..b0a4515555 100644 --- a/embedded_demos/jbrowse-react-circular-genome-view-cra5/yarn.lock +++ b/embedded_demos/jbrowse-react-circular-genome-view-cra5/yarn.lock @@ -1318,14 +1318,14 @@ integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== "@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== + version "1.4.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.0.tgz#8ec64e0df3e7a1971ee1ff5158da87389f167a63" + integrity sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A== dependencies: ajv "^6.12.4" debug "^4.3.2" espree "^9.4.0" - globals "^13.15.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" @@ -1994,9 +1994,9 @@ react-is "^18.2.0" "@mui/x-data-grid@^5.0.1": - version "5.17.14" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.14.tgz#3475d45ba3b18bdb343bbc954346324b2f935df0" - integrity sha512-Cf3yeaEAaFzpWPoDO1YTHWL2Anz69pUnIw4Swa6UehvNwJIz3ZDoVf92xCytviugU14NmL95PjmtHqK72Jt05g== + version "5.17.16" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.16.tgz#4e05a014467bbee68385b5bee1e75004ba0178a3" + integrity sha512-tzgjvO0DgiYWqU9soM6ORurtOrNsqpmdRxIjqJguLHxs2nP5hyhGEDRaHQIfEPv9eXNl9QL3kBO3VfgaFS5NPg== dependencies: "@babel/runtime" "^7.18.9" "@mui/utils" "^5.10.3" @@ -2489,14 +2489,14 @@ integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== "@types/node@*": - version "18.11.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.15.tgz#de0e1fbd2b22b962d45971431e2ae696643d3f5d" - integrity sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw== + version "18.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.16.tgz#966cae211e970199559cfbd295888fca189e49af" + integrity sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA== "@types/node@^16.11.26": - version "16.18.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.9.tgz#47c491cfbc10460571d766c16526748fa9ad96a1" - integrity sha512-nhrqXYxiQ+5B/tPorWum37VgAiefi/wmfJ1QZKGKKecC8/3HqcTTJD0O+VABSPwtseMMF7NCPVT9uGgwn0YqsQ== + version "16.18.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.10.tgz#d7415ef18c94f8d4e4a82ebcc8b8999f965d8920" + integrity sha512-XU1+v7h81p7145ddPfjv7jtWvkSilpcnON3mQ+bDi9Yuf7OI56efOglXRyXWgQ57xH3fEQgh7WOJMncRHVew5w== "@types/parse-json@^4.0.0": version "4.0.0" @@ -5604,7 +5604,7 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: +globals@^13.15.0, globals@^13.19.0: version "13.19.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== @@ -6971,9 +6971,9 @@ json5@^1.0.1: minimist "^1.2.0" json5@^2.1.2, json5@^2.2.0, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" + integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== jsonfile@^6.0.1: version "6.1.0" @@ -8300,9 +8300,9 @@ postcss-normalize@^10.0.1: sanitize.css "*" postcss-opacity-percentage@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz#bd698bb3670a0a27f6d657cc16744b3ebf3b1145" - integrity sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w== + version "1.1.3" + resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz#5b89b35551a556e20c5d23eb5260fbfcf5245da6" + integrity sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A== postcss-ordered-values@^5.1.3: version "5.1.3" diff --git a/embedded_demos/jbrowse-react-circular-genome-view/yarn.lock b/embedded_demos/jbrowse-react-circular-genome-view/yarn.lock index 9df16243b3..fd7d31277f 100644 --- a/embedded_demos/jbrowse-react-circular-genome-view/yarn.lock +++ b/embedded_demos/jbrowse-react-circular-genome-view/yarn.lock @@ -1880,9 +1880,9 @@ react-is "^18.2.0" "@mui/x-data-grid@^5.0.1": - version "5.17.14" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.14.tgz#3475d45ba3b18bdb343bbc954346324b2f935df0" - integrity sha512-Cf3yeaEAaFzpWPoDO1YTHWL2Anz69pUnIw4Swa6UehvNwJIz3ZDoVf92xCytviugU14NmL95PjmtHqK72Jt05g== + version "5.17.16" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.16.tgz#4e05a014467bbee68385b5bee1e75004ba0178a3" + integrity sha512-tzgjvO0DgiYWqU9soM6ORurtOrNsqpmdRxIjqJguLHxs2nP5hyhGEDRaHQIfEPv9eXNl9QL3kBO3VfgaFS5NPg== dependencies: "@babel/runtime" "^7.18.9" "@mui/utils" "^5.10.3" @@ -2278,9 +2278,9 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*": - version "18.11.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.15.tgz#de0e1fbd2b22b962d45971431e2ae696643d3f5d" - integrity sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw== + version "18.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.16.tgz#966cae211e970199559cfbd295888fca189e49af" + integrity sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA== "@types/node@^12.0.0": version "12.20.55" @@ -7684,9 +7684,9 @@ json5@^1.0.1: minimist "^1.2.0" json5@^2.1.2, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" + integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== jsonfile@^4.0.0: version "4.0.0" diff --git a/embedded_demos/jbrowse-react-linear-genome-view-cra5/yarn.lock b/embedded_demos/jbrowse-react-linear-genome-view-cra5/yarn.lock index 847dcf65e7..a9df436522 100644 --- a/embedded_demos/jbrowse-react-linear-genome-view-cra5/yarn.lock +++ b/embedded_demos/jbrowse-react-linear-genome-view-cra5/yarn.lock @@ -1318,14 +1318,14 @@ integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== "@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== + version "1.4.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.0.tgz#8ec64e0df3e7a1971ee1ff5158da87389f167a63" + integrity sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A== dependencies: ajv "^6.12.4" debug "^4.3.2" espree "^9.4.0" - globals "^13.15.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" @@ -2128,9 +2128,9 @@ react-is "^18.2.0" "@mui/x-data-grid@^5.0.1": - version "5.17.14" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.14.tgz#3475d45ba3b18bdb343bbc954346324b2f935df0" - integrity sha512-Cf3yeaEAaFzpWPoDO1YTHWL2Anz69pUnIw4Swa6UehvNwJIz3ZDoVf92xCytviugU14NmL95PjmtHqK72Jt05g== + version "5.17.16" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.16.tgz#4e05a014467bbee68385b5bee1e75004ba0178a3" + integrity sha512-tzgjvO0DgiYWqU9soM6ORurtOrNsqpmdRxIjqJguLHxs2nP5hyhGEDRaHQIfEPv9eXNl9QL3kBO3VfgaFS5NPg== dependencies: "@babel/runtime" "^7.18.9" "@mui/utils" "^5.10.3" @@ -2613,9 +2613,9 @@ integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== "@types/node@*", "@types/node@^18.11.9": - version "18.11.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.15.tgz#de0e1fbd2b22b962d45971431e2ae696643d3f5d" - integrity sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw== + version "18.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.16.tgz#966cae211e970199559cfbd295888fca189e49af" + integrity sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA== "@types/parse-json@^4.0.0": version "4.0.0" @@ -5760,7 +5760,7 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: +globals@^13.15.0, globals@^13.19.0: version "13.19.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== @@ -7149,9 +7149,9 @@ json5@^1.0.1: minimist "^1.2.0" json5@^2.1.2, json5@^2.2.0, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" + integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== jsonfile@^6.0.1: version "6.1.0" @@ -8488,9 +8488,9 @@ postcss-normalize@^10.0.1: sanitize.css "*" postcss-opacity-percentage@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz#bd698bb3670a0a27f6d657cc16744b3ebf3b1145" - integrity sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w== + version "1.1.3" + resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz#5b89b35551a556e20c5d23eb5260fbfcf5245da6" + integrity sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A== postcss-ordered-values@^5.1.3: version "5.1.3" diff --git a/embedded_demos/jbrowse-react-linear-genome-view-nextjs/yarn.lock b/embedded_demos/jbrowse-react-linear-genome-view-nextjs/yarn.lock index 42b44dd0e9..dfd7181849 100644 --- a/embedded_demos/jbrowse-react-linear-genome-view-nextjs/yarn.lock +++ b/embedded_demos/jbrowse-react-linear-genome-view-nextjs/yarn.lock @@ -180,14 +180,14 @@ integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== "@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== + version "1.4.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.0.tgz#8ec64e0df3e7a1971ee1ff5158da87389f167a63" + integrity sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A== dependencies: ajv "^6.12.4" debug "^4.3.2" espree "^9.4.0" - globals "^13.15.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" @@ -677,9 +677,9 @@ react-is "^18.2.0" "@mui/x-data-grid@^5.0.1": - version "5.17.14" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.14.tgz#3475d45ba3b18bdb343bbc954346324b2f935df0" - integrity sha512-Cf3yeaEAaFzpWPoDO1YTHWL2Anz69pUnIw4Swa6UehvNwJIz3ZDoVf92xCytviugU14NmL95PjmtHqK72Jt05g== + version "5.17.16" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.16.tgz#4e05a014467bbee68385b5bee1e75004ba0178a3" + integrity sha512-tzgjvO0DgiYWqU9soM6ORurtOrNsqpmdRxIjqJguLHxs2nP5hyhGEDRaHQIfEPv9eXNl9QL3kBO3VfgaFS5NPg== dependencies: "@babel/runtime" "^7.18.9" "@mui/utils" "^5.10.3" @@ -1878,7 +1878,7 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^13.15.0: +globals@^13.15.0, globals@^13.19.0: version "13.19.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== diff --git a/embedded_demos/jbrowse-react-linear-genome-view-vite/yarn.lock b/embedded_demos/jbrowse-react-linear-genome-view-vite/yarn.lock index 72e52de46f..3cbdaab77d 100644 --- a/embedded_demos/jbrowse-react-linear-genome-view-vite/yarn.lock +++ b/embedded_demos/jbrowse-react-linear-genome-view-vite/yarn.lock @@ -957,9 +957,9 @@ react-is "^18.2.0" "@mui/x-data-grid@^5.0.1": - version "5.17.14" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.14.tgz#3475d45ba3b18bdb343bbc954346324b2f935df0" - integrity sha512-Cf3yeaEAaFzpWPoDO1YTHWL2Anz69pUnIw4Swa6UehvNwJIz3ZDoVf92xCytviugU14NmL95PjmtHqK72Jt05g== + version "5.17.16" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.16.tgz#4e05a014467bbee68385b5bee1e75004ba0178a3" + integrity sha512-tzgjvO0DgiYWqU9soM6ORurtOrNsqpmdRxIjqJguLHxs2nP5hyhGEDRaHQIfEPv9eXNl9QL3kBO3VfgaFS5NPg== dependencies: "@babel/runtime" "^7.18.9" "@mui/utils" "^5.10.3" @@ -973,9 +973,9 @@ integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== "@types/node@^18.11.12": - version "18.11.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.15.tgz#de0e1fbd2b22b962d45971431e2ae696643d3f5d" - integrity sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw== + version "18.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.16.tgz#966cae211e970199559cfbd295888fca189e49af" + integrity sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA== "@types/parse-json@^4.0.0": version "4.0.0" @@ -1561,9 +1561,9 @@ json-stable-stringify@^1.0.1: jsonify "^0.0.1" json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" + integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== jsonify@^0.0.1: version "0.0.1" diff --git a/embedded_demos/jbrowse-react-linear-genome-view/yarn.lock b/embedded_demos/jbrowse-react-linear-genome-view/yarn.lock index f173cb9d33..8314341f0c 100644 --- a/embedded_demos/jbrowse-react-linear-genome-view/yarn.lock +++ b/embedded_demos/jbrowse-react-linear-genome-view/yarn.lock @@ -2014,9 +2014,9 @@ react-is "^18.2.0" "@mui/x-data-grid@^5.0.1": - version "5.17.14" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.14.tgz#3475d45ba3b18bdb343bbc954346324b2f935df0" - integrity sha512-Cf3yeaEAaFzpWPoDO1YTHWL2Anz69pUnIw4Swa6UehvNwJIz3ZDoVf92xCytviugU14NmL95PjmtHqK72Jt05g== + version "5.17.16" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.16.tgz#4e05a014467bbee68385b5bee1e75004ba0178a3" + integrity sha512-tzgjvO0DgiYWqU9soM6ORurtOrNsqpmdRxIjqJguLHxs2nP5hyhGEDRaHQIfEPv9eXNl9QL3kBO3VfgaFS5NPg== dependencies: "@babel/runtime" "^7.18.9" "@mui/utils" "^5.10.3" @@ -2412,9 +2412,9 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*": - version "18.11.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.15.tgz#de0e1fbd2b22b962d45971431e2ae696643d3f5d" - integrity sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw== + version "18.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.16.tgz#966cae211e970199559cfbd295888fca189e49af" + integrity sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA== "@types/node@^12.0.0": version "12.20.55" @@ -7857,9 +7857,9 @@ json5@^1.0.1: minimist "^1.2.0" json5@^2.1.2, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" + integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== jsonfile@^4.0.0: version "4.0.0" From cc6a0eb7c2a0be3a31c05e368c883fe741e27ed7 Mon Sep 17 00:00:00 2001 From: Colin Date: Fri, 16 Dec 2022 16:22:25 -0700 Subject: [PATCH 155/183] [update docs] Remove comment about vite and fix shields.io Updates --- .eslintignore | 2 +- README.md | 2 +- embedded_demos/base/linear/index.tsx | 11 ----------- tsconfig.json | 2 +- website/docs/embedded_components.md | 2 +- 5 files changed, 4 insertions(+), 15 deletions(-) delete mode 100644 embedded_demos/base/linear/index.tsx diff --git a/.eslintignore b/.eslintignore index d5d3bde528..0708397ceb 100644 --- a/.eslintignore +++ b/.eslintignore @@ -13,7 +13,7 @@ products/jbrowse-web/scripts/** /products/jbrowse-web/config/ products/jbrowse-desktop/public/electron.js products/jbrowse-desktop/public/generateFastaIndex.js -/demos/ +/embedded_demos/ webpack.config.js craco.config.js packages/core/util/QuickLRU.js diff --git a/README.md b/README.md index c436b4bb3a..3d5bc3f778 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://img.shields.io/github/workflow/status/GMOD/jbrowse-components/Push/main?logo=github&style=for-the-badge)](https://github.com/GMOD/jbrowse-components/actions?query=branch%3Amain+workflow%3APush+) +[![Build Status](https://img.shields.io/github/actions/workflow/status/GMOD/jbrowse-components/push.yml?branch=main))](https://github.com/GMOD/jbrowse-components/actions) [![Coverage Status](https://img.shields.io/codecov/c/github/GMOD/jbrowse-components/main.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/GMOD/jbrowse-components/branch/main) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjx0aXRsZT5Db250cmlidXRvciBDb3ZlbmFudCBMb2dvPC90aXRsZT48ZyBpZD0iQ2FudmFzIj48ZyBpZD0iR3JvdXAiPjxnIGlkPSJTdWJ0cmFjdCI+PHVzZSB4bGluazpocmVmPSIjcGF0aDBfZmlsbCIgZmlsbD0iIzVFMEQ3MyIvPjwvZz48ZyBpZD0iU3VidHJhY3QiPjx1c2UgeGxpbms6aHJlZj0iI3BhdGgxX2ZpbGwiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU4IDI0KSIgZmlsbD0iIzVFMEQ3MyIvPjwvZz48L2c+PC9nPjxkZWZzPjxwYXRoIGlkPSJwYXRoMF9maWxsIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0gMTgyLjc4NyAxMi4yODQ2QyAxNzMuMDA1IDkuNDk0MDggMTYyLjY3NyA4IDE1MiA4QyA5MC4xNDQxIDggNDAgNTguMTQ0MSA0MCAxMjBDIDQwIDE4MS44NTYgOTAuMTQ0MSAyMzIgMTUyIDIzMkMgMTg4LjQ2NCAyMzIgMjIwLjg1NyAyMTQuNTc1IDI0MS4zMDggMTg3LjU5OEMgMjE5Ljg3IDIyOC4yNzIgMTc3LjE3MyAyNTYgMTI4IDI1NkMgNTcuMzA3NSAyNTYgMCAxOTguNjkyIDAgMTI4QyAwIDU3LjMwNzUgNTcuMzA3NSAwIDEyOCAwQyAxNDcuNjA0IDAgMTY2LjE3OSA0LjQwNzA5IDE4Mi43ODcgMTIuMjg0NloiLz48cGF0aCBpZD0icGF0aDFfZmlsbCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNIDEzNy4wOSA5LjIxMzQyQyAxMjkuNzU0IDcuMTIwNTYgMTIyLjAwOCA2IDExNCA2QyA2Ny42MDgxIDYgMzAgNDMuNjA4MSAzMCA5MEMgMzAgMTM2LjM5MiA2Ny42MDgxIDE3NCAxMTQgMTc0QyAxNDEuMzQ4IDE3NCAxNjUuNjQzIDE2MC45MzEgMTgwLjk4MSAxNDAuNjk4QyAxNjQuOTAzIDE3MS4yMDQgMTMyLjg4IDE5MiA5NiAxOTJDIDQyLjk4MDcgMTkyIDAgMTQ5LjAxOSAwIDk2QyAwIDQyLjk4MDcgNDIuOTgwNyAwIDk2IDBDIDExMC43MDMgMCAxMjQuNjM0IDMuMzA1MzEgMTM3LjA5IDkuMjEzNDJaIi8+PC9kZWZzPjwvc3ZnPg==)](CODE_OF_CONDUCT.md) diff --git a/embedded_demos/base/linear/index.tsx b/embedded_demos/base/linear/index.tsx deleted file mode 100644 index 3992ef9741..0000000000 --- a/embedded_demos/base/linear/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import './index.css' -import App from './App' - -ReactDOM.render( - - - , - document.getElementById('root'), -) diff --git a/tsconfig.json b/tsconfig.json index fbe3e3826d..429b790071 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,7 @@ "**/lib/", "**/umd/", "**/tmp/", - "**/demos/", + "**/embedded_demos/", "**/component_tests/", "**/plugin-development-tools/", "**/webpack.config.js" diff --git a/website/docs/embedded_components.md b/website/docs/embedded_components.md index 9046d37e2d..7e55ab9ff3 100644 --- a/website/docs/embedded_components.md +++ b/website/docs/embedded_components.md @@ -23,7 +23,7 @@ using different bundlers | ------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | create-react-app v4 | [demo](https://jbrowse.org/demos/lgv/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view)) | no polyfills needed in create-react-app v4. on newer versions of node, you can need to use `export NODE_OPTIONS=--openssl-legacy-provider` before building cra4 apps | | create-react-app v5 | [demo](https://jbrowse.org/demos/lgv-cra5/) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-cra5) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-cra5)) | for create-react-app v5, we use craco to update the webpack config to polyfill some node modules. This demo also uses webworkers, which is a unique ability with webpack 5. See https://jbrowse.org/storybook/lgv/main/?path=/story/linear-view--with-web-worker for details | -| vite | [demo](https://jbrowse.org/demos/lgv-vite) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-vite) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-vite)) | for vite, we use rollup to polyfill some node polyfills similar to craco in create-react-app v5. note, may not work with newly released vite 3.x, works in dev but fails in production so this example uses vite 2.x | +| vite | [demo](https://jbrowse.org/demos/lgv-vite) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-vite) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-vite)) | for vite, we use rollup to polyfill some node polyfills similar to craco in create-react-app v5. | | next.js | [demo](https://jbrowse.org/demos/lgv-nextjs) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-nextjs) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-nextjs)) | uses next.js 13. Currently is hardcoded to use /demos/lgv-nextjs/ as sub-uri, update next.config.js to customize as needed | | vanilla js | [demo](https://jbrowse.org/demos/lgv-vanillajs) | [source code](https://github.com/GMOD/jbrowse-components/tree/main/embedded_demos/jbrowse-react-linear-genome-view-vanillajs) ([download](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2FGMOD%2Fjbrowse-components%2Ftree%2Fmain%2Fembedded_demos%2Fjbrowse-react-linear-genome-view-vanillajs)) | uses a script tag to include a UMD bundle, and doesn't require any transpilation or bundling. see also dev tutorial here https://jbrowse.org/jb2/docs/tutorials/embed_linear_genome_view/01_introduction/ | From cccd5240fa3ce5f7c00037983f79e959a0392bfe Mon Sep 17 00:00:00 2001 From: Colin Date: Fri, 16 Dec 2022 16:39:40 -0700 Subject: [PATCH 156/183] [skip ci] Fix shields.io badge again --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d5bc3f778..7e08e22963 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://img.shields.io/github/actions/workflow/status/GMOD/jbrowse-components/push.yml?branch=main))](https://github.com/GMOD/jbrowse-components/actions) +[![Build Status](https://img.shields.io/github/actions/workflow/status/GMOD/jbrowse-components/push.yml?branch=main&logo=github&style=for-the-badge)](https://github.com/GMOD/jbrowse-components/actions) [![Coverage Status](https://img.shields.io/codecov/c/github/GMOD/jbrowse-components/main.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/GMOD/jbrowse-components/branch/main) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjx0aXRsZT5Db250cmlidXRvciBDb3ZlbmFudCBMb2dvPC90aXRsZT48ZyBpZD0iQ2FudmFzIj48ZyBpZD0iR3JvdXAiPjxnIGlkPSJTdWJ0cmFjdCI+PHVzZSB4bGluazpocmVmPSIjcGF0aDBfZmlsbCIgZmlsbD0iIzVFMEQ3MyIvPjwvZz48ZyBpZD0iU3VidHJhY3QiPjx1c2UgeGxpbms6aHJlZj0iI3BhdGgxX2ZpbGwiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU4IDI0KSIgZmlsbD0iIzVFMEQ3MyIvPjwvZz48L2c+PC9nPjxkZWZzPjxwYXRoIGlkPSJwYXRoMF9maWxsIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0gMTgyLjc4NyAxMi4yODQ2QyAxNzMuMDA1IDkuNDk0MDggMTYyLjY3NyA4IDE1MiA4QyA5MC4xNDQxIDggNDAgNTguMTQ0MSA0MCAxMjBDIDQwIDE4MS44NTYgOTAuMTQ0MSAyMzIgMTUyIDIzMkMgMTg4LjQ2NCAyMzIgMjIwLjg1NyAyMTQuNTc1IDI0MS4zMDggMTg3LjU5OEMgMjE5Ljg3IDIyOC4yNzIgMTc3LjE3MyAyNTYgMTI4IDI1NkMgNTcuMzA3NSAyNTYgMCAxOTguNjkyIDAgMTI4QyAwIDU3LjMwNzUgNTcuMzA3NSAwIDEyOCAwQyAxNDcuNjA0IDAgMTY2LjE3OSA0LjQwNzA5IDE4Mi43ODcgMTIuMjg0NloiLz48cGF0aCBpZD0icGF0aDFfZmlsbCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNIDEzNy4wOSA5LjIxMzQyQyAxMjkuNzU0IDcuMTIwNTYgMTIyLjAwOCA2IDExNCA2QyA2Ny42MDgxIDYgMzAgNDMuNjA4MSAzMCA5MEMgMzAgMTM2LjM5MiA2Ny42MDgxIDE3NCAxMTQgMTc0QyAxNDEuMzQ4IDE3NCAxNjUuNjQzIDE2MC45MzEgMTgwLjk4MSAxNDAuNjk4QyAxNjQuOTAzIDE3MS4yMDQgMTMyLjg4IDE5MiA5NiAxOTJDIDQyLjk4MDcgMTkyIDAgMTQ5LjAxOSAwIDk2QyAwIDQyLjk4MDcgNDIuOTgwNyAwIDk2IDBDIDExMC43MDMgMCAxMjQuNjM0IDMuMzA1MzEgMTM3LjA5IDkuMjEzNDJaIi8+PC9kZWZzPjwvc3ZnPg==)](CODE_OF_CONDUCT.md) From 029e42a746af32f1dbcff220e82761d0653140da Mon Sep 17 00:00:00 2001 From: Colin Diesh Date: Sat, 17 Dec 2022 15:53:09 -0700 Subject: [PATCH 157/183] Fix circular view being rendered as a blank area if tab is opened in the background (#3415) --- .../components/Tooltip.tsx | 149 +-- .../CircularView/components/CircularView.tsx | 249 ++--- .../src/CircularView/components/Controls.tsx | 106 +++ .../CircularView/components/ImportForm.tsx | 15 +- .../src/CircularView/models/CircularView.ts | 869 +++++++++--------- .../components/Tooltip.tsx | 81 +- website/docs/config/GCContentAdapter.md | 5 +- .../config/TheTrackHubRegistryConnection.md | 28 - website/docs/models/LinearReadArcsDisplay.md | 68 +- website/docs/models/LinearReadCloudDisplay.md | 10 + yarn.lock | 159 ++-- 11 files changed, 907 insertions(+), 832 deletions(-) create mode 100644 plugins/circular-view/src/CircularView/components/Controls.tsx delete mode 100644 website/docs/config/TheTrackHubRegistryConnection.md diff --git a/plugins/alignments/src/LinearSNPCoverageDisplay/components/Tooltip.tsx b/plugins/alignments/src/LinearSNPCoverageDisplay/components/Tooltip.tsx index d2a3706317..ac099d5319 100644 --- a/plugins/alignments/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +++ b/plugins/alignments/src/LinearSNPCoverageDisplay/components/Tooltip.tsx @@ -1,6 +1,6 @@ import React from 'react' import { observer } from 'mobx-react' -import { Feature } from '@jbrowse/core/util/simpleFeature' +import { Feature } from '@jbrowse/core/util' import { Tooltip } from '@jbrowse/plugin-wiggle' type Count = { @@ -29,80 +29,83 @@ type SNPInfo = { const en = (n: number) => n.toLocaleString('en-US') const toP = (s = 0) => +(+s).toFixed(1) const pct = (n: number, total: number) => `${toP((n / (total || 1)) * 100)}%` +interface Props { + feature: Feature +} +const TooltipContents = React.forwardRef(function ( + { feature }, + reactRef, +) { + const start = feature.get('start') + const end = feature.get('end') + const name = feature.get('refName') + const { + refbase, + all, + total, + ref, + '-1': rn1, + '1': r1, + '0': r0, + ...info + } = feature.get('snpinfo') as SNPInfo + const loc = [name, start === end ? en(start) : `${en(start)}..${en(end)}`] + .filter(f => !!f) + .join(':') -const TooltipContents = React.forwardRef( - ({ feature }, reactRef) => { - const start = feature.get('start') - const end = feature.get('end') - const name = feature.get('refName') - const { - refbase, - all, - total, - ref, - '-1': rn1, - '1': r1, - '0': r0, - ...info - } = feature.get('snpinfo') as SNPInfo - const loc = [name, start === end ? en(start) : `${en(start)}..${en(end)}`] - .filter(f => !!f) - .join(':') - - return ( -
- - - - - - - - - - - - - - - - - - - - - - + return ( +
+
{loc}
BaseCount% of TotalStrandsSource
Total{all}
REF {refbase ? `(${refbase.toUpperCase()})` : ''}{ref}{pct(ref, all)} - {rn1 ? `${rn1}(-)` : ''} - {r1 ? `${r1}(+)` : ''} - -
+ + + + + + + + + + + + + + + + + + + + + - {Object.entries(info as unknown as Record).map( - ([key, entry]) => - Object.entries(entry).map(([base, score]) => ( - - - - - - - - )), - )} - -
{loc}
BaseCount% of TotalStrandsSource
Total{all}
REF {refbase ? `(${refbase.toUpperCase()})` : ''}{ref}{pct(ref, all)} + {rn1 ? `${rn1}(-)` : ''} + {r1 ? `${r1}(+)` : ''} + +
{base.toUpperCase()}{score.total} - {base === 'total' || base === 'skip' - ? '---' - : pct(score.total, all)} - - {score['-1'] ? `${score['-1']}(-)` : ''} - {score['1'] ? `${score['1']}(+)` : ''} - {key}
-
- ) - }, -) + {Object.entries(info as unknown as Record).map( + ([key, entry]) => + Object.entries(entry).map(([base, score]) => ( + + {base.toUpperCase()} + {score.total} + + {base === 'total' || base === 'skip' + ? '---' + : pct(score.total, all)} + + + {score['-1'] ? `${score['-1']}(-)` : ''} + {score['1'] ? `${score['1']}(+)` : ''} + + {key} + + )), + )} + + +
+ ) +}) type Coord = [number, number] diff --git a/plugins/circular-view/src/CircularView/components/CircularView.tsx b/plugins/circular-view/src/CircularView/components/CircularView.tsx index 0be10c1b6d..1038beb058 100644 --- a/plugins/circular-view/src/CircularView/components/CircularView.tsx +++ b/plugins/circular-view/src/CircularView/components/CircularView.tsx @@ -1,22 +1,12 @@ import React from 'react' import { observer } from 'mobx-react' -import { IconButton } from '@mui/material' -import { ResizeHandle, ErrorMessage } from '@jbrowse/core/ui' +import { ResizeHandle } from '@jbrowse/core/ui' import { assembleLocString } from '@jbrowse/core/util' import { makeStyles } from 'tss-react/mui' -import { grey } from '@mui/material/colors' - -// icons -import ZoomOutIcon from '@mui/icons-material/ZoomOut' -import ZoomInIcon from '@mui/icons-material/ZoomIn' -import RotateLeftIcon from '@mui/icons-material/RotateLeft' -import RotateRightIcon from '@mui/icons-material/RotateRight' -import LockOpenIcon from '@mui/icons-material/LockOpen' -import LockIcon from '@mui/icons-material/Lock' -import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons' // locals import Ruler from './Ruler' +import Controls from './Controls' import ImportForm from './ImportForm' import { CircularViewModel } from '../models/CircularView' @@ -38,24 +28,6 @@ const useStyles = makeStyles()(theme => ({ boxSizing: 'content-box', display: 'block', }, - iconButton: { - padding: '4px', - margin: '0 2px 0 2px', - }, - controls: { - overflow: 'hidden', - whiteSpace: 'nowrap', - position: 'absolute', - background: grey[200], - boxSizing: 'border-box', - borderRight: '1px solid #a2a2a2', - borderBottom: '1px solid #a2a2a2', - left: 0, - top: 0, - }, - importFormContainer: { - marginBottom: theme.spacing(4), - }, })) const Slices = observer(({ model }: { model: CircularViewModel }) => { @@ -85,163 +57,84 @@ const Slices = observer(({ model }: { model: CircularViewModel }) => { ) }) -const Controls = observer(function ({ - model, - showingFigure, -}: { - model: CircularViewModel - showingFigure: boolean -}) { - const { classes } = useStyles() - return ( -
- - - - - - - - - - - - - - - - - - {model.lockedFitToWindow ? : } - - - {model.hideTrackSelectorButton ? null : ( - - - - )} -
- ) -}) - const CircularView = observer(({ model }: { model: CircularViewModel }) => { - const { classes } = useStyles() const initialized = !!model.displayedRegions.length && !!model.figureWidth && - !!model.figureHeight + !!model.figureHeight && + model.initialized const showImportForm = !initialized && !model.disableImportForm const showFigure = initialized && !showImportForm return ( -
- {model.error ? ( - - ) : ( - <> - {showImportForm ? : null} - <> - {!showFigure ? null : ( -
-
`${x}px`) - .join(' '), - }} - > - - - - - -
-
- )} - - {model.hideVerticalResizeHandle ? null : ( - - )} - - + <> + {showImportForm || model.error ? ( + + ) : showFigure ? ( + + ) : null} + + ) +}) + +const CircularViewLoaded = observer(function ({ + model, +}: { + model: CircularViewModel +}) { + const { + width, + height, + id, + offsetRadians, + centerXY, + figureWidth, + figureHeight, + hideVerticalResizeHandle, + } = model + const { classes } = useStyles() + return ( +
+
+
`${x}px`).join(' '), + }} + > + + + + + +
+
+ + {hideVerticalResizeHandle ? null : ( + )}
) diff --git a/plugins/circular-view/src/CircularView/components/Controls.tsx b/plugins/circular-view/src/CircularView/components/Controls.tsx new file mode 100644 index 0000000000..5b0942a771 --- /dev/null +++ b/plugins/circular-view/src/CircularView/components/Controls.tsx @@ -0,0 +1,106 @@ +import React from 'react' +import { observer } from 'mobx-react' +import { IconButton } from '@mui/material' +import { makeStyles } from 'tss-react/mui' +import { grey } from '@mui/material/colors' + +// icons +import ZoomOutIcon from '@mui/icons-material/ZoomOut' +import ZoomInIcon from '@mui/icons-material/ZoomIn' +import RotateLeftIcon from '@mui/icons-material/RotateLeft' +import RotateRightIcon from '@mui/icons-material/RotateRight' +import LockOpenIcon from '@mui/icons-material/LockOpen' +import LockIcon from '@mui/icons-material/Lock' +import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons' + +// locals +import { CircularViewModel } from '../models/CircularView' + +const useStyles = makeStyles()({ + iconButton: { + padding: '4px', + margin: '0 2px 0 2px', + }, + controls: { + overflow: 'hidden', + whiteSpace: 'nowrap', + position: 'absolute', + background: grey[200], + boxSizing: 'border-box', + borderRight: '1px solid #a2a2a2', + borderBottom: '1px solid #a2a2a2', + left: 0, + top: 0, + }, +}) + +const Controls = observer(function ({ model }: { model: CircularViewModel }) { + const { classes } = useStyles() + return ( +
+ + + + + + + + + + + + + + + + + + {model.lockedFitToWindow ? : } + + + {model.hideTrackSelectorButton ? null : ( + + + + )} +
+ ) +}) +export default Controls diff --git a/plugins/circular-view/src/CircularView/components/ImportForm.tsx b/plugins/circular-view/src/CircularView/components/ImportForm.tsx index c24fe0960e..6cf2343be0 100644 --- a/plugins/circular-view/src/CircularView/components/ImportForm.tsx +++ b/plugins/circular-view/src/CircularView/components/ImportForm.tsx @@ -7,7 +7,7 @@ import { ErrorMessage, AssemblySelector } from '@jbrowse/core/ui' const useStyles = makeStyles()(theme => ({ importFormContainer: { - marginBottom: theme.spacing(4), + padding: theme.spacing(6), }, })) @@ -15,10 +15,9 @@ const useStyles = makeStyles()(theme => ({ const ImportForm = observer(({ model }: { model: any }) => { const { classes } = useStyles() const session = getSession(model) - const { error: modelError } = model + const { error } = model const { assemblyNames, assemblyManager } = session const [selectedAsm, setSelectedAsm] = useState(assemblyNames[0]) - const [error, setError] = useState(modelError) const assembly = assemblyManager.get(selectedAsm) const assemblyError = assemblyNames.length ? assembly?.error @@ -39,7 +38,7 @@ const ImportForm = observer(({ model }: { model: any }) => { { - setError(undefined) + model.setError(undefined) setSelectedAsm(val) }} session={session} @@ -50,11 +49,15 @@ const ImportForm = observer(({ model }: { model: any }) => { diff --git a/plugins/circular-view/src/CircularView/models/CircularView.ts b/plugins/circular-view/src/CircularView/models/CircularView.ts index 70ad69d6bb..40eb6fc1ab 100644 --- a/plugins/circular-view/src/CircularView/models/CircularView.ts +++ b/plugins/circular-view/src/CircularView/models/CircularView.ts @@ -19,6 +19,7 @@ import { getSession, clamp, isSessionModelWithWidgets, + Region as IRegion, } from '@jbrowse/core/util' import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models' import { calculateStaticSlices, sliceIsVisible } from './slices' @@ -33,10 +34,10 @@ function stateModelFactory(pluginManager: PluginManager) { const minHeight = 40 const minWidth = 100 const defaultHeight = 400 - return types.compose( - BaseViewModel, - types - .model('CircularView', { + return types + .compose( + BaseViewModel, + types.model('CircularView', { /** * #property */ @@ -49,7 +50,7 @@ function stateModelFactory(pluginManager: PluginManager) { /** * #property */ - bpPerPx: 2000000, + bpPerPx: 200, /** * #property */ @@ -102,449 +103,465 @@ function stateModelFactory(pluginManager: PluginManager) { minimumBlockWidth: 20, trackSelectorType: 'hierarchical', - }) - .volatile(() => ({ - width: 0, - })) - .views(self => ({ - /** - * #getter - */ - get staticSlices() { - return calculateStaticSlices(self) - }, - - /** - * #getter - */ - get visibleSection() { - return viewportVisibleSection( - [ - self.scrollX, - self.scrollX + self.width, - self.scrollY, - self.scrollY + self.height, - ], - this.centerXY, - this.radiusPx, - ) - }, - /** - * #getter - */ - get circumferencePx() { - let elidedBp = 0 - for (const r of this.elidedRegions) { - elidedBp += r.widthBp - } - return ( - elidedBp / self.bpPerPx + self.spacingPx * this.elidedRegions.length - ) - }, - /** - * #getter - */ - get radiusPx() { - return this.circumferencePx / (2 * Math.PI) - }, - /** - * #getter - */ - get bpPerRadian() { - return self.bpPerPx * this.radiusPx - }, - /** - * #getter - */ - get pxPerRadian() { - return this.radiusPx - }, - /** - * #getter - */ - get centerXY(): [number, number] { - return [ - this.radiusPx + self.paddingPx, - this.radiusPx + self.paddingPx, - ] - }, - /** - * #getter - */ - get totalBp() { - let total = 0 - for (const region of self.displayedRegions) { - total += region.end - region.start - } - return total - }, - /** - * #getter - */ - get maximumRadiusPx() { - return self.lockedFitToWindow - ? Math.min(self.width, self.height) / 2 - self.lockedPaddingPx - : 1000000 - }, - /** - * #getter - */ - get maxBpPerPx() { - const minCircumferencePx = 2 * Math.PI * self.minimumRadiusPx - return this.totalBp / minCircumferencePx - }, - /** - * #getter - */ - get minBpPerPx() { - // min depends on window dimensions, clamp between old min(0.01) and max - const maxCircumferencePx = 2 * Math.PI * this.maximumRadiusPx - return clamp( - this.totalBp / maxCircumferencePx, - 0.0000000001, - this.maxBpPerPx, + }), + ) + .volatile(() => ({ + volatileWidth: undefined as number | undefined, + error: undefined as unknown, + })) + .views(self => ({ + /** + * #getter + */ + get width() { + if (self.volatileWidth === undefined) { + throw new Error( + 'wait for view to be initialized first before accessing width', ) - }, - /** - * #getter - */ - get atMaxBpPerPx() { - return self.bpPerPx >= this.maxBpPerPx - }, - /** - * #getter - */ - get atMinBpPerPx() { - return self.bpPerPx <= this.minBpPerPx - }, - /** - * #getter - */ - get tooSmallToLock() { - return this.minBpPerPx <= 0.0000000001 - }, - /** - * #getter - */ - get figureDimensions(): [number, number] { - return [ - this.radiusPx * 2 + 2 * self.paddingPx, - this.radiusPx * 2 + 2 * self.paddingPx, - ] - }, - /** - * #getter - */ - get figureWidth() { - return this.figureDimensions[0] - }, - /** - * #getter - */ - get figureHeight() { - return this.figureDimensions[1] - }, - /** - * #getter - * this is displayedRegions, post-processed to - * elide regions that are too small to see reasonably - */ - get elidedRegions() { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const visible: any[] = [] - self.displayedRegions.forEach(region => { - const widthBp = region.end - region.start - const widthPx = widthBp / self.bpPerPx - if (widthPx < self.minVisibleWidth) { - // too small to see, collapse into a single elision region - const lastVisible = visible[visible.length - 1] - if (lastVisible && lastVisible.elided) { - lastVisible.regions.push({ ...region }) - lastVisible.widthBp += widthBp - } else { - visible.push({ - elided: true, - widthBp, - regions: [{ ...region }], - }) - } - } else { - // big enough to see, display it - visible.push({ ...region, widthBp }) + } + return self.volatileWidth + }, + /** + * #getter + */ + get staticSlices() { + return calculateStaticSlices(self) + }, + + /** + * #getter + */ + get visibleSection() { + return viewportVisibleSection( + [ + self.scrollX, + self.scrollX + self.width, + self.scrollY, + self.scrollY + self.height, + ], + this.centerXY, + this.radiusPx, + ) + }, + /** + * #getter + */ + get circumferencePx() { + let elidedBp = 0 + + for (const r of this.elidedRegions) { + elidedBp += r.widthBp + } + return ( + elidedBp / self.bpPerPx + self.spacingPx * this.elidedRegions.length + ) + }, + /** + * #getter + */ + get radiusPx() { + return this.circumferencePx / (2 * Math.PI) + }, + /** + * #getter + */ + get bpPerRadian() { + return self.bpPerPx * this.radiusPx + }, + /** + * #getter + */ + get pxPerRadian() { + return this.radiusPx + }, + /** + * #getter + */ + get centerXY(): [number, number] { + return [this.radiusPx + self.paddingPx, this.radiusPx + self.paddingPx] + }, + /** + * #getter + */ + get totalBp() { + let total = 0 + for (const region of self.displayedRegions) { + total += region.end - region.start + } + return total + }, + /** + * #getter + */ + get maximumRadiusPx() { + return self.lockedFitToWindow + ? Math.min(self.width, self.height) / 2 - self.lockedPaddingPx + : 1000000 + }, + /** + * #getter + */ + get maxBpPerPx() { + const minCircumferencePx = 2 * Math.PI * self.minimumRadiusPx + return this.totalBp / minCircumferencePx + }, + /** + * #getter + */ + get minBpPerPx() { + // min depends on window dimensions, clamp between old min(0.01) and max + const maxCircumferencePx = 2 * Math.PI * this.maximumRadiusPx + return clamp( + this.totalBp / maxCircumferencePx, + 0.0000000001, + this.maxBpPerPx, + ) + }, + /** + * #getter + */ + get atMaxBpPerPx() { + return self.bpPerPx >= this.maxBpPerPx + }, + /** + * #getter + */ + get atMinBpPerPx() { + return self.bpPerPx <= this.minBpPerPx + }, + /** + * #getter + */ + get tooSmallToLock() { + return this.minBpPerPx <= 0.0000000001 + }, + /** + * #getter + */ + get figureDimensions(): [number, number] { + return [ + this.radiusPx * 2 + 2 * self.paddingPx, + this.radiusPx * 2 + 2 * self.paddingPx, + ] + }, + /** + * #getter + */ + get figureWidth() { + return this.figureDimensions[0] + }, + /** + * #getter + */ + get figureHeight() { + return this.figureDimensions[1] + }, + /** + * #getter + * this is displayedRegions, post-processed to + * elide regions that are too small to see reasonably + */ + get elidedRegions() { + const visible: ( + | { + elided: true + widthBp: number + regions: IRegion[] } - }) - - // remove any single-region elisions - for (let i = 0; i < visible.length; i += 1) { - const v = visible[i] - if (v.elided && v.regions.length === 1) { - delete v.elided - visible[i] = { ...v, ...v.regions[0] } + | { + elided: false + widthBp: number + start: number + end: number + refName: string } - } - return visible - }, - /** - * #getter - */ - get assemblyNames() { - const assemblyNames: string[] = [] - self.displayedRegions.forEach(displayedRegion => { - if (!assemblyNames.includes(displayedRegion.assemblyName)) { - assemblyNames.push(displayedRegion.assemblyName) + )[] = [] + self.displayedRegions.forEach(region => { + const widthBp = region.end - region.start + const widthPx = widthBp / self.bpPerPx + if (widthPx < self.minVisibleWidth) { + // too small to see, collapse into a single elision region + const lastVisible = visible[visible.length - 1] + if (lastVisible?.elided) { + lastVisible.regions.push({ ...region }) + lastVisible.widthBp += widthBp + } else { + visible.push({ + elided: true, + widthBp, + regions: [{ ...region }], + }) } - }) - return assemblyNames - }, - /** - * #getter - */ - get initialized() { - const { assemblyManager } = getSession(self) - return this.assemblyNames.every( - a => assemblyManager.get(a)?.initialized, - ) - }, - })) - .views(self => ({ - /** - * #getter - */ - get visibleStaticSlices() { - return self.staticSlices.filter(s => sliceIsVisible(self, s)) - }, - })) - .volatile(() => ({ - error: undefined as unknown, - })) - .actions(self => ({ - /** - * #action - */ - setWidth(newWidth: number) { - self.width = Math.max(newWidth, minWidth) - return self.width - }, - /** - * #action - */ - setHeight(newHeight: number) { - self.height = Math.max(newHeight, minHeight) - return self.height - }, - /** - * #action - */ - resizeHeight(distance: number) { - const oldHeight = self.height - const newHeight = this.setHeight(self.height + distance) - this.setModelViewWhenAdjust(!self.tooSmallToLock) - return newHeight - oldHeight - }, - /** - * #action - */ - resizeWidth(distance: number) { - const oldWidth = self.width - const newWidth = this.setWidth(self.width + distance) - this.setModelViewWhenAdjust(!self.tooSmallToLock) - return newWidth - oldWidth - }, - /** - * #action - */ - rotateClockwiseButton() { - this.rotateClockwise(Math.PI / 6) - }, + } else { + // big enough to see, display it + visible.push({ ...region, widthBp, elided: false }) + } + }) - /** - * #action - */ - rotateCounterClockwiseButton() { - this.rotateCounterClockwise(Math.PI / 6) - }, + // remove any single-region elisions + for (let i = 0; i < visible.length; i += 1) { + const v = visible[i] + if (v.elided && v.regions.length === 1) { + visible[i] = { ...v, ...v.regions[0], elided: false } + } + } + return visible + }, + /** + * #getter + */ + get assemblyNames() { + const assemblyNames: string[] = [] + self.displayedRegions.forEach(displayedRegion => { + if (!assemblyNames.includes(displayedRegion.assemblyName)) { + assemblyNames.push(displayedRegion.assemblyName) + } + }) + return assemblyNames + }, + /** + * #getter + */ + get initialized() { + const { assemblyManager } = getSession(self) + return ( + self.volatileWidth !== undefined && + this.assemblyNames.every(a => assemblyManager.get(a)?.initialized) + ) + }, + })) + .views(self => ({ + /** + * #getter + */ + get visibleStaticSlices() { + return self.staticSlices.filter(s => sliceIsVisible(self, s)) + }, + })) - /** - * #action - */ - rotateClockwise(distance = 0.17) { - self.offsetRadians += distance - }, + .actions(self => ({ + /** + * #action + */ + setWidth(newWidth: number) { + self.volatileWidth = Math.max(newWidth, minWidth) + return self.volatileWidth + }, + /** + * #action + */ + setHeight(newHeight: number) { + self.height = Math.max(newHeight, minHeight) + return self.height + }, + /** + * #action + */ + resizeHeight(distance: number) { + const oldHeight = self.height + const newHeight = this.setHeight(self.height + distance) + this.setModelViewWhenAdjust(!self.tooSmallToLock) + return newHeight - oldHeight + }, + /** + * #action + */ + resizeWidth(distance: number) { + const oldWidth = self.width + const newWidth = this.setWidth(self.width + distance) + this.setModelViewWhenAdjust(!self.tooSmallToLock) + return newWidth - oldWidth + }, + /** + * #action + */ + rotateClockwiseButton() { + this.rotateClockwise(Math.PI / 6) + }, - /** - * #action - */ - rotateCounterClockwise(distance = 0.17) { - self.offsetRadians -= distance - }, + /** + * #action + */ + rotateCounterClockwiseButton() { + this.rotateCounterClockwise(Math.PI / 6) + }, - /** - * #action - */ - zoomInButton() { - this.setBpPerPx(self.bpPerPx / 1.4) - }, + /** + * #action + */ + rotateClockwise(distance = 0.17) { + self.offsetRadians += distance + }, - /** - * #action - */ - zoomOutButton() { - this.setBpPerPx(self.bpPerPx * 1.4) - }, + /** + * #action + */ + rotateCounterClockwise(distance = 0.17) { + self.offsetRadians -= distance + }, - /** - * #action - */ - setBpPerPx(newVal: number) { - self.bpPerPx = clamp(newVal, self.minBpPerPx, self.maxBpPerPx) - }, + /** + * #action + */ + zoomInButton() { + this.setBpPerPx(self.bpPerPx / 1.4) + }, - /** - * #action - */ - setModelViewWhenAdjust(secondCondition: boolean) { - if (self.lockedFitToWindow && secondCondition) { - this.setBpPerPx(self.minBpPerPx) - } - }, + /** + * #action + */ + zoomOutButton() { + this.setBpPerPx(self.bpPerPx * 1.4) + }, - /** - * #action - */ - closeView() { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getParent(self, 2).removeView(self) - }, + /** + * #action + */ + setBpPerPx(newVal: number) { + self.bpPerPx = clamp(newVal, self.minBpPerPx, self.maxBpPerPx) + }, - /** - * #action - */ - setDisplayedRegions(regions: SnapshotOrInstance[]) { - const previouslyEmpty = self.displayedRegions.length === 0 - self.displayedRegions = cast(regions) + /** + * #action + */ + setModelViewWhenAdjust(secondCondition: boolean) { + if (self.lockedFitToWindow && secondCondition) { + this.setBpPerPx(self.minBpPerPx) + } + }, - if (previouslyEmpty) { - this.setBpPerPx(self.minBpPerPx) - } else { - this.setBpPerPx(self.bpPerPx) - } - }, + /** + * #action + */ + closeView() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getParent(self, 2).removeView(self) + }, - /** - * #action - */ - activateTrackSelector() { - if (self.trackSelectorType === 'hierarchical') { - const session = getSession(self) - if (isSessionModelWithWidgets(session)) { - const selector = session.addWidget( - 'HierarchicalTrackSelectorWidget', - 'hierarchicalTrackSelector', - { view: self }, - ) - session.showWidget(selector) - return selector - } - } - throw new Error( - `invalid track selector type ${self.trackSelectorType}`, - ) - }, + /** + * #action + */ + setDisplayedRegions(regions: SnapshotOrInstance[]) { + const previouslyEmpty = self.displayedRegions.length === 0 + self.displayedRegions = cast(regions) - /** - * #action - */ - toggleTrack(trackId: string) { - // if we have any tracks with that configuration, turn them off - const hiddenCount = this.hideTrack(trackId) - // if none had that configuration, turn one on - if (!hiddenCount) { - this.showTrack(trackId) + if (previouslyEmpty) { + this.setBpPerPx(self.minBpPerPx) + } else { + this.setBpPerPx(self.bpPerPx) + } + }, + + /** + * #action + */ + activateTrackSelector() { + if (self.trackSelectorType === 'hierarchical') { + const session = getSession(self) + if (isSessionModelWithWidgets(session)) { + const selector = session.addWidget( + 'HierarchicalTrackSelectorWidget', + 'hierarchicalTrackSelector', + { view: self }, + ) + session.showWidget(selector) + return selector } - }, + } + throw new Error(`invalid track selector type ${self.trackSelectorType}`) + }, - /** - * #action - */ - setError(error: unknown) { - console.error(error) - self.error = error - }, + /** + * #action + */ + toggleTrack(trackId: string) { + // if we have any tracks with that configuration, turn them off + const hiddenCount = this.hideTrack(trackId) + // if none had that configuration, turn one on + if (!hiddenCount) { + this.showTrack(trackId) + } + }, - /** - * #action - */ - showTrack(trackId: string, initialSnapshot = {}) { - const schema = pluginManager.pluggableConfigSchemaType('track') - const conf = resolveIdentifier(schema, getRoot(self), trackId) - const trackType = pluginManager.getTrackType(conf.type) - if (!trackType) { - throw new Error(`unknown track type ${conf.type}`) - } - const viewType = pluginManager.getViewType(self.type) - const supportedDisplays = viewType.displayTypes.map(d => d.name) - const displayConf = conf.displays.find((d: AnyConfigurationModel) => - supportedDisplays.includes(d.type), - ) - const track = trackType.stateModel.create({ - ...initialSnapshot, - type: conf.type, - configuration: conf, - displays: [{ type: displayConf.type, configuration: displayConf }], - }) - self.tracks.push(track) - }, + /** + * #action + */ + setError(error: unknown) { + console.error(error) + self.error = error + }, - /** - * #action - */ - addTrackConf( - configuration: AnyConfigurationModel, - initialSnapshot = {}, - ) { - const { type } = configuration - const name = readConfObject(configuration, 'name') - const trackType = pluginManager.getTrackType(type) - if (!trackType) { - throw new Error(`unknown track type ${configuration.type}`) - } - const viewType = pluginManager.getViewType(self.type) - const supportedDisplays = viewType.displayTypes.map(d => d.name) - const displayConf = configuration.displays.find( - (d: AnyConfigurationModel) => supportedDisplays.includes(d.type), - ) - const track = trackType.stateModel.create({ - ...initialSnapshot, - name, - type, - configuration, - displays: [{ type: displayConf.type, configuration: displayConf }], - }) - self.tracks.push(track) - }, + /** + * #action + */ + showTrack(trackId: string, initialSnapshot = {}) { + const schema = pluginManager.pluggableConfigSchemaType('track') + const conf = resolveIdentifier(schema, getRoot(self), trackId) + const trackType = pluginManager.getTrackType(conf.type) + if (!trackType) { + throw new Error(`unknown track type ${conf.type}`) + } + const viewType = pluginManager.getViewType(self.type) + const supportedDisplays = viewType.displayTypes.map(d => d.name) + const displayConf = conf.displays.find((d: AnyConfigurationModel) => + supportedDisplays.includes(d.type), + ) + const track = trackType.stateModel.create({ + ...initialSnapshot, + type: conf.type, + configuration: conf, + displays: [{ type: displayConf.type, configuration: displayConf }], + }) + self.tracks.push(track) + }, - /** - * #action - */ - hideTrack(trackId: string) { - const schema = pluginManager.pluggableConfigSchemaType('track') - const conf = resolveIdentifier(schema, getRoot(self), trackId) - const t = self.tracks.filter(t => t.configuration === conf) - transaction(() => t.forEach(t => self.tracks.remove(t))) - return t.length - }, + /** + * #action + */ + addTrackConf(configuration: AnyConfigurationModel, initialSnapshot = {}) { + const { type } = configuration + const name = readConfObject(configuration, 'name') + const trackType = pluginManager.getTrackType(type) + if (!trackType) { + throw new Error(`unknown track type ${configuration.type}`) + } + const viewType = pluginManager.getViewType(self.type) + const supportedDisplays = viewType.displayTypes.map(d => d.name) + const displayConf = configuration.displays.find( + (d: AnyConfigurationModel) => supportedDisplays.includes(d.type), + ) + const track = trackType.stateModel.create({ + ...initialSnapshot, + name, + type, + configuration, + displays: [{ type: displayConf.type, configuration: displayConf }], + }) + self.tracks.push(track) + }, - /** - * #action - */ - toggleFitToWindowLock() { - // when going unlocked -> locked and circle is cut off, set to the locked minBpPerPx - self.lockedFitToWindow = !self.lockedFitToWindow - this.setModelViewWhenAdjust(self.atMinBpPerPx) - return self.lockedFitToWindow - }, - })), - ) + /** + * #action + */ + hideTrack(trackId: string) { + const schema = pluginManager.pluggableConfigSchemaType('track') + const conf = resolveIdentifier(schema, getRoot(self), trackId) + const t = self.tracks.filter(t => t.configuration === conf) + transaction(() => t.forEach(t => self.tracks.remove(t))) + return t.length + }, + + /** + * #action + */ + toggleFitToWindowLock() { + // when going unlocked -> locked and circle is cut off, set to the + // locked minBpPerPx + self.lockedFitToWindow = !self.lockedFitToWindow + this.setModelViewWhenAdjust(self.atMinBpPerPx) + return self.lockedFitToWindow + }, + })) } export type CircularViewStateModel = ReturnType diff --git a/plugins/wiggle/src/LinearWiggleDisplay/components/Tooltip.tsx b/plugins/wiggle/src/LinearWiggleDisplay/components/Tooltip.tsx index 3b2c736db3..3b5ffc4bc5 100644 --- a/plugins/wiggle/src/LinearWiggleDisplay/components/Tooltip.tsx +++ b/plugins/wiggle/src/LinearWiggleDisplay/components/Tooltip.tsx @@ -8,48 +8,51 @@ import { toP } from '../../util' const en = (n: number) => n.toLocaleString('en-US') -const TooltipContents = React.forwardRef( - ({ feature }, ref) => { - const start = feature.get('start') - const end = feature.get('end') - const name = feature.get('refName') - const loc = [name, start === end ? en(start) : `${en(start)}..${en(end)}`] - .filter(f => !!f) - .join(':') +interface Props { + feature: Feature +} - return feature.get('summary') !== undefined ? ( -
- {loc} -
- Max: {toP(feature.get('maxScore'))} -
- Avg: {toP(feature.get('score'))} -
- Min: {toP(feature.get('minScore'))} -
- ) : ( -
- {loc} -
- {`${toP(feature.get('score'))}`} -
- ) - }, -) +const TooltipContents = React.forwardRef(function ( + { feature }, + ref, +) { + const start = feature.get('start') + const end = feature.get('end') + const name = feature.get('refName') + const loc = [name, start === end ? en(start) : `${en(start)}..${en(end)}`] + .filter(f => !!f) + .join(':') + + return feature.get('summary') !== undefined ? ( +
+ {loc} +
+ Max: {toP(feature.get('maxScore'))} +
+ Avg: {toP(feature.get('score'))} +
+ Min: {toP(feature.get('minScore'))} +
+ ) : ( +
+ {loc} +
+ {`${toP(feature.get('score'))}`} +
+ ) +}) type Coord = [number, number] -const WiggleTooltip = observer( - (props: { - model: { featureUnderMouse: Feature } - height: number - offsetMouseCoord: Coord - clientMouseCoord: Coord - clientRect?: DOMRect - TooltipContents?: TooltipContentsComponent - }) => { - return - }, -) +const WiggleTooltip = observer(function (props: { + model: { featureUnderMouse: Feature } + height: number + offsetMouseCoord: Coord + clientMouseCoord: Coord + clientRect?: DOMRect + TooltipContents?: TooltipContentsComponent +}) { + return +}) export default WiggleTooltip export { Tooltip } diff --git a/website/docs/config/GCContentAdapter.md b/website/docs/config/GCContentAdapter.md index e51db50c40..495c808935 100644 --- a/website/docs/config/GCContentAdapter.md +++ b/website/docs/config/GCContentAdapter.md @@ -14,5 +14,8 @@ source code. See [Config guide](/docs/config_guide) for more info #### slot: sequenceAdapter ```js -sequenceAdapter: pluginManager.pluggableConfigSchemaType('adapter') +sequenceAdapter: { + type: 'frozen', + defaultValue: null, + } ``` diff --git a/website/docs/config/TheTrackHubRegistryConnection.md b/website/docs/config/TheTrackHubRegistryConnection.md deleted file mode 100644 index 5026d67e0c..0000000000 --- a/website/docs/config/TheTrackHubRegistryConnection.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -id: thetrackhubregistryconnection -title: TheTrackHubRegistryConnection -toplevel: true ---- - -Note: this document is automatically generated from configuration objects in our -source code. See [Config guide](/docs/config_guide) for more info - -## Docs - -### TheTrackHubRegistryConnection - Slots - -#### slot: trackDbId - -```js -trackDbId: { - type: 'string', - defaultValue: '', - description: 'id of the trackDb in The Track Hub Registry', - } -``` - -## TheTrackHubRegistryConnection - Derives from - -```js -baseConfiguration: baseConnectionConfig -``` diff --git a/website/docs/models/LinearReadArcsDisplay.md b/website/docs/models/LinearReadArcsDisplay.md index cff9e60c3f..9359456119 100644 --- a/website/docs/models/LinearReadArcsDisplay.md +++ b/website/docs/models/LinearReadArcsDisplay.md @@ -66,8 +66,33 @@ colorBy: types.maybe( ) ``` +#### property: drawInter + +```js +// type signature +true +// code +drawInter: true +``` + +#### property: drawLongRange + +```js +// type signature +true +// code +drawLongRange: true +``` + ### LinearReadArcsDisplay - Getters +#### getter: lineWidthSetting + +```js +// type +any +``` + #### getter: ready ```js @@ -81,7 +106,7 @@ boolean ```js // type signature -trackMenuItems: () => MenuItem[] +trackMenuItems: () => (MenuDivider | MenuSubHeader | NormalMenuItem | CheckboxMenuItem | RadioMenuItem | SubMenuItem | { ...; })[] ``` #### method: renderSvg @@ -93,13 +118,33 @@ renderSvg: (opts: ExportSvgOptions) => Promise ### LinearReadArcsDisplay - Actions +#### action: reload + +internal, a reference to a HTMLCanvas because we use a autorun to draw the +canvas + +```js +// type signature +reload: () => void +``` + #### action: setRef +internal, a reference to a HTMLCanvas because we use a autorun to draw the +canvas + ```js // type signature setRef: (ref: HTMLCanvasElement) => void ``` +#### action: setColorScheme + +```js +// type signature +setColorScheme: (s: { type: string; }) => void +``` + #### action: setChainData ```js @@ -107,6 +152,20 @@ setRef: (ref: HTMLCanvasElement) => void setChainData: (args: ChainData) => void ``` +#### action: setDrawInter + +```js +// type signature +setDrawInter: (f: boolean) => void +``` + +#### action: setDrawLongRange + +```js +// type signature +setDrawLongRange: (f: boolean) => void +``` + #### action: setLoading ```js @@ -116,6 +175,8 @@ setLoading: (f: boolean) => void #### action: setDrawn +used during tests to detect when we can complete a snapshot test + ```js // type signature setDrawn: (f: boolean) => void @@ -130,6 +191,9 @@ setFilterBy: (filter: Filter) => void #### action: setLastDrawnOffsetPx +allows the drawing to slide around a little bit if it takes a long time to +refresh + ```js // type signature setLastDrawnOffsetPx: (n: number) => void @@ -137,6 +201,8 @@ setLastDrawnOffsetPx: (n: number) => void #### action: setLineWidth +thin, bold, extrabold, etc + ```js // type signature setLineWidth: (n: number) => void diff --git a/website/docs/models/LinearReadCloudDisplay.md b/website/docs/models/LinearReadCloudDisplay.md index 8af36e9a0e..57b772f4a1 100644 --- a/website/docs/models/LinearReadCloudDisplay.md +++ b/website/docs/models/LinearReadCloudDisplay.md @@ -84,6 +84,16 @@ renderSvg: (opts: ExportSvgOptions) => Promise ### LinearReadCloudDisplay - Actions +#### action: reload + +internal, a reference to a HTMLCanvas because we use a autorun to draw the +canvas + +```js +// type signature +reload: () => void +``` + #### action: setRef ```js diff --git a/yarn.lock b/yarn.lock index fb8e5de956..aeaafc8c8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1498,15 +1498,15 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb" integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== -"@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== +"@eslint/eslintrc@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.0.tgz#8ec64e0df3e7a1971ee1ff5158da87389f167a63" + integrity sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A== dependencies: ajv "^6.12.4" debug "^4.3.2" espree "^9.4.0" - globals "^13.15.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" @@ -1529,9 +1529,9 @@ integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== "@gmod/bam@^1.1.15": - version "1.1.17" - resolved "https://registry.yarnpkg.com/@gmod/bam/-/bam-1.1.17.tgz#4e56d0cb2718ef9e3e269ebd453af5f38c09701c" - integrity sha512-bmLe36rbOXVwxfDTdKnE0ACnJnSkC/JrRBcTQI35TWsHZFEaJVcYr2h7+6gBM1R2lrIj06V9/0VKXCL7WKYyog== + version "1.1.18" + resolved "https://registry.yarnpkg.com/@gmod/bam/-/bam-1.1.18.tgz#de28533289f0f659f962a59226d9c342590a0d50" + integrity sha512-2Sn4zLV7DKYyrmYbNJRMchKGfhfIVm6LZEl2h7MSuGmAEtGfa1RBVOfG8Cu6VDC+lIFrgS4ys4vUFFxaWaxgSQ== dependencies: "@gmod/bgzf-filehandle" "^1.4.4" abortable-promise-cache "^1.5.0" @@ -1543,9 +1543,9 @@ quick-lru "^2.0.0" "@gmod/bbi@^2.0.3": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@gmod/bbi/-/bbi-2.0.4.tgz#112612a3353c64c8598f3f3165b51f1981aa14bc" - integrity sha512-c1bkk6bq6/WpYKNIGVgvWJ/U5wEVd46YYdvx0xLbfPoXgIhEreB7kZBbh+BxXaovl/mIbjP17mElSSz2+kB5Ig== + version "2.0.5" + resolved "https://registry.yarnpkg.com/@gmod/bbi/-/bbi-2.0.5.tgz#0f67bbda7031b08e8819a38fb640719e4776a79c" + integrity sha512-WHwkf9pUtTU1QFwW3jTW/CD6rDu5YifXrUNuMvEfPYQYDqIIqIZiMt5FQyXecwB6076M6ncIZ2S7ER1MR3ZbAQ== dependencies: abortable-promise-cache "^1.4.1" binary-parser "^2.1.0" @@ -1577,9 +1577,9 @@ long "^4.0.0" "@gmod/cram@^1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@gmod/cram/-/cram-1.7.1.tgz#878f31bc5c038ec789fc0513d769fad5b964a806" - integrity sha512-RylBdSTLsKrwp9S1ZDa8hrSRYFRfvDHxH12vM4KrxlIRrpvH5uajGENMigqcL+TQhN9etZeZ+pEBi+lbz8kiXQ== + version "1.7.2" + resolved "https://registry.yarnpkg.com/@gmod/cram/-/cram-1.7.2.tgz#1df185050d74752600fbb100cbca620c4aa03572" + integrity sha512-LJIsAau4c/+UIHsN8VY9rwMccQbeefJCbi0FV2fqwbtVtEa9hhPPWOl6hb6lM+Lo8eDtApPRr3hqGA507megJg== dependencies: "@gmod/binary-parser" "^1.3.5" "@jkbonfield/htscodecs" "^0.5.1" @@ -1593,9 +1593,9 @@ quick-lru "^4.0.1" "@gmod/faidx@^1.0.1": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@gmod/faidx/-/faidx-1.0.3.tgz#e323a59e0134510c0bdf33ad0737c46782ac35f3" - integrity sha512-SjSf/hESkZ4YPwwNM/e9Mi3liklMHtUy21qU4m1r4esLgM0Ve55gF5ODz5ycGhlnQmZlzEahW4aC7+IqYgE2aQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@gmod/faidx/-/faidx-1.0.4.tgz#335f998fd7c32366955e6105d10744888f503757" + integrity sha512-Pcr5EB8h/qXSTge9QDvhumYKbT7gTtvfFTkaj9/fEYAIEdXL6bP5VCq3cjeX79SQkKoYZ2rm5Ohxf34XTcJOSw== dependencies: pump "^3.0.0" split2 "^4.1.0" @@ -1631,13 +1631,12 @@ quick-lru "^4.0.0" "@gmod/tabix@^1.5.2": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@gmod/tabix/-/tabix-1.5.4.tgz#62fa8f598aaeed3eac8b0889b29b352b7e3fc59d" - integrity sha512-mgJYVkEemd4CCWIb+Y9epqV2jU3Ocvfq/zBtu+tF+dlBCQJtv5f6iglyH321QbMKNgEoof0pZIQX3qLetSkAHw== + version "1.5.5" + resolved "https://registry.yarnpkg.com/@gmod/tabix/-/tabix-1.5.5.tgz#fa1ea10831a5f8e33c8227970fd856b4b3be9b1c" + integrity sha512-aH9RXEhwI7m280JknYWszYCETmw78HvPvPD6Ba3DC1t5Mf4MVBHGQyqk1WQJnJxqyGJMRQ0nnJWw9KLCnTtnxQ== dependencies: "@gmod/bgzf-filehandle" "^1.3.3" abortable-promise-cache "^1.4.1" - es6-promisify "^6.0.1" generic-filehandle "^3.0.0" long "^4.0.0" quick-lru "^4.0.0" @@ -1662,9 +1661,9 @@ integrity sha512-7WggfnPpNsELg0GGszEpXJhlGK3jnKcJsyGY6oPIfKwf7M+1a9gLbeqperuLlINPXuM4oQ0H4hlad9YUNYEGeg== "@gmod/vcf@^5.0.9": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@gmod/vcf/-/vcf-5.0.9.tgz#258ea28c5343efcfc2d510f9c5cda45b00bc66d8" - integrity sha512-pqv1irMcJ8qO9YvVwEvtD9YD6Up3YsYp5FplWOK+mR5YtsiopPQKNgmkL1Ks3Jv9aQNko0wQ9T1FZkNgzd6Fzg== + version "5.0.10" + resolved "https://registry.yarnpkg.com/@gmod/vcf/-/vcf-5.0.10.tgz#6c2d7952b15f61642454be90119ea89fd3c227de" + integrity sha512-o7QuPcOeXlJpzwQaFmgojhNvJE4yB9fhrfVEDKpkDjV27pAqwMy89367vtXu4JfBFE9t4zZ6sQRkqYaJ+cIheg== "@hapi/hoek@^9.0.0": version "9.3.0" @@ -1678,10 +1677,10 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@humanwhocodes/config-array@^0.11.6": - version "0.11.7" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" - integrity sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw== +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" @@ -3096,9 +3095,9 @@ react-is "^18.2.0" "@mui/x-data-grid@^5.0.0", "@mui/x-data-grid@^5.0.1": - version "5.17.14" - resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.14.tgz#3475d45ba3b18bdb343bbc954346324b2f935df0" - integrity sha512-Cf3yeaEAaFzpWPoDO1YTHWL2Anz69pUnIw4Swa6UehvNwJIz3ZDoVf92xCytviugU14NmL95PjmtHqK72Jt05g== + version "5.17.16" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.16.tgz#4e05a014467bbee68385b5bee1e75004ba0178a3" + integrity sha512-tzgjvO0DgiYWqU9soM6ORurtOrNsqpmdRxIjqJguLHxs2nP5hyhGEDRaHQIfEPv9eXNl9QL3kBO3VfgaFS5NPg== dependencies: "@babel/runtime" "^7.18.9" "@mui/utils" "^5.10.3" @@ -3662,9 +3661,9 @@ "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" - integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== "@sideway/pinpoint@^2.0.0": version "2.0.0" @@ -5122,19 +5121,19 @@ form-data "^3.0.0" "@types/node@*": - version "18.11.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.15.tgz#de0e1fbd2b22b962d45971431e2ae696643d3f5d" - integrity sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw== + version "18.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.16.tgz#966cae211e970199559cfbd295888fca189e49af" + integrity sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA== "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^16.11.26": - version "16.18.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.9.tgz#47c491cfbc10460571d766c16526748fa9ad96a1" - integrity sha512-nhrqXYxiQ+5B/tPorWum37VgAiefi/wmfJ1QZKGKKecC8/3HqcTTJD0O+VABSPwtseMMF7NCPVT9uGgwn0YqsQ== + version "16.18.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.10.tgz#d7415ef18c94f8d4e4a82ebcc8b8999f965d8920" + integrity sha512-XU1+v7h81p7145ddPfjv7jtWvkSilpcnON3mQ+bDi9Yuf7OI56efOglXRyXWgQ57xH3fEQgh7WOJMncRHVew5w== "@types/node@^14.14.0": - version "14.18.34" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.34.tgz#cd2e6fa0dbfb08a62582a7b967558e73c32061ec" - integrity sha512-hcU9AIQVHmPnmjRK+XUUYlILlr9pQrsqSrwov/JK1pnf3GTQowVBhx54FbvM0AU/VXGH4i3+vgXS5EguR7fysA== + version "14.18.35" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.35.tgz#879c4659cb7b3fe515844f029c75079c941bb65c" + integrity sha512-2ATO8pfhG1kDvw4Lc4C0GXIMSQFFJBCo/R1fSgTwmUlq5oy95LXyjDQinsRVgQY6gp6ghh3H91wk9ES5/5C+Tw== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -6626,9 +6625,9 @@ available-typed-arrays@^1.0.5: integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== axe-core@^4.4.3: - version "4.6.0" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.0.tgz#1d07514866fa51262734b3357932fcf86961383a" - integrity sha512-L3ZNbXPTxMrl0+qTXAzn9FBRvk5XdO56K8CvcCKtlxv44Aw2w2NCclGuvCWxHPw1Riiq3ncP/sxFYj2nUqdoTw== + version "4.6.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.1.tgz#79cccdee3e3ab61a8f42c458d4123a6768e6fbce" + integrity sha512-lCZN5XRuOnpG4bpMq8v0khrWtUOn+i8lZSb6wHZH56ZfbIEv6XwJV84AAueh9/zi7qPVJ/E4yz6fmsiyOmXR4w== axios@^0.25.0: version "0.25.0" @@ -8637,12 +8636,12 @@ css-loader@^5.0.1: semver "^7.3.5" css-loader@^6.5.1: - version "6.7.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.2.tgz#26bc22401b5921686a10fbeba75d124228302304" - integrity sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q== + version "6.7.3" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.3.tgz#1e8799f3ccc5874fdd55461af51137fcc5befbcd" + integrity sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ== dependencies: icss-utils "^5.1.0" - postcss "^8.4.18" + postcss "^8.4.19" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" @@ -9886,7 +9885,7 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw== -es6-promisify@^6.0.1, es6-promisify@^6.1.1: +es6-promisify@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.1.1.tgz#46837651b7b06bf6fff893d03f29393668d01621" integrity sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg== @@ -10164,12 +10163,12 @@ eslint-webpack-plugin@^3.1.1: schema-utils "^4.0.0" eslint@^8.0.0, eslint@^8.3.0: - version "8.29.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.29.0.tgz#d74a88a20fb44d59c51851625bc4ee8d0ec43f87" - integrity sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg== + version "8.30.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.30.0.tgz#83a506125d089eef7c5b5910eeea824273a33f50" + integrity sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ== dependencies: - "@eslint/eslintrc" "^1.3.3" - "@humanwhocodes/config-array" "^0.11.6" + "@eslint/eslintrc" "^1.4.0" + "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" @@ -10188,7 +10187,7 @@ eslint@^8.0.0, eslint@^8.3.0: file-entry-cache "^6.0.1" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.15.0" + globals "^13.19.0" grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" @@ -11060,9 +11059,9 @@ gauge@^4.0.3: wide-align "^1.1.5" generic-filehandle@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/generic-filehandle/-/generic-filehandle-3.0.0.tgz#10d5ddc4adf105de616224dba58370f3dcc3877c" - integrity sha512-THrFJ1fq7wjwMmSN7sdp0JnQKZDVE7aooVwHTWREKHrCGVJGKKiD5BSaboNHiFU84RIzJ+oBOqQsN//ISWj1ZQ== + version "3.0.1" + resolved "https://registry.yarnpkg.com/generic-filehandle/-/generic-filehandle-3.0.1.tgz#9bea8ad8f572bb07c16097df6fccc05a87ec77ff" + integrity sha512-E0eVkls2Ni7fUQ47VxYJN45/ow821pITHcgyaMZXBUt1czZUionT6HSqE0t+bcAozt5MYeJCxVxIrlTxoASXuQ== dependencies: es6-promisify "^6.1.1" @@ -11358,7 +11357,7 @@ globals@^11.1.0, globals@^11.12.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: +globals@^13.19.0: version "13.19.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== @@ -13243,9 +13242,9 @@ jest-leak-detector@^29.3.1: pretty-format "^29.3.1" jest-localstorage-mock@^2.4.3: - version "2.4.22" - resolved "https://registry.yarnpkg.com/jest-localstorage-mock/-/jest-localstorage-mock-2.4.22.tgz#9d70be92bfc591c0be289ee2f71de1b4b2a5ca9b" - integrity sha512-60PWSDFQOS5v7JzSmYLM3dPLg0JLl+2Vc4lIEz/rj2yrXJzegsFLn7anwc5IL0WzJbBa/Las064CHbFg491/DQ== + version "2.4.25" + resolved "https://registry.yarnpkg.com/jest-localstorage-mock/-/jest-localstorage-mock-2.4.25.tgz#9be525ebcd4eb791a445dbeba8474ceb2abeb434" + integrity sha512-VdQ8PTpNzUJDx/KY3hBrTwxqVMzMS+LccngC15EZSFdxJ+VeeCYmyW7BSzubk9FUKCVeXPjYPibzXe6swXYA+g== jest-matcher-utils@^27.5.1: version "27.5.1" @@ -13956,9 +13955,9 @@ json5@^1.0.1: minimist "^1.2.0" json5@^2.1.2, json5@^2.1.3, json5@^2.2.0, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" + integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== jsonc-parser@3.2.0: version "3.2.0" @@ -14081,9 +14080,9 @@ language-subtag-registry@^0.3.20: integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== language-tags@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.6.tgz#c087cc42cd92eb71f0925e9e271d4f8be5a93430" - integrity sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg== + version "1.0.7" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.7.tgz#41cc248730f3f12a452c2e2efe32bc0bbce67967" + integrity sha512-bSytju1/657hFjgUzPAPqszxH62ouE8nQFoFaVlIQfne4wO/wXC9A4+m8jYve7YBBvi59eq0SUpcshvG8h5Usw== dependencies: language-subtag-registry "^0.3.20" @@ -15328,9 +15327,9 @@ node-polyfill-webpack-plugin@^2.0.1: vm-browserify "^1.1.2" node-releases@^2.0.6: - version "2.0.7" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.7.tgz#593edbc7c22860ee4d32d3933cfebdfab0c0e0e5" - integrity sha512-EJ3rzxL9pTWPjk5arA0s0dgXpnyiAbJDE6wHT62g7VsgrgQgmmZ+Ru++M1BFofncWja+Pnn3rEr3fieRySAdKQ== + version "2.0.8" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" + integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== nopt@^5.0.0: version "5.0.0" @@ -16874,9 +16873,9 @@ postcss-normalize@^10.0.1: sanitize.css "*" postcss-opacity-percentage@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz#bd698bb3670a0a27f6d657cc16744b3ebf3b1145" - integrity sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w== + version "1.1.3" + resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz#5b89b35551a556e20c5d23eb5260fbfcf5245da6" + integrity sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A== postcss-ordered-values@^5.1.3: version "5.1.3" @@ -17030,7 +17029,7 @@ postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.35, postcss@^7.0 picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.2.15, postcss@^8.3.5, postcss@^8.4.18, postcss@^8.4.4: +postcss@^8.2.15, postcss@^8.3.5, postcss@^8.4.18, postcss@^8.4.19, postcss@^8.4.4: version "8.4.20" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.20.tgz#64c52f509644cecad8567e949f4081d98349dc56" integrity sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g== @@ -18337,9 +18336,9 @@ rxjs@^6.0.0, rxjs@^6.5.2: tslib "^1.9.0" rxjs@^7.5.2, rxjs@^7.5.4, rxjs@^7.5.5: - version "7.6.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.6.0.tgz#361da5362b6ddaa691a2de0b4f2d32028f1eb5a2" - integrity sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ== + version "7.8.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== dependencies: tslib "^2.1.0" From 96d062b6a53bd9c2fca0dcd18ccbf53d97065c29 Mon Sep 17 00:00:00 2001 From: Colin Diesh Date: Sat, 17 Dec 2022 16:01:22 -0700 Subject: [PATCH 158/183] Fix rendering alignment arcs on files that need refname renaming and add jitter setting (#3416) * Add exome example to storybook * Fix refname renaming arc view * Add to read cloud * Remove umd example * Avoid drawing circle with such large radius that it glitches out the rendering * Add jitter --- .../src/LinearReadArcsDisplay/configSchema.ts | 10 + .../src/LinearReadArcsDisplay/drawFeats.ts | 127 +++++++----- .../src/LinearReadArcsDisplay/model.tsx | 45 ++++ .../src/LinearReadCloudDisplay/drawFeats.ts | 54 ++--- .../package.json | 1 - .../umd_example/assembly.js | 33 --- .../umd_example/genomeView.js | 28 --- .../umd_example/index.html | 42 ---- .../umd_example/tracks.js | 22 -- .../package.json | 1 - .../JBrowseLinearGenomeView.stories.tsx | 97 +++++++++ .../umd_example/GRCh38.aliases.txt | 194 ------------------ .../umd_example/assembly.js | 32 --- .../umd_example/genomeView.js | 39 ---- .../umd_example/index.html | 48 ----- .../umd_example/tracks.js | 82 -------- .../webpack.config.js | 3 +- 17 files changed, 247 insertions(+), 611 deletions(-) delete mode 100644 products/jbrowse-react-circular-genome-view/umd_example/assembly.js delete mode 100644 products/jbrowse-react-circular-genome-view/umd_example/genomeView.js delete mode 100644 products/jbrowse-react-circular-genome-view/umd_example/index.html delete mode 100644 products/jbrowse-react-circular-genome-view/umd_example/tracks.js delete mode 100644 products/jbrowse-react-linear-genome-view/umd_example/GRCh38.aliases.txt delete mode 100644 products/jbrowse-react-linear-genome-view/umd_example/assembly.js delete mode 100644 products/jbrowse-react-linear-genome-view/umd_example/genomeView.js delete mode 100644 products/jbrowse-react-linear-genome-view/umd_example/index.html delete mode 100644 products/jbrowse-react-linear-genome-view/umd_example/tracks.js diff --git a/plugins/alignments/src/LinearReadArcsDisplay/configSchema.ts b/plugins/alignments/src/LinearReadArcsDisplay/configSchema.ts index b081299f51..6a95b56bc7 100644 --- a/plugins/alignments/src/LinearReadArcsDisplay/configSchema.ts +++ b/plugins/alignments/src/LinearReadArcsDisplay/configSchema.ts @@ -28,6 +28,16 @@ function configSchemaF(pluginManager: PluginManager) { defaultValue: 1, }, + /** + * #slot + */ + jitter: { + type: 'number', + description: + 'jitters the x position so e.g. if 100 long reads map to same x position, arcs slightly spread out from there', + defaultValue: 2, + }, + /** * #slot */ diff --git a/plugins/alignments/src/LinearReadArcsDisplay/drawFeats.ts b/plugins/alignments/src/LinearReadArcsDisplay/drawFeats.ts index 206c0906fb..b4ba27d84c 100644 --- a/plugins/alignments/src/LinearReadArcsDisplay/drawFeats.ts +++ b/plugins/alignments/src/LinearReadArcsDisplay/drawFeats.ts @@ -9,6 +9,7 @@ import { } from '../shared/color' import { ChainData } from '../shared/fetchChains' import { featurizeSA } from '../MismatchParser' +import { Assembly } from '@jbrowse/core/assemblyManager/assembly' export function hasPairedReads(features: ChainData) { for (const f of features.chains.values()) { @@ -21,6 +22,17 @@ export function hasPairedReads(features: ChainData) { type LGV = LinearGenomeViewModel +function jitter(n: number) { + return Math.random() * 2 * n - n +} + +interface CoreFeat { + strand: number + refName: string + start: number + end: number +} + export default async function drawFeats( self: { setLastDrawnOffsetPx: (n: number) => void @@ -31,34 +43,48 @@ export default async function drawFeats( height: number chainData?: ChainData lineWidthSetting: number + jitterVal: number }, ctx: CanvasRenderingContext2D, ) { - const { chainData } = self + const { + chainData, + height, + colorBy, + drawInter, + drawLongRange, + lineWidthSetting, + jitterVal, + } = self if (!chainData) { return } - const displayHeight = self.height const view = getContainingView(self) as LGV const { assemblyManager } = getSession(self) self.setLastDrawnOffsetPx(view.offsetPx) - ctx.lineWidth = self.lineWidthSetting + ctx.lineWidth = lineWidthSetting const { chains, stats } = chainData const hasPaired = hasPairedReads(chainData) const assemblyName = view.assemblyNames[0] const asm = assemblyManager.get(assemblyName) - const type = self.colorBy?.type || 'insertSizeAndOrientation' + const type = colorBy?.type || 'insertSizeAndOrientation' + if (!asm) { + return + } + + function drawLineAtOffset(p: number, c: string) { + // draws a vertical line off to middle of nowhere if the second end not found + ctx.strokeStyle = c + ctx.beginPath() + ctx.moveTo(p, 0) + ctx.lineTo(p, height) + ctx.stroke() + } function draw( - k1: { - strand: number - refName: string - start: number - end: number - tlen?: number - pair_orientation?: string - }, - k2: { strand: number; refName: string; start: number; end: number }, + k1: CoreFeat & { tlen?: number; pair_orientation?: string }, + k2: CoreFeat, + assembly: Assembly, longRange?: boolean, ) { const s1 = k1.strand @@ -68,14 +94,16 @@ export default async function drawFeats( const p1 = f1 ? k1.start : k1.end const p2 = hasPaired ? (f2 ? k2.start : k2.end) : f2 ? k2.end : k2.start - - const r1 = view.bpToPx({ refName: k1.refName, coord: p1 }) - const r2 = view.bpToPx({ refName: k2.refName, coord: p2 }) + const ra1 = assembly.getCanonicalRefName(k1.refName) + const ra2 = assembly.getCanonicalRefName(k2.refName) + const r1 = view.bpToPx({ refName: ra1, coord: p1 }) + const r2 = view.bpToPx({ refName: ra2, coord: p2 }) if (r1 && r2) { const radius = (r2.offsetPx - r1.offsetPx) / 2 const absrad = Math.abs(radius) const p = r1.offsetPx - view.offsetPx + const p2 = r2.offsetPx - view.offsetPx // bezier (used for non-long-range arcs) requires moveTo before beginPath // arc (used for long-range) requires moveTo after beginPath (or else a @@ -99,9 +127,7 @@ export default async function drawFeats( } else if (type === 'insertSize') { ctx.strokeStyle = getInsertSizeColor(k1, k2, stats) || 'grey' } else if (type === 'gradient') { - ctx.strokeStyle = `hsl(${ - Math.log10(Math.abs(p1 - p2)) * 10 - },50%,50%)` + ctx.strokeStyle = `hsl(${Math.log10(absrad) * 10},50%,50%)` } } else { if (type === 'orientation' || type === 'insertSizeAndOrientation') { @@ -113,53 +139,55 @@ export default async function drawFeats( ctx.strokeStyle = 'grey' } } else if (type === 'gradient') { - ctx.strokeStyle = `hsl(${ - Math.log10(Math.abs(p1 - p2)) * 10 - },50%,50%)` + ctx.strokeStyle = `hsl(${Math.log10(absrad) * 10},50%,50%)` } } } const destX = p + radius * 2 - const destY = Math.min(displayHeight, absrad) + const destY = Math.min(height + jitter(jitterVal), absrad) if (longRange) { - ctx.arc(p + radius, 0, absrad, 0, Math.PI) + // avoid drawing gigantic circles that glitch out the rendering, + // instead draw vertical lines + if (absrad > 100_000) { + drawLineAtOffset(p + jitter(jitterVal), 'red') + drawLineAtOffset(p2 + jitter(jitterVal), 'red') + } else { + ctx.arc(p + radius + jitter(jitterVal), 0, absrad, 0, Math.PI) + ctx.stroke() + } } else { - ctx.bezierCurveTo(p, destY, destX, destY, destX, 0) + ctx.bezierCurveTo( + p + jitter(jitterVal), + destY, + destX, + destY, + destX + jitter(jitterVal), + 0, + ) + ctx.stroke() } - ctx.stroke() - } else if (r1 && self.drawInter) { - // draws a vertical line off to middle of nowhere if the second end not found - const p = r1.offsetPx - view.offsetPx - ctx.strokeStyle = 'purple' - ctx.beginPath() - ctx.moveTo(p, 0) - ctx.lineTo(p, displayHeight) - ctx.stroke() + } else if (r1 && drawInter) { + drawLineAtOffset(r1.offsetPx - view.offsetPx, 'purple') } } for (let i = 0; i < chains.length; i++) { let chain = chains[i] - - if (chain.length === 1 && self.drawLongRange) { + if (chain.length === 1 && drawLongRange) { // singleton feature const f = chain[0] // special case where we look at RPOS/RNEXT if (hasPaired) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const refName = asm?.getCanonicalRefName(f.next_ref!) || f.next_ref! + const refName = f.next_ref! // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const coord = f.next_pos! draw( f, - { - refName, - start: coord, - end: coord, - strand: f.strand, - }, + { refName, start: coord, end: coord, strand: f.strand }, + asm, true, ) } @@ -171,16 +199,7 @@ export default async function drawFeats( for (let i = 0; i < features.length - 1; i++) { const f = features[i] const v1 = features[i + 1] - draw( - f, - { - refName: asm?.getCanonicalRefName(v1.refName) || v1.refName, - start: v1.start, - end: v1.end, - strand: v1.strand, - }, - true, - ) + draw(f, v1, asm, true) } } } else { @@ -192,7 +211,7 @@ export default async function drawFeats( chain = chain.filter(f => !(f.flags & 2048)) } for (let i = 0; i < chain.length - 1; i++) { - draw(chain[i], chain[i + 1], false) + draw(chain[i], chain[i + 1], asm, false) } } } diff --git a/plugins/alignments/src/LinearReadArcsDisplay/model.tsx b/plugins/alignments/src/LinearReadArcsDisplay/model.tsx index f118100145..e834f00cc2 100644 --- a/plugins/alignments/src/LinearReadArcsDisplay/model.tsx +++ b/plugins/alignments/src/LinearReadArcsDisplay/model.tsx @@ -65,6 +65,11 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) { */ lineWidth: types.maybe(types.number), + /** + * #property + */ + jitter: types.maybe(types.number), + /** * #property */ @@ -175,6 +180,15 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) { setLineWidth(n: number) { self.lineWidth = n }, + + /** + * #action + * jitter val, helpful to jitter the x direction so you see better evidence when e.g. 100 + * long reads map to same x position + */ + setJitter(n: number) { + self.jitter = n + }, })) .views(self => { @@ -190,6 +204,13 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) { get lineWidthSetting() { return self.lineWidth ?? getConf(self, 'lineWidth') }, + + /** + * #getter + */ + get jitterVal(): number { + return self.jitter ?? getConf(self, 'jitter') + }, /** * #getter */ @@ -245,6 +266,30 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) { }, ], }, + { + label: 'Jitter x-positions', + subMenu: [ + { + type: 'checkbox', + checked: this.jitterVal === 0, + label: 'None', + onClick: () => self.setJitter(0), + }, + { + type: 'checkbox', + checked: this.jitterVal === 2, + label: 'Small', + onClick: () => self.setJitter(2), + }, + { + type: 'checkbox', + checked: this.jitterVal === 10, + label: 'Large', + onClick: () => self.setJitter(10), + }, + ], + }, + { label: 'Draw inter-region vertical lines', type: 'checkbox', diff --git a/plugins/alignments/src/LinearReadCloudDisplay/drawFeats.ts b/plugins/alignments/src/LinearReadCloudDisplay/drawFeats.ts index 9de423dc57..2921c598a7 100644 --- a/plugins/alignments/src/LinearReadCloudDisplay/drawFeats.ts +++ b/plugins/alignments/src/LinearReadCloudDisplay/drawFeats.ts @@ -1,5 +1,5 @@ import { getConf } from '@jbrowse/core/configuration' -import { getContainingView } from '@jbrowse/core/util' +import { getContainingView, getSession } from '@jbrowse/core/util' import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view' // locals @@ -60,9 +60,15 @@ export default async function drawFeats( if (!chainData) { return } + const { assemblyManager } = getSession(self) const featureHeight = getConf(self, 'featureHeight') const displayHeight = self.height const view = getContainingView(self) as LGV + const assemblyName = view.assemblyNames[0] + const asm = assemblyManager.get(assemblyName) + if (!asm) { + return + } self.setLastDrawnOffsetPx(view.offsetPx) @@ -76,22 +82,13 @@ export default async function drawFeats( if (chain[0].flags & 1 && chain.length > 1) { const v0 = chain[0] const v1 = chain[1] - const r1s = view.bpToPx({ - refName: v0.refName, - coord: v0.start, - }) - const r1e = view.bpToPx({ - refName: v0.refName, - coord: v0.end, - }) - const r2s = view.bpToPx({ - refName: v1.refName, - coord: v1.start, - }) - const r2e = view.bpToPx({ - refName: v1.refName, - coord: v1.end, - }) + const ra1 = asm.getCanonicalRefName(v0.refName) + const ra2 = asm.getCanonicalRefName(v1.refName) + const r1s = view.bpToPx({ refName: ra1, coord: v0.start }) + const r1e = view.bpToPx({ refName: ra1, coord: v0.end }) + const r2s = view.bpToPx({ refName: ra2, coord: v1.start }) + const r2e = view.bpToPx({ refName: ra2, coord: v1.end }) + let distance = 0 if ( @@ -121,22 +118,13 @@ export default async function drawFeats( for (let i = 1; i < chain.length; i++) { const v0 = chain[i - 1] const v1 = chain[i] - const r1s = view.bpToPx({ - refName: v0.refName, - coord: v0.start, - }) - const r1e = view.bpToPx({ - refName: v0.refName, - coord: v0.end, - }) - const r2s = view.bpToPx({ - refName: v1.refName, - coord: v1.start, - }) - const r2e = view.bpToPx({ - refName: v1.refName, - coord: v1.end, - }) + const ra1 = asm.getCanonicalRefName(v0.refName) + const ra2 = asm.getCanonicalRefName(v1.refName) + const r1s = view.bpToPx({ refName: ra1, coord: v0.start }) + const r1e = view.bpToPx({ refName: ra1, coord: v0.end }) + const r2s = view.bpToPx({ refName: ra2, coord: v1.start }) + const r2e = view.bpToPx({ refName: ra2, coord: v1.end }) + let distance = 0 if ( diff --git a/products/jbrowse-react-circular-genome-view/package.json b/products/jbrowse-react-circular-genome-view/package.json index c5844f0707..8cc88222bb 100644 --- a/products/jbrowse-react-circular-genome-view/package.json +++ b/products/jbrowse-react-circular-genome-view/package.json @@ -24,7 +24,6 @@ "docs" ], "scripts": { - "start:umd": "cross-env NODE_ENV=development webpack-dev-server", "prebuild": "npm run clean", "build": "npm-run-all build:*", "prepublishOnly": "node output-version.js > src/version.js && git add -A src && git commit -m '[skip ci] Bump version.js'", diff --git a/products/jbrowse-react-circular-genome-view/umd_example/assembly.js b/products/jbrowse-react-circular-genome-view/umd_example/assembly.js deleted file mode 100644 index 55f9a967ad..0000000000 --- a/products/jbrowse-react-circular-genome-view/umd_example/assembly.js +++ /dev/null @@ -1,33 +0,0 @@ -export default { - name: 'volvox', - aliases: ['vvx'], - sequence: { - type: 'ReferenceSequenceTrack', - trackId: 'volvox_refseq', - adapter: { - type: 'TwoBitAdapter', - twoBitLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/volvox/volvox.2bit', - locationType: 'UriLocation', - }, - }, - }, - refNameAliases: { - adapter: { - type: 'FromConfigAdapter', - adapterId: 'W6DyPGJ0UU', - features: [ - { - refName: 'ctgA', - uniqueId: 'alias1', - aliases: ['A', 'contigA'], - }, - { - refName: 'ctgB', - uniqueId: 'alias2', - aliases: ['B', 'contigB'], - }, - ], - }, - }, -} diff --git a/products/jbrowse-react-circular-genome-view/umd_example/genomeView.js b/products/jbrowse-react-circular-genome-view/umd_example/genomeView.js deleted file mode 100644 index 25d26b402d..0000000000 --- a/products/jbrowse-react-circular-genome-view/umd_example/genomeView.js +++ /dev/null @@ -1,28 +0,0 @@ -/* global JBrowseReactCircularGenomeView React, ReactDOM */ -import assembly from './assembly.js' -import tracks from './tracks.js' - -const { createViewState, JBrowseCircularGenomeView } = - JBrowseReactCircularGenomeView -const { createElement } = React -const { render } = ReactDOM - -const updates = document.getElementById('update') -const state = new createViewState({ - assembly, - tracks, - onChange: patch => { - updates.innerHTML += JSON.stringify(patch) + '\n' - }, -}) - -const textArea = document.getElementById('viewstate') -document.getElementById('showviewstate').addEventListener('click', () => { - textArea.innerHTML = JSON.stringify(state.session.view, undefined, 2) -}) - -const domContainer = document.getElementById('jbrowse_circular_genome_view') -render( - createElement(JBrowseCircularGenomeView, { viewState: state }), - domContainer, -) diff --git a/products/jbrowse-react-circular-genome-view/umd_example/index.html b/products/jbrowse-react-circular-genome-view/umd_example/index.html deleted file mode 100644 index 94ce4ff284..0000000000 --- a/products/jbrowse-react-circular-genome-view/umd_example/index.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - Using JBrowse Circular Genome View - - - - - - - - - - -

Using JBrowse Circular Genome View!

-
- - - -

updates:

- - - diff --git a/products/jbrowse-react-circular-genome-view/umd_example/tracks.js b/products/jbrowse-react-circular-genome-view/umd_example/tracks.js deleted file mode 100644 index dc2b2624b8..0000000000 --- a/products/jbrowse-react-circular-genome-view/umd_example/tracks.js +++ /dev/null @@ -1,22 +0,0 @@ -export default [ - { - type: 'VariantTrack', - trackId: 'volvox_sv_test', - name: 'volvox structural variant test', - category: ['VCF'], - assemblyNames: ['volvox'], - adapter: { - type: 'VcfTabixAdapter', - vcfGzLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/volvox/volvox.dup.vcf.gz', - locationType: 'UriLocation', - }, - index: { - location: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/volvox/volvox.dup.vcf.gz.tbi', - locationType: 'UriLocation', - }, - }, - }, - }, -] diff --git a/products/jbrowse-react-linear-genome-view/package.json b/products/jbrowse-react-linear-genome-view/package.json index 116522c3ec..bd1462ff69 100644 --- a/products/jbrowse-react-linear-genome-view/package.json +++ b/products/jbrowse-react-linear-genome-view/package.json @@ -28,7 +28,6 @@ "docs" ], "scripts": { - "start:umd": "cross-env NODE_ENV=development webpack-dev-server", "build": "npm-run-all build:*", "prepublishOnly": "node output-version.js > src/version.js && git add -A src && git commit -m '[skip ci] Bump version.js'", "build:esm": "tsc --build tsconfig.build.esm.json", diff --git a/products/jbrowse-react-linear-genome-view/stories/JBrowseLinearGenomeView.stories.tsx b/products/jbrowse-react-linear-genome-view/stories/JBrowseLinearGenomeView.stories.tsx index 85fb446c9c..090b6cb37a 100644 --- a/products/jbrowse-react-linear-genome-view/stories/JBrowseLinearGenomeView.stories.tsx +++ b/products/jbrowse-react-linear-genome-view/stories/JBrowseLinearGenomeView.stories.tsx @@ -498,6 +498,103 @@ export const WithInlinePlugins = () => { return } +export const Hg38Exome = () => { + const assembly = { + name: 'GRCh38', + sequence: { + type: 'ReferenceSequenceTrack', + trackId: 'GRCh38-ReferenceSequenceTrack', + adapter: { + type: 'BgzipFastaAdapter', + fastaLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz', + }, + faiLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz.fai', + }, + gziLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz.gzi', + }, + }, + }, + aliases: ['hg38'], + refNameAliases: { + adapter: { + type: 'RefNameAliasAdapter', + location: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/hg38_aliases.txt', + }, + }, + }, + } + + const tracks = [ + { + type: 'FeatureTrack', + trackId: + 'GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff', + name: 'NCBI RefSeq Genes', + category: ['Genes'], + assemblyNames: ['GRCh38'], + adapter: { + type: 'Gff3TabixAdapter', + gffGzLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz', + }, + index: { + location: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.tbi', + }, + }, + }, + renderer: { + type: 'SvgFeatureRenderer', + }, + }, + { + type: 'AlignmentsTrack', + trackId: 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome', + name: 'NA12878 Exome', + category: ['1000 Genomes', 'Alignments'], + assemblyNames: ['GRCh38'], + adapter: { + type: 'CramAdapter', + cramLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/alignments/NA12878/NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome.cram', + locationType: 'UriLocation', + }, + craiLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/alignments/NA12878/NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome.cram.crai', + locationType: 'UriLocation', + }, + sequenceAdapter: { + type: 'BgzipFastaAdapter', + fastaLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz', + locationType: 'UriLocation', + }, + faiLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz.fai', + locationType: 'UriLocation', + }, + gziLocation: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz.gzi', + locationType: 'UriLocation', + }, + }, + }, + }, + ] + + const state = createViewState({ + assembly, + tracks, + location: '1:100,987,269..100,987,368', + }) + state.session.view.showTrack('NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome') + + return +} export const WithExternalPlugins = () => { // usage with buildtime plugins // this plugins array is then passed to the createViewState constructor diff --git a/products/jbrowse-react-linear-genome-view/umd_example/GRCh38.aliases.txt b/products/jbrowse-react-linear-genome-view/umd_example/GRCh38.aliases.txt deleted file mode 100644 index cb4caabd4b..0000000000 --- a/products/jbrowse-react-linear-genome-view/umd_example/GRCh38.aliases.txt +++ /dev/null @@ -1,194 +0,0 @@ -1 CM000663.2 chr1 NC_000001.11 -10 CM000672.2 chr10 NC_000010.11 -11 CM000673.2 chr11 NC_000011.10 -12 CM000674.2 chr12 NC_000012.12 -13 CM000675.2 chr13 NC_000013.11 -14 CM000676.2 chr14 NC_000014.9 -15 CM000677.2 chr15 NC_000015.10 -16 CM000678.2 chr16 NC_000016.10 -17 CM000679.2 chr17 NC_000017.11 -18 CM000680.2 chr18 NC_000018.10 -19 CM000681.2 chr19 NC_000019.10 -2 CM000664.2 chr2 NC_000002.12 -20 CM000682.2 chr20 NC_000020.11 -21 CM000683.2 chr21 NC_000021.9 -22 CM000684.2 chr22 NC_000022.11 -3 CM000665.2 chr3 NC_000003.12 -4 CM000666.2 chr4 NC_000004.12 -5 CM000667.2 chr5 NC_000005.10 -6 CM000668.2 chr6 NC_000006.12 -7 CM000669.2 chr7 NC_000007.14 -8 CM000670.2 chr8 NC_000008.11 -9 CM000671.2 chr9 NC_000009.12 -GL000008.2 chr4_GL000008v2_random NT_113793.3 -GL000009.2 chr14_GL000009v2_random NT_113796.3 -GL000194.1 chr14_GL000194v1_random NT_113888.1 -GL000195.1 chrUn_GL000195v1 NT_113901.1 -GL000205.2 chr17_GL000205v2_random NT_113930.2 -GL000208.1 chr5_GL000208v1_random NT_113948.1 -GL000213.1 chrUn_GL000213v1 NT_167208.1 -GL000214.1 chrUn_GL000214v1 NT_167209.1 -GL000216.2 chrUn_GL000216v2 NT_167211.2 -GL000218.1 chrUn_GL000218v1 NT_113889.1 -GL000219.1 chrUn_GL000219v1 NT_167213.1 -GL000220.1 chrUn_GL000220v1 NT_167214.1 -GL000221.1 chr3_GL000221v1_random NT_167215.1 -GL000224.1 chrUn_GL000224v1 NT_167218.1 -GL000225.1 chr14_GL000225v1_random NT_167219.1 -GL000226.1 chrUn_GL000226v1 NT_167220.1 -KI270302.1 chrUn_KI270302v1 NT_187396.1 -KI270303.1 chrUn_KI270303v1 NT_187398.1 -KI270304.1 chrUn_KI270304v1 NT_187397.1 -KI270305.1 chrUn_KI270305v1 NT_187399.1 -KI270310.1 chrUn_KI270310v1 NT_187402.1 -KI270311.1 chrUn_KI270311v1 NT_187406.1 -KI270312.1 chrUn_KI270312v1 NT_187405.1 -KI270315.1 chrUn_KI270315v1 NT_187404.1 -KI270316.1 chrUn_KI270316v1 NT_187403.1 -KI270317.1 chrUn_KI270317v1 NT_187407.1 -KI270320.1 chrUn_KI270320v1 NT_187401.1 -KI270322.1 chrUn_KI270322v1 NT_187400.1 -KI270329.1 chrUn_KI270329v1 NT_187459.1 -KI270330.1 chrUn_KI270330v1 NT_187458.1 -KI270333.1 chrUn_KI270333v1 NT_187461.1 -KI270334.1 chrUn_KI270334v1 NT_187460.1 -KI270335.1 chrUn_KI270335v1 NT_187462.1 -KI270336.1 chrUn_KI270336v1 NT_187465.1 -KI270337.1 chrUn_KI270337v1 NT_187466.1 -KI270338.1 chrUn_KI270338v1 NT_187463.1 -KI270340.1 chrUn_KI270340v1 NT_187464.1 -KI270362.1 chrUn_KI270362v1 NT_187469.1 -KI270363.1 chrUn_KI270363v1 NT_187467.1 -KI270364.1 chrUn_KI270364v1 NT_187468.1 -KI270366.1 chrUn_KI270366v1 NT_187470.1 -KI270371.1 chrUn_KI270371v1 NT_187494.1 -KI270372.1 chrUn_KI270372v1 NT_187491.1 -KI270373.1 chrUn_KI270373v1 NT_187492.1 -KI270374.1 chrUn_KI270374v1 NT_187490.1 -KI270375.1 chrUn_KI270375v1 NT_187493.1 -KI270376.1 chrUn_KI270376v1 NT_187489.1 -KI270378.1 chrUn_KI270378v1 NT_187471.1 -KI270379.1 chrUn_KI270379v1 NT_187472.1 -KI270381.1 chrUn_KI270381v1 NT_187486.1 -KI270382.1 chrUn_KI270382v1 NT_187488.1 -KI270383.1 chrUn_KI270383v1 NT_187482.1 -KI270384.1 chrUn_KI270384v1 NT_187484.1 -KI270385.1 chrUn_KI270385v1 NT_187487.1 -KI270386.1 chrUn_KI270386v1 NT_187480.1 -KI270387.1 chrUn_KI270387v1 NT_187475.1 -KI270388.1 chrUn_KI270388v1 NT_187478.1 -KI270389.1 chrUn_KI270389v1 NT_187473.1 -KI270390.1 chrUn_KI270390v1 NT_187474.1 -KI270391.1 chrUn_KI270391v1 NT_187481.1 -KI270392.1 chrUn_KI270392v1 NT_187485.1 -KI270393.1 chrUn_KI270393v1 NT_187483.1 -KI270394.1 chrUn_KI270394v1 NT_187479.1 -KI270395.1 chrUn_KI270395v1 NT_187476.1 -KI270396.1 chrUn_KI270396v1 NT_187477.1 -KI270411.1 chrUn_KI270411v1 NT_187409.1 -KI270412.1 chrUn_KI270412v1 NT_187408.1 -KI270414.1 chrUn_KI270414v1 NT_187410.1 -KI270417.1 chrUn_KI270417v1 NT_187415.1 -KI270418.1 chrUn_KI270418v1 NT_187412.1 -KI270419.1 chrUn_KI270419v1 NT_187411.1 -KI270420.1 chrUn_KI270420v1 NT_187413.1 -KI270422.1 chrUn_KI270422v1 NT_187416.1 -KI270423.1 chrUn_KI270423v1 NT_187417.1 -KI270424.1 chrUn_KI270424v1 NT_187414.1 -KI270425.1 chrUn_KI270425v1 NT_187418.1 -KI270429.1 chrUn_KI270429v1 NT_187419.1 -KI270435.1 chrUn_KI270435v1 NT_187424.1 -KI270438.1 chrUn_KI270438v1 NT_187425.1 -KI270442.1 chrUn_KI270442v1 NT_187420.1 -KI270448.1 chrUn_KI270448v1 NT_187495.1 -KI270465.1 chrUn_KI270465v1 NT_187422.1 -KI270466.1 chrUn_KI270466v1 NT_187421.1 -KI270467.1 chrUn_KI270467v1 NT_187423.1 -KI270468.1 chrUn_KI270468v1 NT_187426.1 -KI270507.1 chrUn_KI270507v1 NT_187437.1 -KI270508.1 chrUn_KI270508v1 NT_187430.1 -KI270509.1 chrUn_KI270509v1 NT_187428.1 -KI270510.1 chrUn_KI270510v1 NT_187427.1 -KI270511.1 chrUn_KI270511v1 NT_187435.1 -KI270512.1 chrUn_KI270512v1 NT_187432.1 -KI270515.1 chrUn_KI270515v1 NT_187436.1 -KI270516.1 chrUn_KI270516v1 NT_187431.1 -KI270517.1 chrUn_KI270517v1 NT_187438.1 -KI270518.1 chrUn_KI270518v1 NT_187429.1 -KI270519.1 chrUn_KI270519v1 NT_187433.1 -KI270521.1 chrUn_KI270521v1 NT_187496.1 -KI270522.1 chrUn_KI270522v1 NT_187434.1 -KI270528.1 chrUn_KI270528v1 NT_187440.1 -KI270529.1 chrUn_KI270529v1 NT_187439.1 -KI270530.1 chrUn_KI270530v1 NT_187441.1 -KI270538.1 chrUn_KI270538v1 NT_187443.1 -KI270539.1 chrUn_KI270539v1 NT_187442.1 -KI270544.1 chrUn_KI270544v1 NT_187444.1 -KI270548.1 chrUn_KI270548v1 NT_187445.1 -KI270579.1 chrUn_KI270579v1 NT_187450.1 -KI270580.1 chrUn_KI270580v1 NT_187448.1 -KI270581.1 chrUn_KI270581v1 NT_187449.1 -KI270582.1 chrUn_KI270582v1 NT_187454.1 -KI270583.1 chrUn_KI270583v1 NT_187446.1 -KI270584.1 chrUn_KI270584v1 NT_187453.1 -KI270587.1 chrUn_KI270587v1 NT_187447.1 -KI270588.1 chrUn_KI270588v1 NT_187455.1 -KI270589.1 chrUn_KI270589v1 NT_187451.1 -KI270590.1 chrUn_KI270590v1 NT_187452.1 -KI270591.1 chrUn_KI270591v1 NT_187457.1 -KI270593.1 chrUn_KI270593v1 NT_187456.1 -KI270706.1 chr1_KI270706v1_random NT_187361.1 -KI270707.1 chr1_KI270707v1_random NT_187362.1 -KI270708.1 chr1_KI270708v1_random NT_187363.1 -KI270709.1 chr1_KI270709v1_random NT_187364.1 -KI270710.1 chr1_KI270710v1_random NT_187365.1 -KI270711.1 chr1_KI270711v1_random NT_187366.1 -KI270712.1 chr1_KI270712v1_random NT_187367.1 -KI270713.1 chr1_KI270713v1_random NT_187368.1 -KI270714.1 chr1_KI270714v1_random NT_187369.1 -KI270715.1 chr2_KI270715v1_random NT_187370.1 -KI270716.1 chr2_KI270716v1_random NT_187371.1 -KI270717.1 chr9_KI270717v1_random NT_187372.1 -KI270718.1 chr9_KI270718v1_random NT_187373.1 -KI270719.1 chr9_KI270719v1_random NT_187374.1 -KI270720.1 chr9_KI270720v1_random NT_187375.1 -KI270721.1 chr11_KI270721v1_random NT_187376.1 -KI270722.1 chr14_KI270722v1_random NT_187377.1 -KI270723.1 chr14_KI270723v1_random NT_187378.1 -KI270724.1 chr14_KI270724v1_random NT_187379.1 -KI270725.1 chr14_KI270725v1_random NT_187380.1 -KI270726.1 chr14_KI270726v1_random NT_187381.1 -KI270727.1 chr15_KI270727v1_random NT_187382.1 -KI270728.1 chr16_KI270728v1_random NT_187383.1 -KI270729.1 chr17_KI270729v1_random NT_187384.1 -KI270730.1 chr17_KI270730v1_random NT_187385.1 -KI270731.1 chr22_KI270731v1_random NT_187386.1 -KI270732.1 chr22_KI270732v1_random NT_187387.1 -KI270733.1 chr22_KI270733v1_random NT_187388.1 -KI270734.1 chr22_KI270734v1_random NT_187389.1 -KI270735.1 chr22_KI270735v1_random NT_187390.1 -KI270736.1 chr22_KI270736v1_random NT_187391.1 -KI270737.1 chr22_KI270737v1_random NT_187392.1 -KI270738.1 chr22_KI270738v1_random NT_187393.1 -KI270739.1 chr22_KI270739v1_random NT_187394.1 -KI270740.1 chrY_KI270740v1_random NT_187395.1 -KI270741.1 chrUn_KI270741v1 NT_187497.1 -KI270742.1 chrUn_KI270742v1 NT_187513.1 -KI270743.1 chrUn_KI270743v1 NT_187498.1 -KI270744.1 chrUn_KI270744v1 NT_187499.1 -KI270745.1 chrUn_KI270745v1 NT_187500.1 -KI270746.1 chrUn_KI270746v1 NT_187501.1 -KI270747.1 chrUn_KI270747v1 NT_187502.1 -KI270748.1 chrUn_KI270748v1 NT_187503.1 -KI270749.1 chrUn_KI270749v1 NT_187504.1 -KI270750.1 chrUn_KI270750v1 NT_187505.1 -KI270751.1 chrUn_KI270751v1 NT_187506.1 -KI270752.1 chrUn_KI270752v1 NT_187507.1 -KI270753.1 chrUn_KI270753v1 NT_187508.1 -KI270754.1 chrUn_KI270754v1 NT_187509.1 -KI270755.1 chrUn_KI270755v1 NT_187510.1 -KI270756.1 chrUn_KI270756v1 NT_187511.1 -KI270757.1 chrUn_KI270757v1 NT_187512.1 -MT chrM J01415.2 NC_012920.1 -X CM000685.2 chrX NC_000023.11 -Y CM000686.2 chrY NC_000024.10 diff --git a/products/jbrowse-react-linear-genome-view/umd_example/assembly.js b/products/jbrowse-react-linear-genome-view/umd_example/assembly.js deleted file mode 100644 index 7fd62437ec..0000000000 --- a/products/jbrowse-react-linear-genome-view/umd_example/assembly.js +++ /dev/null @@ -1,32 +0,0 @@ -export default { - name: 'GRCh38', - sequence: { - type: 'ReferenceSequenceTrack', - trackId: 'GRCh38-ReferenceSequenceTrack', - adapter: { - type: 'BgzipFastaAdapter', - fastaLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz', - locationType: 'UriLocation', - }, - faiLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz.fai', - locationType: 'UriLocation', - }, - gziLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz.gzi', - locationType: 'UriLocation', - }, - }, - }, - aliases: ['hg38'], - refNameAliases: { - adapter: { - type: 'RefNameAliasAdapter', - location: { - uri: 'GRCh38.aliases.txt', - locationType: 'UriLocation', - }, - }, - }, -} diff --git a/products/jbrowse-react-linear-genome-view/umd_example/genomeView.js b/products/jbrowse-react-linear-genome-view/umd_example/genomeView.js deleted file mode 100644 index ee06a2b3d6..0000000000 --- a/products/jbrowse-react-linear-genome-view/umd_example/genomeView.js +++ /dev/null @@ -1,39 +0,0 @@ -/* global JBrowseReactLinearGenomeView React, ReactDOM */ -import assembly from './assembly.js' -import tracks from './tracks.js' - -const { createViewState, JBrowseLinearGenomeView } = - JBrowseReactLinearGenomeView -const { createElement } = React -const { render } = ReactDOM - -const updates = document.getElementById('update') -const state = new createViewState({ - assembly, - tracks, - location: '1:100,987,269..100,987,368', - onChange: patch => { - updates.innerHTML += JSON.stringify(patch) + '\n' - }, -}) - -function navTo(event) { - state.session.view.navToLocString(event.target.dataset.location) -} -const buttons = document.getElementsByTagName('button') -for (const button of buttons) { - if (button.dataset.type === 'gene_button') { - button.addEventListener('click', navTo) - } -} - -const textArea = document.getElementById('viewstate') -document.getElementById('showviewstate').addEventListener('click', () => { - textArea.innerHTML = JSON.stringify(state.session.view, undefined, 2) -}) - -const domContainer = document.getElementById('jbrowse_linear_genome_view') -render( - createElement(JBrowseLinearGenomeView, { viewState: state }), - domContainer, -) diff --git a/products/jbrowse-react-linear-genome-view/umd_example/index.html b/products/jbrowse-react-linear-genome-view/umd_example/index.html deleted file mode 100644 index 3b381131a3..0000000000 --- a/products/jbrowse-react-linear-genome-view/umd_example/index.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - Using JBrowse Linear Genome View - - - - - - - - - - -

Using JBrowse Linear Genome View!

- - -
- - - -

updates:

- - - diff --git a/products/jbrowse-react-linear-genome-view/umd_example/tracks.js b/products/jbrowse-react-linear-genome-view/umd_example/tracks.js deleted file mode 100644 index bf72d97944..0000000000 --- a/products/jbrowse-react-linear-genome-view/umd_example/tracks.js +++ /dev/null @@ -1,82 +0,0 @@ -export default [ - { - type: 'BasicTrack', - trackId: - 'GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff', - name: 'NCBI RefSeq Genes', - category: ['Genes'], - assemblyNames: ['GRCh38'], - adapter: { - type: 'Gff3TabixAdapter', - gffGzLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz', - locationType: 'UriLocation', - }, - index: { - location: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz.tbi', - locationType: 'UriLocation', - }, - indexType: 'TBI', - }, - }, - renderer: { - type: 'SvgFeatureRenderer', - }, - }, - { - type: 'AlignmentsTrack', - trackId: 'NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome', - name: 'NA12878 Exome', - category: ['1000 Genomes', 'Alignments'], - assemblyNames: ['GRCh38'], - adapter: { - type: 'CramAdapter', - cramLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/alignments/NA12878/NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome.cram', - locationType: 'UriLocation', - }, - craiLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/alignments/NA12878/NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome.cram.crai', - locationType: 'UriLocation', - }, - sequenceAdapter: { - type: 'BgzipFastaAdapter', - fastaLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz', - locationType: 'UriLocation', - }, - faiLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz.fai', - locationType: 'UriLocation', - }, - gziLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/fasta/GRCh38.fa.gz.gzi', - locationType: 'UriLocation', - }, - }, - }, - }, - { - type: 'VariantTrack', - trackId: - 'ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf', - name: '1000 Genomes Variant Calls', - category: ['1000 Genomes', 'Variants'], - assemblyNames: ['GRCh38'], - adapter: { - type: 'VcfTabixAdapter', - vcfGzLocation: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/variants/ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf.gz', - locationType: 'UriLocation', - }, - index: { - location: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/variants/ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf.gz.tbi', - locationType: 'UriLocation', - }, - indexType: 'TBI', - }, - }, - }, -] diff --git a/products/jbrowse-react-linear-genome-view/webpack.config.js b/products/jbrowse-react-linear-genome-view/webpack.config.js index 619f418cb9..a67bedeead 100644 --- a/products/jbrowse-react-linear-genome-view/webpack.config.js +++ b/products/jbrowse-react-linear-genome-view/webpack.config.js @@ -22,8 +22,7 @@ module.exports = { }, devServer: { port: 9000, - open: true, - openPage: 'umd_example/', + open: 'umd_example/', headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': '*', From 3f9727dbc958863f9078e3e613b4e3b874b7972b Mon Sep 17 00:00:00 2001 From: Colin Date: Sat, 17 Dec 2022 19:00:00 -0700 Subject: [PATCH 159/183] Fixup tests Simplify some code --- packages/core/util/types/index.ts | 7 +- .../src/LinearReadArcsDisplay/configSchema.ts | 4 +- .../src/CircularView/models/CircularView.ts | 1 - .../src/ucsc-trackhub/model.ts | 41 +- .../src/ucsc-trackhub/ucscTrackHub.js | 442 ------------------ .../src/ucsc-trackhub/ucscTrackHub.ts | 326 +++++++++++++ products/jbrowse-cli/src/types/common.ts | 5 +- .../jbrowse-web/src/tests/Connection.test.tsx | 9 +- .../jbrowse-web/src/tests/Dotplot.test.tsx | 15 +- ...nspector.test.tsx => SVInspector.test.tsx} | 0 ...eadsheet.test.tsx => Spreadsheet.test.tsx} | 0 yarn.lock | 30 +- 12 files changed, 382 insertions(+), 498 deletions(-) delete mode 100644 plugins/data-management/src/ucsc-trackhub/ucscTrackHub.js create mode 100644 plugins/data-management/src/ucsc-trackhub/ucscTrackHub.ts rename products/jbrowse-web/src/tests/{2.SVInspector.test.tsx => SVInspector.test.tsx} (100%) rename products/jbrowse-web/src/tests/{1.Spreadsheet.test.tsx => Spreadsheet.test.tsx} (100%) diff --git a/packages/core/util/types/index.ts b/packages/core/util/types/index.ts index 63ab36d3e0..473bf47da0 100644 --- a/packages/core/util/types/index.ts +++ b/packages/core/util/types/index.ts @@ -385,7 +385,12 @@ export interface LocalPathLocation export interface UriLocation extends SnapshotIn {} export function isUriLocation(location: unknown): location is UriLocation { - return typeof location === 'object' && location !== null && 'uri' in location + return ( + typeof location === 'object' && + location !== null && + 'uri' in location && + !!location.uri + ) } export class AuthNeededError extends Error { constructor(public message: string, public url: string) { diff --git a/plugins/alignments/src/LinearReadArcsDisplay/configSchema.ts b/plugins/alignments/src/LinearReadArcsDisplay/configSchema.ts index 6a95b56bc7..79e04ec5c5 100644 --- a/plugins/alignments/src/LinearReadArcsDisplay/configSchema.ts +++ b/plugins/alignments/src/LinearReadArcsDisplay/configSchema.ts @@ -34,8 +34,8 @@ function configSchemaF(pluginManager: PluginManager) { jitter: { type: 'number', description: - 'jitters the x position so e.g. if 100 long reads map to same x position, arcs slightly spread out from there', - defaultValue: 2, + 'jitters the x position so e.g. if many reads map to exact same x position, jittering makes it easy to see that there are many of them', + defaultValue: 0, }, /** diff --git a/plugins/circular-view/src/CircularView/models/CircularView.ts b/plugins/circular-view/src/CircularView/models/CircularView.ts index 40eb6fc1ab..5b34e07c30 100644 --- a/plugins/circular-view/src/CircularView/models/CircularView.ts +++ b/plugins/circular-view/src/CircularView/models/CircularView.ts @@ -487,7 +487,6 @@ function stateModelFactory(pluginManager: PluginManager) { * #action */ setError(error: unknown) { - console.error(error) self.error = error }, diff --git a/plugins/data-management/src/ucsc-trackhub/model.ts b/plugins/data-management/src/ucsc-trackhub/model.ts index 4f8bbcbd16..32c5678ca4 100644 --- a/plugins/data-management/src/ucsc-trackhub/model.ts +++ b/plugins/data-management/src/ucsc-trackhub/model.ts @@ -43,11 +43,11 @@ export default function UCSCTrackHubConnection(pluginManager: PluginManager) { const genomesFileLocation = hubUri ? { uri: new URL(genomeFile, hubUri).href, - locationType: 'UriLocation', + locationType: 'UriLocation' as const, } : { localPath: genomeFile, - locationType: 'LocalPathLocation', + locationType: 'LocalPathLocation' as const, } const genomesFile = await fetchGenomesFile(genomesFileLocation) const trackDbData = [] @@ -67,43 +67,32 @@ export default function UCSCTrackHubConnection(pluginManager: PluginManager) { `Cannot find assembly for "${genomeName}" from the genomes file for connection "${connectionName}"`, ) } - const trackDb = genome.get('trackDb') - if (!trackDb) { + const db = genome.get('trackDb') + if (!db) { throw new Error('genomesFile not found on hub') } - const trackDbFileLocation = hubUri + const base = new URL(genomeFile, hubUri) + const trackDbLoc = hubUri ? { - uri: new URL(trackDb, new URL(genomeFile, hubUri)).href, - locationType: 'UriLocation', + uri: new URL(db, base).href, + locationType: 'UriLocation' as const, } : { - localPath: trackDb, - locationType: 'LocalPathLocation', + localPath: db, + locationType: 'LocalPathLocation' as const, } - trackDbData.push([ - trackDbFileLocation, - await fetchTrackDbFile(trackDbFileLocation), - genomeName, - conf, - ] as const) + const trackDb = await fetchTrackDbFile(trackDbLoc) + trackDbData.push([trackDbLoc, trackDb, genomeName, conf] as const) } for (const [ - trackDbFileLocation, + trackDbLoc, trackDbFile, genomeName, conf, ] of trackDbData) { - const sequenceAdapter = readConfObject(conf, [ - 'sequence', - 'adapter', - ]) + const seqAdapter = readConfObject(conf, ['sequence', 'adapter']) self.addTrackConfs( - generateTracks( - trackDbFile, - trackDbFileLocation, - genomeName, - sequenceAdapter, - ), + generateTracks(trackDbFile, trackDbLoc, genomeName, seqAdapter), ) } } catch (e) { diff --git a/plugins/data-management/src/ucsc-trackhub/ucscTrackHub.js b/plugins/data-management/src/ucsc-trackhub/ucscTrackHub.js deleted file mode 100644 index 74165a9400..0000000000 --- a/plugins/data-management/src/ucsc-trackhub/ucscTrackHub.js +++ /dev/null @@ -1,442 +0,0 @@ -import { objectHash } from '@jbrowse/core/util' -import { openLocation } from '@jbrowse/core/util/io' -import { - generateUnsupportedTrackConf, - generateUnknownTrackConf, -} from '@jbrowse/core/util/tracks' -import ucscAssemblies from './ucscAssemblies' - -export { ucscAssemblies } - -export async function fetchHubFile(hubFileLocation) { - try { - const hubFileText = await openLocation(hubFileLocation).readFile('utf8') - const { HubFile } = await import('@gmod/ucsc-hub') - return new HubFile(hubFileText) - } catch (error) { - throw new Error(`Not a valid hub.txt file, got error: '${error}'`) - } -} - -export async function fetchGenomesFile(genomesFileLocation) { - const genomesFileText = await openLocation(genomesFileLocation).readFile( - 'utf8', - ) - const { GenomesFile } = await import('@gmod/ucsc-hub') - return new GenomesFile(genomesFileText) -} - -export async function fetchTrackDbFile(trackDbFileLocation) { - const text = await openLocation(trackDbFileLocation).readFile('utf8') - const { TrackDbFile } = await import('@gmod/ucsc-hub') - return new TrackDbFile(text) -} - -export function generateTracks( - trackDb, - trackDbFileLocation, - assemblyName, - sequenceAdapter, -) { - const tracks = [] - - trackDb.forEach((track, trackName) => { - const trackKeys = Array.from(track.keys()) - const parentTrackKeys = [ - 'superTrack', - 'compositeTrack', - 'container', - 'view', - ] - if (trackKeys.some(key => parentTrackKeys.includes(key))) { - return - } - const parentTracks = [] - let currentTrackName = trackName - do { - currentTrackName = trackDb.get(currentTrackName).get('parent') - if (currentTrackName) { - ;[currentTrackName] = currentTrackName.split(' ') - parentTracks.push(trackDb.get(currentTrackName)) - } - } while (currentTrackName) - parentTracks.reverse() - const categories = parentTracks.map(parentTrack => - parentTrack.get('shortLabel'), - ) - const res = makeTrackConfig( - track, - categories, - trackDbFileLocation, - trackDb, - sequenceAdapter, - ) - res.trackId = `ucsc-trackhub-${objectHash(res)}` - res.assemblyNames = [assemblyName] - tracks.push(res) - }) - - return tracks -} - -function makeTrackConfig( - track, - categories, - trackDbFileLocation, - trackDb, - sequenceAdapter, -) { - let trackType = track.get('type') - if (!trackType) { - trackType = trackDb.get(track.get('parent')).get('type') - } - let baseTrackType = trackType.split(' ')[0] - if ( - baseTrackType === 'bam' && - track.get('bigDataUrl').toLowerCase().endsWith('cram') - ) { - baseTrackType = 'cram' - } - let bigDataLocation - if (trackDbFileLocation.uri) { - bigDataLocation = { - uri: new URL(track.get('bigDataUrl'), trackDbFileLocation.uri).href, - locationType: 'UriLocation', - } - } else { - bigDataLocation = { - localPath: track.get('bigDataUrl'), - locationType: 'LocalPathLocation', - } - } - let bigDataIndexLocation - - switch (baseTrackType) { - case 'bam': - if (trackDbFileLocation.uri) { - bigDataIndexLocation = track.get('bigDataIndex') - ? { - uri: new URL(track.get('bigDataIndex'), trackDbFileLocation.uri) - .href, - locationType: 'UriLocation', - } - : { - uri: new URL( - `${track.get('bigDataUrl')}.bai`, - trackDbFileLocation.uri, - ).href, - locationType: 'UriLocation', - } - } else { - bigDataIndexLocation = track.get('bigDataIndex') - ? { - localPath: track.get('bigDataIndex'), - locationType: 'LocalPathLocation', - } - : { - localPath: `${track.get('bigDataUrl')}.bai`, - locationType: 'LocalPathLocation', - } - } - return { - type: 'AlignmentsTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BamAdapter', - bamLocation: bigDataLocation, - index: { - location: bigDataIndexLocation, - }, - }, - } - case 'bed': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'bed5FloatScore': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'bedGraph': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'bedRnaElements': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'bigBarChart': - return { - type: 'FeatureTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BigBedAdapter', - bigBedLocation: bigDataLocation, - }, - renderer: { - type: 'SvgFeatureRenderer', - }, - } - case 'bigBed': - return { - type: 'FeatureTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BigBedAdapter', - bigBedLocation: bigDataLocation, - }, - } - case 'bigGenePred': - return { - type: 'FeatureTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BigBedAdapter', - bigBedLocation: bigDataLocation, - }, - } - case 'bigChain': - return { - type: 'FeatureTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BigBedAdapter', - bigBedLocation: bigDataLocation, - }, - renderer: { - type: 'SvgFeatureRenderer', - }, - } - case 'bigInteract': - return { - type: 'FeatureTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BigBedAdapter', - bigBedLocation: bigDataLocation, - }, - renderer: { - type: 'SvgFeatureRenderer', - }, - } - case 'bigMaf': - return { - type: 'FeatureTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BigBedAdapter', - bigBedLocation: bigDataLocation, - }, - renderer: { - type: 'SvgFeatureRenderer', - }, - } - case 'bigPsl': - return { - type: 'FeatureTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BigBedAdapter', - bigBedLocation: bigDataLocation, - }, - renderer: { - type: 'SvgFeatureRenderer', - }, - } - case 'bigWig': - return { - type: 'QuantitativeTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BigWigAdapter', - bigWigLocation: bigDataLocation, - }, - } - case 'broadPeak': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'coloredExon': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'cram': - if (trackDbFileLocation.uri) { - bigDataIndexLocation = track.get('bigDataIndex') - ? { - uri: new URL(track.get('bigDataIndex'), trackDbFileLocation.uri) - .href, - locationType: 'UriLocation', - } - : { - uri: new URL( - `${track.get('bigDataUrl')}.crai`, - trackDbFileLocation.uri, - ).href, - locationType: 'UriLocation', - } - } else { - bigDataIndexLocation = track.get('bigDataIndex') - ? { - localPath: track.get('bigDataIndex'), - locationType: 'LocalPathLocation', - } - : { - localPath: `${track.get('bigDataUrl')}.crai`, - locationType: 'LocalPathLocation', - } - } - return { - type: 'AlignmentsTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'CramAdapter', - cramLocation: bigDataLocation, - craiLocation: bigDataIndexLocation, - sequenceAdapter, - }, - } - case 'gvf': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'ld2': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'narrowPeak': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'bigNarrowPeak': - return { - type: 'FeatureTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'BigBedAdapter', - bigBedLocation: bigDataLocation, - }, - } - case 'peptideMapping': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'vcfTabix': - if (trackDbFileLocation.uri) { - bigDataIndexLocation = track.get('bigDataIndex') - ? { - uri: new URL(track.get('bigDataIndex'), trackDbFileLocation.uri) - .href, - locationType: 'UriLocation', - } - : { - uri: new URL( - `${track.get('bigDataUrl')}.tbi`, - trackDbFileLocation.uri, - ).href, - locationType: 'UriLocation', - } - } else { - bigDataIndexLocation = track.get('bigDataIndex') - ? { - localPath: track.get('bigDataIndex'), - locationType: 'LocalPathLocation', - } - : { - localPath: `${track.get('bigDataUrl')}.tbi`, - locationType: 'LocalPathLocation', - } - } - return { - type: 'VariantTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'VcfTabixAdapter', - vcfGzLocation: bigDataLocation, - index: { - location: bigDataIndexLocation, - }, - }, - } - case 'wig': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'wigMaf': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - case 'hic': - return { - type: 'HicTrack', - name: track.get('shortLabel'), - description: track.get('longLabel'), - category: categories, - adapter: { - type: 'HicAdapter', - hicLocation: bigDataLocation, - }, - } - case 'halSnake': - return generateUnsupportedTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - - default: - return generateUnknownTrackConf( - track.get('shortLabel'), - baseTrackType, - categories, - ) - } -} diff --git a/plugins/data-management/src/ucsc-trackhub/ucscTrackHub.ts b/plugins/data-management/src/ucsc-trackhub/ucscTrackHub.ts new file mode 100644 index 0000000000..882ed63c5d --- /dev/null +++ b/plugins/data-management/src/ucsc-trackhub/ucscTrackHub.ts @@ -0,0 +1,326 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { RaStanza, TrackDbFile } from '@gmod/ucsc-hub' +import { FileLocation, isUriLocation, objectHash } from '@jbrowse/core/util' +import { openLocation } from '@jbrowse/core/util/io' +import { + generateUnsupportedTrackConf, + generateUnknownTrackConf, +} from '@jbrowse/core/util/tracks' +import ucscAssemblies from './ucscAssemblies' + +export { ucscAssemblies } + +export async function fetchHubFile(hubLocation: FileLocation) { + try { + const hubFileText = await openLocation(hubLocation).readFile('utf8') + const { HubFile } = await import('@gmod/ucsc-hub') + return new HubFile(hubFileText) + } catch (error) { + throw new Error(`Not a valid hub.txt file, got error: '${error}'`) + } +} + +export async function fetchGenomesFile(genomesLoc: FileLocation) { + const genomesFileText = await openLocation(genomesLoc).readFile('utf8') + const { GenomesFile } = await import('@gmod/ucsc-hub') + return new GenomesFile(genomesFileText) +} + +export async function fetchTrackDbFile(trackDbLoc: FileLocation) { + const text = await openLocation(trackDbLoc).readFile('utf8') + const { TrackDbFile } = await import('@gmod/ucsc-hub') + return new TrackDbFile(text) +} + +export function makeLoc(first: string, base: { uri: string }) { + return { + uri: new URL(first, base.uri).href, + locationType: 'UriLocation', + } +} + +export function makeLocAlt(first: string, alt: string, base: { uri: string }) { + return first ? makeLoc(first, base) : makeLoc(alt, base) +} + +export function makeLoc2(first: string, alt?: string) { + return first + ? { + uri: first, + locationType: 'LocalPath', + } + : { + uri: alt, + locationType: 'UriLocation', + } +} + +export function generateTracks( + trackDb: TrackDbFile, + trackDbLoc: FileLocation, + assemblyName: string, + sequenceAdapter: any, +) { + const tracks: any = [] + + trackDb.forEach((track, trackName) => { + const trackKeys = Array.from(track.keys()) as string[] + const parentTrackKeys = [ + 'superTrack', + 'compositeTrack', + 'container', + 'view', + ] + if (trackKeys.some(key => parentTrackKeys.includes(key))) { + return + } + const parentTracks = [] + let currentTrackName = trackName + do { + currentTrackName = trackDb.get(currentTrackName)?.get('parent') || '' + if (currentTrackName) { + ;[currentTrackName] = currentTrackName.split(' ') + parentTracks.push(trackDb.get(currentTrackName)) + } + } while (currentTrackName) + parentTracks.reverse() + const categories = parentTracks + .map(p => p?.get('shortLabel')) + .filter((f): f is string => !!f) + const res = makeTrackConfig( + track, + categories, + trackDbLoc, + trackDb, + sequenceAdapter, + ) + tracks.push({ + ...res, + trackId: `ucsc-trackhub-${objectHash(res)}`, + assemblyNames: [assemblyName], + }) + }) + + return tracks +} + +function makeTrackConfig( + track: RaStanza, + categories: string[], + trackDbLoc: FileLocation, + trackDb: TrackDbFile, + sequenceAdapter: any, +) { + let trackType = track.get('type') + const name = track.get('shortLabel') || '' + const bigDataUrl = track.get('bigDataUrl') || '' + const bigDataIdx = track.get('bigDataIndex') || '' + const isUri = isUriLocation(trackDbLoc) + if (!trackType) { + trackType = trackDb.get(track.get('parent') || '')?.get('type') + } + let baseTrackType = trackType?.split(' ')[0] || '' + if (baseTrackType === 'bam' && bigDataUrl.toLowerCase().endsWith('cram')) { + baseTrackType = 'cram' + } + const bigDataLocation = isUri + ? makeLoc(bigDataUrl, trackDbLoc) + : makeLoc2(bigDataUrl) + + switch (baseTrackType) { + case 'bam': + return { + type: 'AlignmentsTrack', + name: track.get('shortLabel'), + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BamAdapter', + bamLocation: bigDataLocation, + index: { + location: isUri + ? makeLocAlt(bigDataIdx, bigDataUrl + '.bai', trackDbLoc) + : makeLoc2(bigDataIdx, bigDataUrl + '.bai'), + }, + }, + } + + case 'bigBarChart': + return { + type: 'FeatureTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BigBedAdapter', + bigBedLocation: bigDataLocation, + }, + renderer: { + type: 'SvgFeatureRenderer', + }, + } + case 'bigBed': + return { + type: 'FeatureTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BigBedAdapter', + bigBedLocation: bigDataLocation, + }, + } + case 'bigGenePred': + return { + type: 'FeatureTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BigBedAdapter', + bigBedLocation: bigDataLocation, + }, + } + case 'bigChain': + return { + type: 'FeatureTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BigBedAdapter', + bigBedLocation: bigDataLocation, + }, + renderer: { + type: 'SvgFeatureRenderer', + }, + } + case 'bigInteract': + return { + type: 'FeatureTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BigBedAdapter', + bigBedLocation: bigDataLocation, + }, + renderer: { + type: 'SvgFeatureRenderer', + }, + } + case 'bigMaf': + return { + type: 'FeatureTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BigBedAdapter', + bigBedLocation: bigDataLocation, + }, + renderer: { + type: 'SvgFeatureRenderer', + }, + } + case 'bigPsl': + return { + type: 'FeatureTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BigBedAdapter', + bigBedLocation: bigDataLocation, + }, + renderer: { + type: 'SvgFeatureRenderer', + }, + } + case 'bigWig': + return { + type: 'QuantitativeTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BigWigAdapter', + bigWigLocation: bigDataLocation, + }, + } + + case 'cram': + return { + type: 'AlignmentsTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'CramAdapter', + cramLocation: bigDataLocation, + craiLocation: isUri + ? makeLocAlt(bigDataIdx, bigDataUrl + '.crai', trackDbLoc) + : makeLoc2(bigDataIdx, bigDataUrl + '.crai'), + sequenceAdapter, + }, + } + + case 'bigNarrowPeak': + return { + type: 'FeatureTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'BigBedAdapter', + bigBedLocation: bigDataLocation, + }, + } + case 'peptideMapping': + return generateUnsupportedTrackConf(name, baseTrackType, categories) + case 'vcfTabix': + return { + type: 'VariantTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'VcfTabixAdapter', + vcfGzLocation: bigDataLocation, + index: { + location: isUri + ? makeLocAlt(bigDataIdx, bigDataUrl + '.tbi', trackDbLoc) + : makeLoc2(bigDataIdx, bigDataUrl + '.tbi'), + }, + }, + } + + case 'hic': + return { + type: 'HicTrack', + name, + description: track.get('longLabel'), + category: categories, + adapter: { + type: 'HicAdapter', + hicLocation: bigDataLocation, + }, + } + + // unsupported types + // case 'gvf': + // case 'ld2': + // case 'narrowPeak': + // case 'wig': + // case 'wigMaf': + // case 'halSnake': + // case 'bed': + // case 'bed5FloatScore': + // case 'bedGraph': + // case 'bedRnaElements': + // case 'broadPeak': + // case 'coloredExon': + default: + return generateUnknownTrackConf(name, baseTrackType, categories) + } +} diff --git a/products/jbrowse-cli/src/types/common.ts b/products/jbrowse-cli/src/types/common.ts index b8740f0911..a7f16aaeb9 100644 --- a/products/jbrowse-cli/src/types/common.ts +++ b/products/jbrowse-cli/src/types/common.ts @@ -31,7 +31,10 @@ export function isURL(FileName: string) { function makeLocation(location: string, protocol: string) { if (protocol === 'uri') { - return { uri: location, locationType: 'UriLocation' } as UriLocation + return { + uri: location, + locationType: 'UriLocation', + } as UriLocation } if (protocol === 'localPath') { return { diff --git a/products/jbrowse-web/src/tests/Connection.test.tsx b/products/jbrowse-web/src/tests/Connection.test.tsx index 518c70b65e..8bfb26e915 100644 --- a/products/jbrowse-web/src/tests/Connection.test.tsx +++ b/products/jbrowse-web/src/tests/Connection.test.tsx @@ -17,7 +17,10 @@ const readBuffer2 = generateReadBuffer( new LocalFile(require.resolve(`../../test_data/volvoxhub/hub1/${url}`)), ) -const root = 'https://s3.amazonaws.com/jbrowse.org/volvoxhub/' +const delay = 20000 +const opts = [{}, delay] +const root = 'https://jbrowse.org/volvoxhub/' + test('Open up a UCSC trackhub connection', async () => { // @ts-ignore fetch.mockResponse(async request => { @@ -35,8 +38,8 @@ test('Open up a UCSC trackhub connection', async () => { fireEvent.click(await findByText(/Open connection/)) fireEvent.click(await findByText('Next')) fireEvent.change(await findByTestId('urlInput'), { - target: { value: 'https://s3.amazonaws.com/jbrowse.org/volvoxhub/hub.txt' }, + target: { value: 'https://jbrowse.org/volvoxhub/hub.txt' }, }) fireEvent.click(await findByText('Connect')) - await findByText('CRAM - Volvox Sorted') + await findByText('CRAM - Volvox Sorted', ...opts) }, 20000) diff --git a/products/jbrowse-web/src/tests/Dotplot.test.tsx b/products/jbrowse-web/src/tests/Dotplot.test.tsx index 8df979567d..901ca43fe5 100644 --- a/products/jbrowse-web/src/tests/Dotplot.test.tsx +++ b/products/jbrowse-web/src/tests/Dotplot.test.tsx @@ -17,6 +17,7 @@ if (!window.TextDecoder) { } const delay = { timeout: 25000 } +const opts = [{}, delay] expect.extend({ toMatchImageSnapshot }) setup() @@ -27,8 +28,8 @@ beforeEach(() => { test('open a dotplot view', async () => { const { findByTestId } = createView(config) - expectCanvasMatch(await findByTestId('prerendered_canvas_done', {}, delay)) -}, 20000) + expectCanvasMatch(await findByTestId('prerendered_canvas_done', ...opts)) +}, 30000) test('open a dotplot view with import form', async () => { const { findByTestId, findAllByTestId, findByText } = createView(config) @@ -43,15 +44,15 @@ test('open a dotplot view with import form', async () => { value: 'peach', }, }) - fireEvent.click(await findByText('New track', {}, delay)) - fireEvent.change(await findByTestId('urlInput', {}, delay), { + fireEvent.click(await findByText('New track', ...opts)) + fireEvent.change(await findByTestId('urlInput', ...opts), { target: { value: 'peach_grape_small.paf.gz', }, }) fireEvent.click(await findByText('Launch')) - expectCanvasMatch(await findByTestId('prerendered_canvas_done', {}, delay)) + expectCanvasMatch(await findByTestId('prerendered_canvas_done', ...opts)) }, 30000) test('inverted dotplot', async () => { @@ -59,7 +60,7 @@ test('inverted dotplot', async () => { ...config, defaultSession: dotplotSession.session, }) - expectCanvasMatch(await findByTestId('prerendered_canvas_done', {}, delay), 0) + expectCanvasMatch(await findByTestId('prerendered_canvas_done', ...opts), 0) }, 30000) test('inverted dotplot flip axes', async () => { @@ -67,7 +68,7 @@ test('inverted dotplot flip axes', async () => { ...config, defaultSession: dotplotSessionFlipAxes.session, }) - expectCanvasMatch(await findByTestId('prerendered_canvas_done', {}, delay), 0) + expectCanvasMatch(await findByTestId('prerendered_canvas_done', ...opts), 0) }, 30000) // session with dotplots and linear synteny views with both orientations tested diff --git a/products/jbrowse-web/src/tests/2.SVInspector.test.tsx b/products/jbrowse-web/src/tests/SVInspector.test.tsx similarity index 100% rename from products/jbrowse-web/src/tests/2.SVInspector.test.tsx rename to products/jbrowse-web/src/tests/SVInspector.test.tsx diff --git a/products/jbrowse-web/src/tests/1.Spreadsheet.test.tsx b/products/jbrowse-web/src/tests/Spreadsheet.test.tsx similarity index 100% rename from products/jbrowse-web/src/tests/1.Spreadsheet.test.tsx rename to products/jbrowse-web/src/tests/Spreadsheet.test.tsx diff --git a/yarn.lock b/yarn.lock index aeaafc8c8d..e5ee65bd67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1656,9 +1656,9 @@ long "^4.0.0" "@gmod/ucsc-hub@^0.1.6": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@gmod/ucsc-hub/-/ucsc-hub-0.1.6.tgz#62c3ef806c542f255381d7fe59faa16ab2f95bf5" - integrity sha512-7WggfnPpNsELg0GGszEpXJhlGK3jnKcJsyGY6oPIfKwf7M+1a9gLbeqperuLlINPXuM4oQ0H4hlad9YUNYEGeg== + version "0.1.7" + resolved "https://registry.yarnpkg.com/@gmod/ucsc-hub/-/ucsc-hub-0.1.7.tgz#ea8d0c1978fc8bbd1ab63061e8105beb3d1509f1" + integrity sha512-FHCgRLOYn5wjCGVDkGnW0wda7UAIFqVrKE5+0lh1EpUOu/UM1kfaidsJnha4Jc9V0+KCevSl4haVv0ZaMWqw2w== "@gmod/vcf@^5.0.9": version "5.0.10" @@ -5121,9 +5121,9 @@ form-data "^3.0.0" "@types/node@*": - version "18.11.16" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.16.tgz#966cae211e970199559cfbd295888fca189e49af" - integrity sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA== + version "18.11.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.17.tgz#5c009e1d9c38f4a2a9d45c0b0c493fe6cdb4bcb5" + integrity sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng== "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^16.11.26": version "16.18.10" @@ -6030,9 +6030,9 @@ add-stream@^1.0.0: integrity sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ== address@^1.0.1, address@^1.1.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/address/-/address-1.2.1.tgz#25bb61095b7522d65b357baa11bc05492d4c8acd" - integrity sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA== + version "1.2.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" + integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== adjust-sourcemap-loader@^4.0.0: version "4.0.0" @@ -14009,14 +14009,14 @@ junk@^3.1.0: integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== just-diff-apply@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.4.1.tgz#1debed059ad009863b4db0e8d8f333d743cdd83b" - integrity sha512-AAV5Jw7tsniWwih8Ly3fXxEZ06y+6p5TwQMsw0dzZ/wPKilzyDgdAnL0Ug4NNIquPUOh1vfFWEHbmXUqM5+o8g== + version "5.5.0" + resolved "https://registry.yarnpkg.com/just-diff-apply/-/just-diff-apply-5.5.0.tgz#771c2ca9fa69f3d2b54e7c3f5c1dfcbcc47f9f0f" + integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== just-diff@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.1.1.tgz#8da6414342a5ed6d02ccd64f5586cbbed3146202" - integrity sha512-u8HXJ3HlNrTzY7zrYYKjNEfBlyjqhdBkoyTVdjtn7p02RJD5NvR8rIClzeGA7t+UYP1/7eAkWNLU0+P3QrEqKQ== + version "5.2.0" + resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.2.0.tgz#60dca55891cf24cd4a094e33504660692348a241" + integrity sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw== jwt-decode@^3.1.2: version "3.1.2" From 68bec32945827c208612bb82c1a6944140fc3395 Mon Sep 17 00:00:00 2001 From: Colin Date: Sat, 17 Dec 2022 21:50:12 -0700 Subject: [PATCH 160/183] Improve multi-variant descriptions --- .../src/VcfTabixAdapter/VcfFeature.ts | 54 ++++++++----------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/plugins/variants/src/VcfTabixAdapter/VcfFeature.ts b/plugins/variants/src/VcfTabixAdapter/VcfFeature.ts index 41cfbeae70..76f0e64bef 100644 --- a/plugins/variants/src/VcfTabixAdapter/VcfFeature.ts +++ b/plugins/variants/src/VcfTabixAdapter/VcfFeature.ts @@ -1,11 +1,8 @@ import { Feature } from '@jbrowse/core/util/simpleFeature' -import { parseBreakend } from '@gmod/vcf' +import VCF, { parseBreakend } from '@gmod/vcf' /* eslint-disable no-underscore-dangle */ -/** - * VCF Feature creation with lazy genotype evaluation. - */ interface Samples { [key: string]: { [key: string]: { values: string[] | number[] | null } @@ -28,15 +25,14 @@ export default class VCFFeature implements Feature { // eslint-disable-next-line @typescript-eslint/no-explicit-any private variant: any - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private parser: any + private parser: VCF private data: FeatureData private _id: string // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(args: { variant: any; parser: any; id: string }) { + constructor(args: { variant: any; parser: VCF; id: string }) { this.variant = args.variant this.parser = args.parser this.data = this.dataFromVariant(this.variant) @@ -50,26 +46,21 @@ export default class VCFFeature implements Feature { : this.data[field] || this.variant[field] } - set(): void {} + set() {} - parent(): undefined { + parent() { return undefined } - children(): undefined { + children() { return undefined } - tags(): string[] { - const t = [ - ...Object.keys(this.data), - ...Object.keys(this.variant), - 'samples', - ] - return t + tags() { + return [...Object.keys(this.data), ...Object.keys(this.variant), 'samples'] } - id(): string { + id() { return this._id } @@ -126,27 +117,26 @@ export default class VCFFeature implements Feature { // Combine descriptions like ["SNV G -> A", "SNV G -> T"] to ["SNV G -> A,T"] if (descriptions.size > 1) { + const descs = [...descriptions] const prefixes = new Set( - [...descriptions].map(desc => { + descs.map(desc => { const prefix = desc.split('->') return prefix[1] ? prefix[0] : desc }), ) - const new_descs = [...prefixes].map(prefix => { - const suffixes = [...descriptions] - .map(desc => { - const pref = desc.split('-> ') - return pref[1] && pref[0] === prefix ? pref[1] : '' - }) - .filter(f => !!f) - - return suffixes.length - ? prefix + '-> ' + suffixes.join(',') - : [...descriptions].join(',') - }) + descriptions = new Set( + [...prefixes].map(prefix => { + const suffixes = descs + .map(desc => { + const pref = desc.split('-> ') + return pref[1] && pref[0] === prefix ? pref[1] : '' + }) + .filter(f => !!f) - descriptions = new Set(new_descs) + return suffixes.length ? prefix + '-> ' + suffixes.join(',') : prefix + }), + ) } if (soTerms.size) { return [[...soTerms].join(','), [...descriptions].join(',')] From a4e9f67e9855a99ce3495e85aadfd98b38c38ca7 Mon Sep 17 00:00:00 2001 From: Colin Date: Sat, 17 Dec 2022 22:12:42 -0700 Subject: [PATCH 161/183] Refactor VcfFeature and add test for multi-variant descriptions --- plugins/variants/src/VcfAdapter/VcfAdapter.ts | 7 +- plugins/variants/src/VcfFeature/index.test.ts | 132 ++++++++++ plugins/variants/src/VcfFeature/index.ts | 104 ++++++++ plugins/variants/src/VcfFeature/util.ts | 140 ++++++++++ .../src/VcfTabixAdapter/VcfFeature.test.ts | 118 --------- .../src/VcfTabixAdapter/VcfFeature.ts | 240 ------------------ .../src/VcfTabixAdapter/VcfTabixAdapter.ts | 2 +- plugins/variants/src/index.ts | 2 +- 8 files changed, 381 insertions(+), 364 deletions(-) create mode 100644 plugins/variants/src/VcfFeature/index.test.ts create mode 100644 plugins/variants/src/VcfFeature/index.ts create mode 100644 plugins/variants/src/VcfFeature/util.ts delete mode 100644 plugins/variants/src/VcfTabixAdapter/VcfFeature.test.ts delete mode 100644 plugins/variants/src/VcfTabixAdapter/VcfFeature.ts diff --git a/plugins/variants/src/VcfAdapter/VcfAdapter.ts b/plugins/variants/src/VcfAdapter/VcfAdapter.ts index f73ab309b5..df8ef987a6 100644 --- a/plugins/variants/src/VcfAdapter/VcfAdapter.ts +++ b/plugins/variants/src/VcfAdapter/VcfAdapter.ts @@ -2,16 +2,15 @@ import { BaseFeatureDataAdapter, BaseOptions, } from '@jbrowse/core/data_adapters/BaseAdapter' -import { Region } from '@jbrowse/core/util/types' +import { Region, Feature } from '@jbrowse/core/util' import { openLocation } from '@jbrowse/core/util/io' import { ObservableCreate } from '@jbrowse/core/util/rxjs' -import { Feature } from '@jbrowse/core/util/simpleFeature' import IntervalTree from '@flatten-js/interval-tree' import { unzip } from '@gmod/bgzf-filehandle' import VCF from '@gmod/vcf' // local -import VcfFeature from '../VcfTabixAdapter/VcfFeature' +import VcfFeature from '../VcfFeature' const readVcf = (f: string) => { const header: string[] = [] @@ -106,7 +105,7 @@ export default class VcfAdapter extends BaseFeatureDataAdapter { try { const { start, end, refName } = region const { header, intervalTree } = await this.setup() - const parser = new VCF({ header: header }) + const parser = new VCF({ header }) intervalTree[refName]?.search([start, end]).forEach(f => observer.next( new VcfFeature({ diff --git a/plugins/variants/src/VcfFeature/index.test.ts b/plugins/variants/src/VcfFeature/index.test.ts new file mode 100644 index 0000000000..f5c8dfb3ab --- /dev/null +++ b/plugins/variants/src/VcfFeature/index.test.ts @@ -0,0 +1,132 @@ +import VcfParser from '@gmod/vcf' +import VcfFeature from './index' + +test('test usage of the VcfFeature', () => { + const parser = new VcfParser({ + header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, + }) + const line = `lcl|Scaffald_1\t80465\trs118266897\tR\tA\t29\tPASS\tNS=3;0,14;AF=0.5;DB;112;PG2.1` + + const variant = parser.parseLine(line) + + const f = new VcfFeature({ + parser, + variant, + id: 'myuniqueid', + }) + expect(f.id()).toEqual('myuniqueid') + expect(f.get('name')).toEqual('rs118266897') +}) + +test('try INS feature with END less than start', () => { + const parser = new VcfParser({ + header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, + }) + const line = `chr1\t100\trs123\tR\tA\t29\tPASS\tEND=1;SVTYPE=INS` + + const variant = parser.parseLine(line) + + const f = new VcfFeature({ + parser, + variant, + id: 'myuniqueid', + }) + expect(f.id()).toEqual('myuniqueid') + expect(f.get('start')).toEqual(99) + expect(f.get('end')).toEqual(100) +}) + +test('try DEL feature with END info field valid', () => { + const parser = new VcfParser({ + header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, + }) + const line = `chr1\t100\trs123\tR\t\t29\tPASS\tEND=1000;SVTYPE=DEL` + + const variant = parser.parseLine(line) + + const f = new VcfFeature({ + parser, + variant, + id: 'myuniqueid', + }) + expect(f.id()).toEqual('myuniqueid') + expect(f.get('start')).toEqual(99) + expect(f.get('end')).toEqual(1000) +}) + +test('multiple SVs', () => { + const parser = new VcfParser({ + header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, + }) + const line = `chr1\t100\trs123\tR\t,\t29\tPASS\tEND=1000;SVTYPE=DEL` + + const variant = parser.parseLine(line) + + const f = new VcfFeature({ + parser, + variant, + id: 'myuniqueid', + }) + expect(f.get('description')).toEqual(',') +}) +test('BND', () => { + const parser = new VcfParser({ + header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, + }) + const line = `chr1\t100\trs123\tR\tG[ctgA:34200[\t29\tPASS\tEND=1000;SVTYPE=BND` + + const variant = parser.parseLine(line) + + const f = new VcfFeature({ + parser, + variant, + id: 'myuniqueid', + }) + expect(f.get('description')).toEqual('G[ctgA:34200[') +}) +test('multiple BND', () => { + const parser = new VcfParser({ + header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, + }) + const line = `chr1\t100\trs123\tR\tG[ctgA:34200[,G[ctgA:44200[\t29\tPASS\tEND=1000;SVTYPE=BND` + + const variant = parser.parseLine(line) + + const f = new VcfFeature({ + parser, + variant, + id: 'myuniqueid', + }) + expect(f.get('description')).toEqual('G[ctgA:34200[,G[ctgA:44200[') +}) +test('multiple SNV', () => { + const parser = new VcfParser({ + header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, + }) + const line = `chr1\t100\trs123\tG\tA,C\t29\tPASS\tHELLO=world` + + const variant = parser.parseLine(line) + + const f = new VcfFeature({ + parser, + variant, + id: 'myuniqueid', + }) + expect(f.get('description')).toEqual('SNV G -> A,C') +}) + +test('multiple SNV2', () => { + const parser = new VcfParser({ + header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, + }) + const line = `chr1\t100\trs123\tG\tAT,<*>\t29\tPASS\tHELLO=world` + + const variant = parser.parseLine(line) + + const f = new VcfFeature({ + parser, + variant, + id: 'myuniqueid', + }) + expect(f.get('description')).toEqual('insertion G -> AT,<*>') +}) diff --git a/plugins/variants/src/VcfFeature/index.ts b/plugins/variants/src/VcfFeature/index.ts new file mode 100644 index 0000000000..dc0acf2633 --- /dev/null +++ b/plugins/variants/src/VcfFeature/index.ts @@ -0,0 +1,104 @@ +import { Feature } from '@jbrowse/core/util' +import VCF from '@gmod/vcf' + +// locals +import { getSOTermAndDescription } from './util' + +/* eslint-disable no-underscore-dangle */ + +interface Samples { + [key: string]: { + [key: string]: { values: string[] | number[] | null } + } +} + +interface FeatureData { + [key: string]: unknown + refName: string + start: number + end: number + description?: string + type?: string + name?: string + aliases?: string[] + samples?: Samples +} + +export default class VCFFeature implements Feature { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private variant: any + + private parser: VCF + + private data: FeatureData + + private _id: string + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(args: { variant: any; parser: VCF; id: string }) { + this.variant = args.variant + this.parser = args.parser + this.data = this.dataFromVariant(this.variant) + this._id = args.id + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get(field: string): any { + return field === 'samples' + ? this.variant.SAMPLES + : this.data[field] || this.variant[field] + } + + set() {} + + parent() { + return undefined + } + + children() { + return undefined + } + + tags() { + return [...Object.keys(this.data), ...Object.keys(this.variant), 'samples'] + } + + id() { + return this._id + } + + dataFromVariant(variant: { + REF: string + POS: number + ALT: string[] + CHROM: string + INFO: any // eslint-disable-line @typescript-eslint/no-explicit-any + ID: string[] + }): FeatureData { + const { REF, ALT, POS, CHROM, INFO, ID } = variant + const start = POS - 1 + const [type, description] = getSOTermAndDescription(REF, ALT, this.parser) + const isTRA = ALT?.some(f => f === '') + const isSymbolic = ALT?.some(f => f.indexOf('<') !== -1) + + return { + refName: CHROM, + start, + end: isSymbolic && INFO.END && !isTRA ? +INFO.END[0] : start + REF.length, + description, + type, + name: ID?.join(','), + aliases: ID && ID.length > 1 ? variant.ID.slice(1) : undefined, + } + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + toJSON(): any { + return { + uniqueId: this._id, + ...this.variant, + ...this.data, + samples: this.variant.SAMPLES, + } + } +} diff --git a/plugins/variants/src/VcfFeature/util.ts b/plugins/variants/src/VcfFeature/util.ts new file mode 100644 index 0000000000..6219d32484 --- /dev/null +++ b/plugins/variants/src/VcfFeature/util.ts @@ -0,0 +1,140 @@ +import VCF, { parseBreakend } from '@gmod/vcf' + +const altTypeToSO: { [key: string]: string | undefined } = { + DEL: 'deletion', + INS: 'insertion', + DUP: 'duplication', + INV: 'inversion', + INVDUP: 'inverted duplication', + CNV: 'copy_number_variation', + TRA: 'translocation', + 'DUP:TANDEM': 'tandem_duplication', + NON_REF: 'sequence_variant', + '*': 'sequence_variant', +} + +/** + * Get a sequence ontology (SO) term that describes the variant type + */ +export function getSOTermAndDescription( + ref: string, + alt: string[], + parser: VCF, +): string[] { + // it's just a remark if there are no alternate alleles + if (!alt || alt.length === 0) { + return ['remark', 'no alternative alleles'] + } + + const soTerms = new Set() + let descriptions = new Set() + alt.forEach(a => { + let [soTerm, description] = getSOAndDescFromAltDefs(ref, a, parser) + if (!soTerm) { + ;[soTerm, description] = getSOAndDescByExamination(ref, a) + } + if (soTerm && description) { + soTerms.add(soTerm) + descriptions.add(description) + } + }) + + // Combine descriptions like ["SNV G -> A", "SNV G -> T"] to ["SNV G -> A,T"] + if (descriptions.size > 1) { + const descs = [...descriptions] + const prefixes = new Set( + descs.map(desc => { + const prefix = desc.split('->') + return prefix[1] ? prefix[0] : desc + }), + ) + + descriptions = new Set( + [...prefixes].map(prefix => { + const suffixes = descs + .map(desc => { + const pref = desc.split('-> ') + return pref[1] && pref[0] === prefix ? pref[1] : '' + }) + .filter(f => !!f) + + return suffixes.length ? prefix + '-> ' + suffixes.join(',') : prefix + }), + ) + } + if (soTerms.size) { + return [[...soTerms].join(','), [...descriptions].join(',')] + } + return [] +} + +export function getSOAndDescFromAltDefs( + ref: string, + alt: string, + parser: VCF, +): string[] { + if (typeof alt === 'string' && !alt.startsWith('<')) { + return [] + } + + // look for a definition with an SO type for this + let soTerm = altTypeToSO[alt] + // if no SO term but ALT is in metadata, assume sequence_variant + if (!soTerm && parser.getMetadata('ALT', alt)) { + soTerm = 'sequence_variant' + } + if (soTerm) { + return [soTerm, alt] + } + + // try to look for a definition for a parent term if we can + const modAlt = alt.split(':') + if (modAlt.length > 1) { + return getSOAndDescFromAltDefs( + ref, + `<${modAlt.slice(0, modAlt.length - 1).join(':')}>`, + parser, + ) + } + + // no parent + return [] +} + +// note: term SNV is used instead of SNP because SO definition of SNP says +// abundance must be at least 1% in population, and can't be sure we meet +// that +export function getSOAndDescByExamination(ref: string, alt: string) { + const bnd = parseBreakend(alt) + if (bnd) { + return ['breakend', alt] + } else if (ref.length === 1 && alt.length === 1) { + return ['SNV', makeDescriptionString('SNV', ref, alt)] + } else if (alt === '') { + return ['insertion', alt] + } else if (alt === '') { + return ['deletion', alt] + } else if (alt === '') { + return ['deletion', alt] + } else if (alt === '') { + return ['translocation', alt] + } else if (alt.includes('<')) { + return ['sv', alt] + } else if (ref.length === alt.length) { + if (ref.split('').reverse().join('') === alt) { + return ['inversion', makeDescriptionString('inversion', ref, alt)] + } else { + return ['substitution', makeDescriptionString('substitution', ref, alt)] + } + } else if (ref.length <= alt.length) { + return ['insertion', makeDescriptionString('insertion', ref, alt)] + } else if (ref.length > alt.length) { + return ['deletion', makeDescriptionString('deletion', ref, alt)] + } + + return ['indel', makeDescriptionString('indel', ref, alt)] +} + +function makeDescriptionString(soTerm: string, ref: string, alt: string) { + return `${soTerm} ${ref} -> ${alt}` +} diff --git a/plugins/variants/src/VcfTabixAdapter/VcfFeature.test.ts b/plugins/variants/src/VcfTabixAdapter/VcfFeature.test.ts deleted file mode 100644 index eb6e53b85a..0000000000 --- a/plugins/variants/src/VcfTabixAdapter/VcfFeature.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import VcfParser from '@gmod/vcf' -import VcfFeature from './VcfFeature' - -test('test usage of the VcfFeature', () => { - const parser = new VcfParser({ - header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, - }) - const line = `lcl|Scaffald_1\t80465\trs118266897\tR\tA\t29\tPASS\tNS=3;0,14;AF=0.5;DB;112;PG2.1` - - const variant = parser.parseLine(line) - - const f = new VcfFeature({ - parser, - variant, - id: 'myuniqueid', - }) - expect(f.id()).toEqual('myuniqueid') - expect(f.get('name')).toEqual('rs118266897') -}) - -test('try INS feature with END less than start', () => { - const parser = new VcfParser({ - header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, - }) - const line = `chr1\t100\trs123\tR\tA\t29\tPASS\tEND=1;SVTYPE=INS` - - const variant = parser.parseLine(line) - - const f = new VcfFeature({ - parser, - variant, - id: 'myuniqueid', - }) - expect(f.id()).toEqual('myuniqueid') - expect(f.get('start')).toEqual(99) - expect(f.get('end')).toEqual(100) -}) - -test('try DEL feature with END info field valid', () => { - const parser = new VcfParser({ - header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, - }) - const line = `chr1\t100\trs123\tR\t\t29\tPASS\tEND=1000;SVTYPE=DEL` - - const variant = parser.parseLine(line) - - const f = new VcfFeature({ - parser, - variant, - id: 'myuniqueid', - }) - expect(f.id()).toEqual('myuniqueid') - expect(f.get('start')).toEqual(99) - expect(f.get('end')).toEqual(1000) -}) - -describe('test SV description', () => { - it('multiple SVs', () => { - const parser = new VcfParser({ - header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, - }) - const line = `chr1\t100\trs123\tR\t,\t29\tPASS\tEND=1000;SVTYPE=DEL` - - const variant = parser.parseLine(line) - - const f = new VcfFeature({ - parser, - variant, - id: 'myuniqueid', - }) - expect(f.get('description')).toEqual(',') - }) - it('BND', () => { - const parser = new VcfParser({ - header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, - }) - const line = `chr1\t100\trs123\tR\tG[ctgA:34200[\t29\tPASS\tEND=1000;SVTYPE=BND` - - const variant = parser.parseLine(line) - - const f = new VcfFeature({ - parser, - variant, - id: 'myuniqueid', - }) - expect(f.get('description')).toEqual('G[ctgA:34200[') - }) - it('multiple BND', () => { - const parser = new VcfParser({ - header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, - }) - const line = `chr1\t100\trs123\tR\tG[ctgA:34200[,G[ctgA:44200[\t29\tPASS\tEND=1000;SVTYPE=BND` - - const variant = parser.parseLine(line) - - const f = new VcfFeature({ - parser, - variant, - id: 'myuniqueid', - }) - expect(f.get('description')).toEqual('G[ctgA:34200[,G[ctgA:44200[') - }) - it('multiple SNV', () => { - const parser = new VcfParser({ - header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`, - }) - const line = `chr1\t100\trs123\tG\tA,C\t29\tPASS\tHELLO=world` - - const variant = parser.parseLine(line) - - const f = new VcfFeature({ - parser, - variant, - id: 'myuniqueid', - }) - expect(f.get('description')).toEqual('SNV G -> A,C') - }) -}) diff --git a/plugins/variants/src/VcfTabixAdapter/VcfFeature.ts b/plugins/variants/src/VcfTabixAdapter/VcfFeature.ts deleted file mode 100644 index 76f0e64bef..0000000000 --- a/plugins/variants/src/VcfTabixAdapter/VcfFeature.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { Feature } from '@jbrowse/core/util/simpleFeature' -import VCF, { parseBreakend } from '@gmod/vcf' - -/* eslint-disable no-underscore-dangle */ - -interface Samples { - [key: string]: { - [key: string]: { values: string[] | number[] | null } - } -} - -interface FeatureData { - [key: string]: unknown - refName: string - start: number - end: number - description?: string - type?: string - name?: string - aliases?: string[] - samples?: Samples -} - -export default class VCFFeature implements Feature { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private variant: any - - private parser: VCF - - private data: FeatureData - - private _id: string - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(args: { variant: any; parser: VCF; id: string }) { - this.variant = args.variant - this.parser = args.parser - this.data = this.dataFromVariant(this.variant) - this._id = args.id - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get(field: string): any { - return field === 'samples' - ? this.variant.SAMPLES - : this.data[field] || this.variant[field] - } - - set() {} - - parent() { - return undefined - } - - children() { - return undefined - } - - tags() { - return [...Object.keys(this.data), ...Object.keys(this.variant), 'samples'] - } - - id() { - return this._id - } - - dataFromVariant(variant: { - REF: string - POS: number - ALT: string[] - CHROM: string - INFO: any // eslint-disable-line @typescript-eslint/no-explicit-any - ID: string[] - }): FeatureData { - const { REF, ALT, POS, CHROM, INFO, ID } = variant - const start = POS - 1 - const [SO_term, description] = this._getSOTermAndDescription(REF, ALT) - const isTRA = ALT?.some(f => f === '') - const isSymbolic = ALT?.some(f => f.indexOf('<') !== -1) - - return { - refName: CHROM, - start, - end: isSymbolic && INFO.END && !isTRA ? +INFO.END[0] : start + REF.length, - description, - type: SO_term, - name: ID?.join(','), - aliases: ID && ID.length > 1 ? variant.ID.slice(1) : undefined, - } - } - - /** - * Get a sequence ontology (SO) term that describes the variant type - */ - _getSOTermAndDescription( - ref: string, - alt: string[], - ): [string, string] | [undefined, undefined] { - // it's just a remark if there are no alternate alleles - if (!alt || alt.length === 0) { - return ['remark', 'no alternative alleles'] - } - - const soTerms = new Set() - let descriptions = new Set() - alt.forEach(a => { - let [soTerm, description] = this._getSOAndDescFromAltDefs(ref, a) - - if (!soTerm) { - ;[soTerm, description] = this._getSOAndDescByExamination(ref, a) - } - if (soTerm && description) { - soTerms.add(soTerm) - descriptions.add(description) - } - }) - - // Combine descriptions like ["SNV G -> A", "SNV G -> T"] to ["SNV G -> A,T"] - if (descriptions.size > 1) { - const descs = [...descriptions] - const prefixes = new Set( - descs.map(desc => { - const prefix = desc.split('->') - return prefix[1] ? prefix[0] : desc - }), - ) - - descriptions = new Set( - [...prefixes].map(prefix => { - const suffixes = descs - .map(desc => { - const pref = desc.split('-> ') - return pref[1] && pref[0] === prefix ? pref[1] : '' - }) - .filter(f => !!f) - - return suffixes.length ? prefix + '-> ' + suffixes.join(',') : prefix - }), - ) - } - if (soTerms.size) { - return [[...soTerms].join(','), [...descriptions].join(',')] - } - return [undefined, undefined] - } - - static _altTypeToSO: { [key: string]: string | undefined } = { - DEL: 'deletion', - INS: 'insertion', - DUP: 'duplication', - INV: 'inversion', - INVDUP: 'inverted duplication', - CNV: 'copy_number_variation', - TRA: 'translocation', - 'DUP:TANDEM': 'tandem_duplication', - NON_REF: 'sequence_variant', - '*': 'sequence_variant', - } - - _getSOAndDescFromAltDefs( - ref: string, - alt: string, - ): [string, string] | [undefined, undefined] { - if (typeof alt === 'string' && !alt.startsWith('<')) { - return [undefined, undefined] - } - - // look for a definition with an SO type for this - let soTerm = VCFFeature._altTypeToSO[alt] - // if no SO term but ALT is in metadata, assume sequence_variant - if (!soTerm && this.parser.getMetadata('ALT', alt)) { - soTerm = 'sequence_variant' - } - if (soTerm) { - return [soTerm, alt] - } - - // try to look for a definition for a parent term if we can - const modAlt = alt.split(':') - if (modAlt.length > 1) { - return this._getSOAndDescFromAltDefs( - ref, - `<${modAlt.slice(0, modAlt.length - 1).join(':')}>`, - ) - } - - // no parent - return [undefined, undefined] - } - - // note: term SNV is used instead of SNP because SO definition of SNP says - // abundance must be at least 1% in population, and can't be sure we meet - // that - _getSOAndDescByExamination(ref: string, alt: string): [string, string] { - const bnd = parseBreakend(alt) - if (bnd) { - return ['breakend', alt] - } else if (ref.length === 1 && alt.length === 1) { - return ['SNV', this._makeDescriptionString('SNV', ref, alt)] - } else if (alt === '') { - return ['insertion', alt] - } else if (alt === '') { - return ['deletion', alt] - } else if (alt === '') { - return ['deletion', alt] - } else if (alt === '') { - return ['translocation', alt] - } else if (alt.includes('<')) { - return ['sv', alt] - } else if (ref.length === alt.length) { - if (ref.split('').reverse().join('') === alt) { - return ['inversion', this._makeDescriptionString('inversion', ref, alt)] - } - return [ - 'substitution', - this._makeDescriptionString('substitution', ref, alt), - ] - } else if (ref.length <= alt.length) { - return ['insertion', this._makeDescriptionString('insertion', ref, alt)] - } else if (ref.length > alt.length) { - return ['deletion', this._makeDescriptionString('deletion', ref, alt)] - } - - return ['indel', this._makeDescriptionString('indel', ref, alt)] - } - - _makeDescriptionString(soTerm: string, ref: string, alt: string): string { - return `${soTerm} ${ref} -> ${alt}` - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - toJSON(): any { - return { - uniqueId: this._id, - ...this.variant, - ...this.data, - samples: this.variant.SAMPLES, - } - } -} diff --git a/plugins/variants/src/VcfTabixAdapter/VcfTabixAdapter.ts b/plugins/variants/src/VcfTabixAdapter/VcfTabixAdapter.ts index 6d573c5197..60d373436b 100644 --- a/plugins/variants/src/VcfTabixAdapter/VcfTabixAdapter.ts +++ b/plugins/variants/src/VcfTabixAdapter/VcfTabixAdapter.ts @@ -17,7 +17,7 @@ import { Observer } from 'rxjs' import { GenericFilehandle } from 'generic-filehandle' // local -import VcfFeature from './VcfFeature' +import VcfFeature from '../VcfFeature' export default class extends BaseFeatureDataAdapter { private configured?: Promise<{ diff --git a/plugins/variants/src/index.ts b/plugins/variants/src/index.ts index 2875ff752c..69ce13a9f7 100644 --- a/plugins/variants/src/index.ts +++ b/plugins/variants/src/index.ts @@ -24,4 +24,4 @@ export default class VariantsPlugin extends Plugin { } } -export { default as VcfFeature } from './VcfTabixAdapter/VcfFeature' +export { default as VcfFeature } from './VcfFeature' From 3f29635c3e4205af8ee390fd2883e154310ea491 Mon Sep 17 00:00:00 2001 From: Colin Diesh Date: Mon, 19 Dec 2022 21:43:37 -0700 Subject: [PATCH 162/183] Fix rendering base-level alignments on synteny visualizations, especially in inverted regions (#3419) --- plugins/bed/src/BedAdapter/BedAdapter.test.ts | 5 - .../MCScanAnchorsAdapter.test.ts | 9 +- .../src/PAFAdapter/PAFAdapter.test.ts | 9 - .../src/PAFAdapter/PAFAdapter.ts | 224 +++-------------- .../src/PAFAdapter/SyntenyFeature.ts | 15 ++ .../src/PAFAdapter/util.test.ts | 7 + .../src/PAFAdapter/util.ts | 180 ++++++++++++++ .../src/DotplotRenderer/DotplotRenderer.ts | 16 +- .../components/ImportSyntenyTrackSelector.tsx | 11 +- .../gff3/src/Gff3Adapter/Gff3Adapter.test.ts | 3 - plugins/gtf/src/GtfAdapter/GtfAdapter.test.ts | 3 - .../LGVSyntenyDisplay/stateModelFactory.ts | 12 +- .../components/LinearSyntenyRendering.tsx | 216 ++--------------- .../LinearSyntenyRenderer/components/util.ts | 164 +++++++++++++ .../components/ImportSyntenyTrackSelector.tsx | 3 +- .../src/LinearSyntenyView/model.ts | 37 ++- .../importAdapters/ImportUtils.test.ts | 5 +- .../importAdapters/STARFusionImport.test.ts | 4 +- .../importAdapters/VcfImport.test.ts | 4 - .../src/VcfAdapter/VcfAdapter.test.ts | 2 - .../__snapshots__/jbrowseModel.test.ts.snap | 81 +++++++ .../jbrowse-web/src/tests/Connection.test.tsx | 2 +- .../jbrowse-web/src/tests/Dotplot.test.tsx | 10 - .../jbrowse-web/src/tests/ExportSvg.test.tsx | 3 - .../jbrowse-web/src/tests/JBrowse.test.tsx | 2 - .../jbrowse-web/src/tests/Loader.test.tsx | 71 ++---- .../src/tests/SVInspector.test.tsx | 5 - .../src/tests/Spreadsheet.test.tsx | 4 - .../jbrowse-web/src/tests/Synteny.test.tsx | 225 ++++++++++++++++++ ...-tsx-inverted-dotplot-flip-axes-1-snap.png | Bin 17630 -> 17491 bytes ...ally-flipped-inverted-alignment-1-snap.png | Bin 0 -> 1680 bytes ...ation-both-horizontally-flipped-1-snap.png | Bin 0 -> 8433 bytes ...-orientation-inverted-alignment-1-snap.png | Bin 0 -> 8586 bytes ...ation-inverted-alignment-bottom-1-snap.png | Bin 0 -> 1660 bytes ...ation-both-horizontally-flipped-1-snap.png | Bin 0 -> 9262 bytes ...h-horizontally-flipped-both-rev-1-snap.png | Bin 0 -> 9336 bytes ...both-horizontally-flipped-rev-1-1-snap.png | Bin 0 -> 1938 bytes ...both-horizontally-flipped-rev-2-1-snap.png | Bin 0 -> 1931 bytes products/jbrowse-web/src/tests/loaderUtil.tsx | 24 ++ test_data/grape_peach_synteny/config.json | 57 +++++ .../peach.chrom.sizes | 0 test_data/grape_peach_synteny/subset.paf | 1 + test_data/volvox/config.json | 34 +++ .../volvox/output_prefix3.simseq.genome.fa | 2 + .../output_prefix3.simseq.genome.fa.fai | 1 + test_data/volvox/volvox-simple-inv.paf | 3 + test_data/volvox/volvox_del.paf | 6 - test_data/volvox/volvox_ins.paf | 6 - test_data/volvox/volvox_inv_indels.paf | 7 + 49 files changed, 918 insertions(+), 555 deletions(-) create mode 100644 plugins/comparative-adapters/src/PAFAdapter/SyntenyFeature.ts create mode 100644 plugins/comparative-adapters/src/PAFAdapter/util.test.ts create mode 100644 plugins/comparative-adapters/src/PAFAdapter/util.ts create mode 100644 plugins/linear-comparative-view/src/LinearSyntenyRenderer/components/util.ts create mode 100644 products/jbrowse-web/src/tests/Synteny.test.tsx create mode 100644 products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-horizontally-flipped-inverted-alignment-1-snap.png create mode 100644 products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-regular-orientation-both-horizontally-flipped-1-snap.png create mode 100644 products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-regular-orientation-inverted-alignment-1-snap.png create mode 100644 products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-regular-orientation-inverted-alignment-bottom-1-snap.png create mode 100644 products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-1-snap.png create mode 100644 products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-both-rev-1-snap.png create mode 100644 products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-rev-1-1-snap.png create mode 100644 products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-rev-2-1-snap.png create mode 100644 products/jbrowse-web/src/tests/loaderUtil.tsx create mode 100644 test_data/grape_peach_synteny/config.json rename test_data/{ => grape_peach_synteny}/peach.chrom.sizes (100%) create mode 100644 test_data/grape_peach_synteny/subset.paf create mode 100644 test_data/volvox/output_prefix3.simseq.genome.fa create mode 100644 test_data/volvox/output_prefix3.simseq.genome.fa.fai create mode 100644 test_data/volvox/volvox-simple-inv.paf create mode 100644 test_data/volvox/volvox_inv_indels.paf diff --git a/plugins/bed/src/BedAdapter/BedAdapter.test.ts b/plugins/bed/src/BedAdapter/BedAdapter.test.ts index d772b882e3..ba3289df5e 100644 --- a/plugins/bed/src/BedAdapter/BedAdapter.test.ts +++ b/plugins/bed/src/BedAdapter/BedAdapter.test.ts @@ -2,11 +2,6 @@ import { toArray } from 'rxjs/operators' import BedAdapter from './BedAdapter' import MyConfigSchema from './configSchema' -import { TextDecoder } from 'web-encoding' -if (!window.TextDecoder) { - window.TextDecoder = TextDecoder -} - test('adapter can fetch features from volvox-bed12.bed', async () => { const adapter = new BedAdapter( MyConfigSchema.create({ diff --git a/plugins/comparative-adapters/src/MCScanAnchorsAdapter/MCScanAnchorsAdapter.test.ts b/plugins/comparative-adapters/src/MCScanAnchorsAdapter/MCScanAnchorsAdapter.test.ts index 2637eef08a..cb1325ec8f 100644 --- a/plugins/comparative-adapters/src/MCScanAnchorsAdapter/MCScanAnchorsAdapter.test.ts +++ b/plugins/comparative-adapters/src/MCScanAnchorsAdapter/MCScanAnchorsAdapter.test.ts @@ -1,14 +1,7 @@ import { toArray } from 'rxjs/operators' import Adapter from './MCScanAnchorsAdapter' -import { TextEncoder, TextDecoder } from 'web-encoding' -import configSchema from './configSchema' -if (!window.TextEncoder) { - window.TextEncoder = TextEncoder -} -if (!window.TextDecoder) { - window.TextDecoder = TextDecoder -} +import configSchema from './configSchema' test('adapter can fetch features from mcscan anchors file', async () => { const adapter = new Adapter( diff --git a/plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.test.ts b/plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.test.ts index ca7e9ba7bc..7c2b69daef 100644 --- a/plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.test.ts +++ b/plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.test.ts @@ -2,15 +2,6 @@ import { toArray } from 'rxjs/operators' import Adapter from './PAFAdapter' import MyConfigSchema from './configSchema' -import { TextEncoder, TextDecoder } from 'web-encoding' - -if (!window.TextEncoder) { - window.TextEncoder = TextEncoder -} -if (!window.TextDecoder) { - window.TextDecoder = TextDecoder -} - test('adapter can fetch features from peach_grape.paf', async () => { const adapter = new Adapter( MyConfigSchema.create({ diff --git a/plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.ts b/plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.ts index 4081f4790b..9a74c60c5e 100644 --- a/plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.ts +++ b/plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.ts @@ -6,7 +6,7 @@ import { Region } from '@jbrowse/core/util/types' import { doesIntersect2 } from '@jbrowse/core/util/range' import { openLocation } from '@jbrowse/core/util/io' import { ObservableCreate } from '@jbrowse/core/util/rxjs' -import { SimpleFeature, Feature } from '@jbrowse/core/util' +import { Feature } from '@jbrowse/core/util' import { AnyConfigurationModel, readConfObject, @@ -15,134 +15,17 @@ import { unzip } from '@gmod/bgzf-filehandle' import { MismatchParser } from '@jbrowse/plugin-alignments' // locals -import { zip, isGzip } from '../util' -const { getOrientedMismatches } = MismatchParser - -export interface PAFRecord { - qname: string - qstart: number - qend: number - tname: string - tstart: number - tend: number - strand: number - extra: { - cg?: string - blockLen?: number - mappingQual: number - numMatches?: number - meanScore?: number - } -} - -// based on "weighted mean" method from dotPlotly -// https://github.com/tpoorten/dotPlotly -// License for dotPlotly reproduced here -// -// MIT License - -// Copyright (c) 2017 Tom Poorten - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -function getWeightedMeans(ret: PAFRecord[]) { - // in the weighted mean longer alignments factor in more - // heavily of all the fragments of a query vs the reference that it mapped - // to - // - // this uses a combined key query+'-'+ref to iteratively map all the - // alignments that match a particular ref from a particular query (so 1d - // array of what could be a 2d map) - // - // the result is a single number that says e.g. chr5 from human mapped to - // chr5 on mouse with 0.8 quality, and that0.8 is then attached to all the - // pieces of chr5 on human that mapped to chr5 on mouse. if chr5 on human - // also more weakly mapped to chr6 on mouse, then it would have another - // value e.g. 0.6. this can show strong and weak levels of synteny, - // especially in polyploidy situations - const scoreMap: { [key: string]: { quals: number[]; len: number[] } } = {} - for (let i = 0; i < ret.length; i++) { - const entry = ret[i] - const query = entry.qname - const target = entry.tname - const key = query + '-' + target - if (!scoreMap[key]) { - scoreMap[key] = { quals: [], len: [] } - } - scoreMap[key].quals.push(entry.extra.mappingQual) - scoreMap[key].len.push(entry.extra.blockLen || 1) - } - - const meanScoreMap = Object.fromEntries( - Object.entries(scoreMap).map(([key, val]) => { - const vals = zip(val.quals, val.len) - return [key, weightedMean(vals)] - }), - ) - for (let i = 0; i < ret.length; i++) { - const entry = ret[i] - const query = entry.qname - const target = entry.tname - const key = query + '-' + target - entry.extra.meanScore = meanScoreMap[key] - } - - let min = 10000 - let max = 0 - for (let i = 0; i < ret.length; i++) { - const entry = ret[i] - min = Math.min(entry.extra.meanScore || 0, min) - max = Math.max(entry.extra.meanScore || 0, max) - } - for (let i = 0; i < ret.length; i++) { - const entry = ret[i] - const b = entry.extra.meanScore || 0 - entry.extra.meanScore = (b - min) / (max - min) - } - - return ret -} - -// https://gist.github.com/stekhn/a12ed417e91f90ecec14bcfa4c2ae16a -function weightedMean(tuples: [number, number][]) { - const [valueSum, weightSum] = tuples.reduce( - ([valueSum, weightSum], [value, weight]) => [ - valueSum + value * weight, - weightSum + weight, - ], - [0, 0], - ) - return valueSum / weightSum -} +import SyntenyFeature from './SyntenyFeature' +import { isGzip } from '../util' +import { + getWeightedMeans, + flipCigar, + parsePAF, + swapIndelCigar, + PAFRecord, +} from './util' -class SyntenyFeature extends SimpleFeature { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get(arg: string): any { - if (arg === 'mismatches') { - const cg = this.get('cg') - const flip = this.get('flipInsDel') - - return cg ? getOrientedMismatches(flip, cg) : [] - } - return super.get(arg) - } -} +const { parseCigar } = MismatchParser interface PAFOptions extends BaseOptions { config?: AnyConfigurationModel @@ -173,52 +56,7 @@ export default class PAFAdapter extends BaseFeatureDataAdapter { throw new Error('Data exceeds maximum string length (512MB)') } const text = new TextDecoder('utf8', { fatal: true }).decode(buf) - - return text - .split(/\n|\r\n|\r/) - .filter(line => !!line) - .map(line => { - const [ - qname, - , - qstart, - qend, - strand, - tname, - , - tstart, - tend, - numMatches, - blockLen, - mappingQual, - ...fields - ] = line.split('\t') - - const rest = Object.fromEntries( - fields.map(field => { - const r = field.indexOf(':') - const fieldName = field.slice(0, r) - const fieldValue = field.slice(r + 3) - return [fieldName, fieldValue] - }), - ) - - return { - tname, - tstart: +tstart, - tend: +tend, - qname, - qstart: +qstart, - qend: +qend, - strand: strand === '-' ? -1 : 1, - extra: { - numMatches: +numMatches, - blockLen: +blockLen, - mappingQual: +mappingQual, - ...rest, - }, - } as PAFRecord - }) + return parsePAF(text) } async hasDataForRefName() { @@ -259,6 +97,9 @@ export default class PAFAdapter extends BaseFeatureDataAdapter { return ObservableCreate(async observer => { let pafRecords = await this.setup(opts) const { config } = opts + + // note: this is not the adapter config, it is responding to a display + // setting passed in via the opts parameter if (config && readConfObject(config, 'colorBy') === 'meanQueryIdentity') { pafRecords = getWeightedMeans(pafRecords) } @@ -281,6 +122,8 @@ export default class PAFAdapter extends BaseFeatureDataAdapter { let mateName = '' let mateStart = 0 let mateEnd = 0 + const flip = index === 0 + const assemblyName = assemblyNames[+!flip] if (index === 0) { start = r.qstart end = r.qend @@ -298,37 +141,38 @@ export default class PAFAdapter extends BaseFeatureDataAdapter { } const { extra, strand } = r if (refName === qref && doesIntersect2(qstart, qend, start, end)) { - const { numMatches = 0, blockLen = 1 } = extra - const flip = index === 0 + const { numMatches = 0, blockLen = 1, cg, ...rest } = extra + + let CIGAR = extra.cg + if (extra.cg) { + if (flip && strand === -1) { + CIGAR = flipCigar(parseCigar(extra.cg)).join('') + } else if (flip) { + CIGAR = swapIndelCigar(extra.cg) + } + } + observer.next( new SyntenyFeature({ - uniqueId: `${i}`, - assemblyName: assemblyNames[+!flip], + uniqueId: i + assemblyName, + assemblyName, start, end, type: 'match', refName, strand, - - // this is a special property of how to interpret CIGAR on PAF, - // intrinsic to the data format. the CIGAR is read backwards for - // features aligning to the negative strand of the target, which - // is actually different than how it works in e.g. BAM/SAM (which - // is visible during alignments track read vs ref) - revCigar: true, - - // depending on whether the query or target is queried, the - // "rev" flag - flipInsDel: flip, + ...rest, + CIGAR, syntenyId: i, identity: numMatches / blockLen, + numMatches, + blockLen, mate: { start: mateStart, end: mateEnd, refName: mateName, assemblyName: assemblyNames[+flip], }, - ...extra, }), ) } diff --git a/plugins/comparative-adapters/src/PAFAdapter/SyntenyFeature.ts b/plugins/comparative-adapters/src/PAFAdapter/SyntenyFeature.ts new file mode 100644 index 0000000000..21261e8fc3 --- /dev/null +++ b/plugins/comparative-adapters/src/PAFAdapter/SyntenyFeature.ts @@ -0,0 +1,15 @@ +import { SimpleFeature } from '@jbrowse/core/util' +import { MismatchParser } from '@jbrowse/plugin-alignments' + +// locals +const { getMismatches } = MismatchParser + +export default class SyntenyFeature extends SimpleFeature { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get(arg: string): any { + if (arg === 'mismatches') { + return getMismatches(this.get('CIGAR')) + } + return super.get(arg) + } +} diff --git a/plugins/comparative-adapters/src/PAFAdapter/util.test.ts b/plugins/comparative-adapters/src/PAFAdapter/util.test.ts new file mode 100644 index 0000000000..c729f54060 --- /dev/null +++ b/plugins/comparative-adapters/src/PAFAdapter/util.test.ts @@ -0,0 +1,7 @@ +import { flipCigar } from './util' + +test('flip cigar', () => { + expect(flipCigar(['3', 'M', '5', 'D', '5', 'M', '5', 'I', '6', 'M'])).toEqual( + ['6', 'M', '5', 'D', '5', 'M', '5', 'I', '3', 'M'], + ) +}) diff --git a/plugins/comparative-adapters/src/PAFAdapter/util.ts b/plugins/comparative-adapters/src/PAFAdapter/util.ts new file mode 100644 index 0000000000..804d98d5cd --- /dev/null +++ b/plugins/comparative-adapters/src/PAFAdapter/util.ts @@ -0,0 +1,180 @@ +import { zip } from '../util' + +export interface PAFRecord { + qname: string + qstart: number + qend: number + tname: string + tstart: number + tend: number + strand: number + extra: { + cg?: string + blockLen?: number + mappingQual: number + numMatches?: number + meanScore?: number + } +} +// based on "weighted mean" method from https://github.com/tpoorten/dotPlotly +// License reproduced here +// +// MIT License + +// Copyright (c) 2017 Tom Poorten + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Notes: in the weighted mean longer alignments factor in more heavily of all +// the fragments of a query vs the reference that it mapped to +// +// this uses a combined key query+'-'+ref to iteratively map all the alignments +// that match a particular ref from a particular query (so 1d array of what +// could be a 2d map) +// +// the result is a single number that says e.g. chr5 from human mapped to chr5 +// on mouse with 0.8 quality, and that0.8 is then attached to all the pieces of +// chr5 on human that mapped to chr5 on mouse. if chr5 on human also more +// weakly mapped to chr6 on mouse, then it would have another value e.g. 0.6. +// this can show strong and weak levels of synteny, especially in polyploidy +// situations + +export function getWeightedMeans(ret: PAFRecord[]) { + const scoreMap: { [key: string]: { quals: number[]; len: number[] } } = {} + for (let i = 0; i < ret.length; i++) { + const entry = ret[i] + const query = entry.qname + const target = entry.tname + const key = query + '-' + target + if (!scoreMap[key]) { + scoreMap[key] = { quals: [], len: [] } + } + scoreMap[key].quals.push(entry.extra.mappingQual) + scoreMap[key].len.push(entry.extra.blockLen || 1) + } + + const meanScoreMap = Object.fromEntries( + Object.entries(scoreMap).map(([key, val]) => { + const vals = zip(val.quals, val.len) + return [key, weightedMean(vals)] + }), + ) + for (let i = 0; i < ret.length; i++) { + const entry = ret[i] + const query = entry.qname + const target = entry.tname + const key = query + '-' + target + entry.extra.meanScore = meanScoreMap[key] + } + + let min = 10000 + let max = 0 + for (let i = 0; i < ret.length; i++) { + const entry = ret[i] + min = Math.min(entry.extra.meanScore || 0, min) + max = Math.max(entry.extra.meanScore || 0, max) + } + for (let i = 0; i < ret.length; i++) { + const entry = ret[i] + const b = entry.extra.meanScore || 0 + entry.extra.meanScore = (b - min) / (max - min) + } + + return ret +} + +// https://gist.github.com/stekhn/a12ed417e91f90ecec14bcfa4c2ae16a +function weightedMean(tuples: [number, number][]) { + const [valueSum, weightSum] = tuples.reduce( + ([valueSum, weightSum], [value, weight]) => [ + valueSum + value * weight, + weightSum + weight, + ], + [0, 0], + ) + return valueSum / weightSum +} + +export function parsePAF(text: string) { + return text + .split(/\n|\r\n|\r/) + .filter(line => !!line) + .map(line => { + const [ + qname, + , + qstart, + qend, + strand, + tname, + , + tstart, + tend, + numMatches, + blockLen, + mappingQual, + ...fields + ] = line.split('\t') + + const rest = Object.fromEntries( + fields.map(field => { + const r = field.indexOf(':') + const fieldName = field.slice(0, r) + const fieldValue = field.slice(r + 3) + return [fieldName, fieldValue] + }), + ) + + return { + tname, + tstart: +tstart, + tend: +tend, + qname, + qstart: +qstart, + qend: +qend, + strand: strand === '-' ? -1 : 1, + extra: { + numMatches: +numMatches, + blockLen: +blockLen, + mappingQual: +mappingQual, + ...rest, + }, + } as PAFRecord + }) +} + +export function flipCigar(cigar: string[]) { + const arr = [] + for (let i = cigar.length - 2; i >= 0; i -= 2) { + arr.push(cigar[i]) + const op = cigar[i + 1] + if (op === 'D') { + arr.push('I') + } else if (op === 'I') { + arr.push('D') + } else { + arr.push(op) + } + } + return arr +} + +export function swapIndelCigar(cigar: string) { + return cigar.replaceAll('D', 'K').replaceAll('I', 'D').replaceAll('K', 'I') +} diff --git a/plugins/dotplot-view/src/DotplotRenderer/DotplotRenderer.ts b/plugins/dotplot-view/src/DotplotRenderer/DotplotRenderer.ts index d22c0d54f8..299ee939b0 100644 --- a/plugins/dotplot-view/src/DotplotRenderer/DotplotRenderer.ts +++ b/plugins/dotplot-view/src/DotplotRenderer/DotplotRenderer.ts @@ -224,9 +224,7 @@ export default class DotplotRenderer extends ComparativeRenderer { } else { let currX = b1 let currY = e1 - const cigar = feature.get('cg') || feature.get('CIGAR') - const flipInsDel = feature.get('flipInsDel') - + const cigar = feature.get('CIGAR') if (drawCigar && cigar) { const cigarOps = parseCigar(cigar) @@ -240,17 +238,9 @@ export default class DotplotRenderer extends ComparativeRenderer { currX += (val / hBpPerPx) * strand currY += val / vBpPerPx } else if (op === 'D' || op === 'N') { - if (flipInsDel) { - currY += val / vBpPerPx - } else { - currX += (val / hBpPerPx) * strand - } + currX += (val / hBpPerPx) * strand } else if (op === 'I') { - if (flipInsDel) { - currX += (val / hBpPerPx) * strand - } else { - currY += val / vBpPerPx - } + currY += val / vBpPerPx } currX = clampWithWarnX(currX, b1, b2, feature) currY = clampWithWarnY(currY, e1, e2, feature) diff --git a/plugins/dotplot-view/src/DotplotView/components/ImportSyntenyTrackSelector.tsx b/plugins/dotplot-view/src/DotplotView/components/ImportSyntenyTrackSelector.tsx index 0153f456e2..813a38967a 100644 --- a/plugins/dotplot-view/src/DotplotView/components/ImportSyntenyTrackSelector.tsx +++ b/plugins/dotplot-view/src/DotplotView/components/ImportSyntenyTrackSelector.tsx @@ -1,17 +1,19 @@ import React, { useState, useEffect } from 'react' import { Select, MenuItem, Paper, Typography } from '@mui/material' +import { getTrackName } from '@jbrowse/core/util/tracks' import { getSession } from '@jbrowse/core/util' - -import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons' import { ErrorMessage } from '@jbrowse/core/ui' import { AnyConfigurationModel, readConfObject, } from '@jbrowse/core/configuration' - import { observer } from 'mobx-react' + +// icons +import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons' + +// locals import { DotplotViewModel } from '../model' -import { getTrackName } from '@jbrowse/core/util/tracks' function f(track: AnyConfigurationModel, assembly1: string, assembly2: string) { const assemblyNames = readConfObject(track, 'assemblyNames') @@ -51,6 +53,7 @@ const Selector = observer( // onChange's to the select box setShowTrackId(value) }, [value, setShowTrackId]) + return ( diff --git a/plugins/gff3/src/Gff3Adapter/Gff3Adapter.test.ts b/plugins/gff3/src/Gff3Adapter/Gff3Adapter.test.ts index 87f65d7be3..d3a22b9881 100644 --- a/plugins/gff3/src/Gff3Adapter/Gff3Adapter.test.ts +++ b/plugins/gff3/src/Gff3Adapter/Gff3Adapter.test.ts @@ -2,9 +2,6 @@ import { toArray } from 'rxjs/operators' import configSchema from './configSchema' import Gff3Adapter from './Gff3Adapter' -import { TextDecoder } from 'web-encoding' -window.TextDecoder = TextDecoder - describe('adapter can fetch features from volvox.gff3', () => { let adapter: Gff3Adapter beforeEach(() => { diff --git a/plugins/gtf/src/GtfAdapter/GtfAdapter.test.ts b/plugins/gtf/src/GtfAdapter/GtfAdapter.test.ts index 13029e0648..199db6fef2 100644 --- a/plugins/gtf/src/GtfAdapter/GtfAdapter.test.ts +++ b/plugins/gtf/src/GtfAdapter/GtfAdapter.test.ts @@ -2,9 +2,6 @@ import { toArray } from 'rxjs/operators' import configSchema from './configSchema' import GtfAdapter from './GtfAdapter' -import { TextDecoder } from 'web-encoding' -window.TextDecoder = TextDecoder - describe('adapter can fetch features from volvox.sorted.gtf', () => { let adapter: GtfAdapter beforeEach(() => { diff --git a/plugins/linear-comparative-view/src/LGVSyntenyDisplay/stateModelFactory.ts b/plugins/linear-comparative-view/src/LGVSyntenyDisplay/stateModelFactory.ts index 79a8828214..43c07ef705 100644 --- a/plugins/linear-comparative-view/src/LGVSyntenyDisplay/stateModelFactory.ts +++ b/plugins/linear-comparative-view/src/LGVSyntenyDisplay/stateModelFactory.ts @@ -116,9 +116,9 @@ async function navToSynteny(feature: Feature, self: any) { }) as LSV const f = (n: number) => Math.floor(n) const l1 = `${featRef}:${f(rFeatStart)}-${f(rFeatEnd)}` - const l2 = `${mateRef}:${f(rMateStart)}-${f(rMateEnd)}${ - strand === -1 ? '[rev]' : '' - }` + const m1 = Math.min(rMateStart, rMateEnd) + const m2 = Math.max(rMateStart, rMateEnd) + const l2 = `${mateRef}:${f(m1)}-${f(m2)}${strand === -1 ? '[rev]' : ''}` await when(() => view2.width !== undefined) await Promise.all([ view2.views[0].navToLocString(l1, featAsm), @@ -166,6 +166,12 @@ function stateModelFactory(schema: AnyConfigurationSchemaType) { }, } }) + .actions(self => ({ + afterCreate() { + // use color by strand to help indicate inversions better + self.setColorScheme({ type: 'strand' }) + }, + })) } export default stateModelFactory diff --git a/plugins/linear-comparative-view/src/LinearSyntenyRenderer/components/LinearSyntenyRendering.tsx b/plugins/linear-comparative-view/src/LinearSyntenyRenderer/components/LinearSyntenyRendering.tsx index 6790d08138..0aac9a8c97 100644 --- a/plugins/linear-comparative-view/src/LinearSyntenyRenderer/components/LinearSyntenyRendering.tsx +++ b/plugins/linear-comparative-view/src/LinearSyntenyRenderer/components/LinearSyntenyRendering.tsx @@ -12,23 +12,19 @@ import { getContainingView, getSession, isSessionModelWithWidgets, - ViewSnap, - AssemblyManager, } from '@jbrowse/core/util' -import { bpToPx } from '@jbrowse/core/util/Base1DUtils' // locals -import { interstitialYPos, generateMatches } from '../../util' +import { interstitialYPos } from '../../util' import { LinearSyntenyViewModel } from '../../LinearSyntenyView/model' import { LinearComparativeDisplay } from '../../LinearComparativeDisplay/stateModelFactory' import SyntenyTooltip from './SyntenyTooltip' +import { drawMatchSimple, layoutMatches, px } from './util' -const [LEFT, , RIGHT] = [0, 1, 2, 3] +const { parseCigar } = MismatchParser const MAX_COLOR_RANGE = 255 * 255 * 255 // max color range -type RectTuple = [number, number, number, number] - const colorMap = { I: '#ff03', N: '#0a03', @@ -42,164 +38,6 @@ function getId(r: number, g: number, b: number, unitMultiplier: number) { return Math.floor((r * 255 * 255 + g * 255 + b - 1) / unitMultiplier) } -function px(view: ViewSnap, arg: { refName: string; coord: number }) { - const r = bpToPx({ ...arg, self: view })?.offsetPx - if (r === undefined) { - console.warn('unknown coord', arg) - } - return r -} - -interface Match { - layout: RectTuple - feature: Feature - level: number - refName: string -} - -function drawMatchSimple({ - match, - ctx, - offsets, - cb, - viewSnaps, - showIntraviewLinks, - hideTiny, - height, - drawCurves, -}: { - match: Match[] - ctx: CanvasRenderingContext2D - offsets: number[] - cb: (ctx: CanvasRenderingContext2D) => void - viewSnaps: ViewSnap[] - showIntraviewLinks: boolean - hideTiny: boolean - height: number - drawCurves?: boolean -}) { - // we follow a path in the list of chunks, not from top to bottom, just - // in series following x1,y1 -> x2,y2 - for (let i = 0; i < match.length - 1; i += 1) { - const { layout: c1, feature: f1, level: l1, refName: ref1 } = match[i] - const { layout: c2, feature: f2, level: l2, refName: ref2 } = match[i + 1] - const v1 = viewSnaps[l1] - const v2 = viewSnaps[l2] - - if (!c1 || !c2) { - console.warn('received null layout for a overlay feature') - return - } - - // disable rendering connections in a single level - if (!showIntraviewLinks && l1 === l2) { - continue - } - const length1 = f1.get('end') - f1.get('start') - const length2 = f2.get('end') - f2.get('start') - - if ((length1 < v1.bpPerPx || length2 < v2.bpPerPx) && hideTiny) { - continue - } - - const px11 = px(v1, { refName: ref1, coord: c1[LEFT] }) - const px12 = px(v1, { refName: ref1, coord: c1[RIGHT] }) - const px21 = px(v2, { refName: ref2, coord: c2[LEFT] }) - const px22 = px(v2, { refName: ref2, coord: c2[RIGHT] }) - if ( - px11 === undefined || - px12 === undefined || - px21 === undefined || - px22 === undefined - ) { - continue - } - - const x11 = px11 - offsets[l1] - const x12 = px12 - offsets[l1] - const x21 = px21 - offsets[l2] - const x22 = px22 - offsets[l2] - - const y1 = interstitialYPos(l1 < l2, height) - const y2 = interstitialYPos(l2 < l1, height) - const mid = (y2 - y1) / 2 - - // drawing a line if the results are thin: drawing a line results in much - // less pixellation than filling in a thin polygon - if (length1 < v1.bpPerPx || length2 < v2.bpPerPx) { - ctx.beginPath() - ctx.moveTo(x11, y1) - if (drawCurves) { - ctx.bezierCurveTo(x11, mid, x21, mid, x21, y2) - } else { - ctx.lineTo(x21, y2) - } - ctx.stroke() - } else { - ctx.beginPath() - ctx.moveTo(x11, y1) - ctx.lineTo(x12, y1) - if (drawCurves) { - ctx.bezierCurveTo(x12, mid, x22, mid, x22, y2) - } else { - ctx.lineTo(x22, y2) - } - ctx.lineTo(x21, y2) - if (drawCurves) { - ctx.bezierCurveTo(x21, mid, x11, mid, x11, y1) - } else { - ctx.lineTo(x11, y1) - } - ctx.closePath() - cb(ctx) - } - } -} - -function layoutMatches( - features: Feature[][], - assemblyManager?: AssemblyManager, -) { - const matches = [] - for (let i = 0; i < features.length; i++) { - for (let j = i; j < features.length; j++) { - if (i !== j) { - for (const [f1, f2] of generateMatches(features[i], features[j], f => - f.get('syntenyId'), - )) { - let f1s = f1.get('start') - let f1e = f1.get('end') - const f2s = f2.get('start') - const f2e = f2.get('end') - if (f1.get('strand') === -1) { - ;[f1e, f1s] = [f1s, f1e] - } - const a1 = assemblyManager?.get(f1.get('assemblyName')) - const a2 = assemblyManager?.get(f2.get('assemblyName')) - const r1 = f1.get('refName') - const r2 = f2.get('refName') - - matches.push([ - { - feature: f1, - level: i, - refName: a1?.getCanonicalRefName(f1.get('refName')) || r1, - layout: [f1s, 0, f1e, 10] as RectTuple, - }, - { - feature: f2, - level: j, - refName: a2?.getCanonicalRefName(f1.get('refName')) || r2, - layout: [f2s, 0, f2e, 10] as RectTuple, - }, - ]) - } - } - } - } - return matches -} - function getResources(model: LinearComparativeDisplay) { const inWorker = !('type' in model) if (!inWorker && isAlive(model)) { @@ -272,13 +110,7 @@ function LinearSyntenyRendering({ ) const parsedCIGARs = useMemo( - () => - new Map( - es.flat().map(f => { - const cigar = f.get('cg') || f.get('CIGAR') - return [f.id(), cigar ? MismatchParser.parseCigar(cigar) : undefined] - }), - ), + () => new Map(es.flat().map(f => [f.id(), parseCigar(f.get('CIGAR'))])), [es], ) const drawCurves = parentView?.drawCurves @@ -371,10 +203,10 @@ function LinearSyntenyRendering({ continue } - const px11 = px(v1, { refName: ref1, coord: c1[LEFT] }) - const px12 = px(v1, { refName: ref1, coord: c1[RIGHT] }) - const px21 = px(v2, { refName: ref2, coord: c2[LEFT] }) - const px22 = px(v2, { refName: ref2, coord: c2[RIGHT] }) + const px11 = px(v1, { refName: ref1, coord: f1.get('start') }) + const px12 = px(v1, { refName: ref1, coord: f1.get('end') }) + const px21 = px(v2, { refName: ref2, coord: f2.get('start') }) + const px22 = px(v2, { refName: ref2, coord: f2.get('end') }) if ( px11 === undefined || px12 === undefined || @@ -406,19 +238,15 @@ function LinearSyntenyRendering({ } ctx1.stroke() } else { - let cx1 = x11 - let cx2 = x21 - - // we have to read the CIGAR backwards when looking at negative strand features - const f1flipped = f1.get('revCigar') && f1.get('strand') === -1 - const flipInsDel = f1.get('flipInsDel') - // flip the direction of the CIGAR drawing in horizontally flipped // modes const rev1 = x11 < x12 ? 1 : -1 - const rev2 = x21 < x22 ? 1 : -1 + const rev2 = (x21 < x22 ? 1 : -1) * f1.get('strand') + let cx1 = x11 + let cx2 = f1.get('strand') === -1 ? x22 : x21 const cigar = parsedCIGARs.get(f1.id()) + if (cigar) { // continuingFlag helps speed up zoomed out by skipping draw // commands on very small CIGAR features @@ -426,11 +254,7 @@ function LinearSyntenyRendering({ let px1 = 0 let px2 = 0 const unitMultiplier2 = Math.floor(MAX_COLOR_RANGE / cigar.length) - for ( - let j = f1flipped ? cigar.length - 2 : 0; - f1flipped ? j >= 0 : j < cigar.length; - j += f1flipped ? -2 : 2 - ) { + for (let j = 0; j < cigar.length; j += 2) { const idx = j * unitMultiplier2 + 1 const r = Math.floor(idx / (255 * 255)) % 255 const g = Math.floor(idx / 255) % 255 @@ -451,17 +275,9 @@ function LinearSyntenyRendering({ cx1 += d1 * rev1 cx2 += d2 * rev2 } else if (op === 'D' || op === 'N') { - if (flipInsDel) { - cx2 += d2 * rev2 - } else { - cx1 += d1 * rev1 - } + cx1 += d1 * rev1 } else if (op === 'I') { - if (flipInsDel) { - cx1 += d1 * rev1 - } else { - cx2 += d2 * rev2 - } + cx2 += d2 * rev2 } // check that we are even drawing in view here, e.g. that all @@ -475,7 +291,7 @@ function LinearSyntenyRendering({ // if it is a small feature and not the last element of the // CIGAR (which could skip rendering it entire if we did turn // it on), then turn on continuing flag - const isNotLast = f1flipped ? j > 2 : j < cigar.length - 2 + const isNotLast = j < cigar.length - 2 if ( Math.abs(cx1 - px1) < 1 && Math.abs(cx2 - px2) < 1 && diff --git a/plugins/linear-comparative-view/src/LinearSyntenyRenderer/components/util.ts b/plugins/linear-comparative-view/src/LinearSyntenyRenderer/components/util.ts new file mode 100644 index 0000000000..8df7b3d6ba --- /dev/null +++ b/plugins/linear-comparative-view/src/LinearSyntenyRenderer/components/util.ts @@ -0,0 +1,164 @@ +import { AssemblyManager, Feature, ViewSnap } from '@jbrowse/core/util' +import { bpToPx } from '@jbrowse/core/util/Base1DUtils' + +// locals +import { generateMatches, interstitialYPos } from '../../util' + +type RectTuple = [number, number, number, number] + +const [LEFT, , RIGHT] = [0, 1, 2, 3] + +interface Match { + layout: RectTuple + feature: Feature + level: number + refName: string +} + +export function drawMatchSimple({ + match, + ctx, + offsets, + cb, + viewSnaps, + showIntraviewLinks, + hideTiny, + height, + drawCurves, +}: { + match: Match[] + ctx: CanvasRenderingContext2D + offsets: number[] + cb: (ctx: CanvasRenderingContext2D) => void + viewSnaps: ViewSnap[] + showIntraviewLinks: boolean + hideTiny: boolean + height: number + drawCurves?: boolean +}) { + // we follow a path in the list of chunks, not from top to bottom, just + // in series following x1,y1 -> x2,y2 + for (let i = 0; i < match.length - 1; i += 1) { + const { layout: c1, feature: f1, level: l1, refName: ref1 } = match[i] + const { layout: c2, feature: f2, level: l2, refName: ref2 } = match[i + 1] + const v1 = viewSnaps[l1] + const v2 = viewSnaps[l2] + + if (!c1 || !c2) { + console.warn('received null layout for a overlay feature') + return + } + + // disable rendering connections in a single level + if (!showIntraviewLinks && l1 === l2) { + continue + } + const length1 = f1.get('end') - f1.get('start') + const length2 = f2.get('end') - f2.get('start') + + if ((length1 < v1.bpPerPx || length2 < v2.bpPerPx) && hideTiny) { + continue + } + + const px11 = px(v1, { refName: ref1, coord: c1[LEFT] }) + const px12 = px(v1, { refName: ref1, coord: c1[RIGHT] }) + const px21 = px(v2, { refName: ref2, coord: c2[LEFT] }) + const px22 = px(v2, { refName: ref2, coord: c2[RIGHT] }) + if ( + px11 === undefined || + px12 === undefined || + px21 === undefined || + px22 === undefined + ) { + continue + } + + const x11 = px11 - offsets[l1] + const x12 = px12 - offsets[l1] + const x21 = px21 - offsets[l2] + const x22 = px22 - offsets[l2] + + const y1 = interstitialYPos(l1 < l2, height) + const y2 = interstitialYPos(l2 < l1, height) + const mid = (y2 - y1) / 2 + + // drawing a line if the results are thin: drawing a line results in much + // less pixellation than filling in a thin polygon + if (length1 < v1.bpPerPx || length2 < v2.bpPerPx) { + ctx.beginPath() + ctx.moveTo(x11, y1) + if (drawCurves) { + ctx.bezierCurveTo(x11, mid, x21, mid, x21, y2) + } else { + ctx.lineTo(x21, y2) + } + ctx.stroke() + } else { + ctx.beginPath() + ctx.moveTo(x11, y1) + ctx.lineTo(x12, y1) + if (drawCurves) { + ctx.bezierCurveTo(x12, mid, x22, mid, x22, y2) + } else { + ctx.lineTo(x22, y2) + } + ctx.lineTo(x21, y2) + if (drawCurves) { + ctx.bezierCurveTo(x21, mid, x11, mid, x11, y1) + } else { + ctx.lineTo(x11, y1) + } + ctx.closePath() + cb(ctx) + } + } +} + +export function px(view: ViewSnap, arg: { refName: string; coord: number }) { + return bpToPx({ ...arg, self: view })?.offsetPx +} + +export function layoutMatches( + feats: Feature[][], + assemblyManager?: AssemblyManager, +) { + const matches = [] + for (let i = 0; i < feats.length; i++) { + for (let j = i; j < feats.length; j++) { + if (i !== j) { + for (const [f1, f2] of generateMatches(feats[i], feats[j], f => + f.get('syntenyId'), + )) { + let f1s = f1.get('start') + let f1e = f1.get('end') + const f2s = f2.get('start') + const f2e = f2.get('end') + + if (f1.get('strand') === -1) { + ;[f1e, f1s] = [f1s, f1e] + } + const a1 = assemblyManager?.get(f1.get('assemblyName')) + const a2 = assemblyManager?.get(f2.get('assemblyName')) + const r1 = f1.get('refName') + const r2 = f2.get('refName') + + matches.push([ + { + feature: f1, + level: i, + refName: a1?.getCanonicalRefName(r1) || r1, + layout: [f1s, 0, f1e, 10] as RectTuple, + }, + { + feature: f2, + level: j, + refName: a2?.getCanonicalRefName(r2) || r2, + layout: [f2s, 0, f2e, 10] as RectTuple, + }, + ]) + } + } + } + } + return matches +} diff --git a/plugins/linear-comparative-view/src/LinearSyntenyView/components/ImportSyntenyTrackSelector.tsx b/plugins/linear-comparative-view/src/LinearSyntenyView/components/ImportSyntenyTrackSelector.tsx index f804701710..f9918437d6 100644 --- a/plugins/linear-comparative-view/src/LinearSyntenyView/components/ImportSyntenyTrackSelector.tsx +++ b/plugins/linear-comparative-view/src/LinearSyntenyView/components/ImportSyntenyTrackSelector.tsx @@ -7,8 +7,9 @@ import { AnyConfigurationModel, readConfObject, } from '@jbrowse/core/configuration' - import { observer } from 'mobx-react' + +// locals import { LinearSyntenyViewModel } from '../model' function f(track: AnyConfigurationModel, assembly1: string, assembly2: string) { diff --git a/plugins/linear-comparative-view/src/LinearSyntenyView/model.ts b/plugins/linear-comparative-view/src/LinearSyntenyView/model.ts index 3fa0b109e3..c2a79c5df4 100644 --- a/plugins/linear-comparative-view/src/LinearSyntenyView/model.ts +++ b/plugins/linear-comparative-view/src/LinearSyntenyView/model.ts @@ -18,26 +18,25 @@ export default function stateModelFactory(pluginManager: PluginManager) { return types .compose( baseModel(pluginManager), - types - .model('LinearSyntenyView', { - /** - * #property - */ - type: types.literal('LinearSyntenyView'), - /** - * #property - */ - drawCurves: false, - }) - .actions(self => ({ - /** - * #action - */ - toggleCurves() { - self.drawCurves = !self.drawCurves - }, - })), + types.model('LinearSyntenyView', { + /** + * #property + */ + type: types.literal('LinearSyntenyView'), + /** + * #property + */ + drawCurves: false, + }), ) + .actions(self => ({ + /** + * #action + */ + toggleCurves() { + self.drawCurves = !self.drawCurves + }, + })) .views(self => { const superMenuItems = self.menuItems return { diff --git a/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/ImportUtils.test.ts b/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/ImportUtils.test.ts index 4f05a64b52..227769807b 100644 --- a/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/ImportUtils.test.ts +++ b/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/ImportUtils.test.ts @@ -1,10 +1,9 @@ import { promises as fsPromises } from 'fs' import path from 'path' -import { parseCsvBuffer } from './ImportUtils' -import { TextDecoder } from 'web-encoding' +// locals +import { parseCsvBuffer } from './ImportUtils' import SpreadsheetModel from '../models/Spreadsheet' -window.TextDecoder = TextDecoder test('csv to spreadsheet snapshot', async () => { const filepath = path.join( diff --git a/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/STARFusionImport.test.ts b/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/STARFusionImport.test.ts index 1000332274..df8046c763 100644 --- a/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/STARFusionImport.test.ts +++ b/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/STARFusionImport.test.ts @@ -1,9 +1,9 @@ import { promises as fsPromises } from 'fs' import path from 'path' + +// locals import { parseSTARFusionBuffer } from './STARFusionImport' -import { TextDecoder } from 'web-encoding' import SpreadsheetModel from '../models/Spreadsheet' -window.TextDecoder = TextDecoder test('starfusion import', async () => { const filepath = path.join( diff --git a/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/VcfImport.test.ts b/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/VcfImport.test.ts index 2e5a957ec3..9454a9e95a 100644 --- a/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/VcfImport.test.ts +++ b/plugins/spreadsheet-view/src/SpreadsheetView/importAdapters/VcfImport.test.ts @@ -1,4 +1,3 @@ -import { TextDecoder, TextEncoder } from 'web-encoding' import { promises as fsPromises } from 'fs' import path from 'path' import { parseVcfBuffer, splitVcfFileHeaderAndBody } from './VcfImport' @@ -26,9 +25,6 @@ describe('vcf file splitter', () => { }) }) -window.TextEncoder = TextEncoder -window.TextDecoder = TextDecoder - test('vcf file import', async () => { const filepath = path.join( __dirname, diff --git a/plugins/variants/src/VcfAdapter/VcfAdapter.test.ts b/plugins/variants/src/VcfAdapter/VcfAdapter.test.ts index 690c26e47c..337fd1f429 100644 --- a/plugins/variants/src/VcfAdapter/VcfAdapter.test.ts +++ b/plugins/variants/src/VcfAdapter/VcfAdapter.test.ts @@ -1,8 +1,6 @@ import { toArray } from 'rxjs/operators' import Adapter from './VcfAdapter' import configSchema from './configSchema' -import { TextDecoder } from 'web-encoding' -window.TextDecoder = TextDecoder test('adapter can fetch variants from volvox.vcf', async () => { const adapter = new Adapter( diff --git a/products/jbrowse-web/src/__snapshots__/jbrowseModel.test.ts.snap b/products/jbrowse-web/src/__snapshots__/jbrowseModel.test.ts.snap index d9896e2e5c..7ac2ee0d02 100644 --- a/products/jbrowse-web/src/__snapshots__/jbrowseModel.test.ts.snap +++ b/products/jbrowse-web/src/__snapshots__/jbrowseModel.test.ts.snap @@ -390,6 +390,44 @@ exports[`JBrowse model creates with non-empty snapshot 1`] = ` "type": "ReferenceSequenceTrack", }, }, + { + "name": "volvox_random_inv", + "sequence": { + "adapter": { + "faiLocation": { + "internetAccountId": undefined, + "internetAccountPreAuthorization": undefined, + "locationType": "UriLocation", + "uri": "output_prefix3.simseq.genome.fa.fai", + }, + "fastaLocation": { + "internetAccountId": undefined, + "internetAccountPreAuthorization": undefined, + "locationType": "UriLocation", + "uri": "output_prefix3.simseq.genome.fa", + }, + "metadataLocation": { + "internetAccountId": undefined, + "internetAccountPreAuthorization": undefined, + "locationType": "UriLocation", + "uri": "/path/to/fa.metadata.yaml", + }, + "type": "IndexedFastaAdapter", + }, + "displays": [ + { + "displayId": "volvox_random_inv-ReferenceSequenceTrack-LinearReferenceSequenceDisplay", + "type": "LinearReferenceSequenceDisplay", + }, + { + "displayId": "volvox_random_inv-ReferenceSequenceTrack-LinearGCContentDisplay", + "type": "LinearGCContentDisplay", + }, + ], + "trackId": "volvox_random_inv-ReferenceSequenceTrack", + "type": "ReferenceSequenceTrack", + }, + }, ], "configuration": {}, "connections": [], @@ -3956,6 +3994,49 @@ exports[`JBrowse model creates with non-empty snapshot 1`] = ` "trackId": "volvox-simple-inv-paired.bam", "type": "AlignmentsTrack", }, + { + "adapter": { + "assemblyNames": [ + "volvox_random_inv", + "volvox", + ], + "pafLocation": { + "internetAccountId": undefined, + "internetAccountPreAuthorization": undefined, + "locationType": "UriLocation", + "uri": "volvox_inv_indels.paf", + }, + "type": "PAFAdapter", + }, + "assemblyNames": [ + "volvox_random_inv", + "volvox", + ], + "category": [ + "Synteny", + ], + "displays": [ + { + "displayId": "volvox_inv_indels-DotplotDisplay", + "type": "DotplotDisplay", + }, + { + "displayId": "volvox_inv_indels-LinearComparativeDisplay", + "type": "LinearComparativeDisplay", + }, + { + "displayId": "volvox_inv_indels-LinearSyntenyDisplay", + "type": "LinearSyntenyDisplay", + }, + { + "displayId": "volvox_inv_indels-LGVSyntenyDisplay", + "type": "LGVSyntenyDisplay", + }, + ], + "name": "volvox_inv_indels", + "trackId": "volvox_inv_indels", + "type": "SyntenyTrack", + }, ], } `; diff --git a/products/jbrowse-web/src/tests/Connection.test.tsx b/products/jbrowse-web/src/tests/Connection.test.tsx index 8bfb26e915..c889a5a7e6 100644 --- a/products/jbrowse-web/src/tests/Connection.test.tsx +++ b/products/jbrowse-web/src/tests/Connection.test.tsx @@ -42,4 +42,4 @@ test('Open up a UCSC trackhub connection', async () => { }) fireEvent.click(await findByText('Connect')) await findByText('CRAM - Volvox Sorted', ...opts) -}, 20000) +}, 40000) diff --git a/products/jbrowse-web/src/tests/Dotplot.test.tsx b/products/jbrowse-web/src/tests/Dotplot.test.tsx index 901ca43fe5..f71e99374f 100644 --- a/products/jbrowse-web/src/tests/Dotplot.test.tsx +++ b/products/jbrowse-web/src/tests/Dotplot.test.tsx @@ -1,6 +1,4 @@ import { fireEvent } from '@testing-library/react' -import { toMatchImageSnapshot } from 'jest-image-snapshot' -import { TextEncoder, TextDecoder } from 'web-encoding' import path from 'path' // locals @@ -9,17 +7,9 @@ import dotplotSession from './dotplot_inverted_test.json' import dotplotSessionFlipAxes from './dotplot_inverted_flipaxes.json' import { setup, doBeforeEach, expectCanvasMatch, createView } from './util' -if (!window.TextEncoder) { - window.TextEncoder = TextEncoder -} -if (!window.TextDecoder) { - window.TextDecoder = TextDecoder -} - const delay = { timeout: 25000 } const opts = [{}, delay] -expect.extend({ toMatchImageSnapshot }) setup() beforeEach(() => { diff --git a/products/jbrowse-web/src/tests/ExportSvg.test.tsx b/products/jbrowse-web/src/tests/ExportSvg.test.tsx index 2779f2dbf1..1227cf74d2 100644 --- a/products/jbrowse-web/src/tests/ExportSvg.test.tsx +++ b/products/jbrowse-web/src/tests/ExportSvg.test.tsx @@ -1,5 +1,4 @@ import { fireEvent, waitFor } from '@testing-library/react' -import { TextEncoder } from 'web-encoding' import fs from 'fs' import path from 'path' import FileSaver from 'file-saver' @@ -7,8 +6,6 @@ import FileSaver from 'file-saver' // locals import { hts, createView, setup, doBeforeEach } from './util' -window.TextEncoder = TextEncoder - // mock from https://stackoverflow.com/questions/44686077 jest.mock('file-saver', () => ({ saveAs: jest.fn() })) diff --git a/products/jbrowse-web/src/tests/JBrowse.test.tsx b/products/jbrowse-web/src/tests/JBrowse.test.tsx index 1470603ef8..6cb245c802 100644 --- a/products/jbrowse-web/src/tests/JBrowse.test.tsx +++ b/products/jbrowse-web/src/tests/JBrowse.test.tsx @@ -1,7 +1,6 @@ import '@testing-library/jest-dom/extend-expect' import { fireEvent } from '@testing-library/react' -import { TextEncoder } from 'web-encoding' import { readConfObject, getConf } from '@jbrowse/core/configuration' import PluginManager from '@jbrowse/core/PluginManager' @@ -15,7 +14,6 @@ import TestPlugin from './TestPlugin' jest.mock('../makeWorkerInstance', () => () => {}) -window.TextEncoder = TextEncoder setup() const delay = { timeout: 15000 } diff --git a/products/jbrowse-web/src/tests/Loader.test.tsx b/products/jbrowse-web/src/tests/Loader.test.tsx index 7cd3c58150..ac4e7a287f 100644 --- a/products/jbrowse-web/src/tests/Loader.test.tsx +++ b/products/jbrowse-web/src/tests/Loader.test.tsx @@ -1,31 +1,22 @@ -// we use mainthread rpc so we mock the makeWorkerInstance to an empty file import React from 'react' import { render, waitFor } from '@testing-library/react' -import { TextEncoder, TextDecoder } from 'web-encoding' + +import { toMatchImageSnapshot } from 'jest-image-snapshot' import { LocalFile } from 'generic-filehandle' import rangeParser from 'range-parser' + +// local +import { App } from './loaderUtil' + import { Image, createCanvas } from 'canvas' -import { QueryParamProvider } from 'use-query-params' -import { WindowHistoryAdapter } from 'use-query-params/adapters/window' -import { Loader } from '../Loader' +jest.mock('../makeWorkerInstance', () => () => {}) // @ts-ignore global.nodeImage = Image // @ts-ignore global.nodeCreateCanvas = createCanvas -jest.mock('../makeWorkerInstance', () => () => {}) - -const delay = { timeout: 20000 } - -if (!window.TextEncoder) { - window.TextEncoder = TextEncoder -} -if (!window.TextDecoder) { - window.TextDecoder = TextDecoder -} - const getFile = (url: string) => new LocalFile( require.resolve(`../../${url.replace(/http:\/\/localhost\//, '')}`), @@ -103,29 +94,20 @@ const readBuffer = async (url: string, args: RequestInit) => { } } +jest.mock('../makeWorkerInstance', () => () => {}) + +expect.extend({ toMatchImageSnapshot }) + +const delay = { timeout: 20000 } + // @ts-ignore jest.spyOn(global, 'fetch').mockImplementation(readBuffer) -function App({ search }: { search: string }) { - const location = { - ...window.location, - search, - } - Object.defineProperty(window, 'location', { - writable: true, - value: location, - }) - return ( - - - - ) -} - afterEach(() => { localStorage.clear() sessionStorage.clear() }) + test('errors with config in URL that does not exist', async () => { console.error = jest.fn() const { findByText } = render() @@ -196,23 +178,6 @@ test('can use a spec url for lgv', async () => { ) }, 40000) -test('can use a spec url for dotplot view', async () => { - const { findByTestId } = render( - , - ) - - await findByTestId('prerendered_canvas_done', {}, delay) -}, 40000) - -test('can use a spec url for synteny view', async () => { - console.warn = jest.fn() - const { findByTestId } = render( - , - ) - - await findByTestId('synteny_canvas', {}, delay) -}, 40000) - test('can use a spec url for spreadsheet view', async () => { console.warn = jest.fn() const { findByText } = render( @@ -230,3 +195,11 @@ test('can use a spec url for sv inspector view', async () => { await findByText('ctgB:1982..1983', {}, delay) }, 40000) + +test('can use a spec url for dotplot view', async () => { + const { findByTestId } = render( + , + ) + + await findByTestId('prerendered_canvas_done', {}, delay) +}, 40000) diff --git a/products/jbrowse-web/src/tests/SVInspector.test.tsx b/products/jbrowse-web/src/tests/SVInspector.test.tsx index 021bf70c8d..4480356273 100644 --- a/products/jbrowse-web/src/tests/SVInspector.test.tsx +++ b/products/jbrowse-web/src/tests/SVInspector.test.tsx @@ -1,12 +1,7 @@ import '@testing-library/jest-dom/extend-expect' import { fireEvent, waitFor } from '@testing-library/react' -import { TextEncoder, TextDecoder } from 'web-encoding' - import { doBeforeEach, createView, setup } from './util' -window.TextEncoder = TextEncoder -window.TextDecoder = TextDecoder - setup() beforeEach(() => { diff --git a/products/jbrowse-web/src/tests/Spreadsheet.test.tsx b/products/jbrowse-web/src/tests/Spreadsheet.test.tsx index 267f7e6958..07ab2af60f 100644 --- a/products/jbrowse-web/src/tests/Spreadsheet.test.tsx +++ b/products/jbrowse-web/src/tests/Spreadsheet.test.tsx @@ -1,12 +1,8 @@ import '@testing-library/jest-dom/extend-expect' import { fireEvent, waitFor } from '@testing-library/react' -import { TextEncoder, TextDecoder } from 'web-encoding' import { createView, setup, doBeforeEach } from './util' -window.TextEncoder = TextEncoder -window.TextDecoder = TextDecoder - setup() beforeEach(() => { diff --git a/products/jbrowse-web/src/tests/Synteny.test.tsx b/products/jbrowse-web/src/tests/Synteny.test.tsx new file mode 100644 index 0000000000..df4eb3b289 --- /dev/null +++ b/products/jbrowse-web/src/tests/Synteny.test.tsx @@ -0,0 +1,225 @@ +import React from 'react' +import { render } from '@testing-library/react' + +import { toMatchImageSnapshot } from 'jest-image-snapshot' +import { LocalFile } from 'generic-filehandle' +import rangeParser from 'range-parser' + +// local +import { App } from './loaderUtil' +import { expectCanvasMatch } from './util' + +const getFile = (url: string) => + new LocalFile( + require.resolve(`../../${url.replace(/http:\/\/localhost\//, '')}`), + ) + +const readBuffer = async (url: string, args: RequestInit) => { + // this is the analytics + if (url.match(/jb2=true/)) { + return { + ok: true, + async json() { + return {} + }, + } + } + try { + const file = getFile(url) + const maxRangeRequest = 2000000 // kind of arbitrary, part of the rangeParser + if (args.headers && 'range' in args.headers) { + const range = rangeParser(maxRangeRequest, args.headers.range) + if (range === -2 || range === -1) { + throw new Error(`Error parsing range "${args.headers.range}"`) + } + const { start, end } = range[0] + const len = end - start + 1 + const buf = Buffer.alloc(len) + const { bytesRead } = await file.read(buf, 0, len, start) + const stat = await file.stat() + return new Response(buf.slice(0, bytesRead), { + status: 206, + headers: [['content-range', `${start}-${end}/${stat.size}`]], + }) + } + const body = await file.readFile() + return new Response(body, { status: 200 }) + } catch (e) { + console.error(e) + return new Response(undefined, { status: 404 }) + } +} + +jest.mock('../makeWorkerInstance', () => () => {}) + +expect.extend({ toMatchImageSnapshot }) + +const delay = { timeout: 20000 } + +// @ts-ignore +jest.spyOn(global, 'fetch').mockImplementation(readBuffer) + +afterEach(() => { + localStorage.clear() + sessionStorage.clear() +}) + +test('horizontally flipped inverted alignment', async () => { + console.warn = jest.fn() + const str = `?config=test_data%2Fgrape_peach_synteny%2Fconfig.json&session=spec-${JSON.stringify( + { + views: [ + { + type: 'LinearSyntenyView', + tracks: ['subset'], + views: [ + { loc: 'Pp01:28,845,211..28,845,272[rev]', assembly: 'peach' }, + { loc: 'chr1:316,306..316,364', assembly: 'grape' }, + ], + }, + ], + }, + )}` + const { findByTestId } = render() + expectCanvasMatch(await findByTestId('synteny_canvas', {}, delay)) +}, 40000) + +test('regular orientation inverted alignment', async () => { + console.warn = jest.fn() + const str = `?config=test_data%2Fgrape_peach_synteny%2Fconfig.json&session=spec-${JSON.stringify( + { + views: [ + { + type: 'LinearSyntenyView', + tracks: ['subset'], + views: [ + { loc: 'Pp01:28,845,211..28,845,272', assembly: 'peach' }, + { loc: 'chr1:316,306..316,364', assembly: 'grape' }, + ], + }, + ], + }, + )}` + const { findByTestId } = render() + expectCanvasMatch(await findByTestId('synteny_canvas', {}, delay)) +}, 40000) + +test('regular orientation inverted alignment bottom', async () => { + console.warn = jest.fn() + const str = `?config=test_data%2Fgrape_peach_synteny%2Fconfig.json&session=spec-${JSON.stringify( + { + views: [ + { + type: 'LinearSyntenyView', + tracks: ['subset'], + views: [ + { loc: 'Pp01:28,845,211..28,845,272', assembly: 'peach' }, + { loc: 'chr1:316,306..316,364[rev]', assembly: 'grape' }, + ], + }, + ], + }, + )}` + const { findByTestId } = render() + expectCanvasMatch(await findByTestId('synteny_canvas', {}, delay)) +}, 40000) + +test('regular orientation both horizontally flipped', async () => { + console.warn = jest.fn() + const str = `?config=test_data%2Fgrape_peach_synteny%2Fconfig.json&session=spec-${JSON.stringify( + { + views: [ + { + type: 'LinearSyntenyView', + tracks: ['subset'], + views: [ + { loc: 'Pp01:28,845,211..28,845,272[rev]', assembly: 'peach' }, + { loc: 'chr1:316,306..316,364[rev]', assembly: 'grape' }, + ], + }, + ], + }, + )}` + const { findByTestId } = render() + expectCanvasMatch(await findByTestId('synteny_canvas', {}, delay)) +}, 40000) + +test('switch rows regular orientation both horizontally flipped', async () => { + console.warn = jest.fn() + const str = `?config=test_data%2Fgrape_peach_synteny%2Fconfig.json&session=spec-${JSON.stringify( + { + views: [ + { + type: 'LinearSyntenyView', + tracks: ['subset'], + views: [ + { loc: 'chr1:316,306..316,364', assembly: 'grape' }, + { loc: 'Pp01:28,845,211..28,845,272', assembly: 'peach' }, + ], + }, + ], + }, + )}` + const { findByTestId } = render() + expectCanvasMatch(await findByTestId('synteny_canvas', {}, delay)) +}, 40000) + +test('switch rows regular orientation both horizontally flipped rev 1', async () => { + console.warn = jest.fn() + const str = `?config=test_data%2Fgrape_peach_synteny%2Fconfig.json&session=spec-${JSON.stringify( + { + views: [ + { + type: 'LinearSyntenyView', + tracks: ['subset'], + views: [ + { loc: 'chr1:316,306..316,364[rev]', assembly: 'grape' }, + { loc: 'Pp01:28,845,211..28,845,272', assembly: 'peach' }, + ], + }, + ], + }, + )}` + const { findByTestId } = render() + expectCanvasMatch(await findByTestId('synteny_canvas', {}, delay)) +}, 40000) + +test('switch rows regular orientation both horizontally flipped rev2', async () => { + console.warn = jest.fn() + const str = `?config=test_data%2Fgrape_peach_synteny%2Fconfig.json&session=spec-${JSON.stringify( + { + views: [ + { + type: 'LinearSyntenyView', + tracks: ['subset'], + views: [ + { loc: 'chr1:316,306..316,364', assembly: 'grape' }, + { loc: 'Pp01:28,845,211..28,845,272[rev]', assembly: 'peach' }, + ], + }, + ], + }, + )}` + const { findByTestId } = render() + expectCanvasMatch(await findByTestId('synteny_canvas', {}, delay)) +}, 40000) + +test('switch rows regular orientation both horizontally flipped both rev', async () => { + console.warn = jest.fn() + const str = `?config=test_data%2Fgrape_peach_synteny%2Fconfig.json&session=spec-${JSON.stringify( + { + views: [ + { + type: 'LinearSyntenyView', + tracks: ['subset'], + views: [ + { loc: 'chr1:316,306..316,364[rev]', assembly: 'grape' }, + { loc: 'Pp01:28,845,211..28,845,272[rev]', assembly: 'peach' }, + ], + }, + ], + }, + )}` + const { findByTestId } = render() + expectCanvasMatch(await findByTestId('synteny_canvas', {}, delay)) +}, 40000) diff --git a/products/jbrowse-web/src/tests/__image_snapshots__/dotplot-test-tsx-inverted-dotplot-flip-axes-1-snap.png b/products/jbrowse-web/src/tests/__image_snapshots__/dotplot-test-tsx-inverted-dotplot-flip-axes-1-snap.png index 9f1fb97f054b183a7a17b34320a857cb56948291..f08b1f6e67e8f040fd13b209c17ad6a0fae03aa4 100644 GIT binary patch literal 17491 zcmeHP3sh5QwvG=5tG80yGTs3p?KrLNrHXCEC@N{I6{{9hLW1QH-zWl6F@QW0(K@u+ zz;vvS3P=?dNFV``XMkX-Fe;DwIPwTdPzo83K)^JRggov(C!B*;?M!Fgd)Jz~OxLPw zot*sV-+O=i+k1ci(J$Bdc)vK=X)=XEdGV7K%hpjScGVQh#CIqC4zAoioOT)h_}!*e z-peRf?7#1-vX4?IuTef(_8}uSy;rfb;mw{5l}?ks*o(TUe(-hgSw8n#7dCyrZs6wm z#Rnq%R~5_+WHNr2U*h)p=c`#C&3t{0U?Te324vvy3Tgfp=AlyuHUtZACOf*%4L|Z` z(`D{dpY(%Wt-E{mFH!4Un^(u~Zu^QBK2+WR2ld6G5yOhqvtK3BEecI~Ca>IL2U~mo zdG3M#Ll2n3_1n2O(p<*l&s4s}7V`oW{(Y5K*W(vD(@mRnS=153I_|j&^wS0#LPNV_ zMy%6S5_2U(p$JIQ>?<0N|Gp;gx@fxb>XqRKeAn>28r9&1KjA;v|3VAX`l&8Fs(Uz9 z_zI%R=jFCbz23v05FX9rmbK^m(AWp)>s<1crK8sy3~RC)EIh59K6m*JUYTbBd4EXO z->S1c+Y(-|RH3(!*$-w;z!s`WkC{I;kS8*XH+dt4C`~w}#T(0b%KpY7<|0HPn0cbe zPX9g4Z6*vhsO^O>E)C5p+WyXbaZzwYaC*y03%S@gxeyxl9bXNl()RUtl`?RaOw zHEng1O@72S(|0<#4M>V%n@KGCa0zjH)0w)nBZqpX40H*)^*+U+}8w1^T$Jx+zP|XrW?z5TP);e1V{!_3qH#h#XBd@a}OW`#p;`v5?Fl`V6eCAi=KLN zJ1zd+Le3?{hYgzrj`}O)`tgg3#`DWZU2un7-cjH&Y>p@$&;PE_Rknxld{j>1-+9$= z^ZB|x3EAyMB=**WAKCK!8d-Zl61$3k>Ow|hh4DiO)YY(3&!9dwvByQSwOcgIY={#z zH-)JQCQy@5wssp|2Fo)-nVt4`2%Juh25;3nHD`BRBv#(X>MH0imG{5aQ&{(U<1ZkM zxPf1q8JW3Wg~a{kC57YpIYog8B{}rMQb=$d=+j7x9o}G{|Wgs<$r2 zh;B8W=u}0#s@4ROhUcu^Mpr^^2_apyq~T4X$elAv7h@^SfLMBu#gYZFEXHHG^wjix zV)?%OXh7gAnDz6Rotb1d2Hgrs;#WbEI#e3VONrZ~rh5Ed;SP|aa^9(r5k8WoH095h z1T3021CnHkgi%^>SUI@sgtOa#T$o5A=mevbCB+=(14a{`nXA44NxJMQG*v*x9&-VK ziJp$uw~Ayf5a?frGvS+sEtm_Q4rls}gmIQ|K;#25dM=0y@(ehifiT5xURdqdN08cl zY2M(4;Y7@&Fjz%Lu2Sr%cfcSZ?$4dd8X>mQ*u;6Y4RZkhoabj%F{HchKFa|RV+;bR z9*4CKWd19W1>uIilK9Ep0bPITIYfGQM3{m0qsPGSp-$fO=wpZ$6Ns)PvaqBSm_`&= z2nJn&{3HnKWYou-8_ib$r2Je+<}hM|Q8|O+VJ(2gox9Dhnz*~Q_a<;*5oS(7MNwP} zX?zb*_CeUjQ6v%ylZ<$1G%xFKO1w<*OV_5$IK=AOGXzOD<<&G3NT0t*vUZE=R;+SM z$waiW+75C<)}M=pXUQV3!_D~2+6}m2L*qO zFZEPFif(_hM#&{L9Lbz9Zf;o+wl7AvQu~JfmSE;7DzWdLAht_xbz8LkyQv;icZ56= zP=E??witykJ9N+topzLwHYY}pqeBgE9!)C1ExD23nAzhUw%0e74YnW2x zZkUy1a|0Pn1r*zAVwt97Xi$bB^L-&xc!ZU$y_L(qPW0{CceL$gQ#_I_3M9Ry37=77 zV#-~9SbS%}xqUat^_5}S6;e-(o>uF=MURsE*$I>?DK$?vrJ#q2heCIf4-q{=M3lIW zLs|CYv!VPy({k}ItxZ=E&1HjOLskQ?641iPnkTEOc*F&0N!{-2M}f|KJyN#w!bt=q za?csJJ=Ft+<4J-i~b6c0Es3$6vC56Ps z8R-0f9oQ?NNNY$^uNAyEG6EjU_ADAtC!!r5T>zaD{Z6b`_i=LNMb62yWeS3#$_V-| zDQ(Z~7l-VCR{w%=weBi;e=#&hOEVzz{z_abYGHQ9_`3s#;tdCxEGKApI@sHR&Q3T# z7!;A0lbtkibQPq^I%oz-B5EY*xkb206f+hbaS9Ws=e)=1SxdIhQo%jbpNYNT8Qp~$ z?sVSJhu9IBxJ~U}>X8z7Ivg17@$&RQG!rRpA8j~~5WB=?C@}O(tUN;w`;xttcn6G7 zcOXgaL(Cm=;`W8fPDlEu1M#HNe!)}DoH)K18>8TNDiT8bBJ3q+sMnn-MQ;o4JF z@l?_zm)?fSroB*hHw)B$kwlH@sTA#QeJGC@e)ZZfrU-u+jGs0|kUbYUMf_^dbqKTP z-@oS?nNgplKmkC|q$s%P5fw@H!G2><>S);<) zT{+!YIH5+-o>8MDDWY4(OAHG4~r8v6o+Er+yGU5tT;7-ZT4WzugW}pBFE>dmp;8X;X2n~ z<<)T8RQyY!3op13f1d=McUFUzXfG)g#dGk53a z8|F#>&hQLHjBS==PhK`d3MKm)KL_R$k&I%Cu z1Wl24yg@Qgl#nNE=-I3rxhq35vMBkQxcqmIM7G0)x$? z0~Bl|9xF?P{LoWbgqNk5m$Z*d0r4VI3J`+4_1Gv(zx|k88y{iEW9bMKk97$9KncLB zQuz}~0FL1&l>jAH!}3817$zN!DFGOcpQ-A}6>O zvhJ%`wiKSa7(p(imq#LePK26-g^SB-I z_!#_dERUf(ZT5iZcLXG@(K3{sSqN<&c14vLjdmbhyNRR^_1vBJ+6}VD`%?@LV z^}jMZ5Y1*F2`x>|GX()H@U8n3Lop{#^`szx0s3?p0CasHGg)LNsA8L%cDNU7Ht}N! z;&`ILu!}f`fE*)bth5ZaIZQW#!@piAJMFjtCr{2s>h^EiQla(dOO1`_LBY!|_;ux; zA(nNeZrI6tPUxxuDubU@v6YNpF!x+s4u%2|FRLOJyR5$z(VT|;lDE7!-?Fmn?Qh&l z^c_)%D!1BFL60vtR-w+E08f1B1*_~qr-_ui)S3n}{g5hwzus<@3_oz7m?qMg)_uBe z^hy&nxo3^}XqLdku#-MLf8@XL9}gr4hnS^EVn*UPOBY>IX&kZahT7V{RfQj&P(swW z8m26J*;K4v8}00ypI+K42kvkF&-&TpD4p5rv&%m*-)%9sC86$2>f(BpIhM=P$&rI< zy_7=f^nLXTjY7#Q%{CrFTSWY-s6~;6th$j-U(>t%=IJ79{_d|{p-?P8_$5rB)TBLl zh^q2<`EjZT7SjjZL1%rl!AD0E*@p~=)(bh+NmkpjjsD9af2zcii_-I4Q9+`+shC!4 z-KcXzE_<-2;AZ_94m@q zD~sZ%n_{6UiqWMZ@)JCGn*N1%s$pAhLRpYGPFEPEUouN*Ew7NooiS2H^-T)_2;rX$ zzDS`|Dq$uM2{xmAs{vJn8l*l<>vzU5or-0uI(rx+SV9GQ+j-hc_##bLs?ifd zS^HZTo1_srA&-R5l z!RHmFzquHdN3iy1=2)^(_j!l+;@4wWwFR2j>0+GE56yo=Ukm-y`Rjf*vp$buSU=Pu zC2lo*OXDOPkmU;k%;%wtLa6Kn>-TBjOv4vxx;7%TUWz+Muh8?V*Z!57KpelewXzb= zWLBDZSgOzDFh9U=Z@ohg>sM&#ITof)?QA~}=Z4PN_gTVMB4SJrHL^3!CRn4T|(TK-ZyavWUYOf>D50W4mU!#OsQtlOEuS zE2@B~jsc@uTabEE8fclXoM4Ru#aNRgCTb^s>k}DKzKSES_t`jMmDH-6trN*rEU|l`D%AFBw~$w(9Bf zq2zbj8Z1*a{vl3s&zs+f9 z^)X^tlAr6~EN0$m?|mAK4F{GF-iCy)X|5h-$e8AJSugP$mFRJ_W}&Iy525Gqq(-*$ z8<=}wA3MaD6tC%Eb*4h`j0`b?V@kGQoHUSKdm3$K%7R=>g|g`H3V$z)ei&oUsVFrn zse>*3oJx9#E>+|np7AQal9CcXa=-Eqyu88%e0hb$_SONvRfq6{5{qbJ3cuQzfi}Nm za>ef+NQUxv9=exqtO8{+tUPY_*pD0$&OBu=FKaT0hUbkwk!!|LIImeZL11c1KzDAU z-)rWCEk3$Vmy4=P^cfER{32r@vRVBNKE6?BLm6_Gs7_I&&LeVxaH} zr!_V+SmAWrYZuW{;dNH$iBqYas>si^rIDkTdn$`7ii}|+iIck%x?XSGSu*-iX-3Xe zI4o12a_DrRx!y3<3>~$lV^ajuh90T9(UcB!)ytCPwBlbUJb8g|jBr6;wg(I7Z5 zoS;917I^w)5T^D<3#VOl5UpzT9^lOX-jGJ@CrWwai#An+*M4^ILr1-%+}B303UT=7 zz41<&v~LBS4j!&I5;Vkfn;tkj1oFcFFFV(rfu-#nF6-~hcTy> z?6$KU8UjjL!POG0ly)J>&eIjX^QkqNVL>TWM*V#DXsL<}OSm;M+^(5ydSCEhkQG5V zKRq%i2@dNid{0&8`1fuyrAa^Fyz_+=?9op`7)0~5=i zYCj;c2(aqy%<9Z05gD29wJuQzo78ZEQdJj;O-5DhzG1EfZ}|zX4?Y%GXVuTu61bl( zFB{L-5guzg0&M32?%DzP3CU<3g>Y{)7jTXtTf$NcX@sqyu}*_?{RE1v^^2ZRp&Joy zX)x05Hh`mm_PrsHxE!TqD%L0Fo4pS0sUG}Gj|brzg%87CP(M3!r`LXZN9;1KnfPL1 zseh=*U3XJlBaxFksf46yo=2^riu&X3mf9%u;@+sPQzJ(qQF;8XI1>r5h1t$39jN9M zcwJ_X={wI7GE?ioi}i8{zZPKBLgq)h*y}U%caW-QJoB0uvk3P}Gr~Uatwq;qrSEZL`w(WlYRq`!E}*KbL_moLrl&czbYyPF(sZ7{hY*G$M$6G(+isNd@;7gs~g##n|fhovF2sg1xYae6g7Ag}b zs_@UXG!Bs~l~vP^;I6JAuLu7`b+RQ*{v3kC8uqcCMb{*^c|3;dJHTx_zzxFwh{fmjk-1d*3i4ss?(1qGc#N zSY%dq1er6?SKzXSX+0aKp4MyYa(BQmjG%=;OjfbiI&(r-{Jqiai3*1KHE1MpipdS3 z=Y`0~EV)zXF`V7*zMq{p)mb$`wn>ZNzbL3JM1B2bcX+8Az1WFT<1sWpO)}as)0naV zMGJ47$$*-s$f~xD{$%dmY!Tnz9L+rg$#@XWLPiKX;9Z4`{OZj=xY?~%|DRtj~7NE!vMDF!di4S zcMti{T&Ui8b3CY*Wo?tx93@%4y=QKtx7tW9hQT7@5-q)0Q8ifBeFz-7RNy^NRZ75C zTY-k_jt#}Xq{Rz3!2nRed1pW%DTfHxBC@;`Vx^vrcHrRe%+%V1TSTzlt`}yCcvW!~ zhWfeCV^2ZGI8Bnn%QZ5ztWH&~Wryz1Gh1zAlb?^j_1xf&qiqz+aO#$~@j&(7-J(+q zRl+%c!a8dTrQk0u^IdCAAAJu31$?F~3^9j&PlJwAzt#b}(3zUI3K>p~n^@p_7bbjo zkM#T`7+vA>&c}K@z#HBk{q3r0Fu%hm;P3!mQftuGCC8gWueLEuNF*Gr27tnJYNpaQL&qDsQ3G{wc!iE5bZ|vJz@9^C ztrriN?X4gycW>Wj6CBe05E-c7b9ns1ifrQ%#L>H8I)3kkj&+IWjOHHq8fuCZ$A@AlKw6>sEt<(v&(ThDx$Y(5RH`$dXzl&dq zZIhN5oe}H(^Y#O3=N1*GBi5!Sm{xGamT=tzpjzkho!PxrkSU(FDSeIlia z(TxvB_%m)S$1lfz5}Dt`bRhYDGoo6l|12}Y)yDf-t~Rb1G2TM7^n044h)e{{vg>HRoGfumuwIrW3se0yi~f2Z&yl%zBar&E zRF%8MECJnfY+9!ZEfKmEBX-blhwPphZ{vV#9tc~~AK}HLx9cZfqtXvl*%s*wM%q&?A)m}}G^qLgiK;#Ygs zss5^+G1%L80#q&P^1f&j6yG!}(Cdx#13-UNhGfyWW6-O&axW%??O~Ooo8!D-B;0Q;&OT$j8~bFL8&sL&BnwwsnQn)c({y7GQ=Dp zRkW6kx5i@HR5fThr-;?lZz0|yD%|hRNJhSW#3(<(UlId?!@Lggi#%CXJIJ}&MR3xr z0E@1`%*!6r=B_P=zd0(krHET**FDs$zJ z4W;)%f#`xEEgPK23nzz=7h(2`$ApQUAT+#4%!Ug!ksvg{4Et;+h^95ED#;OKXn6y# zz$Q7ukpnTy^hR&x#=}fH1q8Zer_}A1+vGMM2#b&t@~1grJ1AmuP{c0nw&TT-UcLj{ zpMVK2)+sXSR`T+%DXEEUEV!6ZN2XNzbV&`B10KEw8e7$)N-9idzhyLkR7oY(oJx4L z!T8xzl~kwHKraxk_+_BFjSv%BRHUeh$%ZnV#3qKh*1p&i5|G1Si%f;?dk&uSom=ns zz+^lN3zMZzrzeH4C?8t&1Ne(EAh-YqKID#tRRCdCMPLGr$D=xGRD}`yix8$_05UqtXAM4!FG2l?g zZT*Ge{d2c3p-?6aVE-DblVA%Eh$xf|XXZk_16W6)Ok=Dgts#psjTk zk+p6?i$E0>mos42GGE!I=Kmv(Lbldq*>_!hfFG;o-i9 zVI=p!!(dsL%v+rkeC^hq zx*z{s#|{(+RM#9E*s9%I{Z-yFo-pu>w$upqiu1j3edoA#Y5v^Wi_uF z_4N@}TJ#6MOg?_BtRl8Be4$Kk&@`mksdAM&|4Lqe?%*zUJu{PUgVT+BBCo`D=uVO^{^(}1?~V~mD~ry>zUM~I;uY$}(})KuclAm> z>yKtFUXa?EEeaF(7fl@W(r|y;VS8rtK?c*$x}d|yuY!8R9#M-ZYl2)Mgu0Zl|&k zZ%gHtQg4kY?4CJpsG6o7s@s-i>rEXKwY6rxwjeax%y1vyt8VY}Bx(*L@hyjtBhB0} z*Tn3+<6ei6h^Fl4$k%50-JNqO)hid2k~ht%%a&8e51&grr9TA6Ur!6-B}6t*;uH^~ zeLgl4*M@`B<+`;&L*n^Y8ZG!gcuiR=*_vMdxj9f%NSc?B5#m0BKIzH;iwJq}^UT%jeyZ6S!B` z3**AHy@pRH`A=E0m3gb$K^|6n%Y%RRtffp^TbDoLYa@xjJoK#L%QW|8ldc)|gP2|1 zGWgET9PqQJ59OPZrqgz+!yu|g45F4(hKN{Ovs`1|N*E%oZzxyD{1fS#s)c(>f8u>T z#NbtQ?~j)|7f#%NxBohvstkpQNWBc_i0N|*%Zkd%;r?^ZA6g@IbYw+s_qev|!^~jU zPxvKZiD=pquYo0!2}@*_PGyEqTrO-j%WR*o?e$re{op8V1k~52WZbLO?H@6hyI-kI zro(4|HHO(^IdDiEnt^Z%DA`?$Av(K=gD%kavMHY`FOAIu0R^BCpBCe2TF9WqWw>>~ zzNw4zi29>K%``Z7F4MO~UlUM<=Cj@lAYLW7<^W zYGqm$M=Giv8uL;sP+Q@Ww(HQ%;pgr*o=yh6vK+NSL#P9uk(TM2LN(9!gtNkhS(iVR zl6Mw1_^WdfUxe^Gx4;miuBIbhnAhQx@b!SOGF%jvC)_p>CdXEKw|lw$00GzWcmj<4 z8y9(*{LT(n(7HUp_eMrT)d;HT-QxQB4C1Aga~og7QoSUOHkHbd(E~R$gC9AN!aNtZ zRjolCmfKua6rQ|blJ8J5l7Sv>StwI%l}0%&H5EaZbPkg(mDf%gAHf$RxdRjRbkF@% zw(af+hH-~lXB|47I6$^~U?7&kf89DXh7oZxmldvaq$>DnWxip1q^ClQkMTL87VK^E zg)6Kq*-RhnJ}YyTLfCNyO(L)Eb>vl48+$aFSV(lqY~qskc&C-Z^~6GjN?BjPE7qmp z&npZo$(vQRV=~+l9g7?9Q>ON`D#(lR<}m>oU78_W!5SNtTawqbv-gIo>+lmc%*1Tr zs?F_%8K=qXg_*X9O<2VEDnDs%Y0$t`O-KW4%bABwjH##MWhkGm( zgdwt)9?$FwaoNF1Z?okXTx;`d_m=LUuC_Ti83vTM?az+a_f!4!E^0WSMzL>Gz6T>^ zM7{N*$r$@s>JfZ(=y64ON5eYu(h58NIUNUEt-pG2Oo#`fmQ<@glfrqZwd~AAjBK}X z&NNfF`O1HQ*Dn+uXB)qc4Xp{>LY~n%m#@7H^@XDMl+o5WH%ixyR zvk~6KvRLytlP1?Zd}QW^?@QvI8-~6#Oyzn;ree+|1@<83WW%$DQM?MDPnE^D>V;uJ zqnEEcX1}}AM=CDcN!Q_K*Uw|h$|Pm^v1+RO-dyS{tYkKha5}?LA>A2&1v)4m8r~*a zU|x6Z_-^`@=5UHK+%?fwd2_xti7KJH(xELWh+3H~oXt^4slGCN37|kXK!GFp`p7(I zs(%zVAF_XCY<(m$^em6#v4TH|Sdx7JKrJuE z&@eMYH81xxq#a)7u$69@oB?R+%v%Q;auM{C<+K9zL4W{qh{`$EqHz0|ZY3Yptkrx9 zEhE4_y$d`%shK4H_E-Xeita)8Kq;>T5G_eQ#4zjygc6CZq|UEhQXmu^Ct6Y<@+}rY z-|pWsr&xXmDs%IkS_@V$)$JV1zkY3}ZT?0mtIFURy$+OPj>T+;7Ihos*@6|=TkQ6y zkzxOtV0rhx8h5MIPVVlQHN#(0Z+=SwC@SIHfX#QVE&I(3=V=Y%pb7G?fT$SWBhs5{ zi|Z?X4f|{LS4%*P0&s9XeTuoStWWX|t{36U)i5YZDd8h^u%HkvRj=I(BJ8X<1wY05r7(+-i-vASeZ50{s20g@eBb8~;dQV}RZ?WWL)!j+doX+j}m9 zFd&@z6)k2U$H^;uPr3P9sU@!GW3JQkNFfSlj-LRv5&+VPsJK5Cvkv01`u9R(1>shJ z#{3rQ1vdjYof;>bN)j*&>Ec<~d;wEqCm?K$@5AI%N8j!NlTZ3Y;yyb0SYH0De-V}s z)zQ!X-nbbZW)6FfG-rnc5cT!7xm6KFN7+h%{9S3_+N<(g5B||(M_Zbh5uO^|=K&dV zL2)sCzNe^zolr=mc}S@ZAE)KKnwC6h4tT2CXGygNokrbXo7MK<1Eq#=use8KL;>$( zF%Ui|q~T#SngzoT6`H2uhfH%6(wvsk|JKved?}}s7?rY7nEzx{eT;~#`8F*(xZLyX zLSpjyk#hU4Ug$Ct;MmuuAs(aUkA_0auPBDJJwV&cA2hGURufVZTG7G?gGzf0nM8_9 zA;oV{hoAb?UZFNk2OyOBq)LrYHu%Iak@H`PacVk#Y4~X|p7u|@Fs(l0*g935k4;0x zF1q$M6GKlcIrKoOyF^VZ*$jj!y!-arKHrW&>OHl=X@%!^411r+5hyS-%7&gxu(K2pA;MbuDIA$V;UzZ9bE z(raJYJ~|-1Lk&m@>=!feC}DarG4xPx%*ed`Ag`Nnth-8d+4bD08x??W)gxhfgQi4> zzO{SOxq-q;TfziQ`^|U*bQ~;2BrMxKkUyo*tOdtJW zybM2+`G#pmddD1b@{(_}4(}gRa3+y{K33kcP_9IHC9&_xjL#FMt(@Byf^tP09Y^%a z0L+0U3NN)WCw`H*_b)<^8NF zLf&7iC8>^&{`QR%ah(!?{vSvDVG9Q1e_>q6 z68zaFPH|$KEc`N^m22A@9UU1ESu1?TG`BiB9#V>o(sEq~{*n5e-!W5Ke~{&)4_5C5 z5Ll$(AjWOdeCP0idqlu9R0oBKAy$31#G*47t!EuZEro6i$=^7Zmgp|jElJEq1_g#t z_2+oyjaD)pJ1s7A^qbV#$lOf7=}f-_fzAq>-&beK{_y^0%#)+cKhBm&5*Yk&b({FG zX|~E+%wcZQZ|JfJ$O*(blcU^7f{sz5ZpI%vX%jB!+%dZeqLWeb@Zi0%KEB)!=$``= z`*yRQQ*A};ty~Z%R`0-$H^_Q+?8+rw2o?&+kSyCYuRCVIxn35_Uaa(F8FO?V*dYpP ztzLriE;=`8=WE@t6l^(tIg6z)!0TZ0ypOwKz7Jmd3wgZfCfL?QMyP)<1}N)xESD?C zWb#1U!Sm}+%=znvI~$LjE7`aG%C3KYvoa{y=|J+u+#{{0k~XjY;D@Pumf)q=*3Ou2 z)^jmA@tJ2|aCz_a_XBtKO`H3{tTR`hT~M~Rp<&(Wf2|Mw*LQ2@X(C6qJMfmS_~PZ! zQMaWJvlpq=Q=gf4d~epMD`yT<+o=haF@PXmHC9f_w0Cn^neKh{g+|{b{srf|D=v~0 zH^cJ6G)!7+`)z!VbK}U&`058IOdwZmXJw}{_X#&Mi194Ey~f?N09z@6$uBU&0Sx4e z?Gne0ULy}LY%#1mA!TLmmQClM+k8eIK5_lc-qk68AG_Z(OLaTt*0>P=qI;34p#{gG zE>n0{j=v|>b+PLi+@%Zh+T)HED^b%et*sy zQ`1bYb6H@g5{Wxzj<>~iW?)(0Z<)sUVnZfypQdjCK)}k+rlmwnxgpXy30wN1_yWjyU>_pY@ zU(+2ZEn1sLW{$6cUwEVdCqqlrdwe^uRL$Uk10QE@O8#QD^ro%vw$EuNFpY+qivdiD;R-3xD39H1kqE;z?| zTjm}xa8a2=_is8dTp%FB5Af_%cC^fyzDWv>v?G3sAvR#8Y|>C41k~z@Kxejjr*b8< z{k|R&YS3EC&gvHbB&9d)(vJhoc;^Yl*%~h=TJbivHIfBxX0k$A8Q37DVDOmL+7Wi- z#nCzAN7n5#bWqpNSUBD2&!rSErxg#+xh9hjRb1aQ!w}!~kDdWqeI+uRoht5l=jNi6 z)nBFC)4tpq>U9{M3sO2bvOBZs7>aQZFf*tdxk(B?X(*jtZwEB!eZcVzRr}*}S||f} z58zc99o#+A`C5)u-;jjX>5OlZ{1*7KqG+nTxJ66}IdBvg2BTmBMZs*Aa_fX)ZjJ`! z+~k#vP8~mxvrHivp_8Vy>GewzWNB@Br>Eo5VGVtHeBtI@d=U|F%Vq;}L7g7T5jU&| zi@5|AJ5=>TZ7O|#o0qjf)*K6T9zzUJ)5WBxt!>4g(SgRencYO`x3`iz@to%K$~gF0kXehmv@Zu0K4{?7H*it{ne z)N93=*t*Co&JB>IJ0OPhX`P}Wz`5c2`Pz1@l}_n_yg{Nsg+t=CK!G}=H{8(c5nVvTf2Y4Mi2*bpx(9! zq9)_*@o=Uxu_r!n^Dbt)&Q4X1cua~Hyin{)RHy0uv-{QzcTrYZ>BKTeA%cF9&QC7Q z%Dh3L;R=x|zpTwf%^_5OtTu!0vk2XTp;qZB%hr|7*VYiKqhn}oa1(V|?6Ck!6cPHO z$I^K{xbxzZ(iyr3QOLKX{5U?1R>)Q`m@m5>3vBG;HD47V1Otn&o${( zOBvV`XU405H+JK)V!4U4vb^vgL-~zrCxq@|m@rsS$!vsp_8joes;1s!TjndD%KV5+42N zg>saeZ`cs|L6^deCoIFKGBxYft%1hToUD9VL{hc|dA?;oS*$H(vzZ?zTr4}#0DN4f zQM8pH(U{j;5v~Z9^LNd7jS-lVooc*R4+~Lwta>WbmVCVN!7NPaC3@H+ozEMruSq#H zDlfS!A+cIwu`q8CDTy7PrE-GrBdS-3{P0;*7^>pxEsNnDqq;R+fBF|!`nlmn6j0RiErZb5hjv&f@^flPB-wzl9%5Qvkd6`a(yD=XO z_n~rcztW!G1azoF%goEV(s_UsPW6{E0eo1H>ccnVA-Yd-4%+TvZh~j5Roko)nL_uP z_GBPmqfgE?;mG+TEPko37dwI9Z?wCb>UeyIq1jdWEOA=p+MtP!q28c!N8^wTOHYz{ zWXuf{8fx&?0uFqBW!SB*!RiRCM?5mqrPV*lHXPvEk*c^(rJw6^;xgm?D`#~D_zA#w zo=zPv7&V9+_5K-gXf1BQy&&v|F)3C}$5jW*T3{0*eNB4;Uy4n_PoBz6MK@)tZQLv^ z0tn~?g_d^-w5|)*{FrY-&xfor>y)*cz5>oC%&ky_#^-Rtzc!BF2iMKU3iLON{@aUJfc7-NV;0j(5WwvPG+W^{4UAX}2@AHrg^f)$KrjcyWvLfawA6?nZo*`u#q@ zCt3P-jeXVvM(3-Sy9$WUVIa%7`c~{P!VO<|?M&oF-M2VDFKg9Uu>-qac6tiwz0P;X zcc?9pCLjI`!@5X;t{tyuah9C1CJn(mhaz%+eGPu3PBfjLqsxI(GcAQ>`k8-(tmN!0!JW-VsTyWRzQlr%asR(~HP& zA8Z=4^SgWU^<0D*G_S8boqEN|Hvfc@Sm7+YtB^S;o={U|&d?6IMuN*LS4!B%eQFLW z$*xw)H|uSo{5WLmn;W1MR(BjrWH6S_BUr-mJShe*umYSJla#G_w<|%#zvG>5{If#j zFRg^?{x(r8DzkrjfahR-m^zR@GK`fQ=NW`eu>23_7@M-c={3okDlXPZplokEuwD77 z@jTOl#pmTam?rz)woQ6VY>{-dTXP!cviTQU3XCIo%D5@$m!ddek`3&wqxoiKwfpTpYof{f8CL8uxYh`|X>;ub+!4 zL5>1*G!iE#z_ak9og%+`Q1)+eN88l#HR>@;ISm?NWdhsnI(4<4U8pw z7%S_e*~VS)ahzN4XtScgkdEKO`2v4u<0UTxn0%>R8SIv!TL8dMf060i_S%?jpT?i=-f*7VMIfX4?Xec0ax9IDWq)kSK;h9 zs`8=(A@iDjvC};ss>N2{kB(6&B4U+y`<8@UKoRe&-~KtSPU%V`=j5P;4ddU@ilu;b z86<@OXlazXEY1uSReCOivg6erJrX@3?UT#J1{F!bK9v1dRANjFg$pY7C{%- zsp=&#DVzYL{`n&&2qX;oE3`<3vsXJc6P=0($+>CZ5m>Z@)A@)A`o`Ivp3Ad5NAFTn z*VO{@P=rZ8J>|XtzUiUBxuB$Yf+VQq3VhhT14Ee-n!KIqpK zf1D5cmg0lJ85)X|V7L0VH^YpGz>GKwGh!?4{E2ovp}j9XuNAih2~5ts1GGgf6}i3E zFby;jN$^9nBCeG-Vp7$)F)>U&IVs)Rb>hiE2}^O3%I|^+L@k?N#3OY@z*6N*WqL{RBK9Rm?CFkgEFluCz?bfMh`n{%P8 zjqvJ%zM&A>kh7P)*e+T=5dx3F>x%pOQ6m0tc_(q@R2W5snHlzjN<+9I_z|Wcl%I30 zg8g2_WML3N6JT*XilgcA?%Yd?Mc)df_3 z^W+^m7(`JCHJ!3sH@7!E?}97W4&H#*BoTbD#Mw(qQMqsS=0J)zLtNed8BOqbI8(WJb956y6R0_z(X4c; z$g^9q6~QrAoSWlk7~TcitD3S(L>SOsieRdo{ik5=*xc@?-$$fMu%KE*6~CJ+e*o1R zCsfaiqTgDM5V{k@$NJwpCrhY9ex*ObG)wS)`M~=nQC>S^0j!~7w={Rtaf0BK+HT6hSX7C0DwAL__eUz46{fXw@~!15%Qc_E z%+J44WFrrwgXiroKP3lv#?<9MC%j-V4T03mfz)`OI(wx#QqQi3yw65TklJ7kRT0Jd zlvm?W=iA_)HIaF-O6q#u;V1LAkI;GS)1@5gWexduFeuV)ja7ePuV13Jj1%Tq@) z)N3gbEu1FG>_~44CE_ACq4Bv zCesO+%y)^d)AI8Qy@9CE@Q%7i$scg@6S&;pLjLG!Sxn>)B-q8+uc1cWqM|pr97zEi zoCF&$aBHi{->7qYV0H7Ol~kGvwlyl-@0?O6cYn z0WmG{RleNwvj!!g8x$IcdQ!Exr=pl5aw&Thwg5F}>QC;E@wli}$e;t!aqNJ_j->5v zzCal(Zvoi^AloA(HlPxVCu6QaV%6{)cW}QGa}gyv^$Aqw)=&uC#n3{y1SLnc#Z$w_ z!95{_dw%Ly{?^?@5QIMm2)+OEZsMu&{NLG4cv2Oq=YMQBL6+*@xSQAnGrEj`oxgcG zG4`vV%QbudBm|zABYve%dAIu}&@*6f6?pY$a|E%08$+6ibPoUT4H70om*Dm$fdM+3 z^&c8l&I zfYFwqohP*wFy(Y?pQutP?4SyRcc^=$`y(5-uKqxBa<5$5JGpQJ)IAy03t>v(?gm0X zZT<@KR?AX5n9d7+1$3p{f>jfhT(CF=z-_{u&44-SUFPsvz?@P7w*LUTmg`je7W<{- zq#Z`-`b-t#M|;#S>NAzn^h)$*jC*9X`bk4^(NDYr2+$zdwr(Dv+O!~w)%Agr9*QS< zfe1TQI)PL;8cqf#q$_UvGFTlGqWfDTVbrvAbM&bh{p9sT-fvk?oP-Md{NG+U1b=Nn z9P#%85I#s;NvEe7c$pm3NHXxNl4whNs;7$K_wQz}^|#+owEuJ?|wZao>AuH?9%B`_U)= E7ktrL;Q#;t diff --git a/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-horizontally-flipped-inverted-alignment-1-snap.png b/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-horizontally-flipped-inverted-alignment-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..fcbcc7e886dd8128200b5a267dc07b9250559608 GIT binary patch literal 1680 zcmYLK2{crD82|1VX3APwMsG%Ba2`fO*^)7~7*y)nvPD{?V@V|=be|S4@zMw_LY-cl zro|&=sgXyd;!(u0RF67RD5OPs->rAfJ9EyR`@i4s`~834@B9Au9`s)6raXK8YyeOe zxx1_ZkkiHc4NL{RrmprmjDHI2J>6VjhWZuPoX^1rt|AvlpTvT#VIWo7k)M{V|-SjU*U+GuXzoDFu2vFubYp#A1&!pO8={Ho~TxO*OdZr?BB z#GT)^pQ{q3&IZe`1$V~%Z%5TA0n4L5iGbw0MN%z*y1BZL`}LT>NZgsE4SaQi=G|r3 zWMwVQC%}1*F8J~!m)i!l?vxNVY@I0Z<)l3Qlz7B!(VH}gW6_yH-{DgZ>+BdbS-2X4 zrMnXBQ&)7x833P!OixBs8dZnJAFwoG+(-zQlB%`XK@kD)pa_TyuZQ}k{2Q*pF$W6y zE;O#-Y|DKLX^6<~Hz{}A-!CVDA4{VbUz+6YnM!)^3$o(xTLwK!F8E_+N`61^ovH9c z9#QHZ|FaN|Ekdrp-`90UXND8}2#lSXG)l02)IPJh0EFA9wObK0d!s~gkpVrd5XoFC zddc$Phujh%YiFsE86WzI(ox?^Q^U2hK*@sypNWr)twVV2gFTcqeIXcs?on}nVPy)N zL>S$X+-xWO5Hi1x7M4n}71`}8eHCd{M<(9){ezDzLy{W@04a^XIp_oUB(v z@9&y1VO$q&`qakB=H5zBp_5{?{|UQ4!up>IJDorV*??*+r@9F7aY0K~IN?n>{&INt z$fjWJI$E)SqJCL86SGxGk^KA}s$J_L$(iV4Vbtn-l(5sjN@qejx)L=~B`4v=GJ@nG z(+R6C^@^V$Ioa(Kwxx;YU$qf*^E|)&kfk%a%_RIPSz5$a%`ZGgVHn16NHk1`=d$l zdd!=4VSS@X;x2p7{L!~*;Jp_&TYnjux4sg>1xGZ-yo2c{nO-_Jc^l$Zeb3${3_aJf z9cR9bIsmNg*!>&W+{jgkzOEzodQFY|TlCntf&G66cqqDdVN+BZC!>=_CvTu=dAE7{ zYfp*<^6)JO_kGsnLd2gCm{jRP88wvwD<*6Yaq|(JK!GFHq2ZJJisWH8wLEOplWXJY z&~!aqh>+76ujxG$zo43ahTu6A#AJm)R zu07W*?prZ^5pe800#+?c%e|?rNE%Y)JlcRoh-S_nW0={F`Q;m9P88g;@yK6{ReO1o zP-ZnrOoRk=zN7K6uGa3#aC)#<1^7Hw9kL^Egv9sDV>F>1kB78eDf=(H&%v3Ha;#N= zqG)tx6qk3{>9({D?#6PxoRRWF$lhJ#n<{GZw_wjY{G?iH6t+PQu}1Ok8HKZ!#Itx!hIFCG`_xrE_1{Rc=pjXWYBaU zzG2xGRQk5slGv8E{K`{`KN2n_C~(CN2X^WSft8MOF5VAnow|iy9>F^DI^^B1Nd}gs z-|f g-+}E9@0ErZSRG7N^%r>Y@e>0g*Oe|;oWj!n1-PY&BLDyZ literal 0 HcmV?d00001 diff --git a/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-regular-orientation-both-horizontally-flipped-1-snap.png b/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-regular-orientation-both-horizontally-flipped-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..f67fc40e25d78962a70869092b83550a417ff6e4 GIT binary patch literal 8433 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 z z%8i9bU~V@np2_X+bp_6 zKDE1I-CIh#g&2kI14HF-U4so;(Dj|!a0C9)-E3bQ41bPo`KltFKXd8yV6U1<0 KItur((*FbUV5O)4 literal 0 HcmV?d00001 diff --git a/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-1-snap.png b/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..5248cffabf8cbecbf98b2887fe4bda3b3f50825a GIT binary patch literal 9262 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 literal 0 HcmV?d00001 diff --git a/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-both-rev-1-snap.png b/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-both-rev-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..2e0a3765c69149f8437828b5b3c9018a2e733e18 GIT binary patch literal 9336 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 literal 0 HcmV?d00001 diff --git a/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-rev-1-1-snap.png b/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-rev-1-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..038be94030f4690fac5fbd7bda4a8163d1be74c4 GIT binary patch literal 1938 zcmYjR3sh5A7QHV7`~3&i+M?=KR=r1F^8&&Ll|R+vc)wci#6o&h5))t+`av&Y&Mk zFYcjI|6M|*wifE9cCj}eJWNgYaQULlkrXqOGB*xyc!ZD^NcN#PZ%`WSJF!M;iN zGSQmMn)77Y5cSs}EUJSq65NoSa$^LGNB*}q=Xf)^?$KpYGBIg~Aqi>b!mU246o)L( zms>>7|NowEx%*B?0rXNxx9nk`qtmLH+dJFcCyKp>eQguX%Ek%7gZM2IO&pesVzn6ied4`d1&~0 zU~`@$m$lml%^H!AM~FJ;LDvD=hfRr}B#do3KqNLC#0U&6U%U_44l0D>gP&EB2rj!B zi1jC~M7eAT;rbwe`ClH8$K1=B>DCBj%q^mAuN}y1=CbS;RmSFw7)bD*L6nf>ABAx2k@#lqUQ;w-2GX!d2F~IitwGA z_-U-^z*ZqTOR_=K(SFIu&uQ{wsn2uGm^^&nUpqgMV6`WEowNavc+kb_ThOQS$e^`} zf^9nE9m%>+aA5F|Fp0Ae|N6qc$aoU0v>pO69xka^(^)p$42}6Y9swdwgb#5{ObMGw z>+vVDOKyG(LD5`R3wgZPtalA^u94JB_W()PvLmYwDDp7CJXWht|8$aFYYA~U%3yYt zQ$fq(-xgil0-ME6uhLdykUJ>E`0SeUB~Ue)M(1&)XODxmHv%cj^F})vn?S7QAlVy zp}}oy*%95YuJ&Z0LQt(d$?lG4*6qq+?B)8K*PDU?L?3`iOIDdkP7Sw}`& z6t-n21365ECZ;ZgV+3aZ2M^lHz=IrsP)qdt97f(zq&12ulR;_46ReHc- ze@F^9QSR~QtA$&cE^2zEz1vNb@4{a5VcUUQE1S5ZTlr2QQc3`Uw z`T;*-CE|VR^ovtd$2A#SkOmyae3qrOf(|&`9-V<%HdsMo8P}8^qmEB6bqjO#nMFXe z`)Z1oDZL4Yyu|ZN^SHV}Dx;65*UZnn8?nn@N6wtgOzfV|vH^XU;GoROKX6$D;yRvF zt*r6>if*}hOgOGFKac(n1)DD37tyyD!&-g7AQqL2%5Ut+P*eBKU`L#I&-|f#9JwX5E_8lo94%}^XrJhUKC=vHg}8~&H*|ep@u~)@hL5d^jf?8n=d9M);Z-~| zim%k=wM83|cr`d;N{MS**8wr)R06?i|MhL8#kOBG*8^S?)&ZI*lBH+p7JP!^S9WN| zxN8?`Wx4)@-fQ5)U!O=0Y`a3sP;dD;wmu9x)ECJ6ty?iCj@Je>%JDihHPQwr((^{yA6(j(9{Q^2RftlKC)rN9`cAOAyuY!ZIW%#Ud!xq zr^wy1>j`*%$6zh?UDC&s?w(M~)(^Q<74%T${@UbJG5E6(ia7CzH8dVY3v>EfcZM(w kiB0(Ga^AC-G3HNFEaq&CUA36PM{pfI;_uz@X;kii0OG4NJ^%m! literal 0 HcmV?d00001 diff --git a/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-rev-2-1-snap.png b/products/jbrowse-web/src/tests/__image_snapshots__/synteny-test-tsx-switch-rows-regular-orientation-both-horizontally-flipped-rev-2-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..52dc216a523c46450c0b6dcf7bc0df991882a330 GIT binary patch literal 1931 zcmYk73se(l8ixN#fM7Hs2uL9mVp{?Q7A>Gi4KN^=MG=BUj1Uo&Qltt?Pzsd9DHf$x z77B8SB1E);Mdad&i`>G2V%9*(9wat$v6jF+kK4hK+8E6$NfM`s=3hRnVoM{V7mt7N~+KC|5RM< zpwV(JD-^8jcca6YjkCs)p9fJNeCJ7uEOOI$IP(vzo0<*9ha>ugbU$qI+!-AQ22Zh*v99RX%B`N>Uq29TK2-^0%O znH5(@9qR3!WECing1YNE4D7630!oDU?=C*xXi55Q@9VHOxmQ?3?hoqarGXvSe#L;o zl*z!ll?{i-9rM;VT@&e|wJ@;TZlI3up^rW#u9cdWF1?s-l%~9$W0B`6QBC?Rkh5Qf ztyGGvVFG)ox!w-((NeKALdT#iQu9`o<^v44q&Nj%TKoO9$5)kKsuDdYKpqTxzvZnf zA3Fnj2wA^wZLaZ`{g@HEj7a%J=J(}yZnUqQ&f8MiIYniGo-E=})tsI>J;CMlI!ny; z!Cz`p@&;N8XD(^icBb9?+Ggi=;xJ7gJzDK13x9q2_~u{6S3}=`s4^&m6fQI(oYt^0 zU|d&*@6D9XXtI(4)+Cq(#)>0k0o&k4_K=E zK#aHc0l}xa_ZQ~5umYQCXynT13H4)5JxtIGW$SKmE{gz7rGt%Dz~w`t$xCN8I8AD+ zQ-jBn7DR3HQxu8WIRwD?T$3d#e+d{41wiRio`O|45WNs#Tog_P? zyET-cwaN#Iuc!qT5GHH;Lk+5QebBa$($@RY8L@DLRdB)iA*(ConFUF8b+@A2)xf!N1%A&w718~JKU(fSLb&!o7M<*T315W$r3r-$_ z>^HmGX89C}3&jNe#D5qo?bS~1RE4yYBrdSulANjckC7V+Pe6no5(9AiLr>i~^g56t z;hcj3?Y*wG{A-^UQ2IYhy9J$2gU)?o)1Z0i2_Ir<1vKfr+iu-GP_OSZDakhW;+*~d zn64Z>4Dz z{^t%|t|FQw3u}as@piygy_l-y0jl!<{;<)ge|+}4Pg__!DQdG@3!pCDr78=DrXt5~ zGeDI$QKe+KcvX8h!>KX=L^VPIng1M9t+(gBTeTV{mZb({+qjqcG}(UW+NKidykhI9 zX##p%t>pM4m^d6RbmcBEyC)GJOvvvD&V-AiImyo+PR_v&!dMX$gPWdC`M8|0NC7KU zC^ajl#fGAb!4VgrNyLNsO4E$$iPaGN1ah6iA_n+sIT*hO@AqQo+(0lD;rtWXNU$Bx z(Ml)E3{Klu`^NgE7u&78dCN+CB;XlC{UHxDFWbKv^_~u-h~W_j4m7RGs+}y@=E@8r zfje(2?E(jTy&T7r2Ms_`CKU6?cC_##n1*nfMg$nC4hFzIEfpO6Q`lM!o@R-M-YXI^ y888NJ!{`XrYVj;0b%Jjk>hjT5b_S>mU8UU{inb10@mzp!R}kPE () => {}) + +export function App({ search }: { search: string }) { + const location = { + ...window.location, + search, + } + Object.defineProperty(window, 'location', { + writable: true, + value: location, + }) + return ( + + + + ) +} diff --git a/test_data/grape_peach_synteny/config.json b/test_data/grape_peach_synteny/config.json new file mode 100644 index 0000000000..7f54b0aed4 --- /dev/null +++ b/test_data/grape_peach_synteny/config.json @@ -0,0 +1,57 @@ +{ + "configuration": { + "rpc": { + "defaultDriver": "MainThreadRpcDriver" + } + }, + "assemblies": [ + { + "name": "grape", + "sequence": { + "type": "ReferenceSequenceTrack", + "trackId": "grape-ReferenceSequenceTrack", + "adapter": { + "type": "ChromSizesAdapter", + "chromSizesLocation": { + "uri": "grape.chrom.sizes", + "locationType": "UriLocation" + } + } + } + }, + { + "name": "peach", + "sequence": { + "type": "ReferenceSequenceTrack", + "trackId": "peach-ReferenceSequenceTrack", + "adapter": { + "type": "ChromSizesAdapter", + "chromSizesLocation": { + "uri": "peach.chrom.sizes", + "locationType": "UriLocation" + } + } + } + } + ], + "connections": [], + "defaultSession": { + "name": "New Session" + }, + "tracks": [ + { + "type": "SyntenyTrack", + "trackId": "subset", + "name": "subset", + "adapter": { + "type": "PAFAdapter", + "pafLocation": { + "uri": "subset.paf", + "locationType": "UriLocation" + }, + "assemblyNames": ["peach", "grape"] + }, + "assemblyNames": ["peach", "grape"] + } + ] +} diff --git a/test_data/peach.chrom.sizes b/test_data/grape_peach_synteny/peach.chrom.sizes similarity index 100% rename from test_data/peach.chrom.sizes rename to test_data/grape_peach_synteny/peach.chrom.sizes diff --git a/test_data/grape_peach_synteny/subset.paf b/test_data/grape_peach_synteny/subset.paf new file mode 100644 index 0000000000..8a17a1e5c7 --- /dev/null +++ b/test_data/grape_peach_synteny/subset.paf @@ -0,0 +1 @@ +Pp01 47851208 28845209 28845603 - chr1 23037639 315959 316369 330 413 2 NM:i:83 ms:i:328 AS:i:328 nn:i:0 tp:A:P cm:i:6 s1:i:46 s2:i:0 de:f:0.1791 rl:i:921840 cg:Z:145M2D3M3D1M3D5M2D6M2D7M1D6M2D12M2I9M1D2M1I160M3D35M diff --git a/test_data/volvox/config.json b/test_data/volvox/config.json index d01096c090..acd6aad470 100644 --- a/test_data/volvox/config.json +++ b/test_data/volvox/config.json @@ -155,6 +155,24 @@ } } } + }, + { + "name": "volvox_random_inv", + "sequence": { + "type": "ReferenceSequenceTrack", + "trackId": "volvox_random_inv-ReferenceSequenceTrack", + "adapter": { + "type": "IndexedFastaAdapter", + "fastaLocation": { + "uri": "output_prefix3.simseq.genome.fa", + "locationType": "UriLocation" + }, + "faiLocation": { + "uri": "output_prefix3.simseq.genome.fa.fai", + "locationType": "UriLocation" + } + } + } } ], "configuration": {}, @@ -2025,6 +2043,22 @@ } }, "assemblyNames": ["volvox"] + }, + + { + "type": "SyntenyTrack", + "trackId": "volvox_inv_indels", + "name": "volvox_inv_indels", + "category": ["Synteny"], + "adapter": { + "type": "PAFAdapter", + "pafLocation": { + "uri": "volvox_inv_indels.paf", + "locationType": "UriLocation" + }, + "assemblyNames": ["volvox_random_inv", "volvox"] + }, + "assemblyNames": ["volvox_random_inv", "volvox"] } ], "plugins": [ diff --git a/test_data/volvox/output_prefix3.simseq.genome.fa b/test_data/volvox/output_prefix3.simseq.genome.fa new file mode 100644 index 0000000000..8cfeff68c1 --- /dev/null +++ b/test_data/volvox/output_prefix3.simseq.genome.fa @@ -0,0 +1,2 @@ +>ctgA +CGGCATTGGTTGCGGAGTTGAACATACGGCGCGATTAGGAACACTTCCGTCTCTCACTTTTATACGTTATGATTGGTTCTGCCTTGGTTAGATTGGTAGTAGTAGCGGCGCTTAAATGCTATACCAAATTGAGAACTCGAGCGCCTAGGCAAATCAGAAGCGGTTAGTTATGAGGGATTTAGGCGCTGCGGCCAAAGGTACTTCTTATGACACGTTGGACAAGCGGTTAGAAGACCAACCTGACCACCAAACCGTCCATATTAAACCGGTTATCTTCTCGAACGGCGGCTGTGGTCTCACCATGCAATTTAAACAGGTGAGAAAGATTGCTACAAATACGAGACGCTGTCACCGATGCTGTTCAGTCTGTTGCTCCTGGTCGCTCCGTTGTACCCAGGCTACTTATGAAAGAGCGCAGATACTGTAGGACGGTATCGATCATGGTAGCATAGCATTCTGATAACATGTATGGAGTTATCCGTCGCTGGGGCCGGACGGTCCGTTTATTTTGACCCTTCTGAAATGATTAGCATGCAAAGATTGCGACTAGTACAATACGGTACATCGGTGATTCTACATCTGGGTACCATAAACAAATGTGACGGACCGAATTTGGACGGTGCGTTCTCAAGCTGGTTGACGCCTCAGCAACATAGGCTTCCTTTCCCACGCATCTGCGAGAAAAGCAACTCAATTAACCCCTTGGTACCGTGGGCAGCATTCTGTCACGGTGAAGCCCAGTTCATCCTGAATCGCCGAATGGGACTATAAAAGTTAGCTTTATCTTGCAGGCTGCCGCGAGTGGGATGGGTTGGTCACAGGAATCAAAGCGGAGGCACTACATGCAGCTTATTTACGACGGTATTCTTAAAGTTTTTAAGACTAATGTATTTCATGGGTAGTTCGGTTTGTTTTATTGCTACTACGGCTATCTTGTAGACGACACCTACTTTTAGCACTACGCCGAGCGCAATAACCCCCCGGAAAGCAACTTGCTACTGGGAGGGGGTTTATCATCCATTACGGCTAGATAGGGGTTATCAGTACTACCAAGAAGTATTCGCTAGGAGGGTGGATATTAACAGTGAGAAAAAGTTCGGACTGGGCATGAAACGTGTGTCAGACGGAGCTTGAGGGACTGAATGGGTTCCCAGGCCCTTGAGGTGGATGTCACCTCGGGTTACGTGCCTCTATTACAGAGGTATCTTAATGGCCGCATCCAGCCTTGTGGCTGGGTCTACGTACGCGTGGGCACCATACGTATGTCTGGCAGGAAAGGTCAATCATGCTTGTTTCTCGTCGCAGAAACGTTCACATACTATTGGCTCGCGGGATCGAACGGGCCTGATTATTTTTCCAGCTCCTGCGTTCCTATCACGCCAATCTGAATCGCTAAAAAATGTTATAGACATGCCCATTGCTAGCAAGGATGGAAAACCGCATTCTACAACACCCTAGAATTACTTCAGCACTAACTCTAAGATACCGGAAAACCGTAGGTGAACCACTTGTTGAAGGGCAATGCCTAGGTGCCTTGCACTGGCGATTCGTGGAGTAACCTTGCTACCATTTCACCTTTTCTAGGTATGTTACATGCGATCGCCGATTGTAGTCAGTCGTAACGCAGGAAGGAATCAATACATGACGGTTTACCGAAAGCTATGAGGGCGCATCATAACGTGCGCTCTTTACTAAGGACACGTGTAGTCGGCTGATTTCGCGCAGAACTATACTTTCGTCTCACCGGACAGTGACTGCTCCGAACTTGGGGGCACTAGTAGATTCTAGCTAGAGACCGCAAAGAAACGCGAAAATGCCGACTCGCTAGGTGGCTATGGCGTCCAGCTATAGCTTGTGAGTTTTATGTAACGACAGCGATTCCGCGAAGTTTTCCCACGACACGGAGTCGTGACAATCTATTGAGAGGAAATGTCAGACAAAGGCTACCTCGCTGATGAGAACGATCCGGCGACTTGAGGGAATTCTACGGGGAGATCAACCGTCTGTTACCAACGAAGACAGCTGGTGCGCTCGTTAGCATATGAAGGGTATTTCGCAGCCTAATATCTGTTCCGTTGCGCTAAAACGCATGCAACGGCCGCTTTTATCACCGGACTAGCTATGTGCGAGCATCTATGTTAACGTGAGATTGAAGGTTTGTGAACTTTGATGAAAGGTAGACTTCAATTACCGAGTTACAACGAATGTGGGGGCCTATTTACTACCCACAGGTATCATAATGAACATCTCTTCTGTGAAATACTGCCGGTACGCCGAGAAGATCCCCACATTGCGTGTCACCAAGACCCAGTGTATTGGGCGTCGCTGGTTATAGTATCGCGGAGGCACCCGATGTGCTGCTAATCGACTACAAACTCGCACCACAGAGGCGACCGGTGCGGGATAGGGGGAACGCGAAGCCACGTGGTGGTACCGCTCCTGATGATACGAGCATGTGCTGGCACGGCCTGCCCTATGTCCAGCTAACAGGTGTCACATAATATCGCATCTATAGTTGAGTATGCTTATCGCGTGAGCGTTACTTGTCGATTCTCGGGTAGAATCGTCGTAGCTCATTCTAGTCCTTAGCGTAAGCAACGATATCCCGGATATTATAGGGGCAATAGGTCCACCCACCACGAGGTGTGACGAATATTTAACGCCCTAAGTACTAGTTTAACGGAGACTATAAACAATTATGCACGTTGAAAGATTAGGGTACTGCACACGTGTCACGTTGACGATGTAAACACCATCAATAGGTTGTTCTTATGCGATGCCCATACCTGCGGTGCAACGTCCTAAACTTATGCGAAACTTCTAATTCAGCCGACGTTGAGAACGCCAGTTTTTCCACATAAACGGACGACCTATTATCTCTGTAGTACAAGGAAAGCCCAGATCAAAAAATTGGAGTCTTGTCCGTAGCTCTTGGGTGGAGAGTGTACCACGAAACCTATACTAGGACTCAATTTCTCGTTCCACGCCCGGACATAGCAGCCTAGTGTTGTAGGTAGGCGTACAGAGGGCTCTATCCGAATAGCACAAAGACTCACCTAGTGCGTATAGGTCGTGCTCTGCGGTTTACGGCTCCACAGAAGAAATACCCACTGTCAGCTTTGTCGTGGGGACTTAGCCAAAACCTGAACGCAAACCGCATGGAGCTCTGTTTTGGTGGCGTGGAGTGCCCGCCTTTATGCTGGGTCAAAAGTACGTATATGATTCGGGGTCGGGGCGAATTGGCTCTTATGGTTTAGTAACTAGTTATTGTGCATCAGAACCGACTTATGCCCCGATTTTGCACCTCCCGAGAGAAATGGTACTAGTCATTGTCTCGGGGTCATTTCAACTCAAGGGATAATCTGCCACGCGGTACCGCCGAAATCCATCCCGAACGGCTGGTACTCTATGTTCTGGAGACCGAAATGGAGCCAGCTGCTATCAACGGACGCAAACCCTCGCTCTTTTCTATTGGCCCGGAGCTATTCGACTAATACTTGCGATGGCTGGGTTCATCTCTCTGCTTCGGCCGCGATCCTGCTGCCTATGGCCACAACACGAAAGAGCTGGCACGATCTTGGATTAACGTCCTCCACCATAGTCCAACGGACAGTGAAATAGGAGAGCGCACGGATACGAGTACGACGGGCCGCCCAATTACGCTGGTCCTTGTGTTATACTGTTTACTTGCCCGGTAAGGTGTACAGCATTGTACCCACCGGTGGACGGACCTTAGCGTCGTCTCCGCAGGTGTGGAGATTTCGCTACCTGTTGCATTGGGGCCTCGCCTTACGTTTTTTTCGACGGAGCGACTGCGTGCCCGCCAGGCCAGACCCTCATCATGGGATTTTTACTGCCTTGGACGGCAGATTCTGATGCTGGTAACGCGCTTGGCACACACCCTGCGTCGTAATTACGTATATATACGCTATCACGCTCTATTTTACCAGGGCGGGCCCCGCCTCAACGATCGGTAGTCTCCGTTAGAACAGTTGCTCTCCGTTAGCACTCACAACTCGGAGAATGAGCTTACACAGTCGTATAGTTCACTTTTTCCGGGCGACTTACTCTCCACAGTTCCCTGGAATGCGTTCGTACAGGGAGCTCCTGGGTCTAATGTGCTACCGCCTAACTTCGTTAGTGGTAAGGTCTCGTGTGCACCTCCAAATTAATCGCTACCAGTTTTCTATTAACCCCGAAACGCGGTTTGCTACGGGTTGGAAGCGAGTACCGGTTAAGGGAAGTTACGTAAAACGCCAACATAGTGAGGATGCTTAAAACACTGCATGCGAACAACGACCGAACGAGGAGCCATAATAAACAAAATGGATGGTGGTTAGAAATCCGCGCACAAGTGTCGTGCCACTCTCGTAAAGAATGATTGCTTAGCTATCTATGAGAAACCTAATGGCACGCACTGTGTCGCCGAGGAAAAACCACCATCGACAAGACGCTGAAATCGAGAATAGACACAACGTGCAGCGCGCGTACTATTAAGCACGATACCAGCCATCTTGAGCGCAGACGATGATCAGCCTTACGGAGATTACTTCTAAAGCGGACACCTCACGTAGCTCTCTATCGATGCATTGCATGCGCATGGCTTGAGCGAACCACTGCTAACGCTTGGATGACGTTGTTAGTCGGATTCACTCCATACTGTCCTTGTTAATCATCATAACTTCCCGAATTAAAACTCATTTTTCCTACCCTCATTTGTTTCATGGCGGCTGATCGACAACAGGCTTGTAGCGCACGGCTCTTGCAGTAGAATAACCATAGTCTTATCGCAGCCGCAGGTCAGCCGACAACACGCTATATAATATATCCCAGACTTCCACTGCGGTATTTTGGATCGTGGCGTCTGGGACAGTCACACTACTTTAACGGGAGCGCCGTTCAATGTCACTGCGAATGAGTCCATCCGCAGTTCGGTTGCGATTTTATTCTTCCCTGCGCCATCTGTCCGGATTCCTTCTTCTTGTCGGAGAATCGCCACTGCTCGATCAACAACTCATCAGTACTTATTGACCGTTTCATGCTATTACACCGGTCACACTGATGATAACGCTGCGGTTTATCCCCCCCAATGACGCGCACATCAGCGTCTTCTTTCGGGTGCGTCCTGTCGTACCCCTATTGCTCATGCATCTCCCAGCCGAGCAATGCTTTGGACAGGATGGTAACCAAGCTGGAGTGGGTTCTAGCGTGGGTTCGACACAGCTCGGTTCGTATAACCAACCGCATGCGAACTGCGAGTGCCTCGCCCCGAGCGCCGCTGTCTACTTTGTTTAGAGGAAGAAGCAACAGCTGCACCAGCAATCCGCATCTGCATATAGGTGGCCGTCTCCTCCGTGGCGCGCCGCTACGTTTAGCGCACGTGCAAAAGGCTTGTTACTATAGTGCGTCACACGTTGGTTCGGAGACGGTTCACCTCGAGCGCGCTATGTCGGATCTGCGCCCCCATGAGCGGCCGCTGTCCGGCGGCACGAATAATATAGTGCAAGAAAACACCTGGGAAGACTACGGTTATATATGATGGAACCCTCACAGCATTCTAAAGGTTTGACAAACTTAAAAATGTGGGCCGCGGCCTGCTGTGAATCCCGGACGGTTTGGCCAATATAGCCTAGGAGTCTTTGGAAGGTAAGCTTTTCCTAGATCACCATACTATCGTTGTCAGTGGCCAACGGTTTCGTTAACTCTCGTCGCGTACCCAGTATCGGCGCAATAGGCCTTTTGTAACCCTTGGAAGATTAGTCGATCGTAACGTTCAACCCAGGTGAGGGCAAACGCTTCCGTCGAGTTCTTCACTTTCAGGTACGTGCCACTTATCTATACCGGACACTTGTTAAGCAGTGTTTGCGGATGTGGTTAAATTTGATGGCAGATTTCTGCAACCCACATATTGGGTGTTTTAGCTATAGCTGTTCCTTCGAGGTCGTCGTTTAACCCTCCTGTTGCTAGATCATATGTATCGTTGTTCGAGACGCTAGTAGTCGCTCCTGACCGAATCTTACAGGGTTCGATCTCTGCTCGGCTCGTACGACTTTGCCGCTGGGCCGGTGCTATAATTATAATGATAAAGGGAGTCGCACAACTGCAGATTACTGACACTTGAAAGACTTGAAGCATAGTGTTGAATAGAAAAGTATACTGTGACAAAAACAGGGGCCTGTGTCAGTGTCCTCAGTGATCTGGATATCAATCACAAGCCTTGTTAGCAGAGATGGTTTCTGCCGTTACGCATAGTGAATGGCCGGACTTTAATATTGCCCTGCTTGCGCTAGCTAGTACTGCGGGGGCTCTCTTTCCCCCTATTGATATTCTGAGCGGGCAGAATGCGGGTAGATCAGCATTCATGTCAGGCTTCTATCAACGTCTCATTCACCCTTGGAGTGTGACCTACGTGTTAACAGGCAATGTAGCCACCGAGAAGCCCGTTCAAAGACAAATAAGCCCGAAATTAAACAGACACGGTACTATGGAGTGTGGTACCAGTGTTTGGTTTCAGCCTGTCGGTTAACCTCGCAGGGCTAGGAGAATGAGCTGCAAGTAGGTGATTACACGAAGTCTTCCCCCCCAGAAAGGGAACAGTCTTATGAGAGTAGAGAATGTCGTCTTATTGAGTTTGTCTATGCACTCCTATTATTCAAGAGCATGCCCACCGAAGAGATGCGTCGCTCGTAGCGTAGACGTCGTAATGACCCAGTGGCGTTCGGCCTATTCGCGTCGGACGCGCGCGCTACTACAACGAGAGGGTTCTGAAAGTGCATGTACACTGAAAATTCTAATGTTGAGTAAACAACGCGCTGACCAATTGTTCCAGGCTTGTGACATTATGGCTGTCTGCGCCCGCTCAATGATCATCAAGACGTTCATTGTATGTTAACGCATATCAGGTTTAGTTATCCTATACTTTTTACGCCGCGCGCTTGGAACAGATAATTCTCCCAGCGACTCGCGGTTTCAAAAAGAACCATGTTATACCATCCTGTTCCCAATTCTGCCTGGTGTGCGGAGAAAAGACCGCCTCCTAGATAGCGTACCCGCTCGGTATTGGTAAGGTGGGGAAGCCAAGTCGAACGCATATTTCTTGGTTATATCACAGGCCACGTTCTATACGGAAGTGGCCGGATACGATTTGACGTTCTACCCCGAGAGCGCATTCTTGTTTGTTACCTATCCGAACGCGGCGCTTCTTATTCCAGATTACCCAGAGCCGGAATGTGCGGCAAGCTTTATCCATGAACAGATCTGGCGAGACTCCAGCAGTGCTTAGATGCAGGTAGTCCGGCCGGGAGAACGTCCGTTCAGTGAGTGCGGTGCTTTATTTCAATCTGGACCCGACTGCGCTCAAGGGTTGCAAGTTGTTGTGCGCCCAGTAATAGGCGACTCGTCGAATGGTCTGGTACATTGCATTTTCATCGGTACGGCGCTTAAATAAGGCATTTCTCACCCCAGTAAGTTTAAGGGGTATCAGGACCCGAAGCTTCTCTCCGCTGTCCATATTGGAGGTAGATCATTGTGGATGTTATGCAATGAAACTAAAAGTTATGGTGCTCCCACCGGGGCGCTCTAAAGAAGAGTGAGCTAAATTTGATACATTTAAATTGCTATTCCAACCCGGAGTCCTGAACCGGAACAGTAATGAAACTTCAACCATGCCGGGACAGACCTAGAAGAAGGGAAGTTGCTGTATATGGGAGTAAAGCATCAATGGATGTGGTCCGTAATGGTGCCGGAAGTGACTATAGATGACGCACCGGTGGCATGCTATAGTATGTCCTACGGCGCCGCCGGACCTCGAAGCTGAAATCTAGACACCGGCACACCACTAGCCGTAGCGACGGTGCGCGGCACGCTGCCCTGCGCGAAGGCCGGCTAACACGCATAAAACCCCCAAACACTCGAGCTACGCGGGAATTCACTCAGGCTGTGCTTCGAAAACGTGTAGTCTCATTACAATAATCATAATACCTCCAAGACTCTGGTATGACTCTCTTACCGTAGGGACATTTCGGGCACTAGGGAAACGGGCCTTAGAGCTGAGGACTTTGAAACGTCGATAAAACCATCGCGGAACTAGCTGCGTTAGAACTCCATACGGGTCGCAAGCTTGGGTCCTGTCCCGGCAGCTGCAAGTGCTACGGCAGGAGGGGATCTACCTACGGCAGAACGAGCCCCTGCCGTAATGAGGCGCGTCTCTAATTCGTCAGTAAGTTACTATGTCCGGAGGACGCCTCTACGAGTTGACACTCTGGCTAGGCCACCTGTCCGCCGCTCGGGTACCCCCATCTGCTATCACAACTCACCTCGCCTTACGGAATTCTTGGTTGCCAGTCATCCGAGTCACTTAAGCAGCGTGGTACATCGTAATACCGTTTCGGGCGCCAGCCATATTCCCAGCCCAAGCGGCTGCATAATTACAGCGCCTGGCACGCTAATCGATCCCACAAGCCTTGGTAGATGACCCTTAGCCCTAAAGCGCCCTCTGACCTAAATCTGCACGTGATACTTGATTATTTGTAAATGAGCAGGACAGGGTAGATGAACCAAAGATATCTCTAGGTTTGCACAATTGCAGACAAATACATCCGCGCAAGCCCGGACGGCTGTACTCACACAGACTAGTCCATCTCCCTCAGTCAGCACTAAGATTCCCACGTGACCAGGGCGACGGGCTGCAGCCAAACGTATCTTGATATCTACTTAAGTCAAGGTTGACTCCGAACCCTATGGGTCGGGCCACAGGTCTTATTCTCGGCGTTCCATGCTTTGTTTCAAACTCGCTGCTATAAGAGCGTGAAAGCGCTGGAGGGCATAGTTTATGCCCAAATTGCCGCGTAGATCCGTCGGGTATGTCGCTATAATAGGACTGCTCGAGGTAGGCGGTAACGGCGTCCCGCCTTCAGTAAAGCGCGCGGAACTCGAATCGAGTTACAGGACTTGACCGAGGTTCATATCCAAGTTTATGTTACCCCACGCGATCGGAGTCCGGGATGCTCGATCTGCGACGAGCGGAAGACGGACCGAAATATCGTCAACCGCGCGCAGAACCATTAGCATCTAACCTTTAGCCTGGGTGTTAACGGGTGGCTCACTCGGCGGTTCCAGTACTCTTTGTGTCAACTTTCCGACAGTAAGTTGCGAACATGATGCTCTTACGTGATTCCCACAGTTTCCACTCAGGCATGCTTTCTTTATACACCAATAGTTATGGCGGCACCTTCAGCGACTCCCACGAAGGACGGCTATGACGTTCACCATTCGCCGCAAAGGCTCGCTAACGAGTTCTGTAGTTTGTTCCGGGCCAGATCCGTGCAACAGACCCTCTACCGCTGTTATTAAGTTACCAGCGTTGTCCTTACGACATGGTCGTCGAGTACTCGCGTAGTCAAACGCCAGGACTAGCAGTCGACGAGCATTATTGTCCGATACTTACTACAATACTTACCGATATTGACGGATCAAACTCCCTCCACTCAGGTCCCTACGTCCGGGACCGCATTTTGCATATATTGGTTCCCAGAGCGTATCTAAGTTAGTCTTTGACCGGTTCACCGATGCCTCGCGTAGAACGATGCTTCTCGTTACCTCAATGATAGGCTGTAACGTAAGTGCATTCCCAATCTCACTCGTGCCTTGTCCACCTTCCGTGAAGCGAAGCAATACCGGAATACGTGGCTTCGTAATATTTTGAGATATGGGGCTTCGGGACGCTCAAGACTTCTCATGACAAACAAAGATGAAGAGACTGCACCCTATCCCAGAGTAGCTGATGGACTAACGCTAATTTGGTCAGGGCAGCTATCGCATCCCGCAGGTGTAGGCGGAGACTTTTTTTTTTGCGGTTGACAGGTAATCTCACGGGGTATAAGCACGGTTATTTACGCAAGCGACGTCCCTGGGAGAATCCGCCCAGAGGACGTAGGACCCCATAATCCATAAATACTGCGGTCGAAACTTCCTTCGTGACAGAAACGCTTCTAGGGGATGCGGTCCCCGCATTCAGAGTTCTACTTTGGCCAGCGTGAGACTTAACAACTCCCTGTACTTACGCGGTACATTGACGTCGTTCAGTTCCAGTGTGACCTGTTACCGAATAACGTGTTCTGGATGGGACGAATACGACTGAAGTCCACTCCAAAGCACCTTTTGGGACTTTCCAACGAGCCTGTTGGTCCGTTAAGCGGTATTTAGCAAATAGCATCACCCTTGTCGGTAACCGAACTACCCATTGGGATCATCGTGAGCTCGAAACACTAGAGCGGACCAATAAGACGAAGATAGGTAACCGGAGCCCGGATGGACATTTGATTCGGCTCTAAAGCTTGTCGCAGAGAAAACTGGGGGGAAAAGCGGGTTGTTTAGTCACACTCCTCATCGGCAATCTACGCTCGCGGGGAATTGACTTTAGCGGCCCGCTTAGACAGGTGTGGGACACTAGCTAGACACGACATAACAGCACCTTCCTGGCCAGCCAGAAATAGTCCTGGACCCTGACATCCAGCCTTCCGACGGACCATAATGTGAGCCGTAGCGCCCACGACGATCAACGGGAGAAATTTACAAAAGGCTGTGTGAATGCTACGCTCGGACTTAGTCTACCATTGCCAATCGAAACGAAAACGCAACGCACAGCATACAAACGTTGTACCATGCCGGAGCGGTTGATCAATTACAGAAACAGAGGTCGGTATGTAATTAAACTATGATCCGAACAAATGTAGGTTTACACAGCTACGCTCGTCCATTGGCGTACGTGCATCGCGTGCTAATACTACGTCATGCCGGCCCGCGATGCACGTACGAAAGGAATACCCTGTCTGCCCCCCGCGCGAGTTACGCTGTCTACGGACGCACATACCGAGCACTGTCGTTCGAAGCCTATTGAGCCCAGCCGAGCTCCTTATGGCCGCAACGCTGGTGCGGCAGCTGATAAATTCCACAGTACACGATCCTCGTTAAGATCTCGGCATAGTAAGTCGTCATCATTTCACATGGTTAGGAGAGATAAATACATGGTTCTGGTACAACCACGGATTTGTAGGAACCCTTGGGCCTGGTGAGTGCTACATAAAATTCTCCGGTATGAGACAACCAAACGGGTGCTGGATGTTGATTCCCGGCCCAGAGTTAGATGTCCATATCATTCATCCATTGCCGACCGACCCAATGCCTAAATCAGAGGCGCCTAGCTAGTTCTTGTAGGTGTGCCACGTCCGGCCACGCAGACACGACCCGTCGATTCTTGGCGAGTGTCACCATACCGGATTGGCATCGAAGTCTTGGGAGAACTCAAGCTTTTGCACTTTCTGGGAAGTTAGCCAGTTTGGTGTGCGTATGCTTAGACAGTCAAACGCCCCTGGGACGAATTGCTAACCCTAGTTGCCACGCCGGAACCATCTTCTTAGGGAGAGAGACAAATCCGACGTTAGATATGTTCATCTTAGCGTCTTCGTGAGTCTGAAATGTATCACTCACCGCAGAATACGGCGAATGTCTGTTTGCCCTGGACTGACGGAATTGGCTTCAAAAGCCGACTAGATTTTGGTACGGTTCTATCCGCGATGTAATTACCTATCCTAGGTATATCGCTAGAATGCGAATAGCGAGTACAGTGTAGCAAGGCCCTTTGTTTAGCAAGTGAGTTTGCTCTAAACAGTTGTCAAAACGTAGGCACAATAGTGCGATTCTTCTAAATCCGGGAAGCTCATGGCCGCTGGGCAGAAAACAGAATAATTCAACCCTCCATTGTTCGTTTACAAAGCTCGATGAGTGCCCTTGCTGGACGAGGTTAGGCGGTTCTCTCGAGTTTCTGAGTTGATGGCTGGCGGGAGTAGGAGCACTTCCTACAGATTCGGAATTGCCTGGAGGACAGTTGGAAATAACCTTTGTCGATGCATGAGGTTGTGTTCTGGGAGTAGGCTCCCAAGGTAGCGGTCATGCTGGGGCAGCCCTAAGTTTGTTATTAGTGGCAGTTTGCGTCTGAAACTACATTAGCATGAGGAAGCTTCCTAACCCCGTTTTGCTCTGGAGGGATCTTCTAAAGCCAGGTATCGCCCGCTACGATGCTCGGAGCGGTGGTTCACAGGCTACCTTGCTCAAAGGCTTAAGGCTAATCATAGCAACAAGTGCGAAAGGACTCTTTCAGATTTCGGAAAGGGTGACACAAGGGCAGGGCGGTCCTACCCTTCTGATTCCCTACCGTTTTGTAGCAAGGGTCAAGGCTAGGTCTTATACGTCCCGAACCTTTAAACCACTATCCCACCCCTGAGGGGGGAAGTTGCGCGTTAAAGTATAGAATAACAGTACACTTATAGGTTCTCTTCCGGCGAGCCGTCATACAGCACCGAGCGCCGTTGAACGCGGATTAACGCGTATTGCGTGCAGAAAATAAACGCTCTGCCAGCATATTGGAGTGTCGCACTTGAAATATTGAACAACACCGCATTCAATAGCCTGATTGAGGAAGGACCGAATAGTAGGGCTTCACTACCTCCCTGACGGCCCTAGGACTTATACTCAAAAAGACCTTCCACACGACCACTTACCGCGCGAAGAGGGCTATATACGCATGCAATAGCAGATCTTGCCGTTCGCTGAGTGTCACCCGAAGGGTCAGAAAGGCAGATAGGGCGGAGAGACCAGGTCGACACCCAAGCAGCTCTCGTCTAGTGGTAGCAGCTATGTCGTGAGACGCAACATCAGGAGTCGTGTCTATCGTATGTACATCATTATTGTCAGTAATGACGGCTATCTGTGATAGGACTCGTCCTCAATACCCAGAGTTACGCTAAGAAGACGGCAGTCTATCAGTTAGAACGTCAGTATTGGCCTTTGAAGTCTATGCCATGCTACCAATCGTACGCTTAAACGCGAATCGGGAAGGCCAATCTAGGCGGTTTTGACCCCGGACTCTTAAGAGCTGTAGCCTAGTCCAATGATGGCTAGACATAAGTCAACGAATTTACTCTATACGAGGGGCGGGAACCGGCCAATTTTTAGAGAGCACGAGAGCCTTGTCAGCGGCCAGCACTACTATCCTCGAGTCCCTCTATCCTAGACGTAGTAACAATATACCCTAGAGAGAATGTCCGTCTAGGCTTCCGTCTAGCCCTTCGTTCGCGTGAACCGCCCGCTAAGTCCTTCCGTCAGTTCCTCAAGAGTGGTATTGGGCAGGCCCAGTCTTATAACGCATATACTTGTGGCACTCTATTACTTTACCAGCGATGATCACTGCGTTAATTAATCGGGAATTTACCAATGCACTCTCGAATCATGCTCACAGCTGAGCAACGGTGCTGCTCACACCAATTACATATGAGTCGTGTTTAGCGTTGGAGCGGAGATGATTTCCATCTGTTCGCGCGCATCACGAGTAACCAATATACGGTTATCCAGCGTATCTGTTCTGACCGGGTTGGTAGCGAACCCTTTTGCAAGCCGGCTTAAGTGATGTGAAGTGGGAGTGATAACTTAAGCCGCTCACTTCGGGGGGGACTCTTTATATTGGTGCTGGAATACAGAACGGCGTTCGTTAGTCGCCCTAATCGGGCGCACATGTAGTGGACGGTTTAAACGCCTACTCGATTAACACCACAGTAGGTACTCTATCAGCAGCGTGGTGCAATATGCTAGAAGTTTCACAAACTTTCCGCATGAGGCTCAGCGGCAGCGTCCACTCCCAATGGCCACGTGCCGGTAGCGATGTTTGGTCGGAATTAAGTCTCCTTCGGGATACGAACCGGATTTAAAGAGCCTCGGAGACGACTAAACGAATCGTCACGCATCTTGCCCAAGGTGCTGAGCCTTGTCGCCTTCTACATTAATGCAATGCGTTCGAAGCTCTGACCGCAAACAGGAATAAGTTCAGGACATGAAAAGTGCAAGGAGTTCGCATAATTGGAACGACCCACTTATATAGGTGCTTTTGAGAGATGTGTGTACGACCTCTTCGATGTCCTAGGTTACAATGCTCCGGTAAGTCAAGGCACATAGAAAGAACAACATAGCCAACTGAGAGTGCCTCATGTAGATGAAATACAACATCGCTTTAAGCTCTCAACCGATGTAGAGATTTTGGGCGGCGTTGACAGCGTGCGCTCCGTATTTGCATGGAAGAGACCAATTTGGTTCCGAATTCGTCTAACTCAACTAACATCATACTATATGCCGGATTCTCGCGGTTGGACCCCCCTGCCAATTCGGGTTAAAACCACTCCCCCCATGTAGGGAGCGGAACTTGTTACAATATCACAAAATATCAGTCCTTTCACGATCGCTCATATAGACGGAGAAGGGGACTGAGCTGTTAGATAGTGACGTCGAGCCCTGGCGTAACGGTGGCCCGCATTACCCAATTTTACCACTTTGTATGAGGAGCTCAACCTAAGTCAACACGCGACCATGCATACGAGGTCGCCTCAGTAATGGAGAAGGCTGCTGTTTGCATGGAGAATAGCGCTGGCTACAGCATCACACGATGTGACACTTGCTAATGTGAAGGAGGTGTTTGGGATGAGCCTACGGGGATGTTGTATCCCTGCCCTGTAGGCACGTTGGGACTTAGCGCGATATCTAGATAACTAAGGCGCCAGCCGCGGCTGTTTGCCGCGAAGTCGTGTGATGCTGTACAACGAAGGGCGAGCGTGTTAACATGAACACGTTACTAGACTAGATCCAAGCGTTATCGTTCGTTTGCAGTCTCCATGTTGTGCCGACAAGGACAAGCGACGTATCAAATCGACTGAAATGAATCAGCTACCTCAGACACATCGAGCTCTCGGTAACATGGGGCCTTGTGGTTGCACCGTAAAAGGGGGATCAGCCCATCCATCCTCTGTAAACCTACAATCGCGGCTAAGTATCACTACGCTTAATACGAACCATTAGACATTCGATCGAGAGACCTGGTTTGACTTCCCTTTTGCTTTAGTGGCCATCGCAACCGACCTCCCCTCCCTTACGCCTTATACACCTTCAGTGCAAATTCATGCTTCAGCGAACAACTGGACTGTCTGTTGGTACGTCCACGGGGGCTTATTCATTAGAAAGCCCCCTAACTGTCACCGTTATATGGTTCACACATGAGCTGATCACCTAGAGAGTCGTCATGCACATTCAGCCTAACAAGGAACAGTTATGGAGGATTATCTTCATTTGCAGCACCAATCGACGTTGTACTGGTCTATCTGTCGGTTAGTCCGCCGACCGGCGAGGCAAGCCCCCTATTTCACGGATAATGCTGGCCCGACGTCGATGAGTGTAAGTCTATAAGGCCGGCCGCGGTAGTTAACGAGACCAACATAGAACTATCATACTAGCTGGCAATGATCAATAGGGTCTAGTGCCACTGTCCTTCGAGCCCTCCCTAATTTAGCGCGACCGGTTTCCTATTGGCCTGTGGGGTTGCGGGCGCGTCCGCTTTTTAAAAGAATGGTCCTTAACACCTACCCGGAAGAATGTCATTGCGTATAGCACACTCTCCCCATTAGCCTAGACACGTCGTCGCCCGTCGGACTAGGCTGAATTTAATCCGGGGATATCGAACTTTCAGCGCTGTGCCTACGGGGACTCACGATGCTGTGAACCGCCCCAAGTCTCTGGGGCTCCCAAAATCCAGGTTGGATTACGGGACTCAGCGCCGGTACACGTTACGCATTACGGGGTGGAGTCCTAAGATAGGTGAATGAAAGGGCTTCGCTAAACCAGTAAGCATTTAACAGGACATCGGCGTCACGTCTCGCGTGATAAACCGGAGTGAGTTAACACGGCGCACAAGATCCTATTCCATGATAAACACCTTATGCCAATCCAGCGTCATCCCTCGCTGCCAATAAAATTTCACGACCTGCCTACTGACTAACCGTCATGAGAGATGAATTCCGACACCCGTCATCGACGTGTTGCAAGCACAGCCCCAGATTTAGGATAGGTACAGCTAACGTATGGGATGCTACAACGTGTGTTACATCAGAGTCCAGCGCAATAAAGTTATTAAGGCTGTTTTTTCGAGTACCTAGAACCCCGTCTATCCCGATGGCCTCTAGCTCCAGCGTGATTAAAAAGAGTATTTATTTCAAGCCGAAAAGGCGCCCTGGAGTAGTCCGGATACCAGTAACGAATTCAGGGGCGCACCCTGGTAGTGGTAACGTTGTCCCCTAACCACCAACTTTAATTAGGTAAGAGCCAGTAATTATTTACTACCCGCACCGGTCGCGACTGCCGCGACGCACAGCAAGCACTTAGGCGGTCTCTCAGGCTAGCGCGGTTCACGCCTCGTCTCTGAGTCGGTTGCCTTGTACGTGAACCGACTGATGCTTGACCATGAGGGTCATCGTCAGGGAAGTGAGGGCCGAAGTGGCCGCGCCGGAGTGTGCCTCGATGTTACCTTACACCGAGAAACTATGACATGTTACTGACCGGAGCATAGCATTTGTTCGGAGCGTCATGACCCGTCCCAGCCGATCTGCACCATTCAGTGACCATCCACAAGTCTCTAGGGTTTAGCCAAAGGTGACAGGTCAAGCACTTGCGCATCGCCTAGTCGATTATGCAACGTCTCTGAACAGTAGCACTTATCTCCCGCCGTGTACTCACGCAGTTTGAACTTACTATACATACGCCTACTATTGCGTTGCTAACACAGCGTCCTAATACCTCACTTGGCAGAGGGTACCGCGACGCCTCCTAGTATGGGGGGAGTCAGGTCAGAGTGTATAGAGATGCGATTTTTATTCCCGCATAGGGGTTCAGATGAATCGACCTCTCATTTGTAAAAGGAGGCGCAGAATTCGCTGATACGATCGCTAAGGCACGATAAGCAGGGAATGAAGGTTGGAGCAAGATAATATCCCCGGACGCGGGTCCTGTCGCAACGAGCGGCCGTGAGACCTCGGTGCGCACCGGCTTCGATCAGGCCGAAAAAATCGACTGCGTGACGATATTTTTGCTACTGTGGGGGATTTATGTCCCGGTAGTGCGTTCTAGCAGAGGATAAACTTTACAAGGGGCGAATTGGTGTCAGAGCGCCCTCCGTGCTATTCCGCGGGCCACACCGGCCGTTAATGGCACTTCGGGTTAGAGGATGGGTTATTCATCTTCCAGGCATTAAATCGACCGACCGCGGTTATGCTGCCTACTTAGCCATTTCCTAAAACAGGATGTAAAAAGGCCGCGAGGGTTCGAACCTATGGTTGAGATACAGAGTCATAGAGTCAGATGCGCAGGGAGCGCACGGATCCACATGGCAGTTAACTAATTATTAATATCGAGTTACTCCCCCCTTAGGCCTCGTATGGGGTCTTGCAGTCGTCCTCCGAGGTTATTCTGTCTCGTTCCAACGCTATTCGTTACCACTTTGCACCTCTTTGCTAAGCAGGATTGAGAACATCTCACTCACTACGAGCCTTGACTTTCAGCACGCGCGTACGAGGCACTGGGCTTGCCTCGTCCTTTGCCCCGAAACGGGAGCTAGTGTAGGTGCCTTCCCGCCCTGACTGATGTCACGTCTATCGGTTTATTAAGGTCGGGGACCATTACGAGATTACCCCGCGACCTTCGTCCAAATGGGATCACGGCAACGTTCCACGAGGGGCGTTGCCTGAGCCACTATCGGGTATCAGTCCCTTCCGAATACCGATCTAGGGAGCACCGCCAAGCCATTACCTCGATTAACAGCAAAGCCTACTCATCGCATACACGGTTCTTGGGGTTTGACGGGTCAGTAGCAGACTTCCGGCGGGATGATATTTTATCCGATTGAGCTTGCTTTAAGCCGCCCAGGCCACCCTCGCGGCCGTGGCTCTTCTCTTAAATGGCCCGCACATGTTAGCGGTTCCTTCCTTCCCCGAGCCCTATACTGAACGAGAGTGGGTGGACAGACTCGCCCTCTGATGCTGTGTTCCAACTGGTTCCAAGTCCGGCGCCTGTGGATCTACCGACGCGACCACAACATTATACCAATGTCTAGGCTTAGCTCGAAAACAGTAAGCCTTAGAACTAGGAGTCGTAGCTTCTTCTAAATGAAGGCAGCGTCATAGCCATCGCAACGTTAGCGCTTCTCAATGCTGTCATGTTCACTCGCCGTACATAAAATACAACACAAGTGATTTTAGGTTTAACAAGGATTTTTACTCGCCACGTACTGCTTAAAAATAAAACCCACGAGTAAAGACTTCTGATCGGTGCCGGTAGTGCTCAAACAAAAACCTCTAGCTAACGGGAGGAGGTAGAACGGAATCCCTACCAATCTTATCTCTACTTAAAGAACAAAGCGCGCGGTACATGGGCAGACGAGAATGAGACCTCGTTGCGTGATCGATGGTTATTAGACCCTTCCAGTTTAGCGCGCATGCGATAAATAGTGTACGGCGCGCACTTTCAGAGGAAATACACTCTCTCCATACGATGTACATCGATGCTATCTCTTTAAGCAGAGTCGCCAGGCCTATATGGGCGCTTTAGACGCTGAAGAACCTATTACTATGCTTACCAAAGCGCGCCTATCTAGGAAAACTTTGCGCATGAACGCGAGGCGTTCGCATAGGTAGCCTAGAATGCCCAAAGTACGGGGTCGTCCATATGGCACCGCCGTGTGGAGAGAATCCCGGCAGCAACCTGGAACTGTTGGGCTCACCTCATCGGCGCTCGGGTAGAGACTTTTTCTTTTTTAAGCCGTACTCTACGATCTAGCGATCGCAACTTTTCGACAGGTTGAGGAGTCTGCAACTCGGGCCGTTCGTGGGATCTTCGCATTGCGAAAGTACTGGGATACTAGAGGTTTTATCGGGCCTCGCTTGAGGTCTGTCTGGACCATCGCCCAGTCGATAGAGCGGGGGTGATTTAAATAAGAAATATGTTATGCCCGATTTGGAACCAACCAGTCCCGGTAGCGGTACAAAAGCCCCTTTCCTGCTAGTTCTATGCTTAAAGCGTACTCCTGTTACAATCCGTAGGCAACCTGAAGAGCCTGGTTTTCCTTTATTTCGGTTTGCCATTTCCAATGTGATACTGTGCGGCGACCTTAATGCTTTTTGGTAAAAAACCATACCGTAGATCCAGCGCACGCGTTCAGACCGGGTCCCGGTTTGGCGGTCACTCGTACTCTGCACTGTTCAGCTAGAGGGGTCTCCTATCCGAGGTACCGGTCGTCTAACGGGTGGTTGATCACTACCAGGGACGCGTCTGGCGTCTACTCTACCAACAGTTAAGGAGGGCCAACAAGTTCATGGGCATCGTATAACTTGAAGTGCCATGGACAAATAATAATTGCTCCACGTCCCAAACTCTCAACTGGTACGCCACCTCCGCAACCGAGCTCGTTCAAGTGTCCTTTAGCTGGCGCCGAGTGGGAACCTAGAGTCTACCCAACGCAGGATTCTCGAGTACATAATGTCTGTAGCCAGCGACTATGACACTTATGGTGAGAGAGCCCCTAAGAAAGTGTTGCGGCTCCGCCCCGGTGCGCTTGTTCGGTGGGCGTATATAGAACCATGGGGATATAATGAATGGTAACATATCTGCCCGTTGACAAGCCGCTATTATGACATTCAGGGTTGTGATACTATTATGGCCCTTCACGGTCACTTGGGACGGCCCTAAACAGGATTAGTAAAGTCGGGGTCTAAGTGTAAGCGATAGCAACTGCGGTTGTGAATCGCGTAGACCGCTTGATGATGTAAAATAACAGTTTTCATGTCTTTCGCAATTACCGCGTGTAGTCTGAGGACGGCCCCTTCTCGATATGAAATAGTTCGTCCGCTGTTGACAGGTGTCCTCAATGAGGACCGGAACCGCTTTACAGACTCTGAGGAGCGTAGCGCCCTACGTCCTTCCTGATCCAGCCGCACCCATAGCTAGTAAACAAGTGACGTGCGAGACACAAAGTCAAGGCCTGGGCAAGCTGCGAATGACATTGAAGCGTACTCGATCCAGATCTGCGCCATTGAGTTAAAAGGAGCTATACGCCTGTCTCCAGCCGATTGCAGAAACAGATCCCTAATAGGAGAACTGGGGCTCCATTGTCCCACCGACACCCTCCTTCCTATCCCACTGCGCCCCATTGCTGGTAACCCAAACCTCTCACGACGATTCAGTAGCATGTAGTAAGGTTAAAATCGGTGGGGCGAGTAGTGTTTATGATCAGAAGACTCTAATAGTTCAAGATTGCAAACCCTGATGACAAGATCCATTGATTATCGCCAGTGGGATGGCGGCAAAATCGGTACCATACCTCGAGCTTATTTGCCTCACTCCTCGGGGAACCATATAGACTTGCACTTTGGCAAAAACTGGTCGGGATTCAACCGACCTTTTATGCACTGGACCCTCTGCTGAGGGCCTCTTGTCAATAAAGCTTGCGTTATAAGTTCGCGCTTGATGTCCAGGATTATTATTCCAGACGTGGAACGGTCGAAATGATAGCCTTATCTGTTAACTACCTGAATTAGGGTTGCGCATTACATAAAGGACGACAAGGTGGCACTGGGAGGCTGCATTCTTATAACGGGCATATGCGCGGAACACCACATACCTAGAGTTGGATGGCCTACCTCCGGGTCCGGGTCGAGGCCCCCGTGGAACAATAACACTGTCCACTTGGTTTGTAAAGAACCTCCACTCGTCATGACACGGCCTAGCGGTGTTATCGGCGACGTCAAATATGGGTTCTTTACAAGTGAATCAGGCTTACCAGGCGGACCAGACACCCACGAGTGAGACCTGGATCCCGGGGGGATGGCATACATCCCATACGCTCACTCAGCATGCACGTTCCTAGCAACATGCTGGAACCTTAATTCTACGTTCACTTGTCACGTAGCCATTAGCTGGACAGAGTGTCATTCGTTGTGATGGCCCTTGAGCTATACTTCTAACGCCAATCCATGTTTGCAAACATAGCTCCAGTGCGCTATTCGACGGAGGTTTTAGCCTCTGCACCATGTTATGGCTCTTATAGGTGTTGGGTCGACGATAAGTGGGTGACTGGAGTAAAGAAGACGCTAAAAAGCTCAAGATCACGCGCGATATCTTTAACGGAGTACGCAGCAATCGGCTAAGACCAGTGTCTACTAGCTCGTGATCGGCACCGGCCGTATCTGGTGTAGTAGTCAGCCCCGCATCACCCGTGCCTGTATGCGAGCTCTTGACTCTATCAGTCATTAGAGCACACGTTTAGACCTGAGACCAAGCGTAGGTCACTATCGAGATTACCCCTAGTTGATTAATGCTCTTGACGTGTATGATTACACCGACCGGCGAATAACTCTGCACGCGCGTGACGGTTTCCCGCTAGAAGAAGCCATATCATGTGTACACACAGCTGTGAGCCGCACAAAGACCTCCCCTCGGTCGATAGCGTTATGGTCGTTCACGATTCCAATAAGTGCCAGATGTCGGAACAACTATGTTTCATAAAGACGCGTGACATGATATGGTTTGCTATGAAGCCACTACGAAAATGCCAGTGCTTCTCTTTATATTTTGCAGATGTGGGCAAGTGCATCCATTATAGTCTCTTTACGGTACCTGGATTCTCCAGCCTACTGACTACTTTCTTATATCCGTTGGGGGGGGACGCTCAACAGAGCGCGGACTCAGGATATTTCAGATAACGACACCTTAGGTGAGCTAGTCTGGCCGGCAGTCGTTCCGGGATACCCGCAAAATTTATGCCGGTGATGACTCTAGGTTTAGCTCGATCCACGCAATATCGACAATACAAGGTTTGCCTCCTCTTTCGTCTATCACGCAACTTAGAGTTGCTAAAATTGTCGTAGTCCGTGCGTTAAAGCTCCCTACATTTCTAAGCTGTAACGACACCACACGTATCCTGCAACAAGAGTCATGGTCTCTATTTCTCATATCCGAATGAATATCGTCCTGTTTGAGACACGGGTGGGAATCTCGGCAAGATGGCCGAAGATGATGCGTAGCACTGTCACGTTCAAAAGCTCACAACTCTGCCCACTGCCCAGCCCTAACAGAATCTGGAGACAATAGCGCTGTCCCATGTGGCGGAGAGTTGCTTAATAGAAACGCTATACCGTGGGATCACATACGTCACGTACTCCGGTGACACTATTTATTATTCCTTTACCTCCAATGACGCCCGTCCTTGCATCGCATTTTGTAGTTCGTTACTGCCGACCATAAAAACGTGCTGAGCCCTGACGTCACGTAAACGTCCTATCGGTGTGAGGTCCGTCGTGCTACTCTGAAATTTGGAAACTACCAGTTTGTTGGCGGGAATATCCAACCTGTGCGGCATACACGCTACGCGATGGGGTTGGTACATGCGAAAAACTACCCAAGCACCGGGCGCGGCGAGACACCGTATGAGTATGTTAGTGATGATTTACGGAGCCGTTTTTTGAACTCACACTGACGATGAATCCTTGTCAAACAGGGTTCTCGTTCGTGCAACGATTAAGCATTCTGAACGTGGTACGTGCACATAGCTGGAGAGTTGCGCGAATCTCTTTCGTACCGTATTCTACCTGATCGCTAGCTTTCCGGGGTAACGACATCGGCAATGATGAGTAGCAGCCAGTATACCATACATGACAGGAACTGCCATACTCCGCCTTAACACGCGAGATTTGAACCGCAAGGATGTGCGTAAGCCTGTTCACCATCGCCGATCTTGAAACGATACACGGGTAGGGTGTACGTGGGCAGATGCGATGCACCTTCCGCTGGTTAAAGGTGCCTCACTTGATTCGACAAGACCACCCAACCGAGCCACGTGGTTCCGACGACTCAATGATTTCCGTATGGGTCCAATCGAGCACCCCGGGTATCCACTCCTTCCCCAGAGAAATTTGAGCACTGTTGGGGGTGAGTGTTCCGTGTAATTGTGAATCCGAGTATGTGTTTTGCAGTGGGTCTGTAGCATGGAAAGTTACCCTAACCGGCTCTGGCATTATCAACGGTGGATGTGGGTGGTTAAACAGCTTCCCTTCGGAGACTTAATGGACTAAGAACGAATGACGTGACGCGAGAACGAGAACTCACCATCTGGCGCCCACGGAGCCATATTTTTGATTGGTAAATCGCCGCATAGTGCCCGATACGATGCGTATTGAGCGTAAACGGCGCAGCCTCTATCATGAGGTGATTGCTAAGTCTACGGATCATCCTACCCCAGTGCTGACGAAGGAATTAAAGACCTATTTCCGAACGGACCGTGGGGAAGGAGGGTATTGTTGGCGCGGCCTTCAATTATCCAGCGTATACCCCGATGGCTACTGCTACATCTAAAGTGACTAGCCAGAAAAACCTACCCTCGCCCCACGAGGCTCGTTGATCATCTCAATCATAGATTGATTGCACGGCTCTTGTGTGCACATTGAGACAATTTCTAAACCGACAAGTTTAATGGCTCCTGCGTCACGCTGGACCTTCATGCAATGGTCCATATATATCTGTCCTCCCATAGCCCGCGACCGGGTCTCGACTCAACTGTGTTTTCGCTATCCCCAGGCTAGCACTTCTATCTCAAGCCTTGTTACCCTAGTCATAGTGTTACTGCTAGCGTATAGGGTATTTAGTCATCAGTAGACGGCCGCTTTCGTATGGCAGCAACCGTCCCATCCGGCTCAATTAAGTCTACATCCGGACTTGGGTCTAGATATTCCTATACGAAAATATAGTCTCGCACCGCCTCACTCGTTAAGTTCAGGGCGGCGTCACACTTGTTCGCGGCTCCTCATGGGATCTTTACCCGATCGGTTGATGCCAATTAAATGTCTACACCGGACTGGCGTGTCCCGAGACGACTTTATACACGTGTGACGAGTCTATACGAATAGGAAGGTCTAGTGGGAGGACTGGAAAAACTCCTGCCTACCGGGTCGAATTATTTACGCGTGTTACAATATGTAATTTAGTGCTGGTCGATGCTGTCTCCGGGATTTTTTATCTAAAAGCATCCTTTTGGGTGTACTCTATCGCAGTCGCAGACAGCAGTGGTTTTCGACGTCAGTCGTACAGGGGAGCGTGAAAGCCACACCTTTCTGTAGCTGTTTGAAAGGTCAGCCCGGTTTACTCAAGGTGACTCCTTCGAATCACACGTCGCTGGAGTCGCCTTAGCAGGGTGCTATACGAGTGATAGAGCACTGAACCCACTATAGTCTTAGTGATTTCATGTTTTACTTACGGACCGAAAACTTCGTGGGGTTTTGTCAACGATACGTTGAATGCACGCATGCCTCATAAACTGATGCACTGCCACGTCTGAAAGAGCGAGAACCTGCAACAAGCGGAAGGTTACGCCCAAGCCAGTGGTGATTCCCCCAGCTTGGAGGGACTCCCCTTAGCGTTGGATGTGCTTTGCCCAGCGGCCTCGGTGTACGGGTTCTCCACCCCACTTGGTTTGGACTGAGAGGTAGCACGTTCTCCTGCGTATCCTACCCAGGGCACCAATCGTGAACCTCGCCTATTATATACGGATAGCAGGGTATCCATTCTTACCATGAGCTCAACCACTCCGCTGAATTCGATGGGCTTTTGGGCCCGCACATCACCGTTTCTATCAAGATGCTGTCAACGCGAATGCTAACGCTATTTACTCGGCGCACACAGATCGATGAAAACCCAACGTGGCGCGGGACGGACTCCAGGAATCGTTACCGTTGAGGATTAGGACCCCATACAACACCTCGGCAGACTACATCAAATGATAAAATTCGCATCTGAATTATAAGGATAGCGCCACATAATGACAACACAGTACAGGTGTCTTTTATAGTTTAGTAGATCGCTCGAGACTTTGCGACAAGAATGGACGTTACCTTAGGGGCGGGTTGAGGATTGAGTAATGCTCCCGCTTAAAGCTGATTCGCTCCTAGAGATCTATAAGCGAAGTTCAACCACCTAATTACCATTGCATAGACCGGGAGAACCTGTGACACCTGTTTCCCATGGGATTAGACGAAGGATTGTTCTGACTTATGCCGAGTGAGCCAGTATTGATTAGTAAAAAATGCGACGTGGTAACCAGGCCTTCGATGGGCCGTCTCTCAGGCAGTAACAACCATATACAACTACAAGTTAAACCCGAAACCGTGGCTACCATGGCTCCATCAGTCGGGTTCCCGCATGGGAACAGTTCCACGTGAGCGGCCTTAGGCCTTGTCGGCAGGACAACCGTCAATGAGATATTCGAAAGTGGGGTTAAAACTGGCCGCCCTTGATCGGTCCTACTAACCACAGTCTAGATCTGAGAACCATTCCACAGTGGAATCGGTCTACGGTTTTCATGATCCCTTCTCGGCCATCCATTGAGAACGATTCTACATAATCTGTTAAGCGCCGCCAATCACTGATTAGCTCGTCAGAAAACCTAAGATACCGTATATAATCACGGAATAGGCGAGGCCGATGACAGATCTAATGTCATAAGGAGTCTTATCCACCGTACCCAAGTCTAAACAACTTAGAATTTGGGGGCAGGTTAGTAGAGCATTGCACCACAGACTCCATGAAATAACGCTCCGCTAGCGGATATAGACTGAAGAAAGTATAGTCAGCTGCCTCCTAAAGGCAGGCGTAGTAGGAGCCAACCTAACGTCGTGGATACAGCATTACTCGGTAGCGTACGGGTAAACACCCACGAACCCGATGAGGAACTCAGAATTTTAGTCCGCGAGAAGTTCCATAATCATATTTCCTTCTGTGCTAGTCAATCTGATGGTGGTCATTATTCTCCATATGTTAGACAGTAGAGTGCGCTTCACGCAGAAGTTCCTTCCGCGCTACTACTTTACTCGAACACTGGCCTTGGAAGCCCGCCGTCGGTGTGTATGACTCAGCGAAGACTCTCCAAGCAGGGGAAATTGCGTCGGCAAACTGAGACTCAGCGTCTCGCGAATTGTATGTAACAGCGATCCCGGCAAAGTTTATACGGTCATATTAACAACAGATGGGAGTAGAGCATGTAGCATTCGGACTGGCACCTCATCGCTTTCGCTGTCTGCGGATCACCGGTCTCTCTTGAACGTAGAGCCTGAGGTGGATCTGAGGCACACACGGCACTTGATATCCGGAATAACCTATGTATAGAGCCTCGGTTGTTCCGTTCGTGGTTCCAATAGCGCCTGAGGGGATTAAACGCCTACCGGAGACAGACACGCGAGTTGTTTTGTGCACGTAAAAATCACTTGATGTTATCGTGTTCGGCCCCTAGTCGCTATTGTCTTCTTTAACTGTCTCACTTCAAACAGCGCCGTATCCATTCTATGCAATTACATACTATTTGACCGACTACGAATCAGACCCACTGAATTGCAGACCTGCGAAACGCATTCCTAGTTCGATCGTAGAGTGGAGAGCTAGCCGTTGTTTACCAGCTTTTATTCTTTCACTTGGGATATAAGGGTCGCGTTGTGTACCCTCCTCGGCTATATAGTTTGCTCCAAGCTAAACGTAAAAGGCCGCGGAACGGAATAGAAACGGGGGAATATCATGTATTTTCATATGTATCTGAGTGTCCGATTTGTATCTTCCACGTCCGGGATGTCTTTGTCATCCCTGCTAGGTCTGGTCACTCCTAGACGGAGTCCAGCGGAGCCTAAGTCTCCCGATTAGTCCACTTCAGATGACATACGAGAGCCTGAATTCGATGCTCTGGTGTCCATGGGACGAAACCCGTAGAATCGCCCGAGTCTGGTCCTATAAGGCACCCGTAATCACCAATCTGTACACGCTTCCTTCAATACGTGGTCGTTCTACCTACTTCCCAGCGGTGCCAGCGGGACCCAAGCCGCTTGGATGCGTCTTCGGGACCCTACTGCGATTTACCTGTGTTGCACTCGCGATATTAGGGAGACAGGTTACCAGCTGACACATGACTAGCTGACCCGGAGCACTCAATTCTGCCCATCGGGGATCTGATGCAAAGTTCCGTCGCATTGGACGGCTTCCCGACTCGTAGCGAAGGAATGGTTACCGTACGCCTTCCAACACACGGCCGCGACAGTAACACAATCTACTCTTGGTACACGGCCCTAGAGTAATTGTGTGCCCCCCATACTCTCTACATGTGATAACAGCGACCCACTAGGTTGCCAAACAGAGTCGGAAGTCCATCGAGTCCAGGGCGCTGTAGGGCCTACAGACTCTCCGATGTCCTGTGTTCTTCGTAATTGCAAATATTTTCGGCCCTTCTGCTTCAGATTCAGTCTGTTCCATCTTACTGGAAATGTTGAGAAACTCGAAAACCGCCGCTAACTCCCTGATATTTGTGGCCCTTACATCAACCGTCCTCATGATAGCCCAAGTTATTGTTGTGCTAAAGGGAGCTGAACGCAGACGTGCAGGAAGTAATTGTGTTAAGCTGTTGACCCTTTAAAGCCTACAGGTTCTTTCAGGTGTTCCGTACTCGCGGCGAGAGTATGATCCAGTAAGCGGCGGACGCTGCGAACCACACCGTCAAAATCCTATGCTCCCTGCGAATTACAACTCAAGGAACAGCCAGGACTTTCGAAGGGTAGTTATTGATGCACATTCTACTGCTACGAACGCAGTCATTCGAACTAAACCATTGGTAATGTAATTGTGATCATTTGCTCTAGCTGCAGAACTCCACCACGTGTTCATCCACATCGGTCTCGTGGAATGGTCCAGGGACCGTCCCAATAGGGGGAATTGCGACCCAACTAATCGATGGATTTGAACATGGGAGCAATTCCCGAATGTTAAACTTGCAACGCGCAGTACTACGAACGTATGGTAGCAATAACGACGCGTACCTTCAGCATCATGTGGGTCGCCATAAATTAGGGCCTAATTGCACCTAATCTGCTGGCTTCTCTAGATGTATCCACAGGCCATTAACTGAGAAGATTGCATAGCGTCATATGATTAGTTTGAAAATAATAATCAATGACGAGCACCCGCATCAATATAGCTACGGAGCTTGGAGAGTCCAATACACCTACATGCTGTGCTTATGTTATGAATCAAAAATTAACTTTGACTAAGTAACGTTAGCCACCAGCCGATGGCGCTGGTCACAACGACCCTGGGTTCCGTTTATTCTCTAAGACAACAAGGGTCACTCTACCAGCGGGGTTAAATATACCGGCCGACTGTCTCGAGATGGTAACTCAATTTGGATCAGCATTAAGTCTAGAGCATTCTGCGAGCGATCTATGCGCACTGACTTACTCTTGGACGATTGAGACATATTTTATGCGGTAGTCATGACTTTAATCGTTTCTACAGAAAGACCGTTTGAAATGGCAGGTAGGAAACAACCCTGCTGGATCCTCCCTAAGTCATCAGTCCGGACGGACAGATCTATAACCTCCAATAAATTGAAGAAAAATGCAAAAGGATGCCAATACCTATGTACATTTTCACGTTTCCCGTGTGGTTCGTGCCAACCCCTGGACGGTGGATGTCCCCGGTGGGTTTTGGACCGGGCGAAATTATTGGGCCACACCCGGACCCACCGAGAGCCTCACTAAGACGTGCATGATAGTTAAGTTCGTAAGTGGGAGCCATTATTGCCGACTGGGGGCGGGTATTTCGTCCCTACACCACTCGCGGCACACTATGGTTGGTTTTTGCGGCTTCACTCAACTACAATCTGGTCTGTAGAGAGTACCGTATTATCTTCCTTGCGCCCTGGGTGCTTAGCGAAGTATGGGGATTAAGGGGCGTGAACAATGCTTCTAAGAGCCCGGCGCTTATGAAACACGGTGTTAACAGCAGCCACTGTGGATACGCATGCGTAGCGAACCCGCGCGAGGGAACACATCTGAGCGTATTGTTTCCCCCGCTAGCAAGCATGACGTCACCTTAGTGAGCAAGAACGCTCGGACTCAACAGTCTAAGCGGAATAATATCGATAGCGAAGAAAGCGCGAGAGGACTAAGCAGAGGAGACGCAAAAGGTGAGCCGCGAGTGATTTAGCCCCTGAGATGGCAGGTATAGCTTCTTCACAGCACGGAATTGAACAAAGTCCGGCGCCCCTGGAAATGCTTAACCCCTTTCGAAACGTCCCAGGTTGGCCGTAAGGTTGGTAACCTAACACCCTACTAGCGGGTCTTGCGTCATGCAAGCGGTGGTTCCATACCACTAACGATACGGATAGGGAGGTTCAGGCGTGGGGCGACACAGTAAGATCTGATGAATCCATGTACCTGTTGCCTCCCCGTCGTACGCGTTGATACTTCGCATCATACTTAGCAGAAAGCATCCATTCGCGACCTCTTCGTACAATGGCAAAGGAGGGCTAGTGCACGGGGTCTCTAATGAATCAACAGTACGAGGTTCATGGCCACAAATGTAACGACGATATGACTCGTTCTATGTCTAGGTCCCACTGCGAGTCTCAGAAAGAATTGTACTTGAATAAAGCCCCCCCCTTAGCAAGTGGTACGTAGCCCACACGATTACGCAGAGTGGGGTAAGCCTCCCCCAGCGACTTGGGGAACGCGCTCACCGGATAGCTACTGCCACTGTAGAGCACTAGCGCATAGTGTATCCATCAGTGATGTACGTAGCCAAGCGGGGAGTCCTTGGTCTTATGACTAGCGCCATGGGGCTATCACTGAGAACGGACATGCAAAACGGTCCGGCCATTCGCACATCCGAGCTGCAACGATCGCCCAAGGATTGGAAATTGTTAATAAAGAGTCCGTGGACAATTTTAGTAACGCGACGCGCATCTCTCCCCGCAAGGCTCTATTTTTAGCTTGACGGCGCGTCTCACGTTGCCGGGCTCAGCTCGAGCCTCACGTGCCGCGCGGGGTACCTCGGTTTGAGGAATGTGTCTTGAACGGTTCGTACCGGCTTAGCGTCGGGTGCTACCCAGGGTTCCAGCAACATTCTTACCCAATGAGGGCGCTATCATCTAACGTATCACGTACCAAGCCAGTAACGGGGTATGCTCAACAAGAACTACGCCTTGCACGTTAGAACGTTAAGTTAGAGCACCTGCCCATCTGGATTGTATCACGGTCGATAAAATCGTAATTGTAGCCTGGATGCAGGAAATCATTACGACAAACGATCGGGTGTGGACCTGCGCCGCAACCGTGGGATGCCTGAACGCAGGCAGAAATCTACAGAGCGTTTGCAGAGGGCCACGGGTATCCACGTAAGTTCGCGCAATAGCAGCATCATCTCGTTCGGGGTTGCTTGCTCTCAGGACTGTCGAGTGGTCCTATACAAGCCGGAGTCCTAAGGGGATTTGGTGCCCCAAGTATAGTTTTGCCGAAGGGGGGGCACATGCCGTGACTACGATTGGGTCTATTGGGCCCGCGACCCGGTGCGCACACGTTGATCTCCACTCCCTCCAAGAAAGACAGAGGGCAAAAAGACAAGCCACGAGCTCTTTCGAAATACAATCTAACATAAGCTATGCTGGATAGTATCTTGGAACCTAGATCCGAGAATGGACGCGTGGATAACGAGCACGTTACCTTTGGGCGGCAGCACTTTTAACACCGTTTAAAAGTAACTCTATAGTTGTCAGCCTTTAAAGATTGCGTTCATTACGGTACGACACCGTCCTACAGCCGGGCCCCCGCGGCAGCGCTTCCATCGTGGAGGCTGTCCCCCATCCTCCGAGACTGCGTTTACCGGTCTGGGGAGACTCCCTAAAGAAACACCGACAGGTGGGGAGGCGGGAATCAAGTTAACGCATTCACGTAATTCACTCCTGTCACTTACATAAAGTTCTGGGTTCGCGCCTTAATATCCATGAGGCATACGATGCGATGGGGACCACGCCTGAGCTAGGAACACAAGTTGATAAGGAGTGAAAATAGTATAGGTCTGGCCCGCCTCGTCTGTTGAGGGCCTTTAAATCGTAATAGGCCTCCGCTCTGAACGTACCGTTGAGAAGTTCGGGTGATTTAGTCCGTACTTATCTGGTGTGGCCCGTGTATAGTAAGAACACGGTTCATCAGTCCGCAAGGTAGTATATGACTACGCGTCGATGACCTAAAGATCACGTTATGGCATCGTCCTGAGCAGCTTATGAAAATTGCTGCGCGTTTCAGGAGAATTGTGTTATAGTGCGGGACGGTAGTTTCGTGGAGAATTATGGGTAGCACGAGATCTATGGACGGGAGGTGACTGTCAGCAGACTTAGTGCGCAGGGCTGGTCGGTATAATCGCTGGGACCACGTACTGGCAAGACGCATGCAGCCTGGTGGACATCCACACCCGCCCTCGCTCAGATAGGACGGACGTGCGGCAATTACCCATCTGGTCATCCTGTGGGCTCGGACAGCGAAAAACAGCAGTTTAAAAAATGGGTTATTGGACATTATCACTTTGTCGTTCGACAGTTTATTGCGACCTCCCGATAGAACCCCTTGCTCGCATTTGAGATTAGGTTATCCGACGTGACAGTTTGATCTGCGGAGAATAGGCCGAGAAGCCCCTACGTACCCTATGATTCCCCCCATGTGCGGTGTAAAACCTTAACCACTACTTATGGATTTCATTCCAAATAAGACGCCTATTATATATACCCGAGTGTGTAAGTCCGGCCCTCGCGGTACCAAATTTCGGCTGCATACCTAGCAAGGTATGTCATCTAAACCTAAGGATCGGTAGTGACGTGTAACTAACACGTGGTTCTTAAGAAGTATTTAGTCCTACGTACTCAGGCATCGATACCCTCGATGTATCGGGTGCAGTGGGCGTCGGGCGGCTAAGAATAGTCCCACTCCTTATGTGAATGAAGACCTAGTGCATAGGCTACACGACCTACGTAGCACCTCGTACAACCTTGCTCCAGGCACGTCTAAATGTCGGATAAGCTGGGTTCGCTCCATGCCTGCGACTCCTGGGCGTCCACATATTTTACAAATCCGGACACAAAGCACTTCCAAATTCTGCGTGTGGGACCTAACACCGGAAGGCAACACAGGGGACATTTAGCCACTCTAAAGACCCGCATACTCCAGGGCATTACACGTAACGCGTTCTCTTCACTACGGAGTCCGCGAGCGAACAATGACTAAGGGCGAGGGCCGTAGATCTCCTTCTCGTAAATATAACTCAAGGTTGCCAGCGCTTATCGGTGCCACCGATTAAAAGTCGTCGGAACCTGTAGCCGAAGAGCGTAAGGACGTTACGTGAGGATACATGGTTTGCGCCCGTCCAATACATAATCGACCATCAAATCGAAAGCGCGTATGCAGTACTCGTACTGCCCAAAGGACAGGCTGCATTCGCACCCCGAGCGTAGCTTACATGTTAAGACCGTGCTCATTTTCCACCCGTAGAGATGAGCACAGATTGCTTGCTACTAGTGTAGTAACATAGTAATAAGAGAACATTCATAGACGTTGGACCACGGTTGAAAGACCGCCTCTTAACTACTGAAAAACAATATTTGTCGCTTAGTAACCAAGATACATTATATCAATCCTCCGCTAATGAAGTTTGGAGGCAAGCCTCACGTACCGTGGATTATGTGTATTACTGCATTGAGGTACACCGCGGCGGGTACGAAGCGGTGTCGGGACGCAACGCGTTCGATACTGTAAGGTTTCGTTGGGGTGCTTGTTAGCCGCTAACGTTCGGGTCGCTTACTTCTGCAGGCTTGATTACTGAGTTCATTCGATCGCGTTCCCAACATTCGGCAGTCTGCATTCCCCCTTTCCAGGTCACTATTCGCCATCATCAATTGGGGAAATCATTTTTAGCGGAATACGACCTCGTACTGCATATGACAGTCTGCGTGCAACAGACTTTCCATGATGCGAGCTGGGACCCCCTGTTCCCGTCTGCTGCGGGATGCAATGCAGTGCGCGAAGGTGCCGCTGATTACGGCATATCTGCTGTCCTGACAATGATGGTGTGAGCGTAACAATCATTCAACAATATGGTTTTCGACCACGGGCCAGGGACTTTAGGCCACTACGCACCTGGGTAACACTCGGCGAACCCGCCTGTGGACATTCCCGGAGTTTAACGACACCTCATTTTCTAACAACCACAAAAAGGCATAGGCAACCGAACTAACCTTTCTGTTAGTTACTATCTGGTGTTCATAGTGTAGGGACGTGATCGGTTAAACGCTTGTAACCCCAGCTTCTGTAGCGCTTTTCTCATAACACTACGTTGGGGAGATACTTGTTGCGCAGTCGTGGGTTAAAAGCGAGCCGACGATAAATGCAATAAGGAAAGCGGACCTGTCACCCTCCACGCGGGTAAATCTCACTCGCTCGTTAGAGGCCGTAAAGCGACATACGGTGTTCCAGTACAAGCCACATGGTAGGAATTCTCCTACTACATTCACCGTCACAGTATTTCGGCCTTTGTTCCCGCCGCCTACGGTTCGTGTGGTGTTCTGAGCATTACATGGGATTGGTCACCCTAGGACTAGCAGTTGTACTGTCACGTTAGGTTGCTGTAGCGTGTCATACATTTTCAGACCCCAATTTGTACGCCCAAGTTGTATCGTCGTGCTGGACGAGACTCCAGCCTTTACCCTAAGCGTTTAGGAAAGCATGTTTCAGACCGCAGAGAACGTTGCAAGATCGATCGTCTTTTCATCGTTCTGTAGCTAGACGCCTGACAAGCTACTAACCCGAACTTTAGGAGTTTCTCTCCTAGACGGACCAGTTGTAAGTAAGGGTTTCTATCGCTAGGCTAGGGTGATGGTAGGTGATAGCCAATGGAGCGGCGCGTTAAAAGGGTTGTAGTGATATGTGCACCAATTTAGATTGTTCCGCTTATTTTCCGTTGGTCATTTAGATACGAGATAGGGGCGCCCCAGTGCCACGAGGTCCATCTTATGCTGTTGCGTAGCTAGAGACCCTACAACTTCTAAGTAAACCTGCCATCAGTGCGCTTGGAGAATTTCAACGCATTACCAAAATGATCCTATGTGGGACCTTACTTGATCCGCGCCCCTATCTAATTAAAGACTTCGCGTTCCCGATGGGCGAACCCCGTTAATCTTGCGTGCCAGTCCAAGTTAACTTCCTCTACGCCCGATGACAAATTGCAATACCGGAGTAAGTTGTTAACCTAGCCTATTTGTACCGAATTTTGGGTTGTTCGAGATAGACCATTGTCTGCAGCGTGGCACGGTAGGAACCCGACGATCATGGGTCCTCTCAACTACGGCTAGTGATCGGTTAATGATTTATGGCAACGACAATCATCCCTCGCCTGGTTCCAATCCAACCCAATGTAAGCACCCTTGTGTCCTACTGATGAAACCGGCGCAATTATGTGAACATTCACGGACCCAACACCTAACAATGGTTTAGTTGTGATATTTAGCGCAAATTAGAGGCTGGGCTGCGAAAAGATTGTGTTCCGCAATGAACCTAACTCTGACGGCATACCCGAGATCCTGATAGAGGTGAGCCCGAATCATGCTTGATTAACGTAACGTTACATTCCTACCCGTGCGAGCTTTTGCGATTGCATACATACCTCAATTGGCGCCCTGTGTCTTAGCAGCACAGGTCGTATAATCTGAAGTACAATACGCCAGTCTACGTTAGGGCTTTGCTAACACATCACCCAAGTTTGAGCATCACGCACGACGCCCTCAAGGTTGTCGTCAACCTGGAACTCCAGCAGGCTGGGCATGAGACGCATGAACACAGGTTGCGACAGTCAGATGGGGAGCTTCATGAAAATCTGATACAGTAATGATATCGTAGCCCCTCTAACTCGGAGAGCGGTGGACTAATGAGTGAGTCCAATCAGTGCATCGGTTTTTAATGCTCGGGACCACGGCTTGCGGCTGCGCGATCTATCCCTCGCGTTAACAAGGGGAATTAAGCAACAAGCCCCAAAAAAAAGCGGCGTCAGGCTACAGACTTTTTCCCTCAGCAACGCAAGGTACAGTGAGTCGAATGTGGCGTCCGTAATCATAGCAGCTGTCTAGCGTGCTTCGTGTCTTGCAGCAAGGGCGGTGCTACACGCATGTTTCACTCCAGCAATCAGTGACATGATGCCGCTCTTCGGTTGTTCTATGCGCGCGCTGGTCGACACCTTAAGCCCAGTTCGTGCGGAATCCCAATTCACCGTTGGCAGCACTAGATAATACCGCAATACTTCGTCAGAGCACAACCAGATCACTTACAACGACCGCAAGTAGCATCCCTATCAGTGCGTATGTGTCGAGGCGCAAGTACTATTGTCGGATGAGGAGGTGTCCCTCAGGACACAGTATCGCTCCTCATTCGACGACCAGTCGTCTAGTTCTATGATAGGCGAAGTGAGTACTCGGATCACTGACGCCCACTTGGAGTGACGACGCCGGCTACTAGCGATGGCAGTTCAGAATGGTGCCTTCGGCCAAGGCGACACTCGGTCCTAATCAGGCACACGCGGTCTCAAAGGCCACTACTCATTATGAGCTCGCTGGATGTCGTGGCAGTGGTCTTACCCTGCGGCTAGCGACCAGAATTTCTACTAGCCTTAGGCGTACAAGCCGTTGGGCAGGTCCGAGTAACCTCTACACTCTATCATGGACCACCCCATCCATGTTTTAAACACAGAGGTCTTAAGAATAGCGATGGTACGGTTGAGAACGTGGGAGTTTTTGCTCTACAGCAAGCACGTTGCAGTGAAGGAGACTTAATCCTAAATCATATCTTTCGGACGAATATGGACCCAACGCTAACGGTCGGCACCTCTTGGGACACCCGCATGTGAATTGGCCCGTCTTGATTGTGTCCGTAGATGCCAACGGTTCAATCAGCCCGGGCCCATCGCACATCGACGACGAGCTTCTACCTCTTCAGCCTTGCAACGTTCTAGAGTCTTGCTGTTCGTAAATTGAGATCTACTGTGCAGACTTTATCGACCCACGATACGCTAATCCGAGCGCGACTCGGGTCTCGGAAAATTATCCGAGGGAGCTCGTTCAATGCGGCGGAGGCTCTGAAGTGAGTTTAAATAGTCACACGCATTCGGGTCCCATACACCTGTTTCGAATGTCCAACGCGAACTCTGGGCAACAGGTCGCGAGACTTTGAAATGAGGGGGCTGGTGACAGGGCTTTGTAATAGGGGTACTTGAGAATATGTGATATCCCATATGTATTTGACCGGACAAGAATCTCGATCGTAGACTGATAGATCACGATATTGGCAATGGTTCTGGTCATGTTACCGGTGACACTAGTAATAACTGCAGCAGCGTGACGATGTAGGGGAAACAACTTACTCTTGTTTACGACGTATTAGCGCTATACCTCTGTGGGTTGAGTTGTTGTCACGGTCTGCTATAAACATCCTGCTCCGGCACGGTCAAAGACGTCCAATATGCACACGCATCCATCGCCTATCGGACTCAGCTCGCATCGGGAGAAACCTCCAGTCATGGTGAGGCACACCAACAGTGTGGGGTCGGGAACCGTCAAGCCACCTTCAATTTGATCGATCGTCATTACAAGGTCTAAATGCCCAGCGCCGCTCTCCGGCCATGGAAGTATCTCTGCAGCTACGTGCAGAGTCGGTAAGCAATTGACGGCTATAGCCCTATACGTATGTGGACTCAAGGGACATGCTATACCGACTGGCGCTATCGAGGCGAACGACAATTCTATGGGTCATCACCTCATATTTAGATTCAGCCCCTGGTGGAGCGCTTTGATCCAATGTCGGGAGCGGATAATGAGGAGTTCGCTCCTCAAACCTTAGTAGATAACAGCTAGTGCTGTACCAACCTTGGCTGTGCACCAGACGAGCTGGAAGGACCGAGCTGCTAGGGGTCGCAGTATCGGATTGCTCTTTCAGCCTTCTAAGGTAGCGACAGAAACTTAAACTCCTCTCGGTAGCCTTGCTTCGATCTCTCTAACTAGGGTGTACGAGTGGATAAATTACATTAGTCCGGTACAAAAGTCCTCCATTCCGGCGCTAACCCTTAAAAAGCTATGTCCGCTTGATGTAGAAGCGCACTTACGCAGAAAGGGGCGCACGGTAACGATCCGATCTACTTATTCAAAACCGGTTAACGTCGGAATGGGCTCGAAGACGCTAATAGATACACGCGCATTGGCATGGTACCTTCCCAATACTATATCTTTCAAAGAAGGGCGGCGAGCCTATTTACCCACTTCGATTAGGGGATACACACAGGTGATTGAGTTTTCCTGGAACCGTCATCTAATACCGAAGCAGTCATTGCATTTAACTCACTAAATCGCGCCAATTTGCAGCTTATGTTGTTAGGAGGGGCCACGTAGATACTTGCCTAATCCTGAGTATATGAGGGACGATCTGACATGTATCCTCAAGTCGCGCATTCATCCTTAGATTCTAGGAGATGGATTACTAGTGTGTGTCCATAGTTTAACGCAACACATAATTGGTTCGTGTGAATCGTGATGGTTCCTTGGTCCCGACCTCTATGCTAGACCTATACGATGAGCTTCAAAGCTCCACCATCGATGCAACGTGGTCGTACTCAGTATTATGAAACCTTCGCGTTGAACCGCCTGGAAAGAAGCCTATACGTCCAATATCGGAGCTTGTGATCAGCACGCAGGGTATCAGTTAAAGAGCCCAAGACTTAGTCTATGAGCAGAAGACCGAGTTAGGGTATACACCACTGTCAGAATCGCCCTCACTAGGGCTGATATCGTACGTTACACGAGGTCGCTCTTGTCGAGTGCTCGAACGAGTCTATCCCCTATAGGGCGGAAATGTCACGCGTCTCCAGTCGGAGTATCCCGCCACTCGGATCGAACATGACGAGGCTAAAAGGTGTGGCTCAATCGCGCGTAATTCCTGCAAGCCCTCTCTGAGGTGATTACTCGCTGGTTCGAATGAGCCTGCAGGCAGTACCTACAATTCCTGACCACATTCGGAGGTACCTGTTGCGAGGGATGCCCCCATACCTGCGAGGGTAGTAGCATGAAGTATAAAGGCGGCAATCTTCTGTGACAACCCTTCTTCCCTTGGTTAACCAAGACAAGCTGGGATTTATCACCCCTCCCGGGGCACTTGCCCTGTTTTAGAATCACTTGCATCAGTCGCAGTAGTCCGCGTAGATGATGACATGACCCCTCCCACCCATGCAGAGCGTGTGGAGTATATCTCGCGTCGAGTGGCGCCCCTCCAGTCGCATATTTGATAGGTGTTCGTCATAACGCTGGTTTAAATGTACATGCTAGTTAAGTGAAACGATGTCAACATGCGGGCTCGGTTTTGCGCTCCAGGTATATCCTGATCTTGAACTTTTCATGACCGTGTCTGGCGGCAGGGCTGTCGTTTTGCAGGAGGCTTAGGGACCCAGCGACTCCATCTCGAGAATCACGGCTAATCAGTACGCGAAAAAGTTAATCTTCTATCAGTCAGCGGGCGTATAACTTGACCAACAGAGGACGGCAGGTATTGTACTGTGGAGAAACAAATCGTAAGATGGCCGGCGCCAAGTGGATTGTAATATACCGGTGGTTATCGAAGAAGTAAATAGGCCTACCTTCTGGGGAGCAATGGGGTTCTCAGACGCTAACTCTTCGTTCACCTGATATTAACAACACGATTGACTTCAGAGAGGGACCTATCCGGACCCGAAATTACACATAGGGGGGTCCCCGATAAGCCAAGGCGAGGATAGAGGTCTCTGAATCCATGGACTTCCATCAAACCCCGTGTGGGGTTCCGTTCACCAGTCAGGGAGTCATCATGAGCGACTCACTTACTAGTCACGTCCGTGTCAATATGACTGAAGTCGATAAGACGAAGGCTTAATTTACGGAACCCCCGTTTAGTAATCAAGAAACGCATTGGCGCGGGTCTACGTAAGCTCTTCACACCGCCGTCACGAACGTTCACCGGCTTGCATGTGATCGCCGGGTTCCCTTGGAGGGAGTACCTTGGCCCCCCGTATGGTCTTCCAATAGGCACCAAGTATTAGTTGACTGTTGCGACTTAAAATCCAACCCATTCGGACCAATAAGCAATATCGTGCACCCAGGGACTGGAGGGCCTCAGCCCCTTCTGAAACTTAAACCTCGTCTTCTTATGGGTGAAATGTAAGTTTCTACGTCTGGATATCAACGGTTCGGGCCACCTATAGAACGTCTCGTGTGATAGAGATTCGACCAATGAGTCTGGGAAGTTGAGAACACCACCAAGTTTAGAAAAATTCTCCAACTCAGCTACCGGCGACCCAATCCGTTGGGGTAACGCAAAGGGTATTTAGCCGCTCAGAGGCATAGATTAAGATGAAAACGCGACGCCTGCGGCTGTCTTGTGACTACTCCGACCATGCCAGGCTGAGAGGACCCTACGGTGGGTAGTTGGTAGAGCGCCGATATGCTAATGAGTGGAACTGCGGAATGTGTCATATGAGGGTAGCGAATTGGGCTATCACAAAGTCGTGAGCCTGTAAAACGGACGACTCACTAGACCAAGTAAGCACGGCCCTGGCGTGGCGGCGGTACGCGTACAAACCTCCACTGCTTGGGGAATAAGGTGCGAAACACCGTGATCCATTGAGGCGTGGAGGCATAGGCCTGGAGTGCGTACCAATTACGAGCTAAGACCGCTGTCCAGCCCTGGTGATTACGTAGTATAAGCTCGGTACAAGAGCCTGTAATTAGCTTCGGGGGCGGACATCGTTCATATTGCCAGTCACGGCAGAGGCCGCGCTAGCTAGGCCCAGCCCCGATGGAAACACCGTATCGTGCCTAAGGCGTATGGCTATCGCGGCATTGTTAACTTATCAGCAGACCGAATCGGCGATAGTGGACACAACGGTACACGCTAAGCTCTGAGTGCCAGACTCTACCGCGGCGAATCCTTGTCTAAAGGCCCTGTCCTGGGGCTCCCCCACAGAGCAGTAATGGTCGTTCTAGCGGCCGCGTGCATTCAATCGCGCTGGTCAATGCGTTCCATACGTGCCTTAAGCGGGGCAGTGTGATAAGACATTTTGGGGGGTGCATTTCATGTGAGATAATCGTTTCAGAAACCTCTTACGATACCAGCTTACAATTCTCCGACTCGGTACCTAACTTTGCTTATCATCACTAACATAAATCCTCCCAACCAATGTACGGTTTCGAAGGCAGATACCTCTCCCCTGTTAGACTCTAGTTTGCGAGTGCTGCTAGAGGAATCACACCATCGTAACATGGGGAGGCACGCGCTTGGCGTAAGTACCCGCCACGATTACTCTGTTTCATGCGCCAACGCCCGTATAGTCCGTGCTTTTAACGTCCTCAGAAATCACCGACTGATCAGACTGGCGACAATCTGGAGGGAGGTTGGCGAAATGTTGCTCTTCGTTAAAACCTAGTCGTTGGCGCGTATCGCCTCCATAGAACTTCTTACTCAACCTAGTCTGATCGGGACTGGTCGAAAGCAGCCTGCTTAGTAGCATTGGTGTTTATGAGAGCGACGTAGCTTGAGATCTGTAGCCGGATCGATCGACTGATATCACACTTAAAATATGACAGATTTTAACGGTCGATCTCCATATTCCTAGTATCCCAGCACCTTGATACCTCCTGTCCAGCCGACTGCTTGGACCCTAGCACGCGCATATTAAGGGCGCGTTGTAGTTACTCAAAGTAAAGTGGGAGACCCGAGTGATAGGTTACCGCAGGGGGGCACACCTCTGGCACGTAAGGGTGACTGACTCCCCCAGATAACACTGAATTTGTCAGCAGTACATCATAATCTGAGAACATTAGCGAAGACGATGAAACAAGCTGTCTCAATGGATCCCACGTAGGACGAGAGGAAGTGACGGTTATAATACCGACCTCTATGTGTCTAATTAAATCCCTACGGGCCGTAGCGAGCAATGCTCGGAATCTTTAGTAGCGGATCGGTCTTCTACATGGCGCCCAGGTTTGATATTTAACGAGCTAGAGTTTGCTCTAAAGCTCATCTGCTTGGGAAAGTGACCTGGGGTACGACGAAGACACGGGCCTGGTTGCATTTTTAGGCGCGATAGGTAGCATTATCCAGCACGCCGGAGGCACCTGGCATGGGCGTGAAATACAATCCGAAGAGCAGGCATGAAAAAAAAATGAGCGGTCGTGAACCCGAGGGTGCGTCCCTCCGAACGATTGTTCGTTGCCAAGGTAAGGGAGGCCACAGTGACCGACACAACATAGAGGACATCTCAATATGCTTCTGGGTATCACCTGGGTGTGTACAGTGCTCGAACTGTTACAGAGTCCCCGATCTGCGACGTCTGTTCTCGCCTATTCCCCTCTGAAAGGCCCCGACTTGAAAAAACCTACAATCCTAGACACGTTGTGGCCCAGCAACGCGTCTGCTATACAGCAAGTCATTCGTCGAATGGGGCACACAGTATAAGAAGGCCGTGATTCCCAAGGCACATGGCGGGTCTCTCAAAGTCTACGACGTGCGCAACGGGGAAAGGCTGACCGTTTCCGGTAGGCGATCCATCACGTCTGGTTCTGTTATTGTGTTGACACGTTATGAAATATCTGCTGTAGAACGATGGAGGTCTCTACGTAGCTCTGCACGATTCCCTCGGGCCTACACGTTCATGCAATTCTAAGGCGACATCACATGAAAGGGTTGGGTAACAGCCATCTCTGAAGCATTCCCCCAGCTCGCACGGCTCCTTTACCCGCTACTACGATGCGCTAGAACCCACCAACTATGGAGTCAAGTAGTAAGACCATAAAATTTTGGAGCTGAGCGAAAGCAGCTGCGATGTTAGACCAACATTGTCAACAAACTTAATCCGGCAGGCTAGCATCGCAGATTGCCGATACTATGCTAGAGCGATGAATGGGCCTCTTCGAGATCTGCAAAACCATGAACCCTAATCTGGAAAGTTTGATGTTTGGGTGTCGGTAGCCACTCCGGCAACGTAATGTACGCTTGGTTTGATCTAAACATACCTTGTTAGAGTGTCAAGAGGAGGATATTGTCATAGCAGCTACATTAGCCAGCGCAAAGGATAGCTCCGTGGCTGGTCGGAGGCTAGAATCTAGGCAGAACACGTGCGGCTTCTTCACTCTGACGCTCACGCATTGCTTGGCGGTATCTGTCTCATAGTCCGAACTCACAGTAGATAAAGATCCGCGTGATAAGATATGCTAAGTCCGTACTTCAAGGTAGCACCCTCTGTTCATATGGCGATCCTATCATTGCGGGTTCTATACATATGTTGATCCAACCACGGCCGCCGGGCGCGTCGTAGGGATCTAATGGCCAACACATCAATCAAATCAAAGTCCCTCGCGAGGAGCTGTCGGAGAGAACAAGGTTGTCCCGTCCATTAGGTGTGTATGTTGGTACCAGCACCTAATCTGGTCTGGTCAGCAACTGGCTGTTCCAGTATAATAATCTCGAGATAGACTGCCCTGAAAGTAGACATGATAACAATATGTAGGGGGAAATCCCATCGTCAGACTGTGTTGCGGACGATTAGCTTCGCAGAGGTTGGAGCGGCTGTCTTTAAATTATGGATTACGCTTCCGTCGAACGTTGACTAGAGCGTAATCTCTTGGAGGTACCACGAACGTAGTTCCTCACAGTATGTGGCATGGACTACCGGACAGGTCACTTGTATTTATCCTCTTGGGTGAACAATACATACTCCAAGCGTACTCCCTTTGGAGGTAACGGGTTTGGGTTTACATATTACCATGCGTGGTTAGTCCTATAAGCGTTCCGGGCTTTACCATCGCGGATGAACGATCGCGGATCTACCTATCAGCGACATATCTGCCCGTCCACTAAATGATTCAGTATGTGTTAACAGCGTATTAGCATCATCGAGTCATTGAGAGTGGACGTCTCGAAGAGCAATCTGACCCTTTCACTGGGCCATCCCAGTACATATCTATATGCAGACAAAAAGCTCCATACAATATTACCTCTTTCAGTGTGGCAGGTAGACCAACTTGTCGATATTGTCTCATCGTTCCGTGAATGTGCGAGAATTCTTGGTGGCCCTGGAACAGGCGTATAGAAACAATTAAGGCTTTGCTGCGATCGCAGTTCTGGGACAGAGTCTCCTAGACGCTAGATCAGCTAAAACAATGATTGTAGGTAATGTGCGTTGCCGTCGGGCCACCAGCCATTTCATTCGGTTACACCGACTCAGAATTATCTTCTATCGGGTGACTAAACTGATAATGGGGTCAAGCAGTGGTGGTTCCCGCTTTCTTGCAGTGCTTTTGTGAGCAGAATACTCTCGAGCCTCCAGTACGCCTGAGTATGGCGCGCCCACCAATTGGCGCCGTTAATGCGTACACAAGCAGAACCAGTCACATAAGCTTATGCCCTTAAGAAGATCACCGGGTTTGCGCTCCTGATTCAGGTAACCGGACGTCCGTGTACAACGAACCATACTAATGAGTTCTAGCATCGCTAGACTATTTACCAACGCGTCTTGAACCCTTCGACAATTACAAATGCAAAGTATTAGTTGGGTCAGTGACGTAGCTAAATCTCAAGGGCTGCAGTAAGGGGCCAAAAATTCCGCCGCATTAGCTGTTCCATCCAGAACCGGACAGTCTCGATATAAACTTACGGCTGCGAACTGGCCCTTATGAATTACATCCACGTGCTAGTTAACATAGGACGAGCACTTGTCCCGGACTAATTGCCGATCGACCGTCATTCTCCGTACGATGGTACTGAACCGTGTGATGGGTATGTCATACGTTTCACCCCAAGCGACCCTGCGCTTTCATTCCAGTTGAAACGCGTAGAATGGGAGCAAGCTTTGCTGAGGTGCGATATTCGGGAGACCTGGTCAGCAGGATTGATGGAATGGGTTTTATAGTGTGGATTCCCTGCTGAAATCTCTCTTCCTGGTTGAGGGCCTTGCTTCCCTTCCACAATGATTGTAAGATTTCCTAAGGCCCCAGCTGCTGGAACTGTGAGGTCTTAAACCTCTTTCCTTTATAAATTACCCTAGTCTCAGGTATCTTCCTTATCGTGGTATGGTAGATGCTTCCGTCAGAACGGATTATACAGTTGATGTTAAGCCAGCTCCCTCCTTATAATAATCTCTCTCCCCTCTACTCTGCCTGCTCATCTATGTATGTATGTATCTGTCATCTATCCTCTCTATTGCTTTTCTTTTAGAACTCTGACAACATAGACATTAAATAACAATGTTGTATACCTGAATAATTATATAACTACTGCTTGGGGGTGTGTCGTTGTGTTGTGTGGTGAGGGGGTGTAGCAAATGCTAGGTTGATCTAGCCTAACACCTTCCAGATGACCCTAGGTTACTGGCCTTTCCCCAAAGAGTCCTGGTCTACCAGTCATCTCCCACAATTTGGCTCATCTTCTTGGCTCAGGACAATAAATGACCCAGCCAGATCCTCTAGAGTCGACCTGCAGGCATGCAAGCTTGCGGCGTAATCATGGTCATAGACCTGTTTCTGTGTGTAAATTGTTATGCGCTCACATTGCTCCACACAACGAGCTGCGGAAGCATACAGTAGAGCCTGGCCCTAAATTGCCATGGTGGCGTATGAGGAGCTATCACACTCACATTTATGCGTTTGCAGCTCACTGCCGCTTTTCTACACGGGAAACTTCGCTGACAAGCTGCATTTAATGATCGGTCATCGCGCGGGGAGAGCGGTTTTGCGTATTGTGCCGCAGGTGGTTATTTCTCTCAACAGTGAACGGGCACAGCTGATTGTCCCTCACACGCGCTGGGCTGAGAGAGTTGCAGCAAGCCGTACACGCGGGGTTGCCAGCAGGGCGAAATTCCTTGTTGTGATGTGGTCCGAATCGGTTGCAAATGCCCTATAAATCAATAAGCATGTCCGAGGATAGGGTTTTGAGTGTGTGTCAGTTGGCAGAGGTCACTAATAAAGACGTTGACTGCACGTCAGGCAACTCGGAGACGGAGCGAGAAACTATCAGGCGTTACGAGATTAGCACTACGTCAGCTTAAGACATCACCAATCCAGTTTTTTGGGTCAGGTGCGTGAGACTTATCGGAACGTAAAGGAGCCCGTTTTAGCTGAGCGTCATGAACCTGACTAGGAAAAAGAATAAACAAAGCGGGTATCGCGGAATGTACGTGCTGAGCGAAACCAACGCGCTTACGGTCAAGCCGTGCTATGTGTTTGGAGGTAAGTTTCCCTGATAAAGCGGAAAGCAGGGGCTATGTGTTTACGGGGGTCGCATGGAGTACAAGGGCAGGACAAGTTGCGCAGGACATGGTTTTTTTTAAATTGCGGGAGATGGTTGGCTGTCCCCGATGAGATTAGAACCTTGGACGGACTTTTGTTTTAGTTCGGTTCGGTCAGTTGGACTTTGGATTGGTTTTGTTGGGTATTTAAGAAGGGTGTTTGTGGGATGTTTTTTTTGGGTCGAGCATCACTGGACTATGTGTGTCCTGGTATCGTGGCGGGGCCTCGAACTTGAGAGAGGACATTCCGAGATGCAACAACCTAATATGCCGCAAACCTTGTACAGAAGTTGCAACCGCGTTGTGGCTAGGGAACGGATATTCTCCTGGGTACTGACAACCCAGGTCTATCTGGGTTCTATCGACGGTACAGTCAAGCCTCGAGCTTTACTGTCTCCACTTAAGTCTCGCGGGCATTAGGAATGAGTCTGTGGGGTATCAAGGCGAAGTAGCTAGTCCCGTTGTACATTTCGCGCGTTACCTAGGGAGCAGTGAGCTACGAGGGTGTAGTCTCTACACAGCACGGTATATAGGAACGCACGCATAGGGATCATTACCCAGACCTCCTTCCCTATAATTGTTCTATATACATCGGAGTAAATTGTACCTCTCTGACAACATAGGGGGGTTGAAGTTTATGCCGACGACCAAGTCAAACGCAGTACTGTTACACTTTTGCCGGGAATTGTGTAGACCATGCTCAAGGTGGTGGTCGAGTTACTTCTACTATCTACTGCTTCTCATTACACATCTCTTTGGGCTACAGGCCCAGTTATCAGTATTAGAGGCTGCCTGGTAGAGAAAGTGAGATCTGCCGGACGGTCTCAGGTCGCTACAAGTGTGGGTTAGGCATAGGTAAGCCTAAGGGGTAGGTACTCGTTTTATTCGATTGAGCTACAAGGTATAACGTTGCCTCTCACCATTTCGGCTCACGGGACTCTTACCTCGCCTCACCGCTAGACACCCTGCAGACCGTTAGGGGTACTGACAACCGATCAGGCGACTCCTAAAGGACCTCAGCCTCCCAGCACATGAGGTCGCTCGCAACATGCTGTTCACGCAGTCCATACAGGAACAGCAACGAGACAAGAGCGAGAGCAACACTAGACTTGGAGACATACCACCCATCTGTTTACTGACTTCGATTCAGGTAGAAAGACCTGTGGAACTGGGCGACGCGTGCGTTTATCCGTCTTCCAATCTCCACAGAATCTGCTCCGTCAGAGTATGACTAACGCACTGATGTTTTTTGTTCAGGTAACGGATTACGGGGCGAGATCCAGGGGTGCACATTCAACGGGACCCCAAGGAAAAGTTCGGGGTACTTTGGGGCGCGGTCCAACTCTCGGACTTATTGGGCGACGTAAAGCACACCTCTTGCTCATACAGTGCACAGACCGCTGTAGCATTGTTCAAACACAGCCTCCATTCCGCGACAGTCCTGTGTACAGGTGCCTCGGGCCCAAAATTCCCACTTATACCAAAACTCTCCCGGACTTTTTTCGTCCTTGCAGGGGAAAGGACCATGACTCCACGTTTATAGGGGATCGTTGGGGCCGGACAATCGTGTTTCAACATAATAATGACGTAGGAGGTACATTTCTCGGGGCGGTAGGTCAGAAAGAATATAGACCCTAAATTTACGCAAAACGTGCACACGATTGTAGGATGGAGTCGTATTAGGAACTCAGGGCGGGTGGAAACGAGTGTAGTTTTAGGCTTACCATGAACCGTCGCGCGGAAAATGATGACAGAATGGACGGTGCTTTTTTGCTGGGGCCAACAAAACTCGCTTATGTTCGCCATTGGATGTAAAGGTACCCGCTATACTACGCAGCGACCCCTTCACGACGCATTGTTGATTCCAAATGCTTCCGTTAGGGGAGACGTCATGGAGGTGTTGTGAGCGGTCGCTAGGAATCTTCCTCCAGAGCCCAGCGCCACTAGTTAGCAACCCAGAGTTTCATATGAGCTCCAGTAACACGAAGGTACATAAGGAACGCAAGCAAGCCTTCGGGACTACATAGAACTTGGGGTGCTCTAAGCGGGGATCTTAATAGACAAGAGGCCATCGTATGGGTCAAATTTCATGTTTCGTCAGATGCCCTATCCGTGGACACCTATCAAAGCATCATATCCTAAGACTATCTCTCGCTTTGGTATGATTCAATATGCAGCCCTGTCCCGTAAAATGACGGGCCAGAGGATACCTGATCACTACCTATTTCAGCTACCACTGTTCGAAACCTTCTCTGGCCGCGTGAATTATATAATCCCCCAACCCTTCAGTCGGTGCAGTCTACGTAAACTTCCATCTAATCGACGTAGCGTAGGGGGCTACGGGAGTCGCACAAGCCGAAGCATTTCTGCTCTGTTTCGTTACAACTGAATCCTCATCACGGCAGCGCTACCGGTGCCACCTAGTCATCCCCGCGAGTGCTTAACCGTCGAGATGGCTCGAACCGTGTTTCGAAGTGCGCTATTGAACCAGTTAAAAGCAGGGCGACTGGTCCTTGTAGATTGTCGGACTTCGATAACTATCACACAATTTGGCATAAGTATAGTCAAGAAGGCTCAGATAGGCACACGAAAATAGGTTGACCGCTTGAGACACGTTCAGGTTGCCCGGCAACGAAGCGCGCTTGCTCCATAACCTATTGCAGAGCGTAACGACCCTCTTTCACCGTAGCGCCCCCATGTCGGGGCAAGCAGCAATACACGGTCACTTAATCCTCCCACCGATAAATCCTATGTTCTGACTCTACGTACCTCCTAGAGAAGGTGAGGAGACTCGGCTGGAACATAAGTATAACGTTTACCAAACAATGTCATAACACAGAAAGGAGGATGCCCGGTGGGGTCGAAGATAACTGTGGATTCTTGAGCGATATCTGCTAACGGCCAGGCTGTCATGGAAGTCACGCGCACGAAGCTATAAGTTATGAACAGATACCCATGCCGGGGCGGCAGCGGTACCTAGTCCTAAATACCGACACGTCCCTGAGGCCCCGCTAGTCAAGGCTTAAAATATACGCTGATTTGTCTACCAATCGGATCTTCGATTAACTGTCACCGGCATATAAGTCTACCCTGGACCCAGCAATGACGAAAAACTTTTCCGAATGATCATTGTGAGTTTACAGAGATTAGGATACACAAAAAAATTTACGTAAACGTCCAGTTACCCCCTGTGTGACCCTTGGCCACAAACCGCTAAGGCAGCATGGCGCGTTGAGTCCAGGGTTTGTGACACCAGACAGGCTGAATACCCTGTGGCAGGGTTGTTAGCTGTTAGCTATTCGGATCTCGATTTCAGAGGGTCCGTTGAACCGGGCAGTACAAGACACCCCACACCACTGCGCGTGTTGGGTGGTACGTCCGCCAGTCTGCAAATTCCCTACGTTATGCAAATTTCCTTCCCGGCGTTCTTGATACACCTGTACTACTCCTAAAGGCTGAAACTGCTCCGTCTTCGGGACACCCACACAACAGCTACCGGAAAATCGTGAGGATGCACTCAAGGGTCTTGCATTGTTGATCTATACAGCTCTCGCAGGTGTGCTACTGTTTTCCACAATGGCAAGGGGTCGTGTAGGTCTTATCGTAGGAGTACCCCGTGATCTGGTATACCTGCTATCTATTCCACCAAATAGATAGCCGGGGTCTTAATAGTTTTATTCCGGATCATCAAGTGACAGTCCATGGGTAAACGGTAAGTTCGTACGCTGGGCGGTGATCCCCGCTTATAAACGAGCAAACCGCCAAGCAACTATCCCCCAAACGTCACGCAACATTGCGGGCTTCCAGCTAGTAGTTGTCAATGTGCATGTGACTACTTAACTCCACAAAGGGACGCGTTGCAAGCTGCCTGAACTCGTCACGTCTCATGCCTAGCTCCCGGTTGAAACACGAAGACGCGTGAACCTCATCTTTGCCTTACTATTCGCCTCCTAGCTTCTTGATGTGGCCCTGCGACATGGACCCTGGGTATGTTAGTGAAATTACAGAGTGTCGGTACACCTCATGCCTGCACCTAAAAACGAACTTACGTGTATAGAGATTATGTCATCCGTCACCT diff --git a/test_data/volvox/output_prefix3.simseq.genome.fa.fai b/test_data/volvox/output_prefix3.simseq.genome.fa.fai new file mode 100644 index 0000000000..8824ba0ae3 --- /dev/null +++ b/test_data/volvox/output_prefix3.simseq.genome.fa.fai @@ -0,0 +1 @@ +ctgA 49186 6 49186 49187 diff --git a/test_data/volvox/volvox-simple-inv.paf b/test_data/volvox/volvox-simple-inv.paf new file mode 100644 index 0000000000..4c5383ded9 --- /dev/null +++ b/test_data/volvox/volvox-simple-inv.paf @@ -0,0 +1,3 @@ +ctgA 50002 29995 50002 + ctgA 50001 29993 50000 20006 20007 60 NM:i:1 ms:i:40008 AS:i:40008 nn:i:0 tp:A:P cm:i:3790 s1:i:20147 s2:i:10001 de:f:0.0000 zd:i:2 rl:i:0 cg:Z:20007M +ctgA 50002 0 19911 + ctgA 50001 0 19911 19911 19911 60 NM:i:0 ms:i:39822 AS:i:39822 nn:i:0 tp:A:P cm:i:3722 s1:i:19780 s2:i:10001 de:f:0 zd:i:1 rl:i:0 cg:Z:19911M +ctgA 50002 20000 30001 - ctgA 50001 19999 30000 10001 10001 60 NM:i:0 ms:i:20002 AS:i:20002 nn:i:0 tp:A:P cm:i:1883 s1:i:10001 s2:i:0 de:f:0 rl:i:0 cg:Z:4000M1000D1000M1000I4001M diff --git a/test_data/volvox/volvox_del.paf b/test_data/volvox/volvox_del.paf index 8e1c01442b..864ae503ce 100644 --- a/test_data/volvox/volvox_del.paf +++ b/test_data/volvox/volvox_del.paf @@ -1,7 +1 @@ ctgA 45141 0 45141 + ctgA 50001 0 50001 45141 50001 60 NM:i:4860 ms:i:90254 AS:i:85398 nn:i:0 tp:A:P cm:i:8494 s1:i:44546 s2:i:0 de:f:0.0000 rl:i:0 cg:Z:28498M4860D16643M -ctgB 6079 0 6079 + ctgB 6079 0 6079 6079 6079 11 NM:i:0 ms:i:12158 AS:i:12158 nn:i:0 tp:A:P cm:i:1213 s1:i:6077 s2:i:6027 de:f:0 rl:i:0 cg:Z:6079M -ctgB 6079 50 6079 + ctgB 6079 0 6029 6029 6029 0 NM:i:0 ms:i:12058 AS:i:12058 nn:i:0 tp:A:S cm:i:1203 s1:i:6027 de:f:0 rl:i:0 cg:Z:6029M -ctgB 6079 0 6029 + ctgB 6079 50 6079 6029 6029 0 NM:i:0 ms:i:12058 AS:i:12058 nn:i:0 tp:A:S cm:i:1203 s1:i:6027 de:f:0 rl:i:0 cg:Z:6029M -ctgB 6079 100 6079 + ctgB 6079 0 5979 5979 5979 0 NM:i:0 ms:i:11958 AS:i:11958 nn:i:0 tp:A:S cm:i:1193 s1:i:5977 de:f:0 rl:i:0 cg:Z:5979M -ctgB 6079 0 5979 + ctgB 6079 100 6079 5979 5979 0 NM:i:0 ms:i:11958 AS:i:11958 nn:i:0 tp:A:S cm:i:1193 s1:i:5977 de:f:0 rl:i:0 cg:Z:5979M -ctgB 6079 0 5929 + ctgB 6079 150 6079 5929 5929 0 NM:i:0 ms:i:11858 AS:i:11858 nn:i:0 tp:A:S cm:i:1183 s1:i:5927 de:f:0 rl:i:0 cg:Z:5929M diff --git a/test_data/volvox/volvox_ins.paf b/test_data/volvox/volvox_ins.paf index effcf00f16..b0aba955a7 100644 --- a/test_data/volvox/volvox_ins.paf +++ b/test_data/volvox/volvox_ins.paf @@ -1,7 +1 @@ ctgA 54801 0 54801 + ctgA 50001 0 50001 50001 54801 60 NM:i:4800 ms:i:99974 AS:i:95178 nn:i:0 tp:A:P cm:i:9413 s1:i:49418 s2:i:0 de:f:0.0000 rl:i:0 cg:Z:31198M4800I18803M -ctgB 6079 0 6079 + ctgB 6079 0 6079 6079 6079 11 NM:i:0 ms:i:12158 AS:i:12158 nn:i:0 tp:A:P cm:i:1213 s1:i:6077 s2:i:6027 de:f:0 rl:i:0 cg:Z:6079M -ctgB 6079 50 6079 + ctgB 6079 0 6029 6029 6029 0 NM:i:0 ms:i:12058 AS:i:12058 nn:i:0 tp:A:S cm:i:1203 s1:i:6027 de:f:0 rl:i:0 cg:Z:6029M -ctgB 6079 0 6029 + ctgB 6079 50 6079 6029 6029 0 NM:i:0 ms:i:12058 AS:i:12058 nn:i:0 tp:A:S cm:i:1203 s1:i:6027 de:f:0 rl:i:0 cg:Z:6029M -ctgB 6079 100 6079 + ctgB 6079 0 5979 5979 5979 0 NM:i:0 ms:i:11958 AS:i:11958 nn:i:0 tp:A:S cm:i:1193 s1:i:5977 de:f:0 rl:i:0 cg:Z:5979M -ctgB 6079 0 5979 + ctgB 6079 100 6079 5979 5979 0 NM:i:0 ms:i:11958 AS:i:11958 nn:i:0 tp:A:S cm:i:1193 s1:i:5977 de:f:0 rl:i:0 cg:Z:5979M -ctgB 6079 0 5929 + ctgB 6079 150 6079 5929 5929 0 NM:i:0 ms:i:11858 AS:i:11858 nn:i:0 tp:A:S cm:i:1183 s1:i:5927 de:f:0 rl:i:0 cg:Z:5929M diff --git a/test_data/volvox/volvox_inv_indels.paf b/test_data/volvox/volvox_inv_indels.paf new file mode 100644 index 0000000000..2d2974c93e --- /dev/null +++ b/test_data/volvox/volvox_inv_indels.paf @@ -0,0 +1,7 @@ +ctgA 49186 26805 49184 + ctgA 50001 27258 50001 20447 24398 60 NM:i:3951 ms:i:29302 AS:i:26545 nn:i:0 tp:A:P cm:i:1101 s1:i:8814 s2:i:212 de:f:0.0807 zd:i:2 rl:i:0 cg:Z:39M3D58M1I4M1I3M2D20M3I2M1D42M15D1M1D15M6D2M4D24M1D10M1D64M1D62M1I11M2D10M4D23M1I25M4I23M1I20M2D3M1I7M10I7M1I4M1I5M1I15M10I21M1D3M3I6M1D12M1I6M1D30M1I5M1I6M9D4M1D33M1D2M1D8M1D3M1I5M5I25M2I10M1I34M1I6M5D4M4D6M1D34M1D13M1I25M2D19M1I25M1D1M2I24M1I3M1I20M1D2M1I7M1I3M2I6M4I14M2D29M1D3M2D7M1D4M1D6M2D8M4I30M1I8M4D18M1I7M1D3M1D1M1D9M1D11M3D21M5I6M1D1M1D2M1I5M4I27M1I19M1D7M1D9M5I50M1D3M7D33M1D5M1D2M1D32M34D1M1D6M3I40M1D21M2I33M1I2M1I14M1D4M2D17M1I30M1D9M1I53M1D22M1I5M1I6M2D13M4I19M2D6M2I3M1I15M3D4M1I15M1I3M1D8M7I1M1I56M3D12M1D17M1D5M1D52M7D13M1D2M1D4M1I2M3D9M1D18M1I9M6D9M1I1M2D5M1I6M2D3M3D1M2D3M2D17M2D11M5D4M4D12M1D6M1D9M2D3M1I12M1I7M1I17M1I6M1I13M1I15M2I19M2D6M2I2M1I9M2D20M12I12M1D35M3D6M1I23M3D7M1I8M2D7M1I10M1I41M2I20M1D34M1I2M1I10M1I12M2D10M1D13M13D18M1I27M2D13M2I26M3D4M1D9M2I5M3D39M1D19M1D9M1I12M2D33M1D22M1I31M1I28M1D11M2I5M1D22M1D3M27D38M1I8M1D8M1I29M5D8M1I46M1I18M1D6M1D61M1D16M6D4M3D13M1D19M5D4M1D5M2D6M6D4M7D7M1D7M1D7M1I41M5I3M1I11M1D17M1I3M1I3M1I5M1D11M2I30M5I4M1I6M1D18M1D10M1D50M1I8M1I16M1D9M26D11M1I44M1D19M1I9M2D2M1I18M1D8M15I19M1D13M1I8M1I6M1I4M3I7M1D4M1D3M1I2M1I3M3D1M1D20M1I15M1D11M1D22M2I41M2D12M1I7M2D6M8D3M7D2M2D27M1D19M1D2M1D41M1I13M1D38M1D11M4D7M1I14M1D1M3D37M1D2M1D4M1D3M4D2M1D21M1D18M1I11M2D13M1I23M25I15M1I2M1I3M7I18M1I35M1D11M1I5M1I45M4D5M1D7M1D6M1D35M1I34M2I9M1D6M1I47M1I10M1I34M1I5M1D6M1I1M1I4M8I28M3D6M1I19M1D18M1I7M5D5M1D22M1D3M2I5M12D8M1I71M2I23M1I6M1D3M2D5M2I12M1D12M1I24M1D14M1I3M1D24M1D26M22D42M1D9M1I1M1I9M1D6M1D12M1I6M3D17M12D7M1I11M2I19M1D17M1D17M2I2M2I9M2D10M1D6M2D29M1D3M1D19M1I4M3I8M1I1M2I55M1I43M3I42M1I33M22I22M1D3M1D24M1I40M1D21M1D2M1I5M1D2M1D18M1D20M2D12M1I22M1I4M4I84M1I21M1I35M1D3M4D13M1I5M1I10M4I11M1D20M1D5M1D16M1I14M1D8M1I58M1I9M1I5M26I6M1D30M2I7M1I9M1D9M1I38M5I1M2I8M2I14M2D1M2D13M1D2M1I12M1D21M1D8M1D17M2I4M4I7M1I12M1I23M1D12M1I8M8D12M1I5M1D18M1I39M1I2M1D9M1I5M2D16M1D23M3D6M2I17M3D1M1D13M1I49M1D25M1I5M1I13M1I9M1D21M1D6M1I13M1I2M2D3M2D3M1D3M3D22M1I1M1I10M4I1M4I3M2I7M2D3M2I15M1I9M1I3M4D6M7D1M1D16M1I9M1D6M9D4M3D7M1D5M1I7M1D5M1D31M1D10M1I29M1D6M5D4M1I3M5D22M1I1M1I4M4D2M1I15M2I4M1I1M1I5M2D5M2D7M1D6M1D1M1D14M4D25M1D30M1I30M6D15M3I12M1I9M1I72M1I9M1D8M1D6M1I5M1D18M5I3M1D4M3I3M7I17M1D5M1D19M2I3M1I14M3I2M1I10M1I2M1I38M4I2M1D25M1D23M2I4M1I8M1I3M19I16M1D21M1I21M2I6M23I9M1I9M11I3M5I17M1I2M8I5M1I5M1D12M1D25M1D20M1D10M3I2M4D3M1I18M3I6M2D35M1D2M3I2M1D9M1I1M1I14M1D2M10D1M2D4M1I6M5D3M6D1M6D18M1D8M1I6M1I49M1D18M1I1M1I7M2D24M1D13M1D1M26D8M2D2M1I19M1D13M1I9M1I5M1I3M10D10M5I1M1I15M1D27M1I15M1I30M2D12M1D10M3D22M5D4M1D9M1I33M1D3M1D12M9I4M1D6M5I3M8I8M1I33M5I13M1D22M1D12M1I17M6I5M3D12M1I12M1D1M1D7M1I44M1D1M1D12M1I47M1D8M2D27M1D1M1D35M1D9M1I6M3D52M3I6M1I27M1I8M1I13M1D13M4I16M3I43M1I13M1D31M1D63M1D30M1I6M1D1M1D15M20D18M2D15M9D8M3D16M11D9M1D18M1I27M8I2M3I1M1I11M2D24M2D27M1D2M1D52M1I50M1I4M1D7M7D23M1I6M1D34M2I15M2D11M1I9M1D21M6D1M1D11M2I2M1I7M27D18M1I2M1D10M4D12M3D1M2D15M5D1M1D6M6D6M1D40M5I8M1I8M35I6M1I27M1D15M2I3M18I5M4I2M1I4M4I8M1I39M2I5M6D9M38D3M1I24M1D2M1D17M1D9M2D7M9I12M1I1M1I15M1I10M2D11M1D10M1D19M3I20M1D43M1D8M1I4M3I1M2I2M3I5M3D13M1I9M5I6M1D6M1I2M2D23M1I8M1I19M1D12M1I20M1D5M1I16M2I10M15I5M2D7M1I14M4D13M1D16M1I4M1D5M2D5M2D3M1I14M17I6M1I16M6I13M5D1M1D6M1I4M1D2M2I17M1D33M1I4M1D16M1I44M1I19M1I19M1D10M13D13M1I5M2D12M1D4M1I10M1D15M1D5M1I7M1I8M1D30M1D3M1D6M2I30M1D33M2I7M4I8M1D2M1D16M1D20M1I3M1D35M1I5M1D10M4D5M1D3M1D19M2I5M2I8M5I49M1D14M1I15M1I11M1I4M3D5M1D50M1I11M7I2M4I9M1I18M2I11M1I5M2I13M5I3M2D19M2I11M2I47M4I1M2I7M1I26M1D4M1D6M1D6M1I24M2I5M3D9M1D13M1I9M1I22M1D3M1I12M1I4M4D32M1I2M2I15M3D3M1D10M1D7M1D11M1I4M1D3M7I2M1D11M1I4M1I1M2I9M8I15M1I9M1I44M1D4M2D10M1D15M1I23M5I4M2D7M4I14M1D10M2I35M1I5M3D25M1D17M1I18M5D5M2I15M2D21M1D1M1D55M7I11M1D5M1I13M2I13M1I12M1D18M1I9M3I29M1I5M1D11M2I11M1I4M4I13M1D10M3I83M1D24M1D6M2D4M1I5M1D6M1D34M1D4M2D11M1D4M2D47M10I7M1D40M3D11M2I39M1I19M1I2M1I3M1D22M2I17M2I4M1D2M1I7M1I27M1I7M1I17M1I7M1I15M1D8M1I3M1I1M1I44M1I1M3D4M1I2M1I19M1D15M1I6M4D28M1I8M1D13M1I4M1D11M1D19M2I69M1I43M4D24M1D25M2I13M2I16M1D4M1D8M1D42M1I11M1I7M3D1M2D1M5D14M2D2M1I28M1D5M1I25M1D46M7I8M2I30M1I4M1I19M1D22M1I30M1D10M5I7M1I5M1I3M2I12M11D3M1I7M8D2M6D2M3D7M2D4M1I1M2D5M1D18M2I1M1I1M1I4M1I10M3I18M1I27M2D5M1I6M1D7M2I7M22D30M1D31M1D19M2I46M1I3M1I27M6D13M1I37M1I12M2D14M28I3M1I3M1D18M6I25M1I8M1D9M2D17M1D2M4I10M1I44M1I6M1D14M3I10M2D7M1D10M2I9M15I18M1D13M1I15M1D25M1D13M1I1M1I18M5I17M1D16M1D16M1I29M1I24M1D17M1D15M2I10M1I11M3D3M1I2M1D50M11D2M2D2M1D1M2D1M1D9M1D18M2I41M1I26M1I25M1I22M4D6M2D2M13D23M1D11M1D25M1D7M3I9M1I4M1D16M1I4M1D2M1I6M1I20M1D37M2D42M1I3M1D47M1D11M1D3M1D14M1D7M1D19M7D11M3D10M2I7M1I11M1D48M1I2M1I10M4I2M1I18M1D67M2D4M4D6M1D17M1I6M2I23M1D9M1D1M3D8M1D14M2D2M3I6M4D9M1D5M5D22M1D6M2D25M8D1M2D4M1D2M4D31M2I7M1I11M11D16M2D11M2D14M1D8M2D3M1I21M1I6M2I1M1I6M1D27M1D1M1D33M1D21M2D1M1D8M1D5M2D6M1D8M1I34M1D3M4I7M1I2M1I16M1I1M1I46M1D12M1I7M1I31M6I10M1I1M1I1M1I5M11I10M1I19M1D22M1D10M1D11M1I8M1I24M1D6M2I7M3D2M1D17M1I24M1I2M4D19M1I14M2D17M1D9M3I16M1D10M1D25M1I26M11D3M2D5M1D2M8D30M2I15M2I13M1D6M5D5M3D17M2D17M2I18M2D11M1I11M2I3M1D18M1D9M1I8M1I16M1D2M1D13M3D1M1D10M1D11M1I8M3D3M1I26M1D4M2D8M1D3M2D4M1I4M2I11M1D10M1D4M1D8M6D1M1D5M7D2M1D5M8D3M2D13M4I1M1I17M1D21M1D22M1I10M1D41M1I2M1I2M1I5M1D41M1D29M1I5M1D5M1I8M1I15M1D9M1D22M1D29M4D7M1I9M1D8M1I32M2D15M1I15M1I23M1I7M10I19M1D38M1I7M1I4M1I16M28D29M7D28M1D52M2I6M1I1M1I1M1I3M1I25M2D15M1I9M5D6M1D12M1I6M1I4M2I11M5D1M2D1M4D15M3I11M3D4M1I8M1D40M6I12M1D17M1D9M5D1M1D2M1D25M1I23M2D9M1D11M1I3M2D28M1I12M1I17M15I10M1D9M1I2M1I8M4I11M1D9M1I6M1I8M1I40M2D3M2I8M1D11M1D16M1D6M1I7M1I34M2I27M1I5M1D3M2D39M1I30M1D22M1I10M3D61M2I19M2I6M1D7M1I23M1I4M1D3M1D6M2I12M2D11M14I13M1D6M4I9M2D11M1I17M1I4M1D8M1D3M1I4M1I35M1D47M1D17M1I7M1I2M1I25M1I13M1D3M1D18M1I13M2D10M2I19M2I6M1D6M1I7M1I11M1I6M3D2M1D11M1I6M1D17M12I1M1I2M1I9M1D1M1D7M7I4M1I9M8I12M1I37M1I25M4I1M1D7M3D13M1I4M1D2M1D43M1D11M1I4M1I6M1D31M2I38M2D12M2D6M2I39M1I19M6I1M1I12M1D16M1D50M1D40M1D24M1I4M2D5M1D5M2I13M2I4M7D2M2D2M7D1M3D15M1I8M1D10M10D41M1I34M1I10M1D11M1I3M3D7M3I23M9D47M2D19M1I36M42D2M1I3M1D3M1D7M1D5M3D6M1D16M1D13M1D39M14I7M1I16M1D19M1D14M1D8M1I34M2I3M1D6M1D12M2I3M1D10M2I1M1D6M39D2M2D16M2I3M2I3M1D23M1D14M3D3M2D6M3D2M1D1M1D10M1D25M1I17M2I33M2D2M1I7M1I36M2D13M1I9M1D6M3I11M1I2M1D8M1D21M1I34M1D12M2I3M1I8M4I11M1D59M5D18M2I11M1I4M7D26M2I77M5D11M1I16M2D6M5I19M1D13M1D1M2D23M1D6M2I11M1D35M1D10M1I14M1D30M1D12M38D2M1D3M1I2M1I5M2D7M1I1M2I3M1I57M1I31M1I9M1I63M1I40M2I3M1I25M1I4M2I21M15I10M1I30M2D3M2D6M2D15M1I6M1I48M2I27M1D6M1D77M2D3M1I22M3D20M1I14M1I29M1I16M1D20M1D5M30D25M6D7M1I17M1I5M14D16M1I3M1D15M1D41M1D13M1I13M1D5M2D19M2I6M1D29M1I2M2D16M1D6M1D8M1I4M9I13M3D10M1I16M4D27M1D1M3D6M2I9M5I38M1D3M2D1M1D4M2I5M1I24M1D18M1I3M5D2M3D3M1I3M1D1M1D5M1D4M1D11M5D3M8D6M1I3M2D5M1D11M1I19M1I1M1I15M1I3M1D15M1D5M3D1M1D12M1D23M1D1M1D6M4I2M4I9M1I17M1I4M1D18M3I7M1I51M11D11M1D12M3D17M1D14M2I12M4I10M1D26M1I45M1I3M1D15M2I11M1I28M1I4M2D9M1I31M3I15M1I11M1D3M4I10M1I21M1D31M8I23M4I24M1D13M5I19M2D1M1D5M10D10M1I23M1I20M2I32M1D5M1D23M1D9M1I14M1I22M1D6M1D19M3I14M1I18M1D10M1D7M1D14M1D46M1D7M21I5M1D17M3D2M4D7M1D16M1I2M1I14M1I3M1I4M1I16M2I42M1I7M1I17M5D1M1D29M13D24M1I3M8I5M12I9M1I23M1D8M +ctgA 49186 2214 15925 + ctgA 50001 2212 16198 12504 15030 60 NM:i:2526 ms:i:17647 AS:i:15934 nn:i:0 tp:A:P cm:i:637 s1:i:5099 s2:i:212 de:f:0.0837 zd:i:3 rl:i:0 cg:Z:10M1I4M2I21M1I10M1I14M1I5M1D29M2D9M1I15M1I27M1I12M1D5M1I11M1D7M2I4M2D9M1D4M6D32M1D13M1I55M1I8M1I1M1I12M1I2M1D22M2I56M1D11M1I5M5I17M3I3M1I13M1D16M1I50M1I2M1I10M1D1M1D42M1D2M2D33M22I3M1I2M1I6M1D8M2D5M1D50M1D6M3D6M2D12M3I13M1D9M5D1M1D10M1I3M1D5M1I3M1I4M1I24M1I5M2D30M1D3M1I15M1D9M1I3M1I18M1I9M1D11M2D6M1I23M4D1M1D22M1I3M1I11M2D1M1D7M1D4M1D18M1I6M1I5M2D5M1I66M2I3M1D8M1I13M1I8M1I3M2D4M1D20M1I5M2I22M1I6M8I14M1I6M1D8M2D5M9D7M2I4M1D11M1I20M2D2M1I33M8I10M2I7M1I18M4I5M1D4M4D1M1D33M3I10M1D20M2D8M1D9M1I10M1D7M1I8M1D6M2I66M1I3M2I2M1I24M1D63M1I14M8I7M1I13M1D31M1I16M4D41M1D3M1D4M23I9M1D2M1D8M1I1M1I12M1I2M1I3M1D33M1I24M2I3M1I4M1I12M122D6M4D2M1D13M1I3M1D35M1I4M1I13M1D32M1D6M1I5M1I5M1D12M1D8M1D48M1I2M1D19M1D9M1I24M1D13M1D8M1I5M7I5M2D10M1I11M3I15M1I6M2I4M1I18M1D14M1D3M1D1M2D17M1D12M4D15M1I3M1D31M1D1M1D1M1D1M1D12M5I2M1D31M2I18M1D5M20D10M1I20M3D11M1I14M1I1M9I4M4I2M4I2M3I4M1D10M1D50M1I14M1I11M8I11M2D9M1I20M3I5M1I27M1D5M3I9M2I9M3D34M1I21M1D15M1I9M1I2M1I2M1I8M1I26M3D4M1D3M1I2M1I25M1I1M1I14M6I45M2I21M1I17M2D23M1D6M2I8M2D20M1I24M1D9M1I1M2D6M1D3M1D9M8I4M2I30M2D8M1D7M1I50M1I3M3I26M3D17M1D17M1D5M1D8M1I29M2D2M1I3M1I6M1D11M3I20M3I19M1I17M2I33M1D35M2D7M2D3M18D1M3D10M5D11M1D8M1D6M1I9M1I4M2D5M1D13M1D22M1D16M14I22M1D3M1I19M1D53M1I13M1I20M8I8M1I6M6D28M3D28M9D23M5D26M1I6M1D5M1D24M1I5M2I13M1I2M4I2M1I4M4I8M1I14M1I12M1I28M1I18M4I10M2D8M1I62M1I14M2I5M1I18M1D1M3I4M1I13M1D4M2I3M1D8M4D2M8D71M1I8M4I23M1I15M5D13M1D6M1D8M7I16M2I13M2D27M1D46M1I20M2D6M1I9M1D4M1I7M1D6M1I2M6I11M4D25M1I22M1D14M2D20M1D30M3I7M3D4M1I22M1D4M1D10M1D20M1I21M3I3M1I8M1D14M1I10M2I45M1D14M1D17M1D27M2D3M1I2M2I4M1D13M1D5M4I6M2I11M1I11M4I6M1I15M1I8M1I6M9I1M1I19M1I4M1I8M2D9M1I7M1D33M1D19M1D3M1D35M3D4M1D6M2D16M3D1M1D30M26D28M1D7M2I12M2D10M1I7M1D16M1D4M1I18M1I33M1I31M1I7M1I12M1D7M1D16M5I3M1I4M1D4M1D28M8D12M1D4M1D7M3D11M1D15M1D12M1I12M3I6M2D4M1D16M1I11M1D5M1D2M2I22M4D14M2I19M1D7M3I17M1I22M5D6M1D14M2D1M1D12M1I11M10I16M1D21M1D24M5D17M1D48M1D3M1D4M1D12M1D13M4D9M1I22M1I21M1I12M1D11M3D2M1D9M1I10M1D4M1I4M1D18M1I3M1D18M2D4M1I21M4D5M3I3M1D21M1I33M1D19M1I38M2I27M1I7M1I15M2I8M5D2M1D15M1I7M2I2M1I11M1D24M1I4M1D14M1I51M1D47M1D3M4D4M3D3M1I3M1D13M1D3M1I13M1I10M3I34M1D3M1I17M1D5M1I8M1D28M1I13M2I2M1I16M1D21M2I22M5I8M1I3M1I9M1D12M2D11M1I7M1I2M2D10M3I26M4D12M1D3M1D12M7I12M1I5M1D5M8D5M1D8M1I31M1D3M1D6M1I12M2D4M1D24M2I10M1I17M1D1M3D11M1D8M2D16M1D18M2D3M6D2M1D6M24D10M2I14M1I11M1I4M1D10M1I34M4I14M1D21M1D6M43D8M4D2M1D12M1D7M5D4M2I15M1D39M1D6M1I9M3D1M3D2M2D4M4D2M3D1M1D2M1D10M1I16M3D17M1I1M1I28M1D10M1D11M1D28M1D11M2I17M1I14M1I7M2D6M1D4M7D3M7D10M1I6M1I57M2D4M1D5M1D18M2I50M5I38M1D6M1D15M1D5M1I58M4I28M1I15M1D14M3I4M2D4M1I11M4D4M1D44M1I25M1I8M2D12M1I7M1D31M1D9M26I5M1I19M1D1M1D9M1D5M1D7M34I1M1I38M1I1M1I21M1D4M11D29M1D11M1D7M3I19M2I36M1D12M1I21M1I2M7I23M2I19M1I6M1I16M2I2M3D10M1D11M10I5M1I15M2D18M1I17M1I7M2D6M2I6M1D27M1I17M1I23M5I29M4D3M1I41M1D33M1D9M1D11M6I25M1D17M3D6M1I8M1I14M1D13M1D12M1I16M1I12M1I3M1D12M1I28M1D32M1D15M1I32M6I2M2I8M1D6M1D23M22I30M1I9M10D58M3I7M1D12M1I10M6I31M1D14M1I38M1I13M3D14M1D23M1I6M1I10M3I22M1I10M1I8M5I67M1I13M4D3M5D6M1I4M1I8M3D8M3I2M1I11M1D11M1I4M2I1M1I2M1I5M3I9M2D1M1D4M1D6M2D5M1D5M1I8M4D8M1I7M2I3M3D15M6D1M1D9M1D4M1D6M1D36M1D7M1D18M1I5M1D3M2I20M1D13M4I7M5I2M2I43M1I5M1D12M1I7M1D31M1I13M1D13M1I6M1D1M1D4M3D7M1D31M2D26M1I7M1I8M1D7M1I17M1D18M1I6M1D4M7D13M1I6M1D7M1I31M1D5M1I15M1D20M1I20M1I26M3I2M3I1M9I5M1I24M2D25M1D14M1D5M3D2M1I7M1I3M1I19M1I17M1I16M2I5M5D8M1I15M1I3M2I5M5I15M1D13M3D7M1D9M5I10M2D5M1I10M2I1M1I2M1I9M8I2M1D5M1I6M6D7M1I13M3I7M1I3M1D15M2D18M1D3M1D2M1D5M1D5M2I9M1I1M1I27M1I11M1D16M8I1M2I8M1I15M1I6M1I10M1D24M1D3M1D19M1I12M1I24M1D8M1D2M1D2M1I6M1D31M2I5M5D16M5I5M1I6M3D11M14D12M3I29M1I12M1I5M23I2M1I4M4I6M1D41M2I21M1D17M1D5M1D25M3I18M1D6M1D5M1D42M1I3M1D31M1I3M1D14M1D21M1I8M3D4M1I19M6D10M2I12M7I32M8I2M1D18M2D31M1D15M1I21M1I6M1I3M1I39M2I1M1D10M1D3M1I36M12D45M1D7M1I4M1I1M3I7M1I3M1D5M1D6M1D14M1D14M1I18M1D1M1D6M1I1M2D1M1D4M2D8M1D27M5I20M12D11M1D9M1D29M3D26M2D4M2D3M1I6M37D1M1D4M5D3M1D3M6D3M4D16M1D27M2D3M1D2M2D60M4D5M4I8M21D26M1D16M1D3M1I8M1D22M1I8M10D3M2D3M3D13M36D4M1D40M1I6M1I5M1I7M2I7M1I13M2I2M1D8M2I6M1I9M1I3M1D4M4I3M1D5M1I11M1D4M4D23M1I21M1I17M1D37M2I10M1D36M1D8M1D1M1D9M1I6M1D1M5D1M7D3M2D2M10D2M2D12M1I27M2I1M1D16M1D18M1D25M1D29M1I12M2I18M1I2M3I3M7I13M1D30M3D2M2D18M1D4M2D36M1I19M1D19M1I6M1I4M3D20M2D13M1I61M1I12M3D1M1D6M2D4M2D3M1I7M1D34M1I9M1D5M5D9M3D11M1D4M1I4M1I3M4D2M3D1M8D22M1I10M1D12M1D57M1D25M1D58M4I26M1I2M1I2M1I7M2I43M1I3M1D6M1D9M2D20M1D5M41D5M1D30M1D32M1D12M2I9M4I17M2D44M1D2M1I29M14I1M1I17M1I8M1D27M4I14M1D9M1D4M1I6M1D12M5D26M3I2M1I2M1I18M +ctgA 49186 18755 26306 + ctgA 50001 19011 26774 6872 8319 60 NM:i:1447 ms:i:9750 AS:i:8692 nn:i:0 tp:A:P cm:i:376 s1:i:3010 s2:i:212 de:f:0.0832 zd:i:3 rl:i:0 cg:Z:17M1I43M1D4M7D2M7D13M2I3M1I7M1I19M1D5M1D32M1D5M2I63M4D20M5D50M2I12M1I16M3D8M1I19M2D38M1D28M7I2M1I5M4I1M1I3M2I2M1D25M1I10M1D2M1D19M1I5M1D32M1I8M5D4M8D7M1D1M1D26M2D16M2I6M7D14M4I2M1I3M2I4M1D32M6I8M1D12M1D10M1D24M4D2M1D43M1D22M1I11M1I30M2D31M1D19M1D2M1D10M3I48M1I26M4D11M1D4M1D11M2D4M1D18M7I8M1I10M1I2M1D61M1D4M1I18M1D13M3D3M2D5M8D47M1I30M3D6M1I44M2D14M1I40M9D1M2D24M1D16M39I5M1I38M3I22M4D35M1I19M1I19M1I26M1D4M1I10M4D39M1I10M2I10M1I10M1I3M1D1M5D16M1I13M1D11M1D25M1D21M8I22M4D11M1D9M5D17M1D73M1I36M6D4M1D19M1I52M1I15M24I1M1I2M2I15M1I2M6I2M1I28M1D41M4I46M4I14M1D7M21D4M1I20M1D2M1I15M1I3M1I29M1I20M5D37M2I9M1I16M1I24M1I22M3D1M2D47M1I6M1D6M1I19M1D18M1I1M1I18M1I3M1I2M1I5M35D8M1D13M1I7M4I31M1I6M1D83M2D1M1I26M1I15M1I3M2D5M2D12M1D42M1I1M2I12M4D2M4D6M26D8M3D1M1D2M4D9M1D30M2D3M2D28M1I4M1I41M1D18M1I41M1I12M1I19M4I35M5I31M4I31M1I3M1D11M1I25M2D2M3D30M1D5M1I8M1D22M1I7M1D32M1I29M6D1M7D10M3D1M1D5M1I15M7I18M2I10M4D42M1D15M1I13M1D5M1D2M2I38M1I9M2I7M3I20M1D15M1I5M1D3M1D8M2I12M1D10M1D3M1D6M1D30M1D94M1I18M1I4M5D3M1D29M1D2M1D2M1D5M1I4M1D13M1I9M6I10M1D6M1D12M2I5M2D1M1D11M2D1M1D10M5D9M8D17M1D35M1D4M1I16M5I1M1I9M8I21M23I2M1I38M1D1M1D8M1D27M1I42M1I9M2D1M1I5M1D23M2I5M1I28M1D25M1D24M1D5M1I21M1I4M4I7M1D7M1I25M21I5M1I4M1I42M2D26M1D7M1I10M1I1M1I19M10D4M1D14M1D42M8D28M3D2M3D13M1I17M1D11M1I22M1I19M1I1M4I9M2D2M1I14M8I9M2D9M1I13M2D11M3D2M2I5M1I3M1I6M3D10M1I9M1D21M1I5M2I9M2I10M1D3M2I9M1I21M3D23M1I8M1I3M1I26M1I33M8D7M1I9M10D10M1I7M1D52M10D13M1I6M2D39M1D6M1D26M1I4M1D4M32D24M1D21M2D1M1D7M1D1M1D15M1D5M1D24M2I1M1I6M1D2M1D19M35D41M3I6M3I15M2D3M1D14M2I9M3D19M2D26M1D30M1I6M3D32M1I5M1D36M1D8M1D2M2D2M1D6M3I3M7I2M2I17M1D12M1D6M1I40M3D28M2I2M2I21M1D4M1I9M1I4M1I31M2I11M1D31M1D3M2D3M5D7M4D7M1D6M1D6M8D4M1I1M2D19M3I14M1D7M1D12M1I4M3I2M4I11M1I8M1I17M1D11M1D3M1D15M17I8M3D42M1I17M1D25M1I15M1I11M2I7M1D23M4I14M2D20M1I9M3D2M1D17M1D34M1I3M1I4M1D13M1I2M1I6M1I6M1D12M2D1M4D2M4D4M1I16M36D6M1D32M1D27M1D4M1D2M1I11M20D16M3D4M1I16M1D27M1D14M1D21M2I14M1D10M3D5M2D3M3D2M1D1M4D5M1D2M3D7M1D7M1I9M1D5M1D4M1D16M1D9M9D1M1D2M1D4M1D3M2I6M2D5M1I22M1D3M1I8M1D8M3D43M1I4M1D31M1I17M3I8M2I12M6I13M1I16M1D11M2D4M1I5M2D2M2D2M1I29M1I12M2D13M1I7M14D1M1D3M5D10M1D12M5I19M1I10M1I3M7I2M14I3M9I13M2I5M1I13M1D4M1D18M1I3M1D28M1I5M4D35M1D23M3D11M2I8M1D17M1I9M1I23M4D4M1D9M1I3M1I26M1I5M3I1M1I14M1D42M1I5M16I15M1D28M4D5M1I5M1D4M1D11M2D5M1I4M1I3M1I9M +ctgA 49186 16409 18309 + ctgA 50001 16650 18559 1708 2080 60 NM:i:372 ms:i:2437 AS:i:2143 nn:i:0 tp:A:P cm:i:95 s1:i:761 s2:i:212 de:f:0.0792 zd:i:3 rl:i:0 cg:Z:19M5D3M3I13M1D9M1D5M2I5M8D39M1I36M2D33M1D22M1D10M2I8M1D5M1D16M6D5M2D11M2I12M1I7M1I3M3I3M1D3M1I13M1D21M1I4M2I1M1D14M1I12M1I2M1D12M6I5M1D19M10D28M1D26M1D41M1I11M1D9M1I9M4D15M6D13M1D25M5D3M1I27M2D7M1D17M7D18M2I16M1I12M1D22M2I5M12D5M7D2M1D13M1I13M2D2M1I15M1I19M1D17M1I58M1I8M11I8M1I6M2D6M1D2M1I8M2D4M15D6M1D1M2I8M3I25M1D21M1I43M1I6M1D12M2I10M14I5M1D19M3D4M1I95M2D16M2I9M3I8M1I3M1D3M4D1M1D5M3D14M1I7M1I6M2I40M1I1M2I5M1D5M1I11M7D25M10I16M1I2M2D21M1D4M1D4M1D15M2D3M1I13M13I28M2D5M1I33M1I11M1D36M1D19M1I22M1D18M1I1M1I12M1D6M9D11M2D4M1I5M10D16M5I21M1D5M1I11M37I13M3I4M1D9M1I3M1I12M1I58M1D14M +ctgA 49186 3 1851 + ctgA 50001 0 1859 1633 2044 60 NM:i:411 ms:i:2207 AS:i:1899 nn:i:0 tp:A:P cm:i:87 s1:i:696 s2:i:212 de:f:0.0908 zd:i:1 rl:i:0 cg:Z:4M1I16M1I5M3I33M1D14M3D7M1D25M2I5M2I4M1D17M3D11M1D5M3D3M1I15M3I1M2I6M1I2M6D6M2D24M1I3M2D29M1I2M1I3M1I6M1I10M2D8M19D33M1D23M2D9M1D11M1I6M1D5M1D28M1I13M1D5M1I2M1I49M5D6M2I21M1D1M2D2M2D4M4I4M1I5M1I3M1D2M1I4M37D17M2I16M6D5M3D3M6D16M5I4M8I5M4I1M1I52M2D2M1I5M28D33M1D17M2I2M2D31M42I5M1I9M1I7M1D8M1I40M1I39M1I2M1D3M2I11M2I6M2I10M1D14M1I12M1I16M1D6M3I6M2I4M1I1M1I27M1I3M10I3M2D11M3D3M1I35M1I1M3D3M2D19M3D9M4D23M1I3M1I28M1I52M1I28M1D20M2I66M1I3M2I7M1D7M2D9M1D11M1D11M1D7M1I3M1I30M1D11M1D15M2I7M3D3M1I12M6I35M1D4M1D17M1D15M1I5M1D4M1I5M1I3M5D10M1D2M1D8M7I13M5I9M1D4M1I45M6I4M1I19M2D16M1D1M2I23M7I11M1I19M1D13M +ctgA 49186 18309 18754 - ctgA 50001 18559 19001 421 463 60 NM:i:42 ms:i:690 AS:i:668 nn:i:0 tp:A:I cm:i:0 s1:i:0 s2:i:0 de:f:0.0539 rl:i:0 cg:Z:5M1I15M2I30M1D20M1D13M2D2M1D14M8I11M1D18M1I7M1I56M1D33M1D52M2D7M1I19M7D26M1I9M2I10M1I27M1D18M1I15M2I17M +ctgA 49186 1860 2214 - ctgA 50001 1859 2192 321 366 60 NM:i:45 ms:i:501 AS:i:468 nn:i:0 tp:A:I cm:i:0 s1:i:0 s2:i:0 de:f:0.0614 rl:i:0 cg:Z:14M1D4M1D3M1D9M1I12M5D20M2I43M1I10M1I7M2D2M1D8M1I30M1I9M1I2M1I6M15I39M2I16M4I2M1I35M1I2M1I6M1D42M From 557d8b9ad36ae9177f83386c8db149cc9e4f2ada Mon Sep 17 00:00:00 2001 From: Colin Diesh Date: Mon, 19 Dec 2022 22:20:29 -0700 Subject: [PATCH 163/183] Add ability to revcomp sequence in the "Get sequence" dialog (#3421) --- .../components/GetSequenceDialog.tsx | 121 ++++++++++++------ 1 file changed, 84 insertions(+), 37 deletions(-) diff --git a/plugins/linear-genome-view/src/LinearGenomeView/components/GetSequenceDialog.tsx b/plugins/linear-genome-view/src/LinearGenomeView/components/GetSequenceDialog.tsx index a666764bff..185ee3d433 100644 --- a/plugins/linear-genome-view/src/LinearGenomeView/components/GetSequenceDialog.tsx +++ b/plugins/linear-genome-view/src/LinearGenomeView/components/GetSequenceDialog.tsx @@ -2,10 +2,13 @@ import React, { useEffect, useMemo, useState } from 'react' import { makeStyles } from 'tss-react/mui' import { Button, + Checkbox, CircularProgress, Container, DialogActions, DialogContent, + FormGroup, + FormControlLabel, TextField, Typography, } from '@mui/material' @@ -14,7 +17,13 @@ import { saveAs } from 'file-saver' import { getConf } from '@jbrowse/core/configuration' import copy from 'copy-to-clipboard' import { Dialog } from '@jbrowse/core/ui' -import { getSession, Feature, Region } from '@jbrowse/core/util' +import { + getSession, + reverse, + complement, + Feature, + Region, +} from '@jbrowse/core/util' import { formatSeqFasta } from '@jbrowse/core/util/formatFastaStrings' // icons @@ -80,9 +89,12 @@ function SequenceDialog({ const { classes } = useStyles() const session = getSession(model) const [error, setError] = useState() - const [sequence, setSequence] = useState() - const loading = Boolean(sequence === undefined) + const [sequenceChunks, setSequenceChunks] = useState() + const [rev, setReverse] = useState(false) + const [copied, setCopied] = useState(false) + const [comp, setComplement] = useState(false) const { leftOffset, rightOffset } = model + const loading = Boolean(sequenceChunks === undefined) // avoid infinite looping of useEffect // random note: the current selected region can't be a computed because it @@ -106,27 +118,7 @@ function SequenceDialog({ controller.signal, ) if (active) { - setSequence( - formatSeqFasta( - chunks - .filter(f => !!f) - .map(chunk => { - const chunkSeq = chunk.get('seq') - const chunkRefName = chunk.get('refName') - const chunkStart = chunk.get('start') + 1 - const chunkEnd = chunk.get('end') - const chunkLocstring = `${chunkRefName}:${chunkStart}-${chunkEnd}` - if (chunkSeq?.length !== chunkEnd - chunkStart + 1) { - throw new Error( - `${chunkLocstring} returned ${chunkSeq.length.toLocaleString()} bases, but should have returned ${( - chunkEnd - chunkStart - ).toLocaleString()}`, - ) - } - return { header: chunkLocstring, seq: chunkSeq } - }), - ), - ) + setSequenceChunks(chunks) } } else { throw new Error('Selected region is out of bounds') @@ -143,7 +135,36 @@ function SequenceDialog({ controller.abort() active = false } - }, [model, session, regionsSelected, setSequence]) + }, [model, session, regionsSelected]) + + const sequence = sequenceChunks + ? formatSeqFasta( + sequenceChunks + .filter(f => !!f) + .map(chunk => { + let chunkSeq = chunk.get('seq') + const chunkRefName = chunk.get('refName') + const chunkStart = chunk.get('start') + 1 + const chunkEnd = chunk.get('end') + const chunkLocstring = `${chunkRefName}:${chunkStart}-${chunkEnd}` + if (chunkSeq?.length !== chunkEnd - chunkStart + 1) { + throw new Error( + `${chunkLocstring} returned ${chunkSeq.length.toLocaleString()} bases, but should have returned ${( + chunkEnd - chunkStart + ).toLocaleString()}`, + ) + } + + if (rev) { + chunkSeq = reverse(chunkSeq) + } + if (comp) { + chunkSeq = complement(chunkSeq) + } + return { header: chunkLocstring, seq: chunkSeq } + }), + ) + : '' const sequenceTooLarge = sequence ? sequence.length > 1_000_000 : false @@ -158,14 +179,13 @@ function SequenceDialog({ title="Reference sequence" > - {error ? {`${error}`} : null} - {loading && !error ? ( + {error ? ( + {`${error}`} + ) : loading ? ( Retrieving reference sequence... @@ -176,6 +196,7 @@ function SequenceDialog({ variant="outlined" multiline minRows={5} + maxRows={10} disabled={sequenceTooLarge} className={classes.dialogContent} fullWidth @@ -191,25 +212,51 @@ function SequenceDialog({ }, }} /> + + setReverse(event.target.checked)} + /> + } + label="Reverse sequence" + /> + setComplement(event.target.checked)} + /> + } + label="Complement sequence" + /> + + + Note: Check both boxes for the "reverse complement" +