@apollo-annotation/jbrowse-plugin-apollo 0.3.9 → 0.3.11

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 (31) hide show
  1. package/dist/index.esm.js +235 -120
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +233 -118
  4. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  5. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  6. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  7. package/dist/jbrowse-plugin-apollo.umd.development.js +562 -298
  8. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  9. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  10. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  11. package/package.json +4 -4
  12. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -1
  13. package/src/ApolloInternetAccount/model.ts +6 -2
  14. package/src/BackendDrivers/CollaborationServerDriver.ts +11 -5
  15. package/src/ChangeManager.ts +19 -4
  16. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +29 -9
  17. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +2 -2
  18. package/src/LinearApolloDisplay/glyphs/util.ts +17 -0
  19. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceOverlay.ts +18 -25
  20. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceTrack.ts +41 -59
  21. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +33 -2
  22. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +101 -3
  23. package/src/components/AddAssembly.tsx +1 -1
  24. package/src/components/ImportFeatures.tsx +1 -1
  25. package/src/components/OntologyTermAutocomplete.tsx +2 -2
  26. package/src/components/OntologyTermMultiSelect.tsx +2 -2
  27. package/src/makeDisplayComponent.tsx +1 -1
  28. package/src/session/ClientDataStore.ts +1 -1
  29. package/src/session/session.ts +4 -0
  30. package/src/util/displayUtils.ts +28 -0
  31. package/src/util/glyphUtils.ts +18 -0
@@ -1615,7 +1615,6 @@
1615
1615
  });
1616
1616
  AssemblySpecificChange$3.AssemblySpecificChange = void 0;
1617
1617
  AssemblySpecificChange$3.isAssemblySpecificChange = isAssemblySpecificChange$1;
1618
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
1619
1618
  var Change_1$1 = Change$3;
1620
1619
  function isAssemblySpecificChange$1(
1621
1620
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1632,7 +1631,47 @@
1632
1631
  _this.assembly = json.assembly;
1633
1632
  return _this;
1634
1633
  }
1635
- return _createClass(AssemblySpecificChange);
1634
+ _createClass(AssemblySpecificChange, [{
1635
+ key: "getIndexedIds",
1636
+ value: function getIndexedIds(feature, idsToIndex) {
1637
+ var indexedIds = [];
1638
+ var _iterator = _createForOfIteratorHelper(idsToIndex !== null && idsToIndex !== void 0 ? idsToIndex : []),
1639
+ _step;
1640
+ try {
1641
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
1642
+ var additionalId = _step.value;
1643
+ var attributes = feature.attributes;
1644
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1645
+ var idValue = attributes instanceof Map ? attributes.get(additionalId) : attributes === null || attributes === void 0 ? void 0 : attributes[additionalId];
1646
+ if (idValue) {
1647
+ indexedIds.push(idValue[0]);
1648
+ }
1649
+ }
1650
+ } catch (err) {
1651
+ _iterator.e(err);
1652
+ } finally {
1653
+ _iterator.f();
1654
+ }
1655
+ if (feature.children) {
1656
+ var childrenIterable = feature.children instanceof Map ? feature.children.values() : Object.values(feature.children);
1657
+ var _iterator2 = _createForOfIteratorHelper(childrenIterable),
1658
+ _step2;
1659
+ try {
1660
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
1661
+ var child = _step2.value;
1662
+ var childIndexedIds = this.getIndexedIds(child, idsToIndex);
1663
+ indexedIds.push.apply(indexedIds, _toConsumableArray(childIndexedIds));
1664
+ }
1665
+ } catch (err) {
1666
+ _iterator2.e(err);
1667
+ } finally {
1668
+ _iterator2.f();
1669
+ }
1670
+ }
1671
+ return indexedIds;
1672
+ }
1673
+ }]);
1674
+ return AssemblySpecificChange;
1636
1675
  }(Change_1$1.Change);
1637
1676
  AssemblySpecificChange$3.AssemblySpecificChange = AssemblySpecificChange$2;
1638
1677
 
@@ -4706,7 +4745,6 @@
4706
4745
  });
4707
4746
  AssemblySpecificChange$1.AssemblySpecificChange = void 0;
4708
4747
  AssemblySpecificChange$1.isAssemblySpecificChange = isAssemblySpecificChange;
4709
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
4710
4748
  var Change_1 = Change$1;
4711
4749
  function isAssemblySpecificChange(
4712
4750
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -4723,7 +4761,47 @@
4723
4761
  _this.assembly = json.assembly;
4724
4762
  return _this;
4725
4763
  }
4726
- return _createClass(AssemblySpecificChange);
4764
+ _createClass(AssemblySpecificChange, [{
4765
+ key: "getIndexedIds",
4766
+ value: function getIndexedIds(feature, idsToIndex) {
4767
+ var indexedIds = [];
4768
+ var _iterator = _createForOfIteratorHelper(idsToIndex !== null && idsToIndex !== void 0 ? idsToIndex : []),
4769
+ _step;
4770
+ try {
4771
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
4772
+ var additionalId = _step.value;
4773
+ var attributes = feature.attributes;
4774
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
4775
+ var idValue = attributes instanceof Map ? attributes.get(additionalId) : attributes === null || attributes === void 0 ? void 0 : attributes[additionalId];
4776
+ if (idValue) {
4777
+ indexedIds.push(idValue[0]);
4778
+ }
4779
+ }
4780
+ } catch (err) {
4781
+ _iterator.e(err);
4782
+ } finally {
4783
+ _iterator.f();
4784
+ }
4785
+ if (feature.children) {
4786
+ var childrenIterable = feature.children instanceof Map ? feature.children.values() : Object.values(feature.children);
4787
+ var _iterator2 = _createForOfIteratorHelper(childrenIterable),
4788
+ _step2;
4789
+ try {
4790
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
4791
+ var child = _step2.value;
4792
+ var childIndexedIds = this.getIndexedIds(child, idsToIndex);
4793
+ indexedIds.push.apply(indexedIds, _toConsumableArray(childIndexedIds));
4794
+ }
4795
+ } catch (err) {
4796
+ _iterator2.e(err);
4797
+ } finally {
4798
+ _iterator2.f();
4799
+ }
4800
+ }
4801
+ return indexedIds;
4802
+ }
4803
+ }]);
4804
+ return AssemblySpecificChange;
4727
4805
  }(Change_1.Change);
4728
4806
  AssemblySpecificChange$1.AssemblySpecificChange = AssemblySpecificChange;
4729
4807
 
@@ -5626,7 +5704,7 @@
5626
5704
  var util_1$9 = require$$1__default$1["default"];
5627
5705
  var bson_objectid_1$2 = /*#__PURE__*/tslib_1$4.__importDefault(objectid);
5628
5706
  var gffReservedKeys_1 = gffReservedKeys;
5629
- function gff3ToAnnotationFeature(gff3Feature, refSeq, featureIds) {
5707
+ function gff3ToAnnotationFeature(gff3Feature, refSeq) {
5630
5708
  var _gff3Feature = _slicedToArray(gff3Feature, 1),
5631
5709
  firstFeature = _gff3Feature[0];
5632
5710
  var end = firstFeature.end,
@@ -5650,7 +5728,7 @@
5650
5728
  _getFeatureMinMax2 = _slicedToArray(_getFeatureMinMax, 2),
5651
5729
  min = _getFeatureMinMax2[0],
5652
5730
  max = _getFeatureMinMax2[1];
5653
- var convertedChildren = convertChildren(gff3Feature, refSeq, featureIds);
5731
+ var convertedChildren = convertChildren(gff3Feature, refSeq);
5654
5732
  var convertedAttributes = convertFeatureAttributes$1(gff3Feature);
5655
5733
  var feature = {
5656
5734
  _id: new bson_objectid_1$2["default"]().toHexString(),
@@ -5674,9 +5752,6 @@
5674
5752
  if (convertedAttributes) {
5675
5753
  feature.attributes = convertedAttributes;
5676
5754
  }
5677
- if (featureIds) {
5678
- featureIds.push(feature._id);
5679
- }
5680
5755
  return feature;
5681
5756
  }
5682
5757
  function getFeatureMinMax(gff3Feature) {
@@ -5810,7 +5885,7 @@
5810
5885
  if (firstChildFeatureLocation.type === 'CDS') {
5811
5886
  cdsFeatures.push(childFeature);
5812
5887
  } else {
5813
- var child = gff3ToAnnotationFeature(childFeature, refSeq, featureIds);
5888
+ var child = gff3ToAnnotationFeature(childFeature, refSeq);
5814
5889
  convertedChildren[child._id] = child;
5815
5890
  }
5816
5891
  }
@@ -5820,7 +5895,7 @@
5820
5895
  _iterator2.f();
5821
5896
  }
5822
5897
  if (cdsFeatures.length > 0) {
5823
- var processedCDS = processCDS(cdsFeatures, refSeq, featureIds);
5898
+ var processedCDS = processCDS(cdsFeatures, refSeq);
5824
5899
  var _iterator3 = _createForOfIteratorHelper(processedCDS),
5825
5900
  _step3;
5826
5901
  try {
@@ -6052,7 +6127,7 @@
6052
6127
  * should be represented in our internal structure
6053
6128
  * @param cdsFeatures -
6054
6129
  */
6055
- function processCDS(cdsFeatures, refSeq, featureIds) {
6130
+ function processCDS(cdsFeatures, refSeq) {
6056
6131
  var locationCounts = cdsFeatures.map(function (cds) {
6057
6132
  return cds.length;
6058
6133
  });
@@ -6063,7 +6138,7 @@
6063
6138
  return count > 1;
6064
6139
  })) {
6065
6140
  return cdsFeatures.map(function (cds) {
6066
- return gff3ToAnnotationFeature(cds, refSeq, featureIds);
6141
+ return gff3ToAnnotationFeature(cds, refSeq);
6067
6142
  });
6068
6143
  }
6069
6144
  // If all CDS have a single location, we guess that this GFF3 represented CDS
@@ -6089,7 +6164,7 @@
6089
6164
  });
6090
6165
  // If no overlaps, assume it's a single CDS feature
6091
6166
  if (!overlapping) {
6092
- return [gff3ToAnnotationFeature(sortedCDSLocations, refSeq, featureIds)];
6167
+ return [gff3ToAnnotationFeature(sortedCDSLocations, refSeq)];
6093
6168
  }
6094
6169
  // Some CDS locations overlap, the best we can do is use the original order to
6095
6170
  // guess how to group the locations into features
@@ -6126,7 +6201,7 @@
6126
6201
  _iterator12.f();
6127
6202
  }
6128
6203
  return groupedLocations.map(function (group) {
6129
- return gff3ToAnnotationFeature(group, refSeq, featureIds);
6204
+ return gff3ToAnnotationFeature(group, refSeq);
6130
6205
  });
6131
6206
  }
6132
6207
 
@@ -6486,71 +6561,76 @@
6486
6561
  key: "addFeatureIntoDb",
