@ca-plant-list/ca-plant-list 0.3.1 → 0.3.3

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 (59) hide show
  1. package/.vscode/settings.json +5 -0
  2. package/data/exceptions.json +88 -83
  3. package/data/glossary/ovary.md +3 -0
  4. package/data/glossary/pistil.md +3 -0
  5. package/data/glossary/stigma.md +3 -0
  6. package/data/glossary/style.md +3 -0
  7. package/data/illustrations/inkscape/pistil.svg +156 -0
  8. package/data/synonyms.csv +8 -2
  9. package/data/taxa.csv +56 -54
  10. package/data/text/Ceanothus-cuneatus-var-cuneatus.md +1 -0
  11. package/data/text/Ceanothus-leucodermis.md +1 -0
  12. package/data/text/Delphinium-californicum-subsp-californicum.md +1 -0
  13. package/data/text/Delphinium-decorum-subsp-decorum.md +1 -0
  14. package/data/text/Delphinium-hesperium-subsp-hesperium.md +1 -0
  15. package/data/text/Delphinium-patens-subsp-patens.md +1 -0
  16. package/data/text/Galium-andrewsii-subsp-gatense.md +1 -0
  17. package/data/text/Helianthella-californica-var-californica.md +1 -0
  18. package/data/text/Helianthella-castanea.md +1 -0
  19. package/data/text/Iris-macrosiphon.md +1 -0
  20. package/data/text/Lasthenia-californica-subsp-californica.md +1 -0
  21. package/data/text/Lasthenia-gracilis.md +1 -0
  22. package/data/text/Lithophragma-affine.md +1 -1
  23. package/data/text/Lithophragma-parviflorum-var-parviflorum.md +1 -1
  24. package/data/text/Lomatium-californicum.md +0 -0
  25. package/data/text/Lomatium-dasycarpum-subsp-dasycarpum.md +1 -0
  26. package/data/text/Lomatium-utriculatum.md +1 -0
  27. package/data/text/Nemophila-heterophylla.md +1 -0
  28. package/data/text/Nemophila-parviflora-var-parviflora.md +1 -0
  29. package/data/text/Plectritis-ciliosa.md +1 -0
  30. package/data/text/Plectritis-macrocera.md +1 -0
  31. package/data/text/Primula-clevelandii-var-patula.md +1 -0
  32. package/data/text/Primula-hendersonii.md +1 -0
  33. package/data/text/Sidalcea-diploscypha.md +1 -0
  34. package/data/text/Thysanocarpus-curvipes.md +1 -0
  35. package/data/text/Thysanocarpus-laciniatus.md +1 -0
  36. package/data/text/Trillium-chloropetalum.md +1 -1
  37. package/data/text/Viola-douglasii.md +1 -0
  38. package/data/text/Viola-pedunculata.md +1 -0
  39. package/data/text/Viola-purpurea-subsp-quercetorum.md +1 -0
  40. package/ebook/css/main.css +5 -0
  41. package/lib/basepagerenderer.js +30 -28
  42. package/lib/csv.js +13 -0
  43. package/lib/dateutils.js +36 -16
  44. package/lib/ebook/pages/taxonpage.js +63 -36
  45. package/lib/errorlog.js +16 -11
  46. package/lib/exceptions.js +2 -0
  47. package/lib/families.js +109 -71
  48. package/lib/files.js +103 -45
  49. package/lib/generictaxaloader.js +15 -7
  50. package/lib/html.js +2 -2
  51. package/lib/index.js +38 -3
  52. package/lib/taxa.js +175 -86
  53. package/lib/taxaloader.js +28 -15
  54. package/lib/taxaprocessor.js +6 -8
  55. package/lib/taxon.js +109 -56
  56. package/lib/web/pagetaxon.js +1 -1
  57. package/package.json +6 -8
  58. package/scripts/build-ebook.js +30 -25
  59. package/lib/index.d.ts +0 -327
@@ -4,88 +4,115 @@ import { EBookPage } from "../ebookpage.js";
4
4
  import { XHTML } from "../xhtml.js";
5
5
  import { Markdown } from "../../markdown.js";
6
6
  import { DateUtils } from "../../dateutils.js";
7
+ // eslint-disable-next-line no-unused-vars
8
+ import { Taxon } from "../../taxon.js";
7
9
 
