@jbrowse/plugin-alignments 1.6.9 → 1.7.3

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.
Files changed (131) hide show
  1. package/dist/AlignmentsFeatureDetail/AlignmentsFeatureDetail.d.ts +6 -6
  2. package/dist/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +216 -0
  3. package/dist/AlignmentsFeatureDetail/index.d.ts +13 -13
  4. package/dist/AlignmentsFeatureDetail/index.js +63 -0
  5. package/dist/AlignmentsFeatureDetail/index.test.js +60 -0
  6. package/dist/AlignmentsTrack/index.d.ts +2 -2
  7. package/dist/AlignmentsTrack/index.js +37 -0
  8. package/dist/BamAdapter/BamAdapter.d.ts +40 -30
  9. package/dist/BamAdapter/BamAdapter.js +598 -0
  10. package/dist/BamAdapter/BamAdapter.test.js +177 -0
  11. package/dist/BamAdapter/BamSlightlyLazyFeature.d.ts +33 -33
  12. package/dist/BamAdapter/BamSlightlyLazyFeature.js +176 -0
  13. package/dist/BamAdapter/MismatchParser.d.ts +28 -28
  14. package/dist/BamAdapter/MismatchParser.js +384 -0
  15. package/dist/BamAdapter/MismatchParser.test.js +259 -0
  16. package/dist/BamAdapter/configSchema.d.ts +2 -2
  17. package/dist/BamAdapter/configSchema.js +48 -0
  18. package/dist/BamAdapter/index.d.ts +3 -3
  19. package/dist/BamAdapter/index.js +36 -0
  20. package/dist/CramAdapter/CramAdapter.d.ts +52 -43
  21. package/dist/CramAdapter/CramAdapter.js +660 -0
  22. package/dist/CramAdapter/CramAdapter.test.js +138 -0
  23. package/dist/CramAdapter/CramSlightlyLazyFeature.d.ts +49 -49
  24. package/dist/CramAdapter/CramSlightlyLazyFeature.js +447 -0
  25. package/dist/CramAdapter/CramTestAdapters.d.ts +29 -29
  26. package/dist/CramAdapter/CramTestAdapters.js +234 -0
  27. package/dist/CramAdapter/configSchema.d.ts +3 -3
  28. package/dist/CramAdapter/configSchema.js +40 -0
  29. package/dist/CramAdapter/index.d.ts +3 -3
  30. package/dist/CramAdapter/index.js +36 -0
  31. package/dist/HtsgetBamAdapter/HtsgetBamAdapter.d.ts +9 -9
  32. package/dist/HtsgetBamAdapter/HtsgetBamAdapter.js +97 -0
  33. package/dist/HtsgetBamAdapter/configSchema.d.ts +2 -2
  34. package/dist/HtsgetBamAdapter/configSchema.js +31 -0
  35. package/dist/HtsgetBamAdapter/index.d.ts +3 -3
  36. package/dist/HtsgetBamAdapter/index.js +42 -0
  37. package/dist/LinearAlignmentsDisplay/components/AlignmentsDisplay.d.ts +7 -7
  38. package/dist/LinearAlignmentsDisplay/components/AlignmentsDisplay.js +69 -0
  39. package/dist/LinearAlignmentsDisplay/index.d.ts +2 -2
  40. package/dist/LinearAlignmentsDisplay/index.js +31 -0
  41. package/dist/LinearAlignmentsDisplay/models/configSchema.d.ts +4 -4
  42. package/dist/LinearAlignmentsDisplay/models/configSchema.js +25 -0
  43. package/dist/LinearAlignmentsDisplay/models/configSchema.test.js +83 -0
  44. package/dist/LinearAlignmentsDisplay/models/model.d.ts +105 -105
  45. package/dist/LinearAlignmentsDisplay/models/model.js +250 -0
  46. package/dist/LinearPileupDisplay/components/ColorByModifications.d.ts +14 -14
  47. package/dist/LinearPileupDisplay/components/ColorByModifications.js +123 -0
  48. package/dist/LinearPileupDisplay/components/ColorByTag.d.ts +9 -9
  49. package/dist/LinearPileupDisplay/components/ColorByTag.js +98 -0
  50. package/dist/LinearPileupDisplay/components/FilterByTag.d.ts +18 -18
  51. package/dist/LinearPileupDisplay/components/FilterByTag.js +203 -0
  52. package/dist/LinearPileupDisplay/components/LinearPileupDisplayBlurb.d.ts +13 -13
  53. package/dist/LinearPileupDisplay/components/LinearPileupDisplayBlurb.js +32 -0
  54. package/dist/LinearPileupDisplay/components/SetFeatureHeight.d.ts +16 -16
  55. package/dist/LinearPileupDisplay/components/SetFeatureHeight.js +99 -0
  56. package/dist/LinearPileupDisplay/components/SetMaxHeight.d.ts +10 -10
  57. package/dist/LinearPileupDisplay/components/SetMaxHeight.js +90 -0
  58. package/dist/LinearPileupDisplay/components/SortByTag.d.ts +9 -9
  59. package/dist/LinearPileupDisplay/components/SortByTag.js +95 -0
  60. package/dist/LinearPileupDisplay/configSchema.d.ts +6 -6
  61. package/dist/LinearPileupDisplay/configSchema.js +47 -0
  62. package/dist/LinearPileupDisplay/configSchema.test.js +92 -0
  63. package/dist/LinearPileupDisplay/index.d.ts +2 -2
  64. package/dist/LinearPileupDisplay/index.js +30 -0
  65. package/dist/LinearPileupDisplay/model.d.ts +319 -321
  66. package/dist/LinearPileupDisplay/model.js +602 -0
  67. package/dist/LinearSNPCoverageDisplay/components/Tooltip.d.ts +10 -10
  68. package/dist/LinearSNPCoverageDisplay/components/Tooltip.js +63 -0
  69. package/dist/LinearSNPCoverageDisplay/index.d.ts +2 -2
  70. package/dist/LinearSNPCoverageDisplay/index.js +30 -0
  71. package/dist/LinearSNPCoverageDisplay/models/configSchema.d.ts +2 -2
  72. package/dist/LinearSNPCoverageDisplay/models/configSchema.js +57 -0
  73. package/dist/LinearSNPCoverageDisplay/models/configSchema.test.js +62 -0
  74. package/dist/LinearSNPCoverageDisplay/models/model.d.ts +346 -96
  75. package/dist/LinearSNPCoverageDisplay/models/model.js +237 -0
  76. package/dist/NestedFrequencyTable.d.ts +14 -14
  77. package/dist/NestedFrequencyTable.js +152 -0
  78. package/dist/PileupRPC/rpcMethods.d.ts +34 -34
  79. package/dist/PileupRPC/rpcMethods.js +285 -0
  80. package/dist/PileupRenderer/PileupLayoutSession.d.ts +29 -29
  81. package/dist/PileupRenderer/PileupLayoutSession.js +79 -0
  82. package/dist/PileupRenderer/PileupRenderer.d.ts +125 -125
  83. package/dist/PileupRenderer/PileupRenderer.js +1220 -0
  84. package/dist/PileupRenderer/components/PileupRendering.d.ts +23 -23
  85. package/dist/PileupRenderer/components/PileupRendering.js +270 -0
  86. package/dist/PileupRenderer/components/PileupRendering.test.js +36 -0
  87. package/dist/PileupRenderer/configSchema.d.ts +2 -2
  88. package/dist/PileupRenderer/configSchema.js +72 -0
  89. package/dist/PileupRenderer/index.d.ts +2 -2
  90. package/dist/PileupRenderer/index.js +25 -0
  91. package/dist/PileupRenderer/sortUtil.d.ts +8 -8
  92. package/dist/PileupRenderer/sortUtil.js +112 -0
  93. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +67 -71
  94. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +606 -0
  95. package/dist/SNPCoverageAdapter/configSchema.d.ts +3 -3
  96. package/dist/SNPCoverageAdapter/configSchema.js +22 -0
  97. package/dist/SNPCoverageAdapter/index.d.ts +3 -3
  98. package/dist/SNPCoverageAdapter/index.js +45 -0
  99. package/dist/SNPCoverageRenderer/SNPCoverageRenderer.d.ts +20 -20
  100. package/dist/SNPCoverageRenderer/SNPCoverageRenderer.js +296 -0
  101. package/dist/SNPCoverageRenderer/configSchema.d.ts +2 -2
  102. package/dist/SNPCoverageRenderer/configSchema.js +40 -0
  103. package/dist/SNPCoverageRenderer/index.d.ts +3 -3
  104. package/dist/SNPCoverageRenderer/index.js +34 -0
  105. package/dist/declare.d.js +1 -0
  106. package/dist/index.d.ts +10 -10
  107. package/dist/index.js +154 -6
  108. package/dist/index.test.js +26 -0
  109. package/dist/shared.d.ts +25 -25
  110. package/dist/shared.js +96 -0
  111. package/dist/util.d.ts +19 -19
  112. package/dist/util.js +135 -0
  113. package/package.json +5 -8
  114. package/src/BamAdapter/BamAdapter.ts +35 -8
  115. package/src/CramAdapter/CramAdapter.ts +42 -15
  116. package/src/LinearPileupDisplay/components/SetMaxHeight.tsx +1 -1
  117. package/src/LinearPileupDisplay/model.ts +2 -22
  118. package/src/LinearSNPCoverageDisplay/models/model.ts +6 -36
  119. package/src/PileupRenderer/PileupRenderer.tsx +3 -6
  120. package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +11 -17
  121. package/dist/AlignmentsFeatureDetail/index.test.d.ts +0 -1
  122. package/dist/LinearAlignmentsDisplay/models/configSchema.test.d.ts +0 -1
  123. package/dist/LinearPileupDisplay/configSchema.test.d.ts +0 -1
  124. package/dist/LinearSNPCoverageDisplay/models/configSchema.test.d.ts +0 -1
  125. package/dist/PileupRenderer/components/PileupRendering.test.d.ts +0 -1
  126. package/dist/plugin-alignments.cjs.development.js +0 -8438
  127. package/dist/plugin-alignments.cjs.development.js.map +0 -1
  128. package/dist/plugin-alignments.cjs.production.min.js +0 -2
  129. package/dist/plugin-alignments.cjs.production.min.js.map +0 -1
  130. package/dist/plugin-alignments.esm.js +0 -8430
  131. package/dist/plugin-alignments.esm.js.map +0 -1