6487
6562
  value: function () {
6488
6563
  var _addFeatureIntoDb = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(gff3Feature, backend) {
6489
- var featureModel, refSeqModel, user, assembly, refSeqCache, _gff3Feature, refName, refSeqDoc, _yield$refSeqModel$fi, featureIds, newFeature;
6564
+ var INDEXED_IDS, idsToIndex, assembly, refSeqCache, featureModel, refSeqModel, user, _gff3Feature, refName, refSeqDoc, _yield$refSeqModel$fi, newFeature, allIds, indexedIds;
6490
6565
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
6491
6566
  while (1) switch (_context3.prev = _context3.next) {
6492
6567
  case 0:
6493
- featureModel = backend.featureModel, refSeqModel = backend.refSeqModel, user = backend.user;
6568
+ INDEXED_IDS = browser$1.env.INDEXED_IDS;
6569
+ if (INDEXED_IDS) {
6570
+ idsToIndex = INDEXED_IDS.split(',');
6571
+ }
6494
6572
  assembly = this.assembly, refSeqCache = this.refSeqCache;
6573
+ featureModel = backend.featureModel, refSeqModel = backend.refSeqModel, user = backend.user;
6495
6574
  _gff3Feature = _slicedToArray(gff3Feature, 1), refName = _gff3Feature[0].seq_id;
6496
6575
  if (refName) {
6497
- _context3.next = 5;
6576
+ _context3.next = 7;
6498
6577
  break;
6499
6578
  }
6500
6579
  throw new Error("Valid seq_id not found in feature ".concat(JSON.stringify(gff3Feature)));
6501
- case 5:
6580
+ case 7:
6502
6581
  refSeqDoc = refSeqCache.get(refName);
6503
6582
  if (refSeqDoc) {
6504
- _context3.next = 20;
6583
+ _context3.next = 22;
6505
6584
  break;
6506
6585
  }
6507
- _context3.next = 9;
6586
+ _context3.next = 11;
6508
6587
  return refSeqModel.findOne({
6509
6588
  assembly: assembly,
6510
6589
  name: refName
6511
6590
  }).exec();
6512
- case 9:
6591
+ case 11:
6513
6592
  _context3.t1 = _yield$refSeqModel$fi = _context3.sent;
6514
6593
  _context3.t0 = _context3.t1 !== null;
6515
6594
  if (!_context3.t0) {
6516
- _context3.next = 13;
6595
+ _context3.next = 15;
6517
6596
  break;
6518
6597
  }
6519
6598
  _context3.t0 = _yield$refSeqModel$fi !== void 0;
6520
- case 13:
6599
+ case 15:
6521
6600
  if (!_context3.t0) {
6522
- _context3.next = 17;
6601
+ _context3.next = 19;
6523
6602
  break;
6524
6603
  }
6525
6604
  _context3.t2 = _yield$refSeqModel$fi;
6526
- _context3.next = 18;
6605
+ _context3.next = 20;
6527
6606
  break;
6528
- case 17:
6607
+ case 19:
6529
6608
  _context3.t2 = undefined;
6530
- case 18:
6609
+ case 20:
6531
6610
  refSeqDoc = _context3.t2;
6532
6611
  if (refSeqDoc) {
6533
6612
  refSeqCache.set(refName, refSeqDoc);
6534
6613
  }
6535
- case 20:
6614
+ case 22:
6536
6615
  if (refSeqDoc) {
6537
- _context3.next = 22;
6616
+ _context3.next = 24;
6538
6617
  break;
6539
6618
  }
6540
6619
  throw new Error("RefSeq was not found by assembly \"".concat(assembly, "\" and seq_id \"").concat(refName, "\" not found"));
6541
- case 22:
6542
- // Let's add featureId to parent feature
6543
- featureIds = [];
6544
- newFeature = (0, GFF3_1.gff3ToAnnotationFeature)(gff3Feature, refSeqDoc._id, featureIds); // Add into Mongo
6620
+ case 24:
6621
+ newFeature = (0, GFF3_1.gff3ToAnnotationFeature)(gff3Feature, refSeqDoc._id);
6622
+ allIds = this.getAllIds(newFeature);
6623
+ indexedIds = this.getIndexedIds(newFeature, idsToIndex); // Add into Mongo
6545
6624
  // We cannot use Mongo 'session' / transaction here because Mongo has 16 MB limit for transaction
6546
- _context3.next = 26;
6625
+ _context3.next = 29;
6547
6626
  return featureModel.create([_objectSpread2(_objectSpread2({
6548
- allIds: featureIds
6627
+ allIds: allIds,
6628
+ indexedIds: indexedIds
6549
6629
  }, newFeature), {}, {
6550
6630
  user: user,
6551
6631
  status: -1
6552
6632
  })]);
6553
- case 26:
6633
+ case 29:
6554
6634
  case "end":
6555
6635
  return _context3.stop();
6556
6636
  }
@@ -6561,6 +6641,19 @@
6561
6641
  }
6562
6642
  return addFeatureIntoDb;
6563
6643
  }()
6644
+ }, {
6645
+ key: "getAllIds",
6646
+ value: function getAllIds(feature) {
6647
+ var allIds = [feature._id];
6648
+ if (feature.children) {
6649
+ for (var _i = 0, _Object$values = Object.values(feature.children); _i < _Object$values.length; _i++) {
6650
+ var child = _Object$values[_i];
6651
+ var childIds = this.getAllIds(child);
6652
+ allIds.push.apply(allIds, _toConsumableArray(childIds));
6653
+ }
6654
+ }
6655
+ return allIds;
6656
+ }
6564
6657
  }]);
6565
6658
  return FromFileBaseChange;
6566
6659
  }(common_1$n.AssemblySpecificChange);
@@ -16952,17 +17045,22 @@
16952
17045
  value: function () {
16953
17046
  var _executeOnServer = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(backend) {
16954
17047
  var _this2 = this;
16955
- var featureModel, session, changes, logger, _iterator, _step, _loop;
17048
+ var featureModel, session, changes, logger, INDEXED_IDS, idsToIndex, _iterator, _step, _loop;
16956
17049
  return _regeneratorRuntime().wrap(function _callee$(_context2) {
16957
17050
  while (1) switch (_context2.prev = _context2.next) {
16958
17051
  case 0:
16959
17052
  featureModel = backend.featureModel, session = backend.session;
16960
- changes = this.changes, logger = this.logger; // Loop the changes
17053
+ changes = this.changes, logger = this.logger;
17054
+ INDEXED_IDS = browser$1.env.INDEXED_IDS;
17055
+ if (INDEXED_IDS) {
17056
+ idsToIndex = INDEXED_IDS.split(',');
17057
+ }
17058
+ // Loop the changes
16961
17059
  _iterator = _createForOfIteratorHelper(changes);
16962
- _context2.prev = 3;
17060
+ _context2.prev = 5;
16963
17061
  _loop = /*#__PURE__*/_regeneratorRuntime().mark(function _loop() {
16964
17062
  var _logger$debug3;
16965
- var change, deletedFeature, parentFeatureId, featureDoc, errMsg, _logger$debug, deletedIds, _logger$debug2;
17063
+ var change, deletedFeature, parentFeatureId, featureDoc, errMsg, _logger$debug, deletedIds, indexedIds, _logger$debug2;
16966
17064
  return _regeneratorRuntime().wrap(function _loop$(_context) {
16967
17065
  while (1) switch (_context.prev = _context.next) {
16968
17066
  case 0:
@@ -17003,59 +17101,71 @@
17003
17101
  featureDoc.allIds = featureDoc.allIds.filter(function (id) {
17004
17102
  return !deletedIds.includes(id);
17005
17103
  });
17104
+ indexedIds = _this2.getIndexedIds(featureDoc, idsToIndex);
17105
+ if (featureDoc.indexedIds) {
17106
+ if (indexedIds.length > 0) {
17107
+ featureDoc.indexedIds = indexedIds;
17108
+ } else {
17109
+ delete featureDoc.indexedIds;
17110
+ }
17111
+ } else {
17112
+ if (indexedIds.length > 0) {
17113
+ featureDoc.indexedIds = indexedIds;
17114
+ }
17115
+ }
17006
17116
  // Save updated document in Mongo
17007
17117
  featureDoc.markModified('children'); // Mark as modified. Without this save() -method is not updating data in database
17008
- _context.prev = 20;
17009
- _context.next = 23;
17118
+ _context.prev = 22;
17119
+ _context.next = 25;
17010
17120
  return featureDoc.save();
17011
- case 23:
17012
- _context.next = 29;
17013
- break;
17014
17121
  case 25:
17015
- _context.prev = 25;
17016
- _context.t0 = _context["catch"](20);
17122
+ _context.next = 31;
17123
+ break;
17124
+ case 27:
17125
+ _context.prev = 27;
17126
+ _context.t0 = _context["catch"](22);
17017
17127
  (_logger$debug2 = logger.debug) === null || _logger$debug2 === void 0 || _logger$debug2.call(logger, "*** FAILED: ".concat(_context.t0));
17018
17128
  throw _context.t0;
17019
- case 29:
17129
+ case 31:
17020
17130
  (_logger$debug3 = logger.debug) === null || _logger$debug3 === void 0 || _logger$debug3.call(logger, "Feature \"".concat(deletedFeature._id, "\" deleted from document \"").concat(featureDoc._id, "\""));
17021
- case 30:
17131
+ case 32:
17022
17132
  case "end":
17023
17133
  return _context.stop();
17024
17134
  }
17025
- }, _loop, null, [[20, 25]]);
17135
+ }, _loop, null, [[22, 27]]);
17026
17136
  });
17027
17137
  _iterator.s();
17028
- case 6:
17138
+ case 8:
17029
17139
  if ((_step = _iterator.n()).done) {
17030
- _context2.next = 12;
17140
+ _context2.next = 14;
17031
17141
  break;
17032
17142
  }
17033
- return _context2.delegateYield(_loop(), "t0", 8);
17034
- case 8:
17143
+ return _context2.delegateYield(_loop(), "t0", 10);
17144
+ case 10:
17035
17145
  if (!_context2.t0) {
17036
- _context2.next = 10;
17146
+ _context2.next = 12;
17037
17147
  break;
17038
17148
  }
17039
- return _context2.abrupt("continue", 10);
17040
- case 10:
17041
- _context2.next = 6;
17042
- break;
17149
+ return _context2.abrupt("continue", 12);
17043
17150
  case 12:
17044
- _context2.next = 17;
17151
+ _context2.next = 8;
17045
17152
  break;
17046
17153
  case 14:
17047
- _context2.prev = 14;
17048
- _context2.t1 = _context2["catch"](3);
17154
+ _context2.next = 19;
17155
+ break;
17156
+ case 16:
17157
+ _context2.prev = 16;
17158
+ _context2.t1 = _context2["catch"](5);
17049
17159
  _iterator.e(_context2.t1);
17050
- case 17:
17051
- _context2.prev = 17;
17160
+ case 19:
17161
+ _context2.prev = 19;
17052
17162
  _iterator.f();
17053
- return _context2.finish(17);
17054
- case 20:
17163
+ return _context2.finish(19);
17164
+ case 22:
17055
17165
  case "end":
17056
17166
  return _context2.stop();
17057
17167
  }
17058
- }, _callee, this, [[3, 14, 17, 20]]);
17168
+ }, _callee, this, [[5, 16, 19, 22]]);
17059
17169
  }));
