@ca-plant-list/ca-plant-list 0.3.4 → 0.3.6

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 (39) hide show
  1. package/data/synonyms.csv +6 -3
  2. package/data/taxa.csv +345 -342
  3. package/data/text/Agoseris-grandiflora-var-grandiflora.md +1 -0
  4. package/data/text/Agoseris-heterophylla-var-cryptopleura.md +1 -0
  5. package/data/text/Delphinium-variegatum-subsp-variegatum.md +1 -0
  6. package/data/text/Fritillaria-affinis.md +1 -0
  7. package/data/text/Fritillaria-agrestis.md +1 -0
  8. package/data/text/Fritillaria-liliacea.md +1 -0
  9. package/data/text/Galium-aparine.md +1 -0
  10. package/data/text/Galium-triflorum.md +1 -0
  11. package/data/text/Hypochaeris-glabra.md +1 -1
  12. package/data/text/Hypochaeris-radicata.md +1 -1
  13. package/data/text/Lithophragma-affine.md +1 -1
  14. package/data/text/Lupinus-formosus-var-formosus.md +1 -1
  15. package/data/text/Lupinus-latifolius-var-latifolius.md +1 -1
  16. package/data/text/Lupinus-microcarpus-var-densiflorus.md +1 -1
  17. package/data/text/Lupinus-microcarpus-var-microcarpus.md +1 -1
  18. package/data/text/Lupinus-succulentus.md +1 -1
  19. package/data/text/Pinus-ponderosa-var-pacifica.md +1 -0
  20. package/data/text/Pinus-sabiniana.md +1 -0
  21. package/data/text/Quercus-douglasii.md +1 -0
  22. package/data/text/Quercus-kelloggii.md +1 -0
  23. package/data/text/Quercus-lobata.md +1 -0
  24. package/data/text/Sidalcea-diploscypha.md +1 -1
  25. package/ebook/css/main.css +10 -1
  26. package/jekyll/assets/css/main.css +10 -1
  27. package/lib/ebook/image.js +6 -4
  28. package/lib/ebook/pages/taxonpage.js +9 -36
  29. package/lib/externalsites.js +14 -21
  30. package/lib/html.js +70 -41
  31. package/lib/htmltaxon.js +52 -0
  32. package/lib/index.js +1 -0
  33. package/lib/taxa.js +9 -8
  34. package/lib/taxon.js +6 -0
  35. package/lib/textutils.js +10 -0
  36. package/lib/web/pagetaxon.js +105 -90
  37. package/package.json +2 -2
  38. /package/data/text/{Claytonia-parviflora.md → Claytonia-parviflora-subsp-parviflora.md} +0 -0
  39. /package/data/text/{Claytonia-perfoliata.md → Claytonia-perfoliata-subsp-perfoliata.md} +0 -0