@@ -0,0 +1,1220 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports["default"] = void 0;
9
+
10
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
11
+
12
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
+
14
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
15
+
16
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
17
+
18
+ var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
19
+
20
+ var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
21
+
22
+ var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
23
+
24
+ var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
25
+
26
+ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
27
+
28
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
29
+
30
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
31
+
32
+ var _color = _interopRequireDefault(require("color"));
33
+
34
+ var _BoxRendererType2 = _interopRequireDefault(require("@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType"));
35
+
36
+ var _ui = require("@jbrowse/core/ui");
37
+
38
+ var _util = require("@jbrowse/core/util");
39
+
40
+ var _offscreenCanvasUtils = require("@jbrowse/core/util/offscreenCanvasUtils");
41
+
42
+ var _dataAdapterCache = require("@jbrowse/core/data_adapters/dataAdapterCache");
43
+
44
+ var _configuration = require("@jbrowse/core/configuration");
45
+
46
+ var _MismatchParser = require("../BamAdapter/MismatchParser");
47
+
48
+ var _sortUtil = require("./sortUtil");
49
+
50
+ var _util2 = require("../util");
51
+
52
+ var _PileupLayoutSession = require("./PileupLayoutSession");
53
+
54
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
55
+
56
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
57
+
58
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
59
+
60
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
61
+
62
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
63
+
64
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }
65
+
66
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
67
+
68
+ function getColorBaseMap(theme) {
69
+ return {
70
+ A: theme.palette.bases.A.main,
71
+ C: theme.palette.bases.C.main,
72
+ G: theme.palette.bases.G.main,
73
+ T: theme.palette.bases.T.main,
74
+ deletion: '#808080' // gray
75
+
76
+ };
77
+ }
78
+
79
+ function getContrastBaseMap(theme) {
80
+ return Object.fromEntries(Object.entries(getColorBaseMap(theme)).map(function (_ref) {
81
+ var _ref2 = (0, _slicedToArray2["default"])(_ref, 2),
82
+ key = _ref2[0],
83
+ value = _ref2[1];
84
+
85
+ return [key, theme.palette.getContrastText(value)];
86
+ }));
87
+ }
88
+
89
+ var alignmentColoring = {
90
+ color_fwd_strand_not_proper: '#ECC8C8',
91
+ color_rev_strand_not_proper: '#BEBED8',
92
+ color_fwd_strand: '#EC8B8B',
93
+ color_rev_strand: '#8F8FD8',
94
+ color_fwd_missing_mate: '#D11919',
95
+ color_rev_missing_mate: '#1919D1',
96
+ color_fwd_diff_chr: '#000',
97
+ color_rev_diff_chr: '#969696',
98
+ color_pair_lr: '#c8c8c8',
99
+ color_pair_rr: 'navy',
100
+ color_pair_rl: 'teal',
101
+ color_pair_ll: 'green',
102
+ color_nostrand: '#c8c8c8',
103
+ color_interchrom: 'orange',
104
+ color_longinsert: 'red',
105
+ color_shortinsert: 'pink'
106
+ };
107
+
108
+ function shouldDrawMismatches(type) {
109
+ return !['methylation', 'modifications'].includes(type || '');
110
+ }
111
+
112
+ var PileupRenderer = /*#__PURE__*/function (_BoxRendererType) {
113
+ (0, _inherits2["default"])(PileupRenderer, _BoxRendererType);
114
+
115
+ var _super = _createSuper(PileupRenderer);
116
+
117
+ function PileupRenderer() {
118
+ var _this;
119
+
120
+ (0, _classCallCheck2["default"])(this, PileupRenderer);
121
+
122
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
123
+ args[_key] = arguments[_key];
124
+ }
125
+
126
+ _this = _super.call.apply(_super, [this].concat(args));
127
+ (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "supportsSVG", true);
128
+ return _this;
129
+ }
130
+
131
+ (0, _createClass2["default"])(PileupRenderer, [{
132
+ key: "getCharWidthHeight",
133
+ value: // get width and height of chars the height is an approximation: width
134
+ // letter M is approximately the height
135
+ function getCharWidthHeight(ctx) {
136
+ var charWidth = ctx.measureText('A').width;
137
+ var charHeight = ctx.measureText('M').width;
138
+ return {
139
+ charWidth: charWidth,
140
+ charHeight: charHeight
141
+ };
142
+ }
143
+ }, {
144
+ key: "layoutFeature",
145
+ value: function layoutFeature(_ref3) {
146
+ var feature = _ref3.feature,
147
+ layout = _ref3.layout,
148
+ bpPerPx = _ref3.bpPerPx,
149
+ region = _ref3.region,
150
+ showSoftClip = _ref3.showSoftClip,
151
+ heightPx = _ref3.heightPx,
152
+ displayMode = _ref3.displayMode;
153
+ var expansionBefore = 0;
154
+ var expansionAfter = 0; // Expand the start and end of feature when softclipping enabled
155
+
156
+ if (showSoftClip) {
157
+ var mismatches = feature.get('mismatches');
158
+ var seq = feature.get('seq');
159
+
160
+ if (seq) {
161
+ for (var i = 0; i < mismatches.length; i += 1) {
162
+ var _mismatches$i = mismatches[i],
163
+ type = _mismatches$i.type,
164
+ start = _mismatches$i.start,
165
+ _mismatches$i$cliplen = _mismatches$i.cliplen,
166
+ cliplen = _mismatches$i$cliplen === void 0 ? 0 : _mismatches$i$cliplen;
167
+
168
+ if (type === 'softclip') {
169
+ start === 0 ? expansionBefore = cliplen : expansionAfter = cliplen;
170
+ }
171
+ }
172
+ }
173
+ }
174
+
175
+ var _bpSpanPx = (0, _util.bpSpanPx)(feature.get('start') - expansionBefore, feature.get('end') + expansionAfter, region, bpPerPx),
176
+ _bpSpanPx2 = (0, _slicedToArray2["default"])(_bpSpanPx, 2),
177
+ leftPx = _bpSpanPx2[0],
178
+ rightPx = _bpSpanPx2[1];
179
+
180
+ if (displayMode === 'compact') {
181
+ heightPx /= 3;
182
+ }
183
+
184
+ if (feature.get('refName') !== region.refName) {
185
+ throw new Error("feature ".concat(feature.id(), " is not on the current region's reference sequence ").concat(region.refName));
186
+ }
187
+
188
+ var topPx = layout.addRect(feature.id(), feature.get('start') - expansionBefore, feature.get('end') + expansionAfter, heightPx);
189
+
190
+ if (topPx === null) {
191
+ return null;
192
+ }
193
+
194
+ return {
195
+ feature: feature,
196
+ leftPx: leftPx,
197
+ rightPx: rightPx,
198
+ topPx: displayMode === 'collapse' ? 0 : topPx,
199
+ heightPx: heightPx
200
+ };
201
+ } // expands region for clipping to use. possible improvement: use average read
202
+ // size to set the heuristic maxClippingSize expansion (e.g. short reads
203
+ // don't have to expand a softclipping size a lot, but long reads might)
204
+
205
+ }, {
206
+ key: "getExpandedRegion",
207
+ value: function getExpandedRegion(region, renderArgs) {
208
+ var config = renderArgs.config,
209
+ showSoftClip = renderArgs.showSoftClip;
210
+ var maxClippingSize = (0, _configuration.readConfObject)(config, 'maxClippingSize');
211
+ var start = region.start,
212
+ end = region.end;
213
+ var len = end - start;
214
+ var bpExpansion = Math.max(len, showSoftClip ? Math.round(maxClippingSize) : 0);
215
+ return _objectSpread(_objectSpread({}, region), {}, {
216
+ start: Math.floor(Math.max(start - bpExpansion, 0)),
217
+ end: Math.ceil(end + bpExpansion)
218
+ });
219
+ }
220
+ }, {
221
+ key: "colorByOrientation",
222
+ value: function colorByOrientation(feature, config) {
223
+ return alignmentColoring[this.getOrientation(feature, config) || 'color_nostrand'];
224
+ }
225
+ }, {
226
+ key: "getOrientation",
227
+ value: function getOrientation(feature, config) {
228
+ var orientationType = (0, _configuration.readConfObject)(config, 'orientationType');
229
+ var type = _util2.orientationTypes[orientationType];
230
+ var orientation = type[feature.get('pair_orientation')];
231
+ var map = {
232
+ LR: 'color_pair_lr',
233
+ RR: 'color_pair_rr',
234
+ RL: 'color_pair_rl',
235
+ LL: 'color_pair_ll'
236
+ };
237
+ return map[orientation];
238
+ }
239
+ }, {
240
+ key: "colorByInsertSize",
241
+ value: function colorByInsertSize(feature, _config) {
242
+ return feature.get('is_paired') && feature.get('seq_id') !== feature.get('next_seq_id') ? '#555' : "hsl(".concat(Math.abs(feature.get('template_length')) / 10, ",50%,50%)");
243
+ }
244
+ }, {
245
+ key: "colorByStranded",
246
+ value: function colorByStranded(feature, _config) {
247
+ var flags = feature.get('flags');
248
+ var strand = feature.get('strand'); // is paired
249
+
250
+ if (flags & 1) {
251
+ var revflag = flags & 64;
252
+ var flipper = revflag ? -1 : 1; // proper pairing
253
+
254
+ if (flags & 2) {
255
+ return strand * flipper === 1 ? 'color_rev_strand' : 'color_fwd_strand';
256
+ }
257
+
258
+ if (feature.get('multi_segment_next_segment_unmapped')) {
259
+ return strand * flipper === 1 ? 'color_rev_missing_mate' : 'color_fwd_missing_mate';
260
+ }
261
+
262
+ if (feature.get('seq_id') === feature.get('next_seq_id')) {
263
+ return strand * flipper === 1 ? 'color_rev_strand_not_proper' : 'color_fwd_strand_not_proper';
264
+ } // should only leave aberrant chr
265
+
266
+
267
+ return strand === 1 ? 'color_fwd_diff_chr' : 'color_rev_diff_chr';
268
+ }
269
+
270
+ return strand === 1 ? 'color_fwd_strand' : 'color_rev_strand';
271
+ }
272
+ }, {
273
+ key: "colorByPerBaseLettering",
274
+ value: function colorByPerBaseLettering(ctx, feat, _config, region, bpPerPx, props) {
275
+ var colorForBase = props.colorForBase,
276
+ contrastForBase = props.contrastForBase,
277
+ charWidth = props.charWidth,
278
+ charHeight = props.charHeight;
279
+ var heightLim = charHeight - 2;
280
+ var feature = feat.feature,
281
+ topPx = feat.topPx,
282
+ heightPx = feat.heightPx;
283
+ var seq = feature.get('seq');
284
+ var cigarOps = (0, _MismatchParser.parseCigar)(feature.get('CIGAR'));
285
+ var widthPx = 1 / bpPerPx;
286
+ var start = feature.get('start');
287
+ var soffset = 0; // sequence offset
288
+
289
+ var roffset = 0; // reference offset
290
+
291
+ for (var i = 0; i < cigarOps.length; i += 2) {
292
+ var len = +cigarOps[i];
293
+ var op = cigarOps[i + 1];
294
+
295
+ if (op === 'S' || op === 'I') {
296
+ soffset += len;
297
+ } else if (op === 'D' || op === 'N') {
298
+ roffset += len;
299
+ } else if (op === 'M' || op === 'X' || op === '=') {
300
+ for (var m = 0; m < len; m++) {
301
+ var letter = seq[soffset + m];
302
+ ctx.fillStyle = colorForBase[letter];
303
+
304
+ var _bpSpanPx3 = (0, _util.bpSpanPx)(start + roffset + m, start + roffset + m + 1, region, bpPerPx),
305
+ _bpSpanPx4 = (0, _slicedToArray2["default"])(_bpSpanPx3, 1),
306
+ leftPx = _bpSpanPx4[0];
307
+
308
+ ctx.fillRect(leftPx, topPx, widthPx + 0.5, heightPx);
309
+
310
+ if (widthPx >= charWidth && heightPx >= heightLim) {
311
+ // normal SNP coloring
312
+ ctx.fillStyle = contrastForBase[letter];
313
+ ctx.fillText(letter, leftPx + (widthPx - charWidth) / 2 + 1, topPx + heightPx);
314
+ }
315
+ }
316
+
317
+ soffset += len;
318
+ roffset += len;
319
+ }
320
+ }
321
+ }
322
+ }, {
323
+ key: "colorByPerBaseQuality",
324
+ value: function colorByPerBaseQuality(ctx, feat, _config, region, bpPerPx) {
325
+ var feature = feat.feature,
326
+ topPx = feat.topPx,
327
+ heightPx = feat.heightPx;
328
+ var qual = feature.get('qual') || '';
329
+ var scores = qual.split(' ').map(function (val) {
330
+ return +val;
331
+ });
332
+ var cigarOps = (0, _MismatchParser.parseCigar)(feature.get('CIGAR'));
333
+ var width = 1 / bpPerPx;
334
+ var start = feature.get('start');
335
+ var soffset = 0; // sequence offset
336
+
337
+ var roffset = 0; // reference offset
338
+
339
+ for (var i = 0; i < cigarOps.length; i += 2) {
340
+ var len = +cigarOps[i];
341
+ var op = cigarOps[i + 1];
342
+
343
+ if (op === 'S' || op === 'I') {
344
+ soffset += len;
345
+ } else if (op === 'D' || op === 'N') {
346
+ roffset += len;
347
+ } else if (op === 'M' || op === 'X' || op === '=') {
348
+ for (var m = 0; m < len; m++) {
349
+ var score = scores[soffset + m];
350
+ ctx.fillStyle = "hsl(".concat(score === 255 ? 150 : score * 1.5, ",55%,50%)");
351
+
352
+ var _bpSpanPx5 = (0, _util.bpSpanPx)(start + roffset + m, start + roffset + m + 1, region, bpPerPx),
353
+ _bpSpanPx6 = (0, _slicedToArray2["default"])(_bpSpanPx5, 1),
354
+ leftPx = _bpSpanPx6[0];
355
+
356
+ ctx.fillRect(leftPx, topPx, width + 0.5, heightPx);
357
+ }
358
+
359
+ soffset += len;
360
+ roffset += len;
361
+ }
362
+ }
363
+ } // ML stores probabilities as array of numerics and MP is scaled phred scores
364
+ // https://github.com/samtools/hts-specs/pull/418/files#diff-e765c6479316309f56b636f88189cdde8c40b854c7bdcce9ee7fe87a4e76febcR596
365
+ //
366
+ // if we have ML or Ml, it is an 8bit probability, divide by 255
367
+ //
368
+ // if we have MP or Mp it is phred scaled ASCII, which can go up to 90 but
369
+ // has very high likelihood basecalls at that point, we really only care
370
+ // about low qual calls <20 approx
371
+ //
372
+
373
+ }, {
374
+ key: "colorByModifications",
375
+ value: function colorByModifications(ctx, layoutFeature, _config, region, bpPerPx, props) {
376
+ var feature = layoutFeature.feature,
377
+ topPx = layoutFeature.topPx,
378
+ heightPx = layoutFeature.heightPx;
379
+ var _props$modificationTa = props.modificationTagMap,
380
+ modificationTagMap = _props$modificationTa === void 0 ? {} : _props$modificationTa;
381
+ var mm = (0, _util2.getTagAlt)(feature, 'MM', 'Mm') || '';
382
+ var ml = (0, _util2.getTagAlt)(feature, 'ML', 'Ml') || [];
383
+ var probabilities = ml ? (typeof ml === 'string' ? ml.split(',').map(function (e) {
384
+ return +e;
385
+ }) : ml).map(function (e) {
386
+ return e / 255;
387
+ }) : (0, _util2.getTagAlt)(feature, 'MP', 'Mp').split('').map(function (s) {
388
+ return s.charCodeAt(0) - 33;
389
+ }).map(function (elt) {
390
+ return Math.min(1, elt / 50);
391
+ });
392
+ var cigar = feature.get('CIGAR');
393
+ var start = feature.get('start');
394
+ var end = feature.get('end');
395
+ var seq = feature.get('seq');
396
+ var strand = feature.get('strand');
397
+ var cigarOps = (0, _MismatchParser.parseCigar)(cigar);
398
+ var modifications = (0, _MismatchParser.getModificationPositions)(mm, seq, strand); // probIndex applies across multiple modifications e.g.
399
+
400
+ var probIndex = 0;
401
+
402
+ for (var i = 0; i < modifications.length; i++) {
403
+ var _modifications$i = modifications[i],
404
+ type = _modifications$i.type,
405
+ positions = _modifications$i.positions;
406
+ var col = modificationTagMap[type] || 'black';
407
+ var base = (0, _color["default"])(col);
408
+
409
+ var _iterator = _createForOfIteratorHelper((0, _MismatchParser.getNextRefPos)(cigarOps, positions)),
410
+ _step;
411
+
412
+ try {
413
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
414
+ var readPos = _step.value;
415
+
416
+ if (readPos >= 0 && start + readPos < end) {
417
+ var _bpSpanPx7 = (0, _util.bpSpanPx)(start + readPos, start + readPos + 1, region, bpPerPx),
418
+ _bpSpanPx8 = (0, _slicedToArray2["default"])(_bpSpanPx7, 2),
419
+ leftPx = _bpSpanPx8[0],
420
+ rightPx = _bpSpanPx8[1]; // give it a little boost of 0.1 to not make them fully
421
+ // invisible to avoid confusion
422
+
423
+
424
+ ctx.fillStyle = base.alpha(probabilities[probIndex] + 0.1).hsl().string();
425
+ ctx.fillRect(leftPx, topPx, rightPx - leftPx + 0.5, heightPx);
426
+ }
427
+
428
+ probIndex++;
429
+ }
430
+ } catch (err) {
431
+ _iterator.e(err);
432
+ } finally {
433
+ _iterator.f();
434
+ }
435
+ }
436
+ } // Color by methylation is slightly modified version of color by
437
+ // modifications that focuses on CpG sites, with non-methylated CpG colored
438
+ // blue
439
+
440
+ }, {
441
+ key: "colorByMethylation",
442
+ value: function colorByMethylation(ctx, layoutFeature, _config, region, bpPerPx, props) {
443
+ var regionSequence = props.regionSequence;
444
+ var feature = layoutFeature.feature,
445
+ topPx = layoutFeature.topPx,
446
+ heightPx = layoutFeature.heightPx;
447
+ var mm = (0, _util2.getTagAlt)(feature, 'MM', 'Mm') || '';
448
+
449
+ if (!regionSequence) {
450
+ throw new Error('region sequence required for methylation');
451
+ }
452
+
453
+ var cigar = feature.get('CIGAR');
454
+ var fstart = feature.get('start');
455
+ var fend = feature.get('end');
456
+ var seq = feature.get('seq');
457
+ var strand = feature.get('strand');
458
+ var cigarOps = (0, _MismatchParser.parseCigar)(cigar);
459
+ var rstart = region.start,
460
+ rend = region.end;
461
+ var methBins = new Array(rend - rstart).fill(0);
462
+ var modifications = (0, _MismatchParser.getModificationPositions)(mm, seq, strand);
463
+
464
+ for (var i = 0; i < modifications.length; i++) {
465
+ var _modifications$i2 = modifications[i],
466
+ type = _modifications$i2.type,
467
+ positions = _modifications$i2.positions;
468
+
469
+ if (type === 'm' && positions) {
470
+ var _iterator2 = _createForOfIteratorHelper((0, _MismatchParser.getNextRefPos)(cigarOps, positions)),
471
+ _step2;
472
+
473
+ try {
474
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
475
+ var pos = _step2.value;
476
+ var epos = pos + fstart - rstart;
477
+
478
+ if (epos >= 0 && epos < methBins.length) {
479
+ methBins[epos] = 1;
480
+ }
481
+ }
482
+ } catch (err) {
483
+ _iterator2.e(err);
484
+ } finally {
485
+ _iterator2.f();
486
+ }
487
+ }
488
+ }
489
+
490
+ for (var j = fstart; j < fend; j++) {
491
+ var _i = j - rstart;
492
+
493
+ if (_i >= 0 && _i < methBins.length) {
494
+ var l1 = regionSequence[_i].toLowerCase();
495
+
496
+ var l2 = regionSequence[_i + 1].toLowerCase(); // if we are zoomed out, display just a block over the cpg
497
+
498
+
499
+ if (bpPerPx > 2) {
500
+ if (l1 === 'c' && l2 === 'g') {
501
+ var s = rstart + _i;
502
+
503
+ var _bpSpanPx9 = (0, _util.bpSpanPx)(s, s + 2, region, bpPerPx),
504
+ _bpSpanPx10 = (0, _slicedToArray2["default"])(_bpSpanPx9, 2),
505
+ leftPx = _bpSpanPx10[0],
506
+ rightPx = _bpSpanPx10[1];
507
+
508
+ if (methBins[_i] || methBins[_i + 1]) {
509
+ ctx.fillStyle = 'red';
510
+ } else {
511
+ ctx.fillStyle = 'blue';
512
+ }
513
+
514
+ ctx.fillRect(leftPx, topPx, rightPx - leftPx + 0.5, heightPx);
515
+ }
516
+ } // if we are zoomed in, color the c inside the cpg
517
+ else {
518
+ // color
519
+ if (l1 === 'c' && l2 === 'g') {
520
+ var _s = rstart + _i;
521
+
522
+ var _bpSpanPx11 = (0, _util.bpSpanPx)(_s, _s + 1, region, bpPerPx),
523
+ _bpSpanPx12 = (0, _slicedToArray2["default"])(_bpSpanPx11, 2),
524
+ _leftPx = _bpSpanPx12[0],
525
+ _rightPx = _bpSpanPx12[1];
526
+
527
+ if (methBins[_i]) {
528
+ ctx.fillStyle = 'red';
529
+ } else {
530
+ ctx.fillStyle = 'blue';
531
+ }
532
+
533
+ ctx.fillRect(_leftPx, topPx, _rightPx - _leftPx + 0.5, heightPx);
534
+
535
+ var _bpSpanPx13 = (0, _util.bpSpanPx)(_s + 1, _s + 2, region, bpPerPx),
536
+ _bpSpanPx14 = (0, _slicedToArray2["default"])(_bpSpanPx13, 2),
537
+ leftPx2 = _bpSpanPx14[0],
538
+ rightPx2 = _bpSpanPx14[1];
539
+
540
+ if (methBins[_i + 1]) {
541
+ ctx.fillStyle = 'red';
542
+ } else {
543
+ ctx.fillStyle = 'blue';
544
+ }
545
+
546
+ ctx.fillRect(leftPx2, topPx, rightPx2 - leftPx2 + 0.5, heightPx);
547
+ }
548
+ }
549
+ }
550
+ }
551
+ }
552
+ }, {
553
+ key: "drawRect",
554
+ value: function drawRect(ctx, feat, props) {
555
+ var regions = props.regions,
556
+ bpPerPx = props.bpPerPx;
557
+ var heightPx = feat.heightPx,
558
+ topPx = feat.topPx,
559
+ feature = feat.feature;
560
+
561
+ var _regions = (0, _slicedToArray2["default"])(regions, 1),
562
+ region = _regions[0];
563
+
564
+ var _bpSpanPx15 = (0, _util.bpSpanPx)(feature.get('start'), feature.get('end'), region, bpPerPx),
565
+ _bpSpanPx16 = (0, _slicedToArray2["default"])(_bpSpanPx15, 2),
566
+ leftPx = _bpSpanPx16[0],
567
+ rightPx = _bpSpanPx16[1];
568
+
569
+ var flip = region.reversed ? -1 : 1;
570
+ var strand = feature.get('strand') * flip;
571
+
572
+ if (bpPerPx < 10) {
573
+ if (strand === -1) {
574
+ ctx.beginPath();
575
+ ctx.moveTo(leftPx - 5, topPx + heightPx / 2);
576
+ ctx.lineTo(leftPx, topPx + heightPx);
577
+ ctx.lineTo(rightPx, topPx + heightPx);
578
+ ctx.lineTo(rightPx, topPx);
579
+ ctx.lineTo(leftPx, topPx);
580
+ ctx.closePath();
581
+ ctx.fill();
582
+ } else {
583
+ ctx.beginPath();
584
+ ctx.moveTo(leftPx, topPx);
585
+ ctx.lineTo(leftPx, topPx + heightPx);
586
+ ctx.lineTo(rightPx, topPx + heightPx);
587
+ ctx.lineTo(rightPx + 5, topPx + heightPx / 2);
588
+ ctx.lineTo(rightPx, topPx);
589
+ ctx.closePath();
590
+ ctx.fill();
591
+ }
592
+ } else {
593
+ ctx.fillRect(leftPx, topPx, rightPx - leftPx, heightPx);
594
+ }
595
+ }
596
+ }, {
597
+ key: "drawAlignmentRect",
598
+ value: function drawAlignmentRect(ctx, feat, props) {
599
+ var defaultColor = props.defaultColor,
600
+ config = props.config,
601
+ bpPerPx = props.bpPerPx,
602
+ regions = props.regions,
603
+ colorBy = props.colorBy,
604
+ _props$colorTagMap = props.colorTagMap,
605
+ colorTagMap = _props$colorTagMap === void 0 ? {} : _props$colorTagMap,
606
+ colorForBase = props.colorForBase,
607
+ contrastForBase = props.contrastForBase,
608
+ charWidth = props.charWidth,
609
+ charHeight = props.charHeight;
610
+
611
+ var _ref4 = colorBy || {},
612
+ _ref4$tag = _ref4.tag,
613
+ tag = _ref4$tag === void 0 ? '' : _ref4$tag,
614
+ _ref4$type = _ref4.type,
615
+ colorType = _ref4$type === void 0 ? '' : _ref4$type;
616
+
617
+ var feature = feat.feature;
618
+ var region = regions[0]; // first pass for simple color changes that change the color of the
619
+ // alignment
620
+
621
+ switch (colorType) {
622
+ case 'insertSize':
623
+ ctx.fillStyle = this.colorByInsertSize(feature, config);
624
+ break;
625
+
626
+ case 'strand':
627
+ ctx.fillStyle = feature.get('strand') === -1 ? '#8F8FD8' : '#EC8B8B';
628
+ break;
629
+
630
+ case 'mappingQuality':
631
+ ctx.fillStyle = "hsl(".concat(feature.get('mq'), ",50%,50%)");
632
+ break;
633
+
634
+ case 'pairOrientation':
635
+ ctx.fillStyle = this.colorByOrientation(feature, config);
636
+ break;
637
+
638
+ case 'stranded':
639
+ ctx.fillStyle = alignmentColoring[this.colorByStranded(feature, config)];
640
+ break;
641
+
642
+ case 'xs':
643
+ case 'tag':
644
+ {
645
+ var tags = feature.get('tags');
646
+ var val = tags ? tags[tag] : feature.get(tag); // special for for XS/TS tag
647
+
648
+ if (tag === 'XS' || tag === 'TS') {
649
+ var map = {
650
+ '-': 'color_rev_strand',
651
+ '+': 'color_fwd_strand'
652
+ };
653
+ ctx.fillStyle = alignmentColoring[map[val] || 'color_nostrand'];
654
+ } // lower case 'ts' from minimap2 is flipped from xs
655
+
656
+
657
+ if (tag === 'ts') {
658
+ var _map = {
659
+ '-': feature.get('strand') === -1 ? 'color_fwd_strand' : 'color_rev_strand',
660
+ '+': feature.get('strand') === -1 ? 'color_rev_strand' : 'color_fwd_strand'
661
+ };
662
+ ctx.fillStyle = alignmentColoring[_map[val] || 'color_nostrand'];
663
+ } // tag is not one of the autofilled tags, has color-value pairs from
664
+ // fetchValues
665
+ else {
666
+ var foundValue = colorTagMap[val];
667
+ ctx.fillStyle = foundValue || alignmentColoring['color_nostrand'];
668
+ }
669
+
670
+ break;
671
+ }
672
+
673
+ case 'insertSizeAndPairOrientation':
674
+ break;
675
+
676
+ case 'modifications':
677
+ case 'methylation':
678
+ // this coloring is similar to igv.js, and is helpful to color negative
679
+ // strand reads differently because their c-g will be flipped (e.g. g-c
680
+ // read right to left)
681
+ if (feature.get('flags') & 16) {
682
+ ctx.fillStyle = '#c8dcc8';
683
+ } else {
684
+ ctx.fillStyle = '#c8c8c8';
685
+ }
686
+
687
+ break;
688
+
689
+ case 'normal':
690
+ default:
691
+ if (defaultColor) {
692
+ // avoid a readConfObject call here
693
+ ctx.fillStyle = '#c8c8c8';
694
+ } else {
695
+ ctx.fillStyle = (0, _configuration.readConfObject)(config, 'color', {
696
+ feature: feature
697
+ });
698
+ }
699
+
700
+ break;
701
+ }
702
+
703
+ this.drawRect(ctx, feat, props); // second pass for color types that render per-base things that go over the
704
+ // existing drawing
705
+
706
+ switch (colorType) {
707
+ case 'perBaseQuality':
708
+ this.colorByPerBaseQuality(ctx, feat, config, region, bpPerPx);
709
+ break;
710
+
711
+ case 'perBaseLettering':
712
+ this.colorByPerBaseLettering(ctx, feat, config, region, bpPerPx, {
713
+ colorForBase: colorForBase,
714
+ contrastForBase: contrastForBase,
715
+ charWidth: charWidth,
716
+ charHeight: charHeight
717
+ });
718
+ break;
719
+
720
+ case 'modifications':
721
+ this.colorByModifications(ctx, feat, config, region, bpPerPx, props);
722
+ break;
723
+
724
+ case 'methylation':
725
+ this.colorByMethylation(ctx, feat, config, region, bpPerPx, props);
726
+ break;
727
+ }
728
+ }
729
+ }, {
730
+ key: "drawMismatches",
731
+ value: function drawMismatches(ctx, feat, props, opts) {
732
+ var minSubfeatureWidth = opts.minSubfeatureWidth,
733
+ largeInsertionIndicatorScale = opts.largeInsertionIndicatorScale,
734
+ mismatchAlpha = opts.mismatchAlpha,
735
+ _opts$drawSNPs = opts.drawSNPs,
736
+ drawSNPs = _opts$drawSNPs === void 0 ? true : _opts$drawSNPs,
737
+ _opts$drawIndels = opts.drawIndels,
738
+ drawIndels = _opts$drawIndels === void 0 ? true : _opts$drawIndels,
739
+ charWidth = opts.charWidth,
740
+ charHeight = opts.charHeight,
741
+ colorForBase = opts.colorForBase,
742
+ contrastForBase = opts.contrastForBase;
743
+ var bpPerPx = props.bpPerPx,
744
+ regions = props.regions;
745
+ var heightPx = feat.heightPx,
746
+ topPx = feat.topPx,
747
+ feature = feat.feature;
748
+
749
+ var _regions2 = (0, _slicedToArray2["default"])(regions, 1),
750
+ region = _regions2[0];
751
+
752
+ var start = feature.get('start');
753
+ var pxPerBp = Math.min(1 / bpPerPx, 2);
754
+ var w = Math.max(minSubfeatureWidth, pxPerBp);
755
+ var mismatches = feature.get('mismatches');
756
+ var heightLim = charHeight - 2;
757
+
758
+ function getAlphaColor(baseColor, mismatch) {
759
+ var color = baseColor;
760
+
761
+ if (mismatchAlpha && mismatch.qual !== undefined) {
762
+ color = (0, _color["default"])(baseColor).alpha(Math.min(1, mismatch.qual / 50)).hsl().string();
763
+ }
764
+
765
+ return color;
766
+ } // extraHorizontallyFlippedOffset is used to draw interbase items, which
767
+ // are located to the left when forward and right when reversed
768
+
769
+
770
+ var extraHorizontallyFlippedOffset = region.reversed ? 1 / bpPerPx + 1 : -1; // two pass rendering: first pass, draw all the mismatches except wide
771
+ // insertion markers
772
+
773
+ for (var i = 0; i < mismatches.length; i += 1) {
774
+ var mismatch = mismatches[i];
775
+ var mstart = start + mismatch.start;
776
+ var mlen = mismatch.length;
777
+ var mbase = mismatch.base;
778
+
779
+ var _bpSpanPx17 = (0, _util.bpSpanPx)(mstart, mstart + mlen, region, bpPerPx),
780
+ _bpSpanPx18 = (0, _slicedToArray2["default"])(_bpSpanPx17, 2),
781
+ leftPx = _bpSpanPx18[0],
782
+ rightPx = _bpSpanPx18[1];
783
+
784
+ var widthPx = Math.max(minSubfeatureWidth, Math.abs(leftPx - rightPx));
785
+
786
+ if (mismatch.type === 'mismatch' && drawSNPs) {
787
+ var baseColor = colorForBase[mismatch.base] || '#888';
788
+ ctx.fillStyle = getAlphaColor(baseColor, mismatch);
789
+ ctx.fillRect(leftPx, topPx, widthPx, heightPx);
790
+
791
+ if (widthPx >= charWidth && heightPx >= heightLim) {
792
+ // normal SNP coloring
793
+ var contrast = contrastForBase[mismatch.base] || 'black';
794
+ ctx.fillStyle = getAlphaColor(contrast, mismatch);
795
+ ctx.fillText(mbase, leftPx + (widthPx - charWidth) / 2 + 1, topPx + heightPx);
796
+ }
797
+ } else if (mismatch.type === 'deletion' && drawIndels) {
798
+ var _baseColor = colorForBase.deletion;
799
+ ctx.fillStyle = _baseColor;
800
+ ctx.fillRect(leftPx, topPx, widthPx, heightPx);
801
+ var txt = "".concat(mismatch.length);
802
+ var rwidth = (0, _util.measureText)(txt, 10);
803
+
804
+ if (widthPx >= rwidth && heightPx >= heightLim) {
805
+ ctx.fillStyle = contrastForBase.deletion;
806
+ ctx.fillText(txt, (leftPx + rightPx) / 2 - rwidth / 2, topPx + heightPx);
807
+ }
808
+ } else if (mismatch.type === 'insertion' && drawIndels) {
809
+ ctx.fillStyle = 'purple';
810
+ var pos = leftPx + extraHorizontallyFlippedOffset;
811
+ var len = +mismatch.base || mismatch.length;
812
+ var insW = Math.max(minSubfeatureWidth, Math.min(1.2, 1 / bpPerPx));
813
+
814
+ if (len < 10) {
815
+ ctx.fillRect(pos, topPx, insW, heightPx);
816
+
817
+ if (1 / bpPerPx >= charWidth && heightPx >= heightLim) {
818
+ ctx.fillRect(pos - insW, topPx, insW * 3, 1);
819
+ ctx.fillRect(pos - insW, topPx + heightPx - 1, insW * 3, 1);
820
+ ctx.fillText("(".concat(mismatch.base, ")"), pos + 3, topPx + heightPx);
821
+ }
822
+ }
823
+ } else if (mismatch.type === 'hardclip' || mismatch.type === 'softclip') {
824
+ ctx.fillStyle = mismatch.type === 'hardclip' ? 'red' : 'blue';
825
+
826
+ var _pos = leftPx + extraHorizontallyFlippedOffset;
827
+
828
+ ctx.fillRect(_pos, topPx, w, heightPx);
829
+
830
+ if (1 / bpPerPx >= charWidth && heightPx >= heightLim) {
831
+ ctx.fillRect(_pos - w, topPx, w * 3, 1);
832
+ ctx.fillRect(_pos - w, topPx + heightPx - 1, w * 3, 1);
833
+ ctx.fillText("(".concat(mismatch.base, ")"), _pos + 3, topPx + heightPx);
834
+ }
835
+ } else if (mismatch.type === 'skip') {
836
+ // fix to avoid bad rendering note that this was also related to chrome
837
+ // bug https://bugs.chromium.org/p/chromium/issues/detail?id=1131528
838
+ // also affected firefox ref #1236 #2750
839
+ if (leftPx + widthPx > 0) {
840
+ // make small exons more visible when zoomed far out
841
+ var adjustPx = widthPx - (bpPerPx > 10 ? 1.5 : 0);
842
+ ctx.clearRect(leftPx, topPx, adjustPx, heightPx);
843
+ ctx.fillStyle = '#333';
844
+ ctx.fillRect(Math.max(0, leftPx), topPx + heightPx / 2 - 1, adjustPx + (leftPx < 0 ? leftPx : 0), 2);
845
+ }
846
+ }
847
+ } // second pass, draw wide insertion markers on top
848
+
849
+
850
+ if (drawIndels) {
851
+ for (var _i2 = 0; _i2 < mismatches.length; _i2 += 1) {
852
+ var _mismatch = mismatches[_i2];
853
+
854
+ var _mstart = start + _mismatch.start;
855
+
856
+ var _mlen = _mismatch.length;
857
+
858
+ var _bpSpanPx19 = (0, _util.bpSpanPx)(_mstart, _mstart + _mlen, region, bpPerPx),
859
+ _bpSpanPx20 = (0, _slicedToArray2["default"])(_bpSpanPx19, 1),
860
+ _leftPx2 = _bpSpanPx20[0];
861
+
862
+ var _len2 = +_mismatch.base || _mismatch.length;
863
+
864
+ var _txt = "".concat(_len2);
865
+
866
+ if (_mismatch.type === 'insertion' && _len2 >= 10) {
867
+ if (bpPerPx > largeInsertionIndicatorScale) {
868
+ ctx.fillStyle = 'purple';
869
+ ctx.fillRect(_leftPx2 - 1, topPx, 2, heightPx);
870
+ } else if (heightPx > charHeight) {
871
+ var rect = ctx.measureText(_txt);
872
+ var padding = 5;
873
+ ctx.fillStyle = 'purple';
874
+ ctx.fillRect(_leftPx2 - rect.width / 2 - padding, topPx, rect.width + 2 * padding, heightPx);
875
+ ctx.fillStyle = 'white';
876
+ ctx.fillText(_txt, _leftPx2 - rect.width / 2, topPx + heightPx);
877
+ } else {
878
+ var _padding = 2;
879
+ ctx.fillStyle = 'purple';
880
+ ctx.fillRect(_leftPx2 - _padding, topPx, 2 * _padding, heightPx);
881
+ }
882
+ }
883
+ }
884
+ }
885
+ }
886
+ }, {
887
+ key: "drawSoftClipping",
888
+ value: function drawSoftClipping(ctx, feat, props, config, theme) {
889
+ var feature = feat.feature,
890
+ topPx = feat.topPx,
891
+ heightPx = feat.heightPx;
892
+ var regions = props.regions,
893
+ bpPerPx = props.bpPerPx;
894
+
895
+ var _regions3 = (0, _slicedToArray2["default"])(regions, 1),
896
+ region = _regions3[0];
897
+
898
+ var minFeatWidth = (0, _configuration.readConfObject)(config, 'minSubfeatureWidth');
899
+ var mismatches = feature.get('mismatches');
900
+ var seq = feature.get('seq');
901
+
902
+ var _this$getCharWidthHei = this.getCharWidthHeight(ctx),
903
+ charWidth = _this$getCharWidthHei.charWidth,
904
+ charHeight = _this$getCharWidthHei.charHeight;
905
+
906
+ var colorForBase = {
907
+ A: theme.palette.bases.A.main,
908
+ C: theme.palette.bases.C.main,
909
+ G: theme.palette.bases.G.main,
910
+ T: theme.palette.bases.T.main,
911
+ deletion: '#808080' // gray
912
+
913
+ }; // Display all bases softclipped off in lightened colors
914
+
915
+ if (seq) {
916
+ mismatches.filter(function (mismatch) {
917
+ return mismatch.type === 'softclip';
918
+ }).forEach(function (mismatch) {
919
+ var softClipLength = mismatch.cliplen || 0;
920
+ var s = feature.get('start');
921
+ var softClipStart = mismatch.start === 0 ? s - softClipLength : s + mismatch.start;
922
+
923
+ for (var k = 0; k < softClipLength; k += 1) {
924
+ var base = seq.charAt(k + mismatch.start); // If softclip length+start is longer than sequence, no need to
925
+ // continue showing base
926
+
927
+ if (!base) {
928
+ return;
929
+ }
930
+
931
+ var _bpSpanPx21 = (0, _util.bpSpanPx)(softClipStart + k, softClipStart + k + 1, region, bpPerPx),
932
+ _bpSpanPx22 = (0, _slicedToArray2["default"])(_bpSpanPx21, 2),
933
+ softClipLeftPx = _bpSpanPx22[0],
934
+ softClipRightPx = _bpSpanPx22[1];
935
+
936
+ var softClipWidthPx = Math.max(minFeatWidth, Math.abs(softClipLeftPx - softClipRightPx)); // Black accounts for IUPAC ambiguity code bases such as N that
937
+ // show in soft clipping
938
+
939
+ var baseColor = colorForBase[base] || '#000000';
940
+ ctx.fillStyle = baseColor;
941
+ ctx.fillRect(softClipLeftPx, topPx, softClipWidthPx, heightPx);
942
+
943
+ if (softClipWidthPx >= charWidth && heightPx >= charHeight - 5) {
944
+ ctx.fillStyle = theme.palette.getContrastText(baseColor);
945
+ ctx.fillText(base, softClipLeftPx + (softClipWidthPx - charWidth) / 2 + 1, topPx + heightPx);
946
+ }
947
+ }
948
+ });
949
+ }
950
+ }
951
+ }, {
952
+ key: "makeImageData",
953
+ value: function () {
954
+ var _makeImageData = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(ctx, layoutRecords, props) {
955
+ var _this2 = this;
956
+
957
+ var layout, config, showSoftClip, colorBy, configTheme, mismatchAlpha, minSubfeatureWidth, insertScale, defaultColor, theme, colorForBase, contrastForBase, _this$getCharWidthHei2, charWidth, charHeight;
958
+
959
+ return _regenerator["default"].wrap(function _callee$(_context) {
960
+ while (1) {
961
+ switch (_context.prev = _context.next) {
962
+ case 0:
963
+ layout = props.layout, config = props.config, showSoftClip = props.showSoftClip, colorBy = props.colorBy, configTheme = props.theme;
964
+ mismatchAlpha = (0, _configuration.readConfObject)(config, 'mismatchAlpha');
965
+ minSubfeatureWidth = (0, _configuration.readConfObject)(config, 'minSubfeatureWidth');
966
+ insertScale = (0, _configuration.readConfObject)(config, 'largeInsertionIndicatorScale');
967
+ defaultColor = (0, _configuration.readConfObject)(config, 'color') === '#f0f';
968
+ theme = (0, _ui.createJBrowseTheme)(configTheme);
969
+ colorForBase = getColorBaseMap(theme);
970
+ contrastForBase = getContrastBaseMap(theme);
971
+
972
+ if (layout) {
973
+ _context.next = 10;
974
+ break;
975
+ }
976
+
977
+ throw new Error("layout required");
978
+
979
+ case 10:
980
+ if (layout.addRect) {
981
+ _context.next = 12;
982
+ break;
983
+ }
984
+
985
+ throw new Error('invalid layout object');
986
+
987
+ case 12:
988
+ ctx.font = 'bold 10px Courier New,monospace';
989
+ _this$getCharWidthHei2 = this.getCharWidthHeight(ctx), charWidth = _this$getCharWidthHei2.charWidth, charHeight = _this$getCharWidthHei2.charHeight;
990
+ layoutRecords.forEach(function (feat) {
991
+ if (feat === null) {
992
+ return;
993
+ }
994
+
995
+ _this2.drawAlignmentRect(ctx, feat, _objectSpread(_objectSpread({}, props), {}, {
996
+ defaultColor: defaultColor,
997
+ colorForBase: colorForBase,
998
+ contrastForBase: contrastForBase,
999
+ charWidth: charWidth,
1000
+ charHeight: charHeight
1001
+ }));
1002
+
1003
+ _this2.drawMismatches(ctx, feat, props, {
1004
+ mismatchAlpha: mismatchAlpha,
1005
+ drawSNPs: shouldDrawMismatches(colorBy === null || colorBy === void 0 ? void 0 : colorBy.type),
1006
+ drawIndels: shouldDrawMismatches(colorBy === null || colorBy === void 0 ? void 0 : colorBy.type),
1007
+ largeInsertionIndicatorScale: insertScale,
1008
+ minSubfeatureWidth: minSubfeatureWidth,
1009
+ charWidth: charWidth,
1010
+ charHeight: charHeight,
1011
+ colorForBase: colorForBase,
1012
+ contrastForBase: contrastForBase
1013
+ });
1014
+
1015
+ if (showSoftClip) {
1016
+ _this2.drawSoftClipping(ctx, feat, props, config, theme);
1017
+ }
1018
+ });
1019
+
1020
+ case 15:
1021
+ case "end":
1022
+ return _context.stop();
1023
+ }
1024
+ }
1025
+ }, _callee, this);
1026
+ }));
1027
+
1028
+ function makeImageData(_x, _x2, _x3) {
1029
+ return _makeImageData.apply(this, arguments);
1030
+ }
1031
+
1032
+ return makeImageData;
1033
+ }() // we perform a full layout before render as a separate method because the
1034
+ // layout determines the height of the canvas that we use to render
1035
+
1036
+ }, {
1037
+ key: "layoutFeats",
1038
+ value: function layoutFeats(props) {
1039
+ var _this3 = this;
1040
+
1041
+ var layout = props.layout,
1042
+ features = props.features,
1043
+ sortedBy = props.sortedBy,
1044
+ config = props.config,
1045
+ bpPerPx = props.bpPerPx,
1046
+ showSoftClip = props.showSoftClip,
1047
+ regions = props.regions;
1048
+
1049
+ var _regions4 = (0, _slicedToArray2["default"])(regions, 1),
1050
+ region = _regions4[0];
1051
+
1052
+ if (!layout) {
1053
+ throw new Error("layout required");
1054
+ }
1055
+
1056
+ if (!layout.addRect) {
1057
+ throw new Error('invalid layout object');
1058
+ }
1059
+
1060
+ var featureMap = sortedBy !== null && sortedBy !== void 0 && sortedBy.type && region.start === sortedBy.pos ? (0, _sortUtil.sortFeature)(features, sortedBy) : features;
1061
+ var heightPx = (0, _configuration.readConfObject)(config, 'height');
1062
+ var displayMode = (0, _configuration.readConfObject)(config, 'displayMode');
1063
+ var layoutRecords = (0, _util.iterMap)(featureMap.values(), function (feature) {
1064
+ return _this3.layoutFeature({
1065
+ feature: feature,
1066
+ layout: layout,
1067
+ bpPerPx: bpPerPx,
1068
+ region: region,
1069
+ showSoftClip: showSoftClip,
1070
+ heightPx: heightPx,
1071
+ displayMode: displayMode
1072
+ });
1073
+ }, featureMap.size);
1074
+ return layoutRecords;
1075
+ }
1076
+ }, {
1077
+ key: "fetchSequence",
1078
+ value: function () {
1079
+ var _fetchSequence2 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(renderProps) {
1080
+ var sessionId, regions, adapterConfig, sequenceAdapter, _yield$getAdapter, dataAdapter, _regions5, region;
1081
+
1082
+ return _regenerator["default"].wrap(function _callee2$(_context2) {
1083
+ while (1) {
1084
+ switch (_context2.prev = _context2.next) {
1085
+ case 0:
1086
+ sessionId = renderProps.sessionId, regions = renderProps.regions, adapterConfig = renderProps.adapterConfig;
1087
+ sequenceAdapter = adapterConfig.sequenceAdapter;
1088
+
1089
+ if (sequenceAdapter) {
1090
+ _context2.next = 4;
1091
+ break;
1092
+ }
1093
+
1094
+ return _context2.abrupt("return", undefined);
1095
+
1096
+ case 4:
1097
+ _context2.next = 6;
1098
+ return (0, _dataAdapterCache.getAdapter)(this.pluginManager, sessionId, sequenceAdapter);
1099
+
1100
+ case 6:
1101
+ _yield$getAdapter = _context2.sent;
1102
+ dataAdapter = _yield$getAdapter.dataAdapter;
1103
+ _regions5 = (0, _slicedToArray2["default"])(regions, 1), region = _regions5[0];
1104
+ return _context2.abrupt("return", (0, _util2.fetchSequence)(region, dataAdapter));
1105
+
1106
+ case 10:
1107
+ case "end":
1108
+ return _context2.stop();
1109
+ }
1110
+ }
1111
+ }, _callee2, this);
1112
+ }));
1113
+
1114
+ function fetchSequence(_x4) {
1115
+ return _fetchSequence2.apply(this, arguments);
1116
+ }
1117
+
1118
+ return fetchSequence;
1119
+ }()
1120
+ }, {
1121
+ key: "render",
1122
+ value: function () {
1123
+ var _render = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(renderProps) {
1124
+ var _renderProps$colorBy,
1125
+ _this4 = this;
1126
+
1127
+ var features, layout, regions, bpPerPx, layoutRecords, _regions6, region, regionSequence, end, start, width, height, res, results;
1128
+
1129
+ return _regenerator["default"].wrap(function _callee3$(_context3) {
1130
+ while (1) {
1131
+ switch (_context3.prev = _context3.next) {
1132
+ case 0:
1133
+ _context3.next = 2;
1134
+ return this.getFeatures(renderProps);
1135
+
1136
+ case 2:
1137
+ features = _context3.sent;
1138
+ layout = this.createLayoutInWorker(renderProps);
1139
+ regions = renderProps.regions, bpPerPx = renderProps.bpPerPx;
1140
+ layoutRecords = this.layoutFeats(_objectSpread(_objectSpread({}, renderProps), {}, {
1141
+ features: features,
1142
+ layout: layout
1143
+ }));
1144
+ _regions6 = (0, _slicedToArray2["default"])(regions, 1), region = _regions6[0]; // only need reference sequence if there are features and only for some
1145
+ // cases
1146
+
1147
+ if (!(features.size && (0, _util2.shouldFetchReferenceSequence)((_renderProps$colorBy = renderProps.colorBy) === null || _renderProps$colorBy === void 0 ? void 0 : _renderProps$colorBy.type))) {
1148
+ _context3.next = 13;
1149
+ break;
1150
+ }
1151
+
1152
+ _context3.next = 10;
1153
+ return this.fetchSequence(renderProps);
1154
+
1155
+ case 10:
1156
+ _context3.t0 = _context3.sent;
1157
+ _context3.next = 14;
1158
+ break;
1159
+
1160
+ case 13:
1161
+ _context3.t0 = undefined;
1162
+
1163
+ case 14:
1164
+ regionSequence = _context3.t0;
1165
+ end = region.end, start = region.start;
1166
+ width = (end - start) / bpPerPx;
1167
+ height = Math.max(layout.getTotalHeight(), 1);
1168
+ _context3.next = 20;
1169
+ return (0, _offscreenCanvasUtils.renderToAbstractCanvas)(width, height, renderProps, function (ctx) {
1170
+ return _this4.makeImageData(ctx, layoutRecords, _objectSpread(_objectSpread({}, renderProps), {}, {
1171
+ layout: layout,
1172
+ features: features,
1173
+ regionSequence: regionSequence
1174
+ }));
1175
+ });
1176
+
1177
+ case 20:
1178
+ res = _context3.sent;
1179
+ _context3.next = 23;
1180
+ return (0, _get2["default"])((0, _getPrototypeOf2["default"])(PileupRenderer.prototype), "render", this).call(this, _objectSpread(_objectSpread(_objectSpread({}, renderProps), res), {}, {
1181
+ features: features,
1182
+ layout: layout,
1183
+ height: height,
1184
+ width: width
1185
+ }));
1186
+
1187
+ case 23:
1188
+ results = _context3.sent;
1189
+ return _context3.abrupt("return", _objectSpread(_objectSpread(_objectSpread({}, results), res), {}, {
1190
+ features: features,
1191
+ layout: layout,
1192
+ height: height,
1193
+ width: width,
1194
+ maxHeightReached: layout.maxHeightReached
1195
+ }));
1196
+
1197
+ case 25:
1198
+ case "end":
1199
+ return _context3.stop();
1200
+ }
1201
+ }
1202
+ }, _callee3, this);
1203
+ }));
1204
+
1205
+ function render(_x5) {
1206
+ return _render.apply(this, arguments);
1207
+ }
1208
+
1209
+ return render;
1210
+ }()
1211
+ }, {
1212
+ key: "createSession",
1213
+ value: function createSession(args) {
1214
+ return new _PileupLayoutSession.PileupLayoutSession(args);
1215
+ }
1216
+ }]);
1217
+ return PileupRenderer;
1218
+ }(_BoxRendererType2["default"]);
1219
+
1220
+ exports["default"] = PileupRenderer;