archaeopteryx 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +116 -21
  2. package/archaeopteryx.js +1651 -578
  3. package/forester.js +297 -11
  4. package/package.json +1 -1
package/forester.js CHANGED
@@ -20,7 +20,7 @@
20
20
  *
21
21
  */
22
22
 
23
- // v 3.0.0
23
+ // v 3.1.0
24
24
  // 2026-09-04
25
25
  //
26
26
  // forester.js is a general suite for dealing with phylogenetic trees.
@@ -649,6 +649,21 @@
649
649
  const VIS_MAX_NUMERIC_UNIQUE_NUM = 9; // distinct/covered <= 0.9,
650
650
  const VIS_MAX_NUMERIC_UNIQUE_DEN = 10; // integer-exact as well
651
651
  const VIS_EXCLUDED_REF_PREFIX = 'style:';
652
+ // Refs that are never a visualization, however their values distribute.
653
+ // A taxon identifier repeats like a category and passes every statistical
654
+ // test above, yet says nothing a colour could carry that the species name
655
+ // beside it does not -- and reads as "11320" in a legend. Matched on the
656
+ // ref's local name with case and separators ignored, so vipr:NCBI_Taxon_Id,
657
+ // ncbi_taxid, taxon_id and taxonomy_id all count.
658
+ const VIS_EXCLUDED_LOCAL_NAME_RE = /(taxonomy|taxon|tax)id$/;
659
+
660
+ function visExcludedRef(ref) {
661
+ if (ref.indexOf(VIS_EXCLUDED_REF_PREFIX) === 0) {
662
+ return true;
663
+ }
664
+ let local = ref.substring(ref.indexOf(':') + 1).toLowerCase().replace(/[^a-z0-9]/g, '');
665
+ return VIS_EXCLUDED_LOCAL_NAME_RE.test(local);
666
+ }
652
667
 
653
668
  // ---- display normalization --------------------------------------------
654
669
  //
@@ -796,6 +811,14 @@
796
811
  .join(' ');
797
812
  }
798
813
 
