@jbrowse/plugin-alignments 2.1.7 → 2.2.1
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/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +12 -13
- package/dist/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js.map +1 -1
- package/dist/AlignmentsTrack/index.d.ts +1 -1
- package/dist/AlignmentsTrack/index.js +16 -6
- package/dist/AlignmentsTrack/index.js.map +1 -1
- package/dist/BamAdapter/BamAdapter.d.ts +7 -3
- package/dist/BamAdapter/BamAdapter.js +36 -31
- package/dist/BamAdapter/BamAdapter.js.map +1 -1
- package/dist/BamAdapter/BamSlightlyLazyFeature.js +1 -0
- package/dist/BamAdapter/BamSlightlyLazyFeature.js.map +1 -1
- package/dist/BamAdapter/MismatchParser.d.ts +2 -2
- package/dist/BamAdapter/MismatchParser.js +4 -7
- package/dist/BamAdapter/MismatchParser.js.map +1 -1
- package/dist/BamAdapter/configSchema.d.ts +2 -2
- package/dist/BamAdapter/configSchema.js +27 -2
- package/dist/BamAdapter/configSchema.js.map +1 -1
- package/dist/BamAdapter/index.js +7 -5
- package/dist/BamAdapter/index.js.map +1 -1
- package/dist/CramAdapter/CramAdapter.d.ts +13 -7
- package/dist/CramAdapter/CramAdapter.js +56 -61
- package/dist/CramAdapter/CramAdapter.js.map +1 -1
- package/dist/CramAdapter/CramSlightlyLazyFeature.d.ts +15 -23
- package/dist/CramAdapter/CramSlightlyLazyFeature.js +10 -217
- package/dist/CramAdapter/CramSlightlyLazyFeature.js.map +1 -1
- package/dist/CramAdapter/CramTestAdapters.d.ts +1 -1
- package/dist/CramAdapter/CramTestAdapters.js +1 -1
- package/dist/CramAdapter/CramTestAdapters.js.map +1 -1
- package/dist/CramAdapter/configSchema.d.ts +2 -3
- package/dist/CramAdapter/configSchema.js +44 -22
- package/dist/CramAdapter/configSchema.js.map +1 -1
- package/dist/CramAdapter/index.js +7 -5
- package/dist/CramAdapter/index.js.map +1 -1
- package/dist/CramAdapter/util.d.ts +18 -0
- package/dist/CramAdapter/util.js +241 -0
- package/dist/CramAdapter/util.js.map +1 -0
- package/dist/HtsgetBamAdapter/HtsgetBamAdapter.d.ts +5 -2
- package/dist/HtsgetBamAdapter/HtsgetBamAdapter.js +15 -20
- package/dist/HtsgetBamAdapter/HtsgetBamAdapter.js.map +1 -1
- package/dist/HtsgetBamAdapter/configSchema.d.ts +2 -2
- package/dist/HtsgetBamAdapter/configSchema.js +20 -3
- package/dist/HtsgetBamAdapter/configSchema.js.map +1 -1
- package/dist/LinearAlignmentsDisplay/components/AlignmentsDisplay.js +1 -1
- package/dist/LinearAlignmentsDisplay/components/AlignmentsDisplay.js.map +1 -1
- package/dist/LinearAlignmentsDisplay/models/configSchema.d.ts +2 -2
- package/dist/LinearAlignmentsDisplay/models/configSchema.js +23 -6
- package/dist/LinearAlignmentsDisplay/models/configSchema.js.map +1 -1
- package/dist/LinearAlignmentsDisplay/models/model.d.ts +77 -10
- package/dist/LinearAlignmentsDisplay/models/model.js +102 -9
- package/dist/LinearAlignmentsDisplay/models/model.js.map +1 -1
- package/dist/LinearPileupDisplay/configSchema.d.ts +4 -4
- package/dist/LinearPileupDisplay/configSchema.js +22 -5
- package/dist/LinearPileupDisplay/configSchema.js.map +1 -1
- package/dist/LinearPileupDisplay/index.d.ts +3 -0
- package/dist/LinearPileupDisplay/index.js +3 -0
- package/dist/LinearPileupDisplay/index.js.map +1 -1
- package/dist/LinearPileupDisplay/model.d.ts +100 -6
- package/dist/LinearPileupDisplay/model.js +611 -503
- package/dist/LinearPileupDisplay/model.js.map +1 -1
- package/dist/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
- package/dist/LinearSNPCoverageDisplay/models/configSchema.js +33 -4
- package/dist/LinearSNPCoverageDisplay/models/configSchema.js.map +1 -1
- package/dist/LinearSNPCoverageDisplay/models/model.d.ts +87 -4
- package/dist/LinearSNPCoverageDisplay/models/model.js +240 -159
- package/dist/LinearSNPCoverageDisplay/models/model.js.map +1 -1
- package/dist/PileupRPC/rpcMethods.d.ts +1 -1
- package/dist/PileupRPC/rpcMethods.js +12 -7
- package/dist/PileupRPC/rpcMethods.js.map +1 -1
- package/dist/PileupRenderer/PileupLayoutSession.d.ts +1 -1
- package/dist/PileupRenderer/PileupRenderer.d.ts +1 -1
- package/dist/PileupRenderer/PileupRenderer.js +37 -34
- package/dist/PileupRenderer/PileupRenderer.js.map +1 -1
- package/dist/PileupRenderer/configSchema.d.ts +2 -2
- package/dist/PileupRenderer/configSchema.js +37 -2
- package/dist/PileupRenderer/configSchema.js.map +1 -1
- package/dist/PileupRenderer/index.js +8 -6
- package/dist/PileupRenderer/index.js.map +1 -1
- package/dist/SNPCoverageAdapter/configSchema.d.ts +2 -3
- package/dist/SNPCoverageAdapter/configSchema.js +15 -4
- package/dist/SNPCoverageAdapter/configSchema.js.map +1 -1
- package/dist/SNPCoverageAdapter/index.d.ts +1 -2
- package/dist/SNPCoverageAdapter/index.js +17 -14
- package/dist/SNPCoverageAdapter/index.js.map +1 -1
- package/dist/SNPCoverageRenderer/configSchema.d.ts +2 -2
- package/dist/SNPCoverageRenderer/configSchema.js +21 -1
- package/dist/SNPCoverageRenderer/configSchema.js.map +1 -1
- package/dist/SNPCoverageRenderer/index.d.ts +0 -1
- package/dist/SNPCoverageRenderer/index.js +1 -4
- package/dist/SNPCoverageRenderer/index.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/esm/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +12 -13
- package/esm/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js.map +1 -1
- package/esm/AlignmentsTrack/index.d.ts +1 -1
- package/esm/AlignmentsTrack/index.js +16 -6
- package/esm/AlignmentsTrack/index.js.map +1 -1
- package/esm/BamAdapter/BamAdapter.d.ts +7 -3
- package/esm/BamAdapter/BamAdapter.js +36 -31
- package/esm/BamAdapter/BamAdapter.js.map +1 -1
- package/esm/BamAdapter/BamSlightlyLazyFeature.js +1 -0
- package/esm/BamAdapter/BamSlightlyLazyFeature.js.map +1 -1
- package/esm/BamAdapter/MismatchParser.d.ts +2 -2
- package/esm/BamAdapter/MismatchParser.js +4 -7
- package/esm/BamAdapter/MismatchParser.js.map +1 -1
- package/esm/BamAdapter/configSchema.d.ts +2 -2
- package/esm/BamAdapter/configSchema.js +27 -2
- package/esm/BamAdapter/configSchema.js.map +1 -1
- package/esm/BamAdapter/index.js +7 -5
- package/esm/BamAdapter/index.js.map +1 -1
- package/esm/CramAdapter/CramAdapter.d.ts +13 -7
- package/esm/CramAdapter/CramAdapter.js +56 -61
- package/esm/CramAdapter/CramAdapter.js.map +1 -1
- package/esm/CramAdapter/CramSlightlyLazyFeature.d.ts +15 -23
- package/esm/CramAdapter/CramSlightlyLazyFeature.js +10 -217
- package/esm/CramAdapter/CramSlightlyLazyFeature.js.map +1 -1
- package/esm/CramAdapter/CramTestAdapters.d.ts +1 -1
- package/esm/CramAdapter/CramTestAdapters.js +1 -1
- package/esm/CramAdapter/CramTestAdapters.js.map +1 -1
- package/esm/CramAdapter/configSchema.d.ts +2 -3
- package/esm/CramAdapter/configSchema.js +44 -22
- package/esm/CramAdapter/configSchema.js.map +1 -1
- package/esm/CramAdapter/index.js +8 -6
- package/esm/CramAdapter/index.js.map +1 -1
- package/esm/CramAdapter/util.d.ts +18 -0
- package/esm/CramAdapter/util.js +236 -0
- package/esm/CramAdapter/util.js.map +1 -0
- package/esm/HtsgetBamAdapter/HtsgetBamAdapter.d.ts +5 -2
- package/esm/HtsgetBamAdapter/HtsgetBamAdapter.js +15 -20
- package/esm/HtsgetBamAdapter/HtsgetBamAdapter.js.map +1 -1
- package/esm/HtsgetBamAdapter/configSchema.d.ts +2 -2
- package/esm/HtsgetBamAdapter/configSchema.js +20 -3
- package/esm/HtsgetBamAdapter/configSchema.js.map +1 -1
- package/esm/LinearAlignmentsDisplay/components/AlignmentsDisplay.js +1 -1
- package/esm/LinearAlignmentsDisplay/components/AlignmentsDisplay.js.map +1 -1
- package/esm/LinearAlignmentsDisplay/models/configSchema.d.ts +2 -2
- package/esm/LinearAlignmentsDisplay/models/configSchema.js +23 -6
- package/esm/LinearAlignmentsDisplay/models/configSchema.js.map +1 -1
- package/esm/LinearAlignmentsDisplay/models/model.d.ts +77 -10
- package/esm/LinearAlignmentsDisplay/models/model.js +102 -9
- package/esm/LinearAlignmentsDisplay/models/model.js.map +1 -1
- package/esm/LinearPileupDisplay/configSchema.d.ts +4 -4
- package/esm/LinearPileupDisplay/configSchema.js +22 -5
- package/esm/LinearPileupDisplay/configSchema.js.map +1 -1
- package/esm/LinearPileupDisplay/index.d.ts +3 -0
- package/esm/LinearPileupDisplay/index.js +1 -0
- package/esm/LinearPileupDisplay/index.js.map +1 -1
- package/esm/LinearPileupDisplay/model.d.ts +100 -6
- package/esm/LinearPileupDisplay/model.js +611 -503
- package/esm/LinearPileupDisplay/model.js.map +1 -1
- package/esm/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
- package/esm/LinearSNPCoverageDisplay/models/configSchema.js +33 -4
- package/esm/LinearSNPCoverageDisplay/models/configSchema.js.map +1 -1
- package/esm/LinearSNPCoverageDisplay/models/model.d.ts +87 -4
- package/esm/LinearSNPCoverageDisplay/models/model.js +240 -159
- package/esm/LinearSNPCoverageDisplay/models/model.js.map +1 -1
- package/esm/PileupRPC/rpcMethods.d.ts +1 -1
- package/esm/PileupRPC/rpcMethods.js +12 -7
- package/esm/PileupRPC/rpcMethods.js.map +1 -1
- package/esm/PileupRenderer/PileupLayoutSession.d.ts +1 -1
- package/esm/PileupRenderer/PileupRenderer.d.ts +1 -1
- package/esm/PileupRenderer/PileupRenderer.js +37 -34
- package/esm/PileupRenderer/PileupRenderer.js.map +1 -1
- package/esm/PileupRenderer/configSchema.d.ts +2 -2
- package/esm/PileupRenderer/configSchema.js +37 -2
- package/esm/PileupRenderer/configSchema.js.map +1 -1
- package/esm/PileupRenderer/index.js +8 -6
- package/esm/PileupRenderer/index.js.map +1 -1
- package/esm/SNPCoverageAdapter/configSchema.d.ts +2 -3
- package/esm/SNPCoverageAdapter/configSchema.js +15 -4
- package/esm/SNPCoverageAdapter/configSchema.js.map +1 -1
- package/esm/SNPCoverageAdapter/index.d.ts +1 -2
- package/esm/SNPCoverageAdapter/index.js +17 -15
- package/esm/SNPCoverageAdapter/index.js.map +1 -1
- package/esm/SNPCoverageRenderer/configSchema.d.ts +2 -2
- package/esm/SNPCoverageRenderer/configSchema.js +21 -1
- package/esm/SNPCoverageRenderer/configSchema.js.map +1 -1
- package/esm/SNPCoverageRenderer/index.d.ts +0 -1
- package/esm/SNPCoverageRenderer/index.js +1 -3
- package/esm/SNPCoverageRenderer/index.js.map +1 -1
- package/esm/index.d.ts +3 -2
- package/esm/index.js +2 -2
- package/esm/index.js.map +1 -1
- package/package.json +4 -3
- package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +17 -16
- package/src/AlignmentsFeatureDetail/__snapshots__/index.test.js.snap +41 -513
- package/src/AlignmentsFeatureDetail/index.test.js +6 -4
- package/src/AlignmentsTrack/index.ts +18 -12
- package/src/BamAdapter/BamAdapter.ts +42 -41
- package/src/BamAdapter/BamSlightlyLazyFeature.ts +2 -1
- package/src/BamAdapter/MismatchParser.test.ts +21 -12
- package/src/BamAdapter/MismatchParser.ts +7 -10
- package/src/BamAdapter/__snapshots__/BamAdapter.test.ts.snap +135 -135
- package/src/BamAdapter/configSchema.ts +57 -29
- package/src/BamAdapter/index.ts +7 -8
- package/src/CombinationTest.test.ts +107 -0
- package/src/CramAdapter/CramAdapter.test.ts +1 -2
- package/src/CramAdapter/CramAdapter.ts +83 -84
- package/src/CramAdapter/CramSlightlyLazyFeature.ts +18 -218
- package/src/CramAdapter/CramTestAdapters.ts +1 -1
- package/src/CramAdapter/__snapshots__/CramAdapter.test.ts.snap +31 -31
- package/src/CramAdapter/__snapshots__/util.test.ts.snap +14 -0
- package/src/CramAdapter/configSchema.ts +54 -30
- package/src/CramAdapter/index.ts +8 -9
- package/src/CramAdapter/util.test.ts +26 -0
- package/src/CramAdapter/util.ts +251 -0
- package/src/HtsgetBamAdapter/HtsgetBamAdapter.ts +14 -21
- package/src/HtsgetBamAdapter/configSchema.ts +36 -19
- package/src/LinearAlignmentsDisplay/components/AlignmentsDisplay.tsx +3 -1
- package/src/LinearAlignmentsDisplay/models/configSchema.ts +23 -10
- package/src/LinearAlignmentsDisplay/models/model.tsx +107 -11
- package/src/LinearPileupDisplay/configSchema.ts +25 -9
- package/src/LinearPileupDisplay/index.ts +5 -0
- package/src/LinearPileupDisplay/model.ts +151 -34
- package/src/LinearSNPCoverageDisplay/models/configSchema.ts +36 -9
- package/src/LinearSNPCoverageDisplay/models/model.ts +83 -4
- package/src/PileupRPC/rpcMethods.ts +15 -20
- package/src/PileupRenderer/{PileupRenderer.tsx → PileupRenderer.ts} +53 -63
- package/src/PileupRenderer/configSchema.ts +39 -2
- package/src/PileupRenderer/index.ts +8 -9
- package/src/SNPCoverageAdapter/configSchema.ts +21 -12
- package/src/SNPCoverageAdapter/index.ts +17 -18
- package/src/SNPCoverageRenderer/configSchema.ts +23 -1
- package/src/SNPCoverageRenderer/index.ts +1 -8
- package/src/__snapshots__/index.test.ts.snap +1 -1
- package/src/index.ts +11 -4
- package/src/declare.d.ts +0 -1
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
} from '@jbrowse/core/configuration'
|
|
9
9
|
import { linearWiggleDisplayModelFactory } from '@jbrowse/plugin-wiggle'
|
|
10
10
|
import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
11
|
-
|
|
12
11
|
import PluginManager from '@jbrowse/core/PluginManager'
|
|
13
12
|
import { getContainingView } from '@jbrowse/core/util'
|
|
14
13
|
|
|
@@ -21,19 +20,38 @@ const rendererTypes = new Map([['snpcoverage', 'SNPCoverageRenderer']])
|
|
|
21
20
|
|
|
22
21
|
type LGV = LinearGenomeViewModel
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* #stateModel LinearSNPCoverageDisplay
|
|
25
|
+
* extends `LinearWiggleDisplay`
|
|
26
|
+
*/
|
|
27
|
+
function stateModelFactory(
|
|
25
28
|
pluginManager: PluginManager,
|
|
26
29
|
configSchema: AnyConfigurationSchemaType,
|
|
27
|
-
)
|
|
28
|
-
types
|
|
30
|
+
) {
|
|
31
|
+
return types
|
|
29
32
|
.compose(
|
|
30
33
|
'LinearSNPCoverageDisplay',
|
|
31
34
|
linearWiggleDisplayModelFactory(pluginManager, configSchema),
|
|
32
35
|
types.model({
|
|
36
|
+
/**
|
|
37
|
+
* #property
|
|
38
|
+
*/
|
|
33
39
|
type: types.literal('LinearSNPCoverageDisplay'),
|
|
40
|
+
/**
|
|
41
|
+
* #property
|
|
42
|
+
*/
|
|
34
43
|
drawInterbaseCounts: types.maybe(types.boolean),
|
|
44
|
+
/**
|
|
45
|
+
* #property
|
|
46
|
+
*/
|
|
35
47
|
drawIndicators: types.maybe(types.boolean),
|
|
48
|
+
/**
|
|
49
|
+
* #property
|
|
50
|
+
*/
|
|
36
51
|
drawArcs: types.maybe(types.boolean),
|
|
52
|
+
/**
|
|
53
|
+
* #property
|
|
54
|
+
*/
|
|
37
55
|
filterBy: types.optional(
|
|
38
56
|
types.model({
|
|
39
57
|
flagInclude: types.optional(types.number, 0),
|
|
@@ -45,6 +63,9 @@ const stateModelFactory = (
|
|
|
45
63
|
}),
|
|
46
64
|
{},
|
|
47
65
|
),
|
|
66
|
+
/**
|
|
67
|
+
* #property
|
|
68
|
+
*/
|
|
48
69
|
colorBy: types.maybe(
|
|
49
70
|
types.model({
|
|
50
71
|
type: types.string,
|
|
@@ -57,9 +78,15 @@ const stateModelFactory = (
|
|
|
57
78
|
modificationTagMap: observable.map({}),
|
|
58
79
|
}))
|
|
59
80
|
.actions(self => ({
|
|
81
|
+
/**
|
|
82
|
+
* #action
|
|
83
|
+
*/
|
|
60
84
|
setConfig(configuration: AnyConfigurationModel) {
|
|
61
85
|
self.configuration = configuration
|
|
62
86
|
},
|
|
87
|
+
/**
|
|
88
|
+
* #action
|
|
89
|
+
*/
|
|
63
90
|
setFilterBy(filter: {
|
|
64
91
|
flagInclude: number
|
|
65
92
|
flagExclude: number
|
|
@@ -68,10 +95,16 @@ const stateModelFactory = (
|
|
|
68
95
|
}) {
|
|
69
96
|
self.filterBy = cast(filter)
|
|
70
97
|
},
|
|
98
|
+
/**
|
|
99
|
+
* #action
|
|
100
|
+
*/
|
|
71
101
|
setColorBy(colorBy?: { type: string; tag?: string }) {
|
|
72
102
|
self.colorBy = cast(colorBy)
|
|
73
103
|
},
|
|
74
104
|
|
|
105
|
+
/**
|
|
106
|
+
* #action
|
|
107
|
+
*/
|
|
75
108
|
updateModificationColorMap(uniqueModifications: string[]) {
|
|
76
109
|
const colorPalette = ['red', 'blue', 'green', 'orange', 'purple']
|
|
77
110
|
let i = 0
|
|
@@ -87,6 +120,9 @@ const stateModelFactory = (
|
|
|
87
120
|
.views(self => {
|
|
88
121
|
const { renderProps: superRenderProps } = self
|
|
89
122
|
return {
|
|
123
|
+
/**
|
|
124
|
+
* #getter
|
|
125
|
+
*/
|
|
90
126
|
get rendererConfig() {
|
|
91
127
|
const configBlob =
|
|
92
128
|
getConf(self, ['renderers', self.rendererTypeName]) || {}
|
|
@@ -102,17 +138,26 @@ const stateModelFactory = (
|
|
|
102
138
|
getEnv(self),
|
|
103
139
|
)
|
|
104
140
|
},
|
|
141
|
+
/**
|
|
142
|
+
* #getter
|
|
143
|
+
*/
|
|
105
144
|
get drawArcsSetting() {
|
|
106
145
|
return (
|
|
107
146
|
self.drawArcs ?? readConfObject(this.rendererConfig, 'drawArcs')
|
|
108
147
|
)
|
|
109
148
|
},
|
|
149
|
+
/**
|
|
150
|
+
* #getter
|
|
151
|
+
*/
|
|
110
152
|
get drawInterbaseCountsSetting() {
|
|
111
153
|
return (
|
|
112
154
|
self.drawInterbaseCounts ??
|
|
113
155
|
readConfObject(this.rendererConfig, 'drawInterbaseCounts')
|
|
114
156
|
)
|
|
115
157
|
},
|
|
158
|
+
/**
|
|
159
|
+
* #getter
|
|
160
|
+
*/
|
|
116
161
|
get drawIndicatorsSetting() {
|
|
117
162
|
return (
|
|
118
163
|
self.drawIndicators ??
|
|
@@ -120,6 +165,9 @@ const stateModelFactory = (
|
|
|
120
165
|
)
|
|
121
166
|
},
|
|
122
167
|
|
|
168
|
+
/**
|
|
169
|
+
* #getter
|
|
170
|
+
*/
|
|
123
171
|
get modificationsReady() {
|
|
124
172
|
return self.colorBy?.type === 'modifications'
|
|
125
173
|
? Object.keys(JSON.parse(JSON.stringify(self.modificationTagMap)))
|
|
@@ -127,6 +175,9 @@ const stateModelFactory = (
|
|
|
127
175
|
: true
|
|
128
176
|
},
|
|
129
177
|
|
|
178
|
+
/**
|
|
179
|
+
* #method
|
|
180
|
+
*/
|
|
130
181
|
renderProps() {
|
|
131
182
|
const superProps = superRenderProps()
|
|
132
183
|
const { colorBy, filterBy, modificationTagMap } = self
|
|
@@ -144,12 +195,21 @@ const stateModelFactory = (
|
|
|
144
195
|
}
|
|
145
196
|
})
|
|
146
197
|
.actions(self => ({
|
|
198
|
+
/**
|
|
199
|
+
* #action
|
|
200
|
+
*/
|
|
147
201
|
toggleDrawIndicators() {
|
|
148
202
|
self.drawIndicators = !self.drawIndicatorsSetting
|
|
149
203
|
},
|
|
204
|
+
/**
|
|
205
|
+
* #action
|
|
206
|
+
*/
|
|
150
207
|
toggleDrawInterbaseCounts() {
|
|
151
208
|
self.drawInterbaseCounts = !self.drawInterbaseCountsSetting
|
|
152
209
|
},
|
|
210
|
+
/**
|
|
211
|
+
* #action
|
|
212
|
+
*/
|
|
153
213
|
toggleDrawArcs() {
|
|
154
214
|
self.drawArcs = !self.drawArcsSetting
|
|
155
215
|
},
|
|
@@ -195,10 +255,16 @@ const stateModelFactory = (
|
|
|
195
255
|
.views(self => {
|
|
196
256
|
const { trackMenuItems: superTrackMenuItems } = self
|
|
197
257
|
return {
|
|
258
|
+
/**
|
|
259
|
+
* #getter
|
|
260
|
+
*/
|
|
198
261
|
get TooltipComponent() {
|
|
199
262
|
return Tooltip
|
|
200
263
|
},
|
|
201
264
|
|
|
265
|
+
/**
|
|
266
|
+
* #getter
|
|
267
|
+
*/
|
|
202
268
|
get adapterConfig() {
|
|
203
269
|
const subadapter = getConf(self.parentTrack, 'adapter')
|
|
204
270
|
return {
|
|
@@ -207,18 +273,30 @@ const stateModelFactory = (
|
|
|
207
273
|
}
|
|
208
274
|
},
|
|
209
275
|
|
|
276
|
+
/**
|
|
277
|
+
* #getter
|
|
278
|
+
*/
|
|
210
279
|
get rendererTypeName() {
|
|
211
280
|
return rendererTypes.get('snpcoverage')
|
|
212
281
|
},
|
|
213
282
|
|
|
283
|
+
/**
|
|
284
|
+
* #getter
|
|
285
|
+
*/
|
|
214
286
|
get needsScalebar() {
|
|
215
287
|
return true
|
|
216
288
|
},
|
|
217
289
|
|
|
290
|
+
/**
|
|
291
|
+
* #method
|
|
292
|
+
*/
|
|
218
293
|
contextMenuItems() {
|
|
219
294
|
return []
|
|
220
295
|
},
|
|
221
296
|
|
|
297
|
+
/**
|
|
298
|
+
* #method
|
|
299
|
+
*/
|
|
222
300
|
trackMenuItems() {
|
|
223
301
|
return [
|
|
224
302
|
...superTrackMenuItems(),
|
|
@@ -244,6 +322,7 @@ const stateModelFactory = (
|
|
|
244
322
|
},
|
|
245
323
|
}
|
|
246
324
|
})
|
|
325
|
+
}
|
|
247
326
|
|
|
248
327
|
export type SNPCoverageDisplayModel = ReturnType<typeof stateModelFactory>
|
|
249
328
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache'
|
|
2
1
|
import RpcMethodType from '@jbrowse/core/pluggableElementTypes/RpcMethodType'
|
|
2
|
+
import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache'
|
|
3
|
+
import { renameRegionsIfNeeded, Region } from '@jbrowse/core/util'
|
|
3
4
|
import { RenderArgs } from '@jbrowse/core/rpc/coreRpcMethods'
|
|
4
|
-
import { renameRegionsIfNeeded } from '@jbrowse/core/util'
|
|
5
|
-
import { Region } from '@jbrowse/core/util/types'
|
|
6
5
|
import { RemoteAbortSignal } from '@jbrowse/core/rpc/remoteAbortSignals'
|
|
7
|
-
import { toArray } from 'rxjs/operators'
|
|
8
6
|
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
7
|
+
import { toArray } from 'rxjs/operators'
|
|
8
|
+
|
|
9
|
+
// locals
|
|
9
10
|
import { getTagAlt } from '../util'
|
|
10
11
|
import { getModificationTypes } from '../BamAdapter/MismatchParser'
|
|
11
12
|
|
|
@@ -41,14 +42,11 @@ export class PileupGetGlobalValueForTag extends RpcMethodType {
|
|
|
41
42
|
},
|
|
42
43
|
rpcDriverClassName: string,
|
|
43
44
|
) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
const dataAdapter = (
|
|
50
|
-
await getAdapter(this.pluginManager, sessionId, adapterConfig)
|
|
51
|
-
).dataAdapter as BaseFeatureDataAdapter
|
|
45
|
+
const pm = this.pluginManager
|
|
46
|
+
const deArgs = await this.deserializeArguments(args, rpcDriverClassName)
|
|
47
|
+
const { adapterConfig, sessionId, regions, tag } = deArgs
|
|
48
|
+
const dataAdapter = (await getAdapter(pm, sessionId, adapterConfig))
|
|
49
|
+
.dataAdapter as BaseFeatureDataAdapter
|
|
52
50
|
|
|
53
51
|
const features = dataAdapter.getFeaturesInMultipleRegions(regions)
|
|
54
52
|
const featuresArray = await features.pipe(toArray()).toPromise()
|
|
@@ -96,14 +94,11 @@ export class PileupGetVisibleModifications extends RpcMethodType {
|
|
|
96
94
|
},
|
|
97
95
|
rpcDriverClassName: string,
|
|
98
96
|
) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
const dataAdapter = (
|
|
105
|
-
await getAdapter(this.pluginManager, sessionId, adapterConfig)
|
|
106
|
-
).dataAdapter as BaseFeatureDataAdapter
|
|
97
|
+
const pm = this.pluginManager
|
|
98
|
+
const deArgs = await this.deserializeArguments(args, rpcDriverClassName)
|
|
99
|
+
const { adapterConfig, sessionId, regions } = deArgs
|
|
100
|
+
const dataAdapter = (await getAdapter(pm, sessionId, adapterConfig))
|
|
101
|
+
.dataAdapter as BaseFeatureDataAdapter
|
|
107
102
|
|
|
108
103
|
const features = dataAdapter.getFeaturesInMultipleRegions(regions)
|
|
109
104
|
const featuresArray = await features.pipe(toArray()).toPromise()
|
|
@@ -150,7 +150,7 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
150
150
|
|
|
151
151
|
// get width and height of chars the height is an approximation: width
|
|
152
152
|
// letter M is approximately the height
|
|
153
|
-
getCharWidthHeight(
|
|
153
|
+
getCharWidthHeight() {
|
|
154
154
|
const charWidth = measureText('A')
|
|
155
155
|
const charHeight = measureText('M') - 2
|
|
156
156
|
return { charWidth, charHeight }
|
|
@@ -178,9 +178,9 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
178
178
|
|
|
179
179
|
// Expand the start and end of feature when softclipping enabled
|
|
180
180
|
if (showSoftClip) {
|
|
181
|
-
const mismatches = feature.get('mismatches') as Mismatch[]
|
|
181
|
+
const mismatches = feature.get('mismatches') as Mismatch[] | undefined
|
|
182
182
|
const seq = feature.get('seq') as string
|
|
183
|
-
if (seq) {
|
|
183
|
+
if (seq && mismatches) {
|
|
184
184
|
for (let i = 0; i < mismatches.length; i += 1) {
|
|
185
185
|
const { type, start, cliplen = 0 } = mismatches[i]
|
|
186
186
|
if (type === 'softclip') {
|
|
@@ -237,11 +237,7 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
237
237
|
|
|
238
238
|
const maxClippingSize = readConfObject(config, 'maxClippingSize')
|
|
239
239
|
const { start, end } = region
|
|
240
|
-
const
|
|
241
|
-
const bpExpansion = Math.max(
|
|
242
|
-
len,
|
|
243
|
-
showSoftClip ? Math.round(maxClippingSize) : 0,
|
|
244
|
-
)
|
|
240
|
+
const bpExpansion = showSoftClip ? Math.round(maxClippingSize) : 0
|
|
245
241
|
|
|
246
242
|
return {
|
|
247
243
|
// xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
|
|
@@ -853,7 +849,7 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
853
849
|
|
|
854
850
|
const pxPerBp = Math.min(1 / bpPerPx, 2)
|
|
855
851
|
const w = Math.max(minSubfeatureWidth, pxPerBp)
|
|
856
|
-
const mismatches
|
|
852
|
+
const mismatches = feature.get('mismatches') as Mismatch[] | undefined
|
|
857
853
|
const heightLim = charHeight - 2
|
|
858
854
|
|
|
859
855
|
// extraHorizontallyFlippedOffset is used to draw interbase items, which
|
|
@@ -862,6 +858,10 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
862
858
|
? 1 / bpPerPx + 1
|
|
863
859
|
: -1
|
|
864
860
|
|
|
861
|
+
if (!mismatches) {
|
|
862
|
+
return
|
|
863
|
+
}
|
|
864
|
+
|
|
865
865
|
// two pass rendering: first pass, draw all the mismatches except wide
|
|
866
866
|
// insertion markers
|
|
867
867
|
for (let i = 0; i < mismatches.length; i += 1) {
|
|
@@ -1056,9 +1056,9 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
1056
1056
|
const { regions, bpPerPx } = renderArgs
|
|
1057
1057
|
const [region] = regions
|
|
1058
1058
|
const minFeatWidth = readConfObject(config, 'minSubfeatureWidth')
|
|
1059
|
-
const mismatches
|
|
1059
|
+
const mismatches = feature.get('mismatches') as Mismatch[] | undefined
|
|
1060
1060
|
const seq = feature.get('seq')
|
|
1061
|
-
const { charWidth, charHeight } = this.getCharWidthHeight(
|
|
1061
|
+
const { charWidth, charHeight } = this.getCharWidthHeight()
|
|
1062
1062
|
const { bases } = theme.palette
|
|
1063
1063
|
const colorForBase: { [key: string]: string } = {
|
|
1064
1064
|
A: bases.A.main,
|
|
@@ -1069,59 +1069,49 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
1069
1069
|
}
|
|
1070
1070
|
|
|
1071
1071
|
// Display all bases softclipped off in lightened colors
|
|
1072
|
-
if (seq) {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
const [softClipLeftPx, softClipRightPx] = bpSpanPx(
|
|
1091
|
-
softClipStart + k,
|
|
1092
|
-
softClipStart + k + 1,
|
|
1093
|
-
region,
|
|
1094
|
-
bpPerPx,
|
|
1095
|
-
)
|
|
1096
|
-
const softClipWidthPx = Math.max(
|
|
1097
|
-
minFeatWidth,
|
|
1098
|
-
Math.abs(softClipLeftPx - softClipRightPx),
|
|
1099
|
-
)
|
|
1072
|
+
if (!(seq && mismatches)) {
|
|
1073
|
+
return
|
|
1074
|
+
}
|
|
1075
|
+
mismatches
|
|
1076
|
+
.filter(mismatch => mismatch.type === 'softclip')
|
|
1077
|
+
.forEach(mismatch => {
|
|
1078
|
+
const len = mismatch.cliplen || 0
|
|
1079
|
+
const s = feature.get('start')
|
|
1080
|
+
const start = mismatch.start === 0 ? s - len : s + mismatch.start
|
|
1081
|
+
|
|
1082
|
+
for (let k = 0; k < len; k += 1) {
|
|
1083
|
+
const base = seq.charAt(k + mismatch.start)
|
|
1084
|
+
|
|
1085
|
+
// If softclip length+start is longer than sequence, no need to
|
|
1086
|
+
// continue showing base
|
|
1087
|
+
if (!base) {
|
|
1088
|
+
return
|
|
1089
|
+
}
|
|
1100
1090
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1091
|
+
const [leftPx, rightPx] = bpSpanPx(
|
|
1092
|
+
start + k,
|
|
1093
|
+
start + k + 1,
|
|
1094
|
+
region,
|
|
1095
|
+
bpPerPx,
|
|
1096
|
+
)
|
|
1097
|
+
const widthPx = Math.max(minFeatWidth, rightPx - leftPx)
|
|
1098
|
+
|
|
1099
|
+
// Black accounts for IUPAC ambiguity code bases such as N that
|
|
1100
|
+
// show in soft clipping
|
|
1101
|
+
const baseColor = colorForBase[base] || '#000000'
|
|
1102
|
+
ctx.fillStyle = baseColor
|
|
1103
|
+
fillRect(ctx, leftPx, topPx, widthPx, heightPx, canvasWidth)
|
|
1113
1104
|
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
}
|
|
1105
|
+
if (widthPx >= charWidth && heightPx >= charHeight - 5) {
|
|
1106
|
+
ctx.fillStyle = theme.palette.getContrastText(baseColor)
|
|
1107
|
+
ctx.fillText(
|
|
1108
|
+
base,
|
|
1109
|
+
leftPx + (widthPx - charWidth) / 2 + 1,
|
|
1110
|
+
topPx + heightPx,
|
|
1111
|
+
)
|
|
1122
1112
|
}
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1113
|
+
}
|
|
1114
|
+
})
|
|
1125
1115
|
}
|
|
1126
1116
|
|
|
1127
1117
|
makeImageData({
|
|
@@ -1161,7 +1151,7 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
1161
1151
|
}
|
|
1162
1152
|
ctx.font = 'bold 10px Courier New,monospace'
|
|
1163
1153
|
|
|
1164
|
-
const { charWidth, charHeight } = this.getCharWidthHeight(
|
|
1154
|
+
const { charWidth, charHeight } = this.getCharWidthHeight()
|
|
1165
1155
|
const drawSNPsMuted = shouldDrawSNPsMuted(colorBy?.type)
|
|
1166
1156
|
const drawIndels = shouldDrawIndels(colorBy?.type)
|
|
1167
1157
|
for (let i = 0; i < layoutRecords.length; i++) {
|
|
@@ -1271,13 +1261,13 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
1271
1261
|
const features = await this.getFeatures(renderProps)
|
|
1272
1262
|
const layout = this.createLayoutInWorker(renderProps)
|
|
1273
1263
|
const { regions, bpPerPx } = renderProps
|
|
1264
|
+
const [region] = regions
|
|
1274
1265
|
|
|
1275
1266
|
const layoutRecords = this.layoutFeats({
|
|
1276
1267
|
...renderProps,
|
|
1277
1268
|
features,
|
|
1278
1269
|
layout,
|
|
1279
1270
|
})
|
|
1280
|
-
const [region] = regions
|
|
1281
1271
|
|
|
1282
1272
|
// only need reference sequence if there are features and only for some
|
|
1283
1273
|
// cases
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import { ConfigurationSchema } from '@jbrowse/core/configuration'
|
|
2
2
|
import { types } from 'mobx-state-tree'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* #config PileupRenderer
|
|
6
|
+
*/
|
|
7
|
+
function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
8
|
+
|
|
9
|
+
const PileupRenderer = ConfigurationSchema(
|
|
5
10
|
'PileupRenderer',
|
|
6
11
|
{
|
|
7
|
-
|
|
12
|
+
/**
|
|
13
|
+
* #slot
|
|
14
|
+
* default magenta here is used to detect the user has not customized this
|
|
15
|
+
*/
|
|
8
16
|
color: {
|
|
9
17
|
type: 'color',
|
|
10
18
|
description: 'the color of each feature in a pileup alignment',
|
|
11
19
|
defaultValue: '#f0f',
|
|
12
20
|
contextVariable: ['feature'],
|
|
13
21
|
},
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* #slot
|
|
25
|
+
*/
|
|
14
26
|
orientationType: {
|
|
15
27
|
type: 'stringEnum',
|
|
16
28
|
model: types.enumeration('orientationType', ['fr', 'rf', 'ff']),
|
|
@@ -18,6 +30,9 @@ export default ConfigurationSchema(
|
|
|
18
30
|
description:
|
|
19
31
|
'read sequencer orienation. fr is normal "reads pointing at each other ---> <--- while some other sequencers can use other options',
|
|
20
32
|
},
|
|
33
|
+
/**
|
|
34
|
+
* #slot
|
|
35
|
+
*/
|
|
21
36
|
displayMode: {
|
|
22
37
|
type: 'stringEnum',
|
|
23
38
|
model: types.enumeration('displayMode', [
|
|
@@ -28,39 +43,60 @@ export default ConfigurationSchema(
|
|
|
28
43
|
description: 'Alternative display modes',
|
|
29
44
|
defaultValue: 'normal',
|
|
30
45
|
},
|
|
46
|
+
/**
|
|
47
|
+
* #slot
|
|
48
|
+
*/
|
|
31
49
|
minSubfeatureWidth: {
|
|
32
50
|
type: 'number',
|
|
33
51
|
description:
|
|
34
52
|
'the minimum width in px for a pileup mismatch feature. use for increasing/decreasing mismatch marker widths when zoomed out, e.g. 0 or 1',
|
|
35
53
|
defaultValue: 0.7,
|
|
36
54
|
},
|
|
55
|
+
/**
|
|
56
|
+
* #slot
|
|
57
|
+
*/
|
|
37
58
|
maxHeight: {
|
|
38
59
|
type: 'integer',
|
|
39
60
|
description: 'the maximum height to be used in a pileup rendering',
|
|
40
61
|
defaultValue: 1200,
|
|
41
62
|
},
|
|
63
|
+
/**
|
|
64
|
+
* #slot
|
|
65
|
+
*/
|
|
42
66
|
maxClippingSize: {
|
|
43
67
|
type: 'integer',
|
|
44
68
|
description: 'the max clip size to be used in a pileup rendering',
|
|
45
69
|
defaultValue: 10000,
|
|
46
70
|
},
|
|
71
|
+
/**
|
|
72
|
+
* #slot
|
|
73
|
+
*/
|
|
47
74
|
height: {
|
|
48
75
|
type: 'number',
|
|
49
76
|
description: 'the height of each feature in a pileup alignment',
|
|
50
77
|
defaultValue: 7,
|
|
51
78
|
contextVariable: ['feature'],
|
|
52
79
|
},
|
|
80
|
+
/**
|
|
81
|
+
* #slot
|
|
82
|
+
*/
|
|
53
83
|
noSpacing: {
|
|
54
84
|
type: 'boolean',
|
|
55
85
|
description: 'remove spacing between features',
|
|
56
86
|
defaultValue: false,
|
|
57
87
|
},
|
|
88
|
+
/**
|
|
89
|
+
* #slot
|
|
90
|
+
*/
|
|
58
91
|
largeInsertionIndicatorScale: {
|
|
59
92
|
type: 'number',
|
|
60
93
|
description:
|
|
61
94
|
'scale at which to draw the large insertion indicators (bp/pixel)',
|
|
62
95
|
defaultValue: 10,
|
|
63
96
|
},
|
|
97
|
+
/**
|
|
98
|
+
* #slot
|
|
99
|
+
*/
|
|
64
100
|
mismatchAlpha: {
|
|
65
101
|
type: 'boolean',
|
|
66
102
|
defaultValue: false,
|
|
@@ -69,3 +105,4 @@ export default ConfigurationSchema(
|
|
|
69
105
|
},
|
|
70
106
|
{ explicitlyTyped: true },
|
|
71
107
|
)
|
|
108
|
+
export default PileupRenderer
|
|
@@ -4,13 +4,12 @@ import ReactComponent from './components/PileupRendering'
|
|
|
4
4
|
import configSchema from './configSchema'
|
|
5
5
|
|
|
6
6
|
export default function register(pluginManager: PluginManager) {
|
|
7
|
-
pluginManager.addRendererType(
|
|
8
|
-
(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
7
|
+
pluginManager.addRendererType(() => {
|
|
8
|
+
return new PileupRenderer({
|
|
9
|
+
name: 'PileupRenderer',
|
|
10
|
+
ReactComponent,
|
|
11
|
+
configSchema,
|
|
12
|
+
pluginManager,
|
|
13
|
+
})
|
|
14
|
+
})
|
|
16
15
|
}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { ConfigurationSchema } from '@jbrowse/core/configuration'
|
|
2
|
-
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
|
-
import { types } from 'mobx-state-tree'
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
/**
|
|
4
|
+
* #config SNPCoverageAdapter
|
|
5
|
+
*/
|
|
6
|
+
function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
7
|
+
|
|
8
|
+
const configSchema = ConfigurationSchema(
|
|
9
|
+
'SNPCoverageAdapter',
|
|
10
|
+
{
|
|
11
|
+
/**
|
|
12
|
+
* #slot
|
|
13
|
+
* normally refers to a BAM or CRAM adapter
|
|
14
|
+
*/
|
|
15
|
+
subadapter: {
|
|
16
|
+
type: 'frozen',
|
|
17
|
+
defaultValue: null,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{ explicitlyTyped: true },
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
export default configSchema
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import PluginManager from '@jbrowse/core/PluginManager'
|
|
2
2
|
import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'
|
|
3
|
-
import
|
|
3
|
+
import configSchema from './configSchema'
|
|
4
4
|
import { capabilities } from './SNPCoverageAdapter'
|
|
5
5
|
|
|
6
|
-
export default (pluginManager: PluginManager)
|
|
7
|
-
pluginManager.addAdapterType(
|
|
8
|
-
(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
)
|
|
6
|
+
export default function (pluginManager: PluginManager) {
|
|
7
|
+
pluginManager.addAdapterType(() => {
|
|
8
|
+
return new AdapterType({
|
|
9
|
+
name: 'SNPCoverageAdapter',
|
|
10
|
+
adapterMetadata: {
|
|
11
|
+
category: null,
|
|
12
|
+
displayName: null,
|
|
13
|
+
hiddenFromGUI: true,
|
|
14
|
+
description: null,
|
|
15
|
+
},
|
|
16
|
+
getAdapterClass: () =>
|
|
17
|
+
import('./SNPCoverageAdapter').then(r => r.default),
|
|
18
|
+
configSchema,
|
|
19
|
+
adapterCapabilities: capabilities,
|
|
20
|
+
})
|
|
21
|
+
})
|
|
23
22
|
}
|