17060
17170
  function executeOnServer(_x) {
17061
17171
  return _executeOnServer.apply(this, arguments);
@@ -17288,7 +17398,7 @@
17288
17398
  value: function () {
17289
17399
  var _executeOnServer = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(backend) {
17290
17400
  var _logger$debug, _logger$debug2;
17291
- var assemblyModel, featureModel, refSeqModel, session, user, assembly, changes, logger, assemblyDoc, errMsg, featureCnt, _iterator, _step, _logger$debug3, change, addedFeature, allIds, copyFeature, parentFeatureId, _id, refSeq, refSeqDoc, _logger$debug4, _yield$featureModel$c, _yield$featureModel$c2, newFeatureDoc, _topLevelFeature$allI, topLevelFeature, parentFeature, childIds, _logger$verbose, _childIds, allIdsV2, _yield$featureModel$c3, _yield$featureModel$c4, _newFeatureDoc;
17401
+ var assemblyModel, featureModel, refSeqModel, session, user, assembly, changes, logger, assemblyDoc, errMsg, featureCnt, INDEXED_IDS, idsToIndex, _iterator, _step, _logger$debug3, change, addedFeature, allIds, copyFeature, parentFeatureId, _id, refSeq, refSeqDoc, _logger$debug4, indexedIds, _yield$featureModel$c, _yield$featureModel$c2, newFeatureDoc, _indexedIds, _topLevelFeature$allI, _topLevelFeature$inde, _topLevelFeature$inde2, topLevelFeature, parentFeature, childIds, _logger$verbose, _childIds, allIdsV2, _yield$featureModel$c3, _yield$featureModel$c4, _newFeatureDoc;
17292
17402
  return _regeneratorRuntime().wrap(function _callee$(_context) {
17293
17403
  while (1) switch (_context.prev = _context.next) {
17294
17404
  case 0:
@@ -17308,119 +17418,131 @@
17308
17418
  case 9:
17309
17419
  featureCnt = 0;
17310
17420
  (_logger$debug = logger.debug) === null || _logger$debug === void 0 || _logger$debug.call(logger, "changes: ".concat(JSON.stringify(changes)));
17421
+ INDEXED_IDS = browser$1.env.INDEXED_IDS;
17422
+ if (INDEXED_IDS) {
17423
+ idsToIndex = INDEXED_IDS.split(',');
17424
+ }
17311
17425
  // Loop the changes
17312
17426
  _iterator = _createForOfIteratorHelper(changes);
17313
- _context.prev = 12;
17427
+ _context.prev = 14;
17314
17428
  _iterator.s();
17315
- case 14:
17429
+ case 16:
17316
17430
  if ((_step = _iterator.n()).done) {
17317
- _context.next = 61;
17431
+ _context.next = 67;
17318
17432
  break;
17319
17433
  }
17320
17434
  change = _step.value;
17321
17435
  (_logger$debug3 = logger.debug) === null || _logger$debug3 === void 0 || _logger$debug3.call(logger, "change: ".concat(JSON.stringify(change)));
17322
17436
  addedFeature = change.addedFeature, allIds = change.allIds, copyFeature = change.copyFeature, parentFeatureId = change.parentFeatureId;
17323
17437
  _id = addedFeature._id, refSeq = addedFeature.refSeq;
17324
- _context.next = 21;
17438
+ _context.next = 23;
17325
17439
  return refSeqModel.findById(refSeq).session(session).exec();
17326
- case 21:
17440
+ case 23:
17327
17441
  refSeqDoc = _context.sent;
17328
17442
  if (refSeqDoc) {
17329
- _context.next = 24;
17443
+ _context.next = 26;
17330
17444
  break;
17331
17445
  }
17332
17446
  throw new Error("RefSeq was not found by assembly \"".concat(assembly, "\" and seq_id \"").concat(refSeq, "\" not found"));
17333
- case 24:
17447
+ case 26:
17334
17448
  if (!copyFeature) {
17335
- _context.next = 34;
17449
+ _context.next = 37;
17336
17450
  break;
17337
17451
  }
17338
- _context.next = 27;
17452
+ indexedIds = this.getIndexedIds(addedFeature, idsToIndex); // Add into Mongo
17453
+ _context.next = 30;
17339
17454
  return featureModel.create([_objectSpread2(_objectSpread2({}, addedFeature), {}, {
17340
17455
  allIds: allIds,
17456
+ indexedIds: indexedIds,
17341
17457
  status: -1,
17342
17458
  user: user
17343
17459
  })], {
17344
17460
  session: session
17345
17461
  });
17346
- case 27:
17462
+ case 30:
17347
17463
  _yield$featureModel$c = _context.sent;
17348
17464
  _yield$featureModel$c2 = _slicedToArray(_yield$featureModel$c, 1);
17349
17465
  newFeatureDoc = _yield$featureModel$c2[0];
17350
17466
  (_logger$debug4 = logger.debug) === null || _logger$debug4 === void 0 || _logger$debug4.call(logger, "Copied feature, docId \"".concat(newFeatureDoc._id, "\" to assembly \"").concat(assembly, "\""));
17351
17467
  featureCnt++;
17352
- _context.next = 58;
17468
+ _context.next = 64;
17353
17469
  break;
17354
- case 34:
17470
+ case 37:
17471
+ _indexedIds = this.getIndexedIds(addedFeature, idsToIndex); // Adding new child feature
17355
17472
  if (!parentFeatureId) {
17356
- _context.next = 50;
17473
+ _context.next = 56;
17357
17474
  break;
17358
17475
  }
17359
- _context.next = 37;
17476
+ _context.next = 41;
17360
17477
  return featureModel.findOne({
17361
17478
  allIds: parentFeatureId
17362
17479
  }).session(session).exec();
17363
- case 37:
17480
+ case 41:
17364
17481
  topLevelFeature = _context.sent;
17365
17482
  if (topLevelFeature) {
17366
- _context.next = 40;
17483
+ _context.next = 44;
17367
17484
  break;
17368
17485
  }
17369
17486
  throw new Error("Could not find feature with ID \"".concat(parentFeatureId, "\""));
17370
- case 40:
17487
+ case 44:
17371
17488
  parentFeature = this.getFeatureFromId(topLevelFeature, parentFeatureId);
17372
17489
  if (parentFeature) {
17373
- _context.next = 43;
17490
+ _context.next = 47;
17374
17491
  break;
17375
17492
  }
17376
17493
  throw new Error("Could not find feature with ID \"".concat(parentFeatureId, "\" in feature \"").concat(topLevelFeature._id, "\""));
17377
- case 43:
17494
+ case 47:
17378
17495
  this.addChild(parentFeature, addedFeature);
17379
17496
  childIds = this.getChildFeatureIds(addedFeature);
17380
17497
  (_topLevelFeature$allI = topLevelFeature.allIds).push.apply(_topLevelFeature$allI, [_id].concat(_toConsumableArray(childIds)));
17381
- _context.next = 48;
17498
+ if (_indexedIds.length > 0 && !topLevelFeature.indexedIds) {
17499
+ topLevelFeature.indexedIds = [];
17500
+ }
17501
+ (_topLevelFeature$inde = topLevelFeature.indexedIds) === null || _topLevelFeature$inde === void 0 || (_topLevelFeature$inde2 = _topLevelFeature$inde).push.apply(_topLevelFeature$inde2, _toConsumableArray(_indexedIds));
17502
+ _context.next = 54;
17382
17503
  return topLevelFeature.save();
17383
- case 48:
17384
- _context.next = 58;
17504
+ case 54:
17505
+ _context.next = 64;
17385
17506
  break;
17386
- case 50:
17507
+ case 56:
17387
17508
  _childIds = this.getChildFeatureIds(addedFeature);
17388
17509
  allIdsV2 = [_id].concat(_toConsumableArray(_childIds));
17389
- _context.next = 54;
17510
+ _context.next = 60;
17390
17511
  return featureModel.create([_objectSpread2({
17391
17512
  allIds: allIdsV2,
17513
+ indexedIds: _indexedIds,
17392
17514
  status: 0
17393
17515
  }, addedFeature)], {
17394
17516
  session: session
17395
17517
  });
17396
- case 54:
17518
+ case 60:
17397
17519
  _yield$featureModel$c3 = _context.sent;
17398
17520
  _yield$featureModel$c4 = _slicedToArray(_yield$featureModel$c3, 1);
17399
17521
  _newFeatureDoc = _yield$featureModel$c4[0];
17400
17522
  (_logger$verbose = logger.verbose) === null || _logger$verbose === void 0 || _logger$verbose.call(logger, "Added docId \"".concat(_newFeatureDoc._id, "\""));
17401
- case 58:
17523
+ case 64:
17402
17524
  featureCnt++;
17403
- case 59:
17404
- _context.next = 14;
17525
+ case 65:
17526
+ _context.next = 16;
17405
17527
  break;
17406
- case 61:
17407
- _context.next = 66;
17528
+ case 67:
17529
+ _context.next = 72;
17408
17530
  break;
17409
- case 63:
17410
- _context.prev = 63;
17411
- _context.t0 = _context["catch"](12);
17531
+ case 69:
17532
+ _context.prev = 69;
17533
+ _context.t0 = _context["catch"](14);
17412
17534
  _iterator.e(_context.t0);
17413
- case 66:
17414
- _context.prev = 66;
17535
+ case 72:
17536
+ _context.prev = 72;
17415
17537
  _iterator.f();
17416
- return _context.finish(66);
17417
- case 69:
17538
+ return _context.finish(72);
17539
+ case 75:
17418
17540
  (_logger$debug2 = logger.debug) === null || _logger$debug2 === void 0 || _logger$debug2.call(logger, "Added ".concat(featureCnt, " new feature(s) into database."));
17419
- case 70:
17541
+ case 76:
17420
17542
  case "end":
17421
17543
  return _context.stop();
17422
17544
  }
17423
- }, _callee, this, [[12, 63, 66, 69]]);
17545
+ }, _callee, this, [[14, 69, 72, 75]]);
17424
17546
  }));
