@jbrowse/plugin-linear-genome-view 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.
Files changed (147) hide show
  1. package/dist/BaseLinearDisplay/components/LinearBlocks.d.ts +2 -2
  2. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +2 -22
  3. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
  4. package/dist/BaseLinearDisplay/components/Tooltip.d.ts +1 -1
  5. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +149 -4
  6. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +600 -464
  7. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
  8. package/dist/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js +19 -1
  9. package/dist/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js.map +1 -1
  10. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +2 -2
  11. package/dist/BasicTrack/configSchema.d.ts +3 -0
  12. package/dist/BasicTrack/configSchema.js +18 -0
  13. package/dist/BasicTrack/configSchema.js.map +1 -0
  14. package/dist/BasicTrack/index.d.ts +3 -0
  15. package/dist/BasicTrack/index.js +18 -0
  16. package/dist/BasicTrack/index.js.map +1 -0
  17. package/dist/FeatureTrack/configSchema.d.ts +3 -0
  18. package/dist/FeatureTrack/configSchema.js +21 -0
  19. package/dist/FeatureTrack/configSchema.js.map +1 -0
  20. package/dist/FeatureTrack/index.d.ts +3 -0
  21. package/dist/FeatureTrack/index.js +18 -0
  22. package/dist/FeatureTrack/index.js.map +1 -0
  23. package/dist/LinearBareDisplay/configSchema.d.ts +5 -1
  24. package/dist/LinearBareDisplay/configSchema.js +12 -1
  25. package/dist/LinearBareDisplay/configSchema.js.map +1 -1
  26. package/dist/LinearBareDisplay/model.d.ts +16 -0
  27. package/dist/LinearBareDisplay/model.js +16 -0
  28. package/dist/LinearBareDisplay/model.js.map +1 -1
  29. package/dist/LinearBasicDisplay/configSchema.d.ts +5 -1
  30. package/dist/LinearBasicDisplay/configSchema.js +16 -1
  31. package/dist/LinearBasicDisplay/configSchema.js.map +1 -1
  32. package/dist/LinearBasicDisplay/model.d.ts +79 -8
  33. package/dist/LinearBasicDisplay/model.js +167 -111
  34. package/dist/LinearBasicDisplay/model.js.map +1 -1
  35. package/dist/LinearGenomeView/components/CenterLine.d.ts +1 -1
  36. package/dist/LinearGenomeView/components/GetSequenceDialog.d.ts +1 -1
  37. package/dist/LinearGenomeView/components/Gridlines.d.ts +1 -1
  38. package/dist/LinearGenomeView/components/Header.d.ts +1 -1
  39. package/dist/LinearGenomeView/components/ImportForm.d.ts +1 -1
  40. package/dist/LinearGenomeView/components/ImportForm.js +38 -31
  41. package/dist/LinearGenomeView/components/ImportForm.js.map +1 -1
  42. package/dist/LinearGenomeView/components/LinearGenomeView.d.ts +1 -1
  43. package/dist/LinearGenomeView/components/LinearGenomeView.js +2 -24
  44. package/dist/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
  45. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.d.ts +1 -1
  46. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js +7 -5
  47. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -1
  48. package/dist/LinearGenomeView/components/OverviewRubberBand.d.ts +1 -1
  49. package/dist/LinearGenomeView/components/OverviewScaleBar.d.ts +27 -35
  50. package/dist/LinearGenomeView/components/RubberBand.d.ts +1 -1
  51. package/dist/LinearGenomeView/components/ScaleBar.d.ts +1 -1
  52. package/dist/LinearGenomeView/components/TrackContainer.d.ts +1 -1
  53. package/dist/LinearGenomeView/components/TrackContainer.js +12 -9
  54. package/dist/LinearGenomeView/components/TrackContainer.js.map +1 -1
  55. package/dist/LinearGenomeView/components/TrackLabel.js +9 -1
  56. package/dist/LinearGenomeView/components/TrackLabel.js.map +1 -1
  57. package/dist/LinearGenomeView/components/TracksContainer.d.ts +1 -1
  58. package/dist/LinearGenomeView/index.d.ts +273 -18
  59. package/dist/LinearGenomeView/index.js +409 -90
  60. package/dist/LinearGenomeView/index.js.map +1 -1
  61. package/dist/index.d.ts +30 -90
  62. package/dist/index.js +4 -25
  63. package/dist/index.js.map +1 -1
  64. package/esm/BaseLinearDisplay/components/LinearBlocks.d.ts +2 -2
  65. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +2 -22
  66. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
  67. package/esm/BaseLinearDisplay/components/Tooltip.d.ts +1 -1
  68. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +149 -4
  69. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +600 -464
  70. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
  71. package/esm/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js +19 -1
  72. package/esm/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.js.map +1 -1
  73. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +2 -2
  74. package/esm/BasicTrack/configSchema.d.ts +3 -0
  75. package/esm/BasicTrack/configSchema.js +16 -0
  76. package/esm/BasicTrack/configSchema.js.map +1 -0
  77. package/esm/BasicTrack/index.d.ts +3 -0
  78. package/esm/BasicTrack/index.js +13 -0
  79. package/esm/BasicTrack/index.js.map +1 -0
  80. package/esm/FeatureTrack/configSchema.d.ts +3 -0
  81. package/esm/FeatureTrack/configSchema.js +19 -0
  82. package/esm/FeatureTrack/configSchema.js.map +1 -0
  83. package/esm/FeatureTrack/index.d.ts +3 -0
  84. package/esm/FeatureTrack/index.js +13 -0
  85. package/esm/FeatureTrack/index.js.map +1 -0
  86. package/esm/LinearBareDisplay/configSchema.d.ts +5 -1
  87. package/esm/LinearBareDisplay/configSchema.js +14 -2
  88. package/esm/LinearBareDisplay/configSchema.js.map +1 -1
  89. package/esm/LinearBareDisplay/model.d.ts +16 -0
  90. package/esm/LinearBareDisplay/model.js +16 -0
  91. package/esm/LinearBareDisplay/model.js.map +1 -1
  92. package/esm/LinearBasicDisplay/configSchema.d.ts +5 -1
  93. package/esm/LinearBasicDisplay/configSchema.js +18 -2
  94. package/esm/LinearBasicDisplay/configSchema.js.map +1 -1
  95. package/esm/LinearBasicDisplay/model.d.ts +79 -8
  96. package/esm/LinearBasicDisplay/model.js +167 -111
  97. package/esm/LinearBasicDisplay/model.js.map +1 -1
  98. package/esm/LinearGenomeView/components/CenterLine.d.ts +1 -1
  99. package/esm/LinearGenomeView/components/GetSequenceDialog.d.ts +1 -1
  100. package/esm/LinearGenomeView/components/Gridlines.d.ts +1 -1
  101. package/esm/LinearGenomeView/components/Header.d.ts +1 -1
  102. package/esm/LinearGenomeView/components/ImportForm.d.ts +1 -1
  103. package/esm/LinearGenomeView/components/ImportForm.js +40 -33
  104. package/esm/LinearGenomeView/components/ImportForm.js.map +1 -1
  105. package/esm/LinearGenomeView/components/LinearGenomeView.d.ts +1 -1
  106. package/esm/LinearGenomeView/components/LinearGenomeView.js +3 -25
  107. package/esm/LinearGenomeView/components/LinearGenomeView.js.map +1 -1
  108. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.d.ts +1 -1
  109. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js +7 -5
  110. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js.map +1 -1
  111. package/esm/LinearGenomeView/components/OverviewRubberBand.d.ts +1 -1
  112. package/esm/LinearGenomeView/components/OverviewScaleBar.d.ts +27 -35
  113. package/esm/LinearGenomeView/components/RubberBand.d.ts +1 -1
  114. package/esm/LinearGenomeView/components/ScaleBar.d.ts +1 -1
  115. package/esm/LinearGenomeView/components/TrackContainer.d.ts +1 -1
  116. package/esm/LinearGenomeView/components/TrackContainer.js +13 -10
  117. package/esm/LinearGenomeView/components/TrackContainer.js.map +1 -1
  118. package/esm/LinearGenomeView/components/TrackLabel.js +9 -1
  119. package/esm/LinearGenomeView/components/TrackLabel.js.map +1 -1
  120. package/esm/LinearGenomeView/components/TracksContainer.d.ts +1 -1
  121. package/esm/LinearGenomeView/index.d.ts +273 -18
  122. package/esm/LinearGenomeView/index.js +409 -90
  123. package/esm/LinearGenomeView/index.js.map +1 -1
  124. package/esm/index.d.ts +30 -90
  125. package/esm/index.js +4 -25
  126. package/esm/index.js.map +1 -1
  127. package/package.json +2 -2
  128. package/src/BaseLinearDisplay/components/ServerSideRenderedBlockContent.tsx +2 -24
  129. package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +695 -555
  130. package/src/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.ts +20 -1
  131. package/src/BasicTrack/configSchema.ts +23 -0
  132. package/src/BasicTrack/index.ts +18 -0
  133. package/src/FeatureTrack/configSchema.ts +27 -0
  134. package/src/FeatureTrack/index.ts +21 -0
  135. package/src/LinearBareDisplay/configSchema.ts +15 -2
  136. package/src/LinearBareDisplay/model.ts +16 -0
  137. package/src/LinearBasicDisplay/configSchema.ts +19 -2
  138. package/src/LinearBasicDisplay/model.ts +63 -11
  139. package/src/LinearGenomeView/components/ImportForm.tsx +85 -67
  140. package/src/LinearGenomeView/components/LinearGenomeView.tsx +3 -33
  141. package/src/LinearGenomeView/components/LinearGenomeViewSvg.tsx +21 -18
  142. package/src/LinearGenomeView/components/TrackContainer.tsx +38 -27
  143. package/src/LinearGenomeView/components/TrackLabel.tsx +10 -1
  144. package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.tsx.snap +204 -204
  145. package/src/LinearGenomeView/index.test.ts +33 -26
  146. package/src/LinearGenomeView/index.tsx +471 -133
  147. package/src/index.ts +6 -46
