@apollo-annotation/jbrowse-plugin-apollo 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts +8 -2
- package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts.map +1 -1
- package/dist/ApolloInternetAccount/components/LoginButtons.d.ts +8 -3
- package/dist/ApolloInternetAccount/components/LoginButtons.d.ts.map +1 -1
- package/dist/ApolloInternetAccount/configSchema.d.ts +44 -0
- package/dist/ApolloInternetAccount/configSchema.d.ts.map +1 -0
- package/dist/ApolloInternetAccount/model.d.ts +174 -0
- package/dist/ApolloInternetAccount/model.d.ts.map +1 -0
- package/dist/ApolloJobModel.d.ts.map +1 -1
- package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts +8 -0
- package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts.map +1 -0
- package/dist/ApolloSequenceAdapter/configSchema.d.ts +8 -0
- package/dist/ApolloSequenceAdapter/configSchema.d.ts.map +1 -0
- package/dist/ApolloTextSearchAdapter/configSchema.d.ts +20 -0
- package/dist/ApolloTextSearchAdapter/configSchema.d.ts.map +1 -0
- package/dist/BackendDrivers/CollaborationServerDriver.d.ts +1 -1
- package/dist/BackendDrivers/CollaborationServerDriver.d.ts.map +1 -1
- package/dist/BackendDrivers/LocalDriver/LocalDriver.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/ApolloFeatureDetailsWidget.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/Attributes.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/DefaultAttributeEditor.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/DefaultAttributeViewer.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/NumberTextField.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/StringTextField.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/TranscriptSequence.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/TranscriptWidgetEditLocation.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/Translation.d.ts +14 -0
- package/dist/FeatureDetailsWidget/Translation.d.ts.map +1 -0
- package/dist/LinearApolloDisplay/components/CheckResultWarnings.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/configSchema.d.ts +2 -0
- package/dist/LinearApolloDisplay/configSchema.d.ts.map +1 -0
- package/dist/LinearApolloDisplay/glyphs/ExonGlyph.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/glyphs/TranscriptGlyph.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/base.d.ts +1 -18
- package/dist/LinearApolloDisplay/stateModel/base.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/index.d.ts +1 -17
- package/dist/LinearApolloDisplay/stateModel/index.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/layouts.d.ts +2 -19
- package/dist/LinearApolloDisplay/stateModel/layouts.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts +2 -34
- package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/rendering.d.ts +2 -19
- package/dist/LinearApolloDisplay/stateModel/rendering.d.ts.map +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts +2 -0
- package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts.map +1 -0
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts.map +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/index.d.ts +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/rendering.d.ts +1 -1
- package/dist/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts +2 -0
- package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts.map +1 -0
- package/dist/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts +2 -3
- package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts +2 -2
- package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts +2 -3
- package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts.map +1 -1
- package/dist/OntologyManager/OntologyStore/fulltext.d.ts.map +1 -1
- package/dist/OntologyManager/index.d.ts +734 -0
- package/dist/OntologyManager/index.d.ts.map +1 -0
- package/dist/TabularEditor/HybridGrid/NumberCell.d.ts.map +1 -1
- package/dist/components/AddCodingSequence.d.ts +14 -0
- package/dist/components/AddCodingSequence.d.ts.map +1 -0
- package/dist/components/AddRefSeqAliases.d.ts.map +1 -1
- package/dist/components/CopyFeature.d.ts.map +1 -1
- package/dist/components/CreateApolloAnnotation.d.ts.map +1 -1
- package/dist/components/DeleteFeature.d.ts.map +1 -1
- package/dist/components/DownloadGFF3.d.ts.map +1 -1
- package/dist/components/DuplicateFeatureDialog.d.ts +12 -0
- package/dist/components/DuplicateFeatureDialog.d.ts.map +1 -0
- package/dist/components/ImportAnnotations.d.ts +9 -0
- package/dist/components/ImportAnnotations.d.ts.map +1 -0
- package/dist/components/ManageUsers.d.ts.map +1 -1
- package/dist/components/OntologyTermMultiSelect.d.ts.map +1 -1
- package/dist/components/ViewChangeLog.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/extensions/annotationFromPileup.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +830 -434
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +825 -429
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +863 -468
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/dist/session/ClientDataStore.d.ts +2194 -0
- package/dist/session/ClientDataStore.d.ts.map +1 -0
- package/dist/session/handleApolloFeaturesUrlParam.d.ts +8 -0
- package/dist/session/handleApolloFeaturesUrlParam.d.ts.map +1 -0
- package/dist/session/session.d.ts +6788 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/util/displayUtils.d.ts +36 -0
- package/dist/util/displayUtils.d.ts.map +1 -0
- package/dist/util/sequenceUtils.d.ts +2 -0
- package/dist/util/sequenceUtils.d.ts.map +1 -0
- package/package.json +14 -14
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +73 -38
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +31 -10
- package/src/ApolloInternetAccount/model.ts +8 -5
- package/src/ApolloJobModel.ts +3 -8
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +1 -1
- package/src/BackendDrivers/CollaborationServerDriver.ts +7 -11
- package/src/BackendDrivers/LocalDriver/LocalDriver.ts +13 -4
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +1 -0
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +4 -2
- package/src/FeatureDetailsWidget/Attributes.tsx +1 -0
- package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +1 -0
- package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +1 -0
- package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -0
- package/src/FeatureDetailsWidget/StringTextField.tsx +1 -0
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +2 -1
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +17 -354
- package/src/FeatureDetailsWidget/Translation.tsx +358 -0
- package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +4 -1
- package/src/LinearApolloDisplay/glyphs/ExonGlyph.ts +2 -5
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +4 -6
- package/src/LinearApolloDisplay/glyphs/TranscriptGlyph.ts +39 -6
- package/src/LinearApolloDisplay/stateModel/base.ts +47 -23
- package/src/LinearApolloDisplay/stateModel/layouts.ts +7 -5
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +4 -1
- package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -5
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +10 -10
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +5 -3
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +1 -1
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +2 -3
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +12 -20
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +10 -6
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +38 -45
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +1 -4
- package/src/OntologyManager/OntologyStore/fulltext.ts +0 -2
- package/src/OntologyManager/OntologyStore/index.ts +1 -1
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +2 -1
- package/src/components/AddAssemblyAliases.tsx +1 -1
- package/src/components/AddCodingSequence.tsx +255 -0
- package/src/components/AddFeature.tsx +1 -1
- package/src/components/AddRefSeqAliases.tsx +3 -2
- package/src/components/CopyFeature.tsx +9 -7
- package/src/components/CreateApolloAnnotation.tsx +28 -23
- package/src/components/DeleteAssembly.tsx +2 -2
- package/src/components/DeleteFeature.tsx +1 -4
- package/src/components/DownloadGFF3.tsx +4 -3
- package/src/components/DuplicateFeatureDialog.tsx +154 -0
- package/src/components/DuplicateTranscript.tsx +1 -1
- package/src/components/FilterTranscripts.tsx +1 -1
- package/src/components/ImportAnnotations.tsx +108 -0
- package/src/components/ManageUsers.tsx +1 -2
- package/src/components/OntologyTermAutocomplete.tsx +1 -3
- package/src/components/OntologyTermMultiSelect.tsx +3 -1
- package/src/components/ViewChangeLog.tsx +2 -1
- package/src/components/index.ts +2 -0
- package/src/extensions/annotationFromPileup.ts +34 -26
- package/src/index.ts +9 -8
- package/src/session/ClientDataStore.ts +16 -21
- package/src/session/handleApolloFeaturesUrlParam.ts +122 -0
- package/src/session/session.ts +49 -9
- package/src/util/annotationFeatureUtils.ts +1 -1
- package/src/util/displayUtils.ts +1 -1
- package/src/util/glyphUtils.ts +3 -3
- package/src/util/sequenceUtils.ts +29 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnnotationFeature,
|
|
3
|
+
ApolloRefSeqI,
|
|
4
|
+
TranscriptPartCoding,
|
|
5
|
+
} from '@apollo-annotation/mst'
|
|
6
|
+
import styled from '@emotion/styled'
|
|
7
|
+
import {
|
|
8
|
+
type AbstractSessionModel,
|
|
9
|
+
defaultCodonTable,
|
|
10
|
+
revcom,
|
|
11
|
+
} from '@jbrowse/core/util'
|
|
12
|
+
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
|
|
13
|
+
import ContentCutIcon from '@mui/icons-material/ContentCut'
|
|
14
|
+
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
|
15
|
+
import {
|
|
16
|
+
Accordion,
|
|
17
|
+
AccordionDetails,
|
|
18
|
+
AccordionSummary,
|
|
19
|
+
Tooltip,
|
|
20
|
+
Typography,
|
|
21
|
+
} from '@mui/material'
|
|
22
|
+
import React, { useRef } from 'react'
|
|
23
|
+
|
|
24
|
+
import type { ApolloSessionModel } from '../session'
|
|
25
|
+
import { copyToClipboard } from '../util/copyToClipboard'
|
|
26
|
+
|
|
27
|
+
const SequenceContainer = styled('div')({
|
|
28
|
+
display: 'flex',
|
|
29
|
+
justifyContent: 'center',
|
|
30
|
+
alignItems: 'center',
|
|
31
|
+
textAlign: 'left',
|
|
32
|
+
width: '100%',
|
|
33
|
+
overflowWrap: 'break-word',
|
|
34
|
+
wordWrap: 'break-word',
|
|
35
|
+
wordBreak: 'break-all',
|
|
36
|
+
'& span': {
|
|
37
|
+
fontSize: 12,
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const StyledAccordionSummary = styled(AccordionSummary)(() => ({
|
|
42
|
+
minHeight: 30,
|
|
43
|
+
maxHeight: 30,
|
|
44
|
+
'&.Mui-expanded': {
|
|
45
|
+
minHeight: 30,
|
|
46
|
+
maxHeight: 30,
|
|
47
|
+
},
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
export function Translation({
|
|
51
|
+
changeInProgress,
|
|
52
|
+
cdsLocations,
|
|
53
|
+
refData,
|
|
54
|
+
strand,
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
56
|
+
updateCDSLocation,
|
|
57
|
+
cdsMin,
|
|
58
|
+
cdsMax,
|
|
59
|
+
feature,
|
|
60
|
+
session,
|
|
61
|
+
}: {
|
|
62
|
+
changeInProgress: boolean
|
|
63
|
+
cdsLocations: TranscriptPartCoding[][]
|
|
64
|
+
refData: ApolloRefSeqI
|
|
65
|
+
strand: 1 | -1 | undefined
|
|
66
|
+
updateCDSLocation(
|
|
67
|
+
oldLocation: number,
|
|
68
|
+
newLocation: number,
|
|
69
|
+
feature: AnnotationFeature,
|
|
70
|
+
isMin: boolean,
|
|
71
|
+
onComplete?: () => void,
|
|
72
|
+
): boolean
|
|
73
|
+
cdsMin: number
|
|
74
|
+
cdsMax: number
|
|
75
|
+
feature: AnnotationFeature
|
|
76
|
+
session: ApolloSessionModel
|
|
77
|
+
}) {
|
|
78
|
+
const seqRef = useRef<HTMLDivElement>(null)
|
|
79
|
+
const { notify } = session as unknown as AbstractSessionModel
|
|
80
|
+
|
|
81
|
+
const cdsSequences: string[] = []
|
|
82
|
+
const [firstLocation] = cdsLocations
|
|
83
|
+
for (const loc of firstLocation) {
|
|
84
|
+
const seq = refData.getSequence(loc.min, loc.max)
|
|
85
|
+
cdsSequences.push(strand === -1 ? revcom(seq) : seq)
|
|
86
|
+
}
|
|
87
|
+
const cdsSequence = cdsSequences.join('')
|
|
88
|
+
|
|
89
|
+
const proteinSequence: string[] = []
|
|
90
|
+
for (
|
|
91
|
+
let codonGenomicPos = 0;
|
|
92
|
+
codonGenomicPos < cdsSequence.length;
|
|
93
|
+
codonGenomicPos += 3
|
|
94
|
+
) {
|
|
95
|
+
const codonSeq = cdsSequence
|
|
96
|
+
.slice(codonGenomicPos, codonGenomicPos + 3)
|
|
97
|
+
.toUpperCase()
|
|
98
|
+
const protein =
|
|
99
|
+
defaultCodonTable[codonSeq as keyof typeof defaultCodonTable] || '&'
|
|
100
|
+
proteinSequence.push(protein)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const onCopyClick = () => {
|
|
104
|
+
const seqDiv = seqRef.current
|
|
105
|
+
if (!seqDiv) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
void copyToClipboard(seqDiv)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Codon position is the index of the start codon in the CDS genomic sequence
|
|
112
|
+
// Calculate the genomic location of the start codon based on the codon position in the CDS
|
|
113
|
+
const getCodonGenomicLocation = (codonGenomicPosition: number) => {
|
|
114
|
+
const [firstLocation] = cdsLocations
|
|
115
|
+
let cdsLen = 0
|
|
116
|
+
const sortedCDSLocations = firstLocation.toSorted(
|
|
117
|
+
({ min: a }, { min: b }) => a - b,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
// Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
|
|
121
|
+
// and codonGenomicPosition is 25
|
|
122
|
+
// ((10 - 0) + (30 - 20) + (50 - 40)) > 25
|
|
123
|
+
// So, start codon is in (40, 50)
|
|
124
|
+
// 40 + (25-20) = 45 is the genomic location of the start codon
|
|
125
|
+
if (strand === 1) {
|
|
126
|
+
for (const loc of sortedCDSLocations) {
|
|
127
|
+
const locLength = loc.max - loc.min
|
|
128
|
+
if (cdsLen + locLength > codonGenomicPosition) {
|
|
129
|
+
return loc.min + (codonGenomicPosition - cdsLen)
|
|
130
|
+
}
|
|
131
|
+
cdsLen += locLength
|
|
132
|
+
}
|
|
133
|
+
} else if (strand === -1) {
|
|
134
|
+
for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
|
|
135
|
+
const loc = sortedCDSLocations[i]
|
|
136
|
+
const locLength = loc.max - loc.min
|
|
137
|
+
if (cdsLen + locLength > codonGenomicPosition) {
|
|
138
|
+
return loc.max - (codonGenomicPosition - cdsLen)
|
|
139
|
+
}
|
|
140
|
+
cdsLen += locLength
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (strand === 1) {
|
|
145
|
+
return cdsMin
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return cdsMax
|
|
149
|
+
}
|
|
150
|
+
const trimTranslationSequence = () => {
|
|
151
|
+
if (
|
|
152
|
+
(proteinSequence.at(0) === 'M' && proteinSequence.at(-1) === '*') ||
|
|
153
|
+
proteinSequence.length === 0
|
|
154
|
+
) {
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Trim any sequence before first start codon and after stop codon
|
|
159
|
+
const startCodonIndex = proteinSequence.indexOf('M')
|
|
160
|
+
const stopCodonIndex = proteinSequence.lastIndexOf('*')
|
|
161
|
+
|
|
162
|
+
const startCodonPos = startCodonIndex * 3
|
|
163
|
+
const stopCodonPos = stopCodonIndex * 3
|
|
164
|
+
|
|
165
|
+
const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos)
|
|
166
|
+
let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos)
|
|
167
|
+
|
|
168
|
+
if (strand === 1) {
|
|
169
|
+
if (startCodonGenomicLoc > stopCodonGenomicLoc) {
|
|
170
|
+
notify(
|
|
171
|
+
'Start codon genomic location should be less than stop codon genomic location',
|
|
172
|
+
'error',
|
|
173
|
+
)
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
let promise
|
|
177
|
+
stopCodonGenomicLoc += 3 // move to end of stop codon
|
|
178
|
+
if (startCodonGenomicLoc !== cdsMin) {
|
|
179
|
+
promise = new Promise((resolve) => {
|
|
180
|
+
updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
|
|
181
|
+
resolve(true)
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (stopCodonGenomicLoc !== cdsMax) {
|
|
187
|
+
if (promise) {
|
|
188
|
+
void promise.then(() => {
|
|
189
|
+
updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false)
|
|
190
|
+
})
|
|
191
|
+
} else {
|
|
192
|
+
updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (strand === -1) {
|
|
198
|
+
// reverse strand
|
|
199
|
+
if (startCodonGenomicLoc < stopCodonGenomicLoc) {
|
|
200
|
+
notify(
|
|
201
|
+
'Start codon genomic location should be less than stop codon genomic location',
|
|
202
|
+
'error',
|
|
203
|
+
)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
let promise
|
|
207
|
+
stopCodonGenomicLoc -= 3 // move to end of stop codon
|
|
208
|
+
if (startCodonGenomicLoc !== cdsMax) {
|
|
209
|
+
promise = new Promise((resolve) => {
|
|
210
|
+
updateCDSLocation(
|
|
211
|
+
cdsMax,
|
|
212
|
+
startCodonGenomicLoc,
|
|
213
|
+
feature,
|
|
214
|
+
false,
|
|
215
|
+
() => {
|
|
216
|
+
resolve(true)
|
|
217
|
+
},
|
|
218
|
+
)
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (stopCodonGenomicLoc !== cdsMin) {
|
|
223
|
+
if (promise) {
|
|
224
|
+
void promise.then(() => {
|
|
225
|
+
updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true)
|
|
226
|
+
})
|
|
227
|
+
} else {
|
|
228
|
+
updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
notify('Translation sequence trimmed to start and stop codons', 'success')
|
|
233
|
+
}
|
|
234
|
+
return (
|
|
235
|
+
<div>
|
|
236
|
+
<Accordion>
|
|
237
|
+
<StyledAccordionSummary
|
|
238
|
+
expandIcon={<ExpandMoreIcon style={{ color: 'white' }} />}
|
|
239
|
+
aria-controls="panel1-content"
|
|
240
|
+
id="panel1-header"
|
|
241
|
+
>
|
|
242
|
+
<Typography component="span" fontWeight={'bold'}>
|
|
243
|
+
Translation
|
|
244
|
+
</Typography>
|
|
245
|
+
</StyledAccordionSummary>
|
|
246
|
+
<AccordionDetails>
|
|
247
|
+
<SequenceContainer>
|
|
248
|
+
<Typography
|
|
249
|
+
component={'span'}
|
|
250
|
+
ref={seqRef}
|
|
251
|
+
style={{ maxHeight: 120, overflowY: 'scroll' }}
|
|
252
|
+
>
|
|
253
|
+
{proteinSequence.map((protein, idx) => {
|
|
254
|
+
const codonGenomicPos = idx * 3
|
|
255
|
+
if (protein === 'M') {
|
|
256
|
+
return (
|
|
257
|
+
<Typography
|
|
258
|
+
component={'span'}
|
|
259
|
+
style={{
|
|
260
|
+
backgroundColor: changeInProgress
|
|
261
|
+
? 'lightgray'
|
|
262
|
+
: 'yellow',
|
|
263
|
+
cursor: 'pointer',
|
|
264
|
+
border: '1px solid black',
|
|
265
|
+
}}
|
|
266
|
+
key={codonGenomicPos}
|
|
267
|
+
onClick={() => {
|
|
268
|
+
if (changeInProgress) {
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
// NOTE: codonGenomicPos is important here for calculating the genomic location
|
|
272
|
+
// of the start codon. We are using the codonGenomicPos as the key in the typography
|
|
273
|
+
// elements to maintain the genomic postion of the codon start
|
|
274
|
+
const startCodonGenomicLocation =
|
|
275
|
+
getCodonGenomicLocation(codonGenomicPos)
|
|
276
|
+
if (
|
|
277
|
+
startCodonGenomicLocation !== cdsMin &&
|
|
278
|
+
strand === 1
|
|
279
|
+
) {
|
|
280
|
+
updateCDSLocation(
|
|
281
|
+
cdsMin,
|
|
282
|
+
startCodonGenomicLocation,
|
|
283
|
+
feature,
|
|
284
|
+
true,
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
if (
|
|
288
|
+
startCodonGenomicLocation !== cdsMax &&
|
|
289
|
+
strand === -1
|
|
290
|
+
) {
|
|
291
|
+
updateCDSLocation(
|
|
292
|
+
cdsMax,
|
|
293
|
+
startCodonGenomicLocation,
|
|
294
|
+
feature,
|
|
295
|
+
false,
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
}}
|
|
299
|
+
>
|
|
300
|
+
{protein}
|
|
301
|
+
</Typography>
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (protein === '*') {
|
|
306
|
+
return (
|
|
307
|
+
<Typography
|
|
308
|
+
style={{ backgroundColor: 'red', color: 'white' }}
|
|
309
|
+
component={'span'}
|
|
310
|
+
// Pass the codonGenomicPos as the key to maintain the genomic position of the codon
|
|
311
|
+
key={codonGenomicPos}
|
|
312
|
+
>
|
|
313
|
+
{protein}
|
|
314
|
+
</Typography>
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
// Pass the codonGenomicPos as the key to maintain the genomic position of the codon
|
|
318
|
+
return (
|
|
319
|
+
<Typography component={'span'} key={codonGenomicPos}>
|
|
320
|
+
{protein}
|
|
321
|
+
</Typography>
|
|
322
|
+
)
|
|
323
|
+
})}
|
|
324
|
+
</Typography>
|
|
325
|
+
</SequenceContainer>
|
|
326
|
+
<div
|
|
327
|
+
style={{
|
|
328
|
+
marginTop: 10,
|
|
329
|
+
display: 'flex',
|
|
330
|
+
flexDirection: 'row',
|
|
331
|
+
alignItems: 'center',
|
|
332
|
+
gap: 10,
|
|
333
|
+
}}
|
|
334
|
+
>
|
|
335
|
+
<Tooltip title="Copy">
|
|
336
|
+
<button
|
|
337
|
+
onClick={onCopyClick}
|
|
338
|
+
style={{ border: 'none', background: 'none', padding: 0 }}
|
|
339
|
+
disabled={changeInProgress}
|
|
340
|
+
>
|
|
341
|
+
<ContentCopyIcon style={{ fontSize: 15 }} />
|
|
342
|
+
</button>
|
|
343
|
+
</Tooltip>
|
|
344
|
+
<Tooltip title="Trim">
|
|
345
|
+
<button
|
|
346
|
+
onClick={trimTranslationSequence}
|
|
347
|
+
style={{ border: 'none', background: 'none', padding: 0 }}
|
|
348
|
+
disabled={changeInProgress}
|
|
349
|
+
>
|
|
350
|
+
<ContentCutIcon style={{ fontSize: 15 }} />
|
|
351
|
+
</button>
|
|
352
|
+
</Tooltip>
|
|
353
|
+
</div>
|
|
354
|
+
</AccordionDetails>
|
|
355
|
+
</Accordion>
|
|
356
|
+
</div>
|
|
357
|
+
)
|
|
358
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CheckResultI } from '@apollo-annotation/mst'
|
|
2
|
+
import type { Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
2
3
|
import { type AbstractSessionModel, doesIntersect2 } from '@jbrowse/core/util'
|
|
3
4
|
import ErrorIcon from '@mui/icons-material/Error'
|
|
4
5
|
import { Avatar, Badge, Box, Tooltip } from '@mui/material'
|
|
@@ -23,7 +24,9 @@ export const CheckResultWarnings = observer(function CheckResultWarnings({
|
|
|
23
24
|
}
|
|
24
25
|
return lgv.dynamicBlocks.contentBlocks.map((block) => {
|
|
25
26
|
const widthBp = lgv.bpPerPx * apolloRowHeight
|
|
26
|
-
const assembly = assemblyManager.get(block.assemblyName)
|
|
27
|
+
const assembly = assemblyManager.get(block.assemblyName) as
|
|
28
|
+
| Assembly
|
|
29
|
+
| undefined
|
|
27
30
|
if (!assembly) {
|
|
28
31
|
return null
|
|
29
32
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AnnotationFeature } from '@apollo-annotation/mst'
|
|
2
|
-
import type { BaseDisplayModel } from '@jbrowse/core/pluggableElementTypes'
|
|
3
2
|
import type { MenuItem } from '@jbrowse/core/ui'
|
|
4
3
|
import {
|
|
5
4
|
type AbstractSessionModel,
|
|
@@ -106,14 +105,12 @@ function getContextMenuItems(
|
|
|
106
105
|
session,
|
|
107
106
|
} = display
|
|
108
107
|
const [region] = regions
|
|
109
|
-
const currentAssemblyId = display.getAssemblyId(region.assemblyName)
|
|
108
|
+
const currentAssemblyId = display.getAssemblyId(region.assemblyName) as string
|
|
110
109
|
const role = internetAccount ? internetAccount.role : 'admin'
|
|
111
110
|
const admin = role === 'admin'
|
|
112
111
|
const menuItems: MenuItem[] = []
|
|
113
112
|
const adjacentExons = getAdjacentExons(feature, display)
|
|
114
|
-
const lgv = getContainingView(
|
|
115
|
-
display as BaseDisplayModel,
|
|
116
|
-
) as unknown as LinearGenomeViewModel
|
|
113
|
+
const lgv = getContainingView(display) as unknown as LinearGenomeViewModel
|
|
117
114
|
if (adjacentExons.upstream) {
|
|
118
115
|
const exon = adjacentExons.upstream
|
|
119
116
|
menuItems.push({
|
|
@@ -30,12 +30,10 @@ function draw(
|
|
|
30
30
|
const [top, left, width] = getFeatureBox(display, gene, row, block)
|
|
31
31
|
const height = getRowCount(display, gene) * apolloRowHeight
|
|
32
32
|
if (width > 2) {
|
|
33
|
-
|
|
34
|
-
session.getPluginConfiguration(),
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
) as string
|
|
38
|
-
selectedColor = alpha(theme.palette.background.paper, 0.6)
|
|
33
|
+
const selectedColor =
|
|
34
|
+
(readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', {
|
|
35
|
+
featureType: gene.type,
|
|
36
|
+
}) as string | undefined) ?? alpha(theme.palette.background.paper, 0.6)
|
|
39
37
|
ctx.fillStyle = selectedColor
|
|
40
38
|
ctx.fillRect(left, top, width, height)
|
|
41
39
|
}
|
|
@@ -6,7 +6,11 @@ import {
|
|
|
6
6
|
} from '@jbrowse/core/util'
|
|
7
7
|
import type { ContentBlock } from '@jbrowse/core/util/blockTypes'
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
AddCodingSequence,
|
|
11
|
+
DuplicateTranscript,
|
|
12
|
+
MergeTranscripts,
|
|
13
|
+
} from '../../components'
|
|
10
14
|
import {
|
|
11
15
|
isCDSFeature,
|
|
12
16
|
isExonFeature,
|
|
@@ -201,11 +205,11 @@ function getLayout(display: LinearApolloDisplay, feature: AnnotationFeature) {
|
|
|
201
205
|
|
|
202
206
|
function getContextMenuItems(
|
|
203
207
|
display: LinearApolloDisplay,
|
|
204
|
-
|
|
208
|
+
transcript: AnnotationFeature,
|
|
205
209
|
): MenuItem[] {
|
|
206
210
|
const { changeManager, regions, selectedFeature, session } = display
|
|
207
211
|
const [region] = regions
|
|
208
|
-
const currentAssemblyId = display.getAssemblyId(region.assemblyName)
|
|
212
|
+
const currentAssemblyId = display.getAssemblyId(region.assemblyName) as string
|
|
209
213
|
const menuItems: MenuItem[] = []
|
|
210
214
|
if (isSessionModelWithWidgets(session)) {
|
|
211
215
|
menuItems.splice(1, 0, {
|
|
@@ -215,7 +219,7 @@ function getContextMenuItems(
|
|
|
215
219
|
'ApolloTranscriptDetails',
|
|
216
220
|
'apolloTranscriptDetails',
|
|
217
221
|
{
|
|
218
|
-
feature,
|
|
222
|
+
feature: transcript,
|
|
219
223
|
assembly: currentAssemblyId,
|
|
220
224
|
changeManager,
|
|
221
225
|
refName: region.refName,
|
|
@@ -238,7 +242,7 @@ function getContextMenuItems(
|
|
|
238
242
|
doneCallback()
|
|
239
243
|
},
|
|
240
244
|
changeManager,
|
|
241
|
-
sourceFeature:
|
|
245
|
+
sourceFeature: transcript,
|
|
242
246
|
sourceAssemblyId: currentAssemblyId,
|
|
243
247
|
selectedFeature,
|
|
244
248
|
setSelectedFeature: (feature?: AnnotationFeature) => {
|
|
@@ -261,7 +265,7 @@ function getContextMenuItems(
|
|
|
261
265
|
doneCallback()
|
|
262
266
|
},
|
|
263
267
|
changeManager,
|
|
264
|
-
sourceFeature:
|
|
268
|
+
sourceFeature: transcript,
|
|
265
269
|
sourceAssemblyId: currentAssemblyId,
|
|
266
270
|
selectedFeature,
|
|
267
271
|
setSelectedFeature: (feature?: AnnotationFeature) => {
|
|
@@ -273,6 +277,35 @@ function getContextMenuItems(
|
|
|
273
277
|
},
|
|
274
278
|
},
|
|
275
279
|
)
|
|
280
|
+
const { children } = transcript
|
|
281
|
+
if (!children) {
|
|
282
|
+
return []
|
|
283
|
+
}
|
|
284
|
+
const cdsChildren = [...children.values()].filter((child) =>
|
|
285
|
+
isCDSFeature(child, session),
|
|
286
|
+
)
|
|
287
|
+
if (cdsChildren.length === 0) {
|
|
288
|
+
menuItems.push({
|
|
289
|
+
label: 'Add coding sequence',
|
|
290
|
+
onClick: () => {
|
|
291
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
292
|
+
(doneCallback) => [
|
|
293
|
+
AddCodingSequence,
|
|
294
|
+
{
|
|
295
|
+
session,
|
|
296
|
+
handleClose: () => {
|
|
297
|
+
doneCallback()
|
|
298
|
+
},
|
|
299
|
+
changeManager,
|
|
300
|
+
sourceFeature: transcript,
|
|
301
|
+
sourceAssemblyId: currentAssemblyId,
|
|
302
|
+
refName: region.refName,
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
)
|
|
306
|
+
},
|
|
307
|
+
})
|
|
308
|
+
}
|
|
276
309
|
return menuItems
|
|
277
310
|
}
|
|
278
311
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
6
6
|
import type { AnnotationFeature } from '@apollo-annotation/mst'
|
|
7
7
|
import type PluginManager from '@jbrowse/core/PluginManager'
|
|
8
|
+
import type { Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
8
9
|
import {
|
|
9
10
|
type AnyConfigurationSchemaType,
|
|
10
11
|
ConfigurationReference,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
} from '@jbrowse/mobx-state-tree'
|
|
29
30
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
30
31
|
import FactCheckIcon from '@mui/icons-material/FactCheck'
|
|
32
|
+
import InputIcon from '@mui/icons-material/Input'
|
|
31
33
|
import TrackChangesIcon from '@mui/icons-material/TrackChanges'
|
|
32
34
|
import { autorun } from 'mobx'
|
|
33
35
|
|
|
@@ -37,11 +39,13 @@ import {
|
|
|
37
39
|
Export as ExportIcon,
|
|
38
40
|
} from '../../components/DownloadGFF3'
|
|
39
41
|
import { FilterFeatures } from '../../components/FilterFeatures'
|
|
42
|
+
import { ImportAnnotations } from '../../components/ImportAnnotations'
|
|
40
43
|
import { ViewChangeLog } from '../../components/ViewChangeLog'
|
|
41
44
|
import { ViewCheckResults } from '../../components/ViewCheckResults'
|
|
42
45
|
import type { ApolloSessionModel, HoveredFeature } from '../../session'
|
|
43
46
|
import type { ApolloRootModel } from '../../types'
|
|
44
47
|
import { EditZoomThresholdDialog } from '../../util/displayUtils'
|
|
48
|
+
import { LocalDriver } from '../../BackendDrivers'
|
|
45
49
|
|
|
46
50
|
const minDisplayHeight = 20
|
|
47
51
|
|
|
@@ -137,7 +141,9 @@ export function baseModelFactory(
|
|
|
137
141
|
const { assemblyName } = region
|
|
138
142
|
const { assemblyManager } =
|
|
139
143
|
self.session as unknown as AbstractSessionModel
|
|
140
|
-
const assembly = assemblyManager.get(assemblyName)
|
|
144
|
+
const assembly = assemblyManager.get(assemblyName) as
|
|
145
|
+
| Assembly
|
|
146
|
+
| undefined
|
|
141
147
|
if (!assembly) {
|
|
142
148
|
throw new Error(`No assembly found with name ${assemblyName}`)
|
|
143
149
|
}
|
|
@@ -150,8 +156,7 @@ export function baseModelFactory(
|
|
|
150
156
|
) as ApolloInternetAccountModel | undefined
|
|
151
157
|
},
|
|
152
158
|
get changeManager() {
|
|
153
|
-
return
|
|
154
|
-
.changeManager
|
|
159
|
+
return self.session.apolloDataStore.changeManager
|
|
155
160
|
},
|
|
156
161
|
getAssemblyId(assemblyName: string) {
|
|
157
162
|
const { assemblyManager } =
|
|
@@ -163,12 +168,10 @@ export function baseModelFactory(
|
|
|
163
168
|
return assembly.name
|
|
164
169
|
},
|
|
165
170
|
get selectedFeature(): AnnotationFeature | undefined {
|
|
166
|
-
return
|
|
167
|
-
.apolloSelectedFeature
|
|
171
|
+
return self.session.apolloSelectedFeature
|
|
168
172
|
},
|
|
169
173
|
get hoveredFeature(): HoveredFeature | undefined {
|
|
170
|
-
return
|
|
171
|
-
.apolloHoveredFeature
|
|
174
|
+
return self.session.apolloHoveredFeature
|
|
172
175
|
},
|
|
173
176
|
}))
|
|
174
177
|
.actions((self) => ({
|
|
@@ -213,8 +216,12 @@ export function baseModelFactory(
|
|
|
213
216
|
const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self
|
|
214
217
|
return {
|
|
215
218
|
trackMenuItems() {
|
|
216
|
-
const { graphical, table, showCheckResults } = self
|
|
217
|
-
|
|
219
|
+
const { graphical, table, showCheckResults, session } = self
|
|
220
|
+
const [region] = self.regions
|
|
221
|
+
const { assemblyName } = region
|
|
222
|
+
const backendDriver =
|
|
223
|
+
session.apolloDataStore.getBackendDriver(assemblyName)
|
|
224
|
+
const items = [
|
|
218
225
|
...superTrackMenuItems(),
|
|
219
226
|
{
|
|
220
227
|
type: 'subMenu',
|
|
@@ -266,7 +273,7 @@ export function baseModelFactory(
|
|
|
266
273
|
{
|
|
267
274
|
label: 'Filter features by type',
|
|
268
275
|
onClick: () => {
|
|
269
|
-
const session = self
|
|
276
|
+
const { session } = self
|
|
270
277
|
;(self.session as unknown as AbstractSessionModel).queueDialog(
|
|
271
278
|
(doneCallback) => [
|
|
272
279
|
FilterFeatures,
|
|
@@ -284,6 +291,28 @@ export function baseModelFactory(
|
|
|
284
291
|
)
|
|
285
292
|
},
|
|
286
293
|
},
|
|
294
|
+
]
|
|
295
|
+
if (backendDriver instanceof LocalDriver) {
|
|
296
|
+
items.push({
|
|
297
|
+
label: 'Import annotations',
|
|
298
|
+
icon: InputIcon,
|
|
299
|
+
onClick: () => {
|
|
300
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
301
|
+
(doneCallback) => [
|
|
302
|
+
ImportAnnotations,
|
|
303
|
+
{
|
|
304
|
+
session,
|
|
305
|
+
assemblyName,
|
|
306
|
+
handleClose: () => {
|
|
307
|
+
doneCallback()
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
)
|
|
312
|
+
},
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
items.push(
|
|
287
316
|
{
|
|
288
317
|
label: 'Export annotations',
|
|
289
318
|
icon: ExportIcon,
|
|
@@ -294,7 +323,7 @@ export function baseModelFactory(
|
|
|
294
323
|
if (!assembly) {
|
|
295
324
|
return
|
|
296
325
|
}
|
|
297
|
-
const session = self
|
|
326
|
+
const { session } = self
|
|
298
327
|
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
299
328
|
(doneCallback) => [
|
|
300
329
|
DownloadGFF3,
|
|
@@ -319,7 +348,7 @@ export function baseModelFactory(
|
|
|
319
348
|
if (!assembly) {
|
|
320
349
|
return
|
|
321
350
|
}
|
|
322
|
-
const session = self
|
|
351
|
+
const { session } = self
|
|
323
352
|
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
324
353
|
(doneCallback) => [
|
|
325
354
|
ViewChangeLog,
|
|
@@ -344,7 +373,7 @@ export function baseModelFactory(
|
|
|
344
373
|
if (!assembly) {
|
|
345
374
|
return
|
|
346
375
|
}
|
|
347
|
-
const session = self
|
|
376
|
+
const { session } = self
|
|
348
377
|
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
349
378
|
(doneCallback) => [
|
|
350
379
|
ViewCheckResults,
|
|
@@ -359,20 +388,17 @@ export function baseModelFactory(
|
|
|
359
388
|
)
|
|
360
389
|
},
|
|
361
390
|
},
|
|
362
|
-
|
|
391
|
+
)
|
|
392
|
+
return items
|
|
363
393
|
},
|
|
364
394
|
}
|
|
365
395
|
})
|
|
366
396
|
.actions((self) => ({
|
|
367
397
|
setSelectedFeature(feature?: AnnotationFeature) {
|
|
368
|
-
|
|
369
|
-
self.session as unknown as ApolloSessionModel
|
|
370
|
-
).apolloSetSelectedFeature(feature)
|
|
398
|
+
self.session.apolloSetSelectedFeature(feature)
|
|
371
399
|
},
|
|
372
400
|
setHoveredFeature(hoveredFeature?: HoveredFeature) {
|
|
373
|
-
|
|
374
|
-
self.session as unknown as ApolloSessionModel
|
|
375
|
-
).apolloSetHoveredFeature(hoveredFeature)
|
|
401
|
+
self.session.apolloSetHoveredFeature(hoveredFeature)
|
|
376
402
|
},
|
|
377
403
|
showFeatureDetailsWidget(
|
|
378
404
|
feature: AnnotationFeature,
|
|
@@ -411,9 +437,7 @@ export function baseModelFactory(
|
|
|
411
437
|
return
|
|
412
438
|
}
|
|
413
439
|
self.setLoading(true)
|
|
414
|
-
void
|
|
415
|
-
self.session as unknown as ApolloSessionModel
|
|
416
|
-
).apolloDataStore
|
|
440
|
+
void self.session.apolloDataStore
|
|
417
441
|
.loadFeatures(self.regions)
|
|
418
442
|
.then(() => {
|
|
419
443
|
setTimeout(() => {
|