814
+ // The display name for a property ref, as the Color-by menu and legends
815
+ // show it: namespace dropped, then prettified. Exported so the node-data
816
+ // dialog names a property the same way the rest of the viewer does.
817
+ forester.propertyDisplayName = function (ref) {
818
+ let local = ref.indexOf(':') >= 0 ? ref.substring(ref.indexOf(':') + 1) : ref;
819
+ return prettifyVisLabel(local);
820
+ };
821
+
799
822
  forester.visualizationCandidates = function (tree) {
800
823
  let total = 0;
801
824
  let stats = Object.create(null); // id -> {kind, ref, label, nodes, values:Set, multi}; null-proto: ids embed file refs
@@ -832,8 +855,7 @@
832
855
  if (n.properties) {
833
856
  for (let i = 0; i < n.properties.length; ++i) {
834
857
  let p = n.properties[i];
835
- if (p.ref && p.applies_to === 'node'
836
- && p.ref.indexOf(VIS_EXCLUDED_REF_PREFIX) !== 0) {
858
+ if (p.ref && p.applies_to === 'node' && !visExcludedRef(p.ref)) {
837
859
  add('prop:' + p.ref, 'property', p.ref, null, p.value);
838
860
  }
839
861
  }
@@ -1302,6 +1324,17 @@
1302
1324
  // Whether a tree is worth drawing to scale is a question about the
1303
1325
  // majority of its branches, not about whether any branch has a length.
1304
1326
  properties.branchesWithPositiveLength = 0;
1327
+ // Branches that carry a length AT ALL -- an explicit zero is a real
1328
+ // measurement, not a missing one, so these are counted separately from
1329
+ // the "positive" tally above. Split internal-vs-all because a missing
1330
+ // length means different things in the two places: an unmeasured TIP
1331
+ // still draws correctly (at its parent), while an unmeasured INTERNAL
1332
+ // branch destroys the scale. All four counts exclude the root, which
1333
+ // has no branch above it.
1334
+ properties.branchCount = 0;
1335
+ properties.branchesWithLength = 0;
1336
+ properties.internalBranchCount = 0;
1337
+ properties.internalBranchesWithLength = 0;
1305
1338
  properties.averageBranchLength = 0;
1306
1339
  let bl_counter = 0;
1307
1340
  let bl_sum = 0;
@@ -1309,8 +1342,23 @@
1309
1342
  // exist -- skewing the branch-length fraction the viewer uses to choose
1310
1343
  // between a phylogram and a cladogram -- and from phyloXML would take
1311
1344
  // the tree's own name for the longest node name.
1312
- forester.preOrderTraversalAll(realRootOf(tree), function (n) {
1345
+ let rootNode = realRootOf(tree);
1346
+ forester.preOrderTraversalAll(rootNode, function (n) {
1313
1347
  properties.nodeCount += 1;
1348
+ if (n !== rootNode) {
1349
+ let internal = !!(n.children || n._children);
1350
+ let measured = typeof n.branch_length === 'number' && isFinite(n.branch_length);
1351
+ properties.branchCount += 1;
1352
+ if (measured) {
1353
+ properties.branchesWithLength += 1;
1354
+ }
1355
+ if (internal) {
1356
+ properties.internalBranchCount += 1;
1357
+ if (measured) {
1358
+ properties.internalBranchesWithLength += 1;
1359
+ }
1360
+ }
1361
+ }
1314
1362
  if (n.name && n.name.length > 0) {
1315
1363
  properties.nodeNames = true;
1316
1364
  if (n.name.length > properties.longestNodeName) {
@@ -1916,9 +1964,12 @@
1916
1964
  if (confidenceValuesAsInternalNames === undefined) {
1917
1965
  confidenceValuesAsInternalNames = false;
1918
1966
  }
1919
- if ((confidenceValuesInBrackets === true) && (confidenceValuesAsInternalNames === true)) {
1920
- throw ("confidence values cannot be both in brackets and as internal node names");
1921
- }
1967
+ // The two options used to be mutually exclusive and throwing. They are
1968
+ // not: brackets are consumed by the tokenizer, bare labels are handled
1969
+ // afterwards, so a file mixing the two dialects is read correctly with
1970
+ // both on. forester-Java never had this guard, so it was never part of
1971
+ // the shared contract -- and throwing on a combination a user
1972
+ // plausibly wants is what forced callers into workarounds.
1922
1973
 
1923
1974
  let ancs = [];
1924
1975
  let x = {};
@@ -3560,6 +3611,117 @@
3560
3611
  {name: 'Miaolingian', young: 497, old: 506.5, color: '#A6CF86'},
3561
3612
  {name: 'Series 2', young: 506.5, old: 521, color: '#99C078'},
3562
3613
  {name: 'Terreneuvian', young: 521, old: 538.8, color: '#8CB06C'}
3614
+ ],
3615
+ // The 101 ratified Phanerozoic stages, plus the Pridoli: a Series with
3616
+ // no stages of its own, standing in the stage row for its own span
3617
+ // (419.62-422.7 Ma) as on the printed ICS chart -- without it the row
3618
+ // would have a hole there. Same source as every rank above
3619
+ // (Macrostrat international timescale, id 11), which the ranks above
3620
+ // match byte for byte; the desktop generated its table from the same
3621
+ // query, so the two programs band from identical data.
3622
+ age: [
3623
+ {name: 'Meghalayan', young: 0, old: 0.0042, color: '#FEF2E0'},
3624
+ {name: 'Northgrippian', young: 0.0042, old: 0.0082, color: '#FEF2E0'},
3625
+ {name: 'Greenlandian', young: 0.0082, old: 0.0117, color: '#FEF2E0'},
3626
+ {name: 'Late Pleistocene', young: 0.0117, old: 0.129, color: '#FFF2C7'},
3627
+ {name: 'Chibanian', young: 0.129, old: 0.774, color: '#FFF2C7'},
3628
+ {name: 'Calabrian', young: 0.774, old: 1.8, color: '#FFF2C7'},
3629
+ {name: 'Gelasian', young: 1.8, old: 2.58, color: '#FFEDB3'},
3630
+ {name: 'Piacenzian', young: 2.58, old: 3.6, color: '#FFFFBF'},
3631
+ {name: 'Zanclean', young: 3.6, old: 5.333, color: '#FFFFB3'},
3632
+ {name: 'Messinian', young: 5.333, old: 7.246, color: '#FFFF73'},
3633
+ {name: 'Tortonian', young: 7.246, old: 11.63, color: '#FFFF66'},
3634
+ {name: 'Serravallian', young: 11.63, old: 13.82, color: '#FFFF59'},
3635
+ {name: 'Langhian', young: 13.82, old: 15.98, color: '#FFFF4D'},
3636
+ {name: 'Burdigalian', young: 15.98, old: 20.45, color: '#FFFF41'},
3637
+ {name: 'Aquitanian', young: 20.45, old: 23.04, color: '#FFFF33'},
3638
+ {name: 'Chattian', young: 23.04, old: 27.3, color: '#FEE6AA'},
3639
+ {name: 'Rupelian', young: 27.3, old: 33.9, color: '#FED99A'},
3640
+ {name: 'Priabonian', young: 33.9, old: 37.71, color: '#FDCDA1'},
3641
+ {name: 'Bartonian', young: 37.71, old: 41.03, color: '#FDC091'},
3642
+ {name: 'Lutetian', young: 41.03, old: 48.07, color: '#FCB482'},
3643
+ {name: 'Ypresian', young: 48.07, old: 56, color: '#FCA773'},
3644
+ {name: 'Thanetian', young: 56, old: 59.24, color: '#FDBF6F'},
3645
+ {name: 'Selandian', young: 59.24, old: 61.66, color: '#FEBF65'},
3646
+ {name: 'Danian', young: 61.66, old: 66, color: '#FDB462'},
3647
+ {name: 'Maastrichtian', young: 66, old: 72.2, color: '#F2FA8C'},
3648
+ {name: 'Campanian', young: 72.2, old: 83.6, color: '#E6F47F'},
3649
+ {name: 'Santonian', young: 83.6, old: 85.7, color: '#D9EF74'},
3650
+ {name: 'Coniacian', young: 85.7, old: 89.8, color: '#CCE968'},
3651
+ {name: 'Turonian', young: 89.8, old: 93.9, color: '#BFE35D'},
3652
+ {name: 'Cenomanian', young: 93.9, old: 100.5, color: '#B3DE53'},
3653
+ {name: 'Albian', young: 100.5, old: 113.2, color: '#CCEA97'},
3654
+ {name: 'Aptian', young: 113.2, old: 121.4, color: '#BFE48A'},
3655
+ {name: 'Barremian', young: 121.4, old: 125.77, color: '#B3DF7F'},
3656
+ {name: 'Hauterivian', young: 125.77, old: 132.6, color: '#A6D975'},
3657
+ {name: 'Valanginian', young: 132.6, old: 137.05, color: '#99D36A'},
3658
+ {name: 'Berriasian', young: 137.05, old: 143.1, color: '#8CCD60'},
3659
+ {name: 'Tithonian', young: 143.1, old: 149.2, color: '#D9F1F7'},
3660
+ {name: 'Kimmeridgian', young: 149.2, old: 154.8, color: '#CCECF4'},
3661
+ {name: 'Oxfordian', young: 154.8, old: 161.5, color: '#BFE7F1'},
3662
+ {name: 'Callovian', young: 161.5, old: 165.3, color: '#BFE7E5'},
3663
+ {name: 'Bathonian', young: 165.3, old: 168.2, color: '#B3E2E3'},
3664
+ {name: 'Bajocian', young: 168.2, old: 170.9, color: '#A6DDE0'},
3665
+ {name: 'Aalenian', young: 170.9, old: 174.7, color: '#9AD9DD'},
3666
+ {name: 'Toarcian', young: 174.7, old: 184.2, color: '#99CEE3'},
3667
+ {name: 'Pliensbachian', young: 184.2, old: 192.9, color: '#80C5DD'},
3668
+ {name: 'Sinemurian', young: 192.9, old: 199.5, color: '#67BCD8'},
3669
+ {name: 'Hettangian', young: 199.5, old: 201.4, color: '#4EB3D3'},
3670
+ {name: 'Rhaetian', young: 201.4, old: 205.7, color: '#E3B9DB'},
3671
+ {name: 'Norian', young: 205.7, old: 227.3, color: '#D6AAD3'},
3672
+ {name: 'Carnian', young: 227.3, old: 237, color: '#C99BCB'},
3673
+ {name: 'Ladinian', young: 237, old: 241.464, color: '#C983BF'},
3674
+ {name: 'Anisian', young: 241.464, old: 246.7, color: '#BC75B7'},
3675
+ {name: 'Olenekian', young: 246.7, old: 249.9, color: '#B051A5'},
3676
+ {name: 'Induan', young: 249.9, old: 251.902, color: '#A4469F'},
3677
+ {name: 'Changhsingian', young: 251.902, old: 254.14, color: '#FCC0B2'},
3678
+ {name: 'Wuchiapingian', young: 254.14, old: 259.51, color: '#FCB4A2'},
3679
+ {name: 'Capitanian', young: 259.51, old: 264.28, color: '#FB9A85'},
3680
+ {name: 'Wordian', young: 264.28, old: 266.9, color: '#FB8D76'},
3681
+ {name: 'Roadian', young: 266.9, old: 274.4, color: '#FB8069'},
3682
+ {name: 'Kungurian', young: 274.4, old: 283.3, color: '#E38776'},
3683
+ {name: 'Artinskian', young: 283.3, old: 290.1, color: '#E37B68'},
3684
+ {name: 'Sakmarian', young: 290.1, old: 293.52, color: '#E36F5C'},
3685
+ {name: 'Asselian', young: 293.52, old: 298.9, color: '#E36350'},
3686
+ {name: 'Gzhelian', young: 298.9, old: 303.7, color: '#CCD4C7'},
3687
+ {name: 'Kasimovian', young: 303.7, old: 307, color: '#BFD0C5'},
3688
+ {name: 'Moscovian', young: 307, old: 315.2, color: '#C7CBB9'},
3689
+ {name: 'Bashkirian', young: 315.2, old: 323.4, color: '#99C2B5'},
3690
+ {name: 'Serpukhovian', young: 323.4, old: 330.3, color: '#BFC26B'},
3691
+ {name: 'Visean', young: 330.3, old: 346.7, color: '#A6B96C'},
3692
+ {name: 'Tournaisian', young: 346.7, old: 358.86, color: '#8CB06C'},
3693
+ {name: 'Famennian', young: 358.86, old: 372.15, color: '#F2EDC5'},
3694
+ {name: 'Frasnian', young: 372.15, old: 382.31, color: '#F2EDAD'},
3695
+ {name: 'Givetian', young: 382.31, old: 387.95, color: '#F1E185'},
3696
+ {name: 'Eifelian', young: 387.95, old: 393.47, color: '#F1D576'},
3697
+ {name: 'Emsian', young: 393.47, old: 410.62, color: '#E5D075'},
3698
+ {name: 'Pragian', young: 410.62, old: 413.02, color: '#E5C468'},
3699
+ {name: 'Lochkovian', young: 413.02, old: 419.62, color: '#E5B75A'},
3700
+ {name: 'Pridoli', young: 419.62, old: 422.7, color: '#E6F5E1'},
3701
+ {name: 'Ludfordian', young: 422.7, old: 425, color: '#D9F0DF'},
3702
+ {name: 'Gorstian', young: 425, old: 426.7, color: '#CCECDD'},
3703
+ {name: 'Homerian', young: 426.7, old: 430.6, color: '#CCEBD1'},
3704
+ {name: 'Sheinwoodian', young: 430.6, old: 432.9, color: '#BFE6C3'},
3705
+ {name: 'Telychian', young: 432.9, old: 438.6, color: '#BFE6CF'},
3706
+ {name: 'Aeronian', young: 438.6, old: 440.5, color: '#B3E1C2'},
3707
+ {name: 'Rhuddanian', young: 440.5, old: 443.1, color: '#A6DCB5'},
3708
+ {name: 'Hirnantian', young: 443.1, old: 445.2, color: '#A6DBAB'},
3709
+ {name: 'Katian', young: 445.2, old: 452.8, color: '#99D69F'},
3710
+ {name: 'Sandbian', young: 452.8, old: 458.2, color: '#8CD094'},
3711
+ {name: 'Darriwilian', young: 458.2, old: 469.4, color: '#74C69C'},
3712
+ {name: 'Dapingian', young: 469.4, old: 471.3, color: '#66C092'},
3713
+ {name: 'Floian', young: 471.3, old: 477.1, color: '#41B087'},
3714
+ {name: 'Tremadocian', young: 477.1, old: 486.85, color: '#33A97E'},
3715
+ {name: 'Stage 10', young: 486.85, old: 491, color: '#E6F5C9'},
3716
+ {name: 'Jiangshanian', young: 491, old: 494.2, color: '#D9F0BB'},
3717
+ {name: 'Paibian', young: 494.2, old: 497, color: '#CCEBAE'},
3718
+ {name: 'Guzhangian', young: 497, old: 500.5, color: '#CCDFAA'},
3719
+ {name: 'Drumian', young: 500.5, old: 504.5, color: '#BFD99D'},
3720
+ {name: 'Wuliuan', young: 504.5, old: 506.5, color: '#B3D492'},
3721
+ {name: 'Stage 4', young: 506.5, old: 514.5, color: '#B3CA8E'},
3722
+ {name: 'Stage 3', young: 514.5, old: 521, color: '#A6C583'},
3723
+ {name: 'Stage 2', young: 521, old: 529, color: '#A6BA80'},
3724
+ {name: 'Fortunian', young: 529, old: 538.8, color: '#99B575'}
3563
3725
  ]
3564
3726
  };
3565
3727
 
@@ -3573,10 +3735,26 @@
3573
3735
  return ivs.length > 0 ? ivs[ivs.length - 1].old : 0;
3574
3736
  };
3575
3737
 
3576
- // The [coarse, fine] rank pair for a tree reaching back oldMa: always the
3577
- // finest pair that still fully covers the range, so a deep tree never
3578
- // shows blank band segments.
3579
- forester.geoBandRanks = function (oldMa) {
3738
+ // The [coarse, fine] rank pair for the span [youngMa, oldMa] a tree
3739
+ // actually occupies -- the youngest tip to the root, not 0 to the root,
3740
+ // so a fossil-only clade bands on its own window rather than on the
3741
+ // whole stretch back from the present.
3742
+ //
3743
+ // It adapts FINER as well as coarser. A narrow Phanerozoic window --
3744
+ // one or two Series -- bands Series over STAGE (the ICS ages), which is
3745
+ // what a 66-100 Ma dinosaur tree needs to say anything at all: over
3746
+ // Period/Epoch it would read "Cretaceous / Late Cretaceous" and nothing
3747
+ // more. Wider than two Series the stages would be slivers, so the
3748
+ // existing ladder takes over: the finest pair that still fully covers the
3749
+ // range, so a deep tree never shows blank band segments. Shared rule with
3750
+ // the desktop's GeologicTimeScale.bandRanks -- change both or neither.
3751
+ forester.geoBandRanks = function (youngMa, oldMa) {
3752
+ if (oldMa <= forester.geoCoverage('age')) { // stages are Phanerozoic-only
3753
+ let series = forester.geoOverlapping('epoch', youngMa, oldMa).length;
3754
+ if (series > 0 && series <= 2) {
3755
+ return ['epoch', 'age'];
3756
+ }
3757
+ }
3580
3758
  if (oldMa <= forester.geoCoverage('epoch')) {
3581
3759
  return ['period', 'epoch'];
3582
3760
  }
@@ -4075,6 +4253,114 @@
4075
4253
  // extractors: {name, taxonomy, sequence}, each a function(externalNode)
4076
4254
  // returning the text that field would contribute to the node's label, or
4077
4255
  // null / '' when it contributes nothing.
4256
+ /**
4257
+ * Reads bare numeric internal node labels as confidence values -- the
4258
+ * dialect nearly every tree writer emits (RAxML, IQ-TREE, FastTree, MEGA,
4259
+ * BV-BRC) writes branch support as `)100:0.05`, which the New Hampshire
4260
+ * grammar makes an internal node NAME.
4261
+ *
4262
+ * The rule is shared with the desktop Java Archaeopteryx; change it only
4263
+ * together with that implementation.
4264
+ *
4265
+ * mode 'auto' (the default) is ALL-OR-NOTHING: every candidate label must
4266
+ * look like support, so a tree of real clade names is never touched.
4267
+ * mode 'confidence' is PER-NODE and unbounded: every numeric label is
4268
+ * promoted whatever its value, which is how a user forces a mixed tree
4269
+ * (some clade names, some support) or an out-of-range scale. mode 'label'
4270
+ * does nothing -- the labels stay names.
4271
+ *
4272
+ * The ROOT is never promoted in any mode: a confidence belongs to the
4273
+ * branch ABOVE a node and the root has none, so a numeric label there is
4274
+ * almost always the tree's name -- `)99;` would otherwise lose it while
4275
+ * `)MyTree;` kept it.
4276
+ *
4277
+ * Promotion MOVES: the label is cleared as the confidence is added, or the
4278
+ * value would be drawn twice.
4279
+ *
4280
+ * Pure apart from the mutation it is asked to make, and returns the number
4281
+ * of labels promoted so the CALLER can react (the viewer switches its
4282
+ * confidence display on, so support values appear rather than names
4283
+ * silently vanishing). Deliberately does not touch display state itself.
4284
+ *
4285
+ * @param phy - the phylogeny
4286
+ * @param mode - 'auto' | 'confidence' | 'label'
4287
+ * @returns {number} how many internal labels became confidences
4288
+ */
4289
+ forester.promoteInternalLabelsToConfidence = function (phy, mode) {
4290
+ if (mode === 'label') {
4291
+ return 0;
4292
+ }
4293
+ if (mode !== 'confidence') {
4294
+ mode = 'auto';
4295
+ }
4296
+ let root = forester.getTreeRoot(phy);
4297
+ let candidates = [];
4298
+ let anyConfidence = false;
4299
+ forester.preOrderTraversalAll(root, function (n) {
4300
+ if (n === root || !(n.children || n._children)) {
4301
+ return;
4302
+ }
4303
+ if (n.confidences && n.confidences.length > 0) {
4304
+ anyConfidence = true;
4305
+ }
4306
+ if (typeof n.name === 'string' && n.name.length > 0) {
4307
+ candidates.push(n);
4308
+ }
4309
+ });
4310
+ if (candidates.length < 1) {
4311
+ return 0;
4312
+ }
4313
+ let numeric = candidates.map(function (n) {
4314
+ let v = Number(n.name);
4315
+ return (n.name.trim().length > 0 && isFinite(v)) ? v : null;
4316
+ });
4317
+ let chosen;
4318
+ if (mode === 'confidence') {
4319
+ // Per node: promote what parses, leave the rest alone.
4320
+ chosen = candidates.filter(function (n, i) {
4321
+ return numeric[i] !== null;
4322
+ });
4323
+ } else {
4324
+ // A file mixing `)[100]:` and `)95:` leaves the bracketed nodes
4325
+ // with no label at all, so one bare numeric label beside real
4326
+ // confidences is corroborated rather than lonely. Without this a
4327
+ // mixed-dialect tree ships some confidences and one stray name.
4328
+ let need = anyConfidence ? 1 : 2;
4329
+ if (candidates.length < need) {
4330
+ return 0;
4331
+ }
4332
+ if (numeric.some(function (v) {
4333
+ // All-or-nothing: one real clade name and the tree is names.
4334
+ // The range covers every scale in use -- 0-1 posteriors,
4335
+ // 0-100 bootstrap, 0-1000 MrBayes -- and excludes identifiers
4336
+ // such as NCBI taxids that would read as "support 9606".
4337
+ return v === null || v < 0 || v > 1000;
4338
+ })) {
4339
+ return 0;
4340
+ }
4341
+ // Clade numbering is overwhelmingly 1..N, each exactly once. A
4342
+ // real support tree containing every integer 1..N once and nothing
4343
+ // else is essentially impossible at n >= 3, and without this guard
4344
+ // a clade-numbered tree silently becomes "1%, 2%, 3% support".
4345
+ let distinct = new Set(numeric);
4346
+ let min = Math.min.apply(null, numeric);
4347
+ let max = Math.max.apply(null, numeric);
4348
+ if (distinct.size === numeric.length && min === 1 && max === numeric.length) {
4349
+ return 0;
4350
+ }
4351
+ chosen = candidates;
4352
+ }
4353
+ chosen.forEach(function (n) {
4354
+ let v = Number(n.name);
4355
+ // Type deliberately left unset: the value cannot distinguish
4356
+ // bootstrap from posterior from aLRT from TBE, and a wrong type
4357
+ // printed beside a number in a published figure is worse than none.
4358
+ pushConfidence(n, v, '');
4359
+ n.name = '';
4360
+ });
4361
+ return chosen.length;
4362
+ };
4363
+
4078
4364
  forester.suggestLabelFields = function (root, extractors) {
4079
4365
  const FIELDS = ['name', 'taxonomy', 'sequence'];
4080
4366
  const CONTAINMENT_MIN = 0.9;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archaeopteryx",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Archaeopteryx.js is a software tool for the visualization and analysis of highly annotated phylogenetic trees.",
5
5
  "main": "archaeopteryx.js",
6
6
  "types": "archaeopteryx.d.ts",