17425
17547
  function executeOnServer(_x) {
17426
17548
  return _executeOnServer.apply(this, arguments);
@@ -18324,31 +18446,32 @@
18324
18446
  key: "executeOnServer",
18325
18447
  value: function () {
18326
18448
  var _executeOnServer = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(backend) {
18327
- var featureModel, session, changes, logger, featuresForChanges, _iterator, _step, _logger$debug, _logger$debug2, change, featureId, topLevelFeature, errMsg, foundFeature, _errMsg, _iterator2, _step2, _logger$debug4, _step2$value, idx, _change, newAttributes, _featuresForChanges$i, feature, _topLevelFeature, _logger$debug3;
18328
- return _regeneratorRuntime().wrap(function _callee$(_context) {
18329
- while (1) switch (_context.prev = _context.next) {
18449
+ var _this2 = this;
18450
+ var featureModel, session, changes, logger, featuresForChanges, _iterator, _step, _logger$debug, _logger$debug2, change, featureId, topLevelFeature, errMsg, foundFeature, _errMsg, INDEXED_IDS, idsToIndex, _iterator2, _step2, _loop;
18451
+ return _regeneratorRuntime().wrap(function _callee$(_context2) {
18452
+ while (1) switch (_context2.prev = _context2.next) {
18330
18453
  case 0:
18331
18454
  featureModel = backend.featureModel, session = backend.session;
18332
18455
  changes = this.changes, logger = this.logger;
18333
18456
  featuresForChanges = []; // Loop the changes and check that all features are found
18334
18457
  _iterator = _createForOfIteratorHelper(changes);
18335
- _context.prev = 4;
18458
+ _context2.prev = 4;
18336
18459
  _iterator.s();
18337
18460
  case 6:
18338
18461
  if ((_step = _iterator.n()).done) {
18339
- _context.next = 26;
18462
+ _context2.next = 26;
18340
18463
  break;
18341
18464
  }
18342
18465
  change = _step.value;
18343
18466
  featureId = change.featureId; // Search correct feature
18344
- _context.next = 11;
18467
+ _context2.next = 11;
18345
18468
  return featureModel.findOne({
18346
18469
  allIds: featureId
18347
18470
  }).session(session).exec();
18348
18471
  case 11:
18349
- topLevelFeature = _context.sent;
18472
+ topLevelFeature = _context2.sent;
18350
18473
  if (topLevelFeature) {
18351
- _context.next = 16;
18474
+ _context2.next = 16;
18352
18475
  break;
18353
18476
  }
18354
18477
  errMsg = "*** ERROR: The following featureId was not found in database ='".concat(featureId, "'");
@@ -18358,7 +18481,7 @@
18358
18481
  (_logger$debug = logger.debug) === null || _logger$debug === void 0 || _logger$debug.call(logger, "*** Feature found: ".concat(JSON.stringify(topLevelFeature)));
18359
18482
  foundFeature = this.getFeatureFromId(topLevelFeature, featureId);
18360
18483
  if (foundFeature) {
18361
- _context.next = 22;
18484
+ _context2.next = 22;
18362
18485
  break;
18363
18486
  }
18364
18487
  _errMsg = 'ERROR when searching feature by featureId';
@@ -18371,70 +18494,96 @@
18371
18494
  topLevelFeature: topLevelFeature
18372
18495
  });
18373
18496
  case 24:
18374
- _context.next = 6;
18497
+ _context2.next = 6;
18375
18498
  break;
18376
18499
  case 26:
18377
- _context.next = 31;
18500
+ _context2.next = 31;
18378
18501
  break;
18379
18502
  case 28:
18380
- _context.prev = 28;
18381
- _context.t0 = _context["catch"](4);
18382
- _iterator.e(_context.t0);
18503
+ _context2.prev = 28;
18504
+ _context2.t0 = _context2["catch"](4);
18505
+ _iterator.e(_context2.t0);
18383
18506
  case 31:
18384
- _context.prev = 31;
18507
+ _context2.prev = 31;
18385
18508
  _iterator.f();
18386
- return _context.finish(31);
18509
+ return _context2.finish(31);
18387
18510
  case 34:
18511
+ INDEXED_IDS = browser$1.env.INDEXED_IDS;
18512
+ if (INDEXED_IDS) {
18513
+ idsToIndex = INDEXED_IDS.split(',');
18514
+ }
18388
18515
  // Let's update objects
18389
18516
  _iterator2 = _createForOfIteratorHelper(changes.entries());
18390
- _context.prev = 35;
18517
+ _context2.prev = 37;
18518
+ _loop = /*#__PURE__*/_regeneratorRuntime().mark(function _loop() {
18519
+ var _idsToIndex, _logger$debug4;
18520
+ var _step2$value, idx, change, newAttributes, _featuresForChanges$i, feature, topLevelFeature, indexedIdsChanged, indexedIds, _logger$debug3;
18521
+ return _regeneratorRuntime().wrap(function _loop$(_context) {
18522
+ while (1) switch (_context.prev = _context.next) {
18523
+ case 0:
18524
+ _step2$value = _slicedToArray(_step2.value, 2), idx = _step2$value[0], change = _step2$value[1];
18525
+ newAttributes = change.newAttributes;
18526
+ _featuresForChanges$i = featuresForChanges[idx], feature = _featuresForChanges$i.feature, topLevelFeature = _featuresForChanges$i.topLevelFeature;
18527
+ indexedIdsChanged = (_idsToIndex = idsToIndex) === null || _idsToIndex === void 0 ? void 0 : _idsToIndex.some(function (id) {
18528
+ var _feature$attributes;
18529
+ return id in newAttributes || id in ((_feature$attributes = feature === null || feature === void 0 ? void 0 : feature.attributes) !== null && _feature$attributes !== void 0 ? _feature$attributes : {});
18530
+ });
18531
+ feature.attributes = newAttributes;
18532
+ if (indexedIdsChanged) {
18533
+ indexedIds = _this2.getIndexedIds(topLevelFeature, idsToIndex);
18534
+ topLevelFeature.indexedIds = indexedIds;
18535
+ topLevelFeature.markModified('indexedIds');
18536
+ }
18537
+ if (topLevelFeature._id.equals(feature._id)) {
18538
+ topLevelFeature.markModified('attributes'); // Mark as modified. Without this save() -method is not updating data in database
18539
+ } else {
18540
+ topLevelFeature.markModified('children'); // Mark as modified. Without this save() -method is not updating data in database
18541
+ }
18542
+ _context.prev = 7;
18543
+ _context.next = 10;
18544
+ return topLevelFeature.save();
18545
+ case 10:
18546
+ _context.next = 16;
18547
+ break;
18548
+ case 12:
18549
+ _context.prev = 12;
18550
+ _context.t0 = _context["catch"](7);
18551
+ (_logger$debug3 = logger.debug) === null || _logger$debug3 === void 0 || _logger$debug3.call(logger, "*** FAILED: ".concat(_context.t0));
18552
+ throw _context.t0;
18553
+ case 16:
18554
+ (_logger$debug4 = logger.debug) === null || _logger$debug4 === void 0 || _logger$debug4.call(logger, "*** Feature attributes modified (added, edited or deleted), docId: ".concat(JSON.stringify(topLevelFeature)));
18555
+ case 17:
18556
+ case "end":
18557
+ return _context.stop();
18558
+ }
18559
+ }, _loop, null, [[7, 12]]);
18560
+ });
18391
18561
  _iterator2.s();
18392
- case 37:
18562
+ case 40:
18393
18563
  if ((_step2 = _iterator2.n()).done) {
18394
- _context.next = 55;
18564
+ _context2.next = 44;
18395
18565
  break;
18396
18566
  }
18397
- _step2$value = _slicedToArray(_step2.value, 2), idx = _step2$value[0], _change = _step2$value[1];
18398
- newAttributes = _change.newAttributes;
18399
- _featuresForChanges$i = featuresForChanges[idx], feature = _featuresForChanges$i.feature, _topLevelFeature = _featuresForChanges$i.topLevelFeature;
18400
- feature.attributes = newAttributes;
18401
- if (_topLevelFeature._id.equals(feature._id)) {
18402
- _topLevelFeature.markModified('attributes'); // Mark as modified. Without this save() -method is not updating data in database
18403
- } else {
18404
- _topLevelFeature.markModified('children'); // Mark as modified. Without this save() -method is not updating data in database
18405
- }
18406
- _context.prev = 43;
18407
- _context.next = 46;
18408
- return _topLevelFeature.save();
18409
- case 46:
18410
- _context.next = 52;
18411
- break;
18412
- case 48:
18413
- _context.prev = 48;
18414
- _context.t1 = _context["catch"](43);
18415
- (_logger$debug3 = logger.debug) === null || _logger$debug3 === void 0 || _logger$debug3.call(logger, "*** FAILED: ".concat(_context.t1));
18416
- throw _context.t1;
18417
- case 52:
18418
- (_logger$debug4 = logger.debug) === null || _logger$debug4 === void 0 || _logger$debug4.call(logger, "*** Feature attributes modified (added, edited or deleted), docId: ".concat(JSON.stringify(_topLevelFeature)));
18419
- case 53:
18420
- _context.next = 37;
18567
+ return _context2.delegateYield(_loop(), "t1", 42);
18568
+ case 42:
18569
+ _context2.next = 40;
18421
18570
  break;
18422
- case 55:
18423
- _context.next = 60;
18571
+ case 44:
18572
+ _context2.next = 49;
18424
18573
  break;
18425
- case 57:
18426
- _context.prev = 57;
18427
- _context.t2 = _context["catch"](35);
18428
- _iterator2.e(_context.t2);
18429
- case 60:
18430
- _context.prev = 60;
18574
+ case 46:
18575
+ _context2.prev = 46;
18576
+ _context2.t2 = _context2["catch"](37);
18577
+ _iterator2.e(_context2.t2);
18578
+ case 49:
18579
+ _context2.prev = 49;
18431
18580
  _iterator2.f();
18432
- return _context.finish(60);
18433
- case 63:
18581
+ return _context2.finish(49);
18582
+ case 52:
18434
18583
  case "end":
18435
- return _context.stop();
18584
+ return _context2.stop();
18436
18585
  }
18437
- }, _callee, this, [[4, 28, 31, 34], [35, 57, 60, 63], [43, 48]]);
18586
+ }, _callee, this, [[4, 28, 31, 34], [37, 46, 49, 52]]);
18438
18587
  }));
