@ca-plant-list/ca-plant-list 0.4.18 → 0.4.19

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.
@@ -4044,6 +4044,10 @@ Oxalis pes-caprae,6503168,jpeg,cc-by-nc,James Bailey
4044
4044
  Oxalis pilosa,8865145,jpg,cc-by-nc,Carol Blaney
4045
4045
  Oxalis pilosa,8865151,jpg,cc-by-nc,Carol Blaney
4046
4046
  Oxalis pilosa,8865149,jpg,cc-by-nc,Carol Blaney
4047
+ Oxalis smalliana,218331103,jpeg,cc-by-nc,Sadie Hickey
4048
+ Oxalis smalliana,353081201,jpg,cc-by-nc,ardouglas
4049
+ Oxalis smalliana,218331090,jpeg,cc-by-nc,Sadie Hickey
4050
+ Oxalis smalliana,294594629,jpeg,cc-by,Ed Alverson
4047
4051
  Packera breweri,37358638,jpg,cc-by-nc,Stacie Wolny
4048
4052
  Packera breweri,16970444,jpg,cc-by-nc,Stacie Wolny
4049
4053
  Packera breweri,37358664,jpg,cc-by-nc,Stacie Wolny
@@ -0,0 +1 @@
1
+ Frond narrows at base (not triangle shaped). Sori not sunken.
@@ -0,0 +1 @@
1
+ Frond not hairy on center rib. Frond brittle.
@@ -6,8 +6,8 @@ import { GlossaryPages } from "./web/glossarypages.js";
6
6
  class BasePageRenderer {
7
7
  /**
8
8
  * @param {string} outputDir
9
- * @param {Taxa} taxa
10
- * @param {TaxaCol[]} [familyCols]
9
+ * @param {import("./taxa.js").Taxa} taxa
10
+ * @param {import("./htmltaxon.js").TaxaColDef[]} [familyCols]
11
11
  */
12
12
  static renderBasePages(outputDir, taxa, familyCols) {
13
13
  const siteGenerator = new Jekyll(outputDir);
@@ -30,7 +30,7 @@ class BasePageRenderer {
30
30
 
31
31
  /**
32
32
  * @param {string} outputDir
33
- * @param {Taxa} taxa
33
+ * @param {import("./taxa.js").Taxa} taxa
34
34
  */
35
35
  static renderTools(outputDir, taxa) {
36
36
  const names = [];
@@ -23,7 +23,7 @@ class Images {
23
23
  }
24
24
 
25
25
  /**
26
- * @param {Taxon[]} taxa
26
+ * @param {import("../taxon.js").Taxon[]} taxa
27
27
  */
28
28
  async createImages(taxa) {
29
29
  const meter = new ProgressMeter("processing photos", taxa.length);
@@ -102,7 +102,7 @@ class Images {
102
102
  }
103
103
 
104
104
  /**
105
- * @param {Photo} photo
105
+ * @param {import("../photo.js").Photo} photo
106
106
  * @returns {string}
107
107
  */
108
108
  getCompressedFilePath(photo) {
@@ -110,7 +110,7 @@ class Images {
110
110
  }
111
111
 
112
112
  /**
113
- * @param {Photo} photo
113
+ * @param {import("../photo.js").Photo} photo
114
114
  * @returns {string}
115
115
  */
116
116
  getCompressedImageName(photo) {
@@ -118,8 +118,8 @@ class Images {
118
118
  }
119
119
 
120
120
  /**
121
- * @param {Taxon} taxon
122
- * @returns {Photo[]}
121
+ * @param {import("../taxon.js").Taxon} taxon
122
+ * @returns {import("../photo.js").Photo[]}
123
123
  */
124
124
  static getTaxonPhotos(taxon) {
125
125
  const photos = taxon
@@ -8,7 +8,7 @@ class PageListSpecies extends EBookPage {
8
8
  /**
9
9
  *
10
10
  * @param {string} outputDir
11
- * @param {Taxon[]} taxa
11
+ * @param {import("../../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 {Taxon} taxon
16
+ * @param {import("../../taxon.js").Taxon} taxon
17
17
  * @param {Images} images
18
18
  */
19
19
  constructor(outputDir, taxon, images) {
@@ -69,7 +69,7 @@ class TaxonPage extends EBookPage {
69
69
  src: `i/${this.#images.getCompressedImageName(photo)}`,
70
70
  style: "max-width:" + dimensions.width + "px",
71
71
  });
72
- const caption = `${photo.rights === "CC0" ? "By" : "(c)"} ${photo.rightsHolder} ${photo.rights && `(${photo.rights})`}`;
72
+ const caption = photo.getAttribution();
73
73
  if (caption) {
74
74
  img += XHTML.textElement("figcaption", caption);
75
75
  }
@@ -18,7 +18,7 @@ class PlantBook extends EBook {
18
18
  /**
19
19
  * @param {string} outputDir
20
20
  * @param {Config} config
21
- * @param {Taxa} taxa
21
+ * @param {import("../taxa.js").Taxa} taxa
22
22
  */
23
23
  constructor(outputDir, config, taxa) {
24
24
  super(
package/lib/families.js CHANGED
@@ -19,7 +19,7 @@ class Family {
19
19
  }
20
20
 
21
21
  /**
22
- * @param {Taxon} taxon
22
+ * @param {import("./taxon.js").Taxon} taxon
23
23
  */
24
24
  addTaxon(taxon) {
25
25
  if (!this.#data.taxa) {
@@ -68,7 +68,7 @@ class Families {
68
68
 
69
69
  getFamilies() {
70
70
  return Object.values(this.#families).sort((a, b) =>
71
- a.getName().localeCompare(b.getName())
71
+ a.getName().localeCompare(b.getName()),
72
72
  );
73
73
  }
74
74
 
@@ -81,7 +81,7 @@ class Families {
81
81
 
82
82
  /**
83
83
  * @param {string} outputDir
84
- * @param {TaxaCol[]} taxaColumns
84
+ * @param {import("./htmltaxon.js").TaxaColDef[]} [taxaColumns]
85
85
  */
86
86
  renderPages(outputDir, taxaColumns) {
87
87
  new PageFamilyList(outputDir, this.#families).render(taxaColumns);
@@ -109,7 +109,7 @@ class PageFamilyList extends GenericPage {
109
109
  }
110
110
 
111
111
  /**
112
- * @param {TaxaCol[]} taxaColumns
112
+ * @param {import("./htmltaxon.js").TaxaColDef[]} [taxaColumns]
113
113
  */
114
114
  render(taxaColumns) {
115
115
  let html = this.getDefaultIntro();
@@ -121,13 +121,13 @@ class PageFamilyList extends GenericPage {
121
121
 
122
122
  // Render the section page.
123
123
  new PageSection(this.getOutputDir(), name, taxa).render(
124
- taxaColumns
124
+ taxaColumns,
125
125
  );
126
126
 
127
127
  // Render the link.
128
128
  const href = "./" + name + ".html";
129
129
  sectionLinks.push(
130
- HTML.getLink(href, name) + " (" + taxa.length + ")"
130
+ HTML.getLink(href, name) + " (" + taxa.length + ")",
131
131
  );
132
132
  }
133
133
  html += HTML.wrap("ul", HTML.arrayToLI(sectionLinks), {
@@ -150,7 +150,7 @@ class PageFamilyList extends GenericPage {
150
150
  }
151
151
  let cols = HTML.wrap(
152
152
  "td",
153
- HTML.getLink("./" + family.getFileName(), family.getName())
153
+ HTML.getLink("./" + family.getFileName(), family.getName()),
154
154
  );
155
155
  cols += HTML.wrap("td", taxa.length, { class: "right" });
156
156
  html += HTML.wrap("tr", cols);
@@ -176,7 +176,7 @@ class PageFamily extends GenericPage {
176
176
  }
177
177
 
178
178
  /**
179
- * @param {TaxaCol[]} columns
179
+ * @param {import("./htmltaxon.js").TaxaColDef[]} [columns]
180
180
  */
181
181
  render(columns) {
182
182
  let html = this.getDefaultIntro();
@@ -184,7 +184,7 @@ class PageFamily extends GenericPage {
184
184
  html += HTML.wrap(
185
185
  "div",
186
186
  Jepson.getEFloraLink(this.#family.getJepsonID()),
187
- { class: "section" }
187
+ { class: "section" },
188
188
  );
189
189
 
190
190
  html += HTMLTaxon.getTaxaTable(this.#family.getTaxa(), columns);
@@ -199,7 +199,7 @@ class PageSection extends GenericPage {
199
199
  /**
200
200
  * @param {string} outputDir
201
201
  * @param {string} name
202
- * @param {Taxon[]} taxa
202
+ * @param {import("./taxon.js").Taxon[]} taxa
203
203
  */
204
204
  constructor(outputDir, name, taxa) {
205
205
  super(outputDir, name, name);
@@ -207,7 +207,7 @@ class PageSection extends GenericPage {
207
207
  }
208
208
 
209
209
  /**
210
- * @param {TaxaCol[]} [columns]
210
+ * @param {import("./htmltaxon.js").TaxaColDef[]} [columns]
211
211
  */
212
212
  render(columns) {
213
213
  let html = this.getDefaultIntro();
@@ -219,12 +219,12 @@ class PageSection extends GenericPage {
219
219
  }
220
220
 
221
221
  class Sections {
222
- /** @type {Object<string,Taxon[]>} */
222
+ /** @type {Object<string,import("./taxon.js").Taxon[]>} */
223
223
  static #sections = {};
224
224
 
225
225
  /**
226
226
  * @param {string} name
227
- * @param {Taxon} taxon
227
+ * @param {import("./taxon.js").Taxon} taxon
228
228
  */
229
229
  static addTaxon(name, taxon) {
230
230
  let section = this.#sections[name];
package/lib/genera.js CHANGED
@@ -15,7 +15,7 @@ class Genera {
15
15
  }
16
16
 
17
17
  /**
18
- * @param {Taxon} taxon
18
+ * @param {import("./taxon.js").Taxon} taxon
19
19
  */
20
20
  addTaxon(taxon) {
21
21
  const genusName = taxon.getGenusName();
package/lib/htmltaxon.js CHANGED
@@ -6,7 +6,15 @@ import { RarePlants } from "./rareplants.js";
6
6
  import { TextUtils } from "./textutils.js";
7
7
 
8
8
  /**
9
- * @type {Record<string,{title:string,data:function (Taxon):string}>}
9
+ * @typedef {{
10
+ class?: string;
11
+ data: (taxon: import("./taxon.js").Taxon) => string;
12
+ title: string;
13
+ }} TaxaColDef
14
+ */
15
+
16
+ /**
17
+ * @type {Record<string,TaxaColDef>}
10
18
  */
11
19
  const TAXA_LIST_COLS = {
12
20
  CESA: {
@@ -39,6 +47,7 @@ const TAXA_LIST_COLS = {
39
47
  },
40
48
  };
41
49
 
50
+ /** @type {TaxaColDef[]} */
42
51
  const DEFAULT_TAXA_COLUMNS = [
43
52
  TAXA_LIST_COLS.SPECIES,
44
53
  TAXA_LIST_COLS.COMMON_NAME,
@@ -46,7 +55,7 @@ const DEFAULT_TAXA_COLUMNS = [
46
55
 
47
56
  class HTMLTaxon {
48
57
  /**
49
- * @param {Taxon} taxon
58
+ * @param {import("./taxon.js").Taxon} taxon
50
59
  * @returns {string|undefined}
51
60
  */
52
61
  static getCalscapeLink(taxon) {
@@ -88,7 +97,7 @@ class HTMLTaxon {
88
97
  }
89
98
 
90
99
  /**
91
- * @param {Taxon} taxon
100
+ * @param {import("./taxon.js").Taxon} taxon
92
101
  * @param {string} classNames
93
102
  * @param {boolean} [includeColorLink=true]
94
103
  */
@@ -129,7 +138,7 @@ class HTMLTaxon {
129
138
  }
130
139
 
131
140
  /**
132
- * @param {Taxon} taxon
141
+ * @param {import("./taxon.js").Taxon} taxon
133
142
  * @returns {string}
134
143
  */
135
144
  static getFooterHTML(taxon) {
@@ -142,7 +151,7 @@ class HTMLTaxon {
142
151
  }
143
152
 
144
153
  /**
145
- * @param {Taxon} taxon
154
+ * @param {import("./taxon.js").Taxon} taxon
146
155
  */
147
156
  static getLink(taxon) {
148
157
  return (
@@ -182,8 +191,8 @@ class HTMLTaxon {
182
191
  }
183
192
 
184
193
  /**
185
- * @param {Taxon[]} taxa
186
- * @param {TaxaCol[]} [columns]
194
+ * @param {import("./taxon.js").Taxon[]} taxa
195
+ * @param {TaxaColDef[]} [columns]
187
196
  */
188
197
  static getTaxaTable(taxa, columns = DEFAULT_TAXA_COLUMNS) {
189
198
  let html = "<table><thead>";
package/lib/index.d.ts CHANGED
@@ -79,6 +79,16 @@ export class Jekyll {
79
79
  static include(fileName: string): string;
80
80
  }
81
81
 
82
+ type PhotoRights = "CC0" | "CC BY" | "CC BY-NC" | "C" | null;
83
+
84
+ export class Photo {
85
+ getAttribution(): string;
86
+ getExt(): string;
87
+ getId(): number;
88
+ getSourceUrl(): string;
89
+ getUrl(): string;
90
+ }
91
+
82
92
  export class Program {
83
93
  static getIncludeList(dataDir: string): string[];
84
94
  static getProgram(): Command;
@@ -101,14 +111,19 @@ export class Taxon {
101
111
  getBaseFileName(): string;
102
112
  getCalfloraID(): string;
103
113
  getCalfloraTaxonLink(): string;
114
+ getCESA(): string | undefined;
115
+ getCNDDBRank(): string | undefined;
104
116
  getCommonNames(): string[];
105
117
  getFamily(): Family;
118
+ getFESA(): string | undefined;
106
119
  getGenus(): Genus;
107
120
  getGenusName(): string;
121
+ getGlobalRank(): string | undefined;
108
122
  getINatID(): string;
109
123
  getINatTaxonLink(): string;
110
124
  getJepsonID(): string;
111
125
  getName(): string;
126
+ getPhotos(): Photo[];
112
127
  getRPIRankAndThreat(): string;
113
128
  getRPITaxonLink(): string;
114
129
  getSynonyms(): string[];
@@ -7,259 +7,262 @@ import { HTML } from "./html.js";
7
7
  import { HTMLTaxon, TAXA_LIST_COLS } from "./htmltaxon.js";
8
8
 
9
9
  const ENDANGERED_COLS = [
10
- TAXA_LIST_COLS.SPECIES,
11
- TAXA_LIST_COLS.COMMON_NAME,
12
- TAXA_LIST_COLS.CESA,
13
- TAXA_LIST_COLS.FESA,
10
+ TAXA_LIST_COLS.SPECIES,
11
+ TAXA_LIST_COLS.COMMON_NAME,
12
+ TAXA_LIST_COLS.CESA,
13
+ TAXA_LIST_COLS.FESA,
14
14
  ];
15
15
  const RPI_COLUMNS = [
16
- TAXA_LIST_COLS.SPECIES_BARE,
17
- TAXA_LIST_COLS.COMMON_NAME,
18
- TAXA_LIST_COLS.CNPS_RANK,
16
+ TAXA_LIST_COLS.SPECIES_BARE,
17
+ TAXA_LIST_COLS.COMMON_NAME,
18
+ TAXA_LIST_COLS.CNPS_RANK,
19
19
  ];
20
20
 
21
21
  class PageRenderer extends BasePageRenderer {
22
- /**
23
- * @param {string} outputDir
24
- * @param {import('./config.js').Config} config
25
- * @param {Taxa} taxa
26
- */
27
- static render(outputDir, config, taxa) {
28
- super.renderBasePages(outputDir, taxa);
22
+ /**
23
+ * @param {string} outputDir
24
+ * @param {import('./config.js').Config} config
25
+ * @param {import("./taxa.js").Taxa} taxa
26
+ */
27
+ static render(outputDir, config, taxa) {
28
+ super.renderBasePages(outputDir, taxa);
29
29
 
30
- this.renderLists(outputDir, config, taxa);
30
+ this.renderLists(outputDir, config, taxa);
31
31
 
32
- const taxonList = taxa.getTaxonList();
33
- for (const taxon of taxonList) {
34
- new PageTaxon(outputDir, config, taxon).render();
32
+ const taxonList = taxa.getTaxonList();
33
+ for (const taxon of taxonList) {
34
+ new PageTaxon(outputDir, config, taxon).render();
35
+ }
35
36
  }
36
- }
37
37
 
38
- /**
39
- * @param {string} outputDir
40
- * @param {Config} config
41
- * @param {Taxa} taxa
42
- */
43
- static renderLists(outputDir, config, taxa) {
44
38
  /**
45
- * @param {ListInfo[]} listInfo
46
- * @param {Object<string,string>} attributes
47
- * @param {TaxaCol[]} [columns]
48
- * @returns {string}
39
+ * @param {string} outputDir
40
+ * @param {import('./config.js').Config} config
41
+ * @param {import("./taxa.js").Taxa} taxa
49
42
  */
50
- function getListArray(listInfo, attributes = {}, columns) {
51
- const listArray = [];
52
- for (const list of listInfo) {
53
- const listTaxa = [];
54
- const calfloraTaxa = [];
55
- const iNatTaxa = [];
56
- for (const taxon of taxa.getTaxonList()) {
57
- if (list.include(taxon)) {
58
- listTaxa.push(taxon);
59
- calfloraTaxa.push(taxon.getCalfloraName());
60
- iNatTaxa.push(taxon.getINatName());
61
- }
62
- }
43
+ static renderLists(outputDir, config, taxa) {
44
+ /**
45
+ * @param {ListInfo[]} listInfo
46
+ * @param {Object<string,string>} attributes
47
+ * @param {import("./htmltaxon.js").TaxaColDef[]} [columns]
48
+ * @returns {string}
49
+ */
50
+ function getListArray(listInfo, attributes = {}, columns) {
51
+ const listArray = [];
52
+ for (const list of listInfo) {
53
+ const listTaxa = [];
54
+ const calfloraTaxa = [];
55
+ const iNatTaxa = [];
56
+ for (const taxon of taxa.getTaxonList()) {
57
+ if (list.include(taxon)) {
58
+ listTaxa.push(taxon);
59
+ calfloraTaxa.push(taxon.getCalfloraName());
60
+ iNatTaxa.push(taxon.getINatName());
61
+ }
62
+ }
63
63
 
64
- if (listTaxa.length === 0) {
65
- continue;
66
- }
64
+ if (listTaxa.length === 0) {
65
+ continue;
66
+ }
67
67
 
68
- Files.write(
69
- outputDir + "/calflora_" + list.filename + ".txt",
70
- calfloraTaxa.join("\n")
71
- );
72
- Files.write(
73
- outputDir + "/inat_" + list.filename + ".txt",
74
- iNatTaxa.join("\n")
75
- );
68
+ Files.write(
69
+ outputDir + "/calflora_" + list.filename + ".txt",
70
+ calfloraTaxa.join("\n"),
71
+ );
72
+ Files.write(
73
+ outputDir + "/inat_" + list.filename + ".txt",
74
+ iNatTaxa.join("\n"),
75
+ );
76
76
 
77
- const cols = columns ? columns : list.columns;
78
- new PageTaxonList(outputDir, list.name, list.filename).render(
79
- listTaxa,
80
- cols
81
- );
77
+ const cols = columns ? columns : list.columns;
78
+ new PageTaxonList(outputDir, list.name, list.filename).render(
79
+ listTaxa,
80
+ cols,
81
+ );
82
82
 
83
- // Check for sublists.
84
- const subListHTML = list.listInfo
85
- ? getListArray(list.listInfo, { class: "indent" }, cols)
86
- : "";
83
+ // Check for sublists.
84
+ const subListHTML = list.listInfo
85
+ ? getListArray(list.listInfo, { class: "indent" }, cols)
86
+ : "";
87
87
 
88
- listArray.push(
89
- HTML.getLink("./" + list.filename + ".html", list.name) +
90
- " (" +
91
- listTaxa.length +
92
- ")" +
93
- subListHTML
94
- );
95
- }
88
+ listArray.push(
89
+ HTML.getLink("./" + list.filename + ".html", list.name) +
90
+ " (" +
91
+ listTaxa.length +
92
+ ")" +
93
+ subListHTML,
94
+ );
95
+ }
96
96
 
97
- return renderList(listArray, attributes);
98
- }
97
+ return renderList(listArray, attributes);
98
+ }
99
99
 
100
- /**
101
- * @param {string[]} listsHTML
102
- * @param {Object<string,string>} attributes
103
- */
104
- function renderList(listsHTML, attributes = {}) {
105
- return HTML.wrap("ul", HTML.arrayToLI(listsHTML), attributes);
106
- }
100
+ /**
101
+ * @param {string[]} listsHTML
102
+ * @param {Object<string,string>} attributes
103
+ */
104
+ function renderList(listsHTML, attributes = {}) {
105
+ return HTML.wrap("ul", HTML.arrayToLI(listsHTML), attributes);
106
+ }
107
107
 
108
- /**
109
- * @param {string} title
110
- * @param {string} listsHTML
111
- */
112
- function renderSection(title, listsHTML) {
113
- let html = '<div class="section nobullet">';
114
- html += HTML.textElement("h2", title);
115
- html += listsHTML;
116
- html += "</div>";
117
- return html;
118
- }
108
+ /**
109
+ * @param {string} title
110
+ * @param {string} listsHTML
111
+ */
112
+ function renderSection(title, listsHTML) {
113
+ let html = '<div class="section nobullet">';
114
+ html += HTML.textElement("h2", title);
115
+ html += listsHTML;
116
+ html += "</div>";
117
+ return html;
118
+ }
119
119
 
120
- /** @typedef {{name:string,filename:string,include:function(Taxon):boolean,columns?:TaxaCol[],listInfo?:ListInfo[]}} ListInfo */
121
- /** @type {{title:string,listInfo:ListInfo[]}[]} */
122
- const sections = [
123
- {
124
- title: "All Species",
125
- listInfo: [
126
- {
127
- name: config.getLabel("native", "Native"),
128
- filename: "list_native",
129
- include: (t) => t.isNative(),
130
- },
131
- {
132
- name: config.getLabel("introduced", "Introduced"),
133
- filename: "list_introduced",
134
- include: (t) => !t.isNative(),
135
- },
136
- {
137
- name: "All Plants",
138
- filename: "list_all",
139
- include: () => true,
140
- },
141
- ],
142
- },
143
- {
144
- title: "Rare Plants",
145
- listInfo: [
146
- {
147
- name: "CNPS Ranked Plants",
148
- filename: "list_rpi",
149
- include: (t) => t.getRPIRank() !== undefined,
150
- columns: RPI_COLUMNS,
151
- listInfo: [
152
- {
153
- name: RarePlants.getRPIRankDescription("1A"),
154
- filename: "list_rpi_1a",
155
- include: (t) => t.getRPIRank() === "1A",
156
- },
157
- {
158
- name: RarePlants.getRPIRankDescription("1B"),
159
- filename: "list_rpi_1b",
160
- include: (t) => t.getRPIRank() === "1B",
161
- },
162
- {
163
- name: RarePlants.getRPIRankDescription("2A"),
164
- filename: "list_rpi_2a",
165
- include: (t) => t.getRPIRank() === "2A",
166
- },
167
- {
168
- name: RarePlants.getRPIRankDescription("2B"),
169
- filename: "list_rpi_2b",
170
- include: (t) => t.getRPIRank() === "2B",
171
- },
172
- {
173
- name: RarePlants.getRPIRankDescription("3"),
174
- filename: "list_rpi_3",
175
- include: (t) => t.getRPIRank() === "3",
176
- },
177
- {
178
- name: RarePlants.getRPIRankDescription("4"),
179
- filename: "list_rpi_4",
180
- include: (t) => t.getRPIRank() === "4",
181
- },
182
- ],
183
- },
184
- {
185
- name: "Endangered Species",
186
- filename: "list_endangered",
187
- include: (t) =>
188
- t.getCESA() !== undefined || t.getFESA() !== undefined,
189
- columns: ENDANGERED_COLS,
190
- },
191
- ],
192
- },
193
- ];
120
+ /** @typedef {{name:string,filename:string,include:function(import("./taxon.js").Taxon):boolean,columns?:import("./htmltaxon.js").TaxaColDef[],listInfo?:ListInfo[]}} ListInfo */
121
+ /** @type {{title:string,listInfo:ListInfo[]}[]} */
122
+ const sections = [
123
+ {
124
+ title: "All Species",
125
+ listInfo: [
126
+ {
127
+ name: config.getLabel("native", "Native"),
128
+ filename: "list_native",
129
+ include: (t) => t.isNative(),
130
+ },
131
+ {
132
+ name: config.getLabel("introduced", "Introduced"),
133
+ filename: "list_introduced",
134
+ include: (t) => !t.isNative(),
135
+ },
136
+ {
137
+ name: "All Plants",
138
+ filename: "list_all",
139
+ include: () => true,
140
+ },
141
+ ],
142
+ },
143
+ {
144
+ title: "Rare Plants",
145
+ listInfo: [
146
+ {
147
+ name: "CNPS Ranked Plants",
148
+ filename: "list_rpi",
149
+ include: (t) => t.getRPIRank() !== undefined,
150
+ columns: RPI_COLUMNS,
151
+ listInfo: [
152
+ {
153
+ name: RarePlants.getRPIRankDescription("1A"),
154
+ filename: "list_rpi_1a",
155
+ include: (t) => t.getRPIRank() === "1A",
156
+ },
157
+ {
158
+ name: RarePlants.getRPIRankDescription("1B"),
159
+ filename: "list_rpi_1b",
160
+ include: (t) => t.getRPIRank() === "1B",
161
+ },
162
+ {
163
+ name: RarePlants.getRPIRankDescription("2A"),
164
+ filename: "list_rpi_2a",
165
+ include: (t) => t.getRPIRank() === "2A",
166
+ },
167
+ {
168
+ name: RarePlants.getRPIRankDescription("2B"),
169
+ filename: "list_rpi_2b",
170
+ include: (t) => t.getRPIRank() === "2B",
171
+ },
172
+ {
173
+ name: RarePlants.getRPIRankDescription("3"),
174
+ filename: "list_rpi_3",
175
+ include: (t) => t.getRPIRank() === "3",
176
+ },
177
+ {
178
+ name: RarePlants.getRPIRankDescription("4"),
179
+ filename: "list_rpi_4",
180
+ include: (t) => t.getRPIRank() === "4",
181
+ },
182
+ ],
183
+ },
184
+ {
185
+ name: "Endangered Species",
186
+ filename: "list_endangered",
187
+ include: (t) =>
188
+ t.getCESA() !== undefined ||
189
+ t.getFESA() !== undefined,
190
+ columns: ENDANGERED_COLS,
191
+ },
192
+ ],
193
+ },
194
+ ];
194
195
 
195
- let html = '<div class="wrapper">';
196
- for (const section of sections) {
197
- const listHTML = getListArray(section.listInfo);
196
+ let html = '<div class="wrapper">';
197
+ for (const section of sections) {
198
+ const listHTML = getListArray(section.listInfo);
198
199
 
199
- if (listHTML.length > 0) {
200
- html += renderSection(section.title, listHTML);
201
- }
202
- }
203
- html += renderSection(
204
- "Taxonomy",
205
- renderList([HTML.getLink("./list_families.html", "Plant Families")])
206
- );
200
+ if (listHTML.length > 0) {
201
+ html += renderSection(section.title, listHTML);
202
+ }
203
+ }
204
+ html += renderSection(
205
+ "Taxonomy",
206
+ renderList([
207
+ HTML.getLink("./list_families.html", "Plant Families"),
208
+ ]),
209
+ );
207
210
 
208
- html += "</div>";
211
+ html += "</div>";
209
212
 
210
- // Write lists to includes directory so it can be inserted into pages.
211
- Files.write(outputDir + "/_includes/plantlists.html", html);
212
- }
213
+ // Write lists to includes directory so it can be inserted into pages.
214
+ Files.write(outputDir + "/_includes/plantlists.html", html);
215
+ }
213
216
  }
214
217
 
215
218
  class PageTaxonList extends GenericPage {
216
- /**
217
- * @param {string} outputDir
218
- * @param {string} title
219
- * @param {string} baseName
220
- */
221
- constructor(outputDir, title, baseName) {
222
- super(outputDir, title, baseName);
223
- }
219
+ /**
220
+ * @param {string} outputDir
221
+ * @param {string} title
222
+ * @param {string} baseName
223
+ */
224
+ constructor(outputDir, title, baseName) {
225
+ super(outputDir, title, baseName);
226
+ }
224
227
 
225
- /**
226
- *
227
- * @param {Taxon[]} taxa
228
- * @param {TaxaCol[]|undefined} columns
229
- */
230
- render(taxa, columns) {
231
- let html = this.getDefaultIntro();
228
+ /**
229
+ *
230
+ * @param {import("./taxon.js").Taxon[]} taxa
231
+ * @param {import("./htmltaxon.js").TaxaColDef[]|undefined} columns
232
+ */
233
+ render(taxa, columns) {
234
+ let html = this.getDefaultIntro();
232
235
 
233
- html += '<div class="wrapper">';
236
+ html += '<div class="wrapper">';
234
237
 
235
- html += '<div class="section">';
236
- html += HTMLTaxon.getTaxaTable(taxa, columns);
237
- html += "</div>";
238
+ html += '<div class="section">';
239
+ html += HTMLTaxon.getTaxaTable(taxa, columns);
240
+ html += "</div>";
238
241
 
239
- html += '<div class="section nobullet">';
240
- html += HTML.textElement("h2", "Download");
241
- html += "<ul>";
242
- html +=
243
- "<li>" +
244
- HTML.getLink(
245
- "./calflora_" + this.getBaseFileName() + ".txt",
246
- "Calflora List"
247
- ) +
248
- "</li>";
249
- html +=
250
- "<li>" +
251
- HTML.getLink(
252
- "./inat_" + this.getBaseFileName() + ".txt",
253
- "iNaturalist List"
254
- ) +
255
- "</li>";
256
- html += "</ul>";
257
- html += "</div>";
242
+ html += '<div class="section nobullet">';
243
+ html += HTML.textElement("h2", "Download");
244
+ html += "<ul>";
245
+ html +=
246
+ "<li>" +
247
+ HTML.getLink(
248
+ "./calflora_" + this.getBaseFileName() + ".txt",
249
+ "Calflora List",
250
+ ) +
251
+ "</li>";
252
+ html +=
253
+ "<li>" +
254
+ HTML.getLink(
255
+ "./inat_" + this.getBaseFileName() + ".txt",
256
+ "iNaturalist List",
257
+ ) +
258
+ "</li>";
259
+ html += "</ul>";
260
+ html += "</div>";
258
261
 
259
- html += "</div>";
262
+ html += "</div>";
260
263
 
261
- this.writeFile(html);
262
- }
264
+ this.writeFile(html);
265
+ }
263
266
  }
264
267
 
265
268
  export { PageRenderer };
package/lib/photo.js CHANGED
@@ -1,44 +1,74 @@
1
- const CC0 = "CC0";
2
- const CC_BY = "CC BY";
3
- const CC_BY_NC = "CC BY-NC";
4
- const COPYRIGHT = "C";
1
+ /**
2
+ * @typedef {"CC0" | "CC BY" | "CC BY-NC" | "C" | null} PhotoRights
3
+ * @typedef { "cc-by-nc-sa"
4
+ | "cc-by-nc"
5
+ | "cc-by-nc-nd"
6
+ | "cc-by"
7
+ | "cc-by-sa"
8
+ | "cc-by-nd"
9
+ | "pd"
10
+ | "gdfl"
11
+ | "cc0"} InatLicenseCode
12
+ */
5
13
 
6
- class Photo {
7
- /** @type {string?} */
8
- #url;
9
- /** @type {string?} */
10
- rightsHolder;
11
- /** @type {null | typeof COPYRIGHT | typeof CC_BY | typeof CC_BY_NC | typeof CC0} */
12
- rights;
14
+ export class Photo {
15
+ #id;
16
+ #ext;
17
+ #rightsHolder;
18
+ /** @type {PhotoRights} */
19
+ #rights;
13
20
 
14
21
  /**
15
- * @param {string?} url
16
- * @param {string?} rightsHolder
17
- * @param {null | typeof COPYRIGHT | typeof CC_BY | typeof CC_BY_NC | typeof CC0} rights
22
+ * @param {number} id
23
+ * @param {string} ext
24
+ * @param {InatLicenseCode} licenseCode
25
+ * @param {string} rightsHolder
18
26
  */
19
- constructor( url, rightsHolder, rights ) {
20
- this.#url = url;
21
- this.rightsHolder = rightsHolder;
22
- this.rights = rights;
27
+ constructor(id, ext, licenseCode, rightsHolder) {
28
+ this.#id = id;
29
+ this.#ext = ext;
30
+ this.#rightsHolder = rightsHolder;
31
+ if (licenseCode === "cc0") this.#rights = "CC0";
32
+ else if (licenseCode === "cc-by") this.#rights = "CC BY";
33
+ else if (licenseCode === "cc-by-nc") this.#rights = "CC BY-NC";
34
+ else this.#rights = "C";
23
35
  }
24
36
 
25
- getUrl() {
26
- return this.#url;
37
+ /**
38
+ * @returns {string}
39
+ */
40
+ getAttribution() {
41
+ if (this.#rights === "CC0") {
42
+ if (this.#rightsHolder) {
43
+ return `By ${this.#rightsHolder} (${this.#rights})`;
44
+ }
45
+ return this.#rights;
46
+ }
47
+ if (this.#rightsHolder) {
48
+ return `(c) ${this.#rightsHolder} (${this.#rights})`;
49
+ }
50
+ return `(c) (${this.#rights})`;
51
+ }
52
+
53
+ getExt() {
54
+ return this.#ext;
55
+ }
56
+
57
+ getId() {
58
+ return this.#id;
27
59
  }
28
60
 
29
61
  /**
30
- * Return URL of page from whence this photo came
31
- * @return {string?}
62
+ * @returns {string} The URL of the iNaturalist page with details about the image.
32
63
  */
33
64
  getSourceUrl() {
34
- return null;
65
+ return `https://www.inaturalist.org/photos/${this.#id}`;
35
66
  }
36
- }
37
67
 
38
- export {
39
- CC0,
40
- CC_BY,
41
- CC_BY_NC,
42
- COPYRIGHT,
43
- Photo
44
- };
68
+ /**
69
+ * @returns {string} The URL to retrieve the image file.
70
+ */
71
+ getUrl() {
72
+ return `https://inaturalist-open-data.s3.amazonaws.com/photos/${this.#id}/medium.${this.#ext}`;
73
+ }
74
+ }
package/lib/taxa.js CHANGED
@@ -7,10 +7,10 @@ import { Genera } from "./genera.js";
7
7
  import { Taxon } from "./taxon.js";
8
8
  import { Families } from "./families.js";
9
9
  import { FlowerColor } from "./flowercolor.js";
10
- import { InatPhoto } from "./inat_photo.js";
11
10
  import { TaxaCSV } from "./tools/taxacsv.js";
12
11
  import { ErrorLog } from "./errorlog.js";
13
12
  import { Program } from "./program.js";
13
+ import { Photo } from "./photo.js";
14
14
 
15
15
  const FLOWER_COLORS = [
16
16
  { name: "white", color: "white" },
@@ -138,7 +138,7 @@ class Taxa {
138
138
  continue;
139
139
  }
140
140
  taxon.addPhoto(
141
- new InatPhoto(
141
+ new Photo(
142
142
  csvPhoto.id,
143
143
  csvPhoto.ext,
144
144
  csvPhoto.licenseCode,
package/lib/taxon.js CHANGED
@@ -35,12 +35,12 @@ class Taxon {
35
35
  #rankGlobal;
36
36
  /** @type {string[]} */
37
37
  #synonyms = [];
38
- /** @type {Photo[]} */
38
+ /** @type {import("./photo.js").Photo[]}*/
39
39
  #photos = [];
40
40
 
41
41
  /**
42
42
  * @param {TaxonData} data
43
- * @param {Genera} genera
43
+ * @param {import("./genera.js").Genera} genera
44
44
  */
45
45
  constructor(data, genera) {
46
46
  this.#genera = genera;
@@ -98,12 +98,15 @@ class Taxon {
98
98
  }
99
99
 
100
100
  /**
101
- * @param {InatPhoto} photo
101
+ * @param {import("./photo.js").Photo} photo
102
102
  */
103
103
  addPhoto(photo) {
104
104
  this.#photos = this.#photos.concat([photo]);
105
105
  }
106
106
 
107
+ /**
108
+ * @returns {import("./photo.js").Photo[]}
109
+ */
107
110
  getPhotos() {
108
111
  return this.#photos;
109
112
  }
package/lib/tools/rpi.js CHANGED
@@ -12,7 +12,7 @@ class RPI {
12
12
 
13
13
  /**
14
14
  * @param {string} toolsDataDir
15
- * @param {Taxa} taxa
15
+ * @param {import("../taxa.js").Taxa} taxa
16
16
  * @param {Config} config
17
17
  * @param {import("../exceptions.js").Exceptions} exceptions
18
18
  * @param {ErrorLog} errorLog
@@ -202,8 +202,7 @@ class RPI {
202
202
  }
203
203
 
204
204
  /**
205
- *
206
- * @param {Taxa} taxa
205
+ * @param {import("../taxa.js").Taxa} taxa
207
206
  * @param {Config} config
208
207
  * @param {import("../exceptions.js").Exceptions} exceptions
209
208
  * @param {ErrorLog} errorLog
@@ -367,7 +366,7 @@ class RPI {
367
366
 
368
367
  /**
369
368
  * @param {string} toolsDataDir
370
- * @param {Taxa} taxa
369
+ * @param {import("../taxa.js").Taxa} taxa
371
370
  * @param {import("../exceptions.js").Exceptions} exceptions
372
371
  * @param {ErrorLog} errorLog
373
372
  */
@@ -11,8 +11,8 @@ class PageTaxon extends GenericPage {
11
11
 
12
12
  /**
13
13
  * @param {string} outputDir
14
- * @param {Config} config
15
- * @param {Taxon} taxon
14
+ * @param {import("../config.js").Config} config
15
+ * @param {import("../taxon.js").Taxon} taxon
16
16
  */
17
17
  constructor(outputDir, config, taxon) {
18
18
  super(outputDir, taxon.getName(), taxon.getBaseFileName());
@@ -189,7 +189,7 @@ class PageTaxon extends GenericPage {
189
189
 
190
190
  html += HTMLTaxon.getFooterHTML(this.#taxon);
191
191
 
192
- const photos = this.#taxon.getPhotos().slice( 0, 5 );
192
+ const photos = this.#taxon.getPhotos().slice(0, 5);
193
193
  if (photos.length > 0) {
194
194
  let photosHtml = "";
195
195
  for (const photo of photos) {
@@ -202,9 +202,7 @@ class PageTaxon extends GenericPage {
202
202
  />
203
203
  </a>
204
204
  <figcaption>
205
- ${photo.rights === "CC0" ? "By" : "(c)"}
206
- ${photo.rightsHolder}
207
- ${photo.rights && `(${photo.rights})`}
205
+ ${photo.getAttribution()}
208
206
  </figcaption>
209
207
  </figure>
210
208
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ca-plant-list/ca-plant-list",
3
- "version": "0.4.18",
3
+ "version": "0.4.19",
4
4
  "description": "Tools to create Jekyll files for a website listing plants in an area of California.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,8 +22,9 @@
22
22
  },
23
23
  "types": "./lib/index.d.ts",
24
24
  "scripts": {
25
- "check": "npm run eslint && npm run tsc",
25
+ "check": "npm run eslint && npm run tsc && npm run jest",
26
26
  "eslint": "npx eslint",
27
+ "jest": "node --experimental-vm-modules node_modules/jest/bin/jest.js tests",
27
28
  "prettier": "npx prettier -l .",
28
29
  "tsc": "npx tsc"
29
30
  },
@@ -51,10 +52,12 @@
51
52
  "devDependencies": {
52
53
  "@types/archiver": "^6.0.2",
53
54
  "@types/cli-progress": "^3.11.6",
55
+ "@types/jest": "^29.5.14",
54
56
  "@types/markdown-it": "^14.1.2",
55
57
  "@types/node": "^22.10.3",
56
58
  "@types/unzipper": "^0.10.9",
57
59
  "eslint": "^9.17.0",
60
+ "jest": "^29.7.0",
58
61
  "prettier": "^3.4.2",
59
62
  "typescript": "^5.7.2"
60
63
  }
@@ -23,7 +23,6 @@ declare class ErrorLog {
23
23
  declare class Families {
24
24
  getFamilies(): Family[];
25
25
  getFamily(name: string): Family;
26
- renderPages(outputDir: string, cols?: TaxaCol[]): void;
27
26
  }
28
27
 
29
28
  declare class Family {
@@ -41,7 +40,6 @@ declare class FlowerColor {
41
40
  }
42
41
 
43
42
  declare class Genera {
44
- addTaxon(taxon: Taxon): void;
45
43
  getGenus(name: string): Genus;
46
44
  }
47
45
 
@@ -87,15 +85,8 @@ declare class Taxa {
87
85
  isSubset(): boolean;
88
86
  }
89
87
 
90
- declare class TaxaCol {
91
- class?: string;
92
- data: (taxon: Taxon) => string;
93
- title: string;
94
- }
95
-
96
88
  type StatusCode = "N" | "NC" | "U" | "X";
97
89
  declare class Taxon {
98
- constructor(data: TaxonData, genera: Genera, meta: any);
99
90
  getBaseFileName(): string;
100
91
  getBloomEnd(): number | undefined;
101
92
  getBloomStart(): number | undefined;
@@ -104,8 +95,6 @@ declare class Taxon {
104
95
  getCalfloraTaxonLink(): string | undefined;
105
96
  getCalscapeCommonName(): string | undefined;
106
97
  getCalscapeName(): string;
107
- getCESA(): string | undefined;
108
- getCNDDBRank(): string | undefined;
109
98
  getCommonNames(): string[];
110
99
  getFamily(): Family;
111
100
  getFESA(): string | undefined;
@@ -125,7 +114,6 @@ declare class Taxon {
125
114
  getJepsonID(): string;
126
115
  getLifeCycle(): string;
127
116
  getName(): string;
128
- getPhotos(): Photo[];
129
117
  getRPIID(): string | undefined;
130
118
  getRPIRank(): string;
131
119
  getRPIRankAndThreat(): string;
@@ -160,21 +148,6 @@ declare class TaxonData {
160
148
 
161
149
  type PhotoRights = "CC0" | "CC BY" | "CC BY-NC" | "C" | null;
162
150
 
163
- declare class Photo {
164
- url?: string;
165
- rightsHolder: null | string;
166
- rights?: PhotoRights;
167
- getExt(): string;
168
- getId(): number;
169
- getUrl(): string;
170
- getSourceUrl(): string;
171
- }
172
-
173
- declare class InatPhoto extends Photo {
174
- inatPhotoId: number;
175
- ext: string;
176
- }
177
-
178
151
  type InatPhotoInfo = {
179
152
  id: string;
180
153
  ext: string;
@@ -219,7 +192,7 @@ declare class InatApiTaxon {
219
192
  declare class InatApiObservation {
220
193
  observation_photos: {
221
194
  photo: InatApiPhoto;
222
- }[]
195
+ }[];
223
196
  }
224
197
 
225
198
  declare class InatObsPhotosCommandLineOptions extends CommandLineOptions {
package/lib/inat_photo.js DELETED
@@ -1,43 +0,0 @@
1
- import { CC0, CC_BY, CC_BY_NC, COPYRIGHT, Photo } from "./photo.js";
2
-
3
- class InatPhoto extends Photo {
4
- /** @type {number} */
5
- inatPhotoId;
6
- /** @type {string} */
7
- ext;
8
-
9
- /**
10
- * @param {number} id
11
- * @param {string} ext
12
- * @param {InatLicenseCode} licenseCode
13
- * @param {string} attrName
14
- */
15
- constructor(id, ext, licenseCode, attrName) {
16
- /** @type {typeof COPYRIGHT | typeof CC_BY | typeof CC_BY_NC | typeof CC0} */
17
- let rights = COPYRIGHT;
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);
22
- this.inatPhotoId = id;
23
- this.ext = ext;
24
- }
25
-
26
- getExt() {
27
- return this.ext;
28
- }
29
-
30
- getId() {
31
- return this.inatPhotoId;
32
- }
33
-
34
- getUrl() {
35
- return `https://inaturalist-open-data.s3.amazonaws.com/photos/${this.inatPhotoId}/medium.${this.ext}`;
36
- }
37
-
38
- getSourceUrl() {
39
- return `https://www.inaturalist.org/photos/${this.inatPhotoId}`;
40
- }
41
- }
42
-
43
- export { InatPhoto };