@ca-plant-list/ca-plant-list 0.4.22 → 0.4.24

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 (41) hide show
  1. package/lib/basepagerenderer.js +10 -4
  2. package/lib/ebook/images.js +3 -3
  3. package/lib/ebook/pages/{page_list_families.js → pageListFamilies.js} +1 -1
  4. package/lib/ebook/pages/{page_list_flowers.js → pageListFlowers.js} +2 -2
  5. package/lib/ebook/pages/page_list_species.js +1 -1
  6. package/lib/ebook/pages/taxonpage.js +1 -1
  7. package/lib/ebook/pages/tocpage.js +2 -2
  8. package/lib/ebook/plantbook.js +3 -3
  9. package/lib/externalsites.js +86 -37
  10. package/lib/flowercolor.js +2 -2
  11. package/lib/genera.js +4 -4
  12. package/lib/html.js +7 -8
  13. package/lib/htmltaxon.js +106 -27
  14. package/lib/index.d.ts +51 -16
  15. package/lib/index.js +3 -3
  16. package/lib/pagerenderer.js +7 -9
  17. package/lib/taxonomy/families.js +104 -0
  18. package/lib/{taxa.js → taxonomy/taxa.js} +18 -18
  19. package/lib/{taxon.js → taxonomy/taxon.js} +9 -108
  20. package/lib/taxonomy/taxonomy.js +17 -0
  21. package/lib/tools/calflora.js +2 -2
  22. package/lib/tools/calscape.js +3 -3
  23. package/lib/tools/cch2.js +3 -3
  24. package/lib/tools/fna.js +2 -2
  25. package/lib/tools/inat.js +3 -3
  26. package/lib/tools/jepsoneflora.js +1 -1
  27. package/lib/tools/rpi.js +3 -3
  28. package/lib/tools/supplementaltext.js +1 -1
  29. package/lib/types.js +10 -0
  30. package/lib/utils/inat-tools.js +2 -2
  31. package/lib/web/pageFamily.js +146 -0
  32. package/lib/web/pagetaxon.js +20 -56
  33. package/package.json +1 -1
  34. package/scripts/build-ebook.js +4 -4
  35. package/scripts/build-site.js +3 -3
  36. package/scripts/cpl-photos.js +1 -1
  37. package/scripts/cpl-tools.js +1 -1
  38. package/scripts/inatobsphotos.js +2 -2
  39. package/scripts/inattaxonphotos.js +2 -2
  40. package/lib/families.js +0 -243
  41. package/lib/jepson.js +0 -17
@@ -2,12 +2,13 @@ import { Config } from "./config.js";
2
2
  import { Files } from "./files.js";
3
3
  import { Jekyll } from "./jekyll.js";
4
4
  import { GlossaryPages } from "./web/glossarypages.js";
5
+ import { PageFamilyList } from "./web/pageFamily.js";
5
6
 
