@jbrowse/plugin-alignments 1.6.7 → 1.7.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/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +216 -0
- package/dist/AlignmentsFeatureDetail/index.js +63 -0
- package/dist/AlignmentsFeatureDetail/index.test.js +60 -0
- package/dist/AlignmentsTrack/index.js +37 -0
- package/dist/BamAdapter/BamAdapter.js +598 -0
- package/dist/BamAdapter/BamAdapter.test.js +177 -0
- package/dist/BamAdapter/BamSlightlyLazyFeature.d.ts +1 -10
- package/dist/BamAdapter/BamSlightlyLazyFeature.js +176 -0
- package/dist/BamAdapter/MismatchParser.d.ts +3 -5
- package/dist/BamAdapter/MismatchParser.js +384 -0
- package/dist/BamAdapter/MismatchParser.test.js +259 -0
- package/dist/BamAdapter/configSchema.js +48 -0
- package/dist/BamAdapter/index.js +36 -0
- package/dist/CramAdapter/CramAdapter.js +660 -0
- package/dist/CramAdapter/CramAdapter.test.js +138 -0
- package/dist/CramAdapter/CramSlightlyLazyFeature.d.ts +1 -2
- package/dist/CramAdapter/CramSlightlyLazyFeature.js +447 -0
- package/dist/CramAdapter/CramTestAdapters.js +234 -0
- package/dist/CramAdapter/configSchema.js +40 -0
- package/dist/CramAdapter/index.js +36 -0
- package/dist/HtsgetBamAdapter/HtsgetBamAdapter.js +97 -0
- package/dist/HtsgetBamAdapter/configSchema.js +31 -0
- package/dist/HtsgetBamAdapter/index.js +42 -0
- package/dist/LinearAlignmentsDisplay/components/AlignmentsDisplay.js +69 -0
- package/dist/LinearAlignmentsDisplay/index.js +31 -0
- package/dist/LinearAlignmentsDisplay/models/configSchema.js +25 -0
- package/dist/LinearAlignmentsDisplay/models/configSchema.test.js +83 -0
- package/dist/LinearAlignmentsDisplay/models/model.js +250 -0
- package/dist/LinearPileupDisplay/components/ColorByModifications.js +123 -0
- package/dist/LinearPileupDisplay/components/ColorByTag.js +98 -0
- package/dist/LinearPileupDisplay/components/FilterByTag.js +203 -0
- package/dist/LinearPileupDisplay/components/LinearPileupDisplayBlurb.js +32 -0
- package/dist/LinearPileupDisplay/components/SetFeatureHeight.js +99 -0
- package/dist/LinearPileupDisplay/components/SetMaxHeight.js +90 -0
- package/dist/LinearPileupDisplay/components/SortByTag.js +95 -0
- package/dist/LinearPileupDisplay/configSchema.js +47 -0
- package/dist/LinearPileupDisplay/configSchema.test.js +92 -0
- package/dist/LinearPileupDisplay/index.js +30 -0
- package/dist/LinearPileupDisplay/model.js +602 -0
- package/dist/LinearSNPCoverageDisplay/components/Tooltip.js +63 -0
- package/dist/LinearSNPCoverageDisplay/index.js +30 -0
- package/dist/LinearSNPCoverageDisplay/models/configSchema.js +57 -0
- package/dist/LinearSNPCoverageDisplay/models/configSchema.test.js +62 -0
- package/dist/LinearSNPCoverageDisplay/models/model.d.ts +2 -2
- package/dist/LinearSNPCoverageDisplay/models/model.js +237 -0
- package/dist/NestedFrequencyTable.js +152 -0
- package/dist/PileupRPC/rpcMethods.js +285 -0
- package/dist/PileupRenderer/PileupLayoutSession.js +79 -0
- package/dist/PileupRenderer/PileupRenderer.d.ts +20 -6
- package/dist/PileupRenderer/PileupRenderer.js +1220 -0
- package/dist/PileupRenderer/components/PileupRendering.js +270 -0
- package/dist/PileupRenderer/components/PileupRendering.test.js +36 -0
- package/dist/PileupRenderer/configSchema.js +72 -0
- package/dist/PileupRenderer/index.js +25 -0
- package/dist/PileupRenderer/sortUtil.js +112 -0
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +3 -11
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +606 -0
- package/dist/SNPCoverageAdapter/configSchema.js +22 -0
- package/dist/SNPCoverageAdapter/index.js +45 -0
- package/dist/SNPCoverageRenderer/SNPCoverageRenderer.js +296 -0
- package/dist/SNPCoverageRenderer/configSchema.js +40 -0
- package/dist/SNPCoverageRenderer/index.js +34 -0
- package/dist/declare.d.js +1 -0
- package/dist/index.js +154 -6
- package/dist/index.test.js +26 -0
- package/dist/plugin-alignments.cjs.development.js +591 -552
- package/dist/plugin-alignments.cjs.development.js.map +1 -1
- package/dist/plugin-alignments.cjs.production.min.js +1 -1
- package/dist/plugin-alignments.cjs.production.min.js.map +1 -1
- package/dist/plugin-alignments.esm.js +594 -555
- package/dist/plugin-alignments.esm.js.map +1 -1
- package/dist/shared.js +96 -0
- package/dist/util.d.ts +4 -0
- package/dist/util.js +135 -0
- package/package.json +5 -9
- package/src/BamAdapter/BamAdapter.ts +45 -15
- package/src/BamAdapter/BamSlightlyLazyFeature.ts +11 -79
- package/src/BamAdapter/MismatchParser.test.ts +53 -297
- package/src/BamAdapter/MismatchParser.ts +54 -116
- package/src/BamAdapter/configSchema.ts +0 -4
- package/src/CramAdapter/CramAdapter.ts +42 -15
- package/src/CramAdapter/CramSlightlyLazyFeature.ts +3 -10
- package/src/LinearPileupDisplay/components/ColorByModifications.tsx +76 -80
- package/src/LinearPileupDisplay/components/ColorByTag.tsx +24 -23
- package/src/LinearPileupDisplay/components/FilterByTag.tsx +73 -68
- package/src/LinearPileupDisplay/components/SetFeatureHeight.tsx +28 -26
- package/src/LinearPileupDisplay/components/SetMaxHeight.tsx +24 -13
- package/src/LinearPileupDisplay/components/SortByTag.tsx +29 -21
- package/src/LinearPileupDisplay/model.ts +8 -22
- package/src/LinearSNPCoverageDisplay/models/model.ts +6 -36
- package/src/PileupRenderer/PileupRenderer.tsx +178 -60
- package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +189 -244
- package/src/SNPCoverageRenderer/SNPCoverageRenderer.ts +12 -11
- package/src/util.ts +25 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.cigarToMismatches = cigarToMismatches;
|
|
9
|
+
exports.getMismatches = getMismatches;
|
|
10
|
+
exports.getModificationPositions = getModificationPositions;
|
|
11
|
+
exports.getModificationTypes = getModificationTypes;
|
|
12
|
+
exports.getNextRefPos = getNextRefPos;
|
|
13
|
+
exports.mdToMismatches = mdToMismatches;
|
|
14
|
+
exports.parseCigar = parseCigar;
|
|
15
|
+
|
|
16
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
17
|
+
|
|
18
|
+
var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray"));
|
|
19
|
+
|
|
20
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
21
|
+
|
|
22
|
+
var _util = require("@jbrowse/core/util");
|
|
23
|
+
|
|
24
|
+
var _marked = /*#__PURE__*/_regenerator["default"].mark(getNextRefPos);
|
|
25
|
+
|
|
26
|
+
var mdRegex = new RegExp(/(\d+|\^[a-z]+|[a-z])/gi);
|
|
27
|
+
|
|
28
|
+
function parseCigar(cigar) {
|
|
29
|
+
return (cigar || '').split(/([MIDNSHPX=])/);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function cigarToMismatches(ops, seq, ref, qual) {
|
|
33
|
+
var roffset = 0; // reference offset
|
|
34
|
+
|
|
35
|
+
var soffset = 0; // seq offset
|
|
36
|
+
|
|
37
|
+
var mismatches = [];
|
|
38
|
+
var hasRefAndSeq = ref && seq;
|
|
39
|
+
|
|
40
|
+
for (var i = 0; i < ops.length; i += 2) {
|
|
41
|
+
var len = +ops[i];
|
|
42
|
+
var op = ops[i + 1];
|
|
43
|
+
|
|
44
|
+
if (op === 'M' || op === '=' || op === 'E') {
|
|
45
|
+
if (hasRefAndSeq) {
|
|
46
|
+
for (var j = 0; j < len; j++) {
|
|
47
|
+
if ( // @ts-ignore in the full yarn build of the repo, this says that object is possibly undefined for some reason, ignored
|
|
48
|
+
seq[soffset + j].toUpperCase() !== ref[roffset + j].toUpperCase()) {
|
|
49
|
+
mismatches.push({
|
|
50
|
+
start: roffset + j,
|
|
51
|
+
type: 'mismatch',
|
|
52
|
+
base: seq[soffset + j],
|
|
53
|
+
length: 1
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
soffset += len;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (op === 'I') {
|
|
63
|
+
mismatches.push({
|
|
64
|
+
start: roffset,
|
|
65
|
+
type: 'insertion',
|
|
66
|
+
base: "".concat(len),
|
|
67
|
+
length: 0
|
|
68
|
+
});
|
|
69
|
+
soffset += len;
|
|
70
|
+
} else if (op === 'D') {
|
|
71
|
+
mismatches.push({
|
|
72
|
+
start: roffset,
|
|
73
|
+
type: 'deletion',
|
|
74
|
+
base: '*',
|
|
75
|
+
length: len
|
|
76
|
+
});
|
|
77
|
+
} else if (op === 'N') {
|
|
78
|
+
mismatches.push({
|
|
79
|
+
start: roffset,
|
|
80
|
+
type: 'skip',
|
|
81
|
+
base: 'N',
|
|
82
|
+
length: len
|
|
83
|
+
});
|
|
84
|
+
} else if (op === 'X') {
|
|
85
|
+
var r = seq.slice(soffset, soffset + len);
|
|
86
|
+
var q = (qual === null || qual === void 0 ? void 0 : qual.slice(soffset, soffset + len)) || [];
|
|
87
|
+
|
|
88
|
+
for (var _j = 0; _j < len; _j++) {
|
|
89
|
+
mismatches.push({
|
|
90
|
+
start: roffset + _j,
|
|
91
|
+
type: 'mismatch',
|
|
92
|
+
base: r[_j],
|
|
93
|
+
qual: q[_j],
|
|
94
|
+
length: 1
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
soffset += len;
|
|
99
|
+
} else if (op === 'H') {
|
|
100
|
+
mismatches.push({
|
|
101
|
+
start: roffset,
|
|
102
|
+
type: 'hardclip',
|
|
103
|
+
base: "H".concat(len),
|
|
104
|
+
cliplen: len,
|
|
105
|
+
length: 1
|
|
106
|
+
});
|
|
107
|
+
} else if (op === 'S') {
|
|
108
|
+
mismatches.push({
|
|
109
|
+
start: roffset,
|
|
110
|
+
type: 'softclip',
|
|
111
|
+
base: "S".concat(len),
|
|
112
|
+
cliplen: len,
|
|
113
|
+
length: 1
|
|
114
|
+
});
|
|
115
|
+
soffset += len;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (op !== 'I' && op !== 'S' && op !== 'H') {
|
|
119
|
+
roffset += len;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return mismatches;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* parse a SAM MD tag to find mismatching bases of the template versus the reference
|
|
127
|
+
* @returns array of mismatches and their positions
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
function mdToMismatches(mdstring, ops, cigarMismatches, seq, qual) {
|
|
132
|
+
var mismatchRecords = [];
|
|
133
|
+
var curr = {
|
|
134
|
+
start: 0,
|
|
135
|
+
base: '',
|
|
136
|
+
length: 0,
|
|
137
|
+
type: 'mismatch'
|
|
138
|
+
};
|
|
139
|
+
var skips = cigarMismatches.filter(function (cigar) {
|
|
140
|
+
return cigar.type === 'skip';
|
|
141
|
+
});
|
|
142
|
+
var lastCigar = 0;
|
|
143
|
+
var lastTemplateOffset = 0;
|
|
144
|
+
var lastRefOffset = 0;
|
|
145
|
+
var lastSkipPos = 0; // convert a position on the reference sequence to a position
|
|
146
|
+
// on the template sequence, taking into account hard and soft
|
|
147
|
+
// clipping of reads
|
|
148
|
+
|
|
149
|
+
function nextRecord() {
|
|
150
|
+
mismatchRecords.push(curr); // get a new mismatch record ready
|
|
151
|
+
|
|
152
|
+
curr = {
|
|
153
|
+
start: curr.start + curr.length,
|
|
154
|
+
length: 0,
|
|
155
|
+
base: '',
|
|
156
|
+
type: 'mismatch'
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function getTemplateCoordLocal(refCoord) {
|
|
161
|
+
var templateOffset = lastTemplateOffset;
|
|
162
|
+
var refOffset = lastRefOffset;
|
|
163
|
+
|
|
164
|
+
for (var i = lastCigar; i < ops.length && refOffset <= refCoord; i += 2, lastCigar = i) {
|
|
165
|
+
var len = +ops[i];
|
|
166
|
+
var op = ops[i + 1];
|
|
167
|
+
|
|
168
|
+
if (op === 'S' || op === 'I') {
|
|
169
|
+
templateOffset += len;
|
|
170
|
+
} else if (op === 'D' || op === 'P' || op === 'N') {
|
|
171
|
+
refOffset += len;
|
|
172
|
+
} else if (op !== 'H') {
|
|
173
|
+
templateOffset += len;
|
|
174
|
+
refOffset += len;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
lastTemplateOffset = templateOffset;
|
|
179
|
+
lastRefOffset = refOffset;
|
|
180
|
+
return templateOffset - (refOffset - refCoord);
|
|
181
|
+
} // now actually parse the MD string
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
var md = mdstring.match(mdRegex) || [];
|
|
185
|
+
|
|
186
|
+
for (var i = 0; i < md.length; i++) {
|
|
187
|
+
var token = md[i];
|
|
188
|
+
var num = +token;
|
|
189
|
+
|
|
190
|
+
if (!Number.isNaN(num)) {
|
|
191
|
+
curr.start += num;
|
|
192
|
+
} else if (token.startsWith('^')) {
|
|
193
|
+
curr.start += token.length - 1;
|
|
194
|
+
} else {
|
|
195
|
+
// mismatch
|
|
196
|
+
for (var j = 0; j < token.length; j += 1) {
|
|
197
|
+
curr.length = 1;
|
|
198
|
+
|
|
199
|
+
while (lastSkipPos < skips.length) {
|
|
200
|
+
var mismatch = skips[lastSkipPos];
|
|
201
|
+
|
|
202
|
+
if (curr.start >= mismatch.start) {
|
|
203
|
+
curr.start += mismatch.length;
|
|
204
|
+
lastSkipPos++;
|
|
205
|
+
} else {
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
var s = getTemplateCoordLocal(curr.start);
|
|
211
|
+
curr.base = seq[s] || 'X';
|
|
212
|
+
var qualScore = qual === null || qual === void 0 ? void 0 : qual[s];
|
|
213
|
+
|
|
214
|
+
if (qualScore) {
|
|
215
|
+
curr.qual = qualScore;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
curr.altbase = token;
|
|
219
|
+
nextRecord();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return mismatchRecords;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function getMismatches(cigar, md, seq, ref, qual) {
|
|
228
|
+
var mismatches = [];
|
|
229
|
+
var ops = parseCigar(cigar); // parse the CIGAR tag if it has one
|
|
230
|
+
|
|
231
|
+
if (cigar) {
|
|
232
|
+
mismatches = mismatches.concat(cigarToMismatches(ops, seq, ref, qual));
|
|
233
|
+
} // now let's look for CRAM or MD mismatches
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
if (md) {
|
|
237
|
+
mismatches = mismatches.concat(mdToMismatches(md, ops, mismatches, seq, qual));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return mismatches;
|
|
241
|
+
} // get relative reference sequence positions for positions given relative to
|
|
242
|
+
// the read sequence
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
function getNextRefPos(cigarOps, positions) {
|
|
246
|
+
var cigarIdx, readPos, refPos, i, pos, len, op;
|
|
247
|
+
return _regenerator["default"].wrap(function getNextRefPos$(_context) {
|
|
248
|
+
while (1) {
|
|
249
|
+
switch (_context.prev = _context.next) {
|
|
250
|
+
case 0:
|
|
251
|
+
cigarIdx = 0;
|
|
252
|
+
readPos = 0;
|
|
253
|
+
refPos = 0;
|
|
254
|
+
i = 0;
|
|
255
|
+
|
|
256
|
+
case 4:
|
|
257
|
+
if (!(i < positions.length)) {
|
|
258
|
+
_context.next = 12;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
pos = positions[i];
|
|
263
|
+
|
|
264
|
+
for (; cigarIdx < cigarOps.length && readPos < pos; cigarIdx += 2) {
|
|
265
|
+
len = +cigarOps[cigarIdx];
|
|
266
|
+
op = cigarOps[cigarIdx + 1];
|
|
267
|
+
|
|
268
|
+
if (op === 'S' || op === 'I') {
|
|
269
|
+
readPos += len;
|
|
270
|
+
} else if (op === 'D' || op === 'N') {
|
|
271
|
+
refPos += len;
|
|
272
|
+
} else if (op === 'M' || op === 'X' || op === '=') {
|
|
273
|
+
readPos += len;
|
|
274
|
+
refPos += len;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
_context.next = 9;
|
|
279
|
+
return positions[i] - readPos + refPos;
|
|
280
|
+
|
|
281
|
+
case 9:
|
|
282
|
+
i++;
|
|
283
|
+
_context.next = 4;
|
|
284
|
+
break;
|
|
285
|
+
|
|
286
|
+
case 12:
|
|
287
|
+
case "end":
|
|
288
|
+
return _context.stop();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}, _marked);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function getModificationPositions(mm, fseq, fstrand) {
|
|
295
|
+
var seq = fstrand === -1 ? (0, _util.revcom)(fseq) : fseq;
|
|
296
|
+
return mm.split(';').filter(function (mod) {
|
|
297
|
+
return !!mod;
|
|
298
|
+
}).map(function (mod) {
|
|
299
|
+
var _mod$split = mod.split(','),
|
|
300
|
+
_mod$split2 = (0, _toArray2["default"])(_mod$split),
|
|
301
|
+
basemod = _mod$split2[0],
|
|
302
|
+
skips = _mod$split2.slice(1); // regexes based on parse_mm.pl from hts-specs
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
var matches = basemod.match(/([A-Z])([-+])([^,.?]+)([.?])?/);
|
|
306
|
+
|
|
307
|
+
if (!matches) {
|
|
308
|
+
throw new Error('bad format for MM tag');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
var _matches = (0, _slicedToArray2["default"])(matches, 4),
|
|
312
|
+
base = _matches[1],
|
|
313
|
+
strand = _matches[2],
|
|
314
|
+
typestr = _matches[3]; // can be a multi e.g. C+mh for both meth (m) and hydroxymeth (h) so
|
|
315
|
+
// split, and they can also be chemical codes (ChEBI) e.g. C+16061
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
var types = typestr.split(/(\d+|.)/).filter(function (f) {
|
|
319
|
+
return !!f;
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
if (strand === '-') {
|
|
323
|
+
console.warn('unsupported negative strand modifications'); // make sure to return a somewhat matching type even in this case
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
type: 'unsupported',
|
|
327
|
+
positions: []
|
|
328
|
+
};
|
|
329
|
+
} // this logic also based on parse_mm.pl from hts-specs is that in the
|
|
330
|
+
// sequence of the read, if we have a modification type e.g. C+m;2 and a
|
|
331
|
+
// sequence ACGTACGTAC we skip the two instances of C and go to the last
|
|
332
|
+
// C
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
return types.map(function (type) {
|
|
336
|
+
var i = 0;
|
|
337
|
+
return {
|
|
338
|
+
type: type,
|
|
339
|
+
positions: skips.map(function (score) {
|
|
340
|
+
return +score;
|
|
341
|
+
}).map(function (delta) {
|
|
342
|
+
do {
|
|
343
|
+
if (base === 'N' || base === seq[i]) {
|
|
344
|
+
delta--;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
i++;
|
|
348
|
+
} while (delta >= 0 && i < seq.length);
|
|
349
|
+
|
|
350
|
+
var temp = i - 1;
|
|
351
|
+
return fstrand === -1 ? seq.length - 1 - temp : temp;
|
|
352
|
+
}).sort(function (a, b) {
|
|
353
|
+
return a - b;
|
|
354
|
+
})
|
|
355
|
+
};
|
|
356
|
+
});
|
|
357
|
+
}).flat();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function getModificationTypes(mm) {
|
|
361
|
+
var mods = mm.split(';');
|
|
362
|
+
return mods.filter(function (mod) {
|
|
363
|
+
return !!mod;
|
|
364
|
+
}).map(function (mod) {
|
|
365
|
+
var _mod$split3 = mod.split(','),
|
|
366
|
+
_mod$split4 = (0, _slicedToArray2["default"])(_mod$split3, 1),
|
|
367
|
+
basemod = _mod$split4[0];
|
|
368
|
+
|
|
369
|
+
var matches = basemod.match(/([A-Z])([-+])([^,]+)/);
|
|
370
|
+
|
|
371
|
+
if (!matches) {
|
|
372
|
+
throw new Error('bad format for MM tag');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
var _matches2 = (0, _slicedToArray2["default"])(matches, 4),
|
|
376
|
+
typestr = _matches2[3]; // can be a multi e.g. C+mh for both meth (m) and hydroxymeth (h) so
|
|
377
|
+
// split, and they can also be chemical codes (ChEBI) e.g. C+16061
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
return typestr.split(/(\d+|.)/).filter(function (f) {
|
|
381
|
+
return !!f;
|
|
382
|
+
});
|
|
383
|
+
}).flat();
|
|
384
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray"));
|
|
6
|
+
|
|
7
|
+
var _MismatchParser = require("./MismatchParser");
|
|
8
|
+
|
|
9
|
+
var seq = 'AAAAAAAAAACAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCCGGGGGGGGGGGGGGGGGGGGGGGGGTTTTTTTTTTTTTTTTTTTTTTTTT'; // examples come from https://github.com/vsbuffalo/devnotes/wiki/The-MD-Tag-in-BAM-Files
|
|
10
|
+
// and http://seqanswers.com/forums/showthread.php?t=8978
|
|
11
|
+
|
|
12
|
+
test('cigar to mismatches', function () {
|
|
13
|
+
expect((0, _MismatchParser.cigarToMismatches)((0, _MismatchParser.parseCigar)('56M1D45M'), seq)).toEqual([{
|
|
14
|
+
start: 56,
|
|
15
|
+
type: 'deletion',
|
|
16
|
+
base: '*',
|
|
17
|
+
length: 1
|
|
18
|
+
}]);
|
|
19
|
+
});
|
|
20
|
+
test('md to mismatches', function () {
|
|
21
|
+
var cigarMismatches = (0, _MismatchParser.cigarToMismatches)((0, _MismatchParser.parseCigar)('56M1D45M'), seq);
|
|
22
|
+
expect((0, _MismatchParser.mdToMismatches)('10A80', (0, _MismatchParser.parseCigar)('56M1D45M'), cigarMismatches, seq)).toEqual([{
|
|
23
|
+
start: 10,
|
|
24
|
+
type: 'mismatch',
|
|
25
|
+
base: 'C',
|
|
26
|
+
altbase: 'A',
|
|
27
|
+
length: 1
|
|
28
|
+
}]);
|
|
29
|
+
});
|
|
30
|
+
describe('get mismatches', function () {
|
|
31
|
+
it('simple deletion', function () {
|
|
32
|
+
// simple deletion
|
|
33
|
+
expect((0, _MismatchParser.getMismatches)('56M1D45M', '56^A45', seq)).toEqual([{
|
|
34
|
+
start: 56,
|
|
35
|
+
type: 'deletion',
|
|
36
|
+
base: '*',
|
|
37
|
+
length: 1
|
|
38
|
+
}]);
|
|
39
|
+
});
|
|
40
|
+
it('simple insertion', function () {
|
|
41
|
+
// simple insertion
|
|
42
|
+
expect((0, _MismatchParser.getMismatches)('89M1I11M', '100', 'AAAAAAAAAACAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCCGGGGGGGGGGGGGGGGGGGGGGGGGTTTTTTTTTTTTTTTTTTTTTTTTTA')).toEqual([{
|
|
43
|
+
start: 89,
|
|
44
|
+
type: 'insertion',
|
|
45
|
+
base: '1',
|
|
46
|
+
length: 0
|
|
47
|
+
}]);
|
|
48
|
+
});
|
|
49
|
+
it('deletion and a SNP', function () {
|
|
50
|
+
// contains a deletion and a SNP
|
|
51
|
+
// read GGGGG--ATTTTTT
|
|
52
|
+
// ||||| ||||||
|
|
53
|
+
// GGGGGACCTTTTTT
|
|
54
|
+
expect((0, _MismatchParser.getMismatches)('5M2D6M', '5^AC0C5', 'GGGGGATTTTTT')).toEqual([{
|
|
55
|
+
start: 5,
|
|
56
|
+
type: 'deletion',
|
|
57
|
+
base: '*',
|
|
58
|
+
length: 2
|
|
59
|
+
}, {
|
|
60
|
+
start: 7,
|
|
61
|
+
type: 'mismatch',
|
|
62
|
+
base: 'A',
|
|
63
|
+
altbase: 'C',
|
|
64
|
+
length: 1
|
|
65
|
+
}]);
|
|
66
|
+
});
|
|
67
|
+
it('0-length MD entries', function () {
|
|
68
|
+
// 0-length MD entries, which indicates two SNPs right next to each other
|
|
69
|
+
// "They generally occur between SNPs, or between a deletion then a SNP."
|
|
70
|
+
// http://seqanswers.com/forums/showthread.php?t=8978
|
|
71
|
+
//
|
|
72
|
+
// read GGGGGCATTTTT
|
|
73
|
+
// ||||| |||||
|
|
74
|
+
// ref GGGGGACTTTTT
|
|
75
|
+
expect((0, _MismatchParser.getMismatches)('12M', '5A0C5', 'GGGGGCATTTTT')).toEqual([{
|
|
76
|
+
altbase: 'A',
|
|
77
|
+
base: 'C',
|
|
78
|
+
length: 1,
|
|
79
|
+
start: 5,
|
|
80
|
+
type: 'mismatch'
|
|
81
|
+
}, {
|
|
82
|
+
altbase: 'C',
|
|
83
|
+
base: 'A',
|
|
84
|
+
length: 1,
|
|
85
|
+
start: 6,
|
|
86
|
+
type: 'mismatch'
|
|
87
|
+
}]);
|
|
88
|
+
});
|
|
89
|
+
it('non-0-length-MD string', function () {
|
|
90
|
+
// same as above but with the non-0-length MD string
|
|
91
|
+
// not sure if it is entirely legal, but may appear in the wild
|
|
92
|
+
expect((0, _MismatchParser.getMismatches)('12M', '5AC5', 'GGGGGCATTTTT')).toEqual([{
|
|
93
|
+
altbase: 'A',
|
|
94
|
+
base: 'C',
|
|
95
|
+
length: 1,
|
|
96
|
+
start: 5,
|
|
97
|
+
type: 'mismatch'
|
|
98
|
+
}, {
|
|
99
|
+
altbase: 'C',
|
|
100
|
+
base: 'A',
|
|
101
|
+
length: 1,
|
|
102
|
+
start: 6,
|
|
103
|
+
type: 'mismatch'
|
|
104
|
+
}]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
test('basic skip', function () {
|
|
108
|
+
expect((0, _MismatchParser.getMismatches)('6M200N6M', '5AC5', 'GGGGGCATTTTT')).toEqual([{
|
|
109
|
+
base: 'N',
|
|
110
|
+
length: 200,
|
|
111
|
+
start: 6,
|
|
112
|
+
type: 'skip'
|
|
113
|
+
}, {
|
|
114
|
+
altbase: 'A',
|
|
115
|
+
base: 'C',
|
|
116
|
+
length: 1,
|
|
117
|
+
start: 5,
|
|
118
|
+
type: 'mismatch'
|
|
119
|
+
}, {
|
|
120
|
+
altbase: 'C',
|
|
121
|
+
base: 'A',
|
|
122
|
+
length: 1,
|
|
123
|
+
start: 206,
|
|
124
|
+
type: 'mismatch'
|
|
125
|
+
}]);
|
|
126
|
+
});
|
|
127
|
+
test('vsbuffalo', function () {
|
|
128
|
+
// https://github.com/vsbuffalo/devnotes/wiki/The-MD-Tag-in-BAM-Files
|
|
129
|
+
// example 1
|
|
130
|
+
expect((0, _MismatchParser.getMismatches)('89M1I11M', '100', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')).toEqual([{
|
|
131
|
+
base: '1',
|
|
132
|
+
length: 0,
|
|
133
|
+
start: 89,
|
|
134
|
+
type: 'insertion'
|
|
135
|
+
}]); // https://github.com/vsbuffalo/devnotes/wiki/The-MD-Tag-in-BAM-Files
|
|
136
|
+
// example 2
|
|
137
|
+
|
|
138
|
+
expect((0, _MismatchParser.getMismatches)('9M1I91M', '48T42G8', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')).toEqual([{
|
|
139
|
+
base: '1',
|
|
140
|
+
length: 0,
|
|
141
|
+
start: 9,
|
|
142
|
+
type: 'insertion'
|
|
143
|
+
}, {
|
|
144
|
+
altbase: 'T',
|
|
145
|
+
base: 'A',
|
|
146
|
+
length: 1,
|
|
147
|
+
start: 48,
|
|
148
|
+
type: 'mismatch'
|
|
149
|
+
}, {
|
|
150
|
+
altbase: 'G',
|
|
151
|
+
base: 'A',
|
|
152
|
+
length: 1,
|
|
153
|
+
start: 91,
|
|
154
|
+
type: 'mismatch'
|
|
155
|
+
}]);
|
|
156
|
+
});
|
|
157
|
+
test('more skip', function () {
|
|
158
|
+
expect((0, _MismatchParser.getMismatches)('3M200N3M200N3M', '8A', 'GGGGGCATTTTT')).toEqual([{
|
|
159
|
+
base: 'N',
|
|
160
|
+
length: 200,
|
|
161
|
+
start: 3,
|
|
162
|
+
type: 'skip'
|
|
163
|
+
}, {
|
|
164
|
+
base: 'N',
|
|
165
|
+
length: 200,
|
|
166
|
+
start: 206,
|
|
167
|
+
type: 'skip'
|
|
168
|
+
}, {
|
|
169
|
+
altbase: 'A',
|
|
170
|
+
base: 'T',
|
|
171
|
+
length: 1,
|
|
172
|
+
start: 408,
|
|
173
|
+
type: 'mismatch'
|
|
174
|
+
}]);
|
|
175
|
+
expect((0, _MismatchParser.getMismatches)('31M1I17M1D37M', '6G4C20G1A5C5A1^C3A15G1G15', seq).sort(function (a, b) {
|
|
176
|
+
return a.start - b.start;
|
|
177
|
+
})).toMatchInlineSnapshot("\n Array [\n Object {\n \"altbase\": \"G\",\n \"base\": \"A\",\n \"length\": 1,\n \"start\": 6,\n \"type\": \"mismatch\",\n },\n Object {\n \"altbase\": \"C\",\n \"base\": \"A\",\n \"length\": 1,\n \"start\": 11,\n \"type\": \"mismatch\",\n },\n Object {\n \"base\": \"1\",\n \"length\": 0,\n \"start\": 31,\n \"type\": \"insertion\",\n },\n Object {\n \"altbase\": \"G\",\n \"base\": \"C\",\n \"length\": 1,\n \"start\": 32,\n \"type\": \"mismatch\",\n },\n Object {\n \"altbase\": \"A\",\n \"base\": \"C\",\n \"length\": 1,\n \"start\": 34,\n \"type\": \"mismatch\",\n },\n Object {\n \"altbase\": \"C\",\n \"base\": \"C\",\n \"length\": 1,\n \"start\": 40,\n \"type\": \"mismatch\",\n },\n Object {\n \"altbase\": \"A\",\n \"base\": \"C\",\n \"length\": 1,\n \"start\": 46,\n \"type\": \"mismatch\",\n },\n Object {\n \"base\": \"*\",\n \"length\": 1,\n \"start\": 48,\n \"type\": \"deletion\",\n },\n Object {\n \"altbase\": \"A\",\n \"base\": \"G\",\n \"length\": 1,\n \"start\": 52,\n \"type\": \"mismatch\",\n },\n Object {\n \"altbase\": \"G\",\n \"base\": \"G\",\n \"length\": 1,\n \"start\": 68,\n \"type\": \"mismatch\",\n },\n Object {\n \"altbase\": \"G\",\n \"base\": \"G\",\n \"length\": 1,\n \"start\": 70,\n \"type\": \"mismatch\",\n },\n ]\n ");
|
|
178
|
+
});
|
|
179
|
+
test('clipping', function () {
|
|
180
|
+
expect((0, _MismatchParser.getMismatches)('200H10M200H', '9A', 'AAAAAAAAAC')).toEqual([{
|
|
181
|
+
cliplen: 200,
|
|
182
|
+
base: 'H200',
|
|
183
|
+
length: 1,
|
|
184
|
+
start: 0,
|
|
185
|
+
type: 'hardclip'
|
|
186
|
+
}, {
|
|
187
|
+
cliplen: 200,
|
|
188
|
+
base: 'H200',
|
|
189
|
+
length: 1,
|
|
190
|
+
start: 10,
|
|
191
|
+
type: 'hardclip'
|
|
192
|
+
}, {
|
|
193
|
+
altbase: 'A',
|
|
194
|
+
base: 'C',
|
|
195
|
+
length: 1,
|
|
196
|
+
start: 9,
|
|
197
|
+
type: 'mismatch'
|
|
198
|
+
}]);
|
|
199
|
+
expect((0, _MismatchParser.getMismatches)('10S10M10S', '9A', 'AAAAAAAAAAGGGGGGGGGC')).toEqual([{
|
|
200
|
+
cliplen: 10,
|
|
201
|
+
base: 'S10',
|
|
202
|
+
length: 1,
|
|
203
|
+
start: 0,
|
|
204
|
+
type: 'softclip'
|
|
205
|
+
}, {
|
|
206
|
+
cliplen: 10,
|
|
207
|
+
base: 'S10',
|
|
208
|
+
length: 1,
|
|
209
|
+
start: 10,
|
|
210
|
+
type: 'softclip'
|
|
211
|
+
}, {
|
|
212
|
+
altbase: 'A',
|
|
213
|
+
base: 'C',
|
|
214
|
+
length: 1,
|
|
215
|
+
start: 9,
|
|
216
|
+
type: 'mismatch'
|
|
217
|
+
}]);
|
|
218
|
+
});
|
|
219
|
+
test('getNextRefPos basic', function () {
|
|
220
|
+
var cigar = (0, _MismatchParser.parseCigar)('10S10M1I4M1D15M');
|
|
221
|
+
var iter = (0, _MismatchParser.getNextRefPos)(cigar, [5, 10, 15, 20, 25, 30, 35]);
|
|
222
|
+
|
|
223
|
+
var _iter = (0, _toArray2["default"])(iter),
|
|
224
|
+
vals = _iter.slice(0);
|
|
225
|
+
|
|
226
|
+
expect(vals).toEqual([-5, 0, 5, 10, 14, 20, 25]);
|
|
227
|
+
});
|
|
228
|
+
test('getNextRefPos with many indels', function () {
|
|
229
|
+
var cigar = (0, _MismatchParser.parseCigar)('10S4M1D1IM10');
|
|
230
|
+
var iter = (0, _MismatchParser.getNextRefPos)(cigar, [5, 10, 15]);
|
|
231
|
+
|
|
232
|
+
var _iter2 = (0, _toArray2["default"])(iter),
|
|
233
|
+
vals = _iter2.slice(0);
|
|
234
|
+
|
|
235
|
+
expect(vals).toEqual([-5, 0, 5]);
|
|
236
|
+
});
|
|
237
|
+
test('getModificationPositions', function () {
|
|
238
|
+
var positions = (0, _MismatchParser.getModificationPositions)('C+m,2,2,1,4,1', 'AGCTCTCCAGAGTCGNACGCCATYCGCGCGCCACCA', 1);
|
|
239
|
+
expect(positions[0]).toEqual({
|
|
240
|
+
type: 'm',
|
|
241
|
+
positions: [6, 17, 20, 31, 34]
|
|
242
|
+
});
|
|
243
|
+
}); // ? means "modification status of the skipped bases provided."
|
|
244
|
+
|
|
245
|
+
test('getModificationPositions with unknown (?)', function () {
|
|
246
|
+
var positions = (0, _MismatchParser.getModificationPositions)('C+m?,2,2,1,4,1', 'AGCTCTCCAGAGTCGNACGCCATYCGCGCGCCACCA', 1);
|
|
247
|
+
expect(positions[0]).toEqual({
|
|
248
|
+
type: 'm',
|
|
249
|
+
positions: [6, 17, 20, 31, 34]
|
|
250
|
+
});
|
|
251
|
+
}); // . means "modification status of the skipped bases is low probability"
|
|
252
|
+
|
|
253
|
+
test('getModificationPositions with unknown (.)', function () {
|
|
254
|
+
var positions = (0, _MismatchParser.getModificationPositions)('C+m.,2,2,1,4,1', 'AGCTCTCCAGAGTCGNACGCCATYCGCGCGCCACCA', 1);
|
|
255
|
+
expect(positions[0]).toEqual({
|
|
256
|
+
type: 'm',
|
|
257
|
+
positions: [6, 17, 20, 31, 34]
|
|
258
|
+
});
|
|
259
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
|
|
8
|
+
var _configuration = require("@jbrowse/core/configuration");
|
|
9
|
+
|
|
10
|
+
var _mobxStateTree = require("mobx-state-tree");
|
|
11
|
+
|
|
12
|
+
var _default = _mobxStateTree.types.late(function () {
|
|
13
|
+
return (0, _configuration.ConfigurationSchema)('BamAdapter', {
|
|
14
|
+
bamLocation: {
|
|
15
|
+
type: 'fileLocation',
|
|
16
|
+
defaultValue: {
|
|
17
|
+
uri: '/path/to/my.bam',
|
|
18
|
+
locationType: 'UriLocation'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
index: (0, _configuration.ConfigurationSchema)('BamIndex', {
|
|
22
|
+
indexType: {
|
|
23
|
+
model: _mobxStateTree.types.enumeration('IndexType', ['BAI', 'CSI']),
|
|
24
|
+
type: 'stringEnum',
|
|
25
|
+
defaultValue: 'BAI'
|
|
26
|
+
},
|
|
27
|
+
location: {
|
|
28
|
+
type: 'fileLocation',
|
|
29
|
+
defaultValue: {
|
|
30
|
+
uri: '/path/to/my.bam.bai',
|
|
31
|
+
locationType: 'UriLocation'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}),
|
|
35
|
+
fetchSizeLimit: {
|
|
36
|
+
type: 'number',
|
|
37
|
+
defaultValue: 5000000
|
|
38
|
+
},
|
|
39
|
+
sequenceAdapter: {
|
|
40
|
+
type: 'frozen',
|
|
41
|
+
defaultValue: null
|
|
42
|
+
}
|
|
43
|
+
}, {
|
|
44
|
+
explicitlyTyped: true
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
exports["default"] = _default;
|