@@ -59,6 +59,7 @@ import Header from './components/Header'
59
59
  import ZoomControls from './components/ZoomControls'
60
60
  import LinearGenomeView from './components/LinearGenomeView'
61
61
 
62
+ // lazies
62
63
  const SequenceSearchDialog = lazy(
63
64
  () => import('./components/SequenceSearchDialog'),
64
65
  )
@@ -78,6 +79,8 @@ export interface BpOffset {
78
79
  export interface ExportSvgOptions {
79
80
  rasterizeLayers?: boolean
80
81
  filename?: string
82
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
+ Wrapper?: React.FC<any>
81
84
  }
82
85
 
83
86
  function calculateVisibleLocStrings(contentBlocks: BaseBlock[]) {
@@ -85,7 +88,7 @@ function calculateVisibleLocStrings(contentBlocks: BaseBlock[]) {
85
88
  return ''
86
89
  }
87
90
  const isSingleAssemblyName = contentBlocks.every(
88
- block => block.assemblyName === contentBlocks[0].assemblyName,
91
+ b => b.assemblyName === contentBlocks[0].assemblyName,
89
92
  )
90
93
  const locs = contentBlocks.map(block =>
91
94
  assembleLocString({
@@ -119,41 +122,110 @@ function localStorageGetItem(item: string) {
119
122
  : undefined
120
123
  }
121
124
 
125
+ /**
126
+ * #stateModel LinearGenomeView
127
+ */
122
128
  export function stateModelFactory(pluginManager: PluginManager) {
123
129
  return types
124
130
  .compose(
125
131
  BaseViewModel,
126
132
  types.model('LinearGenomeView', {
133
+ /**
134
+ * #property
135
+ */
127
136
  id: ElementId,
128
- type: types.literal('LinearGenomeView'),
137
+
138
+ /**
139
+ * #property
140
+ * this is a string instead of the const literal 'LinearGenomeView' to reduce some
141
+ * typescripting strictness, but you should pass the string 'LinearGenomeView' to
142
+ * the model explicitly
143
+ */
144
+ type: types.literal('LinearGenomeView') as unknown as string,
145
+
146
+ /**
147
+ * #property
148
+ * corresponds roughly to the horizontal scroll of the LGV
149
+ */
129
150
  offsetPx: 0,
151
+
152
+ /**
153
+ * #property
154
+ * corresponds roughly to the zoom level, base-pairs per pixel
155
+ */
130
156
  bpPerPx: 1,
157
+
158
+ /**
159
+ * #property
160
+ * currently displayed regions, can be a single chromosome, arbitrary subsections,
161
+ * or the entire set of chromosomes in the genome, but it not advised to use the
162
+ * entire set of chromosomes if your assembly is very fragmented
163
+ */
131
164
  displayedRegions: types.array(MUIRegion),
132
165
 
133
- // we use an array for the tracks because the tracks are displayed in a
134
- // specific order that we need to keep.
166
+ /**
167
+ * #property
168
+ * array of currently displayed tracks state models instances
169
+ */
135
170
  tracks: types.array(
136
171
  pluginManager.pluggableMstType('track', 'stateModel'),
137
172
  ),
173
+
174
+ /**
175
+ * #property
176
+ * array of currently displayed tracks state model's
177
+ */
138
178
  hideHeader: false,
179
+
180
+ /**
181
+ * #property
182
+ */
139
183
  hideHeaderOverview: false,
184
+
185
+ /**
186
+ * #property
187
+ */
140
188
  hideNoTracksActive: false,
189
+
190
+ /**
191
+ * #property
192
+ */
141
193
  trackSelectorType: types.optional(
142
194
  types.enumeration(['hierarchical']),
143
195
  'hierarchical',
144
196
  ),
197
+
198
+ /**
199
+ * #property
200
+ * how to display the track labels, can be "overlapping", "offset", or "hidden"
201
+ */
145
202
  trackLabels: types.optional(
146
203
  types.string,
147
204
  () => localStorageGetItem('lgv-trackLabels') || 'overlapping',
148
205
  ),
206
+
207
+ /**
208
+ * #property
209
+ * show the "center line"
210
+ */
149
211
  showCenterLine: types.optional(types.boolean, () => {
150
212
  const setting = localStorageGetItem('lgv-showCenterLine')
151
213
  return setting !== undefined && setting !== null ? !!+setting : false
152
214
  }),
215
+
216
+ /**
217
+ * #property
218
+ * show the "cytobands" in the overview scale bar
219
+ */
153
220
  showCytobandsSetting: types.optional(types.boolean, () => {
154
221
  const setting = localStorageGetItem('lgv-showCytobands')
155
222
  return setting !== undefined && setting !== null ? !!+setting : true
156
223
  }),
224
+
225
+ /**
226
+ * #property
227
+ * show the "gridlines" in the track area
228
+ */
157
229
  showGridlines: true,
158
230
  }),
159
231
  )
@@ -178,6 +250,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
178
250
  seqDialogDisplayed: false,
179
251
  }))
180
252
  .views(self => ({
253
+ /**
254
+ * #getter
255
+ */
181
256
  get width(): number {
182
257
  if (self.volatileWidth === undefined) {
183
258
  throw new Error(
@@ -186,10 +261,16 @@ export function stateModelFactory(pluginManager: PluginManager) {
186
261
  }
187
262
  return self.volatileWidth
188
263
  },
264
+ /**
265
+ * #getter
266
+ */
189
267
  get interRegionPaddingWidth() {
190
268
  return INTER_REGION_PADDING_WIDTH
191
269
  },
192
270
 
271
+ /**
272
+ * #getter
273
+ */
193
274
  get assemblyNames() {
194
275
  return [
195
276
  ...new Set(self.displayedRegions.map(region => region.assemblyName)),
@@ -197,16 +278,25 @@ export function stateModelFactory(pluginManager: PluginManager) {
197
278
  },
198
279
  }))
199
280
  .views(self => ({
281
+ /**
282
+ * #method
283
+ */
200
284
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
201
285
  MiniControlsComponent(): React.FC<any> {
202
286
  return MiniControls
203
287
  },
204
288
 
289
+ /**
290
+ * #method
291
+ */
205
292
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
206
293
  HeaderComponent(): React.FC<any> {
207
294
  return Header
208
295
  },
209
296
 
297
+ /**
298
+ * #getter
299
+ */
210
300
  get assemblyErrors() {
211
301
  const { assemblyManager } = getSession(self)
212
302
  const { assemblyNames } = self
@@ -216,23 +306,46 @@ export function stateModelFactory(pluginManager: PluginManager) {
216
306
  .join(', ')
217
307
  },
218
308
 
309
+ /**
310
+ * #getter
311
+ */
219
312
  get assembliesInitialized() {
220
313
  const { assemblyManager } = getSession(self)
221
314
  const { assemblyNames } = self
222
315
  return assemblyNames.every(a => assemblyManager.get(a)?.initialized)
223
316
  },
317
+
318
+ /**
319
+ * #getter
320
+ */
224
321
  get initialized() {
225
322
  return self.volatileWidth !== undefined && this.assembliesInitialized
226
323
  },
324
+
325
+ /**
326
+ * #getter
327
+ */
227
328
  get hasDisplayedRegions() {
228
329
  return self.displayedRegions.length > 0
229
330
  },
331
+
332
+ /**
333
+ * #getter
334
+ */
230
335
  get isSearchDialogDisplayed() {
231
336
  return self.searchResults !== undefined
232
337
  },
338
+
339
+ /**
340
+ * #getter
341
+ */
233
342
  get scaleBarHeight() {
234
343
  return SCALE_BAR_HEIGHT + RESIZE_HANDLE_HEIGHT
235
344
  },
345
+
346
+ /**
347
+ * #getter
348
+ */
236
349
  get headerHeight() {
237
350
  if (self.hideHeader) {
238
351
  return 0
@@ -242,15 +355,26 @@ export function stateModelFactory(pluginManager: PluginManager) {
242
355
  }
243
356
  return HEADER_BAR_HEIGHT + HEADER_OVERVIEW_HEIGHT
244
357
  },
358
+
359
+ /**
360
+ * #getter
361
+ */
245
362
  get trackHeights() {
246
363
  return self.tracks
247
364
  .map(t => t.displays[0].height)
248
365
  .reduce((a, b) => a + b, 0)
249
366
  },
250
367
 
368
+ /**
369
+ * #getter
370
+ */
251
371
  get trackHeightsWithResizeHandles() {
252
372
  return this.trackHeights + self.tracks.length * RESIZE_HANDLE_HEIGHT
253
373
  },
374
+
375
+ /**
376
+ * #getter
377
+ */
254
378
  get height() {
255
379
  return (
256
380
  this.trackHeightsWithResizeHandles +
@@ -258,38 +382,63 @@ export function stateModelFactory(pluginManager: PluginManager) {
258
382
  this.scaleBarHeight
259
383
  )
260
384
  },
385
+
386
+ /**
387
+ * #getter
388
+ */
261
389
  get totalBp() {
262
390
  return self.displayedRegions.reduce((a, b) => a + b.end - b.start, 0)
263
391
  },
264
392
 
393
+ /**
394
+ * #getter
395
+ */
265
396
  get maxBpPerPx() {
266
397
  return this.totalBp / (self.width * 0.9)
267
398
  },
268
399
 
400
+ /**
401
+ * #getter
402
+ */
269
403
  get minBpPerPx() {
270
404
  return 1 / 50
271
405
  },
272
406
 
407
+ /**
408
+ * #getter
409
+ */
273
410
  get error() {
274
411
  return self.volatileError || this.assemblyErrors
275
412
  },
276
413
 
414
+ /**
415
+ * #getter
416
+ */
277
417
  get maxOffset() {
278
418
  // objectively determined to keep the linear genome on the main screen
279
419
  const leftPadding = 10
280
420
  return this.displayedRegionsTotalPx - leftPadding
281
421
  },
282
422
 
423
+ /**
424
+ * #getter
425
+ */
283
426
  get minOffset() {
284
427
  // objectively determined to keep the linear genome on the main screen
285
428
  const rightPadding = 30
286
429
  return -self.width + rightPadding
287
430
  },
288
431
 
432
+ /**
433
+ * #getter
434
+ */
289
435
  get displayedRegionsTotalPx() {
290
436
  return this.totalBp / self.bpPerPx
291
437
  },
292
438
 
439
+ /**
440
+ * #method
441
+ */
293
442
  renderProps() {
294
443
  return {
295
444
  ...getParentRenderProps(self),
@@ -301,6 +450,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
301
450
  }
302
451
  },
303
452
 
453
+ /**
454
+ * #method
455
+ */
304
456
  searchScope(assemblyName: string) {
305
457
  return {
306
458
  assemblyName,
@@ -309,10 +461,16 @@ export function stateModelFactory(pluginManager: PluginManager) {
309
461
  }
310
462
  },
311
463
 
464
+ /**
465
+ * #method
466
+ */
312
467
  getTrack(id: string) {
313
468
  return self.tracks.find(t => t.configuration.trackId === id)
314
469
  },
315
470
 
471
+ /**
472
+ * #method
473
+ */
316
474
  rankSearchResults(results: BaseResult[]) {
317
475
  // order of rank
318
476
  const openTrackIds = self.tracks.map(
@@ -326,7 +484,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
326
484
  return results
327
485
  },
328
486
 
329
- // modifies view menu action onClick to apply to all tracks of same type
487
+ /**
488
+ * #method
489
+ * modifies view menu action onClick to apply to all tracks of same type
490
+ */
330
491
  rewriteOnClicks(trackType: string, viewMenuActions: MenuItem[]) {
331
492
  viewMenuActions.forEach(action => {
332
493
  // go to lowest level menu
@@ -345,7 +506,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
345
506
  }
346
507
  })
347
508
  },
348
-
509
+ /**
510
+ * #getter
511
+ */
349
512
  get trackTypeActions() {
350
513
  const allActions: Map<string, MenuItem[]> = new Map()
351
514
  self.tracks.forEach(track => {
@@ -361,37 +524,61 @@ export function stateModelFactory(pluginManager: PluginManager) {
361
524
  },
362
525
  }))
363
526
  .actions(self => ({
527
+ /**
528
+ * #action
529
+ */
364
530
  setShowCytobands(flag: boolean) {
365
531
  self.showCytobandsSetting = flag
366
532
  localStorage.setItem('lgv-showCytobands', `${+flag}`)
367
533
  },
534
+ /**
535
+ * #action
536
+ */
368
537
  setWidth(newWidth: number) {
369
538
  self.volatileWidth = newWidth
370
539
  },
540
+ /**
541
+ * #action
542
+ */
371
543
  setError(error: Error | undefined) {
372
544
  self.volatileError = error
373
545
  },
374
-
546
+ /**
547
+ * #action
548
+ */
375
549
  toggleHeader() {
376
550
  self.hideHeader = !self.hideHeader
377
551
  },
378
-
552
+ /**
553
+ * #action
554
+ */
379
555
  toggleHeaderOverview() {
380
556
  self.hideHeaderOverview = !self.hideHeaderOverview
381
557
  },
558
+ /**
559
+ * #action
560
+ */
382
561
  toggleNoTracksActive() {
383
562
  self.hideNoTracksActive = !self.hideNoTracksActive
384
563
  },
564
+ /**
565
+ * #action
566
+ */
385
567
  toggleShowGridlines() {
386
568
  self.showGridlines = !self.showGridlines
387
569
  },
388
-
570
+ /**
571
+ * #action
572
+ */
389
573
  scrollTo(offsetPx: number) {
390
574
  const newOffsetPx = clamp(offsetPx, self.minOffset, self.maxOffset)
391
575
  self.offsetPx = newOffsetPx
392
576
  return newOffsetPx
393
577
  },
394
578
 
579
+ /**
580
+ * #action
581
+ */
395
582
  zoomTo(bpPerPx: number) {
396
583
  const newBpPerPx = clamp(bpPerPx, self.minBpPerPx, self.maxBpPerPx)
397
584
  if (newBpPerPx === self.bpPerPx) {
@@ -416,26 +603,41 @@ export function stateModelFactory(pluginManager: PluginManager) {
416
603
  return newBpPerPx
417
604
  },
418
605
 
606
+ /**
607
+ * #action
608
+ * sets offsets used in the get sequence dialog
609
+ */
419
610
  setOffsets(left?: BpOffset, right?: BpOffset) {
420
- // sets offsets used in the get sequence dialog
421
611
  self.leftOffset = left
422
612
  self.rightOffset = right
423
613
  },
424
614
 
615
+ /**
616
+ * #action
617
+ */
425
618
  setSearchResults(results?: BaseResult[], query?: string) {
426
619
  self.searchResults = results
427
620
  self.searchQuery = query
428
621
  },
429
622
 
623
+ /**
624
+ * #action
625
+ */
430
626
  setGetSequenceDialogOpen(open: boolean) {
431
627
  self.seqDialogDisplayed = open
432
628
  },
433
629
 
630
+ /**
631
+ * #action
632
+ */
434
633
  setNewView(bpPerPx: number, offsetPx: number) {
435
634
  this.zoomTo(bpPerPx)
436
635
  this.scrollTo(offsetPx)
437
636
  },
438
637
 
638
+ /**
639
+ * #action
640
+ */
439
641
  horizontallyFlip() {
440
642
  self.displayedRegions = cast(
441
643
  self.displayedRegions
@@ -446,6 +648,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
446
648
  this.scrollTo(self.totalBp / self.bpPerPx - self.offsetPx - self.width)
447
649
  },
448
650
 
651
+ /**
652
+ * #action
653
+ */
449
654
  showTrack(
450
655
  trackId: string,
451
656
  initialSnapshot = {},
@@ -490,7 +695,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
490
695
  }
491
696
  return t[0]
492
697
  },
493
-
698
+ /**
699
+ * #action
700
+ */
494
701
  hideTrack(trackId: string) {
495
702
  const schema = pluginManager.pluggableConfigSchemaType('track')
496
703
  const conf = resolveIdentifier(schema, getRoot(self), trackId)
@@ -500,6 +707,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
500
707
  },
501
708
  }))
502
709
  .actions(self => ({
710
+ /**
711
+ * #action
712
+ */
503
713
  moveTrack(movingId: string, targetId: string) {
504
714
  const oldIndex = self.tracks.findIndex(track => track.id === movingId)
505
715
  if (oldIndex === -1) {
@@ -514,6 +724,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
514
724
  self.tracks.splice(newIndex, 0, track)
515
725
  },
516
726
 
727
+ /**
728
+ * #action
729
+ */
517
730
  closeView() {
518
731
  const parent = getContainingView(self)
519
732
  if (parent) {
@@ -527,6 +740,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
527
740
  }
528
741
  },
529
742
 
743
+ /**
744
+ * #action
745
+ */
530
746
  toggleTrack(trackId: string) {
531
747
  // if we have any tracks with that configuration, turn them off
532
748
  const hiddenCount = self.hideTrack(trackId)
@@ -536,21 +752,33 @@ export function stateModelFactory(pluginManager: PluginManager) {
536
752
  }
537
753
  },
538
754
 
755
+ /**
756
+ * #action
757
+ */
539
758
  setTrackLabels(setting: 'overlapping' | 'offset' | 'hidden') {
540
759
  self.trackLabels = setting
541
760
  localStorage.setItem('lgv-trackLabels', setting)
542
761
  },
543
762
 
763
+ /**
764
+ * #action
765
+ */
544
766
  toggleCenterLine() {
545
767
  self.showCenterLine = !self.showCenterLine
546
768
  localStorage.setItem('lgv-showCenterLine', `${+self.showCenterLine}`)
547
769
  },
548
770
 
771
+ /**
772
+ * #action
773
+ */
549
774
  setDisplayedRegions(regions: Region[]) {
550
775
  self.displayedRegions = cast(regions)
551
776
  self.zoomTo(self.bpPerPx)
552
777
  },
553
778
 
779
+ /**
780
+ * #action
781
+ */
554
782
  activateTrackSelector() {
555
783
  if (self.trackSelectorType === 'hierarchical') {
556
784
  const session = getSession(self)
@@ -568,6 +796,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
568
796
  },
569
797
 
570
798
  /**
799
+ * #method
571
800
  * Helper method for the fetchSequence.
572
801
  * Retrieves the corresponding regions that were selected by the rubberband
573
802
  *
@@ -592,11 +821,17 @@ export function stateModelFactory(pluginManager: PluginManager) {
592
821
  }))
593
822
  },
594
823
 
595
- // schedule something to be run after the next time displayedRegions is set
824
+ /**
825
+ * #action
826
+ * schedule something to be run after the next time displayedRegions is set
827
+ */
596
828
  afterDisplayedRegionsSet(cb: Function) {
597
829
  self.afterDisplayedRegionsSetCallbacks.push(cb)
598
830
  },
599
831
 
832
+ /**
833
+ * #action
834
+ */
600
835
  horizontalScroll(distance: number) {
601
836
  const oldOffsetPx = self.offsetPx
602
837
  // newOffsetPx is the actual offset after the scroll is clamped
@@ -604,34 +839,38 @@ export function stateModelFactory(pluginManager: PluginManager) {
604
839
  return newOffsetPx - oldOffsetPx
605
840
  },
606
841
 
842
+ /**
843
+ * #action
844
+ */
607
845
  center() {
608
846
  const centerBp = self.totalBp / 2
609
847
  const centerPx = centerBp / self.bpPerPx
610
848
  self.scrollTo(Math.round(centerPx - self.width / 2))
611
849
  },
612
850
 
851
+ /**
852
+ * #action
853
+ */
613
854
  showAllRegions() {
614
855
  self.zoomTo(self.maxBpPerPx)
615
856
  this.center()
616
857
  },
617
858
 
859
+ /**
860
+ * #action
861
+ */
618
862
  showAllRegionsInAssembly(assemblyName?: string) {
619
863
  const session = getSession(self)
620
864
  const { assemblyManager } = session
621
865
  if (!assemblyName) {
622
- const assemblyNames = [
623
- ...new Set(
624
- self.displayedRegions.map(region => region.assemblyName),
625
- ),
626
- ]
627
- if (assemblyNames.length > 1) {
866
+ const names = new Set(self.displayedRegions.map(r => r.assemblyName))
867
+ if (names.size > 1) {
628
868
  session.notify(
629
- `Can't perform this with multiple assemblies currently`,
869
+ `Can't perform operation with multiple assemblies currently`,
630
870
  )
631
871
  return
632
872
  }
633
-
634
- ;[assemblyName] = assemblyNames
873
+ ;[assemblyName] = [...names]
635
874
  }
636
875
  const assembly = assemblyManager.get(assemblyName)
637
876
  if (assembly) {
@@ -644,16 +883,26 @@ export function stateModelFactory(pluginManager: PluginManager) {
644
883
  }
645
884
  },
646
885
 
886
+ /**
887
+ * #action
888
+ */
647
889
  setDraggingTrackId(idx?: string) {
648
890
  self.draggingTrackId = idx
649
891
  },
650
892
 
893
+ /**
894
+ * #action
895
+ */
651
896
  setScaleFactor(factor: number) {
652
897
  self.scaleFactor = factor
653
898
  },
654
- // this "clears the view" and makes the view return to the import form
899
+
900
+ /**
901
+ * #action
902
+ * this "clears the view" and makes the view return to the import form
903
+ */
655
904
  clearView() {
656
- this.setDisplayedRegions([])
905
+ self.displayedRegions.clear()
657
906
  self.tracks.clear()
658
907
  // it is necessary to run these after setting displayed regions empty
659
908
  // or else model.offsetPx gets set to Infinity and breaks
@@ -661,6 +910,11 @@ export function stateModelFactory(pluginManager: PluginManager) {
661
910
  self.scrollTo(0)
662
911
  self.zoomTo(10)
663
912
  },
913
+
914
+ /**
915
+ * #action
916
+ * creates an svg export and save using FileSaver
917
+ */
664
918
  async exportSvg(opts: ExportSvgOptions = {}) {
665
919
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
666
920
  const html = await renderToSvg(self as any, opts)
@@ -671,6 +925,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
671
925
  .actions(self => {
672
926
  let cancelLastAnimation = () => {}
673
927
 
928
+ /**
929
+ * #action
930
+ * perform animated slide
931
+ */
674
932
  function slide(viewWidths: number) {
675
933
  const [animate, cancelAnimation] = springAnimate(
676
934
  self.offsetPx,
@@ -687,6 +945,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
687
945
  .actions(self => {
688
946
  let cancelLastAnimation = () => {}
689
947
 
948
+ /**
949
+ * #action
950
+ * perform animated zoom
951
+ */
690
952
  function zoom(targetBpPerPx: number) {
691
953
  self.zoomTo(self.bpPerPx)
692
954
  if (
@@ -715,20 +977,32 @@ export function stateModelFactory(pluginManager: PluginManager) {
715
977
  return { zoom }
716
978
  })
717
979
  .views(self => ({
980
+ /**
981
+ * #getter
982
+ */
718
983
  get canShowCytobands() {
719
984
  return self.displayedRegions.length === 1 && this.anyCytobandsExist
720
985
  },
986
+ /**
987
+ * #getter
988
+ */
721
989
  get showCytobands() {
722
990
  return this.canShowCytobands && self.showCytobandsSetting
723
991
  },
992
+ /**
993
+ * #getter
994
+ */
724
995
  get anyCytobandsExist() {
725
996
  const { assemblyManager } = getSession(self)
726
- const { assemblyNames } = self
727
- return assemblyNames.some(
728
- asm => assemblyManager.get(asm)?.cytobands?.length,
997
+ return self.assemblyNames.some(
998
+ a => assemblyManager.get(a)?.cytobands?.length,
729
999
  )
730
1000
  },
731
-
1001
+ /**
1002
+ * #getter
1003
+ * the cytoband is displayed to the right of the chromosome name,
1004
+ * and that offset is calculated manually with this method
1005
+ */
732
1006
  get cytobandOffset() {
733
1007
  return this.showCytobands
734
1008
  ? measureText(self.displayedRegions[0].refName, 12) + 15
@@ -736,6 +1010,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
736
1010
  },
737
1011
  }))
738
1012
  .views(self => ({
1013
+ /**
1014
+ * #method
1015
+ * return the view menu items
1016
+ */
739
1017
  menuItems(): MenuItem[] {
740
1018
  const { canShowCytobands, showCytobands } = self
741
1019
  const session = getSession(self)
@@ -783,59 +1061,57 @@ export function stateModelFactory(pluginManager: PluginManager) {
783
1061
  icon: SyncAltIcon,
784
1062
  onClick: self.horizontallyFlip,
785
1063
  },
786
- { type: 'divider' },
787
- {
788
- label: 'Show all regions in assembly',
789
- icon: VisibilityIcon,
790
- onClick: self.showAllRegionsInAssembly,
791
- },
792
- {
793
- label: 'Show center line',
794
- icon: VisibilityIcon,
795
- type: 'checkbox',
796
- checked: self.showCenterLine,
797
- onClick: self.toggleCenterLine,
798
- },
799
1064
  {
800
- label: 'Show header',
1065
+ label: 'Show...',
801
1066
  icon: VisibilityIcon,
802
- type: 'checkbox',
803
- checked: !self.hideHeader,
804
- onClick: self.toggleHeader,
805
- },
806
- {
807
- label: 'Show header overview',
808
- icon: VisibilityIcon,
809
- type: 'checkbox',
810
- checked: !self.hideHeaderOverview,
811
- onClick: self.toggleHeaderOverview,
812
- disabled: self.hideHeader,
813
- },
814
- {
815
- label: 'Show no tracks active button',
816
- icon: VisibilityIcon,
817
- type: 'checkbox',
818
- checked: !self.hideNoTracksActive,
819
- onClick: self.toggleNoTracksActive,
820
- },
821
- {
822
- label: 'Show guidelines',
823
- icon: VisibilityIcon,
824
- type: 'checkbox',
825
- checked: self.showGridlines,
826
- onClick: self.toggleShowGridlines,
1067
+ subMenu: [
1068
+ {
1069
+ label: 'Show all regions in assembly',
1070
+ onClick: self.showAllRegionsInAssembly,
1071
+ },
1072
+ {
1073
+ label: 'Show center line',
1074
+ type: 'checkbox',
1075
+ checked: self.showCenterLine,
1076
+ onClick: self.toggleCenterLine,
1077
+ },
1078
+ {
1079
+ label: 'Show header',
1080
+ type: 'checkbox',
1081
+ checked: !self.hideHeader,
1082
+ onClick: self.toggleHeader,
1083
+ },
1084
+ {
1085
+ label: 'Show header overview',
1086
+ type: 'checkbox',
1087
+ checked: !self.hideHeaderOverview,
1088
+ onClick: self.toggleHeaderOverview,
1089
+ disabled: self.hideHeader,
1090
+ },
1091
+ {
1092
+ label: 'Show no tracks active button',
1093
+ type: 'checkbox',
1094
+ checked: !self.hideNoTracksActive,
1095
+ onClick: self.toggleNoTracksActive,
1096
+ },
1097
+ {
1098
+ label: 'Show guidelines',
1099
+ type: 'checkbox',
1100
+ checked: self.showGridlines,
1101
+ onClick: self.toggleShowGridlines,
1102
+ },
1103
+ ...(canShowCytobands
1104
+ ? [
1105
+ {
1106
+ label: 'Show ideogram',
1107
+ type: 'checkbox' as const,
1108
+ checked: self.showCytobands,
1109
+ onClick: () => self.setShowCytobands(!showCytobands),
1110
+ },
1111
+ ]
1112
+ : []),
1113
+ ],
827
1114
  },
828
- ...(canShowCytobands
829
- ? [
830
- {
831
- label: 'Show ideogram',
832
- icon: VisibilityIcon,
833
- type: 'checkbox' as const,
834
- checked: self.showCytobands,
835
- onClick: () => self.setShowCytobands(!showCytobands),
836
- },
837
- ]
838
- : []),
839
1115
  {
840
1116
  label: 'Track labels',
841
1117
  icon: LabelIcon,
@@ -883,6 +1159,15 @@ export function stateModelFactory(pluginManager: PluginManager) {
883
1159
  let currentlyCalculatedStaticBlocks: BlockSet | undefined
884
1160
  let stringifiedCurrentlyCalculatedStaticBlocks = ''
885
1161
  return {
1162
+ /**
1163
+ * #getter
1164
+ * static blocks are an important concept jbrowse uses to avoid
1165
+ * re-rendering when you scroll to the side. when you horizontally
1166
+ * scroll to the right, old blocks to the left may be removed, and
1167
+ * new blocks may be instantiated on the right. tracks may use the
1168
+ * static blocks to render their data for the region represented by
1169
+ * the block
1170
+ */
886
1171
  get staticBlocks() {
887
1172
  const ret = calculateStaticBlocks(self)
888
1173
  const sret = JSON.stringify(ret)
@@ -892,33 +1177,58 @@ export function stateModelFactory(pluginManager: PluginManager) {
892
1177
  }
893
1178
  return currentlyCalculatedStaticBlocks as BlockSet
894
1179
  },
895
-
1180
+ /**
1181
+ * #getter
1182
+ * dynamic blocks represent the exact coordinates of the currently
1183
+ * visible genome regions on the screen. they are similar to static
1184
+ * blocks, but statcic blocks can go offscreen while dynamic blocks
1185
+ * represent exactly what is on screen
1186
+ */
896
1187
  get dynamicBlocks() {
897
1188
  return calculateDynamicBlocks(self)
898
1189
  },
899
-
1190
+ /**
1191
+ * #getter
1192
+ * rounded dynamic blocks are dynamic blocks without fractions of bp
1193
+ */
900
1194
  get roundedDynamicBlocks() {
901
- return this.dynamicBlocks.contentBlocks.map(block => {
902
- return {
903
- ...block,
904
- start: Math.floor(block.start),
905
- end: Math.ceil(block.end),
906
- }
907
- })
1195
+ return this.dynamicBlocks.contentBlocks.map(
1196
+ block =>
1197
+ ({
1198
+ ...block,
1199
+ start: Math.floor(block.start),
1200
+ end: Math.ceil(block.end),
1201
+ } as BaseBlock),
1202
+ )
908
1203
  },
1204
+
1205
+ /**
1206
+ * #getter
1207
+ * a single "combo-locstring" representing all the regions visible
1208
+ * on the screen
1209
+ */
909
1210
  get visibleLocStrings() {
910
1211
  return calculateVisibleLocStrings(this.dynamicBlocks.contentBlocks)
911
1212
  },
1213
+
1214
+ /**
1215
+ * #getter
1216
+ * same as visibleLocStrings, but only updated every 300ms
1217
+ */
912
1218
  get coarseVisibleLocStrings() {
913
1219
  return calculateVisibleLocStrings(self.coarseDynamicBlocks)
914
1220
  },
915
1221
  }
916
1222
  })
917
1223
  .actions(self => ({
1224
+ /**
1225
+ * #action
1226
+ */
918
1227
  setCoarseDynamicBlocks(blocks: BlockSet) {
919
1228
  self.coarseDynamicBlocks = blocks.contentBlocks
920
1229
  self.coarseTotalBp = blocks.totalBp
921
1230
  },
1231
+
922
1232
  afterAttach() {
923
1233
  addDisposer(
924
1234
  self,
@@ -935,6 +1245,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
935
1245
  }))
936
1246
  .actions(self => ({
937
1247
  /**
1248
+ * #action
938
1249
  * offset is the base-pair-offset in the displayed region, index is the index of the
939
1250
  * displayed region in the linear genome view
940
1251
  *
@@ -945,7 +1256,14 @@ export function stateModelFactory(pluginManager: PluginManager) {
945
1256
  moveTo(self, start, end)
946
1257
  },
947
1258
 
948
- navToLocString(locString: string, optAssemblyName?: string) {
1259
+ /**
1260
+ * #action
1261
+ * navigate to the given locstring
1262
+ *
1263
+ * @param locString - e.g. "chr1:1-100"
1264
+ * @param optAssemblyName - (optional) the assembly name to use when navigating to the locstring
1265
+ */
1266
+ async navToLocString(locString: string, optAssemblyName?: string) {
949
1267
  const { assemblyNames } = self
950
1268
  const { assemblyManager } = getSession(self)
951
1269
  const { isValidRefName } = assemblyManager
@@ -956,6 +1274,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
956
1274
  .map(f => f.trim())
957
1275
  .filter(f => !!f)
958
1276
 
1277
+ if (assemblyName) {
1278
+ await assemblyManager.waitForAssembly(assemblyName)
1279
+ }
1280
+
959
1281
  // first try interpreting as a whitespace-separated sequence of
960
1282
  // multiple locstrings
961
1283
  try {
@@ -981,34 +1303,38 @@ export function stateModelFactory(pluginManager: PluginManager) {
981
1303
  }
982
1304
  }
983
1305
 
984
- const locations = parsedLocStrings?.map(region => {
985
- const asmName = region.assemblyName || assemblyName
986
- const asm = assemblyManager.get(asmName)
987
- const { refName } = region
988
- if (!asm) {
989
- throw new Error(`assembly ${asmName} not found`)
990
- }
991
- const { regions } = asm
992
- if (!regions) {
993
- throw new Error(`regions not loaded yet for ${asmName}`)
994
- }
995
- const canonicalRefName = asm.getCanonicalRefName(region.refName)
996
- if (!canonicalRefName) {
997
- throw new Error(`Could not find refName ${refName} in ${asm.name}`)
998
- }
999
- const parentRegion = regions.find(
1000
- region => region.refName === canonicalRefName,
1001
- )
1002
- if (!parentRegion) {
1003
- throw new Error(`Could not find refName ${refName} in ${asmName}`)
1004
- }
1306
+ const locations = await Promise.all(
1307
+ parsedLocStrings?.map(async region => {
1308
+ const asmName = region.assemblyName || assemblyName
1309
+ const asm = await assemblyManager.waitForAssembly(asmName)
1310
+ const { refName } = region
1311
+ if (!asm) {
1312
+ throw new Error(`assembly ${asmName} not found`)
1313
+ }
1314
+ const { regions } = asm
1315
+ if (!regions) {
1316
+ throw new Error(`regions not loaded yet for ${asmName}`)
1317
+ }
1318
+ const canonicalRefName = asm.getCanonicalRefName(region.refName)
1319
+ if (!canonicalRefName) {
1320
+ throw new Error(
1321
+ `Could not find refName ${refName} in ${asm.name}`,
1322
+ )
1323
+ }
1324
+ const parentRegion = regions.find(
1325
+ r => r.refName === canonicalRefName,
1326
+ )
1327
+ if (!parentRegion) {
1328
+ throw new Error(`Could not find refName ${refName} in ${asmName}`)
1329
+ }
1005
1330
 
1006
- return {
1007
- ...region,
1008
- assemblyName: asmName,
1009
- parentRegion,
1010
- }
1011
- })
1331
+ return {
1332
+ ...region,
1333
+ assemblyName: asmName,
1334
+ parentRegion,
1335
+ }
1336
+ }),
1337
+ )
1012
1338
 
1013
1339
  if (locations.length === 1) {
1014
1340
  const loc = locations[0]
@@ -1032,6 +1358,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
1032
1358
  },
1033
1359
 
1034
1360
  /**
1361
+ * #action
1035
1362
  * Navigate to a location based on its refName and optionally start, end,
1036
1363
  * and assemblyName. Can handle if there are multiple displayedRegions
1037
1364
  * from same refName. Only navigates to a location if it is entirely
@@ -1040,12 +1367,15 @@ export function stateModelFactory(pluginManager: PluginManager) {
1040
1367
  *
1041
1368
  * Throws an error if navigation was unsuccessful
1042
1369
  *
1043
- * @param location - a proposed location to navigate to
1370
+ * @param query - a proposed location to navigate to
1044
1371
  */
1045
1372
  navTo(query: NavLocation) {
1046
1373
  this.navToMultiple([query])
1047
1374
  },
1048
1375
 
1376
+ /**
1377
+ * #action
1378
+ */
1049
1379
  navToMultiple(locations: NavLocation[]) {
1050
1380
  const firstLocation = locations[0]
1051
1381
  let { refName } = firstLocation
@@ -1131,18 +1461,14 @@ export function stateModelFactory(pluginManager: PluginManager) {
1131
1461
  )
1132
1462
  return
1133
1463
  }
1134
- let locationIndex = 0
1135
- let locationStart = 0
1136
- let locationEnd = 0
1137
- for (
1138
- locationIndex;
1139
- locationIndex < locations.length;
1140
- locationIndex++
1141
- ) {
1142
- const location = locations[locationIndex]
1143
- const region = self.displayedRegions[index + locationIndex]
1144
- locationStart = location.start || region.start
1145
- locationEnd = location.end || region.end
1464
+ let idx = 0
1465
+ let start = 0
1466
+ let end = 0
1467
+ for (idx; idx < locations.length; idx++) {
1468
+ const location = locations[idx]
1469
+ const region = self.displayedRegions[index + idx]
1470
+ start = location.start || region.start
1471
+ end = location.end || region.end
1146
1472
  if (location.refName !== region.refName) {
1147
1473
  throw new Error(
1148
1474
  `Entered location ${assembleLocString(
@@ -1151,10 +1477,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
1151
1477
  )
1152
1478
  }
1153
1479
  }
1154
- locationIndex -= 1
1480
+ idx -= 1
1155
1481
  const startDisplayedRegion = self.displayedRegions[index]
1156
- const endDisplayedRegion =
1157
- self.displayedRegions[index + locationIndex]
1482
+ const endDisplayedRegion = self.displayedRegions[index + idx]
1158
1483
  this.moveTo(
1159
1484
  {
1160
1485
  index,
@@ -1163,10 +1488,10 @@ export function stateModelFactory(pluginManager: PluginManager) {
1163
1488
  : s - startDisplayedRegion.start,
1164
1489
  },
1165
1490
  {
1166
- index: index + locationIndex,
1491
+ index: index + idx,
1167
1492
  offset: endDisplayedRegion.reversed
1168
- ? endDisplayedRegion.end - locationStart
1169
- : locationEnd - endDisplayedRegion.start,
1493
+ ? endDisplayedRegion.end - start
1494
+ : end - endDisplayedRegion.start,
1170
1495
  },
1171
1496
  )
1172
1497
  return
@@ -1179,6 +1504,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
1179
1504
  },
1180
1505
  }))
1181
1506
  .views(self => ({
1507
+ /**
1508
+ * #method
1509
+ */
1182
1510
  rubberBandMenuItems(): MenuItem[] {
1183
1511
  return [
1184
1512
  {
@@ -1197,6 +1525,9 @@ export function stateModelFactory(pluginManager: PluginManager) {
1197
1525
  ]
1198
1526
  },
1199
1527
 
1528
+ /**
1529
+ * #method
1530
+ */
1200
1531
  bpToPx({
1201
1532
  refName,
1202
1533
  coord,
@@ -1210,6 +1541,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
1210
1541
  },
1211
1542
 
1212
1543
  /**
1544
+ * #method
1213
1545
  * scrolls the view to center on the given bp. if that is not in any
1214
1546
  * of the displayed regions, does nothing
1215
1547
  * @param coord - basepair at which you want to center the view
@@ -1227,10 +1559,16 @@ export function stateModelFactory(pluginManager: PluginManager) {
1227
1559
  }
1228
1560
  },
1229
1561
 
1562
+ /**
1563
+ * #method
1564
+ */
1230
1565
  pxToBp(px: number) {
1231
1566
  return pxToBp(self, px)
1232
1567
  },
1233
1568
 
1569
+ /**
1570
+ * #getter
1571
+ */
1234
1572
  get centerLineInfo() {
1235
1573
  return self.displayedRegions.length
1236
1574
  ? this.pxToBp(self.width / 2)