18439
18588
  function executeOnServer(_x) {
18440
18589
  return _executeOnServer.apply(this, arguments);
@@ -18445,13 +18594,13 @@
18445
18594
  key: "executeOnLocalGFF3",
18446
18595
  value: function () {
18447
18596
  var _executeOnLocalGFF = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(_backend) {
18448
- return _regeneratorRuntime().wrap(function _callee2$(_context2) {
18449
- while (1) switch (_context2.prev = _context2.next) {
18597
+ return _regeneratorRuntime().wrap(function _callee2$(_context3) {
18598
+ while (1) switch (_context3.prev = _context3.next) {
18450
18599
  case 0:
18451
18600
  throw new Error('applyToLocalGFF3 not implemented');
18452
18601
  case 1:
18453
18602
  case "end":
18454
- return _context2.stop();
18603
+ return _context3.stop();
18455
18604
  }
18456
18605
  }, _callee2);
18457
18606
  }));
@@ -18465,49 +18614,49 @@
18465
18614
  value: function () {
18466
18615
  var _executeOnClient = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(dataStore) {
18467
18616
  var _iterator3, _step3, _step3$value, idx, changedId, feature;
18468
- return _regeneratorRuntime().wrap(function _callee3$(_context3) {
18469
- while (1) switch (_context3.prev = _context3.next) {
18617
+ return _regeneratorRuntime().wrap(function _callee3$(_context4) {
18618
+ while (1) switch (_context4.prev = _context4.next) {
18470
18619
  case 0:
18471
18620
  if (dataStore) {
18472
- _context3.next = 2;
18621
+ _context4.next = 2;
18473
18622
  break;
18474
18623
  }
18475
18624
  throw new Error('No data store');
18476
18625
  case 2:
18477
18626
  _iterator3 = _createForOfIteratorHelper(this.changedIds.entries());
18478
- _context3.prev = 3;
18627
+ _context4.prev = 3;
18479
18628
  _iterator3.s();
18480
18629
  case 5:
18481
18630
  if ((_step3 = _iterator3.n()).done) {
18482
- _context3.next = 13;
18631
+ _context4.next = 13;
18483
18632
  break;
18484
18633
  }
18485
18634
  _step3$value = _slicedToArray(_step3.value, 2), idx = _step3$value[0], changedId = _step3$value[1];
18486
18635
  feature = dataStore.getFeature(changedId);
18487
18636
  if (feature) {
18488
- _context3.next = 10;
18637
+ _context4.next = 10;
18489
18638
  break;
18490
18639
  }
18491
18640
  throw new Error("Could not find feature with identifier \"".concat(changedId, "\""));
18492
18641
  case 10:
18493
18642
  feature.setAttributes(new Map(Object.entries(this.changes[idx].newAttributes)));
18494
18643
  case 11:
18495
- _context3.next = 5;
18644
+ _context4.next = 5;
18496
18645
  break;
18497
18646
  case 13:
18498
- _context3.next = 18;
18647
+ _context4.next = 18;
18499
18648
  break;
18500
18649
  case 15:
18501
- _context3.prev = 15;
18502
- _context3.t0 = _context3["catch"](3);
18503
- _iterator3.e(_context3.t0);
18650
+ _context4.prev = 15;
18651
+ _context4.t0 = _context4["catch"](3);
18652
+ _iterator3.e(_context4.t0);
18504
18653
  case 18:
18505
- _context3.prev = 18;
18654
+ _context4.prev = 18;
18506
18655
  _iterator3.f();
18507
- return _context3.finish(18);
18656
+ return _context4.finish(18);
18508
18657
  case 21:
18509
18658
  case "end":
18510
- return _context3.stop();
18659
+ return _context4.stop();
18511
18660
  }
18512
18661
  }, _callee3, this, [[3, 15, 18, 21]]);
18513
18662
  }));
@@ -23804,7 +23953,7 @@
23804
23953
  d: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6z"
23805
23954
  }), 'Add');
23806
23955
 
23807
- var version = "0.3.9";
23956
+ var version = "0.3.11";
23808
23957
 
23809
23958
  const ApolloConfigSchema = configuration.ConfigurationSchema('ApolloInternetAccount', {
23810
23959
  baseURL: {
@@ -29343,6 +29492,19 @@
29343
29492
  ]);
29344
29493
  },
29345
29494
  });
29495
+ if (require$$1$2.isSessionModelWithWidgets(session)) {
29496
+ menuItems.push({
29497
+ label: 'Open feature details',
29498
+ onClick: () => {
29499
+ const apolloGeneWidget = session.addWidget('ApolloFeatureDetailsWidget', 'apolloFeatureDetailsWidget', {
29500
+ feature: sourceFeature,
29501
+ assembly: currentAssemblyId,
29502
+ refName: region.refName,
29503
+ });
29504
+ session.showWidget(apolloGeneWidget);
29505
+ },
29506
+ });
29507
+ }
29346
29508
  return menuItems;
29347
29509
  }
29348
29510
  function navToFeatureCenter(feature, paddingPct, refSeqLength) {
@@ -29595,7 +29757,7 @@
29595
29757
  statusMessage: 'Pre-validating',
29596
29758
  progressPct: 0,
29597
29759
  cancelCallback: () => {
29598
- controller.abort();
29760
+ controller.abort('AddAssembly');
29599
29761
  jobsManager.abortJob(job.name);
29600
29762
  },
29601
29763
  };
@@ -47874,7 +48036,7 @@
47874
48036
  });
47875
48037
  }
47876
48038
  return () => {
47877
- controller.abort();
48039
+ controller.abort('OntologyTermAutocomplete matcher');
47878
48040
  };
47879
48041
  }, [session, valueString, filterTerms, ontologyStore, needToLoadCurrentTerm]);
47880
48042
  // effect for loading term autocompletions
@@ -47893,7 +48055,7 @@
47893
48055
  });
47894
48056
  }
47895
48057
  return () => {
47896
- controller.abort();
48058
+ controller.abort('OntologyTermAutocomplete loader');
47897
48059
  };
47898
48060
  }, [
47899
48061
  needToLoadTermChoices,
@@ -48045,17 +48207,24 @@
48045
48207
  // pre-validate
48046
48208
  const session = require$$1$2.getSession(this.dataStore);
48047
48209
  const controller = new AbortController();
48048
- const { jobsManager, isLocked } = require$$1$2.getSession(this.dataStore);
48210
+ // eslint-disable-next-line @typescript-eslint/unbound-method
48211
+ const { jobsManager, isLocked, changeInProgress, setChangeInProgress } = require$$1$2.getSession(this.dataStore);
48049
48212
  if (isLocked) {
48050
48213
  session.notify('Cannot submit changes in locked mode');
48214
+ setChangeInProgress(false);
48051
48215
  return;
48052
48216
  }
48217
+ if (changeInProgress) {
48218
+ session.notify('Could not submit change, there is another change still in progress');
48219
+ return;
48220
+ }
48221
+ setChangeInProgress(true);
48053
48222
  const job = {
48054
48223
  name: change.typeName,
48055
48224
  statusMessage: 'Pre-validating',
48056
48225
  progressPct: 0,
48057
48226
  cancelCallback: () => {
48058
- controller.abort();
48227
+ controller.abort('ChangeManager');
48059
48228
  },
48060
48229
  };
48061
48230
  if (updateJobsManager) {
@@ -48068,6 +48237,7 @@
48068
48237
  jobsManager.abortJob(job.name, msg);
48069
48238
  }
48070
48239
  session.notify(msg, 'error');
48240
+ setChangeInProgress(false);
48071
48241
  return;
48072
48242
  }
48073
48243
  try {
@@ -48080,6 +48250,7 @@
48080
48250
  }
48081
48251
  console.error(error);
48082
48252
  session.notify(`Error encountered in client: ${String(error)}. Data may be out of sync, please refresh the page`, 'error');
48253
+ setChangeInProgress(false);
48083
48254
  return;
48084
48255
  }
48085
48256
  // post-validate
@@ -48110,6 +48281,7 @@
48110
48281
  console.error(error);
48111
48282
  session.notify(String(error), 'error');
48112
48283
  await this.undo(change, false);
48284
+ setChangeInProgress(false);
48113
48285
  return;
48114
48286
  }
48115
48287
  if (!backendResult.ok) {
@@ -48119,6 +48291,7 @@
48119
48291
  }
48120
48292
  session.notify(msg, 'error');
48121
48293
  await this.undo(change, false);
48294
+ setChangeInProgress(false);
48122
48295
  return;
48123
48296
  }
48124
48297
  if (change.notification) {
@@ -48132,6 +48305,7 @@
48132
48305
  if (updateJobsManager) {
48133
48306
  jobsManager.done(job);
48134
48307
  }
48308
+ setChangeInProgress(false);
48135
48309
  }
48136
48310
  async undo(change, submitToBackend = true) {
48137
48311
  const inverseChange = change.getInverse();
@@ -48237,17 +48411,22 @@
48237
48411
  checkSocket(assembly, refSeq, internetAccount) {
48238
48412
  const { socket } = internetAccount;
48239
48413
  const token = internetAccount.retrieveToken();
48414
+ if (!token) {
48415
+ return;
48416
+ }
48417
+ const localSessionId = dist$2.makeUserSessionId(token);
48240
48418
  const channel = `${assembly}-${refSeq}`;
48241
48419
  const changeManager = new ChangeManager(this.clientStore);
48242
48420
  if (!socket.hasListeners(channel)) {
48243
48421
  socket.on(channel, async (message) => {
48244
48422
  // Save server last change sequence into session storage
48245
48423
  internetAccount.setLastChangeSequenceNumber(Number(message.changeSequence));
48246
- if (message.userSessionId !== token && message.channel === channel) {
48247
- const change = dist$3.Change.fromJSON(message.changeInfo);
48248
- if (dist$3.isFeatureChange(change) && this.haveDataForChange(change)) {
48249
- await changeManager.submit(change, { submitToBackend: false });
48250
- }
48424
+ if (message.userSessionId === localSessionId) {
48425
+ return; // we did this change, no need to apply it again
48426
+ }
48427
+ const change = dist$3.Change.fromJSON(message.changeInfo);
48428
+ if (dist$3.isFeatureChange(change) && this.haveDataForChange(change)) {
48429
+ await changeManager.submit(change, { submitToBackend: false });
48251
48430
  }
48252
48431
  });
48253
48432
  }
@@ -49740,7 +49919,7 @@
49740
49919
  statusMessage: 'Uploading file, this may take awhile',
49741
49920
  progressPct: 0,
49742
49921
  cancelCallback: () => {
49743
- controller.abort();
49922
+ controller.abort('ImportFeatures');
49744
49923
  jobsManager.abortJob(job.name);
49745
49924
  },
49746
49925
  };
@@ -50991,7 +51170,7 @@
50991
51170
  }
50992
51171
  });
50993
51172
  return () => {
50994
- controller.abort();
51173
+ controller.abort('AuthTypeSelector');
50995
51174
  };
50996
51175
  }, [baseURL]);
50997
51176
  function handleClick(authType) {
@@ -51428,8 +51607,13 @@
51428
51607
  return;
51429
51608
  }
51430
51609
  if (self.role) {
51431
- await self.initialize(self.role);
51432
- reaction.dispose();
51610
+ try {
51611
+ await self.initialize(self.role);
51612
+ reaction.dispose();
51613
+ }
51614
+ catch {
51615
+ // if initialize fails, do nothing so the autorun runs again
51616
+ }
51433
51617
  }
51434
51618
  }, { name: 'ApolloInternetAccount' });
51435
51619
  },
@@ -55579,6 +55763,7 @@
55579
55763
  const refData = currentAssembly?.getByRefName(refName);
55580
55764
  const { changeManager } = session.apolloDataStore;
55581
55765
  const seqRef = React.useRef(null);
55766
+ const { changeInProgress } = session;
55582
55767
  if (!refData) {
55583
55768
  return null;
55584
55769
  }
@@ -56026,10 +56211,13 @@
56026
56211
  // highlight start codon and stop codons