6
7
  class BasePageRenderer {
7
8
  /**
8
9
  * @param {string} outputDir
9
- * @param {import("./taxa.js").Taxa} taxa
10
- * @param {import("./htmltaxon.js").TaxaColDef[]} [familyCols]
10
+ * @param {import("./types.js").Taxa} taxa
11
+ * @param {import("./types.js").TaxaColDef[]} [familyCols]
11
12
  */
12
13
  static renderBasePages(outputDir, taxa, familyCols) {
13
14
  const siteGenerator = new Jekyll(outputDir);
@@ -21,7 +22,12 @@ class BasePageRenderer {
21
22
  // Copy illustrations.
22
23
  siteGenerator.copyIllustrations(taxa.getFlowerColors());
23
24
 
24
- taxa.getFamilies().renderPages(outputDir, familyCols);
25
+ const fl = new PageFamilyList(
26
+ outputDir,
27
+ taxa.getFamilies().getFamilies(),
28
+ );
29
+ fl.render(familyCols);
30
+ fl.renderPages(outputDir, familyCols);
25
31
 
26
32
  new GlossaryPages(siteGenerator).renderPages();
27
33
 
@@ -30,7 +36,7 @@ class BasePageRenderer {
30
36
 
31
37
  /**
32
38
  * @param {string} outputDir
33
- * @param {import("./taxa.js").Taxa} taxa
39
+ * @param {import("./types.js").Taxa} taxa
34
40
  */
35
41
  static renderTools(outputDir, taxa) {
36
42
  const names = [];
@@ -14,7 +14,7 @@ class Images {
14
14
  /**
15
15
  * @param {import("../sitegenerator.js").SiteGenerator} siteGenerator
16
16
  * @param {string} contentDir
17
- * @param {import("../taxa.js").Taxa} taxa
17
+ * @param {import("../types.js").Taxa} taxa
18
18
  */
19
19
  constructor(siteGenerator, contentDir, taxa) {
20
20
  this.#siteGenerator = siteGenerator;
@@ -23,7 +23,7 @@ class Images {
23
23
  }
24
24
 
25
25
  /**
26
- * @param {import("../taxon.js").Taxon[]} taxa
26
+ * @param {import("../taxonomy/taxon.js").Taxon[]} taxa
27
27
  */
28
28
  async createImages(taxa) {
29
29
  const meter = new ProgressMeter("processing photos", taxa.length);
@@ -118,7 +118,7 @@ class Images {
118
118
  }
119
119
 
120
120
  /**
121
- * @param {import("../taxon.js").Taxon} taxon
121
+ * @param {import("../taxonomy/taxon.js").Taxon} taxon
122
122
  * @returns {import("../photo.js").Photo[]}
123
123
  */
124
124
  static getTaxonPhotos(taxon) {
@@ -6,7 +6,7 @@ class PageListFamilies extends EBookPage {
6
6
 
7
7
  /**
8
8
  * @param {string} outputDir
9
- * @param {import("../../families.js").Families} families
9
+ * @param {import("../../types.js").Families} families
10
10
  */
11
11
  constructor(outputDir, families) {
12
12
  super(outputDir + "/list_families.html", "All Families");
@@ -8,7 +8,7 @@ const FN_FLOWER_TIME_INDEX = "fm.html";
8
8
  class PageListFlowers {
9
9
  /**
10
10
  * @param {string} contentDir
11
- * @param {import("../../taxa.js").Taxa} taxa
11
+ * @param {import("../../types.js").Taxa} taxa
12
12
  */
13
13
  static createPages(contentDir, taxa) {
14
14
  new PageListFlowerTimeIndex(contentDir).create();
@@ -83,7 +83,7 @@ class PageListFlowerTime extends EBookPage {
83
83
 
84
84
  /**
85
85
  * @param {string} outputDir
86
- * @param {import("../../taxa.js").Taxa} taxa
86
+ * @param {import("../../types.js").Taxa} taxa
87
87
  * @param {number} month
88
88
  */
89
89
  constructor(outputDir, taxa, month) {
@@ -8,7 +8,7 @@ class PageListSpecies extends EBookPage {
8
8
  /**
9
9
  *
10
10
  * @param {string} outputDir
11
- * @param {import("../../taxon.js").Taxon[]} taxa
11
+ * @param {import("../../taxonomy/taxon.js").Taxon[]} taxa
12
12
  * @param {string} filename
13
13
  * @param {string} title
14
14
  */
@@ -13,7 +13,7 @@ class TaxonPage extends EBookPage {
13
13
 
14
14
  /**
15
15
  * @param {string} outputDir
16
- * @param {import("../../taxon.js").Taxon} taxon
16
+ * @param {import("../../taxonomy/taxon.js").Taxon} taxon
17
17
  * @param {Images} images
18
18
  */
19
19
  constructor(outputDir, taxon, images) {
@@ -1,13 +1,13 @@
1
1
  import { EBookPage } from "../ebookpage.js";
2
2
  import { XHTML } from "../xhtml.js";
3
- import { PageListFlowers } from "./page_list_flowers.js";
3
+ import { PageListFlowers } from "./pageListFlowers.js";
4
4
 
5
5
  class TOCPage extends EBookPage {
6
6
  #taxa;
7
7
 
8
8
  /**
9
9
  * @param {string} outputDir
10
- * @param {import("../../taxa.js").Taxa} taxa
10
+ * @param {import("../../taxonomy/taxa.js").Taxa} taxa
11
11
  */
12
12
  constructor(outputDir, taxa) {
13
13
  super(outputDir + "/toc.xhtml", "Table of Contents");
@@ -3,9 +3,9 @@ import { EBook } from "./ebook.js";
3
3
  import { EBookSiteGenerator } from "./ebooksitegenerator.js";
4
4
  import { GlossaryPages } from "./glossarypages.js";
5
5
  import { Images } from "./images.js";
6
- import { PageListFamilies } from "./pages/page_list_families.js";
6
+ import { PageListFamilies } from "./pages/pageListFamilies.js";
7
7
  import { PageListFlowerColor } from "./pages/page_list_flower_color.js";
8
- import { PageListFlowers } from "./pages/page_list_flowers.js";
8
+ import { PageListFlowers } from "./pages/pageListFlowers.js";
9
9
  import { PageListSpecies } from "./pages/page_list_species.js";
10
10
  import { TaxonPage } from "./pages/taxonpage.js";
11
11
  import { TOCPage } from "./pages/tocpage.js";
@@ -18,7 +18,7 @@ class PlantBook extends EBook {
18
18
  /**
19
19
  * @param {string} outputDir
20
20
  * @param {import("../config.js").Config} config
21
- * @param {import("../taxa.js").Taxa} taxa
21
+ * @param {import("../types.js").Taxa} taxa
22
22
  */
23
23
  constructor(outputDir, config, taxa) {
24
24
  super(
@@ -1,13 +1,37 @@
1
- /** @typedef {{
2
- coords?: [number, number];
3
- project_id?: string;
4
- subview?: "grid" | "list" | "map";
5
- taxon_id?: string;
6
- }} InatObsOptions */
7
-
8
1
  export class ExternalSites {
9
2
  /**
10
- * @param {import("./taxon.js").Taxon} taxon
3
+ * @param {import("./types.js").Taxon} taxon
4
+ * @param {import("./types.js").Config} config
5
+ * @returns {URL|undefined}
6
+ */
7
+ static getCalfloraObsLink(taxon, config) {
8
+ const name = taxon.getCalfloraName();
9
+ if (!name) {
10
+ return;
11
+ }
12
+ const url = new URL(
13
+ "https://www.calflora.org/entry/observ.html?track=m#srch=t&grezc=5&cols=b&lpcli=t&cc=" +
14
+ config.getCountyCodes().join("!") +
15
+ "&incobs=f&taxon=" +
16
+ name,
17
+ );
18
+ return url;
19
+ }
20
+
21
+ /**
22
+ * @param {import("./types.js").Taxon} taxon
23
+ * @returns {URL|undefined}
24
+ */
25
+ static getCalfloraRefLink(taxon) {
26
+ const calfloraID = taxon.getCalfloraID();
27
+ if (!calfloraID) {
28
+ return;
29
+ }
30
+ return new URL("https://www.calflora.org/app/taxon?crn=" + calfloraID);
31
+ }
32
+
33
+ /**
34
+ * @param {import("./types.js").Taxon} taxon
11
35
  * @returns {URL|undefined}
12
36
  */
13
37
  static getCalscapeLink(taxon) {
@@ -21,7 +45,7 @@ export class ExternalSites {
21
45
  }
22
46
 
23
47
  /**
24
- * @param {import("./taxon.js").Taxon} taxon
48
+ * @param {import("./types.js").Taxon} taxon
25
49
  * @param {import("./config.js").Config} config
26
50
  * @returns {URL|undefined}
27
51
  */
@@ -35,7 +59,7 @@ export class ExternalSites {
35
59
  }
36
60
 
37
61
  /**
38
- * @param {import("./taxon.js").Taxon} taxon
62
+ * @param {import("./types.js").Taxon} taxon
39
63
  * @returns {URL|undefined}
40
64
  */
41
65
  static getCCH2RefLink(taxon) {
@@ -49,7 +73,7 @@ export class ExternalSites {
49
73
  }
50
74
 
51
75
  /**
52
- * @param {import("./taxon.js").Taxon} taxon
76
+ * @param {import("./types.js").Taxon} taxon
53
77
  * @returns {URL|undefined}
54
78
  */
55
79
  static getFNARefLink(taxon) {
@@ -64,40 +88,65 @@ export class ExternalSites {
64
88
  }
65
89
 
66
90
  /**
67
- * @param {InatObsOptions} options
91
+ * @param {import("./types.js").Taxon} taxon
92
+ * @param {import("./config.js").Config} config
93
+ * @returns {URL|undefined}
68
94
  */
69
- static getInatObsLink(options) {
95
+ static getInatObsLink(taxon, config) {
96
+ const iNatID = taxon.getINatID();
97
+ if (!iNatID) {
98
+ return;
99
+ }
100
+
70
101
  const url = new URL(
71
102
  "https://www.inaturalist.org/observations?subview=map",
72
103
  );
104
+ url.searchParams.set("taxon_id", iNatID);
105
+ for (const p of ["place_id", "project_id"]) {
106
+ const v = config.getConfigValue("inat", p);
107
+ if (v) {
108
+ url.searchParams.set(p, v);
109
+ }
110
+ }
111
+
112
+ return url;
113
+ }
73
114
 
74
- if (options.coords) {
75
- const delta = 0.1;
76
- const params = url.searchParams;
77
- const lat = options.coords[1];
78
- const lng = options.coords[0];
79
- params.set("nelat", (lat + delta).toString());
80
- params.set("swlat", (lat - delta).toString());
81
- params.set("nelng", (lng + delta).toString());
82
- params.set("swlng", (lng - delta).toString());
115
+ /**
116
+ * @param {import("./types.js").Taxon} taxon
117
+ * @returns {URL|undefined}
118
+ */
119
+ static getINatRefLink(taxon) {
120
+ const iNatID = taxon.getINatID();
121
+ if (!iNatID) {
122
+ return;
83
123
  }
124
+ return new URL("https://www.inaturalist.org/taxa/" + iNatID);
125
+ }
84
126
 
85
- for (const [k, v] of Object.entries(options)) {
86
- switch (k) {
87
- case "created_d1":
88
- case "list_id":
89
- case "place_id":
90
- case "project_id":
91
- case "subview":
92
- case "taxon_id":
93
- case "taxon_name":
94
- if (typeof v === "string") {
95
- url.searchParams.set(k, v);
96
- }
97
- break;
98
- }
127
+ /**
128
+ * @param {import("./types.js").Taxonomy} taxon
129
+ * @returns {URL|undefined}
130
+ */
131
+ static getJepsonRefLink(taxon) {
132
+ const id = taxon.getJepsonID();
133
+ if (!id) {
134
+ return;
99
135
  }
136
+ return new URL(
137
+ "https://ucjeps.berkeley.edu/eflora/eflora_display.php?tid=" + id,
138
+ );
139
+ }
100
140
 
101
- return url.toString();
141
+ /**
142
+ * @param {import("./types.js").Taxon} taxon
143
+ * @returns {URL|undefined}
144
+ */
145
+ static getRPIRefLink(taxon) {
146
+ const rpiID = taxon.getRPIID();
147
+ if (!rpiID) {
148
+ return;
149
+ }
150
+ return new URL("https://rareplants.cnps.org/Plants/Details/" + rpiID);
102
151
  }
103
152
  }
@@ -3,7 +3,7 @@ import { TextUtils } from "./textutils.js";
3
3
  class FlowerColor {
4
4
  #colorName;
5
5
  #colorCode;
6
- /** @type {import("./taxon.js").Taxon[]} */
6
+ /** @type {import("./types.js").Taxon[]} */
7
7
  #taxa = [];
8
8
 
9
9
  /**
@@ -16,7 +16,7 @@ class FlowerColor {
16
16
  }
17
17
 
18
18
  /**
19
- * @param {import("./taxon.js").Taxon} taxon
19
+ * @param {import("./types.js").Taxon} taxon
20
20
  */
21
21
  addTaxon(taxon) {
22
22
  this.#taxa.push(taxon);
package/lib/genera.js CHANGED
@@ -6,7 +6,7 @@ class Genera {
6
6
  #genera;
7
7
 
8
8
  /**
9
- * @param {import("./families.js").Families} families
9
+ * @param {import("./types.js").Families} families
10
10
  */
11
11
  constructor(families) {
12
12
  const dataDir = Config.getPackageDir() + "/data";
@@ -15,7 +15,7 @@ class Genera {
15
15
  }
16
16
 
17
17
  /**
18
- * @param {import("./taxon.js").Taxon} taxon
18
+ * @param {import("./types.js").Taxon} taxon
19
19
  */
20
20
  addTaxon(taxon) {
21
21
  const genusName = taxon.getGenusName();
@@ -46,7 +46,7 @@ class Genus {
46
46
  #data;
47
47
 
48
48
  /**
49
- * @param {{family:string,familyObj:import("./families.js").Family,taxa:import("./taxon.js").Taxon[]}} data
49
+ * @param {{family:string,familyObj:import("./types.js").Family,taxa:import("./types.js").Taxon[]}} data
50
50
  */
51
51
  constructor(data) {
52
52
  this.#data = data;
@@ -57,7 +57,7 @@ class Genus {
57
57
  }
58
58
 
59
59
  /**
60
- * @returns {import("./taxon.js").Taxon[]}
60
+ * @returns {import("./types.js").Taxon[]}
61
61
  */
62
62
  getTaxa() {
63
63
  return this.#data.taxa.sort((a, b) =>
package/lib/html.js CHANGED
@@ -48,7 +48,7 @@ export class HTML {
48
48
 
49
49
  /**
50
50
  * Generate HTML for an <a> element.
51
- * @param {string|undefined} href
51
+ * @param {URL|string|undefined} href
52
52
  * @param {string} linkText
53
53
  * @param {string|Object<string,string>} [attributes]
54
54
  * @param {boolean} [openInNewWindow] true if the link should open in a new window.
@@ -57,7 +57,7 @@ export class HTML {
57
57
  static getLink(href, linkText, attributes = {}, openInNewWindow) {
58
58
  let html = "<a";
59
59
  if (href !== undefined) {
60
- html += this.renderAttribute("href", href);
60
+ html += this.renderAttribute("href", href.toString());
61
61
  }
62
62
  html += this.renderAttributes(attributes);
63
63
  if (openInNewWindow) {
@@ -70,8 +70,7 @@ export class HTML {
70
70
  * Get a Bootstrap formatted tooltip element.
71
71
  * @param {string} text - The text or HTML that should trigger the tooltip on hover.
72
72
  * @param {string} tooltip - The tooltip text or HTML.
73
- * @param {Object} options
74
- * @param {boolean} [options.icon] [true] display an icon after the text
73
+ * @param {{icon?:boolean}} options
75
74
  * @returns {string} A &lt;span> element to be used as a Bootstrap tooltip.
76
75
  */
77
76
  static getToolTip(text, tooltip, options = {}) {
@@ -106,19 +105,19 @@ export class HTML {
106
105
 
107
106
  /**
108
107
  * @param {string} elName
109
- * @param {string} text
108
+ * @param {string|number} text
110
109
  * @param {Object<string,string>} [attributes]
111
110
  */
112
111
  static textElement(elName, text, attributes = {}) {
113
- return HTML.#getElement(elName, text, attributes, true);
112
+ return HTML.#getElement(elName, text.toString(), attributes, true);
114
113
  }
115
114
 
116
115
  /**
117
116
  * @param {string} elName
118
- * @param {string} text
117
+ * @param {string|number} text
119
118
  * @param {string|Object<string,string>|undefined} [attributes]
120
119
  */
121
120
  static wrap(elName, text, attributes) {
122
- return HTML.#getElement(elName, text, attributes, false);
121
+ return HTML.#getElement(elName, text.toString(), attributes, false);
123
122
  }
124
123
  }
package/lib/htmltaxon.js CHANGED
@@ -7,15 +7,7 @@ import { RarePlants } from "./rareplants.js";
7
7
  import { TextUtils } from "./textutils.js";
8
8
 
9
9
  /**
10
- * @typedef {{
11
- class?: string;
12
- data: (taxon: import("./taxon.js").Taxon) => string;
13
- title: string;
14
- }} TaxaColDef
15
- */
16
-
17
- /**
18
- * @type {Record<string,TaxaColDef>}
10
+ * @type {Record<string,import("./types.js").TaxaColDef>}
19
11
  */
20
12
  const TAXA_LIST_COLS = {
21
13
  CESA: {
@@ -31,7 +23,7 @@ const TAXA_LIST_COLS = {
31
23
  data: (t) =>
32
24
  HTML.getToolTip(
33
25
  HTML.textElement("span", t.getRPIRankAndThreat()),
34
- t.getRPIRankAndThreatTooltip(),
26
+ HTMLTaxon.getRPIRankAndThreatTooltip(t),
35
27
  ),
36
28
  },
37
29
  FESA: {
@@ -40,33 +32,66 @@ const TAXA_LIST_COLS = {
40
32
  },
41
33
  SPECIES: {
42
34
  title: "Species",
43
- data: (t) => t.getHTMLLink(true, true),
35
+ data: (t) => HTMLTaxon.getHTMLLink(t, true, true),
44
36
  },
45
37
  SPECIES_BARE: {
46
38
  title: "Species",
47
- data: (t) => t.getHTMLLink(true, false),
39
+ data: (t) => HTMLTaxon.getHTMLLink(t, true, false),
48
40
  },
49
41
  };
50
42
 
51
- /** @type {TaxaColDef[]} */
43
+ /** @type {import("./types.js").TaxaColDef[]} */
52
44
  const DEFAULT_TAXA_COLUMNS = [
53
45
  TAXA_LIST_COLS.SPECIES,
54
46
  TAXA_LIST_COLS.COMMON_NAME,
55
47
  ];
56
48
 
57
- /** @type {Object<string,{label:string,href:function(import("./taxon.js").Taxon):URL|undefined}>} */
49
+ /** @type {Object<string,{label:string,href:function(import("./types.js").Taxon,import("./types.js").Config):URL|undefined}>} */
50
+ const OBSLINKS = {
51
+ calflora: {
52
+ label: "Calflora",
53
+ href: (taxon, config) =>
54
+ ExternalSites.getCalfloraObsLink(taxon, config),
55
+ },
56
+ cch: {
57
+ label: "CCH2",
58
+ href: (taxon, config) => ExternalSites.getCCH2ObsLink(taxon, config),
59
+ },
60
+ inat: {
61
+ label: "iNaturalist",
62
+ href: (taxon, config) => ExternalSites.getInatObsLink(taxon, config),
63
+ },
64
+ };
65
+
66
+ /** @type {Object<string,{label:string,href:function(import("./types.js").Taxon):URL|undefined}>} */
58
67
  const REFLINKS = {
68
+ calflora: {
69
+ label: "Calflora",
70
+ href: (taxon) => ExternalSites.getCalfloraRefLink(taxon),
71
+ },
59
72
  calscape: {
60
73
  label: "Calscape",
61
74
  href: (taxon) => ExternalSites.getCalscapeLink(taxon),
62
75
  },
76
+ cch: {
77
+ label: "CCH2",
78
+ href: (taxon) => ExternalSites.getCCH2RefLink(taxon),
79
+ },
63
80
  fna: {
64
81
  label: "Flora of North America",
65
82
  href: (taxon) => ExternalSites.getFNARefLink(taxon),
66
83
  },
67
- cch: {
68
- label: "CCH2",
69
- href: (taxon) => ExternalSites.getCCH2RefLink(taxon),
84
+ inat: {
85
+ label: "iNaturalist",
86
+ href: (taxon) => ExternalSites.getINatRefLink(taxon),
87
+ },
88
+ jepson: {
89
+ label: "Jepson eFlora",
90
+ href: (taxon) => ExternalSites.getJepsonRefLink(taxon),
91
+ },
92
+ rpi: {
93
+ label: "CNPS Rare Plant Inventory",
94
+ href: (taxon) => ExternalSites.getRPIRefLink(taxon),
70
95
  },
71
96
  };
72
97
 
@@ -75,23 +100,37 @@ class HTMLTaxon {
75
100
  * @param {string[]} links
76
101
  * @param {URL|string|undefined} href
77
102
  * @param {string} label
103
+ * @param {string} [suffix=""]
78
104
  */
79
- static addLink(links, href, label) {
105
+ static addLink(links, href, label, suffix = "") {
80
106
  if (href === undefined) {
81
107
  return;
82
108
  }
83
109
  const link = HTML.getLink(href.toString(), label, {}, true);
84
- links.push(link);
110
+ links.push(link + suffix);
85
111
  }
86
112
 
87
113
  /**
88
114
  * @param {string[]} links
89
- * @param {import("./taxon.js").Taxon} taxon
115
+ * @param {import("./types.js").Taxon} taxon
116
+ * @param {import("./types.js").Config} config
90
117
  * @param {import("./index.js").RefSourceCode} sourceCode
118
+ * @param {string} [label]
91
119
  */
92
- static addRefLink(links, taxon, sourceCode) {
120
+ static addObsLink(links, taxon, config, sourceCode, label) {
121
+ const source = OBSLINKS[sourceCode];
122
+ this.addLink(links, source.href(taxon, config), label ?? source.label);
123
+ }
124
+
125
+ /**
126
+ * @param {string[]} links
127
+ * @param {import("./types.js").Taxon} taxon
128
+ * @param {import("./index.js").RefSourceCode} sourceCode
129
+ * @param {string} [suffix=""]
130
+ */
131
+ static addRefLink(links, taxon, sourceCode, suffix = "") {
93
132
  const source = REFLINKS[sourceCode];
94
- this.addLink(links, source.href(taxon), source.label);
133
+ this.addLink(links, source.href(taxon), source.label, suffix);
95
134
  }
96
135
 
97
136
  /**
@@ -120,7 +159,7 @@ class HTMLTaxon {
120
159
  }
121
160
 
122
161
  /**
123
- * @param {import("./taxon.js").Taxon} taxon
162
+ * @param {import("./types.js").Taxon} taxon
124
163
  * @param {string} classNames
125
164
  * @param {boolean} [includeColorLink=true]
126
165
  */
@@ -161,7 +200,7 @@ class HTMLTaxon {
161
200
  }
162
201
 
163
202
  /**
164
- * @param {import("./taxon.js").Taxon} taxon
203
+ * @param {import("./types.js").Taxon} taxon
165
204
  * @returns {string}
166
205
  */
167
206
  static getFooterHTML(taxon) {
@@ -174,7 +213,38 @@ class HTMLTaxon {
174
213
  }
175
214
 
176
215
  /**
177
- * @param {import("./taxon.js").Taxon} taxon
216
+ * @param {import("./types.js").Taxon} taxon
217
+ * @param {boolean|string|undefined} href
218
+ * @param {boolean} includeRPI
219
+ */
220
+ static getHTMLLink(taxon, href = true, includeRPI = true) {
221
+ href = href ? "./" + taxon.getFileName() : undefined;
222
+ let className = taxon.isNative() ? "native" : "non-native";
223
+ let isRare = false;
224
+ if (includeRPI && taxon.isRare()) {
225
+ isRare = true;
226
+ className += " rare";
227
+ }
228
+ const attributes = { class: className };
229
+ const link = HTML.wrap(
230
+ "span",
231
+ HTML.getLink(href, taxon.getName()),
232
+ attributes,
233
+ );
234
+ if (isRare) {
235
+ return HTML.getToolTip(
236
+ link,
237
+ this.getRPIRankAndThreatTooltip(taxon),
238
+ {
239
+ icon: false,
240
+ },
241
+ );
242
+ }
243
+ return link;
244
+ }
245
+
246
+ /**
247
+ * @param {import("./types.js").Taxon} taxon
178
248
  */
179
249
  static getLink(taxon) {
180
250
  return (
@@ -214,8 +284,17 @@ class HTMLTaxon {
214
284
  }
215
285
 
216
286
  /**
217
- * @param {import("./taxon.js").Taxon[]} taxa
218
- * @param {TaxaColDef[]} [columns]
287
+ * @param {import("./types.js").Taxon} taxon
288
+ */
289
+ static getRPIRankAndThreatTooltip(taxon) {
290
+ return RarePlants.getRPIRankAndThreatDescriptions(
291
+ taxon.getRPIRankAndThreat(),
292
+ ).join("<br>");
293
+ }
294
+
295
+ /**
296
+ * @param {import("./types.js").Taxon[]} taxa
297
+ * @param {import("./types.js").TaxaColDef[]} [columns]
219
298
  */
220
299
  static getTaxaTable(taxa, columns = DEFAULT_TAXA_COLUMNS) {
221
300
  let html = "<table><thead>";