@@ -0,0 +1 @@
1
+ Height 25-100 cm. Leaf petiole usually purple.
@@ -0,0 +1 @@
1
+ Height 5-60 cm. Leaf petiole usually not purple.
@@ -0,0 +1 @@
1
+ Leaves hairy on both sides; petioles hairy. 10 or fewer flowers in inflorescence.
@@ -0,0 +1 @@
1
+ Spots on flower.
@@ -0,0 +1 @@
1
+ No spots on flower. Smell unpleasant.
@@ -0,0 +1 @@
1
+ Flowers white with green stripes.
@@ -0,0 +1 @@
1
+ Leaves in whorls of 6 to 8. Similar to _G. triflorum_, but _G. aparine_ has hooked prickles.
@@ -0,0 +1 @@
1
+ Leaves in whorls of 6. Similar to _G. aparine_, but _G. triflorum_ does not have hooked prickles.
@@ -1 +1 @@
1
- Annual. [Peduncle](g/peduncle.html) has bracts. Ligules extend only slightly beyond involucre. Leaves hairless or very nearly so. Leaves basal.
1
+ [Peduncle](g/peduncle.html) has bracts. Ligules extend only slightly beyond involucre. Leaves hairless or very nearly so. Leaves basal.
@@ -1 +1 @@
1
- Perennial. [Peduncle](g/peduncle.html) has bracts. Ligules extend much beyond involucre. Leaves bristly and rough to touch. Leaves basal.
1
+ [Peduncle](g/peduncle.html) has bracts. Ligules extend much beyond involucre. Leaves bristly and rough to touch. Leaves basal.
@@ -1 +1 @@
1
- Pedicel 3-10 mm, floral tube conical at base. In open, grassy areas. Similar to [_L. parviflorum_ var. _parviflorum_](./Lithophragma-parviflorum-var-parviflorum.html).
1
+ Pedicel 3-10 mm, floral tube conical at base. In open, grassy areas. Similar to _L. parviflorum_ var. _parviflorum_.
@@ -1 +1 @@
1
- Perennial. Keel margins not hairy.
1
+ Keel margins not hairy.
@@ -1 +1 @@
1
- Perennial. Fruit densely hairy. Upper keel margin hairy from base to middle, not hairy from middle to tip
1
+ Fruit densely hairy. Upper keel margin hairy from base to middle, not hairy from middle to tip
@@ -1 +1 @@
1
- Annual. At least lower part of stem clearly hollow. Lower keel margins usually not hairy or less hairy than upper. Flowers generally yellow or white.
1
+ At least lower part of stem clearly hollow. Lower keel margins usually not hairy or less hairy than upper. Flowers generally yellow or white.
@@ -1 +1 @@
1
- Annual. At least lower part of stem clearly hollow. Lower keel margins usually not hairy or less hairy than upper. Flowers generally pink or purple.
1
+ At least lower part of stem clearly hollow. Lower keel margins usually not hairy or less hairy than upper. Flowers generally pink or purple.
@@ -1 +1 @@
1
- Annual. Fruit not densely hairy.
1
+ Fruit not densely hairy.
@@ -0,0 +1 @@
1
+ 3 needles, deep green and shiny. Mature bark light to medium yellow-brown, with shallow furrows. Cone stalk less than 2cm long.
@@ -0,0 +1 @@
1
+ 3 gray-green needles; needles flexible and usually drooping. Bark dark gray with irregular furrows. Bark on older trees in yellow plates. Cone stalk less than 7 cm.
@@ -0,0 +1 @@
1
+ Leaves usually shallowly lobed; upper sides generally dull and blue-green.
@@ -0,0 +1 @@
1
+ Leaf moderately to deeply lobed. Each lobe with 1-4 teeth, which are usually bristle-tipped.
@@ -0,0 +1 @@
1
+ Leaves usually deeply lobed; upper sides generally shiny and dark green.
@@ -1 +1 @@
1
- Annual. Stipules on upper stem having two or more linear lobes.
1
+ Stipules on upper stem having two or more linear lobes.
@@ -12,11 +12,20 @@ div.section {
12
12
  margin-bottom: .5rem;
13
13
  }
14
14
 
