diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..80663bb --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,34 @@ +{ + "plugins": [ + "prettier", + "react" + ], + "extends": [ + "prettier", + "eslint:recommended", + "plugin:react/recommended" + ], + "rules": { + "prettier/prettier": "error", + "react/prop-types": 0 + }, + "settings": { + "react": { + "version": "detect" + } + }, + "parserOptions": { + "ecmaVersion": 8, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true, + "modules": true + } + }, + "env": { + "browser": true, + "node": true, + "es6": true, + "jest": true + } +} diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..1d63809 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "semi": false, + "singleQuote": true, + "tabWidth": 2 +} \ No newline at end of file diff --git a/package.json b/package.json index 03c10f8..046f213 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "lint": "eslint src" }, "eslintConfig": { "extends": "react-app" @@ -45,6 +46,9 @@ }, "devDependencies": { "babel-cli": "^6.26.0", + "eslint-config-prettier": "^6.10.1", + "eslint-plugin-prettier": "^3.1.2", + "prettier": "^2.0.4", "workerize-loader": "^1.1.0" } } diff --git a/src/App.js b/src/App.js index 374ab30..353f19f 100644 --- a/src/App.js +++ b/src/App.js @@ -1,35 +1,58 @@ -import React, { Component } from 'react'; -import { Select, MenuItem } from '@material-ui/core'; -import { extend, isArray } from 'lodash'; +import React, { Component } from 'react' +import { Select, MenuItem } from '@material-ui/core' +import { extend, isArray } from 'lodash' import colorSchemes from './colorSchemes' -import './App.css'; +import './App.css' -import { getAncestralReconstruction } from './reconstruction'; +import { getAncestralReconstruction } from './reconstruction' // eslint-disable-next-line import/no-webpack-loader-syntax -import getAncestralReconstructionWorker from 'workerize-loader!./reconstruction'; +import getAncestralReconstructionWorker from 'workerize-loader!./reconstruction' -import Stockholm from 'stockholm-js'; -import { Newick } from 'newick'; -import JukesCantor from 'jukes-cantor'; -import RapidNeighborJoining from 'neighbor-joining'; +import Stockholm from 'stockholm-js' +import { Newick } from 'newick' +import JukesCantor from 'jukes-cantor' +import RapidNeighborJoining from 'neighbor-joining' -import MSA from './MSA'; +import MSA from './MSA' class App extends Component { - constructor(props) { - super(props); - + super(props) + // config - const config = extend (this.defaultConfig(), props.config || {}) - const { genericRowHeight, nameFontSize, treeWidth, branchStrokeStyle, nodeHandleRadius, nodeHandleClickRadius, nodeHandleFillStyle, collapsedNodeHandleFillStyle, rowConnectorDash } = config + const config = extend(this.defaultConfig(), props.config || {}) + const { + genericRowHeight, + nameFontSize, + treeWidth, + branchStrokeStyle, + nodeHandleRadius, + nodeHandleClickRadius, + nodeHandleFillStyle, + collapsedNodeHandleFillStyle, + rowConnectorDash, + } = config // tree configuration const treeStrokeWidth = 1 const nodeHandleStrokeStyle = branchStrokeStyle - const availableTreeWidth = treeWidth - nodeHandleRadius - 2*treeStrokeWidth - const scrollbarHeight = 20 // hack, could be platform-dependent, a bit fragile... - const computedTreeConfig = { treeWidth, availableTreeWidth, genericRowHeight, branchStrokeStyle, nodeHandleStrokeStyle, nodeHandleRadius, nodeHandleClickRadius, nodeHandleFillStyle, collapsedNodeHandleFillStyle, rowConnectorDash, treeStrokeWidth, scrollbarHeight } + const availableTreeWidth = + treeWidth - nodeHandleRadius - 2 * treeStrokeWidth + const scrollbarHeight = 20 // hack, could be platform-dependent, a bit fragile... + const computedTreeConfig = { + treeWidth, + availableTreeWidth, + genericRowHeight, + branchStrokeStyle, + nodeHandleStrokeStyle, + nodeHandleRadius, + nodeHandleClickRadius, + nodeHandleFillStyle, + collapsedNodeHandleFillStyle, + rowConnectorDash, + treeStrokeWidth, + scrollbarHeight, + } // font configuration const charFontName = 'Menlo,monospace' @@ -37,263 +60,345 @@ class App extends Component { const nameFontColor = 'black' const charFont = genericRowHeight + 'px ' + charFontName const color = config.color || colorSchemes[config.colorScheme] - const computedFontConfig = { charFont, charFontName, color, nameFontName, nameFontSize, nameFontColor, genericRowHeight } - + const computedFontConfig = { + charFont, + charFontName, + color, + nameFontName, + nameFontSize, + nameFontColor, + genericRowHeight, + } + // state (we will select a dataset in componentDidMount()) - this.state = { config, - datasets: this.props.datasets || [], - computedTreeConfig, - computedFontConfig } + this.state = { + config, + datasets: this.props.datasets || [], + computedTreeConfig, + computedFontConfig, + } this.divRef = React.createRef() this.inputRef = React.createRef() this.msaRef = React.createRef() } - handleDragEnter (evt) { + handleDragEnter(evt) { evt.stopPropagation() evt.preventDefault() } - handleDragOver (evt) { + handleDragOver(evt) { evt.stopPropagation() evt.preventDefault() evt.dataTransfer.dropEffect = 'copy' } - handleDrop (evt) { + handleDrop(evt) { evt.stopPropagation() evt.preventDefault() - this.openFiles (evt.dataTransfer.files) + this.openFiles(evt.dataTransfer.files) } - - handleSelectDataset (evt) { + + handleSelectDataset(evt) { const name = evt.target.value if (name) { - this.setDataset (this.state.datasets.find ((ds) => ds.name === name)) + this.setDataset(this.state.datasets.find((ds) => ds.name === name)) this.msaRef.current.resetView() - } else - this.inputRef.current.click() + } else this.inputRef.current.click() } - handleSelectFile (evt) { - this.openFiles (evt.target.files) + handleSelectFile(evt) { + this.openFiles(evt.target.files) } - - openFiles (files) { - return Promise.all (Array.from(files).map ((file) => this.openFile (file))) + + openFiles(files) { + return Promise.all(Array.from(files).map((file) => this.openFile(file))) } - - openFile (file) { - return new Promise ((resolve, reject) => { + + openFile(file) { + return new Promise((resolve, reject) => { const reader = new FileReader() reader.onload = (e) => { const text = e.target.result - this.addDatasets (text) + this.addDatasets(text) resolve() } - reader.readAsText (file) + reader.readAsText(file) }) } - addDatasets (text) { - const newAlignmentName = (n) => "Alignment " + (this.state.datasets.length + (n || 0) + 1); + addDatasets(text) { + const newAlignmentName = (n) => + 'Alignment ' + (this.state.datasets.length + (n || 0) + 1) let datasets = this.state.datasets - if (this.sniffStockholmRegex.test (text)) { - const stocks = Stockholm.parseAll (text) - datasets = datasets.concat (stocks.map ((stockholmjs, n) => { - const name = stockholmjs.gf.ID ? stockholmjs.gf.ID[0] : newAlignmentName(n) - return { stockholmjs, name } - })) + if (this.sniffStockholmRegex.test(text)) { + const stocks = Stockholm.parseAll(text) + datasets = datasets.concat( + stocks.map((stockholmjs, n) => { + const name = stockholmjs.gf.ID + ? stockholmjs.gf.ID[0] + : newAlignmentName(n) + return { stockholmjs, name } + }) + ) } else { try { - const json = JSON.parse (text) - if (isArray (json)) - datasets = datasets.concat (json) - else - datasets.push (json) + const json = JSON.parse(text) + if (isArray(json)) datasets = datasets.concat(json) + else datasets.push(json) } catch (e) { - datasets.push ({ auto: text, - name: newAlignmentName() }) + datasets.push({ auto: text, name: newAlignmentName() }) } } if (datasets.length > this.state.datasets.length) { const firstDataset = datasets[this.state.datasets.length] - this.setDataset (firstDataset, { datasets }) - if (this.msaRef.current) - this.msaRef.current.resetView() + this.setDataset(firstDataset, { datasets }) + if (this.msaRef.current) this.msaRef.current.resetView() } } - - setDataset (data, extra) { - const datasetID = this.datasetsLoaded = (this.datasetsLoaded || 0) + 1 - this.indexData (data) - .then ((dataWithIndices) => this.setState (extend ({ datasetID, - reconstructingAncestors: false }, - dataWithIndices, - extra || {}))) + + setDataset(data, extra) { + const datasetID = (this.datasetsLoaded = (this.datasetsLoaded || 0) + 1) + this.indexData(data).then((dataWithIndices) => + this.setState( + extend( + { datasetID, reconstructingAncestors: false }, + dataWithIndices, + extra || {} + ) + ) + ) } - async indexData (suppliedData, suppliedConfig) { + async indexData(suppliedData, suppliedConfig) { const config = suppliedConfig || this.state.config - const data = await this.getData (config.cacheData ? suppliedData : extend ({}, suppliedData), config) - const treeIndex = this.buildTreeIndex (data) - const alignIndex = this.buildAlignmentIndex (data) + const data = await this.getData( + config.cacheData ? suppliedData : extend({}, suppliedData), + config + ) + const treeIndex = this.buildTreeIndex(data) + const alignIndex = this.buildAlignmentIndex(data) return { data, treeIndex, alignIndex } } // regexes - get pdbRegex() { return /PDB; +(\S+) +(\S); ([0-9]+)-([0-9]+)/; } /* PFAM format for embedding PDB IDs in Stockholm files */ - get nameEncodedCoordRegex() { return /\/([0-9]+)-([0-9]+)$/; } /* Pfam format for embedding coordinates in names (ugh) */ - get sniffStockholmRegex() { return /^# STOCKHOLM/; } /* regex for sniffing Stockholm format */ - get sniffFastaRegex() { return /^>/; } /* regex for sniffing FASTA format */ + get pdbRegex() { + return /PDB; +(\S+) +(\S); ([0-9]+)-([0-9]+)/ + } /* PFAM format for embedding PDB IDs in Stockholm files */ + get nameEncodedCoordRegex() { + return /\/([0-9]+)-([0-9]+)$/ + } /* Pfam format for embedding coordinates in names (ugh) */ + get sniffStockholmRegex() { + return /^# STOCKHOLM/ + } /* regex for sniffing Stockholm format */ + get sniffFastaRegex() { + return /^>/ + } /* regex for sniffing FASTA format */ // method to get data & build tree if necessary - async getData (data, config) { + async getData(data, config) { if (data.url) - await Promise.all (Object.keys (data.url) - .filter (key => !data[key]) - .map ((key) => { - const url = this.makeURL (data.url[key]) - return fetch (url).then (async (res) => { - if (res.ok) - data[key] = await res.text() - else - console.warn ('Error fetching ' + url, res.statusText) - })})) + await Promise.all( + Object.keys(data.url) + .filter((key) => !data[key]) + .map((key) => { + const url = this.makeURL(data.url[key]) + return fetch(url).then(async (res) => { + if (res.ok) data[key] = await res.text() + else console.warn('Error fetching ' + url, res.statusText) + }) + }) + ) if (data.json) - extend (data, typeof(data.json) === 'string' ? JSON.parse(data.json) : data.json) + extend( + data, + typeof data.json === 'string' ? JSON.parse(data.json) : data.json + ) if (data.auto) { - if (this.sniffStockholmRegex.test (data.auto)) - data.stockholm = data.auto - else if (this.sniffFastaRegex.test (data.auto)) - data.fasta = data.auto + if (this.sniffStockholmRegex.test(data.auto)) data.stockholm = data.auto + else if (this.sniffFastaRegex.test(data.auto)) data.fasta = data.auto else { try { - extend (data, JSON.parse (data.auto)) + extend(data, JSON.parse(data.auto)) } catch (e) { // do nothing if JSON didn't parse } } } if (!(data.branches && data.rowData)) { - if (data.stockholm) // was a Stockholm-format alignment specified? - this.unpackStockholm (data, config, data.stockholm) - else if (data.stockholmjs) // was a StockholmJS object specified? - this.unpackStockholmJS (data, config, data.stockholmjs) - else if (data.fasta) // was a FASTA-format alignment specified? - data.rowData = this.parseFasta (data.fasta) - else - throw new Error ("no sequence data") + if (data.stockholm) + // was a Stockholm-format alignment specified? + this.unpackStockholm(data, config, data.stockholm) + else if (data.stockholmjs) + // was a StockholmJS object specified? + this.unpackStockholmJS(data, config, data.stockholmjs) + else if (data.fasta) + // was a FASTA-format alignment specified? + data.rowData = this.parseFasta(data.fasta) + else throw new Error('no sequence data') // If a Newick-format tree was specified somehow (as a separate data item, or in the Stockholm alignment) then parse it if (data.newick || data.newickjs) { const NewickParser = new Newick() - const newickTree = data.newickjs = data.newickjs || NewickParser.parse (data.newick) + const newickTree = (data.newickjs = + data.newickjs || NewickParser.parse(data.newick)) let nodes = 0 - const getName = (obj) => (obj.name = obj.name || ('node' + (++nodes))) + const getName = (obj) => (obj.name = obj.name || 'node' + ++nodes) data.branches = [] - const traverse = (parent) => { // auto-name internal nodes + const traverse = (parent) => { + // auto-name internal nodes if (parent.branchset) - parent.branchset.forEach ((child) => { - data.branches.push ([getName(parent), getName(child), Math.max (child.length, 0)]) - traverse (child) + parent.branchset.forEach((child) => { + data.branches.push([ + getName(parent), + getName(child), + Math.max(child.length, 0), + ]) + traverse(child) }) } - traverse (newickTree) - data.root = getName (newickTree) - } else { // no Newick tree was specified, so build a quick-and-dirty distance matrix with Jukes-Cantor, and get a tree by neighbor-joining - const taxa = Object.keys(data.rowData).sort(), seqs = taxa.map ((taxon) => data.rowData[taxon]) - console.warn ('Estimating phylogenetic tree (' + taxa.length + ' taxa)...') - const distMatrix = JukesCantor.calcFiniteDistanceMatrix (seqs) - const rnj = new RapidNeighborJoining.RapidNeighborJoining (distMatrix, taxa.map ((name) => ({ name }))) + traverse(newickTree) + data.root = getName(newickTree) + } else { + // no Newick tree was specified, so build a quick-and-dirty distance matrix with Jukes-Cantor, and get a tree by neighbor-joining + const taxa = Object.keys(data.rowData).sort(), + seqs = taxa.map((taxon) => data.rowData[taxon]) + console.warn( + 'Estimating phylogenetic tree (' + taxa.length + ' taxa)...' + ) + const distMatrix = JukesCantor.calcFiniteDistanceMatrix(seqs) + const rnj = new RapidNeighborJoining.RapidNeighborJoining( + distMatrix, + taxa.map((name) => ({ name })) + ) rnj.run() const tree = rnj.getAsObject() let nodes = 0 - const getName = (obj) => { obj.taxon = obj.taxon || { name: 'node' + (++nodes) }; return obj.taxon.name } + const getName = (obj) => { + obj.taxon = obj.taxon || { name: 'node' + ++nodes } + return obj.taxon.name + } data.branches = [] - const traverse = (parent) => { // auto-name internal nodes - parent.children.forEach ((child) => { - data.branches.push ([getName(parent), getName(child), Math.max (child.length, 0)]) - traverse (child) + const traverse = (parent) => { + // auto-name internal nodes + parent.children.forEach((child) => { + data.branches.push([ + getName(parent), + getName(child), + Math.max(child.length, 0), + ]) + traverse(child) }) } - traverse (tree) - data.root = getName (tree) + traverse(tree) + data.root = getName(tree) } } - this.guessSeqCoords (data) // this is an idempotent method; if data came from a Stockholm file, it's already been called (in order to filter out irrelevant structures) + this.guessSeqCoords(data) // this is an idempotent method; if data came from a Stockholm file, it's already been called (in order to filter out irrelevant structures) return data } - // Attempt to figure out start coords relative to database sequences by parsing the sequence names - // This allows us to align to partial structures - // This is pretty hacky; the user can alternatively pass these in through the data.seqCoords field - guessSeqCoords (data) { - if (!data.seqCoords) - data.seqCoords = {} - Object.keys (data.rowData).forEach ((name) => { - const seq = data.rowData[name], len = this.countNonGapChars (seq) + // Attempt to figure out start coords relative to database sequences by parsing the sequence names + // This allows us to align to partial structures + // This is pretty hacky; the user can alternatively pass these in through the data.seqCoords field + guessSeqCoords(data) { + if (!data.seqCoords) data.seqCoords = {} + Object.keys(data.rowData).forEach((name) => { + const seq = data.rowData[name], + len = this.countNonGapChars(seq) if (!data.seqCoords[name]) { - const coordMatch = this.nameEncodedCoordRegex.exec (name) + const coordMatch = this.nameEncodedCoordRegex.exec(name) if (coordMatch) { - const startPos = parseInt(coordMatch[1]), endPos = parseInt(coordMatch[2]) + const startPos = parseInt(coordMatch[1]), + endPos = parseInt(coordMatch[2]) if (endPos + 1 - startPos === len) data.seqCoords[name] = { startPos, endPos } } } if (!data.seqCoords[name]) - data.seqCoords[name] = { startPos: 1, endPos: len } // if we can't guess the start coord, just assume it's the full-length sequence + data.seqCoords[name] = { startPos: 1, endPos: len } // if we can't guess the start coord, just assume it's the full-length sequence }) } - - makeURL (url) { - return url.replace ('%PUBLIC_URL%', process.env.PUBLIC_URL) + + makeURL(url) { + return url.replace('%PUBLIC_URL%', process.env.PUBLIC_URL) } - - unpackStockholm (data, config, stockholm) { - const stockjs = Stockholm.parse (stockholm) - this.unpackStockholmJS (data, config, stockjs) + + unpackStockholm(data, config, stockholm) { + const stockjs = Stockholm.parse(stockholm) + this.unpackStockholmJS(data, config, stockjs) } - unpackStockholmJS (data, config, stock) { - const structure = data.structure = data.structure || {} + unpackStockholmJS(data, config, stock) { + const structure = (data.structure = data.structure || {}) data.rowData = stock.seqdata - this.guessSeqCoords (data) - if (stock.gf.DE) - data.description = stock.gf.DE.join("\n") - if (stock.gf.NH && !data.newick) // did the Stockholm alignment include a tree? + this.guessSeqCoords(data) + if (stock.gf.DE) data.description = stock.gf.DE.join('\n') + if (stock.gf.NH && !data.newick) + // did the Stockholm alignment include a tree? data.newick = stock.gf.NH.join('') - if (stock.gs.DR && !config.structure.noRemoteStructures) // did the Stockholm alignment include links to PDB? - Object.keys(stock.gs.DR).forEach ((node) => { + if (stock.gs.DR && !config.structure.noRemoteStructures) + // did the Stockholm alignment include links to PDB? + Object.keys(stock.gs.DR).forEach((node) => { const seqCoords = data.seqCoords[node] - stock.gs.DR[node].forEach ((dr) => { + stock.gs.DR[node].forEach((dr) => { const match = this.pdbRegex.exec(dr) if (match) { - const pdb = match[1].toLowerCase(), chain = match[2], startPos = parseInt (match[3]), endPos = parseInt (match[4]) - if (startPos <= seqCoords.endPos && endPos >= seqCoords.startPos) { // check structure overlaps sequence - structure[node] = structure[node] || { pdb, - chains: [] } - structure[node].chains.push ({ chain, - startPos, - endPos }) + const pdb = match[1].toLowerCase(), + chain = match[2], + startPos = parseInt(match[3]), + endPos = parseInt(match[4]) + if (startPos <= seqCoords.endPos && endPos >= seqCoords.startPos) { + // check structure overlaps sequence + structure[node] = structure[node] || { pdb, chains: [] } + structure[node].chains.push({ chain, startPos, endPos }) } else - console.warn ('ignoring structure ' + pdb + ' (' + startPos + '...' + endPos + ') since it does not overlap with ' + node) + console.warn( + 'ignoring structure ' + + pdb + + ' (' + + startPos + + '...' + + endPos + + ') since it does not overlap with ' + + node + ) } }) }) } - + componentDidMount() { - this.divRef.current.addEventListener ('dragover', this.handleDragOver.bind(this), false) - this.divRef.current.addEventListener ('dragenter', this.handleDragEnter.bind(this), false) - this.divRef.current.addEventListener ('drop', this.handleDrop.bind(this), false) + this.divRef.current.addEventListener( + 'dragover', + this.handleDragOver.bind(this), + false + ) + this.divRef.current.addEventListener( + 'dragenter', + this.handleDragEnter.bind(this), + false + ) + this.divRef.current.addEventListener( + 'drop', + this.handleDrop.bind(this), + false + ) this.initDataset() } componentWillUnmount() { - this.divRef.current.removeEventListener ('dragover', this.handleDragOver.bind(this), false) - this.divRef.current.removeEventListener ('dragenter', this.handleDragEnter.bind(this), false) - this.divRef.current.removeEventListener ('drop', this.handleDrop.bind(this)) + this.divRef.current.removeEventListener( + 'dragover', + this.handleDragOver.bind(this), + false + ) + this.divRef.current.removeEventListener( + 'dragenter', + this.handleDragEnter.bind(this), + false + ) + this.divRef.current.removeEventListener('drop', this.handleDrop.bind(this)) } componentDidUpdate() { @@ -301,64 +406,74 @@ class App extends Component { } async initDataset() { - if (this.props.stockholm) - this.addDatasets (this.props.stockholm) + if (this.props.stockholm) this.addDatasets(this.props.stockholm) if (this.props.dataurl) - await fetch(this.makeURL (this.props.dataurl)) - .then (async (res) => { - if (res.ok) - this.addDatasets (await res.text()) + await fetch(this.makeURL(this.props.dataurl)).then(async (res) => { + if (res.ok) this.addDatasets(await res.text()) }) - this.setDataset (this.props.data || this.state.datasets[0]) + this.setDataset(this.props.data || this.state.datasets[0]) } - + reconstructMissingNodes() { // check if any nodes are missing; if so, do ancestral sequence reconstruction const { data } = this.state let promise if (data) { const { branches } = data - let rowData = extend ({}, data.rowData) - const missingAncestors = data.branches.filter ((b) => typeof(rowData[b[0]]) === 'undefined').length + let rowData = extend({}, data.rowData) + const missingAncestors = data.branches.filter( + (b) => typeof rowData[b[0]] === 'undefined' + ).length if (missingAncestors && !this.state.reconstructingAncestors) { - this.setState ({ reconstructingAncestors: true }) + this.setState({ reconstructingAncestors: true }) if (window.Worker) { - console.warn ('Reconstructing ancestral sequences in web worker...') + console.warn('Reconstructing ancestral sequences in web worker...') let instance = getAncestralReconstructionWorker() - promise = instance.getAncestralReconstruction ({ branches, rowData, id: this.state.datasetID }) - .then ((result) => { - console.warn ('Ancestral sequence reconstruction complete') + promise = instance + .getAncestralReconstruction({ + branches, + rowData, + id: this.state.datasetID, + }) + .then((result) => { + console.warn('Ancestral sequence reconstruction complete') if (result.id === this.state.datasetID) - this.incorporateAncestralReconstruction (result.ancestralRowData) + this.incorporateAncestralReconstruction(result.ancestralRowData) else - console.warn ('Discarding unused ancestral sequence reconstruction') // guard against bug where ancestral reconstruction comes in after dataset has been changed + console.warn( + 'Discarding unused ancestral sequence reconstruction' + ) // guard against bug where ancestral reconstruction comes in after dataset has been changed }) } else { - console.warn ('Reconstructing ancestral sequences...') - promise = getAncestralReconstruction ({ branches, rowData }) - .then ((result) => { - this.incorporateAncestralReconstruction (result.ancestralRowData) - }) + console.warn('Reconstructing ancestral sequences...') + promise = getAncestralReconstruction({ branches, rowData }).then( + (result) => { + this.incorporateAncestralReconstruction(result.ancestralRowData) + } + ) } } - } else - promise = Promise.resolve() + } else promise = Promise.resolve() return promise } - fn2workerURL (fn) { - const blob = new Blob(['('+fn.toString()+')()'], {type: 'application/javascript'}) + fn2workerURL(fn) { + const blob = new Blob(['(' + fn.toString() + ')()'], { + type: 'application/javascript', + }) return URL.createObjectURL(blob) } - - incorporateAncestralReconstruction (ancestralRowData) { + + incorporateAncestralReconstruction(ancestralRowData) { const { data } = this.state - const rowData = extend ({}, data.rowData, ancestralRowData) - extend (data, { rowData }) - this.setDataset (data) // rebuilds indices + const rowData = extend({}, data.rowData, ancestralRowData) + extend(data, { rowData }) + this.setDataset(data) // rebuilds indices } - defaultColorScheme() { return 'maeditor' } + defaultColorScheme() { + return 'maeditor' + } defaultConfig() { return { treeAlignHeight: 400, @@ -373,106 +488,133 @@ class App extends Component { nodeHandleClickRadius: 40, nodeHandleFillStyle: 'white', collapsedNodeHandleFillStyle: 'black', - rowConnectorDash: [2,2], + rowConnectorDash: [2, 2], structure: { width: 400, height: 400 }, handler: {}, - colorScheme: this.defaultColorScheme() - } } + colorScheme: this.defaultColorScheme(), + } + } // method to parse FASTA (simple enough to build in here) - parseFasta (fasta) { - let seq = {}, name, re = /^>(\S+)/; - fasta.split("\n").forEach ((line) => { + parseFasta(fasta) { + let seq = {}, + name, + re = /^>(\S+)/ + fasta.split('\n').forEach((line) => { const match = re.exec(line) - if (match) - seq[name = match[1]] = '' - else if (name) - seq[name] = seq[name] + line.replace(/[ \t]/g,'') + if (match) seq[(name = match[1])] = '' + else if (name) seq[name] = seq[name] + line.replace(/[ \t]/g, '') }) return seq } // index tree - buildTreeIndex (data) { + buildTreeIndex(data) { const { branches } = data - let { root } = data, rootSpecified = typeof(root) !== 'undefined' - const roots = this.getRoots (branches) + let { root } = data, + rootSpecified = typeof root !== 'undefined' + const roots = this.getRoots(branches) if (roots.length === 0 && (branches.length > 0 || !rootSpecified)) - throw new Error ("No root nodes") + throw new Error('No root nodes') if (rootSpecified) { if (roots.indexOf(root) < 0) - throw new Error ("Specified root node is not a root") + throw new Error('Specified root node is not a root') } else { if (roots.length !== 1) - throw new Error ("Multiple possible root nodes, and no root specified") + throw new Error('Multiple possible root nodes, and no root specified') root = roots[0] } - let children = {}, branchLength = {} + let children = {}, + branchLength = {} children[root] = [] branchLength[root] = 0 - branches.forEach ((branch) => { - const parent = branch[0], child = branch[1], len = branch[2] + branches.forEach((branch) => { + const parent = branch[0], + child = branch[1], + len = branch[2] children[parent] = children[parent] || [] children[child] = children[child] || [] - children[parent].push (child) + children[parent].push(child) branchLength[child] = len }) - let nodes = [], seenNode = {}, descendants = {}, distFromRoot = {}, maxDistFromRoot = 0 + let nodes = [], + seenNode = {}, + descendants = {}, + distFromRoot = {}, + maxDistFromRoot = 0 const addNode = (node) => { - if (!node) - throw new Error ("All nodes must be named") + if (!node) throw new Error('All nodes must be named') if (seenNode[node]) - throw new Error ("All node names must be unique (duplicate '" + node + "')") + throw new Error( + "All node names must be unique (duplicate '" + node + "')" + ) seenNode[node] = true - nodes.push (node) + nodes.push(node) } const addSubtree = (node, parent) => { - distFromRoot[node] = (typeof(parent) !== 'undefined' ? distFromRoot[parent] : 0) + branchLength[node] - maxDistFromRoot = Math.max (maxDistFromRoot, distFromRoot[node]) + distFromRoot[node] = + (typeof parent !== 'undefined' ? distFromRoot[parent] : 0) + + branchLength[node] + maxDistFromRoot = Math.max(maxDistFromRoot, distFromRoot[node]) const kids = children[node] let clade = [] if (kids.length === 2) { - clade = clade.concat (addSubtree (kids[0], node)) - addNode (node) - clade = clade.concat (addSubtree (kids[1], node)) + clade = clade.concat(addSubtree(kids[0], node)) + addNode(node) + clade = clade.concat(addSubtree(kids[1], node)) } else { - addNode (node) - kids.forEach ((child) => clade = clade.concat (addSubtree (child, node))) + addNode(node) + kids.forEach((child) => (clade = clade.concat(addSubtree(child, node)))) } descendants[node] = clade - return [node].concat (clade) + return [node].concat(clade) + } + addSubtree(root) + return { + root, + branches, + children, + descendants, + branchLength, + nodes, + distFromRoot, + maxDistFromRoot, } - addSubtree (root) - return { root, branches, children, descendants, branchLength, nodes, distFromRoot, maxDistFromRoot } } // get the root node(s) of a list of [parent,child,length] branches - getRoots (branches) { - const isNode = {}, hasParent = {} - branches.forEach ((branch) => { + getRoots(branches) { + const isNode = {}, + hasParent = {} + branches.forEach((branch) => { const [p, c] = branch isNode[p] = isNode[c] = hasParent[c] = true }) - return Object.keys(isNode).filter ((n) => !hasParent[n]).sort() + return Object.keys(isNode) + .filter((n) => !hasParent[n]) + .sort() } // index alignment - buildAlignmentIndex (data) { + buildAlignmentIndex(data) { const { rowData } = data - let rowDataAsArray = {}, alignColToSeqPos = {}, seqPosToAlignCol = {}, isChar = {}, columns - Object.keys(rowData).forEach ((node) => { + let rowDataAsArray = {}, + alignColToSeqPos = {}, + seqPosToAlignCol = {}, + isChar = {}, + columns + Object.keys(rowData).forEach((node) => { const row = rowData[node] - if (typeof(columns) !== 'undefined' && columns !== row.length) - console.error ("Inconsistent row lengths") + if (typeof columns !== 'undefined' && columns !== row.length) + console.error('Inconsistent row lengths') columns = row.length - let pos2col = [], pos = 0 - const rowAsArray = this.rowAsArray (row) - alignColToSeqPos[node] = rowAsArray.map ((c, col) => { - if (typeof(c) === 'string') - isChar[c] = true + let pos2col = [], + pos = 0 + const rowAsArray = this.rowAsArray(row) + alignColToSeqPos[node] = rowAsArray.map((c, col) => { + if (typeof c === 'string') isChar[c] = true const isGap = this.isGapChar(c) - if (!isGap) - pos2col.push (col) + if (!isGap) pos2col.push(col) return isGap ? pos : pos++ }) rowDataAsArray[node] = rowAsArray @@ -483,63 +625,76 @@ class App extends Component { } // helpers to recognize gap characters - isGapChar (c) { return typeof(c) === 'string' ? (c === '-' || c === '.') : (!c || Object.keys(c).length === 0) } - countNonGapChars (seq) { return this.rowAsArray(seq).filter ((c) => !this.isGapChar(c)).length } - rowAsArray (row) { return typeof(row) === 'string' ? row.split('') : row } - + isGapChar(c) { + return typeof c === 'string' + ? c === '-' || c === '.' + : !c || Object.keys(c).length === 0 + } + countNonGapChars(seq) { + return this.rowAsArray(seq).filter((c) => !this.isGapChar(c)).length + } + rowAsArray(row) { + return typeof row === 'string' ? row.split('') : row + } + render() { return ( -
- +
- - - - { (this.state.data - ? (this.state.datasets.length - ? (( - {this.state.datasets.map ((data) => ({data.name}))} - Open alignment file - - )) - : (
- {this.state.data.name} -
)) - : '')} - - { (this.state.data && this.state.data.description - ? (
- {this.state.data.description} -
) - : '')} - -
- - { this.state.data && - } - + > + {this.state.datasets.map((data) => ( + + {data.name} + + ))} + + Open alignment file + + + ) : ( +
{this.state.data.name}
+ ) + ) : ( + '' + )} + + {this.state.data && this.state.data.description ? ( +
+ {this.state.data.description} +
+ ) : ( + '' + )} +
+ + {this.state.data && ( + + )}
- ); + ) } } -export default App; +export default App diff --git a/src/App.test.js b/src/App.test.js index 4db7ebc..4a86375 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -1,9 +1,9 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import App from './App'; +import React from 'react' +import { render } from '@testing-library/react' +import App from './App' test('renders learn react link', () => { - const { getByText } = render(); - const linkElement = getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); + const { getByText } = render() + const linkElement = getByText(/learn react/i) + expect(linkElement).toBeInTheDocument() +}) diff --git a/src/MSA.js b/src/MSA.js index 436611d..51f5ec0 100644 --- a/src/MSA.js +++ b/src/MSA.js @@ -1,21 +1,18 @@ -import React, { Component } from 'react'; -import { extend } from 'lodash'; +import React, { Component } from 'react' +import { extend } from 'lodash' -import MSATree from './MSATree'; -import MSAAlignNames from './MSAAlignNames'; -import MSAAlignRows from './MSAAlignRows'; -import MSAStructPanel from './MSAStructPanel'; +import MSATree from './MSATree' +import MSAAlignNames from './MSAAlignNames' +import MSAAlignRows from './MSAAlignRows' +import MSAStructPanel from './MSAStructPanel' class MSA extends Component { constructor(props) { - super(props); + super(props) - const view = extend (this.initialView(), - props.view || {}) + const view = extend(this.initialView(), props.view || {}) - this.state = extend ({ scrollTop: 0, - alignScrollLeft: 0 }, - { view }); + this.state = extend({ scrollTop: 0, alignScrollLeft: 0 }, { view }) this.rowsRef = React.createRef() this.msaRef = React.createRef() @@ -25,377 +22,500 @@ class MSA extends Component { // config/defaults initialView() { return { - collapsed: {}, // true if an internal node has been collapsed by the user - forceDisplayNode: {}, // force a node to be displayed even if it's flagged as collapsed. Used by animation code - nodeScale: {}, // height scaling factor for tree nodes / alignment rows. From 0 to 1 (undefined implies 1) - columnScale: {}, // height scaling factor for alignment columns. From 0 to 1 (undefined implies 1) + collapsed: {}, // true if an internal node has been collapsed by the user + forceDisplayNode: {}, // force a node to be displayed even if it's flagged as collapsed. Used by animation code + nodeScale: {}, // height scaling factor for tree nodes / alignment rows. From 0 to 1 (undefined implies 1) + columnScale: {}, // height scaling factor for alignment columns. From 0 to 1 (undefined implies 1) disableTreeEvents: false, animating: false, - structure: { openStructures: [] } - } } + structure: { openStructures: [] }, + } + } - collapseAnimationFrames() { return 10 } - collapseAnimationDuration() { return 200 } - collapseAnimationMaxFrameSkip() { return 8 } + collapseAnimationFrames() { + return 10 + } + collapseAnimationDuration() { + return 200 + } + collapseAnimationMaxFrameSkip() { + return 8 + } resetView() { - this.setState ({ view: this.initialView() }) + this.setState({ view: this.initialView() }) } - + // get tree collapsed/open state - getComputedView (view) { + getComputedView(view) { view = view || this.state.view const { treeIndex, alignIndex } = this.props const { collapsed, forceDisplayNode } = view const { rowDataAsArray } = alignIndex - let ancestorCollapsed = {}, nodeVisible = {} + let ancestorCollapsed = {}, + nodeVisible = {} const setCollapsedState = (node, parent) => { ancestorCollapsed[node] = ancestorCollapsed[parent] || collapsed[parent] const kids = treeIndex.children[node] - if (kids) - kids.forEach ((child) => setCollapsedState (child, node)) + if (kids) kids.forEach((child) => setCollapsedState(child, node)) } - setCollapsedState (treeIndex.root) - treeIndex.nodes.forEach ((node) => nodeVisible[node] = (!ancestorCollapsed[node] - && (treeIndex.children[node].length === 0 - || forceDisplayNode[node]))) + setCollapsedState(treeIndex.root) + treeIndex.nodes.forEach( + (node) => + (nodeVisible[node] = + !ancestorCollapsed[node] && + (treeIndex.children[node].length === 0 || forceDisplayNode[node])) + ) let columnVisible = new Array(alignIndex.columns).fill(false) - treeIndex.nodes.filter ((node) => nodeVisible[node]).forEach ((node) => { - if (rowDataAsArray[node]) - rowDataAsArray[node].forEach ((c, col) => { if (!this.props.isGapChar(c)) columnVisible[col] = true }) - }) - return extend ({ ancestorCollapsed, nodeVisible, columnVisible }, - view) + treeIndex.nodes + .filter((node) => nodeVisible[node]) + .forEach((node) => { + if (rowDataAsArray[node]) + rowDataAsArray[node].forEach((c, col) => { + if (!this.props.isGapChar(c)) columnVisible[col] = true + }) + }) + return extend({ ancestorCollapsed, nodeVisible, columnVisible }, view) } - + // layout tree - layoutTree (computedView) { + layoutTree(computedView) { const { computedTreeConfig, treeIndex } = this.props const { nodeVisible, nodeScale } = computedView - const { genericRowHeight, nodeHandleRadius, treeStrokeWidth, availableTreeWidth, scrollbarHeight } = computedTreeConfig - let nx = {}, ny = {}, computedRowScale = [], nodeHeight = {}, rowHeight = [], treeHeight = 0 - const rowY = treeIndex.nodes.map ((node) => { - const scale = typeof(nodeScale[node]) !== 'undefined' ? nodeScale[node] : 1 + const { + genericRowHeight, + nodeHandleRadius, + treeStrokeWidth, + availableTreeWidth, + scrollbarHeight, + } = computedTreeConfig + let nx = {}, + ny = {}, + computedRowScale = [], + nodeHeight = {}, + rowHeight = [], + treeHeight = 0 + const rowY = treeIndex.nodes.map((node) => { + const scale = typeof nodeScale[node] !== 'undefined' ? nodeScale[node] : 1 const rh = scale * (nodeVisible[node] ? genericRowHeight : 0) const y = treeHeight - nx[node] = nodeHandleRadius + treeStrokeWidth + availableTreeWidth * treeIndex.distFromRoot[node] / treeIndex.maxDistFromRoot + nx[node] = + nodeHandleRadius + + treeStrokeWidth + + (availableTreeWidth * treeIndex.distFromRoot[node]) / + treeIndex.maxDistFromRoot ny[node] = y + rh / 2 nodeHeight[node] = rh - computedRowScale.push (scale) - rowHeight.push (rh) + computedRowScale.push(scale) + rowHeight.push(rh) treeHeight += rh return y }) treeHeight += scrollbarHeight - return { nx, ny, computedRowScale, nodeHeight, rowHeight, rowY, treeHeight, computedView } + return { + nx, + ny, + computedRowScale, + nodeHeight, + rowHeight, + rowY, + treeHeight, + computedView, + } } // get metrics and other info about alignment font/chars, and do layout - layoutAlignment (computedView) { + layoutAlignment(computedView) { const { alignIndex, computedFontConfig } = this.props const { genericRowHeight, charFont } = computedFontConfig const alignChars = alignIndex.chars - let charWidth = 0, charMetrics = {} - alignChars.forEach ((c) => { - let measureCanvas = this.create ('canvas', null, null, { width: genericRowHeight, height: genericRowHeight }) + let charWidth = 0, + charMetrics = {} + alignChars.forEach((c) => { + let measureCanvas = this.create('canvas', null, null, { + width: genericRowHeight, + height: genericRowHeight, + }) let measureContext = measureCanvas.getContext('2d') measureContext.font = charFont - charMetrics[c] = measureContext.measureText (c) - charWidth = Math.max (charWidth, Math.ceil (charMetrics[c].width)) + charMetrics[c] = measureContext.measureText(c) + charWidth = Math.max(charWidth, Math.ceil(charMetrics[c].width)) }) const charHeight = genericRowHeight - let nextColX = 0, colX = [], colWidth = [], computedColScale = [] + let nextColX = 0, + colX = [], + colWidth = [], + computedColScale = [] for (let col = 0; col < alignIndex.columns; ++col) { - colX.push (nextColX) + colX.push(nextColX) if (computedView.columnVisible[col]) { let scale = computedView.columnScale[col] - if (typeof(scale) === 'undefined') - scale = 1 - computedColScale.push (scale) + if (typeof scale === 'undefined') scale = 1 + computedColScale.push(scale) const width = scale * charWidth - colWidth.push (width) + colWidth.push(width) nextColX += width } else { - computedColScale.push (0) - colWidth.push (0) + computedColScale.push(0) + colWidth.push(0) } } - return { charMetrics, charWidth, charHeight, colX, colWidth, computedColScale, alignWidth: nextColX } + return { + charMetrics, + charWidth, + charHeight, + colX, + colWidth, + computedColScale, + alignWidth: nextColX, + } } // helper to create DOM element (for measurement purposes, or non-React components) - create (type, parent, styles, attrs) { - const element = document.createElement (type) - if (parent) - parent.appendChild (element) + create(type, parent, styles, attrs) { + const element = document.createElement(type) + if (parent) parent.appendChild(element) if (attrs) - Object.keys(attrs).filter ((attr) => typeof(attrs[attr]) !== 'undefined').forEach ((attr) => element.setAttribute (attr, attrs[attr])) - if (styles) - element.style = styles + Object.keys(attrs) + .filter((attr) => typeof attrs[attr] !== 'undefined') + .forEach((attr) => element.setAttribute(attr, attrs[attr])) + if (styles) element.style = styles return element } - + render() { const computedView = this.getComputedView() - const treeLayout = this.layoutTree (computedView) - const alignLayout = this.layoutAlignment (computedView) + const treeLayout = this.layoutTree(computedView) + const alignLayout = this.layoutAlignment(computedView) // record the dimensions for drag handling this.treeHeight = treeLayout.treeHeight this.alignWidth = alignLayout.alignWidth - - return ( -
- -
this.structRef.current.removeLabelFromStructuresOnMouseout()} - style={{ width: this.props.config.containerWidth, - height: treeLayout.treeAlignHeight }}> - - - - - + return ( +
+
+ this.structRef.current.removeLabelFromStructuresOnMouseout() + } + style={{ + width: this.props.config.containerWidth, + height: treeLayout.treeAlignHeight, + }} + > + + + + +
-
) } componentDidMount() { - window.addEventListener ('mouseleave', this.handleMouseLeave.bind(this)) - window.addEventListener ('mouseup', this.handleMouseUp.bind(this)) - window.addEventListener ('mousemove', this.handleMouseMove.bind(this)) - this.msaRef.current.addEventListener ('wheel', this.handleMouseWheel.bind(this), { passive: false }) + window.addEventListener('mouseleave', this.handleMouseLeave.bind(this)) + window.addEventListener('mouseup', this.handleMouseUp.bind(this)) + window.addEventListener('mousemove', this.handleMouseMove.bind(this)) + this.msaRef.current.addEventListener( + 'wheel', + this.handleMouseWheel.bind(this), + { passive: false } + ) } componentWillUnmount() { - window.removeEventListener ('mouseleave', this.handleMouseLeave.bind(this)) - window.removeEventListener ('mouseup', this.handleMouseUp.bind(this)) - window.removeEventListener ('mousemove', this.handleMouseMove.bind(this)) - this.msaRef.current.removeEventListener ('wheel', this.handleMouseWheel.bind(this)) + window.removeEventListener('mouseleave', this.handleMouseLeave.bind(this)) + window.removeEventListener('mouseup', this.handleMouseUp.bind(this)) + window.removeEventListener('mousemove', this.handleMouseMove.bind(this)) + this.msaRef.current.removeEventListener( + 'wheel', + this.handleMouseWheel.bind(this) + ) } - setAlignmentClientSize (w, h) { + setAlignmentClientSize(w, h) { this.alignmentClientWidth = w this.alignmentClientHeight = h } - handleNameClick (node) { + handleNameClick(node) { const { structure } = this.props.data this.nStructs = (this.nStructs || 0) + 1 - const newStructure = { node, - structureInfo: structure[node], - startPos: this.props.data.seqCoords[node].startPos, - mouseoverLabel: [], - trueAtomColor: {}, - key: this.nStructs } + const newStructure = { + node, + structureInfo: structure[node], + startPos: this.props.data.seqCoords[node].startPos, + mouseoverLabel: [], + trueAtomColor: {}, + key: this.nStructs, + } let view = this.state.view - view.structure.openStructures.push (newStructure) - this.setState ({ view }) + view.structure.openStructures.push(newStructure) + this.setState({ view }) } - updateStructure (structure, newStructure) { + updateStructure(structure, newStructure) { let view = this.state.view - view.structure.openStructures = view.structure.openStructures - .map ((s) => (s === structure - ? extend (s, newStructure) - : s)) - this.setState ({ view }) + view.structure.openStructures = view.structure.openStructures.map((s) => + s === structure ? extend(s, newStructure) : s + ) + this.setState({ view }) } - handleCloseStructure (structure) { + handleCloseStructure(structure) { let view = this.state.view - view.structure.openStructures = view.structure.openStructures.filter ((s) => s !== structure) - this.setState ({ view }) + view.structure.openStructures = view.structure.openStructures.filter( + (s) => s !== structure + ) + this.setState({ view }) } - - handleNodeClick (node) { + + handleNodeClick(node) { if (this.scrolling) { this.scrolling = false return } - if (this.state.disableTreeEvents) - return + if (this.state.disableTreeEvents) return const { treeIndex, alignIndex } = this.props const computedView = this.getComputedView() - const { collapsed, nodeScale, columnScale, forceDisplayNode, columnVisible } = computedView - - const alignLayout = this.layoutAlignment (computedView) - const left = this.state.alignScrollLeft, right = left + this.alignmentClientWidth + const { + collapsed, + nodeScale, + columnScale, + forceDisplayNode, + columnVisible, + } = computedView + + const alignLayout = this.layoutAlignment(computedView) + const left = this.state.alignScrollLeft, + right = left + this.alignmentClientWidth const collapseAnimationFrames = this.collapseAnimationFrames() let framesLeft = collapseAnimationFrames - const wasCollapsed = collapsed[node], finalCollapsed = extend ({}, collapsed) + const wasCollapsed = collapsed[node], + finalCollapsed = extend({}, collapsed) if (wasCollapsed) { - collapsed[node] = false // when collapsed[node]=false (vs undefined), it's rendered by renderTree() as a collapsed node, but its descendants are still visible. A bit of a hack... + collapsed[node] = false // when collapsed[node]=false (vs undefined), it's rendered by renderTree() as a collapsed node, but its descendants are still visible. A bit of a hack... delete finalCollapsed[node] - } else - finalCollapsed[node] = true - const finalForceDisplayNode = extend ({}, forceDisplayNode) + } else finalCollapsed[node] = true + const finalForceDisplayNode = extend({}, forceDisplayNode) finalForceDisplayNode[node] = !wasCollapsed - const finalComputedView = this.getComputedView ({ collapsed: finalCollapsed, forceDisplayNode: finalForceDisplayNode }) - let newlyVisibleColumns = [], newlyHiddenColumns = [], persistingVisibleColumns = [] + const finalComputedView = this.getComputedView({ + collapsed: finalCollapsed, + forceDisplayNode: finalForceDisplayNode, + }) + let newlyVisibleColumns = [], + newlyHiddenColumns = [], + persistingVisibleColumns = [] for (let col = 0; col < alignIndex.columns; ++col) { if (finalComputedView.columnVisible[col] !== columnVisible[col]) - (finalComputedView.columnVisible[node] ? newlyVisibleColumns : newlyHiddenColumns).push (col) - const colX = alignLayout.colX[col], colWidth = alignLayout.colWidth[col] - if (columnVisible[col] && finalComputedView.columnVisible[col] && colX >= left && colX + colWidth < right) - persistingVisibleColumns.push (col) + (finalComputedView.columnVisible[node] + ? newlyVisibleColumns + : newlyHiddenColumns + ).push(col) + const colX = alignLayout.colX[col], + colWidth = alignLayout.colWidth[col] + if ( + columnVisible[col] && + finalComputedView.columnVisible[col] && + colX >= left && + colX + colWidth < right + ) + persistingVisibleColumns.push(col) } - const centroidMinusScroll = this.centroidOfColumns (persistingVisibleColumns, alignLayout) - this.state.alignScrollLeft + const centroidMinusScroll = + this.centroidOfColumns(persistingVisibleColumns, alignLayout) - + this.state.alignScrollLeft let lastFrameTime = Date.now() - const expectedTimeBetweenFrames = this.collapseAnimationDuration() / collapseAnimationFrames + const expectedTimeBetweenFrames = + this.collapseAnimationDuration() / collapseAnimationFrames const drawAnimationFrame = () => { - let disableTreeEvents, animating, newCollapsed = collapsed + let disableTreeEvents, + animating, + newCollapsed = collapsed if (framesLeft) { - const scale = (wasCollapsed ? (collapseAnimationFrames + 1 - framesLeft) : framesLeft) / (collapseAnimationFrames + 1) - treeIndex.descendants[node].forEach ((desc) => { nodeScale[desc] = scale }) + const scale = + (wasCollapsed + ? collapseAnimationFrames + 1 - framesLeft + : framesLeft) / + (collapseAnimationFrames + 1) + treeIndex.descendants[node].forEach((desc) => { + nodeScale[desc] = scale + }) nodeScale[node] = 1 - scale - newlyHiddenColumns.forEach ((col) => columnScale[col] = scale) - newlyVisibleColumns.forEach ((col) => columnScale[col] = 1 - scale) + newlyHiddenColumns.forEach((col) => (columnScale[col] = scale)) + newlyVisibleColumns.forEach((col) => (columnScale[col] = 1 - scale)) forceDisplayNode[node] = true disableTreeEvents = true animating = true } else { - treeIndex.descendants[node].forEach ((desc) => { delete nodeScale[desc] }) + treeIndex.descendants[node].forEach((desc) => { + delete nodeScale[desc] + }) delete nodeScale[node] - newlyHiddenColumns.forEach ((col) => delete columnScale[col]) - newlyVisibleColumns.forEach ((col) => delete columnScale[col]) + newlyHiddenColumns.forEach((col) => delete columnScale[col]) + newlyVisibleColumns.forEach((col) => delete columnScale[col]) forceDisplayNode[node] = !wasCollapsed newCollapsed = finalCollapsed disableTreeEvents = false animating = false } - const view = extend ({}, this.state.view, { collapsed: newCollapsed, forceDisplayNode, nodeScale, columnScale, disableTreeEvents, animating }) - const computedView = this.getComputedView (view), alignLayout = this.layoutAlignment (computedView) - const alignScrollLeft = this.boundAlignScrollLeft (this.centroidOfColumns (persistingVisibleColumns, alignLayout) - centroidMinusScroll) - window.requestAnimationFrame (() => { - this.setState ({ alignScrollLeft, view }) - + const view = extend({}, this.state.view, { + collapsed: newCollapsed, + forceDisplayNode, + nodeScale, + columnScale, + disableTreeEvents, + animating, + }) + const computedView = this.getComputedView(view), + alignLayout = this.layoutAlignment(computedView) + const alignScrollLeft = this.boundAlignScrollLeft( + this.centroidOfColumns(persistingVisibleColumns, alignLayout) - + centroidMinusScroll + ) + window.requestAnimationFrame(() => { + this.setState({ alignScrollLeft, view }) + if (framesLeft) { const currentTime = Date.now(), - timeSinceLastFrame = currentTime - lastFrameTime, - timeToNextFrame = Math.max (0, expectedTimeBetweenFrames - timeSinceLastFrame), - frameSkip = Math.min (this.collapseAnimationMaxFrameSkip(), Math.ceil (timeSinceLastFrame / expectedTimeBetweenFrames)) - framesLeft = Math.max (0, framesLeft - frameSkip) + timeSinceLastFrame = currentTime - lastFrameTime, + timeToNextFrame = Math.max( + 0, + expectedTimeBetweenFrames - timeSinceLastFrame + ), + frameSkip = Math.min( + this.collapseAnimationMaxFrameSkip(), + Math.ceil(timeSinceLastFrame / expectedTimeBetweenFrames) + ) + framesLeft = Math.max(0, framesLeft - frameSkip) lastFrameTime = currentTime - setTimeout (drawAnimationFrame, timeToNextFrame) + setTimeout(drawAnimationFrame, timeToNextFrame) } }) } - - drawAnimationFrame (collapseAnimationFrames) + + drawAnimationFrame(collapseAnimationFrames) } - handleAlignmentScroll (alignScrollLeft, scrollTop) { - if (alignScrollLeft !== this.state.alignScrollLeft - || scrollTop !== this.state.scrollTop) - this.setState ({ alignScrollLeft, scrollTop }) + handleAlignmentScroll(alignScrollLeft, scrollTop) { + if ( + alignScrollLeft !== this.state.alignScrollLeft || + scrollTop !== this.state.scrollTop + ) + this.setState({ alignScrollLeft, scrollTop }) } - handleMouseWheel (evt) { + handleMouseWheel(evt) { // nonzero deltaMode is Firefox, means deltaY is in lines instead of pixels // can be corrected for e.g. https://stackoverflow.com/questions/20110224/what-is-the-height-of-a-line-in-a-wheel-event-deltamode-dom-delta-line if (evt.deltaY !== 0) { - const deltaY = evt.deltaY * (evt.deltaMode ? this.props.config.genericRowHeight : 1) + const deltaY = + evt.deltaY * (evt.deltaMode ? this.props.config.genericRowHeight : 1) evt.preventDefault() - this.requestAnimationFrame (() => { - this.setState ({ alignScrollLeft: this.incAlignScrollLeft (evt.deltaX), - scrollTop: this.incScrollTop (deltaY) }) + this.requestAnimationFrame(() => { + this.setState({ + alignScrollLeft: this.incAlignScrollLeft(evt.deltaX), + scrollTop: this.incScrollTop(deltaY), + }) }) } } - handleMouseDown (evt) { + handleMouseDown(evt) { this.mouseDown = true this.lastY = evt.pageY } - handleAlignmentMouseDown (evt) { + handleAlignmentMouseDown(evt) { this.alignMouseDown = true this.lastX = evt.pageX } - handleAlignCharClick (coords) { + handleAlignCharClick(coords) { if (!this.panning && !this.scrolling) { -// console.warn('click',coords) + // console.warn('click',coords) } this.panning = this.scrolling = false } - handleAlignCharMouseOver (coords) { + handleAlignCharMouseOver(coords) { if (!this.panning && !this.scrolling) { - this.structRef.current.addLabelToStructuresOnMouseover (coords) -// console.warn('mouseover',coords) + this.structRef.current.addLabelToStructuresOnMouseover(coords) + // console.warn('mouseover',coords) } } - handleAlignCharMouseOut (coords) { + handleAlignCharMouseOut(coords) { if (!this.panning && !this.scrolling) { this.structRef.current.removeLabelFromStructuresOnMouseout() -// console.warn('mouseout',coords) + // console.warn('mouseout',coords) } } - + handleMouseLeave() { this.alignMouseDown = false this.mouseDown = false @@ -407,83 +527,91 @@ class MSA extends Component { this.mouseDown = false } - centroidOfColumns (cols, alignLayout) { - return cols.length && (cols.reduce ((sum, col) => sum + alignLayout.colX[col] + (alignLayout.colWidth[col] / 2), 0) / cols.length) + centroidOfColumns(cols, alignLayout) { + return ( + cols.length && + cols.reduce( + (sum, col) => + sum + alignLayout.colX[col] + alignLayout.colWidth[col] / 2, + 0 + ) / cols.length + ) } - incAlignScrollLeft (dx) { - return this.boundAlignScrollLeft (this.state.alignScrollLeft + dx) + incAlignScrollLeft(dx) { + return this.boundAlignScrollLeft(this.state.alignScrollLeft + dx) } - incScrollTop (dy) { - return this.boundScrollTop (this.state.scrollTop + dy) + incScrollTop(dy) { + return this.boundScrollTop(this.state.scrollTop + dy) } - boundAlignScrollLeft (x) { - return Math.max (0, Math.min (this.alignWidth - this.alignmentClientWidth, x)) + boundAlignScrollLeft(x) { + return Math.max(0, Math.min(this.alignWidth - this.alignmentClientWidth, x)) } - boundScrollTop (y) { - return Math.max (0, Math.min (this.treeHeight - this.alignmentClientHeight, y)) + boundScrollTop(y) { + return Math.max( + 0, + Math.min(this.treeHeight - this.alignmentClientHeight, y) + ) } - handleMouseMove (evt) { - if (this.alignMouseDown || this.mousedown) - evt.preventDefault() + handleMouseMove(evt) { + if (this.alignMouseDown || this.mousedown) evt.preventDefault() - let { alignScrollLeft, scrollTop } = this.state, updated = false + let { alignScrollLeft, scrollTop } = this.state, + updated = false if (this.alignMouseDown) { const dx = evt.pageX - this.lastX if (dx) { - alignScrollLeft = this.incAlignScrollLeft (-dx) + alignScrollLeft = this.incAlignScrollLeft(-dx) this.panning = true updated = true } - } else - this.panning = false + } else this.panning = false if (this.mouseDown) { const dy = evt.pageY - this.lastY if (dy) { - scrollTop = this.incScrollTop (-dy) + scrollTop = this.incScrollTop(-dy) this.scrolling = true updated = true } - } else - this.scrolling = false + } else this.scrolling = false if (updated) - this.requestAnimationFrame (() => { - this.setState ({ alignScrollLeft, scrollTop }) + this.requestAnimationFrame(() => { + this.setState({ alignScrollLeft, scrollTop }) this.lastX = evt.pageX this.lastY = evt.pageY }) } // request animation frame - requestAnimationFrame (callback) { + requestAnimationFrame(callback) { if (this.animationTimeout) - window.cancelAnimationFrame (this.animationTimeout) - this.animationTimeout = window.requestAnimationFrame (callback) + window.cancelAnimationFrame(this.animationTimeout) + this.animationTimeout = window.requestAnimationFrame(callback) } // set generic timer - setTimer (name, delay, callback) { + setTimer(name, delay, callback) { this.timer = this.timer || {} - this.clearTimer (this, name) - this.timer[name] = window.setTimeout (() => { + this.clearTimer(this, name) + this.timer[name] = window.setTimeout(() => { delete this.timer[name] callback() }, delay) } // clear generic timer - clearTimer (name) { + clearTimer(name) { if (this.timer && this.timer[name]) { - window.clearTimeout (this.timer[name]) + window.clearTimeout(this.timer[name]) delete this.timer[name] } } } -export default MSA; +export default MSA diff --git a/src/MSAAlignCanvas.js b/src/MSAAlignCanvas.js index c4312f6..b5d1ac2 100644 --- a/src/MSAAlignCanvas.js +++ b/src/MSAAlignCanvas.js @@ -1,57 +1,68 @@ -import React, { Component } from 'react'; +import React, { Component } from 'react' class MSAAlignCanvas extends Component { - constructor(props) { - super(props); - this.state = { clientWidth: 0, - clientHeight: 0 } + super(props) + this.state = { clientWidth: 0, clientHeight: 0 } this.canvasRef = React.createRef() } render() { const { top, left, width, height } = this.getDimensions() - return () + return ( + + ) } - getColor (c) { + getColor(c) { const { color } = this.props.computedFontConfig - return color[c.toUpperCase()] || color['default'] || 'black'; + return color[c.toUpperCase()] || color['default'] || 'black' } // offscreenRatio = the proportion of the rendered view that is invisible, on each side. Total rendered area = visible area * (1 + 2 * offscreenRatio)^2 - getOffscreenRatio() { return 1 } + getOffscreenRatio() { + return 1 + } getDimensions() { const { scrollLeft, scrollTop } = this.props const { clientWidth, clientHeight } = this.state const offscreenRatio = this.getOffscreenRatio() - const offscreenWidth = offscreenRatio * clientWidth, offscreenHeight = offscreenRatio * clientHeight - const top = Math.max (0, scrollTop - offscreenHeight), - left = Math.max (0, scrollLeft - offscreenWidth), - bottom = Math.min (this.props.treeLayout.treeHeight, scrollTop + clientHeight + offscreenHeight), - right = Math.min (this.props.alignLayout.alignWidth, scrollLeft + clientWidth + offscreenWidth), - width = right - left, - height = bottom - top + const offscreenWidth = offscreenRatio * clientWidth, + offscreenHeight = offscreenRatio * clientHeight + const top = Math.max(0, scrollTop - offscreenHeight), + left = Math.max(0, scrollLeft - offscreenWidth), + bottom = Math.min( + this.props.treeLayout.treeHeight, + scrollTop + clientHeight + offscreenHeight + ), + right = Math.min( + this.props.alignLayout.alignWidth, + scrollLeft + clientWidth + offscreenWidth + ), + width = right - left, + height = bottom - top return { top, left, bottom, right, width, height } } - - setClientSize (clientWidth, clientHeight) { - if (clientWidth !== this.state.clientWidth - || clientHeight !== this.state.clientHeight) - this.setState ({ clientWidth, clientHeight }) + + setClientSize(clientWidth, clientHeight) { + if ( + clientWidth !== this.state.clientWidth || + clientHeight !== this.state.clientHeight + ) + this.setState({ clientWidth, clientHeight }) } - + componentDidMount() { this.renderVisibleRegion() } - + componentDidUpdate() { this.renderVisibleRegion() } @@ -60,43 +71,68 @@ class MSAAlignCanvas extends Component { const alignCanvas = this.canvasRef.current const ctx = alignCanvas.getContext('2d') const { top, left, bottom, right } = this.getDimensions() - const { computedFontConfig, treeLayout, alignLayout, treeIndex, alignIndex, data } = this.props + const { + computedFontConfig, + treeLayout, + alignLayout, + treeIndex, + alignIndex, + data, + } = this.props const { rowData } = data - ctx.setTransform (1, 0, 0, 1, 0, 0) + ctx.setTransform(1, 0, 0, 1, 0, 0) ctx.globalAlpha = 1 - ctx.clearRect (0, 0, alignCanvas.width, alignCanvas.height) + ctx.clearRect(0, 0, alignCanvas.width, alignCanvas.height) ctx.font = computedFontConfig.charFont - let firstRow, lastRow // firstRow is first (partially) visible row, lastRow is last (partially) visible row - for (let row = firstRow = 0; row < treeLayout.rowHeight.length && treeLayout.rowY[row] < bottom; ++row) { - if (treeLayout.rowY[row] < top) - firstRow = row + let firstRow, lastRow // firstRow is first (partially) visible row, lastRow is last (partially) visible row + for ( + let row = (firstRow = 0); + row < treeLayout.rowHeight.length && treeLayout.rowY[row] < bottom; + ++row + ) { + if (treeLayout.rowY[row] < top) firstRow = row lastRow = row } let colX = 0 for (let col = 0; col < alignIndex.columns && colX < right; ++col) { const xScale = alignLayout.computedColScale[col], - colX = alignLayout.colX[col], - width = alignLayout.colWidth[col] + colX = alignLayout.colX[col], + width = alignLayout.colWidth[col] if (xScale && colX + width >= left) for (let row = firstRow; row <= lastRow; ++row) { const yScale = treeLayout.computedRowScale[row], - rowY = treeLayout.rowY[row], - height = treeLayout.rowHeight[row], - seq = rowData[treeIndex.nodes[row]] + rowY = treeLayout.rowY[row], + height = treeLayout.rowHeight[row], + seq = rowData[treeIndex.nodes[row]] if (height && seq) { - ctx.globalAlpha = Math.min (xScale, yScale) + ctx.globalAlpha = Math.min(xScale, yScale) const c = seq[col] - if (typeof(c) === 'string') { - ctx.setTransform (xScale, 0, 0, yScale, colX - left, rowY + height - top) - ctx.fillStyle = this.getColor (c) - ctx.fillText (c, 0, 0) + if (typeof c === 'string') { + ctx.setTransform( + xScale, + 0, + 0, + yScale, + colX - left, + rowY + height - top + ) + ctx.fillStyle = this.getColor(c) + ctx.fillText(c, 0, 0) } else { let psum = 0 - c.forEach ((cp) => { - const ci = cp[0], p = cp[1] - ctx.setTransform (xScale, 0, 0, yScale * p, colX - left, rowY + height*(1-psum) - top) - ctx.fillStyle = this.getColor (ci) - ctx.fillText (ci, 0, 0) + c.forEach((cp) => { + const ci = cp[0], + p = cp[1] + ctx.setTransform( + xScale, + 0, + 0, + yScale * p, + colX - left, + rowY + height * (1 - psum) - top + ) + ctx.fillStyle = this.getColor(ci) + ctx.fillText(ci, 0, 0) psum += p }) } @@ -106,4 +142,4 @@ class MSAAlignCanvas extends Component { } } -export default MSAAlignCanvas; +export default MSAAlignCanvas diff --git a/src/MSAAlignNames.js b/src/MSAAlignNames.js index 76e1e63..ab7e319 100644 --- a/src/MSAAlignNames.js +++ b/src/MSAAlignNames.js @@ -1,49 +1,66 @@ -import React, { Component } from 'react'; +import React, { Component } from 'react' class MSAAlignNames extends Component { - render() { - const { data, computedFontConfig, treeIndex, config, computedView, treeLayout } = this.props + const { + data, + computedFontConfig, + treeIndex, + config, + computedView, + treeLayout, + } = this.props const structure = data.structure || {} const { nameDivWidth } = config const { nameFontName, nameFontSize } = computedFontConfig const { nodeHeight } = treeLayout - - return (
-
- { treeIndex.nodes - .filter ((node) => computedView.nodeVisible[node]) - .map ((node, row) => { - const style = { height: nodeHeight[node] + 'px' } - const scale = this.props.view.nodeScale[node] - if (typeof(scale) !== 'undefined' && scale !== 1) { - style.transform = 'scale(1,' + scale +')' - style.opacity = scale - } - return (
- { structure[node] - ? (this.props.handleNameClick(node)} - style={{ fontFamily: nameFontName, - fontSize: nameFontSize + 'px' }} - > - { node } - ) - : ( { node } ) } -
) - }) } -
-
) - } + return ( +
+
+ {treeIndex.nodes + .filter((node) => computedView.nodeVisible[node]) + .map((node, row) => { + const style = { height: nodeHeight[node] + 'px' } + const scale = this.props.view.nodeScale[node] + if (typeof scale !== 'undefined' && scale !== 1) { + style.transform = 'scale(1,' + scale + ')' + style.opacity = scale + } + return ( +
+ {structure[node] ? ( + this.props.handleNameClick(node)} + style={{ + fontFamily: nameFontName, + fontSize: nameFontSize + 'px', + }} + > + {node} + + ) : ( + {node} + )} +
+ ) + })} +
+
+ ) + } } -export default MSAAlignNames; +export default MSAAlignNames diff --git a/src/MSAAlignRows.js b/src/MSAAlignRows.js index e9869c3..096fbb3 100644 --- a/src/MSAAlignRows.js +++ b/src/MSAAlignRows.js @@ -1,11 +1,10 @@ -import React, { Component } from 'react'; +import React, { Component } from 'react' -import MSAAlignCanvas from './MSAAlignCanvas'; +import MSAAlignCanvas from './MSAAlignCanvas' class MSAAlignRows extends Component { - constructor(props) { - super(props); + super(props) this.rowsDivRef = React.createRef() this.alignCanvasRef = React.createRef() } @@ -14,102 +13,121 @@ class MSAAlignRows extends Component { const { treeHeight } = this.props.treeLayout const { alignWidth } = this.props.alignLayout - return (
- - - -
- -
) + return ( +
+ + +
+
+ ) } componentDidUpdate() { this.setScrollPos() this.setClientSize() } - + componentDidMount() { this.setScrollPos() this.setClientSize() - window.addEventListener ('resize', this.setClientSize.bind(this)) + window.addEventListener('resize', this.setClientSize.bind(this)) } componentWillUnmount() { - window.removeEventListener ('resize', this.setClientSize.bind(this)) + window.removeEventListener('resize', this.setClientSize.bind(this)) } setClientSize() { - this.props.setClientSize (this.rowsDivRef.current.clientWidth, - this.rowsDivRef.current.clientHeight) - this.alignCanvasRef.current.setClientSize (this.rowsDivRef.current.clientWidth, - this.rowsDivRef.current.clientHeight) + this.props.setClientSize( + this.rowsDivRef.current.clientWidth, + this.rowsDivRef.current.clientHeight + ) + this.alignCanvasRef.current.setClientSize( + this.rowsDivRef.current.clientWidth, + this.rowsDivRef.current.clientHeight + ) } - - setScrollPos (opts) { + + setScrollPos(opts) { opts = opts || this.props this.rowsDivRef.current.scrollLeft = opts.scrollLeft this.rowsDivRef.current.scrollTop = opts.scrollTop } - handleClick (evt) { - this.props.handleAlignCharClick (this.resolveAlignCoords (evt)) + handleClick(evt) { + this.props.handleAlignCharClick(this.resolveAlignCoords(evt)) } - handleMouseMove (evt) { - const coords = this.resolveAlignCoords (evt) - if (!this.lastCoords || coords.row !== this.lastCoords.row || coords.column !== this.lastCoords.column) { - this.props.handleAlignCharMouseOut (this.lastCoords) - this.props.handleAlignCharMouseOver (coords) + handleMouseMove(evt) { + const coords = this.resolveAlignCoords(evt) + if ( + !this.lastCoords || + coords.row !== this.lastCoords.row || + coords.column !== this.lastCoords.column + ) { + this.props.handleAlignCharMouseOut(this.lastCoords) + this.props.handleAlignCharMouseOver(coords) this.lastCoords = coords } } - handleMouseDown (evt) { - this.props.handleMouseDown (evt) + handleMouseDown(evt) { + this.props.handleMouseDown(evt) } - handleScroll (evt) { - this.props.handleScroll (this.rowsDivRef.current.scrollLeft, - this.rowsDivRef.current.scrollTop) + handleScroll(evt) { + this.props.handleScroll( + this.rowsDivRef.current.scrollLeft, + this.rowsDivRef.current.scrollTop + ) } - resolveAlignCoords (evt) { + resolveAlignCoords(evt) { const { treeIndex, alignIndex, treeLayout, alignLayout, data } = this.props const { rowData } = data - const x = parseInt (evt.nativeEvent.offsetX), - y = parseInt (evt.nativeEvent.offsetY) + const x = parseInt(evt.nativeEvent.offsetX), + y = parseInt(evt.nativeEvent.offsetY) let row, column for (row = 0; row < treeIndex.nodes.length - 1; ++row) - if (treeLayout.rowY[row] <= y && treeLayout.rowY[row] + treeLayout.rowHeight[row] > y) + if ( + treeLayout.rowY[row] <= y && + treeLayout.rowY[row] + treeLayout.rowHeight[row] > y + ) break for (column = 0; column < alignIndex.columns - 1; ++column) - if (alignLayout.colX[column] <= x && alignLayout.colX[column] + alignLayout.colWidth[column] > x) + if ( + alignLayout.colX[column] <= x && + alignLayout.colX[column] + alignLayout.colWidth[column] > x + ) break const node = treeIndex.nodes[row], - colToSeqPos = alignIndex.alignColToSeqPos[node], - seqPos = colToSeqPos && colToSeqPos[column], - seq = rowData[node], - c = seq && seq[column], - isGap = this.props.isGapChar(c) + colToSeqPos = alignIndex.alignColToSeqPos[node], + seqPos = colToSeqPos && colToSeqPos[column], + seq = rowData[node], + c = seq && seq[column], + isGap = this.props.isGapChar(c) return { row, column, node, seqPos, c, isGap } } } -export default MSAAlignRows; +export default MSAAlignRows diff --git a/src/MSAStruct.js b/src/MSAStruct.js index 16ef833..f08681c 100644 --- a/src/MSAStruct.js +++ b/src/MSAStruct.js @@ -1,104 +1,105 @@ -import React, { Component } from 'react'; -import { Select, MenuItem } from '@material-ui/core'; -import CloseIcon from '@material-ui/icons/Close'; -import { isArray } from 'lodash'; -import pv from 'bio-pv'; +import React, { Component } from 'react' +import { Select, MenuItem } from '@material-ui/core' +import CloseIcon from '@material-ui/icons/Close' +import { isArray } from 'lodash' +import pv from 'bio-pv' class MSAStruct extends Component { - constructor(props) { - super(props); + super(props) this.pvDivRef = React.createRef() } render() { const wantStructure = isArray(this.props.structure.structureInfo) const structureID = !wantStructure && this.props.structure.structureInfo.pdb - return (
+ return ( +
+
{this.props.structure.node}
-
- {this.props.structure.node} -
+ {structureID && ( +
{structureID}
+ )} - { structureID - && (
- {structureID} -
) } +
+ {wantStructure && ( + + )} +
+ +
+
-
- { wantStructure - && ( - ) } -
- -
-
- -
- -
) +
+
+ ) } - getPvConfig (structureConfig) { + getPvConfig(structureConfig) { const { width, height } = structureConfig - return (structureConfig.pvConfig - || { width, - height, - antialias: true, - quality : 'medium' }) + return ( + structureConfig.pvConfig || { + width, + height, + antialias: true, + quality: 'medium', + } + ) + } + + pdbUrlPrefix() { + return 'https://files.rcsb.org/download/' + } + pdbUrlSuffix() { + return '.pdb' } - pdbUrlPrefix() { return 'https://files.rcsb.org/download/' } - pdbUrlSuffix() { return '.pdb' } - componentDidMount() { - if (!isArray (this.props.structure.structureInfo)) - this.loadStructure() + if (!isArray(this.props.structure.structureInfo)) this.loadStructure() } componentDidUpdate() { - if (!isArray (this.props.structure.structureInfo)) - this.loadStructure() + if (!isArray(this.props.structure.structureInfo)) this.loadStructure() } - handleSelectStructure (evt) { - this.props.updateStructure ({ structureInfo: evt.target.value }) + handleSelectStructure(evt) { + this.props.updateStructure({ structureInfo: evt.target.value }) } - + loadStructure() { if (!this.props.structure.pdbFetchInitiated) { - this.props.updateStructure ({ pdbFetchInitiated: true }) + this.props.updateStructure({ pdbFetchInitiated: true }) const structureConfig = this.props.config - const pvConfig = this.getPvConfig (structureConfig) - const viewer = pv.Viewer (this.pvDivRef.current, pvConfig) + const pvConfig = this.getPvConfig(structureConfig) + const viewer = pv.Viewer(this.pvDivRef.current, pvConfig) const loadFromPDB = !structureConfig.noRemoteStructures - const pdbFilePath = ((loadFromPDB - ? this.pdbUrlPrefix() - : (structureConfig.pdbPrefix || '')) - + this.props.structure.structureInfo.pdb - + (loadFromPDB - ? this.pdbUrlSuffix() - : (structureConfig.pdbSuffix || ''))) - pv.io.fetchPdb (pdbFilePath, (pdb) => { + const pdbFilePath = + (loadFromPDB ? this.pdbUrlPrefix() : structureConfig.pdbPrefix || '') + + this.props.structure.structureInfo.pdb + + (loadFromPDB ? this.pdbUrlSuffix() : structureConfig.pdbSuffix || '') + pv.io.fetchPdb(pdbFilePath, (pdb) => { // display the protein as cartoon, coloring the secondary structure // elements in a rainbow gradient. - this.props.updateStructure ({ pdb, viewer }) + this.props.updateStructure({ pdb, viewer }) this.props.setViewType() viewer.centerOn(pdb) viewer.autoZoom() @@ -106,10 +107,10 @@ class MSAStruct extends Component { } } - handleClose (evt) { - evt.preventDefault(); - this.props.handleCloseStructure (this.props.structure) + handleClose(evt) { + evt.preventDefault() + this.props.handleCloseStructure(this.props.structure) } } -export default MSAStruct; +export default MSAStruct diff --git a/src/MSAStructPanel.js b/src/MSAStructPanel.js index ab282bb..00f5c8c 100644 --- a/src/MSAStructPanel.js +++ b/src/MSAStructPanel.js @@ -1,145 +1,178 @@ -import React, { Component } from 'react'; -import pv from 'bio-pv'; +import React, { Component } from 'react' +import pv from 'bio-pv' -import { Select, MenuItem, FormControlLabel, Checkbox } from '@material-ui/core'; +import { Select, MenuItem, FormControlLabel, Checkbox } from '@material-ui/core' -import MSAStruct from './MSAStruct'; +import MSAStruct from './MSAStruct' class MSAStructPanel extends Component { constructor(props) { - super(props); + super(props) - this.state = { config: this.props.initConfig, - viewMode: 'cartoon', - colorScheme: 'ssSuccession' } + this.state = { + config: this.props.initConfig, + viewMode: 'cartoon', + colorScheme: 'ssSuccession', + } } - + render() { - return (this.props.structures.length - ? (
- -
- - - - - - } - label="Show label on mouseover" - /> - -
- -
- {this.props.structures.map ((structure) => { - return (this.setViewType(structure)} - updateStructure={(info) => this.props.updateStructure(structure,info)} - handleCloseStructure={this.props.handleCloseStructure} - />) - })} -
- -
) - : '') + return this.props.structures.length ? ( +
+
+ + + + + + } + label="Show label on mouseover" + /> +
+ +
+ {this.props.structures.map((structure) => { + return ( + this.setViewType(structure)} + updateStructure={(info) => + this.props.updateStructure(structure, info) + } + handleCloseStructure={this.props.handleCloseStructure} + /> + ) + })} +
+
+ ) : ( + '' + ) } - handleMouseoverLabelConfig (event) { + handleMouseoverLabelConfig(event) { let config = this.state.config config.showMouseoverLabel = event.target.checked - this.setState ({ config }) + this.setState({ config }) + } + + mouseoverLabelDelay() { + return 100 + } + redrawStructureDelay() { + return 500 } - - mouseoverLabelDelay() { return 100 } - redrawStructureDelay() { return 500 } - setViewType (structure, viewMode, colorScheme) { + setViewType(structure, viewMode, colorScheme) { viewMode = viewMode || this.state.viewMode colorScheme = colorScheme || this.state.colorScheme const { pdb, viewer } = structure if (viewer) { viewer.clear() - const geometry = viewer.renderAs ('protein', pdb, viewMode, { color : pv.color[colorScheme]() }) - this.props.updateStructure (structure, { geometry }) + const geometry = viewer.renderAs('protein', pdb, viewMode, { + color: pv.color[colorScheme](), + }) + this.props.updateStructure(structure, { geometry }) } - this.props.structures.forEach ((s) => { - this.props.updateStructure (s, { trueAtomColor: {} }) + this.props.structures.forEach((s) => { + this.props.updateStructure(s, { trueAtomColor: {} }) }) this.requestRedrawStructures() } - handleSelectViewType (evt) { + handleSelectViewType(evt) { const viewMode = evt.target.value - this.setState ({ viewMode }) - this.props.structures.forEach ((s) => this.setViewType (s, viewMode)) + this.setState({ viewMode }) + this.props.structures.forEach((s) => this.setViewType(s, viewMode)) } - handleSelectColorScheme (evt) { + handleSelectColorScheme(evt) { const colorScheme = evt.target.value - this.setState ({ colorScheme }) - this.props.structures.forEach ((s) => this.setViewType (s, undefined, colorScheme)) + this.setState({ colorScheme }) + this.props.structures.forEach((s) => + this.setViewType(s, undefined, colorScheme) + ) } - - addLabelToStructuresOnMouseover (coords) { - this.setTimer ('mouseover', this.mouseoverLabelDelay(), () => { - const labelConfig = this.state.config.label || { font: 'sans-serif', - fontSize : 12, - fontColor: '#f62', - fillStyle: 'white', - backgroundAlpha : 0.4 } + + addLabelToStructuresOnMouseover(coords) { + this.setTimer('mouseover', this.mouseoverLabelDelay(), () => { + const labelConfig = this.state.config.label || { + font: 'sans-serif', + fontSize: 12, + fontColor: '#f62', + fillStyle: 'white', + backgroundAlpha: 0.4, + } const atomHighlightColor = this.state.config.atomHighlightColor || 'red' - this.props.structures.forEach ((s) => { + this.props.structures.forEach((s) => { if (coords.c && !coords.isGap && s.viewer) { const colToSeqPos = this.props.alignIndex.alignColToSeqPos[s.node] if (colToSeqPos) { const seqPos = colToSeqPos[coords.column] const pdbSeqPos = seqPos + s.startPos - this.removeMouseoverLabels (s) - s.structureInfo.chains.forEach ((chainInfo) => { - if ((!chainInfo.startPos || pdbSeqPos >= chainInfo.startPos) - && (!chainInfo.endPos || pdbSeqPos <= chainInfo.endPos)) { + this.removeMouseoverLabels(s) + s.structureInfo.chains.forEach((chainInfo) => { + if ( + (!chainInfo.startPos || pdbSeqPos >= chainInfo.startPos) && + (!chainInfo.endPos || pdbSeqPos <= chainInfo.endPos) + ) { const pdbChain = chainInfo.chain - const residues = s.pdb.residueSelect ((res) => { - return res.num() === pdbSeqPos - && (typeof(pdbChain) === 'undefined' || res.chain().name() === pdbChain) + const residues = s.pdb.residueSelect((res) => { + return ( + res.num() === pdbSeqPos && + (typeof pdbChain === 'undefined' || + res.chain().name() === pdbChain) + ) }) if (residues) { - residues.eachResidue ((res) => { + residues.eachResidue((res) => { const label = 'mouseover' + (s.mouseoverLabel.length + 1) if (this.state.config.showMouseoverLabel) - s.viewer.label (label, res.qualifiedName(), res.centralAtom().pos(), labelConfig) - res.atoms().forEach ((atom) => { + s.viewer.label( + label, + res.qualifiedName(), + res.centralAtom().pos(), + labelConfig + ) + res.atoms().forEach((atom) => { if (!s.trueAtomColor[atom.index()]) { const atomColor = [0, 0, 0, 0] - s.geometry.getColorForAtom (atom, atomColor) + s.geometry.getColorForAtom(atom, atomColor) s.trueAtomColor[atom.index()] = atomColor } }) - this.setColorForAtoms (s.geometry, res.atoms(), atomHighlightColor) - s.mouseoverLabel.push ({ label, res }) + this.setColorForAtoms( + s.geometry, + res.atoms(), + atomHighlightColor + ) + s.mouseoverLabel.push({ label, res }) }) } } @@ -152,58 +185,67 @@ class MSAStructPanel extends Component { } removeLabelFromStructuresOnMouseout() { - this.clearTimer ('mouseover') - this.props.structures.forEach ((s) => { - this.removeMouseoverLabels (s) + this.clearTimer('mouseover') + this.props.structures.forEach((s) => { + this.removeMouseoverLabels(s) }) this.requestRedrawStructures() } - removeMouseoverLabels (structure) { - structure.mouseoverLabel.forEach ((labelInfo) => { + removeMouseoverLabels(structure) { + structure.mouseoverLabel.forEach((labelInfo) => { if (this.state.config.showMouseoverLabel) - structure.viewer.rm (labelInfo.label) + structure.viewer.rm(labelInfo.label) let byColor = {} - labelInfo.res.atoms().forEach ((atom) => { - const trueColor = structure.trueAtomColor[atom.index()], colorString = trueColor.toString() + labelInfo.res.atoms().forEach((atom) => { + const trueColor = structure.trueAtomColor[atom.index()], + colorString = trueColor.toString() byColor[colorString] = byColor[colorString] || { trueColor, atoms: [] } - byColor[colorString].atoms.push (atom) + byColor[colorString].atoms.push(atom) }) - Object.keys(byColor).forEach ((col) => this.setColorForAtoms (structure.geometry, byColor[col].atoms, byColor[col].trueColor)) + Object.keys(byColor).forEach((col) => + this.setColorForAtoms( + structure.geometry, + byColor[col].atoms, + byColor[col].trueColor + ) + ) }) structure.mouseoverLabel = [] } - setColorForAtoms (go, atoms, color) { + setColorForAtoms(go, atoms, color) { let view = go.structure().createEmptyView() - atoms.forEach ((atom) => view.addAtom (atom)) - go.colorBy (pv.color.uniform(color), view) + atoms.forEach((atom) => view.addAtom(atom)) + go.colorBy(pv.color.uniform(color), view) } // delayed request to redraw structure requestRedrawStructures() { - this.setTimer ('redraw', this.redrawStructureDelay(), () => { - this.props.structures.filter ((s) => s.viewer).forEach ((s) => s.viewer.requestRedraw()) + this.setTimer('redraw', this.redrawStructureDelay(), () => { + this.props.structures + .filter((s) => s.viewer) + .forEach((s) => s.viewer.requestRedraw()) }) } // set generic timer - setTimer (name, delay, callback) { + setTimer(name, delay, callback) { this.timer = this.timer || {} - this.clearTimer (this, name) - this.timer[name] = window.setTimeout (() => { + this.clearTimer(this, name) + this.timer[name] = window.setTimeout(() => { delete this.timer[name] callback() }, delay) } // clear generic timer - clearTimer (name) { + clearTimer(name) { if (this.timer && this.timer[name]) { - window.clearTimeout (this.timer[name]) + window.clearTimeout(this.timer[name]) delete this.timer[name] } } } -export default MSAStructPanel; +export default MSAStructPanel diff --git a/src/MSATree.js b/src/MSATree.js index 0a6ccb5..db7f66c 100644 --- a/src/MSATree.js +++ b/src/MSATree.js @@ -1,33 +1,40 @@ -import React, { Component } from 'react'; +import React, { Component } from 'react' class MSATree extends Component { - constructor(props) { - super(props); + super(props) this.canvasRef = React.createRef() } render() { const { treeWidth } = this.props.computedTreeConfig const { treeHeight } = this.props.treeLayout - return (
- -
) + return ( +
+ +
+ ) } componentDidMount() { this.renderTree() - this.canvasRef.current.addEventListener ('click', this.handleClick.bind(this)) + this.canvasRef.current.addEventListener( + 'click', + this.handleClick.bind(this) + ) } componentWillUnmount() { - this.canvasRef.current.removeEventListener ('click', this.handleClick.bind(this)) + this.canvasRef.current.removeEventListener( + 'click', + this.handleClick.bind(this) + ) } componentDidUpdate() { @@ -35,65 +42,94 @@ class MSATree extends Component { } renderTree() { - const { treeIndex, treeLayout, computedView, computedTreeConfig } = this.props - const { collapsed, ancestorCollapsed, forceDisplayNode, nodeScale } = computedView - const { treeWidth, branchStrokeStyle, treeStrokeWidth, rowConnectorDash, nodeHandleRadius, nodeHandleFillStyle, collapsedNodeHandleFillStyle } = computedTreeConfig + const { + treeIndex, + treeLayout, + computedView, + computedTreeConfig, + } = this.props + const { + collapsed, + ancestorCollapsed, + forceDisplayNode, + nodeScale, + } = computedView + const { + treeWidth, + branchStrokeStyle, + treeStrokeWidth, + rowConnectorDash, + nodeHandleRadius, + nodeHandleFillStyle, + collapsedNodeHandleFillStyle, + } = computedTreeConfig const { nx, ny } = treeLayout const treeCanvas = this.canvasRef.current const ctx = treeCanvas.getContext('2d') - ctx.setTransform (1, 0, 0, 1, 0, 0) + ctx.setTransform(1, 0, 0, 1, 0, 0) ctx.globalAlpha = 1 - ctx.clearRect (0, 0, treeCanvas.width, treeCanvas.height) + ctx.clearRect(0, 0, treeCanvas.width, treeCanvas.height) ctx.strokeStyle = branchStrokeStyle ctx.fillStyle = branchStrokeStyle ctx.lineWidth = treeStrokeWidth const makeNodeHandlePath = (node) => { ctx.beginPath() - ctx.arc (nx[node], ny[node], nodeHandleRadius, 0, 2*Math.PI) + ctx.arc(nx[node], ny[node], nodeHandleRadius, 0, 2 * Math.PI) } const setAlpha = (node) => { const scale = nodeScale[node] - ctx.globalAlpha = (typeof(scale) === 'undefined' || forceDisplayNode[node]) ? 1 : scale + ctx.globalAlpha = + typeof scale === 'undefined' || forceDisplayNode[node] ? 1 : scale } - let nodesWithHandles = treeIndex.nodes.filter ((node) => !ancestorCollapsed[node] && treeIndex.children[node].length) - treeIndex.nodes.forEach ((node) => { + let nodesWithHandles = treeIndex.nodes.filter( + (node) => !ancestorCollapsed[node] && treeIndex.children[node].length + ) + treeIndex.nodes.forEach((node) => { if (!ancestorCollapsed[node]) { if (!treeIndex.children[node].length) { - setAlpha (node) - ctx.setLineDash ([]) + setAlpha(node) + ctx.setLineDash([]) ctx.beginPath() - ctx.fillRect (nx[node], ny[node] - nodeHandleRadius, 1, 2*nodeHandleRadius) + ctx.fillRect( + nx[node], + ny[node] - nodeHandleRadius, + 1, + 2 * nodeHandleRadius + ) } if (treeIndex.children[node].length && !collapsed[node]) { - ctx.setLineDash ([]) - treeIndex.children[node].forEach ((child) => { - setAlpha (child) + ctx.setLineDash([]) + treeIndex.children[node].forEach((child) => { + setAlpha(child) ctx.beginPath() - ctx.moveTo (nx[node], ny[node]) - ctx.lineTo (nx[node], ny[child]) - ctx.lineTo (nx[child], ny[child]) + ctx.moveTo(nx[node], ny[node]) + ctx.lineTo(nx[node], ny[child]) + ctx.lineTo(nx[child], ny[child]) ctx.stroke() }) } ctx.globalAlpha = 1 if (treeIndex.children[node].length === 0 || forceDisplayNode[node]) { - setAlpha (node) - ctx.setLineDash (rowConnectorDash) + setAlpha(node) + ctx.setLineDash(rowConnectorDash) ctx.beginPath() - ctx.moveTo (nx[node], ny[node]) - ctx.lineTo (treeWidth, ny[node]) + ctx.moveTo(nx[node], ny[node]) + ctx.lineTo(treeWidth, ny[node]) ctx.stroke() } } }) ctx.strokeStyle = branchStrokeStyle - ctx.setLineDash ([]) - nodesWithHandles.forEach ((node) => { - setAlpha (node) - makeNodeHandlePath (node) + ctx.setLineDash([]) + nodesWithHandles.forEach((node) => { + setAlpha(node) + makeNodeHandlePath(node) // hack: collapsed[node]===false (vs undefined) means that we are animating the open->collapsed transition // so the node's descendants are visible, but the node itself is rendered as collapsed - if (collapsed[node] || (forceDisplayNode[node] && collapsed[node] !== false)) + if ( + collapsed[node] || + (forceDisplayNode[node] && collapsed[node] !== false) + ) ctx.fillStyle = collapsedNodeHandleFillStyle else { ctx.fillStyle = nodeHandleFillStyle @@ -102,24 +138,33 @@ class MSATree extends Component { ctx.fill() }) this.nodesWithHandles = nodesWithHandles - } + } - handleClick (evt) { + handleClick(evt) { evt.preventDefault() const { treeLayout } = this.props - const mouseX = parseInt (evt.offsetX) - const mouseY = parseInt (evt.offsetY) + const mouseX = parseInt(evt.offsetX) + const mouseY = parseInt(evt.offsetY) let closestNode, closestNodeDistSquared - this.nodesWithHandles.forEach ((node) => { - const distSquared = Math.pow (mouseX - treeLayout.nx[node], 2) + Math.pow (mouseY - treeLayout.ny[node], 2) - if (typeof(closestNodeDistSquared) === 'undefined' || distSquared < closestNodeDistSquared) { - closestNodeDistSquared = distSquared - closestNode = node - } - }) - if (closestNode && closestNodeDistSquared <= Math.pow(this.props.config.nodeHandleClickRadius,2)) - this.props.handleNodeClick (closestNode) + this.nodesWithHandles.forEach((node) => { + const distSquared = + Math.pow(mouseX - treeLayout.nx[node], 2) + + Math.pow(mouseY - treeLayout.ny[node], 2) + if ( + typeof closestNodeDistSquared === 'undefined' || + distSquared < closestNodeDistSquared + ) { + closestNodeDistSquared = distSquared + closestNode = node + } + }) + if ( + closestNode && + closestNodeDistSquared <= + Math.pow(this.props.config.nodeHandleClickRadius, 2) + ) + this.props.handleNodeClick(closestNode) } } -export default MSATree; +export default MSATree diff --git a/src/colorSchemes.js b/src/colorSchemes.js index e1a5e36..2ab1a4e 100644 --- a/src/colorSchemes.js +++ b/src/colorSchemes.js @@ -49,9 +49,9 @@ export default { L: 'blue', M: 'blue', V: 'blue', - F: '#c8a2c8', // lilac - W: '#c8a2c8', // lilac - Y: '#c8a2c8', // lilac + F: '#c8a2c8', // lilac + W: '#c8a2c8', // lilac + Y: '#c8a2c8', // lilac H: 'darkblue', K: 'orange', R: 'orange', diff --git a/src/index.js b/src/index.js index 4b589db..3378478 100644 --- a/src/index.js +++ b/src/index.js @@ -1,142 +1,194 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; -import * as serviceWorker from './serviceWorker'; +import React from 'react' +import ReactDOM from 'react-dom' +import './index.css' +import App from './App' +import * as serviceWorker from './serviceWorker' const opts = { - datasets: [{ - name: "Spike_rec_bind", - description: "Spike receptor binding domain", - "branches": [ - ["node3","SPIKE_CVP67/326-526",0.0044021], - ["node3","Q2QKN3_9BETC/326-526",0.000545546], - ["node13","node3",0.184142], - ["node12","SPIKE_CVHN5/322-526",0.284527], - ["node11","C6GHS2_9BETC/324-530",0.169522], - ["node10","C0KYS1_9BETC/324-540",0.100405], - ["node9","SPIKE_CVMA5/324-534",1e-09], - ["node9","Q9J3E7_9BETC/324-534",0.00934477], - ["node10","node9",0.176232], - ["node11","node10",0.0888497], - ["node12","node11",0.114123], - ["node13","node12",0.0925582], - ["node61","node13",0.00672813], - ["node50","A0A0A7UZR7_9BETC/327-532",0.286551], - ["node19","R9QTH3_CVHSA/321-555",0.0615283], - ["node18","A0A0K1Z074_CVHSA/321-555",0.000774412], - ["node18","A0A166ZL64_9NIDO/316-550",0.0340301], - ["node19","node18",0.0321586], - ["node31","node19",0.0980282], - ["node30","ATO98157.1/317-568",0.0214425], - ["node25","Q1T6X6_CVHSA/317-569",0.00652008], - ["node24","SPIKE_CVHSA/317-569",0.0019582], - ["node24","AAP13441.1/317-568",0.00217293], - ["node25","node24",0.00174427], - ["node29","node25",0.14259], - ["node28","QHD43416.1/330-583",0.0415892], - ["node28","QHR63300.2/330-583",0.0492027], - ["node29","node28",0.115808], - ["node30","node29",0.0573048], - ["node31","node30",0.0944591], - ["node39","node31",0.698168], - ["node38","A0A088DJY6_9BETC/351-629",0.790301], - ["node37","A0A1B3Q5W5_9NIDO/364-584",0.270725], - ["node36","A3EXH4_BCHK9/360-565",0.301569], - ["node36","SPIKE_BCHK9/363-567",0.367537], - ["node37","node36",0.079718], - ["node38","node37",0.555752], - ["node39","node38",0.177297], - ["node49","node39",0.761404], - ["node48","U5LNM4_9BETC/366-630",0.476639], - ["node43","A0A023Y9K3_9BETC/354-613",0.201065], - ["node43","A3EXD9_BCHK5/387-650",0.180317], - ["node47","node43",0.151873], - ["node46","SPIKE_CVEMC/380-534",0.256477], - ["node46","SPIKE_BC133/383-645",0.29522], - ["node47","node46",0.0192677], - ["node48","node47",0.164582], - ["node49","node48",0.462409], - ["node50","node49",0.306269], - ["node60","node50",0.131154], - ["node59","A0A0K2RVL1_9BETC/326-539",0.192394], - ["node58","Q4VID5_CVHOC/339-550",0.0579613], - ["node57","H9AA65_9BETC/326-540",0.0419032], - ["node56","B7U2P4_9BETC/326-540",0.0123283], - ["node56","Q06BD7_9BETC/326-540",0.0113589], - ["node57","node56",0.0297464], - ["node58","node57",0.0150836], - ["node59","node58",0.135815], - ["node60","node59",0.0263022], - ["node61","node60",0.00672813]], - "rowData": { - "SPIKE_CVP67/326-526": "PDLP-NCDIEAWLNSKTV--SSPLNWERKIFSNCNFNMGRLMSFIQADSFGCNNIDASRLYGMCFGSITIDKFAIPNSRKVDLQVGKSGYLQSFNYKI--DTAVSSCQL-----------------YYSLPA-AN---VS------VTHYNPSSWNRRY--GFNNQS-F-------------GS-RGLHDAVY-SQQC-----------F---N----------TP---N----T------Y----------C------------------------PCRT--SQCIGG-------------------A-----G--TG------T-------------------CPVGTTVRKCF---AAVT--------KAT---K-----------CT------------------CWCQPDPS-------------T----------------------", - "Q2QKN3_9BETC/326-526": "PDLP-NCDIEAWLNSKTV--SSPLNWERKIFSNCNFNMGRLMSFIQADSFGCNNIDASRLYGMCFGSITIDKFAIPNSRKVDLQVGKSGYLQSFNYKI--DTAVSSCQL-----------------YYSLPA-AN---VS------VTHYNPSSWNRRY--GFNNQS-F-------------GS-RGLHDAVY-SQQC-----------F---N----------TP---N----T------Y----------C------------------------PCRT--SQCIGG-------------------A-----G--TG------T-------------------CPVGTTVRKCF---AAVT--------NAT---K-----------CT------------------CWCQPDPS-------------T----------------------", - "SPIKE_CVHN5/322-526": "PNLP-DCDIDNWLNNVSV--PSPLNWERRIFSNCNFNLSTLLRLVHVDSFSCNNLDKSKIFGSCFNSITVDKFAIPNRRRDDLQLGSSGFLQSSNYKI--DISSSSCQL-----------------YYSLPL-VN---VT------INNFNPSSWNRRY--GF---GSF-------------NL--SSYDVVY-SDHC-----------F---S----------VN---S----D------F----------C------------------------PCAD--PSVVNS-------------------C-----A--KS-KPPSAI-------------------CPAGTKYRHCD---------------LDT---TLYVKNW-----CR------------------CSCLPDPI-------------S----------------------", - "C6GHS2_9BETC/324-530": "RNLP-DCKIEEWLAANTV--PSPLNWERKTFQNCNFNLSSLLRFVQAESLSCSNIDASKVYGMCFGSISIDKFAIPNSRRVDLQLGKSGLLQSFNYKI--STRATSCQL-----------------YYSLAQ-NN---VT------VINHNPSSWNRRY--GFNDVATF-------------GS--GIHDVAY-AEAC-----------F---T----------VG---A----S------Y----------C------------------------PCAK--PSIVYS-------------------C-----V--TG-KPKSAN-------------------CPTGTSHRECN---------------VQA---S------GFKHKCD------------------CTCNPSPL-------------T----------------------", - "C0KYS1_9BETC/324-540": "PNLP-DCKIEEWLTAKSV--PSPLNWERRTFQNCNFNLSSLLRYVQAESLSCNNIDASKVYGMCFGSVSVDKFAIPRSRQIDLQIGNSGFLQTANYKI--DTAATSCQL-----------------YYSLPK-NN---VT------INNYNPSSWNRRY--GFNDAGVF-------------GK--SKHDVAY-AQQC-----------F---T----------VR---P----S------Y----------C------------------------PCAQ--PDIVSA-------------------C-----T--SQTKPMSAY-------------------CPTGTIHRECSLWN----GPHLRSARVGSGTYT-----------CE------------------CTCKPNPF-------------D----------------------", - "SPIKE_CVMA5/324-534": "ANLP-ACNIEEWLTARSV--PSPLNWERKTFQNCNFNLSSLLRYVQAESLFCNNIDASKVYGRCFGSISVDKFAVPRSRQVDLQLGNSGFLQTANYKI--DTAATSCQL-----------------HYTLPK-NN---VT------INNHNPSSWNRRY--GFNDAGVF-------------GK--NQHDVVY-AQQC-----------F---T----------VR---S----S------Y----------C------------------------PCAQ--PDIVSP-------------------C-----T--TQTKPKSAF----------------------------VN---------------VGD---H-----------CEGLGVLEDNCGNADPHKG-CICANNSF-------------I----------------------", - "Q9J3E7_9BETC/324-534": "ANLP-ACNIEEWLTARSV--PSPLNWERKTFQNCNFNLSSLLRYVQAESLFCNNMDASKVYGRCFGSISVDKFAVPRSRQVDLQLGNSGFLQTANYKI--DTAATSCQL-----------------HYTLPK-NN---VT------INNHNPSSWNRRY--GFNDAGVF-------------GK--NQHDVVY-AQQC-----------F---T----------VR---S----S------Y----------C------------------------PCAQ--PDIVSP-------------------C-----T--TQTKPKSAF----------------------------VN---------------VGD---H-----------CEGLGVLEDNCGNADPHKG-CICANNSF-------------I----------------------", - "A0A0A7UZR7_9BETC/327-532": "PNLP-DCEIEQWLNDPQV--PSPISWERKTFSNCNFNMSSLLSKVRATSFSCNNIDASKIYDMCFGSITIDKFAIPNSRKVDLQFGSSGYIQNYNYRL--DQSATSCQL-----------------YYGIPA-NN---VT------VTKKNPSGWNNRY--GFVE---FKPL--------NIGQNYNKYSAIY-STMC-----------F---N----------VP---N----D------Y----------C------------------------PCK------LGCP-----TG-----TVERPQI-----G--TS------TSGQPI---------YDCPGYPWLTS--------------------------------------SA------------------CKQTPATV------------------------------------", - "R9QTH3_CVHSA/321-555": "PNITNRCPFDRVFNASRF--PSVYAWERTKISDCVADYTVLYNSTSFSTFKCYGVSPSKLIDLCFTSVYADTFLIRFSEVRQIAPGETGVIADYNYKLPDEFTGCVIAW-----------------NTANQD-RG---Q-----------YYYRSSRKT--KLKP---FERD-LSSD------------------ENG-----------V---R----------TL---S----T------YDFYP------SVPL----E-YQATRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTSLIKNQCVNFNFNGLKGTGVLTD--------------------------------------SS------------------KKFQSFQQFGRDASDFTDSVRDPQTLQ-----------------", - "A0A0K1Z074_CVHSA/321-555": "PNITNLCPFDKVFNATRF--PSVYAWERTKISDCVADYTVFYNSTSFSTFNCYGVSPSKLIDLCFTSVYADTFLIRFSEVRQVAPGQTGVIADYNYKLPDDFTGCVIAW-----------------NTAKYD-VG---S-----------YFYRSHRSS--KLKP---FERD-LSSE------------------ENG-----------A---R----------TL---S----T------YDFNQ------NVPL----E-YQATRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTSLVKNQCVNFNFNGFKGTGVLTD--------------------------------------SS------------------KTFQSFQQFGRDASDFTDSVRDPKTLQ-----------------", - "A0A166ZL64_9NIDO/316-550": "PNITNVCPFDKVFNATRF--PSVYAWERTKISDCVADYTVFYNSTSFSTFNCYGVSPSKLIDLCFTSVYADTFLIRFSEVRQVAPGQTGVIADYNYKLPDDFIGCVIAW-----------------NTAKQD-VG---S-----------YFYRSHRSS--KLKP---FERD-LSSE------------------ENG-----------V---L----------TL---S----T------YDFNQ------NVPL----E-YQATRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTPLVKNQCVNFNFNGLKGTGVLTD--------------------------------------SS------------------KTFQSFQQFGRDASDFTDSVRDPQTLQ-----------------", - "ATO98157.1/317-568": "PNITNLCPFGEVFNATTF--PSVYAWERKRISNCVADYSVLYNSTSFSTFKCYGVSATKLNDLCFSNVYADSFVVKGDDVRQIAPGQTGVIADYNYKLPDDFLGCVLAW-----------------NTNSKD-SS---TS------GNYNYLYRWVRRS--KLNP---YERD-LSNDIYSPGGQ---SCSAI--GPNC-----------Y---N----------PL---R----P------YGFFT------TAGV----G-HQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTDLIKNQCVNFNFNGLTGTGVLTS--------------------------------------SS------------------KRFQPFQQFGRDVSDFTDSVRDPKTS------------------", - "Q1T6X6_CVHSA/317-569": "PNITNLCPFGEVFNATKF--PSVYAWERKKISNCVADYSVLYNSTFFSTFKCYGVSATKLNDLCFSNVYADSFVVKGDDVRQIAPGQTGVIADYNYKLPDDFMGCVLAW-----------------NTRNID-AT---ST------GNYNYKYRCLRHG--KLRP---FERD-ISNVPFSPDGK---PCTPP--AFNC-----------Y---W----------PL---N----D------YGFYT------TTGI----G-YQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTDLIKNQCVNFNFNGLTGTGVLTP--------------------------------------SS------------------KRFQPFQQFGRDVSDFTDSVRDPKTSE-----------------", - "SPIKE_CVHSA/317-569": "PNITNLCPFGEVFNATKF--PSVYAWERKKISNCVADYSVLYNSTFFSTFKCYGVSATKLNDLCFSNVYADSFVVKGDDVRQIAPGQTGVIADYNYKLPDDFMGCVLAW-----------------NTRNID-AT---ST------GNYNYKYRYLRHG--KLRP---FERD-ISNVPFSPDGK---PCTPP--ALNC-----------Y---W----------PL---N----D------YGFYT------TTGI----G-YQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTDLIKNQCVNFNFNGLTGTGVLTP--------------------------------------SS------------------KRFQPFQQFGRDVSDFTDSVRDPKTSE-----------------", - "AAP13441.1/317-568": "PNITNLCPFGEVFNATKF--PSVYAWERKKISNCVADYSVLYNSTFFSTFKCYGVSATKLNDLCFSNVYADSFVVKGDDVRQIAPGQTGVIADYNYKLPDDFMGCVLAW-----------------NTRNID-AT---ST------GNYNYKYRYLRHG--KLRP---FERD-ISNVPFSPDGK---PCTPP--ALNC-----------Y---W----------PL---N----D------YGFYT------TTGI----G-YQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTDLIKNQCVNFNFNGLTGTGVLTP--------------------------------------SS------------------KRFQPFQQFGRDVSDFTDSVRDPKTS------------------", - "QHD43416.1/330-583": "PNITNLCPFGEVFNATRF--ASVYAWNRKRISNCVADYSVLYNSASFSTFKCYGVSPTKLNDLCFTNVYADSFVIRGDEVRQIAPGQTGKIADYNYKLPDDFTGCVIAW-----------------NSNNLD-SK---VG------GNYNYLYRLFRKS--NLKP---FERD-ISTEIYQAGST---PCNGV-EGFNC-----------Y---F----------PL---Q----S------YGFQP------TNGV----G-YQPYRV------VVLSFE------LLHA-----PA-----TV----C-----G--PK------KSTNLVKNKCVNFNFNGLTGTGVLTE--------------------------------------SN------------------KKFLPFQQFGRDIADTTDAVRDPQTLE-----------------", - "QHR63300.2/330-583": "PNITNLCPFGEVFNATTF--ASVYAWNRKRISNCVADYSVLYNSTSFSTFKCYGVSPTKLNDLCFTNVYADSFVITGDEVRQIAPGQTGKIADYNYKLPDDFTGCVIAW-----------------NSKHID-AK---EG------GNFNYLYRLFRKA--NLKP---FERD-ISTEIYQAGSK---PCNGQ-TGLNC-----------Y---Y----------PL---Y----R------YGFYP------TDGV----G-HQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------KSTNLVKNKCVNFNFNGLTGTGVLTE--------------------------------------SN------------------KKFLPFQQFGRDIADTTDAVRDPQTLE-----------------", - "A0A088DJY6_9BETC/351-629": "ADVSAECPFQSLINVTEATIPSPAFWRRHYVRNCNYDISVFTDNADVYSLQCYGVAPSSLADMCWEEAHIDYMKISEKDIFSFKPSGAGDFAKYNYKLPSDFMGCTVVFTNQELTCNATSGQLCHVYTNNLT-NYPAEAT------AWDKSHYESIERY--QMWS---SENV-YNCELQEVGPQ---QFNRM--PRNC-----------YVRSN----------NS---N----D------Y----------WQPM----S-FRG--VNSLMVAMAITLK------PTRT-----SA-----TV----C-----GY-KQ------KTTPLVLNECITYHIYGYKGTGVIVS--------------------------------------AN------------------YTFQSFQTVQLTSTGSLHSFKYNNTIY-----------------", - "A0A1B3Q5W5_9NIDO/364-584": "---SCAIPYTTIL---EP--PSPAAWVRATISNCTFDFESLLRTLPTYNLKCYGISPARLSTMCYAGVTLDIFKLNTTHLSNMLGSVPDAVSIYNYALPSNFYGCVHAY-----------------HLNSTT-PY---AV------AVPPGAY-----------------------PIKPGGRQ---LFNSF--VSQV-----------L---D----------SP---T----S------Q----------CTPA----N-CMG--V------VVIGLT------PASG-----SN-----LV----C-----P--KA------NDTQVIEGQCVKYNFYGYAGTGVINQ--------------------------------------SD------------------LAIPNNKLFVTSKSGAVLAVRAGD--------------------", - "A3EXH4_BCHK9/360-565": "----------DIV---RP--PQPVVWRRYTVTSCSFDFEAIVNRLPTFELKCFGISPARLAQMCYSSVTLDLFRANTTHLANMLGGVPDLFSKYNYALPSNFYGCVHAY-----------------YINDTNKDY---AI------AQRWPAT-----------------------PITPGGRQ---PYSDY--VRTV-----------L---N----------TP---N----P------S----------CTTL----T-CFG--V------VVISLK------PASG-----RK-----LV----C-----P--SV------NDTDMRTNECVKYNLYGYTGTGVFNV--------------------------------------ST------------------LTIPDSKLFVANGAG-----------------------------", - "SPIKE_BCHK9/363-567": "----------VLQ---DP--PQPVVWRRYMLYDCVFDFTVVVDSLPTHQLQCYGVSPRRLASMCYGSVTLDVMRINETHLNNLFNRVPDTFSLYNYALPDNFYGCLHAF-----------------YLNSTA-PY---AV------ANR---F-----------------------PIKPGGRQ---SNSAF--IDTV-----------I---N----------A---------A------H----------YSPF----SYVYG--L------AVITLK------PAAG-----SK-----LV----C-----P--VA------NDTVVITDRCVQYNLYGYTGTGVLSK--------------------------------------NT-----------------SLVIPDGKVFTASSTGTII--------------------------", - "U5LNM4_9BETC/366-630": "AELT-ECDLDVLFKN-DA--PIIANYSRRVFTNCNYNLTKLLSLVQVDEFVCHKTTPEALATGCYSSLTVDWFALPFSMKSTLAIGSAEAISMFNYNQ--DYSNPTCRI-----------------HAAVTA-NV---ST---ALNFTANANYAYISRC--QGVD---GK------PILLQPGQ-MT---NI--ACRSGVLARPSDADYF---G----------YSFQGR----N------Y----YLGRKSYKPKTDEGD-VQM--V------YVITPK------YDKG-----PD-----TV----CPLKDMG-AAS------SSLDGLLGQCIDYDIHGVVGRGVFQV--------------------------------------CN------------------TTGIASQIFVYDGFGNIIGFHS----------------------", - "A0A023Y9K3_9BETC/354-613": "QG-T-ECDFSPLLKD-EP--PQVYNFSRLVFTNCNYNLTKLLALFQVSQFSCHQVSPSALASGCYSSLTVDYFAYPTYMSSYLQQGSTGEISQFNYKQ--DFSNPTCRI-----------------LATVPA-NL---SASGL---LPKPSNYVWLSECYQNSFT---GKNF-----QYVKAGQ-YTPCLGL--AANG-----------F---E----------KSYQTHRDPVS------K----LAVTGVVTPMT--SA-LQM--A------FIISVQ------YGTD-----SN-----SV----CPMQAVR--ND------TSVDDKLGLCIDYSLYGITGRGVFQN--------------------------------------CT------------------AVGIRQQRFVYDSFDNLVGYHA----------------------", - "A3EXD9_BCHK5/387-650": "ATTQ-ECDFTPMLTG-TP--PPIYDFKRLVFTNCNYNLTKLLSLFQVSEFSCHQVSPSSLATGCYSSLTVDYFAYSTDMSSYLQPGSAGEIVQFNYKQ--DFSNPTCRV-----------------LATVPT-NL---TT------ITKSSNYVHLTECYKSTAY---GKNY-----LYNAPGG-YTPCLSL--ASRG-----------F---T----------TNRQSH----SLELPDGY----LVTTGSVYPVN--GN-LQM--A------FIISVQ------YGTD-----TN-----SV----CPMQALR--ND------TSIEDKLDVCVEYSLHGITGRGVFHN--------------------------------------CT------------------SVGLRNQRFVYDTFDNLVGYHS-----DNGN-------------", - "SPIKE_CVEMC/380-534": "G--V-ECDFSPLLSG-TP--PQVYNFKRLVFTNCNYNLTKLLSLFSVNDFTCSQISPAAIASNCYSSLILDYFSYPLSMKSDLSVSSAGPISQFNYKQ--SFSNPTCLI-----------------LATVPH-NL---TT------ITKPLKYSYINKC--SRFL---SDDR-TEVPQLVNANQ-YSPCVSI--VP--------------------------------------------------------------------------------------------------------------------------S------TV------------------------------------------------------------------------------------------------------------------------------", - "SPIKE_BC133/383-645": "PNVT-ECDFSPMLTG-VA--PQVYNFKRLVFSNCNYNLTKLLSLFAVDEFSCNGISPDAIARGCYSTLTVDYFAYPLSMKSYIRPGSAGNIPLYNYKQ--SFANPTCRV-----------------MASVPD-NV----T------ITKPGAYGYISKC--SRLT---GVNQDIETPLYINPGE-YSICRDF--APLG-----------F---SEDGQVFKRTLTQFEGG----G------L----LIGVGTRVPMT--AN-LEM--G------FVISVQ------YGTG-----TD-----SV----CPMLDLG--DS------LTITNRLGKCVDYSLYGVTGRGVFQN--------------------------------------CT------------------AVGVKQQRFVYDSFDNLVG-------------------------", - "A0A0K2RVL1_9BETC/326-539": "PNLP-DCNMEDWLSAPTV--ASPLNWERRTFSNCNFNMSSLLSLIQADSFSCSNIDAAKLYGMCFGSVTIDKFAIPNNRKVDLQLGNLGYLQSFNYRI--DTTATSCQL-----------------FYSLGA-DN---VT------VTRSNPSAWNRRY--GFND-TMFKPQ--------PAGF-FTNHDVVY-SKQC-----------F---K----------VP---N----T------Y----------C------------------------PCKNNGATCVGNGVSAGVSG-----TT----T-----G--SG------T-------------------CPVGTSYRTCF---------------NPI---Q-----------CA------------------CTCDPEPI-------------N----------------------", - "Q4VID5_CVHOC/339-550": "PDLP-NCNIEAWLNDKSV--PSPLNWERKTFSNCNFNMSSLMSFIQADSFTCNNIDAAKIYGMCFSSITIDKFAIPNRRKVDLQLGNLGYLQSSNYRI--DTTATSCQL-----------------YYNLPA-AN---VS------VSRFNPSTWNKRF--GFIEDSVFVPQ--------PTGV-FTNHSVVY-AQHC-----------F---K----------AP---K----N------F----------C------------------------PCKLNGSC----------PG-----KN----N-----G--IG------T-------------------CPAGTNYLTCD---------------N--------------------------------------LCTLDPI-------------T---------FKAPGTYKCPQTK", - "H9AA65_9BETC/326-540": "PNLP-DCNIEAWLNDKSV--PSPLNWERKTFSNCNFNMSSLMSFIQADSFTCNNIDAAKIYGMCFSSITIDKFAIPNGRKVDLQLGNLGYLQSFNYKI--DTSATSCQL-----------------YYNLPA-AN---VS------VSRLNPSTWNRRF--GFTEQSVFKPQ--------PAGF-FTAHDVVY-AQHC-----------F---K----------AP---T----T------F----------C------------------------PCKLNGSLCVGSG-----SGVDAGFKH----T-----G--IG------T-------------------CPAGTNYLTCY---------------NSV---Q-----------CN------------------CQCTPDPI-------------L----------------------", - "B7U2P4_9BETC/326-540": "PNLP-DCNIEAWLNDKSV--PSPLNWERKTFSNCNFNMSSLMSFIQADSFTCNNIDAAKIYGMCFSSITIDKFAIPNGRKVDLQLGNLGYLQSFNYRI--DTTATSCQL-----------------YYNLPA-AN---VS------VSRFNPSTWNRRF--GFTEQSVFKPQ--------PAGV-FTDHDVVY-AQHC-----------F---K----------AP---T----N------F----------C------------------------PCKLDGSLCVGSG-----SGIDAGYKN----T-----G--IG------T-------------------CPAGTNYLTCH---------------NAA---Q-----------CD------------------CLCTPDPI-------------T----------------------", - "Q06BD7_9BETC/326-540": "PNLP-DCNIEAWLNDKSV--PSPLNWERKTFSNCNFNMSSLMSFIQADSFTCNNIDAAKIYGMCFSSITIDKFAIPNGRKVDLQLGNLGYLQSFNYRI--DTTATSCQL-----------------YYNLPA-AN---VS------VSRFNPSTWNRRF--GFTEQSVFKPQ--------PAGV-FTDHDVVY-AQHC-----------F---K----------AP---T----N------F----------C------------------------PCKLDGSLCVGNG-----PGIDAGYKT----S-----G--IG------T-------------------CPAGTNYLTCH---------------NAA---Q-----------CN------------------CLCTPDPI-------------T----------------------" + datasets: [ + { + name: 'Spike_rec_bind', + description: 'Spike receptor binding domain', + branches: [ + ['node3', 'SPIKE_CVP67/326-526', 0.0044021], + ['node3', 'Q2QKN3_9BETC/326-526', 0.000545546], + ['node13', 'node3', 0.184142], + ['node12', 'SPIKE_CVHN5/322-526', 0.284527], + ['node11', 'C6GHS2_9BETC/324-530', 0.169522], + ['node10', 'C0KYS1_9BETC/324-540', 0.100405], + ['node9', 'SPIKE_CVMA5/324-534', 1e-9], + ['node9', 'Q9J3E7_9BETC/324-534', 0.00934477], + ['node10', 'node9', 0.176232], + ['node11', 'node10', 0.0888497], + ['node12', 'node11', 0.114123], + ['node13', 'node12', 0.0925582], + ['node61', 'node13', 0.00672813], + ['node50', 'A0A0A7UZR7_9BETC/327-532', 0.286551], + ['node19', 'R9QTH3_CVHSA/321-555', 0.0615283], + ['node18', 'A0A0K1Z074_CVHSA/321-555', 0.000774412], + ['node18', 'A0A166ZL64_9NIDO/316-550', 0.0340301], + ['node19', 'node18', 0.0321586], + ['node31', 'node19', 0.0980282], + ['node30', 'ATO98157.1/317-568', 0.0214425], + ['node25', 'Q1T6X6_CVHSA/317-569', 0.00652008], + ['node24', 'SPIKE_CVHSA/317-569', 0.0019582], + ['node24', 'AAP13441.1/317-568', 0.00217293], + ['node25', 'node24', 0.00174427], + ['node29', 'node25', 0.14259], + ['node28', 'QHD43416.1/330-583', 0.0415892], + ['node28', 'QHR63300.2/330-583', 0.0492027], + ['node29', 'node28', 0.115808], + ['node30', 'node29', 0.0573048], + ['node31', 'node30', 0.0944591], + ['node39', 'node31', 0.698168], + ['node38', 'A0A088DJY6_9BETC/351-629', 0.790301], + ['node37', 'A0A1B3Q5W5_9NIDO/364-584', 0.270725], + ['node36', 'A3EXH4_BCHK9/360-565', 0.301569], + ['node36', 'SPIKE_BCHK9/363-567', 0.367537], + ['node37', 'node36', 0.079718], + ['node38', 'node37', 0.555752], + ['node39', 'node38', 0.177297], + ['node49', 'node39', 0.761404], + ['node48', 'U5LNM4_9BETC/366-630', 0.476639], + ['node43', 'A0A023Y9K3_9BETC/354-613', 0.201065], + ['node43', 'A3EXD9_BCHK5/387-650', 0.180317], + ['node47', 'node43', 0.151873], + ['node46', 'SPIKE_CVEMC/380-534', 0.256477], + ['node46', 'SPIKE_BC133/383-645', 0.29522], + ['node47', 'node46', 0.0192677], + ['node48', 'node47', 0.164582], + ['node49', 'node48', 0.462409], + ['node50', 'node49', 0.306269], + ['node60', 'node50', 0.131154], + ['node59', 'A0A0K2RVL1_9BETC/326-539', 0.192394], + ['node58', 'Q4VID5_CVHOC/339-550', 0.0579613], + ['node57', 'H9AA65_9BETC/326-540', 0.0419032], + ['node56', 'B7U2P4_9BETC/326-540', 0.0123283], + ['node56', 'Q06BD7_9BETC/326-540', 0.0113589], + ['node57', 'node56', 0.0297464], + ['node58', 'node57', 0.0150836], + ['node59', 'node58', 0.135815], + ['node60', 'node59', 0.0263022], + ['node61', 'node60', 0.00672813], + ], + rowData: { + 'SPIKE_CVP67/326-526': + 'PDLP-NCDIEAWLNSKTV--SSPLNWERKIFSNCNFNMGRLMSFIQADSFGCNNIDASRLYGMCFGSITIDKFAIPNSRKVDLQVGKSGYLQSFNYKI--DTAVSSCQL-----------------YYSLPA-AN---VS------VTHYNPSSWNRRY--GFNNQS-F-------------GS-RGLHDAVY-SQQC-----------F---N----------TP---N----T------Y----------C------------------------PCRT--SQCIGG-------------------A-----G--TG------T-------------------CPVGTTVRKCF---AAVT--------KAT---K-----------CT------------------CWCQPDPS-------------T----------------------', + 'Q2QKN3_9BETC/326-526': + 'PDLP-NCDIEAWLNSKTV--SSPLNWERKIFSNCNFNMGRLMSFIQADSFGCNNIDASRLYGMCFGSITIDKFAIPNSRKVDLQVGKSGYLQSFNYKI--DTAVSSCQL-----------------YYSLPA-AN---VS------VTHYNPSSWNRRY--GFNNQS-F-------------GS-RGLHDAVY-SQQC-----------F---N----------TP---N----T------Y----------C------------------------PCRT--SQCIGG-------------------A-----G--TG------T-------------------CPVGTTVRKCF---AAVT--------NAT---K-----------CT------------------CWCQPDPS-------------T----------------------', + 'SPIKE_CVHN5/322-526': + 'PNLP-DCDIDNWLNNVSV--PSPLNWERRIFSNCNFNLSTLLRLVHVDSFSCNNLDKSKIFGSCFNSITVDKFAIPNRRRDDLQLGSSGFLQSSNYKI--DISSSSCQL-----------------YYSLPL-VN---VT------INNFNPSSWNRRY--GF---GSF-------------NL--SSYDVVY-SDHC-----------F---S----------VN---S----D------F----------C------------------------PCAD--PSVVNS-------------------C-----A--KS-KPPSAI-------------------CPAGTKYRHCD---------------LDT---TLYVKNW-----CR------------------CSCLPDPI-------------S----------------------', + 'C6GHS2_9BETC/324-530': + 'RNLP-DCKIEEWLAANTV--PSPLNWERKTFQNCNFNLSSLLRFVQAESLSCSNIDASKVYGMCFGSISIDKFAIPNSRRVDLQLGKSGLLQSFNYKI--STRATSCQL-----------------YYSLAQ-NN---VT------VINHNPSSWNRRY--GFNDVATF-------------GS--GIHDVAY-AEAC-----------F---T----------VG---A----S------Y----------C------------------------PCAK--PSIVYS-------------------C-----V--TG-KPKSAN-------------------CPTGTSHRECN---------------VQA---S------GFKHKCD------------------CTCNPSPL-------------T----------------------', + 'C0KYS1_9BETC/324-540': + 'PNLP-DCKIEEWLTAKSV--PSPLNWERRTFQNCNFNLSSLLRYVQAESLSCNNIDASKVYGMCFGSVSVDKFAIPRSRQIDLQIGNSGFLQTANYKI--DTAATSCQL-----------------YYSLPK-NN---VT------INNYNPSSWNRRY--GFNDAGVF-------------GK--SKHDVAY-AQQC-----------F---T----------VR---P----S------Y----------C------------------------PCAQ--PDIVSA-------------------C-----T--SQTKPMSAY-------------------CPTGTIHRECSLWN----GPHLRSARVGSGTYT-----------CE------------------CTCKPNPF-------------D----------------------', + 'SPIKE_CVMA5/324-534': + 'ANLP-ACNIEEWLTARSV--PSPLNWERKTFQNCNFNLSSLLRYVQAESLFCNNIDASKVYGRCFGSISVDKFAVPRSRQVDLQLGNSGFLQTANYKI--DTAATSCQL-----------------HYTLPK-NN---VT------INNHNPSSWNRRY--GFNDAGVF-------------GK--NQHDVVY-AQQC-----------F---T----------VR---S----S------Y----------C------------------------PCAQ--PDIVSP-------------------C-----T--TQTKPKSAF----------------------------VN---------------VGD---H-----------CEGLGVLEDNCGNADPHKG-CICANNSF-------------I----------------------', + 'Q9J3E7_9BETC/324-534': + 'ANLP-ACNIEEWLTARSV--PSPLNWERKTFQNCNFNLSSLLRYVQAESLFCNNMDASKVYGRCFGSISVDKFAVPRSRQVDLQLGNSGFLQTANYKI--DTAATSCQL-----------------HYTLPK-NN---VT------INNHNPSSWNRRY--GFNDAGVF-------------GK--NQHDVVY-AQQC-----------F---T----------VR---S----S------Y----------C------------------------PCAQ--PDIVSP-------------------C-----T--TQTKPKSAF----------------------------VN---------------VGD---H-----------CEGLGVLEDNCGNADPHKG-CICANNSF-------------I----------------------', + 'A0A0A7UZR7_9BETC/327-532': + 'PNLP-DCEIEQWLNDPQV--PSPISWERKTFSNCNFNMSSLLSKVRATSFSCNNIDASKIYDMCFGSITIDKFAIPNSRKVDLQFGSSGYIQNYNYRL--DQSATSCQL-----------------YYGIPA-NN---VT------VTKKNPSGWNNRY--GFVE---FKPL--------NIGQNYNKYSAIY-STMC-----------F---N----------VP---N----D------Y----------C------------------------PCK------LGCP-----TG-----TVERPQI-----G--TS------TSGQPI---------YDCPGYPWLTS--------------------------------------SA------------------CKQTPATV------------------------------------', + 'R9QTH3_CVHSA/321-555': + 'PNITNRCPFDRVFNASRF--PSVYAWERTKISDCVADYTVLYNSTSFSTFKCYGVSPSKLIDLCFTSVYADTFLIRFSEVRQIAPGETGVIADYNYKLPDEFTGCVIAW-----------------NTANQD-RG---Q-----------YYYRSSRKT--KLKP---FERD-LSSD------------------ENG-----------V---R----------TL---S----T------YDFYP------SVPL----E-YQATRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTSLIKNQCVNFNFNGLKGTGVLTD--------------------------------------SS------------------KKFQSFQQFGRDASDFTDSVRDPQTLQ-----------------', + 'A0A0K1Z074_CVHSA/321-555': + 'PNITNLCPFDKVFNATRF--PSVYAWERTKISDCVADYTVFYNSTSFSTFNCYGVSPSKLIDLCFTSVYADTFLIRFSEVRQVAPGQTGVIADYNYKLPDDFTGCVIAW-----------------NTAKYD-VG---S-----------YFYRSHRSS--KLKP---FERD-LSSE------------------ENG-----------A---R----------TL---S----T------YDFNQ------NVPL----E-YQATRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTSLVKNQCVNFNFNGFKGTGVLTD--------------------------------------SS------------------KTFQSFQQFGRDASDFTDSVRDPKTLQ-----------------', + 'A0A166ZL64_9NIDO/316-550': + 'PNITNVCPFDKVFNATRF--PSVYAWERTKISDCVADYTVFYNSTSFSTFNCYGVSPSKLIDLCFTSVYADTFLIRFSEVRQVAPGQTGVIADYNYKLPDDFIGCVIAW-----------------NTAKQD-VG---S-----------YFYRSHRSS--KLKP---FERD-LSSE------------------ENG-----------V---L----------TL---S----T------YDFNQ------NVPL----E-YQATRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTPLVKNQCVNFNFNGLKGTGVLTD--------------------------------------SS------------------KTFQSFQQFGRDASDFTDSVRDPQTLQ-----------------', + 'ATO98157.1/317-568': + 'PNITNLCPFGEVFNATTF--PSVYAWERKRISNCVADYSVLYNSTSFSTFKCYGVSATKLNDLCFSNVYADSFVVKGDDVRQIAPGQTGVIADYNYKLPDDFLGCVLAW-----------------NTNSKD-SS---TS------GNYNYLYRWVRRS--KLNP---YERD-LSNDIYSPGGQ---SCSAI--GPNC-----------Y---N----------PL---R----P------YGFFT------TAGV----G-HQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTDLIKNQCVNFNFNGLTGTGVLTS--------------------------------------SS------------------KRFQPFQQFGRDVSDFTDSVRDPKTS------------------', + 'Q1T6X6_CVHSA/317-569': + 'PNITNLCPFGEVFNATKF--PSVYAWERKKISNCVADYSVLYNSTFFSTFKCYGVSATKLNDLCFSNVYADSFVVKGDDVRQIAPGQTGVIADYNYKLPDDFMGCVLAW-----------------NTRNID-AT---ST------GNYNYKYRCLRHG--KLRP---FERD-ISNVPFSPDGK---PCTPP--AFNC-----------Y---W----------PL---N----D------YGFYT------TTGI----G-YQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTDLIKNQCVNFNFNGLTGTGVLTP--------------------------------------SS------------------KRFQPFQQFGRDVSDFTDSVRDPKTSE-----------------', + 'SPIKE_CVHSA/317-569': + 'PNITNLCPFGEVFNATKF--PSVYAWERKKISNCVADYSVLYNSTFFSTFKCYGVSATKLNDLCFSNVYADSFVVKGDDVRQIAPGQTGVIADYNYKLPDDFMGCVLAW-----------------NTRNID-AT---ST------GNYNYKYRYLRHG--KLRP---FERD-ISNVPFSPDGK---PCTPP--ALNC-----------Y---W----------PL---N----D------YGFYT------TTGI----G-YQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTDLIKNQCVNFNFNGLTGTGVLTP--------------------------------------SS------------------KRFQPFQQFGRDVSDFTDSVRDPKTSE-----------------', + 'AAP13441.1/317-568': + 'PNITNLCPFGEVFNATKF--PSVYAWERKKISNCVADYSVLYNSTFFSTFKCYGVSATKLNDLCFSNVYADSFVVKGDDVRQIAPGQTGVIADYNYKLPDDFMGCVLAW-----------------NTRNID-AT---ST------GNYNYKYRYLRHG--KLRP---FERD-ISNVPFSPDGK---PCTPP--ALNC-----------Y---W----------PL---N----D------YGFYT------TTGI----G-YQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------LSTDLIKNQCVNFNFNGLTGTGVLTP--------------------------------------SS------------------KRFQPFQQFGRDVSDFTDSVRDPKTS------------------', + 'QHD43416.1/330-583': + 'PNITNLCPFGEVFNATRF--ASVYAWNRKRISNCVADYSVLYNSASFSTFKCYGVSPTKLNDLCFTNVYADSFVIRGDEVRQIAPGQTGKIADYNYKLPDDFTGCVIAW-----------------NSNNLD-SK---VG------GNYNYLYRLFRKS--NLKP---FERD-ISTEIYQAGST---PCNGV-EGFNC-----------Y---F----------PL---Q----S------YGFQP------TNGV----G-YQPYRV------VVLSFE------LLHA-----PA-----TV----C-----G--PK------KSTNLVKNKCVNFNFNGLTGTGVLTE--------------------------------------SN------------------KKFLPFQQFGRDIADTTDAVRDPQTLE-----------------', + 'QHR63300.2/330-583': + 'PNITNLCPFGEVFNATTF--ASVYAWNRKRISNCVADYSVLYNSTSFSTFKCYGVSPTKLNDLCFTNVYADSFVITGDEVRQIAPGQTGKIADYNYKLPDDFTGCVIAW-----------------NSKHID-AK---EG------GNFNYLYRLFRKA--NLKP---FERD-ISTEIYQAGSK---PCNGQ-TGLNC-----------Y---Y----------PL---Y----R------YGFYP------TDGV----G-HQPYRV------VVLSFE------LLNA-----PA-----TV----C-----G--PK------KSTNLVKNKCVNFNFNGLTGTGVLTE--------------------------------------SN------------------KKFLPFQQFGRDIADTTDAVRDPQTLE-----------------', + 'A0A088DJY6_9BETC/351-629': + 'ADVSAECPFQSLINVTEATIPSPAFWRRHYVRNCNYDISVFTDNADVYSLQCYGVAPSSLADMCWEEAHIDYMKISEKDIFSFKPSGAGDFAKYNYKLPSDFMGCTVVFTNQELTCNATSGQLCHVYTNNLT-NYPAEAT------AWDKSHYESIERY--QMWS---SENV-YNCELQEVGPQ---QFNRM--PRNC-----------YVRSN----------NS---N----D------Y----------WQPM----S-FRG--VNSLMVAMAITLK------PTRT-----SA-----TV----C-----GY-KQ------KTTPLVLNECITYHIYGYKGTGVIVS--------------------------------------AN------------------YTFQSFQTVQLTSTGSLHSFKYNNTIY-----------------', + 'A0A1B3Q5W5_9NIDO/364-584': + '---SCAIPYTTIL---EP--PSPAAWVRATISNCTFDFESLLRTLPTYNLKCYGISPARLSTMCYAGVTLDIFKLNTTHLSNMLGSVPDAVSIYNYALPSNFYGCVHAY-----------------HLNSTT-PY---AV------AVPPGAY-----------------------PIKPGGRQ---LFNSF--VSQV-----------L---D----------SP---T----S------Q----------CTPA----N-CMG--V------VVIGLT------PASG-----SN-----LV----C-----P--KA------NDTQVIEGQCVKYNFYGYAGTGVINQ--------------------------------------SD------------------LAIPNNKLFVTSKSGAVLAVRAGD--------------------', + 'A3EXH4_BCHK9/360-565': + '----------DIV---RP--PQPVVWRRYTVTSCSFDFEAIVNRLPTFELKCFGISPARLAQMCYSSVTLDLFRANTTHLANMLGGVPDLFSKYNYALPSNFYGCVHAY-----------------YINDTNKDY---AI------AQRWPAT-----------------------PITPGGRQ---PYSDY--VRTV-----------L---N----------TP---N----P------S----------CTTL----T-CFG--V------VVISLK------PASG-----RK-----LV----C-----P--SV------NDTDMRTNECVKYNLYGYTGTGVFNV--------------------------------------ST------------------LTIPDSKLFVANGAG-----------------------------', + 'SPIKE_BCHK9/363-567': + '----------VLQ---DP--PQPVVWRRYMLYDCVFDFTVVVDSLPTHQLQCYGVSPRRLASMCYGSVTLDVMRINETHLNNLFNRVPDTFSLYNYALPDNFYGCLHAF-----------------YLNSTA-PY---AV------ANR---F-----------------------PIKPGGRQ---SNSAF--IDTV-----------I---N----------A---------A------H----------YSPF----SYVYG--L------AVITLK------PAAG-----SK-----LV----C-----P--VA------NDTVVITDRCVQYNLYGYTGTGVLSK--------------------------------------NT-----------------SLVIPDGKVFTASSTGTII--------------------------', + 'U5LNM4_9BETC/366-630': + 'AELT-ECDLDVLFKN-DA--PIIANYSRRVFTNCNYNLTKLLSLVQVDEFVCHKTTPEALATGCYSSLTVDWFALPFSMKSTLAIGSAEAISMFNYNQ--DYSNPTCRI-----------------HAAVTA-NV---ST---ALNFTANANYAYISRC--QGVD---GK------PILLQPGQ-MT---NI--ACRSGVLARPSDADYF---G----------YSFQGR----N------Y----YLGRKSYKPKTDEGD-VQM--V------YVITPK------YDKG-----PD-----TV----CPLKDMG-AAS------SSLDGLLGQCIDYDIHGVVGRGVFQV--------------------------------------CN------------------TTGIASQIFVYDGFGNIIGFHS----------------------', + 'A0A023Y9K3_9BETC/354-613': + 'QG-T-ECDFSPLLKD-EP--PQVYNFSRLVFTNCNYNLTKLLALFQVSQFSCHQVSPSALASGCYSSLTVDYFAYPTYMSSYLQQGSTGEISQFNYKQ--DFSNPTCRI-----------------LATVPA-NL---SASGL---LPKPSNYVWLSECYQNSFT---GKNF-----QYVKAGQ-YTPCLGL--AANG-----------F---E----------KSYQTHRDPVS------K----LAVTGVVTPMT--SA-LQM--A------FIISVQ------YGTD-----SN-----SV----CPMQAVR--ND------TSVDDKLGLCIDYSLYGITGRGVFQN--------------------------------------CT------------------AVGIRQQRFVYDSFDNLVGYHA----------------------', + 'A3EXD9_BCHK5/387-650': + 'ATTQ-ECDFTPMLTG-TP--PPIYDFKRLVFTNCNYNLTKLLSLFQVSEFSCHQVSPSSLATGCYSSLTVDYFAYSTDMSSYLQPGSAGEIVQFNYKQ--DFSNPTCRV-----------------LATVPT-NL---TT------ITKSSNYVHLTECYKSTAY---GKNY-----LYNAPGG-YTPCLSL--ASRG-----------F---T----------TNRQSH----SLELPDGY----LVTTGSVYPVN--GN-LQM--A------FIISVQ------YGTD-----TN-----SV----CPMQALR--ND------TSIEDKLDVCVEYSLHGITGRGVFHN--------------------------------------CT------------------SVGLRNQRFVYDTFDNLVGYHS-----DNGN-------------', + 'SPIKE_CVEMC/380-534': + 'G--V-ECDFSPLLSG-TP--PQVYNFKRLVFTNCNYNLTKLLSLFSVNDFTCSQISPAAIASNCYSSLILDYFSYPLSMKSDLSVSSAGPISQFNYKQ--SFSNPTCLI-----------------LATVPH-NL---TT------ITKPLKYSYINKC--SRFL---SDDR-TEVPQLVNANQ-YSPCVSI--VP--------------------------------------------------------------------------------------------------------------------------S------TV------------------------------------------------------------------------------------------------------------------------------', + 'SPIKE_BC133/383-645': + 'PNVT-ECDFSPMLTG-VA--PQVYNFKRLVFSNCNYNLTKLLSLFAVDEFSCNGISPDAIARGCYSTLTVDYFAYPLSMKSYIRPGSAGNIPLYNYKQ--SFANPTCRV-----------------MASVPD-NV----T------ITKPGAYGYISKC--SRLT---GVNQDIETPLYINPGE-YSICRDF--APLG-----------F---SEDGQVFKRTLTQFEGG----G------L----LIGVGTRVPMT--AN-LEM--G------FVISVQ------YGTG-----TD-----SV----CPMLDLG--DS------LTITNRLGKCVDYSLYGVTGRGVFQN--------------------------------------CT------------------AVGVKQQRFVYDSFDNLVG-------------------------', + 'A0A0K2RVL1_9BETC/326-539': + 'PNLP-DCNMEDWLSAPTV--ASPLNWERRTFSNCNFNMSSLLSLIQADSFSCSNIDAAKLYGMCFGSVTIDKFAIPNNRKVDLQLGNLGYLQSFNYRI--DTTATSCQL-----------------FYSLGA-DN---VT------VTRSNPSAWNRRY--GFND-TMFKPQ--------PAGF-FTNHDVVY-SKQC-----------F---K----------VP---N----T------Y----------C------------------------PCKNNGATCVGNGVSAGVSG-----TT----T-----G--SG------T-------------------CPVGTSYRTCF---------------NPI---Q-----------CA------------------CTCDPEPI-------------N----------------------', + 'Q4VID5_CVHOC/339-550': + 'PDLP-NCNIEAWLNDKSV--PSPLNWERKTFSNCNFNMSSLMSFIQADSFTCNNIDAAKIYGMCFSSITIDKFAIPNRRKVDLQLGNLGYLQSSNYRI--DTTATSCQL-----------------YYNLPA-AN---VS------VSRFNPSTWNKRF--GFIEDSVFVPQ--------PTGV-FTNHSVVY-AQHC-----------F---K----------AP---K----N------F----------C------------------------PCKLNGSC----------PG-----KN----N-----G--IG------T-------------------CPAGTNYLTCD---------------N--------------------------------------LCTLDPI-------------T---------FKAPGTYKCPQTK', + 'H9AA65_9BETC/326-540': + 'PNLP-DCNIEAWLNDKSV--PSPLNWERKTFSNCNFNMSSLMSFIQADSFTCNNIDAAKIYGMCFSSITIDKFAIPNGRKVDLQLGNLGYLQSFNYKI--DTSATSCQL-----------------YYNLPA-AN---VS------VSRLNPSTWNRRF--GFTEQSVFKPQ--------PAGF-FTAHDVVY-AQHC-----------F---K----------AP---T----T------F----------C------------------------PCKLNGSLCVGSG-----SGVDAGFKH----T-----G--IG------T-------------------CPAGTNYLTCY---------------NSV---Q-----------CN------------------CQCTPDPI-------------L----------------------', + 'B7U2P4_9BETC/326-540': + 'PNLP-DCNIEAWLNDKSV--PSPLNWERKTFSNCNFNMSSLMSFIQADSFTCNNIDAAKIYGMCFSSITIDKFAIPNGRKVDLQLGNLGYLQSFNYRI--DTTATSCQL-----------------YYNLPA-AN---VS------VSRFNPSTWNRRF--GFTEQSVFKPQ--------PAGV-FTDHDVVY-AQHC-----------F---K----------AP---T----N------F----------C------------------------PCKLDGSLCVGSG-----SGIDAGYKN----T-----G--IG------T-------------------CPAGTNYLTCH---------------NAA---Q-----------CD------------------CLCTPDPI-------------T----------------------', + 'Q06BD7_9BETC/326-540': + 'PNLP-DCNIEAWLNDKSV--PSPLNWERKTFSNCNFNMSSLMSFIQADSFTCNNIDAAKIYGMCFSSITIDKFAIPNGRKVDLQLGNLGYLQSFNYRI--DTTATSCQL-----------------YYNLPA-AN---VS------VSRFNPSTWNRRF--GFTEQSVFKPQ--------PAGV-FTDHDVVY-AQHC-----------F---K----------AP---T----N------F----------C------------------------PCKLDGSLCVGNG-----PGIDAGYKT----S-----G--IG------T-------------------CPAGTNYLTCH---------------NAA---Q-----------CN------------------CLCTPDPI-------------T----------------------', + }, + structure: { + 'SPIKE_CVHSA/317-569': [ + { + pdb: '5wrg', + chains: [ + { + startPos: 317, + chain: 'A', + }, + { + startPos: 317, + chain: 'B', + }, + { + startPos: 317, + chain: 'C', + }, + ], + }, + { + pdb: '5xlr', + chains: [ + { + startPos: 317, + chain: 'A', + }, + ], + }, + ], + }, }, - structure: { - 'SPIKE_CVHSA/317-569': [{ - pdb: '5wrg', - chains: [{ - startPos: 317, - chain: 'A' - }, { - startPos: 317, - chain: 'B' - }, { - startPos: 317, - chain: 'C' - }] - }, { - pdb: '5xlr', - chains: [{ - startPos: 317, - chain: 'A' - }] - }] - } - }, - { - name: 'Corona_S2', - url: { stockholm: '%PUBLIC_URL%/PF01601_full.txt' } - }], -// dataurl: '%PUBLIC_URL%/PF01601_full.txt', + { + name: 'Corona_S2', + url: { stockholm: '%PUBLIC_URL%/PF01601_full.txt' }, + }, + ], + // dataurl: '%PUBLIC_URL%/PF01601_full.txt', config: { containerHeight: '1000px', handler: { click: (coords) => { - console.warn ('Click ' + coords.node + ' column ' + coords.column + (coords.isGap ? '' : (', position ' + coords.seqPos)) + ' (' + coords.c + ')') - } - } - } + console.warn( + 'Click ' + + coords.node + + ' column ' + + coords.column + + (coords.isGap ? '' : ', position ' + coords.seqPos) + + ' (' + + coords.c + + ')' + ) + }, + }, + }, } ReactDOM.render( @@ -144,9 +196,9 @@ ReactDOM.render( , document.getElementById('root') -); +) // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.unregister(); +serviceWorker.unregister() diff --git a/src/reconstruction.js b/src/reconstruction.js index fc7732f..9efedac 100644 --- a/src/reconstruction.js +++ b/src/reconstruction.js @@ -1,31 +1,39 @@ -import PhylogeneticLikelihood from 'phylogenetic-likelihood'; +import PhylogeneticLikelihood from 'phylogenetic-likelihood' -export const getAncestralReconstruction = async (data) => { +export async function getAncestralReconstruction(data) { const { branches, rowData, id } = data let { gapChar } = data gapChar = gapChar || '-' let ancestralRowData = {} - const alphSize = 20 // assume for ancestral reconstruction purposes these are protein sequences; if not we'll need to pass a different model into getNodePostProfiles + const alphSize = 20 // assume for ancestral reconstruction purposes these are protein sequences; if not we'll need to pass a different model into getNodePostProfiles const maxEntropy = Math.log(alphSize) / Math.log(2) - const deletionRate = .001 - const model = PhylogeneticLikelihood.models.makeGappedModel ({ model: PhylogeneticLikelihood.models[PhylogeneticLikelihood.defaultModel], - deletionRate, - gapChar }) - const { nodeProfile } - = PhylogeneticLikelihood.getNodePostProfiles ({ branchList: branches, - nodeSeq: rowData, - postProbThreshold: .01, - model, - defaultGapChar: gapChar }) - Object.keys(nodeProfile).forEach ((node) => { - ancestralRowData[node] = nodeProfile[node].map ((charProb) => { - if (charProb[gapChar] >= .5) - return gapChar - const chars = Object.keys(charProb).filter ((c) => c !== gapChar).sort ((a, b) => charProb[a] - charProb[b]) - const norm = chars.reduce ((psum, c) => psum + charProb[c], 0) - const probs = chars.map ((c) => charProb[c] / norm) - const entropy = probs.reduce ((s, p) => s - p * Math.log(p), 0) / Math.log(2) - return chars.map ((c, n) => [c, probs[n] * (maxEntropy - entropy) / maxEntropy]) + const deletionRate = 0.001 + const model = PhylogeneticLikelihood.models.makeGappedModel({ + model: PhylogeneticLikelihood.models[PhylogeneticLikelihood.defaultModel], + deletionRate, + gapChar, + }) + const { nodeProfile } = PhylogeneticLikelihood.getNodePostProfiles({ + branchList: branches, + nodeSeq: rowData, + postProbThreshold: 0.01, + model, + defaultGapChar: gapChar, + }) + Object.keys(nodeProfile).forEach((node) => { + ancestralRowData[node] = nodeProfile[node].map((charProb) => { + if (charProb[gapChar] >= 0.5) return gapChar + const chars = Object.keys(charProb) + .filter((c) => c !== gapChar) + .sort((a, b) => charProb[a] - charProb[b]) + const norm = chars.reduce((psum, c) => psum + charProb[c], 0) + const probs = chars.map((c) => charProb[c] / norm) + const entropy = + probs.reduce((s, p) => s - p * Math.log(p), 0) / Math.log(2) + return chars.map((c, n) => [ + c, + (probs[n] * (maxEntropy - entropy)) / maxEntropy, + ]) }) }) return { id, ancestralRowData } diff --git a/src/serviceWorker.js b/src/serviceWorker.js index b04b771..9d6cf14 100644 --- a/src/serviceWorker.js +++ b/src/serviceWorker.js @@ -18,25 +18,25 @@ const isLocalhost = Boolean( window.location.hostname.match( /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ ) -); +) export function register(config) { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href) if (publicUrl.origin !== window.location.origin) { // Our service worker won't work if PUBLIC_URL is on a different origin // from what our page is served on. This might happen if a CDN is used to // serve assets; see https://github.com/facebook/create-react-app/issues/2374 - return; + return } window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` if (isLocalhost) { // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config); + checkValidServiceWorker(swUrl, config) // Add some additional logging to localhost, pointing developers to the // service worker/PWA documentation. @@ -44,24 +44,24 @@ export function register(config) { console.log( 'This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://bit.ly/CRA-PWA' - ); - }); + ) + }) } else { // Is not localhost. Just register service worker - registerValidSW(swUrl, config); + registerValidSW(swUrl, config) } - }); + }) } } function registerValidSW(swUrl, config) { navigator.serviceWorker .register(swUrl) - .then(registration => { + .then((registration) => { registration.onupdatefound = () => { - const installingWorker = registration.installing; + const installingWorker = registration.installing if (installingWorker == null) { - return; + return } installingWorker.onstatechange = () => { if (installingWorker.state === 'installed') { @@ -72,30 +72,30 @@ function registerValidSW(swUrl, config) { console.log( 'New content is available and will be used when all ' + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' - ); + ) // Execute callback if (config && config.onUpdate) { - config.onUpdate(registration); + config.onUpdate(registration) } } else { // At this point, everything has been precached. // It's the perfect time to display a // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); + console.log('Content is cached for offline use.') // Execute callback if (config && config.onSuccess) { - config.onSuccess(registration); + config.onSuccess(registration) } } } - }; - }; + } + } + }) + .catch((error) => { + console.error('Error during service worker registration:', error) }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); } function checkValidServiceWorker(swUrl, config) { @@ -103,39 +103,39 @@ function checkValidServiceWorker(swUrl, config) { fetch(swUrl, { headers: { 'Service-Worker': 'script' }, }) - .then(response => { + .then((response) => { // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get('content-type'); + const contentType = response.headers.get('content-type') if ( response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1) ) { // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { + navigator.serviceWorker.ready.then((registration) => { registration.unregister().then(() => { - window.location.reload(); - }); - }); + window.location.reload() + }) + }) } else { // Service worker found. Proceed as normal. - registerValidSW(swUrl, config); + registerValidSW(swUrl, config) } }) .catch(() => { console.log( 'No internet connection found. App is running in offline mode.' - ); - }); + ) + }) } export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready - .then(registration => { - registration.unregister(); + .then((registration) => { + registration.unregister() + }) + .catch((error) => { + console.error(error.message) }) - .catch(error => { - console.error(error.message); - }); } } diff --git a/src/setupTests.js b/src/setupTests.js index 74b1a27..2eb59b0 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom/extend-expect'; +import '@testing-library/jest-dom/extend-expect' diff --git a/yarn.lock b/yarn.lock index 8c0f484..dbc6d03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1968,6 +1968,14 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" +anymatch@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA== + dependencies: + micromatch "^2.1.5" + normalize-path "^2.0.0" + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -2017,12 +2025,19 @@ arity-n@^1.0.4: resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U= +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= + dependencies: + arr-flatten "^1.0.1" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= -arr-flatten@^1.1.0: +arr-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== @@ -2068,6 +2083,11 @@ array-uniq@^1.0.1: resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -2156,7 +2176,7 @@ astw@^2.0.0: dependencies: acorn "^4.0.3" -async-each@^1.0.1: +async-each@^1.0.0, async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== @@ -2216,7 +2236,29 @@ axobject-query@^2.0.2: resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799" integrity sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ== -babel-code-frame@^6.22.0: +babel-cli@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.26.0.tgz#502ab54874d7db88ad00b887a06383ce03d002f1" + integrity sha1-UCq1SHTX24itALiHoGODzgPQAvE= + dependencies: + babel-core "^6.26.0" + babel-polyfill "^6.26.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + commander "^2.11.0" + convert-source-map "^1.5.0" + fs-readdir-recursive "^1.0.0" + glob "^7.1.2" + lodash "^4.17.4" + output-file-sync "^1.1.2" + path-is-absolute "^1.0.1" + slash "^1.0.0" + source-map "^0.5.6" + v8flags "^2.1.1" + optionalDependencies: + chokidar "^1.6.1" + +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= @@ -2225,6 +2267,31 @@ babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.2" +babel-core@^6.26.0: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + babel-eslint@10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -2244,6 +2311,28 @@ babel-extract-comments@^1.0.0: dependencies: babylon "^6.18.0" +babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-jest@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" @@ -2268,6 +2357,13 @@ babel-loader@8.1.0: pify "^4.0.1" schema-utils "^2.6.5" +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= + dependencies: + babel-runtime "^6.22.0" + babel-plugin-dynamic-import-node@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" @@ -2324,6 +2420,15 @@ babel-plugin-transform-react-remove-prop-types@0.4.24: resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== +babel-polyfill@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" + integrity sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM= + dependencies: + babel-runtime "^6.26.0" + core-js "^2.5.0" + regenerator-runtime "^0.10.5" + babel-preset-jest@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" @@ -2353,7 +2458,20 @@ babel-preset-react-app@^9.1.2: babel-plugin-macros "2.8.0" babel-plugin-transform-react-remove-prop-types "0.4.24" -babel-runtime@^6.26.0: +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= @@ -2361,6 +2479,42 @@ babel-runtime@^6.26.0: core-js "^2.4.0" regenerator-runtime "^0.11.0" +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" @@ -2497,6 +2651,15 @@ brace-expansion@^1.0.0, brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" @@ -2918,6 +3081,22 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +chokidar@^1.6.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + chokidar@^2.0.2, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -3275,7 +3454,7 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.7.0: +convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -3332,7 +3511,7 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== -core-js@^2.4.0: +core-js@^2.4.0, core-js@^2.5.0: version "2.6.11" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== @@ -3696,7 +3875,7 @@ data-urls@^1.0.0, data-urls@^1.1.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -3847,6 +4026,13 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= + dependencies: + repeating "^2.0.0" + detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" @@ -4256,6 +4442,13 @@ escodegen@^1.11.0, escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" +eslint-config-prettier@^6.10.1: + version "6.10.1" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.10.1.tgz#129ef9ec575d5ddc0e269667bf09defcd898642a" + integrity sha512-svTy6zh1ecQojvpbJSgH3aei/Rt7C6i090l5f2WQ4aB05lYHeZIR1qL4wZyyILTbtmnbHP5Yn8MrsOJMGa8RkQ== + dependencies: + get-stdin "^6.0.0" + eslint-config-react-app@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz#698bf7aeee27f0cea0139eaef261c7bf7dd623df" @@ -4330,6 +4523,13 @@ eslint-plugin-jsx-a11y@6.2.3: has "^1.0.3" jsx-ast-utils "^2.2.1" +eslint-plugin-prettier@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" + integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-plugin-react-hooks@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04" @@ -4520,6 +4720,13 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= + dependencies: + is-posix-bracket "^0.1.0" + expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -4533,6 +4740,13 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= + dependencies: + fill-range "^2.1.0" + expect@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" @@ -4617,6 +4831,13 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= + dependencies: + is-extglob "^1.0.0" + extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -4646,6 +4867,11 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^2.0.2: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" @@ -4721,11 +4947,27 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= + filesize@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f" integrity sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg== +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -4857,7 +5099,7 @@ for-in@^1.0.1, for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -for-own@^0.1.3: +for-own@^0.1.3, for-own@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= @@ -4956,6 +5198,11 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" +fs-readdir-recursive@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -4976,7 +5223,7 @@ fsevents@2.1.2, fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== -fsevents@^1.2.7: +fsevents@^1.0.0, fsevents@^1.2.7: version "1.2.12" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.12.tgz#db7e0d8ec3b0b45724fd4d83d43554a8f1f0de5c" integrity sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q== @@ -5014,6 +5261,11 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -5033,6 +5285,21 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= + dependencies: + is-glob "^2.0.0" + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -5103,6 +5370,11 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + globby@8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" @@ -5127,7 +5399,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== @@ -5275,6 +5547,14 @@ hoist-non-react-statics@^3.3.2: dependencies: react-is "^16.7.0" +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -5828,6 +6108,18 @@ is-docker@^2.0.0: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= + dependencies: + is-primitive "^2.0.0" + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -5840,11 +6132,21 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -5867,6 +6169,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -5886,6 +6195,13 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3: resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -5893,6 +6209,11 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -5939,6 +6260,16 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= + is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" @@ -6566,6 +6897,11 @@ jsdom@^14.1.0: ws "^6.1.2" xml-name-validator "^3.0.0" +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -6620,6 +6956,11 @@ json3@^3.3.2: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -6743,10 +7084,10 @@ jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3: array-includes "^3.0.3" object.assign "^4.1.0" -jukes-cantor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/jukes-cantor/-/jukes-cantor-1.0.1.tgz#5a20d28b1b79015224b2c0d971a2ae3b88c7b161" - integrity sha512-8SUhvLBGqIqHCJkQPbKk8qy5ngtfkdd1J7xHyjN+r+K/0MdJqirRxRIBkBxS7jijXXEXxyTRBoAK590kRUtW0Q== +jukes-cantor@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/jukes-cantor/-/jukes-cantor-1.0.4.tgz#10c4a8c94ef31edd2dffbff153ce5dc6fd5c71d6" + integrity sha512-QEkqyO4HsPPun4Vu8Ry8zG3oB/cafBp5dR3M2GwKhmTrufWW9g80udRLBWSrk5XXgLzUiQpEbPATJhibAns+TQ== killable@^1.0.1: version "1.0.1" @@ -6974,7 +7315,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5: +"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -7051,6 +7392,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +math-random@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== + mathjs@^6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-6.6.2.tgz#9411824ca113c158194ed55f47c816bafa63ec8d" @@ -7148,6 +7494,25 @@ microevent.ts@~0.1.1: resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -7446,10 +7811,10 @@ neo-async@^2.5.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== -newick-js@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/newick-js/-/newick-js-1.1.1.tgz#c2c6bf56c1d2232ce9a6e4170576422ddcd5d2fb" - integrity sha512-RaSmZTOtyMklxxu1BSR2Gr6LU+XGXvK+Oc5WVSAUCe7YO5XtkWZRN9qbXDnxtkfxGmHa5wNrC/DSMAeiVFTruA== +newick@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/newick/-/newick-2.0.0.tgz#a10c9838fb18e120ae7241fa9c697507ccdea4af" + integrity sha512-WzIBgGkSUxoOx9pjZq4YhdcflCZqL50PJAXl46hrnrgGdkV4gB0B7//6Zy9pIRb0BnJly5cUFQqRG9sKgnZkkg== next-tick@~1.0.0: version "1.0.0" @@ -7541,7 +7906,7 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.1.1: +normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= @@ -7691,6 +8056,14 @@ object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0 define-properties "^1.1.3" es-abstract "^1.17.0-next.1" +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -7799,6 +8172,11 @@ os-browserify@~0.1.1: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a617d8fe54" integrity sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ= +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + os-locale@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" @@ -7808,11 +8186,20 @@ os-locale@^3.0.0: lcid "^2.0.0" mem "^4.0.0" -os-tmpdir@~1.0.2: +os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= +output-file-sync@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" + integrity sha1-0KM+7+YaIF+suQCS6CZZjVJFznY= + dependencies: + graceful-fs "^4.1.4" + mkdirp "^0.5.1" + object-assign "^4.1.0" + p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -7957,6 +8344,16 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -8037,7 +8434,7 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@^1.0.0: +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= @@ -8890,6 +9287,23 @@ prepend-http@^1.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.4.tgz#2d1bae173e355996ee355ec9830a7a1ee05457ef" + integrity sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w== + pretty-bytes@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" @@ -9102,6 +9516,15 @@ raf@^3.4.1: dependencies: performance-now "^2.1.0" +randomatic@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -9364,7 +9787,7 @@ readable-wrap@^1.0.0: dependencies: readable-stream "^1.1.13-1" -readdirp@^2.2.1: +readdirp@^2.0.0, readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== @@ -9414,6 +9837,11 @@ regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== +regenerator-runtime@^0.10.5: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + integrity sha1-M2w+/BIgrc7dosn6tntaeVWjNlg= + regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" @@ -9432,6 +9860,13 @@ regenerator-transform@^0.14.2: "@babel/runtime" "^7.8.4" private "^0.1.8" +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== + dependencies: + is-equal-shallow "^0.1.3" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -9513,11 +9948,18 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.6.1: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + request-promise-core@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" @@ -10157,6 +10599,13 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== + dependencies: + source-map "^0.5.6" + source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.16" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" @@ -10182,7 +10631,7 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, sourc resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.5.0, source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -10327,10 +10776,10 @@ stealthy-require@^1.1.1: resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= -stockholm-js@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/stockholm-js/-/stockholm-js-1.0.6.tgz#d1ca8b65e36f4168028d01e61cfefb8d264c381b" - integrity sha512-tN/EFTZtfXUGFfq+LEvkUM7kbZCiJtVGl2jRgrS2CYbwYKK1hvIL3OuG9Ege3Q6od5nwCZRJVDQ+UACG/Ics6Q== +stockholm-js@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/stockholm-js/-/stockholm-js-1.0.9.tgz#bac3588f7c28ec0ccaaa88cfcba79b62cfac9e8a" + integrity sha512-qkLwL13AA2djtbpt12h/Eacahc5BFmHa5L4urXpmibB7aWnwx0biQmAHHUzGy2phb0y+mXK+Qtr5vIxMmdqJRg== stream-browserify@^1.0.0: version "1.0.0" @@ -10815,6 +11264,11 @@ to-arraybuffer@^1.0.0: resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -10872,6 +11326,11 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + ts-pnp@1.1.6, ts-pnp@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a" @@ -11123,6 +11582,11 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== +user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + integrity sha1-K1viOjK2Onyd640PKNSFcko98ZA= + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -11187,6 +11651,13 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== +v8flags@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" + integrity sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ= + dependencies: + user-home "^1.1.1" + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"