56027
56212
  if (codonSeq === 'ATG') {
56028
56213
  elements.push(React__default["default"].createElement(material.Typography, { component: 'span', style: {
56029
- backgroundColor: 'yellow',
56214
+ backgroundColor: changeInProgress ? 'lightgray' : 'yellow',
56030
56215
  cursor: 'pointer',
56031
56216
  border: '1px solid black',
56032
56217
  }, key: codonGenomicPos, onClick: () => {
56218
+ if (changeInProgress) {
56219
+ return;
56220
+ }
56033
56221
  // NOTE: codonGenomicPos is important here for calculating the genomic location
56034
56222
  // of the start codon. We are using the codonGenomicPos as the key in the typography
56035
56223
  // elements to maintain the genomic postion of the codon start
@@ -56113,20 +56301,21 @@
56113
56301
  }
56114
56302
  // Trim any sequence before first start codon and after stop codon
56115
56303
  const startCodonIndex = translationSequence.indexOf('M');
56116
- const stopCodonIndex = translationSequence.indexOf('*') + 1;
56304
+ const stopCodonIndex = translationSequence.indexOf('*');
56117
56305
  const startCodonPos = translSeqCodonStartGenomicPosArr[startCodonIndex].codonGenomicPos;
56118
56306
  const stopCodonPos = translSeqCodonStartGenomicPosArr[stopCodonIndex].codonGenomicPos;
56119
56307
  if (!startCodonPos || !stopCodonPos) {
56120
56308
  return;
56121
56309
  }
56122
56310
  const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
56123
- const stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
56311
+ let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
56124
56312
  if (strand === 1) {
56125
56313
  if (startCodonGenomicLoc > stopCodonGenomicLoc) {
56126
56314
  notify('Start codon genomic location should be less than stop codon genomic location', 'error');
56127
56315
  return;
56128
56316
  }
56129
56317
  let promise;
56318
+ stopCodonGenomicLoc += 3; // move to end of stop codon
56130
56319
  if (startCodonGenomicLoc !== cdsMin) {
56131
56320
  promise = new Promise((resolve) => {
56132
56321
  updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
@@ -56152,6 +56341,7 @@
56152
56341
  return;
56153
56342
  }
56154
56343
  let promise;
56344
+ stopCodonGenomicLoc -= 3; // move to end of stop codon
56155
56345
  if (startCodonGenomicLoc !== cdsMax) {
56156
56346
  promise = new Promise((resolve) => {
56157
56347
  updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
@@ -56195,27 +56385,29 @@
56195
56385
  gap: 10,
56196
56386
  } },
56197
56387
  React__default["default"].createElement(material.Tooltip, { title: "Copy" },
56198
- React__default["default"].createElement(ContentCopyIcon, { style: { fontSize: 15, cursor: 'pointer' }, onClick: onCopyClick })),
56388
+ React__default["default"].createElement("button", { onClick: onCopyClick, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress },
56389
+ React__default["default"].createElement(ContentCopyIcon, { style: { fontSize: 15 } }))),
56199
56390
  React__default["default"].createElement(material.Tooltip, { title: "Trim" },
56200
- React__default["default"].createElement(ContentCutIcon, { style: { fontSize: 15, cursor: 'pointer' }, onClick: trimTranslationSequence }))))),
56391
+ React__default["default"].createElement("button", { onClick: trimTranslationSequence, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress },
56392
+ React__default["default"].createElement(ContentCutIcon, { style: { fontSize: 15 } })))))),
56201
56393
  React__default["default"].createElement(material.Grid, { container: true, justifyContent: "center", alignItems: "center", style: { textAlign: 'center', marginTop: 10 } },
56202
56394
  React__default["default"].createElement(material.Grid, { size: 1 }),
56203
56395
  strand === 1 ? (React__default["default"].createElement(material.Grid, { size: 4 },
56204
56396
  React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMin + 1, onChangeCommitted: (newLocation) => {
56205
56397
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
56206
- }, style: { border: '1px solid black', borderRadius: 5 } }))) : (React__default["default"].createElement(material.Grid, { size: 4 },
56398
+ }, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }))) : (React__default["default"].createElement(material.Grid, { size: 4 },
56207
56399
  React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMax, onChangeCommitted: (newLocation) => {
56208
56400
  return updateCDSLocation(cdsMax, newLocation, feature, false);
56209
- }, style: { border: '1px solid black', borderRadius: 5 } }))),
56401
+ }, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }))),
56210
56402
  React__default["default"].createElement(material.Grid, { size: 2 },
56211
56403
  React__default["default"].createElement(material.Typography, { component: 'span' }, "CDS")),
56212
56404
  strand === 1 ? (React__default["default"].createElement(material.Grid, { size: 4 },
56213
56405
  React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMax, onChangeCommitted: (newLocation) => {
56214
56406
  return updateCDSLocation(cdsMax, newLocation, feature, false);
56215
- }, style: { border: '1px solid black', borderRadius: 5 } }))) : (React__default["default"].createElement(material.Grid, { size: 4 },
56407
+ }, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }))) : (React__default["default"].createElement(material.Grid, { size: 4 },
56216
56408
  React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMin + 1, onChangeCommitted: (newLocation) => {
56217
56409
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
56218
- }, style: { border: '1px solid black', borderRadius: 5 } }))),
56410
+ }, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }))),
56219
56411
  React__default["default"].createElement(material.Grid, { size: 1 })))),
56220
56412
  React__default["default"].createElement("div", { style: { marginTop: 5 } }, transcriptExonParts.map((loc, index) => {
56221
56413
  return (React__default["default"].createElement("div", { key: index }, loc.type === 'exon' && (React__default["default"].createElement(material.Grid, { container: true, justifyContent: "center", alignItems: "center", style: { textAlign: 'center' } },
@@ -56224,19 +56416,19 @@
56224
56416
  strand === 1 ? (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
56225
56417
  React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: loc.min + 1, onChangeCommitted: (newLocation) => {
56226
56418
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
56227
- } }))) : (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
56419
+ }, disabled: changeInProgress }))) : (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
56228
56420
  React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: loc.max, onChangeCommitted: (newLocation) => {
56229
56421
  return handleExonLocationChange(loc.max, newLocation, feature, false);
56230
- } }))),
56422
+ }, disabled: changeInProgress }))),
56231
56423
  React__default["default"].createElement(material.Grid, { size: 2 },
56232
56424
  React__default["default"].createElement(Strand, { strand: feature.strand })),
56233
56425
  strand === 1 ? (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
56234
56426
  React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: loc.max, onChangeCommitted: (newLocation) => {
56235
56427
  return handleExonLocationChange(loc.max, newLocation, feature, false);
56236
- } }))) : (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
56428
+ }, disabled: changeInProgress }))) : (React__default["default"].createElement(material.Grid, { size: 4, style: { padding: 0 } },
56237
56429
  React__default["default"].createElement(StyledTextField, { margin: "dense", variant: "outlined", value: loc.min + 1, onChangeCommitted: (newLocation) => {
56238
56430
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
56239
- } }))),
56431
+ }, disabled: changeInProgress }))),
56240
56432
  React__default["default"].createElement(material.Grid, { size: 1 }, index !== transcriptExonParts.length - 1 &&
56241
56433
  getThreePrimeSpliceSite(loc, index).map((site, idx) => (React__default["default"].createElement(material.Typography, { key: idx, component: 'span', color: site.color }, site.spliceSite))))))));
56242
56434
  }))));
@@ -57913,8 +58105,8 @@
57913
58105
  },
57914
58106
  });