15
- img.flr {
15
+ img.flr-color {
16
16
  padding-right: .25rem;
17
17
  width: 1rem;
18
18
  }
19
19
 
20
+ span.flr-time,
21
+ span.lc {
22
+ font-weight: bold;
23
+ }
24
+
25
+ span.lcs {
26
+ margin-right: 1rem;
27
+ }
28
+
20
29
  /* Glossary */
21
30
  div.glossary img {
22
31
  max-height: 300px;
@@ -131,12 +131,21 @@ div.section ul.indent {
131
131
  padding-left: 1rem;
132
132
  }
133
133
 
134
- img.flower-color {
134
+ img.flr-color {
135
135
  margin-bottom: .2rem;
136
136
  margin-right: .5rem;
137
137
  width: 1rem;
138
138
  }
139
139
 
140
+ span.flr-time,
141
+ span.lc {
142
+ font-weight: bold;
143
+ }
144
+
145
+ span.lcs {
146
+ margin-right: 1rem;
147
+ }
148
+
140
149
  /* Forms */
141
150
  input {
142
151
  margin-right: .5em;
@@ -1,9 +1,12 @@
1
1
  class Image {
2
-
3
2
  #src;
4
3
  #credit;
5
4
 
6
- constructor( src, credit ) {
5
+ /**
6
+ * @param {string} src
7
+ * @param {string} credit
8
+ */
9
+ constructor(src, credit) {
7
10
  this.#src = src;
8
11
  this.#credit = credit;
9
12
  }
@@ -15,7 +18,6 @@ class Image {
15
18
  getSrc() {
16
19
  return this.#src;
17
20
  }
18
-
19
21
  }
20
22
 
21
- export { Image };
23
+ export { Image };
@@ -1,11 +1,13 @@
1
- import sizeOf from "image-size";
1
+ import imageSize from "image-size";
2
2
  import { Config, Files } from "@ca-plant-list/ca-plant-list";
3
3
  import { EBookPage } from "../ebookpage.js";
4
4
  import { XHTML } from "../xhtml.js";
5
5
  import { Markdown } from "../../markdown.js";
6
- import { DateUtils } from "../../dateutils.js";
7
6
  // eslint-disable-next-line no-unused-vars
8
7
  import { Taxon } from "../../taxon.js";
8
+ // eslint-disable-next-line no-unused-vars
9
+ import { Image } from "../image.js";
10
+ import { HTMLTaxon } from "../../htmltaxon.js";
9
11
 
10
12
  class TaxonPage extends EBookPage {
11
13
  #outputDir;
@@ -13,10 +15,9 @@ class TaxonPage extends EBookPage {
13
15
  #photos;
14
16
 
15
17
  /**
16
- *
17
18
  * @param {string} outputDir
18
19
  * @param {Taxon} taxon
19
- * @param {*} photos
20
+ * @param {Image[]} photos
20
21
  */
21
22
  constructor(outputDir, taxon, photos) {
22
23
  super(outputDir + "/" + taxon.getFileName(), taxon.getName());
@@ -26,36 +27,6 @@ class TaxonPage extends EBookPage {
26
27
  }
27
28
 
28
29
  renderPageBody() {
29
- /**
30
- * @param {Taxon} taxon
31
- */
32
- function renderBloomInfo(taxon) {
33
- const colors = taxon.getFlowerColors();
34
- const monthStart = taxon.getBloomStart();
35
- const monthEnd = taxon.getBloomEnd();
36
- if (!colors && !monthStart) {
37
- return "";
38
- }
39
- let html = "";
40
- if (colors) {
41
- for (const color of colors) {
42
- html += XHTML.textElement("img", "", {
43
- src: "./i/f-" + color + ".svg",
44
- class: "flr",
45
- });
46
- }
47
- }
48
- if (monthStart && monthEnd) {
49
- html += XHTML.textElement(
50
- "div",
51
- DateUtils.getMonthName(monthStart) +
52
- "-" +
53
- DateUtils.getMonthName(monthEnd)
54
- );
55
- }
56
- return XHTML.wrap("div", html, { class: "section flr" });
57
- }
58
-
59
30
  /**
60
31
  * @param {string} name
61
32
  */
@@ -87,7 +58,7 @@ class TaxonPage extends EBookPage {
87
58
  });
88
59
  }
89
60
 
90
- html += renderBloomInfo(this.#taxon);
61
+ html += HTMLTaxon.getFlowerInfo(this.#taxon, "section flr");
91
62
 
92
63
  html += renderCustomText(this.#taxon.getBaseFileName());
93
64
 
@@ -95,7 +66,9 @@ class TaxonPage extends EBookPage {
95
66
  let photoHTML = "";
96
67
  for (const photo of this.#photos) {
97
68
  const src = photo.getSrc();
98
- const dimensions = sizeOf(this.#outputDir + "/" + src);
69
+ const dimensions = imageSize.imageSize(
70
+ this.#outputDir + "/" + src
71
+ );
99
72
  let img = XHTML.textElement("img", "", {
100
73
  src: src,
101
74
  style: "max-width:" + dimensions.width + "px",
@@ -1,23 +1,18 @@
1
1
  class ExternalSites {
2
+ static getInatObsLink(options) {
3
+ const url = new URL(
4
+ "https://www.inaturalist.org/observations?subview=map"
5
+ );
2
6
 
3
- static getCalscapeLink( taxonName ) {
4
- const url = new URL( "https://calscape.org/loc-California/" + taxonName.replace( "subsp.", "ssp." ) + " ()" );
5
- return url.toString();
6
- }
7
-
8
- static getInatObsLink( options ) {
9
-
10
- const url = new URL( "https://www.inaturalist.org/observations?subview=map" );
11
-
12
- for ( const [ k, v ] of Object.entries( options ) ) {
13
- switch ( k ) {
7
+ for (const [k, v] of Object.entries(options)) {
8
+ switch (k) {
14
9
  case "coords": {
15
- const delta = .1;
10
+ const delta = 0.1;
16
11
  const params = url.searchParams;
17
- params.set( "nelat", v[ 1 ] + delta );
18
- params.set( "swlat", v[ 1 ] - delta );
19
- params.set( "nelng", v[ 0 ] + delta );
20
- params.set( "swlng", v[ 0 ] - delta );
12
+ params.set("nelat", v[1] + delta);
13
+ params.set("swlat", v[1] - delta);
14
+ params.set("nelng", v[0] + delta);
15
+ params.set("swlng", v[0] - delta);
21
16
  break;
22
17
  }
23
18
  case "created_d1":
@@ -27,17 +22,15 @@ class ExternalSites {
27
22
  case "subview":
28
23
  case "taxon_id":
29
24
  case "taxon_name":
30
- if ( v ) {
31
- url.searchParams.set( k, v );
25
+ if (v) {
26
+ url.searchParams.set(k, v);
32
27
  }
33
28
  break;
34
29
  }
35
30
  }
36
31
 
37
32
  return url.toString();
38
-
39
33
  }
40
-
41
34
  }
42
35
 
43
- export { ExternalSites };
36
+ export { ExternalSites };
package/lib/html.js CHANGED
@@ -8,41 +8,60 @@ export const HTML_OPTIONS = {
8
8
 
9
9
  /** HTML utility functions. */
10
10
  export class HTML {
11
-
12
- static arrayToLI( items ) {
13
- return items.reduce( ( itemHTML, currVal ) => itemHTML + "<li>" + currVal + "</li>", "" );
11
+ /**
12
+ * @param {string[]} items
13
+ */
14
+ static arrayToLI(items) {
15
+ return items.reduce(
16
+ (itemHTML, currVal) => itemHTML + "<li>" + currVal + "</li>",
17
+ ""
18
+ );
14
19
  }
15
20
 
16
- static escapeAttribute( value ) {
17
- return value.replaceAll( "\"", "&quot;" );
21
+ /**
22
+ * @param {string} value
23
+ */
24
+ static escapeAttribute(value) {
25
+ return value.replaceAll('"', "&quot;");
18
26
  }
19
27
 
20
- static escapeText( text ) {
21
- return text.replaceAll( "&", "&amp;" ).replaceAll( "<", "&lt;" ).replaceAll( ">", "&gt;" );
28
+ /**
29
+ * @param {string} text
30
+ */
31
+ static escapeText(text) {
32
+ return text
33
+ .replaceAll("&", "&amp;")
34
+ .replaceAll("<", "&lt;")
35
+ .replaceAll(">", "&gt;");
22
36
  }
23
37
 
24
38
  /**
25
39
  * @deprecated
26
40
  */
27
- static getElement( elName, text, attributes = {}, options = 0 ) {
41
+ static getElement(elName, text, attributes = {}, options = 0) {
28
42
  let html = "<" + elName;
29
- html += this.renderAttributes( attributes );
30
- if ( !( options & HTML_OPTIONS.NO_ESCAPE ) ) {
31
- text = this.escapeText( text );
43
+ html += this.renderAttributes(attributes);
44
+ if (!(options & HTML_OPTIONS.NO_ESCAPE)) {
45
+ text = this.escapeText(text);
32
46
  }
33
47
  html += ">" + text + "</" + elName + ">";
34
48
  return html;
35
-
36
49
  }
37
50
 
38
- static #getElement( elName, text, attributes, escape ) {
51
+ /**
52
+ * @param {string} elName
53
+ * @param {string} text
54
+ * @param {string|Object<string,string>|undefined} attributes
55
+ * @param {boolean} escape
56
+ */
57
+ static #getElement(elName, text, attributes, escape) {
39
58
  let html = "<" + elName;
40
- html += this.renderAttributes( attributes );
41
- if ( escape && ( typeof text === "string" ) ) {
42
- text = this.escapeText( text );
59
+ html += this.renderAttributes(attributes);
60
+ if (escape && typeof text === "string") {
61
+ text = this.escapeText(text);
43
62
  }
44
63
  // If tag is empty, make it self-closing so it is XHTML (epub) compatible.
45
- if ( text === "" ) {
64
+ if (text === "") {
46
65
  return html + "/>";
47
66
  }
48
67
  return html + ">" + text + "</" + elName + ">";
@@ -50,22 +69,22 @@ export class HTML {
50
69
 
51
70
  /**
52
71
  * Generate HTML for an &lt;a> element.
53
- * @param {string|undefined} href
54
- * @param {string} linkText
55
- * @param {Object} [attributes]
72
+ * @param {string|undefined} href
73
+ * @param {string} linkText
74
+ * @param {Object} [attributes]
56
75
  * @param {boolean} [openInNewWindow] true if the link should open in a new window.
57
76
  * @returns {string} an HTML &lt;a> element.
58
77
  */
59
- static getLink( href, linkText, attributes = {}, openInNewWindow ) {
78
+ static getLink(href, linkText, attributes = {}, openInNewWindow) {
60
79
  let html = "<a";
61
- if ( href !== undefined ) {
62
- html += this.renderAttribute( "href", href );
80
+ if (href !== undefined) {
81
+ html += this.renderAttribute("href", href);
63
82
  }
64
- html += this.renderAttributes( attributes );
65
- if ( openInNewWindow ) {
66
- html += this.renderAttribute( "target", "_blank" );
83
+ html += this.renderAttributes(attributes);
84
+ if (openInNewWindow) {
85
+ html += this.renderAttribute("target", "_blank");
67
86
  }
68
- return html + ">" + this.escapeText( linkText ) + "</a>";
87
+ return html + ">" + this.escapeText(linkText) + "</a>";
69
88
  }
70
89
 
71
90
  /**
@@ -76,32 +95,42 @@ export class HTML {
76
95
  * @param {boolean} options.icon [true] display an icon after the text
77
96
  * @returns {string} A &lt;span> element to be used as a Bootstrap tooltip.
78
97
  */
79
- static getToolTip( text, tooltip, options = {} ) {
80
- const func = text.charAt( 0 ) === "<" ? HTML.wrap : HTML.textElement;
81
- if ( options.icon !== false ) {
98
+ static getToolTip(text, tooltip, options = {}) {
99
+ const func = text.charAt(0) === "<" ? HTML.wrap : HTML.textElement;
100
+ if (options.icon !== false) {
82
101
  text += " ⓘ";
83
102
  }
84
- return func( "span", text, { "data-bs-html": "true", "title": tooltip } );
103
+ return func("span", text, { "data-bs-html": "true", title: tooltip });
85
104
  }
86
105
 
87
- static renderAttribute( n, v ) {
88
- return " " + n + "=\"" + this.escapeAttribute( v ) + "\"";
106
+ static renderAttribute(n, v) {
107
+ return " " + n + '="' + this.escapeAttribute(v) + '"';
89
108
  }
90
109
 
91
- static renderAttributes( attributes ) {
110
+ /**
111
+ * @param {string|Object<string,string>|undefined} attributes
112
+ */
113
+ static renderAttributes(attributes = {}) {
114
+ if (typeof attributes === "string") {
115
+ return this.renderAttribute("class", attributes);
116
+ }
92
117
  let html = "";
93
- for ( const [ k, v ] of Object.entries( attributes ) ) {
94
- html += this.renderAttribute( k, v );
118
+ for (const [k, v] of Object.entries(attributes)) {
119
+ html += this.renderAttribute(k, v);
95
120
  }
96
121
  return html;
97
122
  }
98
123
 
99
- static textElement( elName, text, attributes = {} ) {
100
- return HTML.#getElement( elName, text, attributes, true );
124
+ static textElement(elName, text, attributes = {}) {
125
+ return HTML.#getElement(elName, text, attributes, true);
101
126
  }
102
127
 
103
- static wrap( elName, text, attributes = {} ) {
104
- return HTML.#getElement( elName, text, attributes, false );
128
+ /**
129
+ * @param {string} elName
130
+ * @param {string} text
131
+ * @param {string|Object<string,string>|undefined} [attributes]
132
+ */
133
+ static wrap(elName, text, attributes) {
134
+ return HTML.#getElement(elName, text, attributes, false);
105
135
  }
106
-
107
136
  }
@@ -0,0 +1,52 @@
1
+ import { DateUtils } from "./dateutils.js";
2
+ import { HTML } from "./html.js";
3
+ // eslint-disable-next-line no-unused-vars
4
+ import { Taxon } from "./taxon.js";
5
+ import { TextUtils } from "./textutils.js";
6
+
7
+ class HTMLTaxon {
8
+ /**
9
+ * @param {Taxon} taxon
10
+ * @param {string} classNames
11
+ */
12
+ static getFlowerInfo(taxon, classNames = "section") {
13
+ const lifeCycle = taxon.getLifeCycle();
14
+ const colors = taxon.getFlowerColors();
15
+ const monthStart = taxon.getBloomStart();
16
+ const monthEnd = taxon.getBloomEnd();
17
+
18
+ const parts = [];
19
+ if (lifeCycle) {
20
+ const text =
21
+ HTML.wrap("span", TextUtils.ucFirst(lifeCycle), "lc") + ".";
22
+ parts.push(HTML.wrap("span", text, "lcs"));
23
+ }
24
+
25
+ if (colors || monthStart) {
26
+ let html = "Flowers: ";
27
+ if (colors) {
28
+ for (const color of colors) {
29
+ html += HTML.textElement("img", "", {
30
+ src: "./i/f-" + color + ".svg",
31
+ alt: color + " flowers",
32
+ title: color,
33
+ class: "flr-color",
34
+ });
35
+ }
36
+ }
37
+ if (monthStart && monthEnd) {
38
+ html += HTML.wrap(
39
+ "span",
40
+ DateUtils.getMonthName(monthStart) +
41
+ "-" +
42
+ DateUtils.getMonthName(monthEnd),
43
+ { class: "flr-time" }
44
+ );
45
+ }
46
+ parts.push(HTML.wrap("span", html));
47
+ }
48
+ return HTML.wrap("div", parts.join(""), { class: classNames });
49
+ }
50
+ }
51
+
52
+ export { HTMLTaxon };
package/lib/index.js CHANGED
@@ -11,6 +11,7 @@
11
11
  * GRank:string;
12
12
  * "inat id":string;
13
13
  * "jepson id":string;
14
+ * life_cycle:"annual"|"perennial"|undefined
14
15
  * SRank:string;
15
16
  * status:string;
16
17
  * "RPI ID":string;
package/lib/taxa.js CHANGED
@@ -2,8 +2,9 @@ import { Config } from "./config.js";
2
2
  import { HTML } from "./html.js";
3
3
  import { CSV } from "./csv.js";
4
4
  import { RarePlants } from "./rareplants.js";
5
- import { ErrorLog, Families, Taxon } from "./index.js";
5
+ import { Families, Taxon } from "./index.js";
6
6
  import { Genera } from "./genera.js";
7
+ import { TextUtils } from "./textutils.js";
7
8
 
8
9
  const FLOWER_COLORS = [
9
10
  { name: "white", color: "white" },
@@ -14,6 +15,7 @@ const FLOWER_COLORS = [
14
15
  { name: "blue", color: "blue" },
15
16
  { name: "purple", color: "purple" },
16
17
  { name: "green", color: "green" },
18
+ { name: "brown", color: "brown" },
17
19
  ];
18
20
 
19
21
  /**
@@ -72,9 +74,7 @@ class FlowerColor {
72
74
  }
73
75
 
74
76
  getColorName(uc = false) {
75
- return uc
76
- ? this.#color[0].toUpperCase() + this.#color.substring(1)
77
- : this.#color;
77
+ return uc ? TextUtils.ucFirst(this.#color) : this.#color;
78
78
  }
79
79
 
80
80
  getFileName() {
@@ -259,9 +259,7 @@ class Taxa {
259
259
  }
260
260
 
261
261
  /**
262
- * @param {*} taxaCSV
263
- * @param {*} inclusionList
264
- * @param {*} taxaMeta
262
+ * @param {import("./index.js").TaxonData[]} taxaCSV
265
263
  * @param {Taxon} taxonClass
266
264
  * @param {boolean} showFlowerErrors
267
265
  */
@@ -294,7 +292,10 @@ class Taxa {
294
292
  const color = this.#flower_colors[colorName];
295
293
  if (!color) {
296
294
  throw new Error(
297
- 'flower color "' + colorName + '" not found'
295
+ 'flower color "' +
296
+ colorName +
297
+ '" not found for ' +
298
+ name
298
299
  );
299
300
  }
300
301
  color.addTaxon(taxon);
package/lib/taxon.js CHANGED
@@ -24,6 +24,7 @@ class Taxon {
24
24
  #iNatID;
25
25
  /**@type {string|undefined} */
26
26
  #iNatSyn;
27
+ #lifeCycle;
27
28
  #flowerColors;
28
29
  #bloomStart;
29
30
  #bloomEnd;
@@ -57,6 +58,7 @@ class Taxon {
57
58
  this.#jepsonID = data["jepson id"];
58
59
  this.#calRecNum = data["calrecnum"];
59
60
  this.#iNatID = data["inat id"];
61
+ this.#lifeCycle = data.life_cycle;
60
62
  const colors = data["flower_color"];
61
63
  this.#flowerColors = colors ? colors.split(",") : undefined;
62
64
  if (data["bloom_start"]) {
@@ -234,6 +236,10 @@ class Taxon {
234
236
  return this.#jepsonID;
235
237
  }
236
238
 
239
+ getLifeCycle() {
240
+ return this.#lifeCycle;
241
+ }
242
+
237
243
  getName() {
238
244
  return this.#name;
239
245
  }
@@ -0,0 +1,10 @@
1
+ class TextUtils {
2
+ /**
3
+ * @param {string} t
4
+ */
5
+ static ucFirst(t) {
6
+ return t[0].toUpperCase() + t.substring(1);
7
+ }
8
+ }
9
+
10
+ export { TextUtils };