8
10
  class TaxonPage extends EBookPage {
9
-
10
11
  #outputDir;
11
12
  #taxon;
12
13
  #photos;
13
14
 
14
- constructor( outputDir, taxon, photos ) {
15
- super( outputDir + "/" + taxon.getFileName(), taxon.getName() );
15
+ /**
16
+ *
17
+ * @param {string} outputDir
18
+ * @param {Taxon} taxon
19
+ * @param {*} photos
20
+ */
21
+ constructor(outputDir, taxon, photos) {
22
+ super(outputDir + "/" + taxon.getFileName(), taxon.getName());
16
23
  this.#outputDir = outputDir;
17
24
  this.#taxon = taxon;
18
25
  this.#photos = photos;
19
26
  }
20
27
 
21
28
  renderPageBody() {
22
-
23
- function renderBloomInfo( taxon ) {
29
+ /**
30
+ * @param {Taxon} taxon
31
+ */
32
+ function renderBloomInfo(taxon) {
24
33
  const colors = taxon.getFlowerColors();
25
34
  const monthStart = taxon.getBloomStart();
26
35
  const monthEnd = taxon.getBloomEnd();
27
- if ( !colors && !monthStart ) {
36
+ if (!colors && !monthStart) {
28
37
  return "";
29
38
  }
30
39
  let html = "";
31
- if ( colors ) {
32
- for ( const color of colors ) {
33
- html += XHTML.textElement( "img", "", { src: "./i/f-" + color + ".svg", class: "flr" } );
40
+ if (colors) {
41
+ for (const color of colors) {
42
+ html += XHTML.textElement("img", "", {
43
+ src: "./i/f-" + color + ".svg",
44
+ class: "flr",
45
+ });
34
46
  }
35
47
  }
36
- if ( monthStart ) {
37
- html += XHTML.textElement( "div", DateUtils.getMonthName( monthStart ) + "-" + DateUtils.getMonthName( monthEnd ) );
38
-
48
+ if (monthStart && monthEnd) {
49
+ html += XHTML.textElement(
50
+ "div",
51
+ DateUtils.getMonthName(monthStart) +
52
+ "-" +
53
+ DateUtils.getMonthName(monthEnd)
54
+ );
39
55
  }
40
- return XHTML.wrap( "div", html, { class: "flr" } );
56
+ return XHTML.wrap("div", html, { class: "section flr" });
41
57
  }
42
58
 
43
-
44
- function renderCustomText( name ) {
59
+ /**
60
+ * @param {string} name
61
+ */
62
+ function renderCustomText(name) {
45
63
  // See if there is custom text.
46
- const fileName = Config.getPackageDir() + "/data/text/" + name + ".md";
47
- if ( !Files.exists( fileName ) ) {
64
+ const fileName =
65
+ Config.getPackageDir() + "/data/text/" + name + ".md";
66
+ if (!Files.exists(fileName)) {
48
67
  return "";
49
68
  }
50
- const text = Files.read( fileName );
51
- return Markdown.strToHTML( text );
69
+ const text = Files.read(fileName);
70
+ return Markdown.strToHTML(text);
52
71
  }
53
72
 
54
73
  const name = this.#taxon.getName();
55
- let html = XHTML.textElement( "h1", name );
74
+ let html = XHTML.textElement("h1", name);
56
75
 
57
76
  const family = this.#taxon.getFamily();
58
- html += XHTML.wrap( "div", XHTML.getLink( family.getFileName(), family.getName() ) );
77
+ html += XHTML.wrap(
78
+ "div",
79
+ XHTML.getLink(family.getFileName(), family.getName()),
80
+ { class: "section" }
81
+ );
59
82
 
60
83
  const cn = this.#taxon.getCommonNames();
61
- if ( cn && cn.length > 0 ) {
62
- html += XHTML.textElement( "p", cn.join( ", " ) );
84
+ if (cn && cn.length > 0) {
85
+ html += XHTML.textElement("div", cn.join(", "), {
86
+ class: "section",
87
+ });
63
88
  }
64
89
 
65
- html += renderBloomInfo( this.#taxon );
90
+ html += renderBloomInfo(this.#taxon);
66
91
 
67
- html += renderCustomText( this.#taxon.getBaseFileName() );
92
+ html += renderCustomText(this.#taxon.getBaseFileName());
68
93
 
69
- if ( this.#photos ) {
94
+ if (this.#photos) {
70
95
  let photoHTML = "";
71
- for ( const photo of this.#photos ) {
96
+ for (const photo of this.#photos) {
72
97
  const src = photo.getSrc();
73
- const dimensions = sizeOf( this.#outputDir + "/" + src );
74
- let img = XHTML.textElement( "img", "", { src: src, style: "max-width:" + dimensions.width + "px" } );
98
+ const dimensions = sizeOf(this.#outputDir + "/" + src);
99
+ let img = XHTML.textElement("img", "", {
100
+ src: src,
101
+ style: "max-width:" + dimensions.width + "px",
102
+ });
75
103
  const caption = photo.getCaption();
76
- if ( caption ) {
77
- img += XHTML.textElement( "figcaption", caption );
104
+ if (caption) {
105
+ img += XHTML.textElement("figcaption", caption);
78
106
  }
79
- photoHTML += XHTML.wrap( "figure", img );
107
+ photoHTML += XHTML.wrap("figure", img);
80
108
  }
81
- if ( photoHTML ) {
82
- html += XHTML.wrap( "div", photoHTML );
109
+ if (photoHTML) {
110
+ html += XHTML.wrap("div", photoHTML);
83
111
  }
84
112
  }
85
113
 
86
114
  return html;
87
115
  }
88
-
89
116
  }
90
117
 
91
- export { TaxonPage };
118
+ export { TaxonPage };
package/lib/errorlog.js CHANGED
@@ -2,30 +2,35 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
 
4
4
  class ErrorLog {
5
-
6
5
  #fileName;
7
6
  #echo;
7
+ /** @type string[] */
8
8
  #errors = [];
9
9
 
10
- constructor( fileName, echo = false ) {
10
+ /**
11
+ * @param {string} fileName
12
+ * @param {boolean} echo
13
+ */
14
+ constructor(fileName, echo = false) {
11
15
  this.#fileName = fileName;
12
16
  this.#echo = echo;
13
17
  }
14
18
 
15
- log( ...args ) {
16
- if ( this.#echo ) {
17
- console.log( args.join() );
19
+ /**
20
+ * @param {...string} args
21
+ */
22
+ log(...args) {
23
+ if (this.#echo) {
24
+ console.log(args.join());
18
25
  }
19
- this.#errors.push( args.join( "\t" ) );
26
+ this.#errors.push(args.join("\t"));
20
27
  }
21
28
 
22
29
  write() {
23
30
  // Make sure directory exists.
24
- fs.mkdirSync( path.dirname( this.#fileName ), { recursive: true } );
25
-
26
- fs.writeFileSync( this.#fileName, this.#errors.join( "\n" ) );
31
+ fs.mkdirSync(path.dirname(this.#fileName), { recursive: true });
32
+ fs.writeFileSync(this.#fileName, this.#errors.join("\n"));
27
33
  }
28
-
29
34
  }
30
35
 
31
- export { ErrorLog };
36
+ export { ErrorLog };
package/lib/exceptions.js CHANGED
@@ -18,6 +18,8 @@ class Exceptions {
18
18
  const localExceptions = readConfig( dir + "/exceptions.json" );
19
19
  for ( const [ k, v ] of Object.entries( localExceptions ) ) {
20
20
  this.#exceptions[ k ] = v;
21
+ // Tag as a local exception so we can distinguish between global and local.
22
+ v.local = true;
21
23
  }
22
24
 
23
25
  }
package/lib/families.js CHANGED
@@ -4,30 +4,38 @@ import { Jepson } from "./jepson.js";
4
4
  import { Taxa } from "./taxa.js";
5
5
  import { Files } from "./files.js";
6
6
  import { Config } from "./config.js";
7
+ // eslint-disable-next-line no-unused-vars
8
+ import { Taxon } from "./index.js";
7
9
 
8
10
  class Family {
9
-
10
11
  #name;
11
12
  #data;
12
13
 
13
- constructor( name, data ) {
14
+ /**
15
+ * @param {string} name
16
+ * @param {*} data
17
+ */
18
+ constructor(name, data) {
14
19
  this.#name = name;
15
20
  this.#data = data;
16
21
  }
17
22
 
18
- addTaxon( taxon ) {
19
- if ( !this.#data.taxa ) {
23
+ /**
24
+ * @param {Taxon} taxon
25
+ */
26
+ addTaxon(taxon) {
27
+ if (!this.#data.taxa) {
20
28
  this.#data.taxa = [];
21
29
  }
22
- this.#data.taxa.push( taxon );
23
- Sections.addTaxon( this.getSectionName(), taxon );
30
+ this.#data.taxa.push(taxon);
31
+ Sections.addTaxon(this.getSectionName(), taxon);
24
32
  }
25
33
 
26
34
  getBaseFileName() {
27
35
  return this.getName();
28
36
  }
29
37
 
30
- getFileName( ext = "html" ) {
38
+ getFileName(ext = "html") {
31
39
  return this.getBaseFileName() + "." + ext;
32
40
  }
33
41
 
@@ -46,163 +54,193 @@ class Family {
46
54
  getTaxa() {
47
55
  return this.#data.taxa;
48
56
  }
49
-
50
57
  }
51
58
 
52
59
  class Families {
53
-
54
60
  static #families;
55
61
 
56
62
  static {
57
-
58
63
  const dataDir = Config.getPackageDir() + "/data";
59
64
 
60
- this.#families = JSON.parse( Files.read( dataDir + "/families.json" ) );
61
- for ( const [ k, v ] of Object.entries( this.#families ) ) {
62
- this.#families[ k ] = new Family( k, v );
65
+ this.#families = JSON.parse(Files.read(dataDir + "/families.json"));
66
+ for (const [k, v] of Object.entries(this.#families)) {
67
+ this.#families[k] = new Family(k, v);
63
68
  }
64
-
65
69
  }
66
70
 
67
71
  static getFamilies() {
68
- return Object.values( this.#families ).sort( ( a, b ) => a.getName().localeCompare( b.getName() ) );
72
+ return Object.values(this.#families).sort((a, b) =>
73
+ a.getName().localeCompare(b.getName())
74
+ );
69
75
  }
70
76
 
71
- static getFamily( familyName ) {
72
- return this.#families[ familyName ];
77
+ /**
78
+ * @param {string} familyName
79
+ */
80
+ static getFamily(familyName) {
81
+ return this.#families[familyName];
73
82
  }
74
83
 
75
- static renderPages( outputDir, taxaColumns ) {
76
- new PageFamilyList( outputDir, this.#families ).render( taxaColumns );
84
+ /**
85
+ * @param {string} outputDir
86
+ * @param {import("./index.js").TaxaCol[]} taxaColumns
87
+ */
88
+ static renderPages(outputDir, taxaColumns) {
89
+ new PageFamilyList(outputDir, this.#families).render(taxaColumns);
77
90
 
78
- const names = Object.keys( this.#families );
79
- for ( const name of names.sort() ) {
80
- const family = this.#families[ name ];
81
- if ( family.getTaxa() ) {
82
- new PageFamily( outputDir, family ).render( taxaColumns );
91
+ const names = Object.keys(this.#families);
92
+ for (const name of names.sort()) {
93
+ const family = this.#families[name];
94
+ if (family.getTaxa()) {
95
+ new PageFamily(outputDir, family).render(taxaColumns);
83
96
  }
84
97
  }
85
98
  }
86
-
87
99
  }
88
100
 
89
101
  class PageFamilyList extends GenericPage {
90
-
91
102
  #families;
92
103
 
93
- constructor( outputDir, families ) {
94
- super( outputDir, "Families", "list_families" );
104
+ /**
105
+ * @param {string} outputDir
106
+ * @param {Object<string,Family>} families
107
+ */
108
+ constructor(outputDir, families) {
109
+ super(outputDir, "Families", "list_families");
95
110
  this.#families = families;
96
111
  }
97
112
 
98
- render( taxaColumns ) {
99
-
113
+ /**
114
+ * @param {import("./index.js").TaxaCol[]} taxaColumns
115
+ */
116
+ render(taxaColumns) {
100
117
  let html = this.getDefaultIntro();
101
118
 
102
119
  const sections = Sections.getSections();
103
120
  const sectionLinks = [];
104
- for ( const name of Object.keys( sections ).sort() ) {
105
-
106
- const taxa = sections[ name ];
121
+ for (const name of Object.keys(sections).sort()) {
122
+ const taxa = sections[name];
107
123
 
108
124
  // Render the section page.
109
- new PageSection( this.getOutputDir(), name, taxa ).render( taxaColumns );
125
+ new PageSection(this.getOutputDir(), name, taxa).render(
126
+ taxaColumns
127
+ );
110
128
 
111
129
  // Render the link.
112
130
  const href = "./" + name + ".html";
113
- sectionLinks.push( HTML.getLink( href, name ) + " (" + taxa.length + ")" );
131
+ sectionLinks.push(
132
+ HTML.getLink(href, name) + " (" + taxa.length + ")"
133
+ );
114
134
  }
115
- html += HTML.wrap( "ul", HTML.arrayToLI( sectionLinks ), { class: "listmenu" } );
135
+ html += HTML.wrap("ul", HTML.arrayToLI(sectionLinks), {
136
+ class: "listmenu",
137
+ });
116
138
 
117
139
  html += "<table>";
118
140
  html += "<thead>";
119
- html += HTML.textElement( "th", "Family" );
120
- html += HTML.textElement( "th", "Number of Species", { class: "right" } );
141
+ html += HTML.textElement("th", "Family");
142
+ html += HTML.textElement("th", "Number of Species", { class: "right" });
121
143
  html += "</thead>";
122
144
 
123
145
  html += "<tbody>";
124
- const names = Object.keys( this.#families ).sort();
125
- for ( const name of names ) {
126
- const family = this.#families[ name ];
146
+ const names = Object.keys(this.#families).sort();
147
+ for (const name of names) {
148
+ const family = this.#families[name];
127
149
  const taxa = family.getTaxa();
128
- if ( !taxa ) {
150
+ if (!taxa) {
129
151
  continue;
130
152
  }
131
- let cols = HTML.wrap( "td", HTML.getLink( "./" + family.getFileName(), family.getName() ) );
132
- cols += HTML.wrap( "td", taxa.length, { class: "right" } );
133
- html += HTML.wrap( "tr", cols );
153
+ let cols = HTML.wrap(
154
+ "td",
155
+ HTML.getLink("./" + family.getFileName(), family.getName())
156
+ );
157
+ cols += HTML.wrap("td", taxa.length, { class: "right" });
158
+ html += HTML.wrap("tr", cols);
134
159
  }
135
160
  html += "</tbody>";
136
161
 
137
162
  html += "</table>";
138
163
 
139
- this.writeFile( html );
164
+ this.writeFile(html);
140
165
  }
141
166
  }
142
167
 
143
168
  class PageFamily extends GenericPage {
144
-
145
169
  #family;
146
170
 
147
- constructor( outputDir, family ) {
148
- super( outputDir, family.getName(), family.getBaseFileName() );
171
+ /**
172
+ * @param {string} outputDir
173
+ * @param {Family} family
174
+ */
175
+ constructor(outputDir, family) {
176
+ super(outputDir, family.getName(), family.getBaseFileName());
149
177
  this.#family = family;
150
178
  }
151
179
 
152
- render( columns ) {
153
-
180
+ /**
181
+ *
182
+ * @param {import("./index.js").TaxaCol[]} columns
183
+ */
184
+ render(columns) {
154
185
  let html = this.getDefaultIntro();
155
186
 
156
187
  html += HTML.wrap(
157
188
  "div",
158
- Jepson.getEFloraLink( this.#family.getJepsonID() ),
189
+ Jepson.getEFloraLink(this.#family.getJepsonID()),
159
190
  { class: "section" }
160
191
  );
161
192
 
162
- html += Taxa.getHTMLTable( this.#family.getTaxa(), columns );
163
-
164
- this.writeFile( html );
193
+ html += Taxa.getHTMLTable(this.#family.getTaxa(), columns);
165
194
 
195
+ this.writeFile(html);
166
196
  }
167
197
  }
168
198
 
169
199
  class PageSection extends GenericPage {
170
-
171
200
  #taxa;
172
201
 
173
- constructor( outputDir, name, taxa ) {
174
- super( outputDir, name, name );
202
+ /**
203
+ * @param {string} outputDir
204
+ * @param {string} name
205
+ * @param {Taxon[]} taxa
206
+ */
207
+ constructor(outputDir, name, taxa) {
208
+ super(outputDir, name, name);
175
209
  this.#taxa = taxa;
176
210
  }
177
211
 
178
- render( columns ) {
179
-
212
+ /**
213
+ * @param {import("./index.js").TaxaCol[]} columns
214
+ */
215
+ render(columns) {
180
216
  let html = this.getDefaultIntro();
181
217
 
182
- html += Taxa.getHTMLTable( this.#taxa, columns );
183
-
184
- this.writeFile( html );
218
+ html += Taxa.getHTMLTable(this.#taxa, columns);
185
219
 
220
+ this.writeFile(html);
186
221
  }
187
222
  }
188
223
 
189
224
  class Sections {
190
-
225
+ /** @type {Object<string,Taxon[]} */
191
226
  static #sections = {};
192
227
 
193
- static addTaxon( name, taxon ) {
194
- let section = this.#sections[ name ];
195
- if ( !section ) {
228
+ /**
229
+ * @param {string} name
230
+ * @param {Taxon} taxon
231
+ */
232
+ static addTaxon(name, taxon) {
233
+ let section = this.#sections[name];
234
+ if (!section) {
196
235
  section = [];
197
- this.#sections[ name ] = section;
236
+ this.#sections[name] = section;
198
237
  }
199
- section.push( taxon );
238
+ section.push(taxon);
200
239
  }
201
240
 
202
241
  static getSections() {
203
242
  return this.#sections;
204
243
  }
205
-
206
244
  }
207
245
 
208
- export { Families };
246
+ export { Families };