57915
58107
  if (require$$1$2.isSessionModelWithWidgets(session)) {
57916
- contextMenuItemsForFeature.push({
57917
- label: 'Open transcript details',
58108
+ contextMenuItemsForFeature.splice(1, 0, {
58109
+ label: 'Open transcript editor',
57918
58110
  onClick: () => {
57919
58111
  const apolloTranscriptWidget = session.addWidget('ApolloTranscriptDetails', 'apolloTranscriptDetails', {
57920
58112
  feature,
@@ -58262,6 +58454,25 @@
58262
58454
  clusters.sort((a, b) => a.message.localeCompare(b.message) || a.start - b.start);
58263
58455
  return clusters;
58264
58456
  }
58457
+ function codonColorCode(letter, theme, highContrast) {
58458
+ if (letter === 'M') {
58459
+ return theme.palette.startCodon;
58460
+ }
58461
+ if (letter === '*') {
58462
+ return highContrast ? theme.palette.text.primary : theme.palette.stopCodon;
58463
+ }
58464
+ return;
58465
+ }
58466
+ function colorCode(letter, theme) {
58467
+ const letterUpper = letter.toUpperCase();
58468
+ if (letterUpper === 'A' ||
58469
+ letterUpper === 'C' ||
58470
+ letterUpper === 'G' ||
58471
+ letterUpper === 'T') {
58472
+ return theme.palette.bases[letterUpper].main.toString();
58473
+ }
58474
+ return 'lightgray';
58475
+ }
58265
58476
 
58266
58477
  const minDisplayHeight$2 = 20;
58267
58478
  function baseModelFactory$2(_pluginManager, configSchema) {
@@ -59087,34 +59298,26 @@
59087
59298
 
59088
59299
  const configSchema$1 = configuration.ConfigurationSchema('LinearApolloReferenceSequenceDisplay', {}, { explicitIdentifier: 'displayId', explicitlyTyped: true });
59089
59300
 
59090
- function getSeqRow(strand, bpPerPx) {
59301
+ function getSeqRow(strand, bpPerPx, reversed) {
59091
59302
  if (bpPerPx > 1 || strand === undefined) {
59092
59303
  return;
59093
59304
  }
59305
+ if (reversed) {
59306
+ return strand === 1 ? 4 : 3;
59307
+ }
59094
59308
  return strand === 1 ? 3 : 4;
59095
59309
  }
59096
- function getTranslationRow(frame, bpPerPx) {
59097
- const offset = bpPerPx <= 1 ? 2 : 0;
59098
- switch (frame) {
59099
- case 3: {
59100
- return 0;
59101
- }
59102
- case 2: {
59103
- return 1;
59104
- }
59105
- case 1: {
59106
- return 2;
59107
- }
59108
- case -1: {
59109
- return 3 + offset;
59110
- }
59111
- case -2: {
59112
- return 4 + offset;
59113
- }
59114
- case -3: {
59115
- return 5 + offset;
59116
- }
59310
+ function getTranslationRow(frame, bpPerPx, reversed) {
59311
+ const frameRows = bpPerPx <= 1 ? [2, 1, 0, 7, 6, 5] : [2, 1, 0, 5, 4, 3];
59312
+ if (reversed) {
59313
+ frameRows.reverse();
59314
+ }
59315
+ frameRows.unshift(0);
59316
+ const row = frameRows.at(frame);
59317
+ if (row === undefined) {
59318
+ throw new Error('could not find row');
59117
59319
  }
59320
+ return row;
59118
59321
  }
59119
59322
  function getLeftPx$1(feature, bpPerPx, offsetPx, block) {
59120
59323
  const blockLeftPx = block.offsetPx - offsetPx;
@@ -59136,7 +59339,7 @@
59136
59339
  ctx.strokeRect(left, top, width, height);
59137
59340
  }
59138
59341
  function drawHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme, selected = false) {
59139
- const row = getSeqRow(feature.strand, bpPerPx);
59342
+ const row = getSeqRow(feature.strand, bpPerPx, block.reversed);
59140
59343
  if (!row) {
59141
59344
  return;
59142
59345
  }
@@ -59160,7 +59363,7 @@
59160
59363
  }
59161
59364
  for (const loc of cdsLocs) {
59162
59365
  const frame = require$$1$2.getFrame(loc.min, loc.max, feature.strand ?? 1, loc.phase);
59163
- const row = getTranslationRow(frame, bpPerPx);
59366
+ const row = getTranslationRow(frame, bpPerPx, block.reversed);
59164
59367
  const left = getLeftPx$1(loc, bpPerPx, offsetPx, block);
59165
59368
  const top = row * rowHeight;
59166
59369
  const width = (loc.max - loc.min) / bpPerPx;
@@ -59183,76 +59386,71 @@
59183
59386
  hoveredFeature?.feature,
59184
59387
  ].filter((f) => f !== undefined)) {
59185
59388
  if (featureTypeOntology.isTypeOf(feature.type, 'CDS')) {
59186
- drawCDSHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme, true);
59389
+ drawCDSHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme, feature._id === selectedFeature?._id);
59187
59390
  }
59188
59391
  else {
59189
- drawHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme, true);
59392
+ drawHighlight(ctx, feature, bpPerPx, offsetPx, rowHeight, block, theme, feature._id === selectedFeature?._id);
59190
59393
  }
59191
59394
  }
59192
59395
  ctx.restore();
59193
59396
  }
59194
59397
  }
59195
59398
 
59196
- function colorCode(letter, theme) {
59197
- const letterUpper = letter.toUpperCase();
59198
- if (letterUpper === 'A' ||
59199
- letterUpper === 'C' ||
59200
- letterUpper === 'G' ||
59201
- letterUpper === 'T') {
59202
- return theme.palette.bases[letterUpper].main.toString();
59203
- }
59204
- return 'lightgray';
59399
+ function getLeftPx(display, feature, block) {
59400
+ const { lgv } = display;
59401
+ const { bpPerPx, offsetPx } = lgv;
59402
+ const blockLeftPx = block.offsetPx - offsetPx;
59403
+ const featureLeftBpDistanceFromBlockLeftBp = block.reversed
59404
+ ? block.end - feature.max
59405
+ : feature.min - block.start;
59406
+ const featureLeftPxDistanceFromBlockLeftPx = featureLeftBpDistanceFromBlockLeftBp / bpPerPx;
59407
+ return blockLeftPx + featureLeftPxDistanceFromBlockLeftPx;
59205
59408
  }
59206
- function codonColorCode(letter, theme, highContrast) {
59207
- if (letter === 'M') {
59208
- return theme.palette.startCodon;
59209
- }
59210
- if (letter === '*') {
59211
- return highContrast ? theme.palette.text.primary : theme.palette.stopCodon;
59212
- }
59213
- return;
59409
+ /**
59410
+ * Perform a canvas strokeRect, but have the stroke be contained within the
59411
+ * given rect instead of centered on it.
59412
+ */
59413
+ function strokeRectInner(ctx, left, top, width, height, color) {
59414
+ ctx.strokeStyle = color;
59415
+ ctx.lineWidth = 1;
59416
+ ctx.strokeRect(left + 0.5, top + 0.5, width - 1, height - 1);
59214
59417
  }
59418
+
59215
59419
  function drawLetter(seqTrackctx, left, top, width, letter) {
59216
59420
  const fontSize = Math.min(width, 10);
59217
59421
  seqTrackctx.fillStyle = '#000';
59218
59422
  seqTrackctx.font = `${fontSize}px`;
59219
59423
  const textWidth = seqTrackctx.measureText(letter).width;
59220
- const textX = left + (width - textWidth) / 2;
59424
+ const textX = Math.round(left + (width - textWidth) / 2);
59221
59425
  seqTrackctx.fillText(letter, textX, top + 10);
59222
59426
  }
59223
- function drawTranslationFrameBackgrounds(canvas, ctx, bpPerPx, theme, dynamicBlocks, highContrast, sequenceRowHeight) {
59427
+ function drawTranslationFrameBackgrounds(ctx, bpPerPx, theme, highContrast, left, width, sequenceRowHeight, reversed) {
59224
59428
  const frames = bpPerPx <= 1 ? [3, 2, 1, 0, 0, -1, -2, -3] : [3, 2, 1, -1, -2, -3];
59429
+ if (reversed) {
59430
+ frames.reverse();
59431
+ }
59225
59432
  for (const [idx, frame] of frames.entries()) {
59226
59433
  const frameColor = theme.palette.framesCDS.at(frame)?.main;
59227
59434
  if (!frameColor) {
59228
59435
  continue;
59229
59436
  }
59230
59437
  const top = idx * sequenceRowHeight;
59231
- const { offsetPx } = dynamicBlocks;
59232
- const left = Math.max(0, -offsetPx);
59233
- const width = dynamicBlocks.totalWidthPx;
59234
59438
  ctx.fillStyle = highContrast ? theme.palette.background.default : frameColor;
59235
59439
  ctx.fillRect(left, top, width, sequenceRowHeight);
59236
59440
  if (highContrast) {
59237
59441
  // eslint-disable-next-line prefer-destructuring
59238
- ctx.strokeStyle = theme.palette.grey[200];
59239
- ctx.strokeRect(left, top, width, sequenceRowHeight);
59240
- }
59241
- }
59242
- // allows inter-region padding lines to show through
59243
- for (const block of dynamicBlocks.getBlocks()) {
59244
- if (block.type === 'InterRegionPaddingBlock') {
59245
- const left = block.offsetPx - dynamicBlocks.offsetPx;
59246
- ctx.clearRect(left, 0, block.widthPx, canvas.height);
59442
+ const strokeStyle = theme.palette.grey[200];
59443
+ strokeRectInner(ctx, left, top, width, sequenceRowHeight, strokeStyle);
59247
59444
  }
59248
59445
  }
59249
59446
  }
59250
59447
  function drawBase(ctx, base, index, leftPx, bpPerPx, rowHeight, theme) {
59251
- const width = 1 / bpPerPx;
59252
- if (width < 1) {
59448
+ if (1 / bpPerPx < 1) {
59253
59449
  return;
59254
59450
  }
59255
- const left = leftPx + index / bpPerPx;
59451
+ const left = Math.round(leftPx + index / bpPerPx);
59452
+ const nextLeft = Math.round(leftPx + (index + 1) / bpPerPx);
59453
+ const width = nextLeft - left;
59256
59454
  const strands = [-1, 1];
59257
59455
  for (const strand of strands) {
59258
59456
  const top = (strand === 1 ? 3 : 4) * rowHeight;
@@ -59260,13 +59458,13 @@
59260
59458
  ctx.fillStyle = colorCode(baseCode, theme);
59261
59459
  ctx.fillRect(left, top, width, rowHeight);
59262
59460
  if (1 / bpPerPx >= 12) {
59263
- ctx.strokeStyle = theme.palette.text.disabled;
59264
- ctx.strokeRect(left, top, width, rowHeight);
59461
+ const strokeStyle = theme.palette.text.disabled;
59462
+ strokeRectInner(ctx, left, top, width, rowHeight, strokeStyle);
59265
59463
  drawLetter(ctx, left, top, width, baseCode);
59266
59464
  }
59267
59465
  }
59268
59466
  }
59269
- function drawCodon(ctx, codon, leftPx, index, theme, highContrast, bpPerPx, bp, rowHeight, showStartCodons, showStopCodons) {
59467
+ function drawCodon$1(ctx, codon, leftPx, index, theme, highContrast, bpPerPx, bp, rowHeight, showStartCodons, showStopCodons) {
59270
59468
  const frameOffsets = (bpPerPx <= 1 ? [0, 2, 1, 0, 7, 6, 5] : [0, 2, 1, 0, 5, 4, 3]).map((b) => b * rowHeight);
59271
59469
  const strands = [-1, 1];
59272
59470
  for (const strand of strands) {
@@ -59276,7 +59474,8 @@
59276
59474
  continue;
59277
59475
  }
59278
59476
  const left = Math.round(leftPx + index / bpPerPx);
59279
- const width = Math.round(3 / bpPerPx);
59477
+ const nextLeft = Math.round(leftPx + (index + 3) / bpPerPx);
59478
+ const width = nextLeft - left;
59280
59479
  const codonCode = strand === 1 ? codon : require$$1$2.revcom(codon);
59281
59480
  const aminoAcidCode = require$$1$2.defaultCodonTable[codonCode];
59282
59481
  const fillColor = codonColorCode(aminoAcidCode, theme, highContrast);
@@ -59287,8 +59486,8 @@
59287
59486
  ctx.fillRect(left, top, width, rowHeight);
59288
59487
  }
59289
59488
  if (1 / bpPerPx >= 4) {
59290
- ctx.strokeStyle = theme.palette.text.disabled;
59291
- ctx.strokeRect(left, top, width, rowHeight);
59489
+ const strokeStyle = theme.palette.text.disabled;
59490
+ strokeRectInner(ctx, left, top, width, rowHeight, strokeStyle);
59292
59491
  drawLetter(ctx, left, top, width, aminoAcidCode);
59293
59492
  }
59294
59493
  }
@@ -59299,9 +59498,10 @@
59299
59498
  return;
59300
59499
  }
59301
59500
  ctx.clearRect(0, 0, canvas.width, canvas.height);
59302
- drawTranslationFrameBackgrounds(canvas, ctx, bpPerPx, theme, dynamicBlocks, highContrast, sequenceRowHeight);
59303
59501
  const { apolloDataStore } = session;
59304
59502
  for (const block of dynamicBlocks.contentBlocks) {
59503
+ const totalOffsetPx = block.offsetPx - offsetPx;
59504
+ drawTranslationFrameBackgrounds(ctx, bpPerPx, theme, highContrast, totalOffsetPx, block.widthPx, sequenceRowHeight, block.reversed);
59305
59505
  const assembly = apolloDataStore.assemblies.get(block.assemblyName);
59306
59506
  const ref = assembly?.getByRefName(block.refName);
59307
59507
  const roundedStart = Math.floor(block.start);
@@ -59311,16 +59511,20 @@
59311
59511
  return;
59312
59512
  }
59313
59513
  seq = seq.toUpperCase();
59314
- const baseOffsetPx = (block.start - roundedStart) / bpPerPx;
59315
- const seqLeftPx = Math.round(block.offsetPx - offsetPx - baseOffsetPx);
59514
+ if (block.reversed) {
59515
+ seq = require$$1$2.revcom(seq);
59516
+ }
59517
+ const baseOffsetPx = (block.reversed ? roundedEnd - block.end : block.start - roundedStart) /
59518
+ bpPerPx;
59519
+ const seqLeftPx = totalOffsetPx - baseOffsetPx;
59316
59520
  for (let i = 0; i < seq.length; i++) {
59317
- const bp = roundedStart + i;
59521
+ const bp = block.reversed ? roundedEnd - i : roundedStart + i;
59318
59522
  const codon = seq.slice(i, i + 3);
59319
59523
  drawBase(ctx, seq[i], i, seqLeftPx, bpPerPx, sequenceRowHeight, theme);
59320
59524
  if (codon.length !== 3) {
59321
59525
  continue;
59322
59526
  }
59323
- drawCodon(ctx, codon, seqLeftPx, i, theme, highContrast, bpPerPx, bp, sequenceRowHeight, showStartCodons, showStopCodons);
59527
+ drawCodon$1(ctx, codon, seqLeftPx, i, theme, highContrast, bpPerPx, bp, sequenceRowHeight, showStartCodons, showStopCodons);
59324
59528
  }
59325
59529
  }
59326
59530
  }
@@ -60384,6 +60588,8 @@
60384
60588
  graphical: true,
60385
60589
  table: false,
60386
60590
  showFeatureLabels: true,
60591
+ showStartCodons: false,
60592
+ showStopCodons: true,
60387
60593
  showCheckResults: true,
60388
60594
  zoomThreshold: 200,
60389
60595
  heightPreConfig: require$$1$3.types.maybe(require$$1$3.types.refinement('displayHeight', require$$1$3.types.number, (n) => n >= minDisplayHeight)),
@@ -60512,6 +60718,12 @@
60512
60718
  toggleShowFeatureLabels() {
60513
60719
  self.showFeatureLabels = !self.showFeatureLabels;
60514
60720
  },
60721
+ toggleShowStartCodons() {
60722
+ self.showStartCodons = !self.showStartCodons;
60723
+ },
60724
+ toggleShowStopCodons() {
60725
+ self.showStopCodons = !self.showStopCodons;
60726
+ },
60515
60727
  toggleShowCheckResults() {
60516
60728
  self.showCheckResults = !self.showCheckResults;
60517
60729
  },
@@ -60526,7 +60738,7 @@
60526
60738
  const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self;
60527
60739
  return {
60528
60740
  trackMenuItems() {
60529
- const { graphical, table, showFeatureLabels, showCheckResults } = self;
60741
+ const { graphical, table, showFeatureLabels, showStartCodons, showStopCodons, showCheckResults, } = self;
60530
60742
  return [
60531
60743
  ...superTrackMenuItems(),
60532
60744
  {
@@ -60565,6 +60777,22 @@
60565
60777
  self.toggleShowFeatureLabels();
60566
60778
  },
60567
60779
  },
60780
+ {
60781
+ label: 'Show start codons',
60782
+ type: 'checkbox',
60783
+ checked: showStartCodons,
60784
+ onClick: () => {
60785
+ self.toggleShowStartCodons();
60786
+ },
60787
+ },
60788
+ {
60789
+ label: 'Show stop codons',
60790
+ type: 'checkbox',
60791
+ checked: showStopCodons,
60792
+ onClick: () => {
60793
+ self.toggleShowStopCodons();
60794
+ },
60795
+ },
60568
60796
  {
60569
60797
  label: 'Check Results',
60570
60798
  type: 'checkbox',
@@ -60641,7 +60869,7 @@
60641
60869
  return;
60642
60870
  }
60643
60871
  void self.session.apolloDataStore.loadFeatures(self.regions);
60644
- if (self.lgv.bpPerPx <= 3) {
60872
+ if (self.lgv.bpPerPx <= self.zoomThreshold) {
60645
60873
  void self.session.apolloDataStore.loadRefSeq(self.regions);
60646
60874
  }
60647
60875
  }, { name: 'LinearApolloSixFrameDisplayLoadFeatures', delay: 1000 }));
@@ -60833,6 +61061,28 @@
60833
61061
  }));
60834
61062
  }
60835
61063
 
61064
+ function drawCodon(ctx, codon, leftPx, index, theme, highContrast, bpPerPx, bp, rowHeight, showFeatureLabels, showStartCodons, showStopCodons) {
61065
+ const frameOffsets = (showFeatureLabels ? [0, 4, 2, 0, 14, 12, 10] : [0, 2, 1, 0, 7, 6, 5]).map((b) => b * rowHeight);
61066
+ const strands = [-1, 1];
61067
+ for (const strand of strands) {
61068
+ const frame = require$$1$2.getFrame(bp, bp + 3, strand, 0);
61069
+ const top = frameOffsets.at(frame);
61070
+ if (top === undefined) {
61071
+ continue;
61072
+ }
61073
+ const left = Math.round(leftPx + index / bpPerPx);
61074
+ const width = Math.round(3 / bpPerPx) === 0 ? 1 : Math.round(3 / bpPerPx);
61075
+ const codonCode = strand === 1 ? codon : require$$1$2.revcom(codon);
61076
+ const aminoAcidCode = require$$1$2.defaultCodonTable[codonCode];
61077
+ const fillColor = codonColorCode(aminoAcidCode, theme, highContrast);
61078
+ if (fillColor &&
61079
+ ((showStopCodons && aminoAcidCode == '*') ||
61080
+ (showStartCodons && aminoAcidCode != '*'))) {
61081
+ ctx.fillStyle = fillColor;
61082
+ ctx.fillRect(left, top, width, rowHeight);
61083
+ }
61084
+ }
61085
+ }
60836
61086
  function renderingModelFactory(pluginManager, configSchema) {
60837
61087
  const LinearApolloSixFrameDisplayLayouts = layoutsModelFactory(pluginManager, configSchema);
60838
61088
  return LinearApolloSixFrameDisplayLayouts.named('LinearApolloSixFrameDisplayRendering')
@@ -60922,11 +61172,11 @@
60922
61172
  }
60923
61173
  }, { name: 'LinearApolloSixFrameDisplayRenderCollaborators' }));
