@ca-plant-list/ca-plant-list 0.4.8 → 0.4.9

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.
@@ -1,3 +1,4 @@
1
+ import { ProgressMeter } from "../progressmeter.js";
1
2
  import { EBook } from "./ebook.js";
2
3
  import { EBookSiteGenerator } from "./ebooksitegenerator.js";
3
4
  import { GlossaryPages } from "./glossarypages.js";
@@ -10,153 +11,160 @@ import { TaxonPage } from "./pages/taxonpage.js";
10
11
  import { TOCPage } from "./pages/tocpage.js";
11
12
 
12
13
  class PlantBook extends EBook {
13
- #taxa;
14
- #glossary;
15
- #images;
16
-
17
- /**
18
- * @param {string} outputDir
19
- * @param {Config} config
20
- * @param {Taxa} taxa
21
- */
22
- constructor(outputDir, config, taxa) {
23
- super(
24
- outputDir,
25
- getRequiredConfigValue(config, "filename"),
26
- getRequiredConfigValue(config, "pub_id"),
27
- getRequiredConfigValue(config, "title")
28
- );
29
-
30
- this.#taxa = taxa;
31
- const generator = new EBookSiteGenerator(this.getContentDir());
32
- this.#glossary = new GlossaryPages(generator);
33
- this.#images = new Images(generator, this.getContentDir(), taxa);
34
- }
35
-
36
- async createPages() {
37
- const contentDir = this.getContentDir();
38
-
39
- await this.#images.createImages();
40
-
41
- console.log("creating taxon pages");
42
- const taxonList = this.#taxa.getTaxonList();
43
- for (const taxon of taxonList) {
44
- const name = taxon.getName();
45
- new TaxonPage(
46
- contentDir,
47
- taxon,
48
- this.#images.getTaxonImages(name)
49
- ).create();
14
+ #taxa;
15
+ #glossary;
16
+ #images;
17
+
18
+ /**
19
+ * @param {string} outputDir
20
+ * @param {Config} config
21
+ * @param {Taxa} taxa
22
+ */
23
+ constructor(outputDir, config, taxa) {
24
+ super(
25
+ outputDir,
26
+ getRequiredConfigValue(config, "filename"),
27
+ getRequiredConfigValue(config, "pub_id"),
28
+ getRequiredConfigValue(config, "title"),
29
+ );
30
+
31
+ this.#taxa = taxa;
32
+ const generator = new EBookSiteGenerator(this.getContentDir());
33
+ this.#glossary = new GlossaryPages(generator);
34
+ this.#images = new Images(generator, this.getContentDir(), taxa);
50
35
  }
51
36
 
52
- // Create lists.
53
- for (const color of this.#taxa.getFlowerColors()) {
54
- new PageListFlowerColor(contentDir, color).create();
37
+ async createPages() {
38
+ const contentDir = this.getContentDir();
39
+
40
+ const taxonList = this.#taxa.getTaxonList();
41
+
42
+ await this.#images.createImages(taxonList);
43
+
44
+ const meter = new ProgressMeter(
45
+ "creating taxon pages",
46
+ taxonList.length,
47
+ );
48
+ for (let index = 0; index < taxonList.length; index++) {
49
+ const taxon = taxonList[index];
50
+ new TaxonPage(contentDir, taxon, this.#images).create();
51
+ meter.update(index + 1);
52
+ }
53
+ meter.stop();
54
+
55
+ // Create lists.
56
+ for (const color of this.#taxa.getFlowerColors()) {
57
+ new PageListFlowerColor(contentDir, color).create();
58
+ }
59
+
60
+ PageListFlowers.createPages(contentDir, this.#taxa);
61
+
62
+ new PageListFamilies(contentDir, this.#taxa.getFamilies()).create();
63
+ for (const family of this.#taxa.getFamilies().getFamilies()) {
64
+ const taxa = family.getTaxa();
65
+ if (!taxa) {
66
+ continue;
67
+ }
68
+ const name = family.getName();
69
+ new PageListSpecies(
70
+ contentDir,
71
+ taxa,
72
+ name + ".html",
73
+ name,
74
+ ).create();
75
+ }
76
+ new PageListSpecies(
77
+ contentDir,
78
+ taxonList,
79
+ "list_species.html",
80
+ "All Species",
81
+ ).create();
82
+
83
+ this.#glossary.renderPages();
84
+
85
+ new TOCPage(contentDir, this.#taxa).create();
55
86
  }
56
87
 
57
- PageListFlowers.createPages(contentDir, this.#taxa);
58
-
59
- new PageListFamilies(contentDir, this.#taxa.getFamilies()).create();
60
- for (const family of this.#taxa.getFamilies().getFamilies()) {
61
- const taxa = family.getTaxa();
62
- if (!taxa) {
63
- continue;
64
- }
65
- const name = family.getName();
66
- new PageListSpecies(contentDir, taxa, name + ".html", name).create();
67
- }
68
- new PageListSpecies(
69
- contentDir,
70
- taxonList,
71
- "list_species.html",
72
- "All Species"
73
- ).create();
74
-
75
- this.#glossary.renderPages();
76
-
77
- new TOCPage(contentDir, this.#taxa).create();
78
- }
79
-
80
- renderManifestEntries() {
81
- let xml = "";
82
-
83
- // Add lists.
84
- xml +=
85
- '<item id="lspecies" href="list_species.html" media-type="application/xhtml+xml" />';
86
- xml +=
87
- '<item id="lfamilies" href="list_families.html" media-type="application/xhtml+xml" />';
88
- for (const color of this.#taxa.getFlowerColors()) {
89
- xml +=
90
- '<item id="l' +
91
- color.getColorName() +
92
- '" href="' +
93
- color.getFileName() +
94
- '" media-type="application/xhtml+xml" />';
88
+ renderManifestEntries() {
89
+ let xml = "";
90
+
91
+ // Add lists.
92
+ xml +=
93
+ '<item id="lspecies" href="list_species.html" media-type="application/xhtml+xml" />';
94
+ xml +=
95
+ '<item id="lfamilies" href="list_families.html" media-type="application/xhtml+xml" />';
96
+ for (const color of this.#taxa.getFlowerColors()) {
97
+ xml +=
98
+ '<item id="l' +
99
+ color.getColorName() +
100
+ '" href="' +
101
+ color.getFileName() +
102
+ '" media-type="application/xhtml+xml" />';
103
+ }
104
+
105
+ // Add family pages.
106
+ for (const family of this.#taxa.getFamilies().getFamilies()) {
107
+ const taxa = family.getTaxa();
108
+ if (!taxa) {
109
+ continue;
110
+ }
111
+ xml +=
112
+ '<item id="fam' +
113
+ family.getName() +
114
+ '" href="' +
115
+ family.getFileName() +
116
+ '" media-type="application/xhtml+xml" />';
117
+ }
118
+
119
+ // Add taxon pages.
120
+ const taxa = this.#taxa.getTaxonList();
121
+ for (let index = 0; index < taxa.length; index++) {
122
+ const taxon = taxa[index];
123
+ xml +=
124
+ '<item id="t' +
125
+ index +
126
+ '" href="' +
127
+ taxon.getFileName() +
128
+ '" media-type="application/xhtml+xml" />';
129
+ }
130
+
131
+ xml += PageListFlowers.getManifestEntries();
132
+ xml += this.#glossary.getManifestEntries();
133
+ xml += this.#images.getManifestEntries();
134
+
135
+ return xml;
95
136
  }
96
137
 
97
- // Add family pages.
98
- for (const family of this.#taxa.getFamilies().getFamilies()) {
99
- const taxa = family.getTaxa();
100
- if (!taxa) {
101
- continue;
102
- }
103
- xml +=
104
- '<item id="fam' +
105
- family.getName() +
106
- '" href="' +
107
- family.getFileName() +
108
- '" media-type="application/xhtml+xml" />';
109
- }
138
+ renderSpineElements() {
139
+ let xml = "";
110
140
 
111
- // Add taxon pages.
112
- const taxa = this.#taxa.getTaxonList();
113
- for (let index = 0; index < taxa.length; index++) {
114
- const taxon = taxa[index];
115
- xml +=
116
- '<item id="t' +
117
- index +
118
- '" href="' +
119
- taxon.getFileName() +
120
- '" media-type="application/xhtml+xml" />';
121
- }
141
+ // Add lists.
142
+ for (const color of this.#taxa.getFlowerColors()) {
143
+ xml += '<itemref idref="l' + color.getColorName() + '"/>';
144
+ }
145
+ xml += PageListFlowers.getSpineEntries();
122
146
 
123
- xml += PageListFlowers.getManifestEntries();
124
- xml += this.#glossary.getManifestEntries();
125
- xml += this.#images.getManifestEntries();
147
+ xml += '<itemref idref="lfamilies"/>';
148
+ xml += '<itemref idref="lspecies"/>';
126
149
 
127
- return xml;
128
- }
150
+ // Add families.
151
+ for (const family of this.#taxa.getFamilies().getFamilies()) {
152
+ const taxa = family.getTaxa();
153
+ if (!taxa) {
154
+ continue;
155
+ }
156
+ xml += '<itemref idref="fam' + family.getName() + '"/>';
157
+ }
129
158
 
130
- renderSpineElements() {
131
- let xml = "";
159
+ // Add taxa.
160
+ for (let index = 0; index < this.#taxa.getTaxonList().length; index++) {
161
+ xml += '<itemref idref="t' + index + '"/>';
162
+ }
132
163
 
133
- // Add lists.
134
- for (const color of this.#taxa.getFlowerColors()) {
135
- xml += '<itemref idref="l' + color.getColorName() + '"/>';
136
- }
137
- xml += PageListFlowers.getSpineEntries();
138
-
139
- xml += '<itemref idref="lfamilies"/>';
140
- xml += '<itemref idref="lspecies"/>';
141
-
142
- // Add families.
143
- for (const family of this.#taxa.getFamilies().getFamilies()) {
144
- const taxa = family.getTaxa();
145
- if (!taxa) {
146
- continue;
147
- }
148
- xml += '<itemref idref="fam' + family.getName() + '"/>';
149
- }
164
+ xml += this.#glossary.getSpineEntries();
150
165
 
151
- // Add taxa.
152
- for (let index = 0; index < this.#taxa.getTaxonList().length; index++) {
153
- xml += '<itemref idref="t' + index + '"/>';
166
+ return xml;
154
167
  }
155
-
156
- xml += this.#glossary.getSpineEntries();
157
-
158
- return xml;
159
- }
160
168
  }
161
169
 
162
170
  /**
@@ -165,11 +173,11 @@ class PlantBook extends EBook {
165
173
  * @returns {string}
166
174
  */
167
175
  function getRequiredConfigValue(config, name) {
168
- const value = config.getConfigValue("ebook", name);
169
- if (value === undefined) {
170
- throw new Error(`Failed to find ebook config for ${name}`);
171
- }
172
- return value;
176
+ const value = config.getConfigValue("ebook", name);
177
+ if (value === undefined) {
178
+ throw new Error(`Failed to find ebook config for ${name}`);
179
+ }
180
+ return value;
173
181
  }
174
182
 
175
183
  export { PlantBook };
package/lib/htmltaxon.js CHANGED
@@ -7,141 +7,148 @@ import { TextUtils } from "./textutils.js";
7
7
  * @type {Record<string,{title:string,data:function (Taxon):string}>}
8
8
  */
9
9
  const TAXA_LIST_COLS = {
10
- CESA: {
11
- title: "California",
12
- data: (t) => RarePlants.getCESADescription(t.getCESA()),
13
- },
14
- COMMON_NAME: {
15
- title: "Common Name",
16
- data: (t) => t.getCommonNames().join(", "),
17
- },
18
- CNPS_RANK: {
19
- title: "CNPS Rank",
20
- data: (t) =>
21
- HTML.getToolTip(
22
- HTML.textElement("span", t.getRPIRankAndThreat()),
23
- t.getRPIRankAndThreatTooltip()
24
- ),
25
- },
26
- FESA: {
27
- title: "Federal",
28
- data: (t) => RarePlants.getFESADescription(t.getFESA()),
29
- },
30
- SPECIES: {
31
- title: "Species",
32
- data: (t) => t.getHTMLLink(true, true),
33
- },
34
- SPECIES_BARE: {
35
- title: "Species",
36
- data: (t) => t.getHTMLLink(true, false),
37
- },
10
+ CESA: {
11
+ title: "California",
12
+ data: (t) => RarePlants.getCESADescription(t.getCESA()),
13
+ },
14
+ COMMON_NAME: {
15
+ title: "Common Name",
16
+ data: (t) => t.getCommonNames().join(", "),
17
+ },
18
+ CNPS_RANK: {
19
+ title: "CNPS Rank",
20
+ data: (t) =>
21
+ HTML.getToolTip(
22
+ HTML.textElement("span", t.getRPIRankAndThreat()),
23
+ t.getRPIRankAndThreatTooltip(),
24
+ ),
25
+ },
26
+ FESA: {
27
+ title: "Federal",
28
+ data: (t) => RarePlants.getFESADescription(t.getFESA()),
29
+ },
30
+ SPECIES: {
31
+ title: "Species",
32
+ data: (t) => t.getHTMLLink(true, true),
33
+ },
34
+ SPECIES_BARE: {
35
+ title: "Species",
36
+ data: (t) => t.getHTMLLink(true, false),
37
+ },
38
38
  };
39
39
 
40
40
  const DEFAULT_TAXA_COLUMNS = [
41
- TAXA_LIST_COLS.SPECIES,
42
- TAXA_LIST_COLS.COMMON_NAME,
41
+ TAXA_LIST_COLS.SPECIES,
42
+ TAXA_LIST_COLS.COMMON_NAME,
43
43
  ];
44
44
 
45
45
  class HTMLTaxon {
46
- /**
47
- * @param {string[]|undefined} colors
48
- */
49
- static getFlowerColors(colors, includeColorLink = true) {
50
- let html = "";
51
- if (colors) {
52
- for (const color of colors) {
53
- const img = HTML.textElement("img", "", {
54
- src: "./i/f-" + color + ".svg",
55
- alt: color + " flowers",
56
- title: color,
57
- class: "flr-color",
58
- });
59
- if (includeColorLink) {
60
- html += HTML.wrap("a", img, {
61
- href: "./list_fc_" + color + ".html",
62
- });
63
- } else {
64
- html += img;
46
+ /**
47
+ * @param {string[]|undefined} colors
48
+ */
49
+ static getFlowerColors(colors, includeColorLink = true) {
50
+ let html = "";
51
+ if (colors) {
52
+ for (const color of colors) {
53
+ const img = HTML.textElement("img", "", {
54
+ src: "./i/f-" + color + ".svg",
55
+ alt: color + " flowers",
56
+ title: color,
57
+ class: "flr-color",
58
+ });
59
+ if (includeColorLink) {
60
+ html += HTML.wrap("a", img, {
61
+ href: "./list_fc_" + color + ".html",
62
+ });
63
+ } else {
64
+ html += img;
65
+ }
66
+ }
65
67
  }
66
- }
68
+ return html;
67
69
  }
68
- return html;
69
- }
70
70
 
71
- /**
72
- * @param {Taxon} taxon
73
- * @param {string} classNames
74
- * @param {boolean} [includeColorLink=true]
75
- */
76
- static getFlowerInfo(taxon, classNames = "section", includeColorLink = true) {
77
- const lifeCycle = taxon.getLifeCycle();
78
- const colors = taxon.getFlowerColors();
79
- const monthStart = taxon.getBloomStart();
80
- const monthEnd = taxon.getBloomEnd();
71
+ /**
72
+ * @param {Taxon} taxon
73
+ * @param {string} classNames
74
+ * @param {boolean} [includeColorLink=true]
75
+ */
76
+ static getFlowerInfo(
77
+ taxon,
78
+ classNames = "section",
79
+ includeColorLink = true,
80
+ ) {
81
+ const lifeCycle = taxon.getLifeCycle();
82
+ const colors = taxon.getFlowerColors();
83
+ const monthStart = taxon.getBloomStart();
84
+ const monthEnd = taxon.getBloomEnd();
81
85
 
82
- const parts = [];
83
- if (lifeCycle) {
84
- const text = HTML.wrap("span", TextUtils.ucFirst(lifeCycle), "lc") + ".";
85
- parts.push(HTML.wrap("span", text, "lcs"));
86
+ const parts = [];
87
+ if (lifeCycle) {
88
+ const text =
89
+ HTML.wrap("span", TextUtils.ucFirst(lifeCycle), "lc") + ".";
90
+ parts.push(HTML.wrap("span", text, "lcs"));
91
+ }
92
+
93
+ if (colors || monthStart) {
94
+ let html = "Flowers: ";
95
+ html += this.getFlowerColors(colors, includeColorLink);
96
+ if (monthStart && monthEnd) {
97
+ html += HTML.wrap(
98
+ "span",
99
+ DateUtils.getMonthName(monthStart) +
100
+ "-" +
101
+ DateUtils.getMonthName(monthEnd),
102
+ { class: "flr-time" },
103
+ );
104
+ }
105
+ parts.push(HTML.wrap("span", html));
106
+ }
107
+ return parts.length === 0
108
+ ? ""
109
+ : HTML.wrap("div", parts.join(""), { class: classNames });
86
110
  }
87
111
 
88
- if (colors || monthStart) {
89
- let html = "Flowers: ";
90
- html += this.getFlowerColors(colors, includeColorLink);
91
- if (monthStart && monthEnd) {
92
- html += HTML.wrap(
93
- "span",
94
- DateUtils.getMonthName(monthStart) +
95
- "-" +
96
- DateUtils.getMonthName(monthEnd),
97
- { class: "flr-time" }
112
+ /**
113
+ * @param {Taxon} taxon
114
+ */
115
+ static getLink(taxon) {
116
+ return (
117
+ HTML.getLink(taxon.getFileName(), taxon.getName()) +
118
+ this.getFlowerColors(taxon.getFlowerColors())
98
119
  );
99
- }
100
- parts.push(HTML.wrap("span", html));
101
120
  }
102
- return HTML.wrap("div", parts.join(""), { class: classNames });
103
- }
104
121
 
105
- /**
106
- * @param {Taxon} taxon
107
- */
108
- static getLink(taxon) {
109
- return (
110
- HTML.getLink(taxon.getFileName(), taxon.getName()) +
111
- this.getFlowerColors(taxon.getFlowerColors())
112
- );
113
- }
114
-
115
- /**
116
- * @param {Taxon[]} taxa
117
- * @param {TaxaCol[]} [columns]
118
- */
119
- static getTaxaTable(taxa, columns = DEFAULT_TAXA_COLUMNS) {
120
- let html = "<table><thead>";
121
- for (const col of columns) {
122
- const className = col.class;
123
- const atts = className !== undefined ? className : {};
124
- html += HTML.textElement("th", col.title, atts);
125
- }
126
- html += "</thead>";
127
- html += "<tbody>";
122
+ /**
123
+ * @param {Taxon[]} taxa
124
+ * @param {TaxaCol[]} [columns]
125
+ */
126
+ static getTaxaTable(taxa, columns = DEFAULT_TAXA_COLUMNS) {
127
+ let html = "<table><thead>";
128
+ for (const col of columns) {
129
+ const className = col.class;
130
+ const atts = className !== undefined ? className : {};
131
+ html += HTML.textElement("th", col.title, atts);
132
+ }
133
+ html += "</thead>";
134
+ html += "<tbody>";
128
135
 
129
- for (const taxon of taxa) {
130
- html += "<tr>";
131
- for (const col of columns) {
132
- const data = col.data(taxon);
133
- const className = col.class;
134
- const atts = className !== undefined ? className : {};
135
- html += HTML.wrap("td", data, atts);
136
- }
137
- html += "</tr>";
138
- }
136
+ for (const taxon of taxa) {
137
+ html += "<tr>";
138
+ for (const col of columns) {
139
+ const data = col.data(taxon);
140
+ const className = col.class;
141
+ const atts = className !== undefined ? className : {};
142
+ html += HTML.wrap("td", data, atts);
143
+ }
144
+ html += "</tr>";
145
+ }
139
146
 
140
- html += "</tbody>";
141
- html += "</table>";
147
+ html += "</tbody>";
148
+ html += "</table>";
142
149
 
143
- return html;
144
- }
150
+ return html;
151
+ }
145
152
  }
146
153
 
147
154
  export { HTMLTaxon, TAXA_LIST_COLS };
package/lib/inat_photo.js CHANGED
@@ -1,10 +1,4 @@
1
- import {
2
- CC0,
3
- CC_BY,
4
- CC_BY_NC,
5
- COPYRIGHT,
6
- Photo,
7
- } from "./photo.js";
1
+ import { CC0, CC_BY, CC_BY_NC, COPYRIGHT, Photo } from "./photo.js";
8
2
 
9
3
  class InatPhoto extends Photo {
10
4
  /** @type {number} */
@@ -18,17 +12,25 @@ class InatPhoto extends Photo {
18
12
  * @param {InatLicenseCode} licenseCode
19
13
  * @param {string} attrName
20
14
  */
21
- constructor( id, ext, licenseCode, attrName ) {
15
+ constructor(id, ext, licenseCode, attrName) {
22
16
  /** @type {typeof COPYRIGHT | typeof CC_BY | typeof CC_BY_NC | typeof CC0} */
23
17
  let rights = COPYRIGHT;
24
- if ( licenseCode === "cc0" ) rights = CC0;
25
- else if ( licenseCode === "cc-by" ) rights = CC_BY;
26
- else if ( licenseCode === "cc-by-nc" ) rights = CC_BY_NC;
27
- super( null, attrName, rights );
18
+ if (licenseCode === "cc0") rights = CC0;
19
+ else if (licenseCode === "cc-by") rights = CC_BY;
20
+ else if (licenseCode === "cc-by-nc") rights = CC_BY_NC;
21
+ super(null, attrName, rights);
28
22
  this.inatPhotoId = id;
29
23
  this.ext = ext;
30
24
  }
31
25
 
26
+ getExt() {
27
+ return this.ext;
28
+ }
29
+
30
+ getId() {
31
+ return this.inatPhotoId;
32
+ }
33
+
32
34
  getUrl() {
33
35
  return `https://inaturalist-open-data.s3.amazonaws.com/photos/${this.inatPhotoId}/medium.${this.ext}`;
34
36
  }
@@ -38,6 +40,4 @@ class InatPhoto extends Photo {
38
40
  }
39
41
  }
40
42
 
41
- export {
42
- InatPhoto
43
- };
43
+ export { InatPhoto };
@@ -0,0 +1,29 @@
1
+ import cliProgress from "cli-progress";
2
+
3
+ export class ProgressMeter {
4
+ #meter;
5
+
6
+ /**
7
+ * @param {string} label
8
+ * @param {number} total
9
+ */
10
+ constructor(label, total) {
11
+ this.#meter = new cliProgress.SingleBar({
12
+ format: `${label} {percentage}% | {value}/{total}{custom}`,
13
+ hideCursor: true,
14
+ });
15
+ this.#meter.start(total, 0, { custom: "" });
16
+ }
17
+
18
+ stop() {
19
+ this.#meter.stop();
20
+ }
21
+
22
+ /**
23
+ * @param {number} current
24
+ * @param {{ custom: string; }} [custom={ custom: "" }]
25
+ */
26
+ update(current, custom = { custom: "" }) {
27
+ this.#meter.update(current, custom);
28
+ }
29
+ }