60924
61174
  require$$1$3.addDisposer(self, mobx.autorun(() => {
60925
- const { canvas, featureLayouts, featuresHeight, lgv } = self;
61175
+ const { apolloRowHeight, canvas, featureLayouts, featuresHeight, lgv, session, theme, showFeatureLabels, showStartCodons, showStopCodons, } = self;
60926
61176
  if (!lgv.initialized || self.regionCannotBeRendered()) {
60927
61177
  return;
60928
61178
  }
60929
- const { displayedRegions, dynamicBlocks } = lgv;
61179
+ const { bpPerPx, offsetPx, displayedRegions, dynamicBlocks } = lgv;
60930
61180
  const ctx = canvas?.getContext('2d');
60931
61181
  if (!ctx) {
60932
61182
  return;
@@ -60950,6 +61200,27 @@
60950
61200
  }
60951
61201
  }
60952
61202
  }
61203
+ if (showStartCodons || showStopCodons) {
61204
+ const { apolloDataStore } = session;
61205
+ for (const block of dynamicBlocks.contentBlocks) {
61206
+ const assembly = apolloDataStore.assemblies.get(block.assemblyName);
61207
+ const ref = assembly?.getByRefName(block.refName);
61208
+ const roundedStart = Math.floor(block.start);
61209
+ const roundedEnd = Math.ceil(block.end);
61210
+ let seq = ref?.getSequence(roundedStart, roundedEnd);
61211
+ if (!seq) {
61212
+ break;
61213
+ }
61214
+ seq = seq.toUpperCase();
61215
+ const baseOffsetPx = (block.start - roundedStart) / bpPerPx;
61216
+ const seqLeftPx = Math.round(block.offsetPx - offsetPx - baseOffsetPx);
61217
+ for (let i = 0; i < seq.length; i++) {
61218
+ const bp = roundedStart + i;
61219
+ const codon = seq.slice(i, i + 3);
61220
+ drawCodon(ctx, codon, seqLeftPx, i, theme, true, bpPerPx, bp, apolloRowHeight, showFeatureLabels, showStartCodons, showStopCodons);
61221
+ }
61222
+ }
61223
+ }
60953
61224
  }, { name: 'LinearApolloSixFrameDisplayRenderFeatures' }));
60954
61225
  },
60955
61226
  }));
@@ -61912,17 +62183,6 @@
61912
62183
  d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-2h2zm0-4h-2V7h2z"
61913
62184
  }), 'Error');
61914
62185
 
61915
- function getLeftPx(display, feature, block) {
61916
- const { lgv } = display;
61917
- const { bpPerPx, offsetPx } = lgv;
61918
- const blockLeftPx = block.offsetPx - offsetPx;
61919
- const featureLeftBpDistanceFromBlockLeftBp = block.reversed
61920
- ? block.end - feature.max
61921
- : feature.min - block.start;
61922
- const featureLeftPxDistanceFromBlockLeftPx = featureLeftBpDistanceFromBlockLeftBp / bpPerPx;
61923
- return blockLeftPx + featureLeftPxDistanceFromBlockLeftPx;
61924
- }
61925
-
61926
62186
  const CheckResultWarnings = mobxReact.observer(function CheckResultWarnings({ display, }) {
61927
62187
  const { classes } = useStyles$1();
61928
62188
  const { apolloDragging, apolloRowHeight, lgv, session, showCheckResults } = display;
@@ -62254,7 +62514,7 @@
62254
62514
  const controller = new AbortController();
62255
62515
  const { signal } = controller;
62256
62516
  function abortDrag() {
62257
- controller.abort();
62517
+ controller.abort('makeDisplayComponent');
62258
62518
  }
62259
62519
  globalThis.addEventListener('mousemove', mouseMove, { signal });
62260
62520
  globalThis.addEventListener('mouseup', abortDrag, { signal });
@@ -62691,7 +62951,7 @@
62691
62951
  statusMessage: `Loading ontology "${name}", version "${version}", this may take a while`,
62692
62952
  progressPct: 0,
62693
62953
  cancelCallback: () => {
62694
- controller.abort();
62954
+ controller.abort('ClientDataStore');
62695
62955
  jobsManager.abortJob(job.name);
62696
62956
  },
62697
62957
  };
@@ -62830,6 +63090,7 @@
62830
63090
  apolloSelectedFeature: require$$1$3.types.safeReference(AnnotationFeatureExtended),
62831
63091
  jobsManager: require$$1$3.types.optional(ApolloJobModel, {}),
62832
63092
  isLocked: require$$1$3.types.optional(require$$1$3.types.boolean, false),
63093
+ changeInProgress: require$$1$3.types.optional(require$$1$3.types.boolean, false),
62833
63094
  })
62834
63095
  .volatile(() => ({
62835
63096
  apolloHoveredFeature: undefined,
@@ -62892,6 +63153,9 @@
62892
63153
  toggleLocked() {
62893
63154
  self.isLocked = !self.isLocked;
62894
63155
  },
63156
+ setChangeInProgress(changeInProgress) {
63157
+ self.changeInProgress = changeInProgress;
63158
+ },
62895
63159
  getPluginConfiguration() {
62896
63160
  const { jbrowse } = require$$1$3.getRoot(self);
62897
63161
  const pluginConfiguration =