@ca-plant-list/ca-plant-list 0.4.23 → 0.4.26

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.
package/data/taxa.csv CHANGED
@@ -720,11 +720,11 @@ Eriophyllum confertiflorum var. confertiflorum,golden-yarrow,N,69866,3423,53397,
720
720
  Eriophyllum jepsonii,Jepson's woolly-sunflower,N,2816,3426,77048,203742,true,Jepson's Woolly Sunflower,,,,,776,4.3,,,S3,G3
721
721
  Eriophyllum lanatum var. achilleoides,,N,7369,10587,80880,260847,true,Common Woollysunflower
722
722
  Eriophyllum staechadifolium,seaside woolly-sunflower,N,2825,3444,60963,203755,true,Seaside Woolly Sunflower
723
- Erodium botrys,long-beaked filaree,X,25037,3446,57090,17986,,,,pink,3,7
724
- Erodium brachycarpum,,X,25038,3447,58171,17987
725
- Erodium cicutarium,red-stemmed filaree,X,25041,3448,47687,17989
723
+ Erodium botrys,long-beaked filaree,X,25037,3446,57090,17986,,,annual,pink,3,7
724
+ Erodium brachycarpum,,X,25038,3447,58171,17987,,,annual,pink,3,7
725
+ Erodium cicutarium,red-stemmed filaree,X,25041,3448,47687,17989,,,annual,pink,3,9
726
726
  Erodium malacoides,,X,76818,3451,77056,17994
727
- Erodium moschatum,white-stem filaree,X,25048,3452,57092,17995
727
+ Erodium moschatum,white-stem filaree,X,25048,3452,57092,17995,,,"annual,biennial",pink,2,9
728
728
  Eryngium aristulatum var. aristulatum,coyote-thistle,N,58845,3458,64189,211707,,California Eryngo
729
729
  Eryngium aristulatum var. hooveri,,N,58846,3459,57904,203761,,Hoover's Button-celery,,,,,783,1B.1,,,S1,G5T1
730
730
  Eryngium aristulatum,Jepson's button celery,N,25070,3457,57901,203759,,Jepson's Button Celery
@@ -1599,7 +1599,7 @@ Salix exigua var. hindsiana,Hind's willow,N,81242,11063,64195,51553,true,Sandbar
1599
1599
  Salix gooddingii,Goodding's black willow,N,42825,7272,60240,31274,true,Goodding's Black Willow
1600
1600
  Salix laevigata,red willow,N,42850,7276,58319,31302,true,Red Willow
1601
1601
  Salix lasiandra var. lasiandra,Pacific willow,N,76043,11065,81358,51640,true,Yellow Willow
1602
- Salix lasiolepis,arroyo willow,N,42855,7277,53452,31307,true,Arroyo Willow
1602
+ Salix lasiolepis,arroyo willow,N,42855,7277,53452,31307,true,Arroyo Willow,perennial,,1,6
1603
1603
  Salix melanopsis,dusky willow,N,42881,7284,78947,31332,true,Dusky Willow
1604
1604
  Salix scouleriana,Scouler's willow,N,42973,7289,71076,31425,true,Scouler's Willow
1605
1605
  Salix sitchensis,Sitka willow,N,42994,7291,61100,31435,true,Sitka Willow
@@ -1676,7 +1676,7 @@ Silene lemmonii,Lemmon's catchfly,N,44533,7603,60523,119295,true,Lemmon's Catchf
1676
1676
  Silene sargentii,Sargent's catchfly,N,44566,7614,62697,119340,true,Sargent's Catchfly,perennial,white,7,8
1677
1677
  Silene verecunda,,N,44589,7617,60581,119364,true,San Francisco Campion
1678
1678
  Silybum marianum,milk-thistle,X,4918,7622,52586,205950,true
1679
- Sinapis arvensis,charlock,X,44609,7625,1562069,32232,true
1679
+ Sinapis arvensis,charlock,X,44609,7625,1562069,32232,true,,annual,yellow,3,10
1680
1680
  Sisymbrium irio,London rocket,X,44651,7628,58085,32256,true
1681
1681
  Sisymbrium officinale,hedge mustard,X,44653,7630,53266,32267,true
1682
1682
  Sisymbrium orientale,,X,44654,7631,58086,32268,true
@@ -0,0 +1,3 @@
1
+ ## Resources
2
+
3
+ - [How to identify Erodium botrys](https://www.inaturalist.org/projects/erodium-botrys-mediterranean-stork-s-bill/journal/61038-how-to-identify-erodium-botrys-mediterranean-stork-s-bill) iNaturalist journal post
@@ -0,0 +1 @@
1
+ Petals with purple streaks.
@@ -0,0 +1,3 @@
1
+ ## Resources
2
+
3
+ - [How to identify Erodium brachycarpum](https://www.inaturalist.org/projects/erodium-brachycarpum-hairy-pitted-stork-s-bill/journal/61042-how-to-identify-erodium-brachycarpum-hairy-pitted-stork-s-bill) iNaturalist journal post
@@ -0,0 +1 @@
1
+ Petals with purple streaks.
@@ -0,0 +1 @@
1
+ Petals not streaked. Leaf compound, fern-like. Bristles on sepals.
@@ -0,0 +1,3 @@
1
+ ## Resources
2
+
3
+ - [How to identify Erodium moschatum](https://www.inaturalist.org/projects/erodium-moschatum-musk-stork-s-bill/journal/61039-how-to-identify-erodium-moschatum-musk-stork-s-bill) iNaturalist journal post
@@ -0,0 +1 @@
1
+ Petals not streaked, except near base. Leaves compound but not fern-like. No bristles on sepals.
@@ -1,141 +1,173 @@
1
1
  import { Utils } from "./utils.js";
2
2
 
3
+ /**
4
+ * @typedef {[string]|[string,string]|[string,string|undefined,string[]]} RawSearchData
5
+ * @typedef {{raw:RawSearchData,searchSci:string,searchCommon?:string,synonyms?:string[]}} SearchData
6
+ */
7
+
3
8
  const MIN_LEN = 2;
4
9
  const MAX_RESULTS = 50;
5
10
 
6
11
  class Search {
7
-
12
+ /** @type {number|undefined} */
8
13
  static #debounceTimer;
14
+ /** @type {SearchData[]} */
9
15
  static #searchData;
10
16
 
11
- static #debounce( timeout = 500 ) {
12
- clearTimeout( this.#debounceTimer );
13
- this.#debounceTimer = setTimeout( Search.#doSearch, timeout );
17
+ /**
18
+ * @param {number} [timeout]
19
+ */
20
+ static #debounce(timeout = 500) {
21
+ clearTimeout(this.#debounceTimer);
22
+ this.#debounceTimer = window.setTimeout(Search.#doSearch, timeout);
14
23
  }
15
24
 
16
25
  static #doSearch() {
17
-
18
- function matchTaxon( taxon, value ) {
19
-
20
- function matchSynonyms( syns, value ) {
26
+ /**
27
+ * @param {SearchData} taxon
28
+ * @param {string} value
29
+ */
30
+ function matchTaxon(taxon, value) {
31
+ /**
32
+ * @param {string[]|undefined} syns
33
+ * @param {string} value
34
+ * @returns {number[]}
35
+ */
36
+ function matchSynonyms(syns, value) {
21
37
  const matchedIndexes = [];
22
- if ( syns ) {
23
- for ( let index = 0; index < syns.length; index++ ) {
24
- if ( syns[ index ].includes( value ) ) {
25
- matchedIndexes.push( index );
38
+ if (syns) {
39
+ for (let index = 0; index < syns.length; index++) {
40
+ if (syns[index].includes(value)) {
41
+ matchedIndexes.push(index);
26
42
  }
27
43
  }
28
44
  }
29
45
  return matchedIndexes;
30
46
  }
31
47
 
32
- const rawData = taxon[ 0 ];
33
- const name = taxon[ 1 ];
34
- const cn = taxon[ 2 ];
35
- const syns = matchSynonyms( taxon[ 3 ], value );
36
- if ( syns.length > 0 ) {
48
+ const name = taxon.searchSci;
49
+ const cn = taxon.searchCommon;
50
+ const syns = matchSynonyms(taxon.synonyms, value);
51
+ if (syns.length > 0) {
37
52
  // Include any matching synonyms.
38
- for ( const index of syns ) {
39
- matches.push( [ rawData[ 0 ], rawData[ 1 ], rawData[ 2 ][ index ] ] );
53
+ for (const index of syns) {
54
+ matches.push([
55
+ taxon.raw[0],
56
+ taxon.raw[1],
57
+ taxon.raw[2] ? taxon.raw[2][index] : undefined,
58
+ ]);
40
59
  }
41
60
  } else {
42
61
  // No synonyms match; see if the scientific or common names match.
43
- const namesMatch = name.includes( value ) || ( cn && cn.includes( value ) );
44
- if ( namesMatch ) {
45
- matches.push( [ rawData[ 0 ], rawData[ 1 ] ] );
62
+ const namesMatch =
63
+ name.includes(value) || (cn && cn.includes(value));
64
+ if (namesMatch) {
65
+ matches.push([taxon.raw[0], taxon.raw[1]]);
46
66
  }
47
67
  }
48
-
49
68
  }
50
69
 
51
70
  Search.#debounceTimer = undefined;
52
71
 
53
- const value = Search.#normalizeName( document.getElementById( "name" ).value );
72
+ const input = Utils.getElement("name");
73
+ if (!(input instanceof HTMLInputElement)) {
74
+ throw new Error();
75
+ }
76
+ const value = Search.#normalizeName(input.value);
54
77
 
78
+ /**
79
+ * @type {([string,string|undefined]|[string,string|undefined,string|undefined])[]}
80
+ */
55
81
  const matches = [];
56
- const shouldSearch = ( value.length >= MIN_LEN );
57
-
58
- if ( shouldSearch ) {
82
+ const shouldSearch = value.length >= MIN_LEN;
59
83
 
84
+ if (shouldSearch) {
60
85
  // If the search data is not done generating, try again later.
61
- if ( !Search.#searchData ) {
62
- this.#debounce( Search.#doSearch );
86
+ if (!Search.#searchData) {
87
+ this.#debounce();
63
88
  }
64
89
 
65
- for ( const taxon of Search.#searchData ) {
66
- matchTaxon( taxon, value );
90
+ for (const taxon of Search.#searchData) {
91
+ matchTaxon(taxon, value);
67
92
  }
68
93
  }
69
94
 
70
- const eBody = document.createElement( "tbody" );
71
- if ( matches.length <= MAX_RESULTS ) {
72
- for ( const match of matches ) {
73
-
74
- const tr = document.createElement( "tr" );
95
+ const eBody = document.createElement("tbody");
96
+ if (matches.length <= MAX_RESULTS) {
97
+ for (const match of matches) {
98
+ const tr = document.createElement("tr");
75
99
 
76
100
  // Scientific name.
77
- const name = match[ 0 ];
78
- const syn = match[ 2 ];
79
- const td1 = document.createElement( "td" );
80
- const link = Utils.domTaxonLink( name );
81
- td1.appendChild( link );
82
- if ( syn ) {
83
- td1.appendChild( document.createTextNode( " (" + syn + ")" ) );
101
+ const name = match[0];
102
+ const syn = match[2];
103
+ const td1 = document.createElement("td");
104
+ const link = Utils.domTaxonLink(name);
105
+ td1.appendChild(link);
106
+ if (syn) {
107
+ td1.appendChild(document.createTextNode(" (" + syn + ")"));
84
108
  }
85
- tr.appendChild( td1 );
109
+ tr.appendChild(td1);
86
110
 
87
- const cn = match[ 1 ];
88
- const td2 = document.createElement( "td" );
89
- if ( cn ) {
111
+ const cn = match[1];
112
+ const td2 = document.createElement("td");
113
+ if (cn) {
90
114
  td2.textContent = cn;
91
115
  }
92
- tr.appendChild( td2 );
93
-
94
- eBody.appendChild( tr );
116
+ tr.appendChild(td2);
95
117
 
118
+ eBody.appendChild(tr);
96
119
  }
97
120
  }
98
121
 
99
122
  // Delete current message
100
- const eMessage = document.getElementById( "message" );
101
- if ( eMessage.firstChild ) {
102
- eMessage.removeChild( eMessage.firstChild );
123
+ const eMessage = Utils.getElement("message");
124
+ if (eMessage.firstChild) {
125
+ eMessage.removeChild(eMessage.firstChild);
103
126
  }
104
- if ( shouldSearch ) {
105
- if ( matches.length === 0 ) {
127
+ if (shouldSearch) {
128
+ if (matches.length === 0) {
106
129
  eMessage.textContent = "Nothing found.";
107
130
  }
108
- if ( matches.length > MAX_RESULTS ) {
131
+ if (matches.length > MAX_RESULTS) {
109
132
  eMessage.textContent = "Too many results.";
110
133
  }
111
134
  }
112
135
 
113
136
  // Delete current results
114
- const eTable = document.getElementById( "results" );
115
- if ( eTable.firstChild ) {
116
- eTable.removeChild( eTable.firstChild );
137
+ const eTable = Utils.getElement("results");
138
+ if (eTable.firstChild) {
139
+ eTable.removeChild(eTable.firstChild);
117
140
  }
118
141
 
119
- eTable.appendChild( eBody );
142
+ eTable.appendChild(eBody);
120
143
  }
121
144
 
122
145
  static async generateSearchData() {
146
+ /** @type {SearchData[]} */
123
147
  const searchData = [];
148
+
149
+ /** @type {RawSearchData[]} */
150
+ // @ts-ignore
124
151
  // eslint-disable-next-line no-undef
125
- for ( const taxon of NAMES ) {
126
- const taxonData = [ taxon ];
127
- taxonData.push( this.#normalizeName( taxon[ 0 ] ) );
128
- if ( taxon[ 1 ] ) {
129
- taxonData.push( taxon[ 1 ].toLowerCase() );
152
+ const names = NAMES;
153
+
154
+ for (const taxon of names) {
155
+ /** @type {SearchData} */
156
+ const taxonData = {
157
+ raw: taxon,
158
+ searchSci: this.#normalizeName(taxon[0]),
159
+ };
160
+ if (taxon[1]) {
161
+ taxonData.searchCommon = taxon[1].toLowerCase();
130
162
  }
131
- if ( taxon[ 2 ] ) {
163
+ if (taxon[2]) {
132
164
  const syns = [];
133
- for ( const syn of taxon[ 2 ] ) {
134
- syns.push( this.#normalizeName( syn ) );
165
+ for (const syn of taxon[2]) {
166
+ syns.push(this.#normalizeName(syn));
135
167
  }
136
- taxonData[ 3 ] = syns;
168
+ taxonData.synonyms = syns;
137
169
  }
138
- searchData.push( taxonData );
170
+ searchData.push(taxonData);
139
171
  }
140
172
  this.#searchData = searchData;
141
173
  }
@@ -145,21 +177,29 @@ class Search {
145
177
  }
146
178
 
147
179
  static #handleSubmit() {
148
- this.#debounce( 0 );
180
+ this.#debounce(0);
149
181
  }
150
182
 
151
183
  static init() {
152
184
  this.generateSearchData();
153
- const eName = document.getElementById( "name" );
185
+ const eName = Utils.getElement("name");
154
186
  eName.focus();
155
- eName.oninput = ( ev ) => { return this.#handleChange( ev ); };
156
- document.getElementById( "search_form" ).onsubmit = () => { this.#handleSubmit(); return false; };
187
+ eName.oninput = () => {
188
+ return this.#handleChange();
189
+ };
190
+ Utils.getElement("search_form").onsubmit = () => {
191
+ this.#handleSubmit();
192
+ return false;
193
+ };
157
194
  }
158
195
 
159
- static #normalizeName( name ) {
160
- return name.toLowerCase().replace( / (subsp|var)\./, "" );
196
+ /**
197
+ * @param {string} name
198
+ * @returns {string}
199
+ */
200
+ static #normalizeName(name) {
201
+ return name.toLowerCase().replace(/ (subsp|var)\./, "");
161
202
  }
162
-
163
203
  }
164
204
 
165
- Search.init();
205
+ Search.init();
@@ -1,10 +1,11 @@
1
1
  class UI {
2
-
3
2
  static init() {
4
- const tooltips = document.querySelectorAll( "span[title]" );
5
- [ ...tooltips ].map( tooltipTriggerEl => new bootstrap.Tooltip( tooltipTriggerEl ) );
3
+ const tooltips = document.querySelectorAll("span[title]");
4
+ [...tooltips].map(
5
+ // @ts-ignore
6
+ (tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl),
7
+ );
6
8
  }
7
-
8
9
  }
9
10
 
10
11
  UI.init();
@@ -1,26 +1,56 @@
1
- class Utils {
2
-
3
- static domElement( name, attributes = {}, content ) {
4
- const e = document.createElement( name );
5
- for ( const [ k, v ] of Object.entries( attributes ) ) {
6
- e.setAttribute( k, v );
1
+ export class Utils {
2
+ /**
3
+ * @param {string} name
4
+ * @param {Object<string,string>} attributes
5
+ * @param {string} [content]
6
+ * @returns {Element}
7
+ */
8
+ static domElement(name, attributes = {}, content) {
9
+ const e = document.createElement(name);
10
+ for (const [k, v] of Object.entries(attributes)) {
11
+ e.setAttribute(k, v);
7
12
  }
8
- if ( content ) {
13
+ if (content) {
9
14
  e.textContent = content;
10
15
  }
11
16
  return e;
12
17
  }
13
18
 
14
- static domLink( href, text, attributes = {} ) {
15
- const e = this.domElement( "a", Object.assign( { href: href }, attributes ) );
19
+ /**
20
+ * @param {string} href
21
+ * @param {string} text
22
+ * @param {Object<string,string>} attributes
23
+ * @returns {Element}
24
+ */
25
+ static domLink(href, text, attributes = {}) {
26
+ const e = this.domElement(
27
+ "a",
28
+ Object.assign({ href: href }, attributes),
29
+ );
16
30
  e.textContent = text;
17
31
  return e;
18
32
  }
19
33
 
20
- static domTaxonLink( name ) {
21
- return this.domLink( "./" + name.replaceAll( ".", "" ).replaceAll( " ", "-" ) + ".html", name );
34
+ /**
35
+ * @param {string} name
36
+ * @returns {Element}
37
+ */
38
+ static domTaxonLink(name) {
39
+ return this.domLink(
40
+ "./" + name.replaceAll(".", "").replaceAll(" ", "-") + ".html",
41
+ name,
42
+ );
22
43
  }
23
44
 
45
+ /**
46
+ * @param {string} id
47
+ * @returns {HTMLElement}
48
+ */
49
+ static getElement(id) {
50
+ const e = document.getElementById(id);
51
+ if (e === null) {
52
+ throw new Error();
53
+ }
54
+ return e;
55
+ }
24
56
  }
25
-
26
- export { Utils };
@@ -4,7 +4,7 @@ import { Jekyll } from "./jekyll.js";
4
4
  import { GlossaryPages } from "./web/glossarypages.js";
5
5
  import { PageFamilyList } from "./web/pageFamily.js";
6
6
 
7
- class BasePageRenderer {
7
+ export class BasePageRenderer {
8
8
  /**
9
9
  * @param {string} outputDir
10
10
  * @param {import("./types.js").Taxa} taxa
@@ -60,5 +60,3 @@ class BasePageRenderer {
60
60
  Files.write(outputDir + "/_includes/names.json", JSON.stringify(names));
61
61
  }
62
62
  }
63
-
64
- export { BasePageRenderer };
package/lib/index.d.ts CHANGED
@@ -46,6 +46,10 @@ export type TaxonData = TaxonomyData & {
46
46
  taxon_name: string;
47
47
  };
48
48
 
49
+ export type TaxonOverrides = {
50
+ status?: NativeStatusCode;
51
+ };
52
+
49
53
  // Classes
50
54
 
51
55
  export class BasePageRenderer {
@@ -73,6 +77,11 @@ export class CSV {
73
77
  fileName: string,
74
78
  delimeter?: string,
75
79
  ): Record<string, string>[];
80
+ static writeFileObject(
81
+ fileName: string,
82
+ data: Record<string, any>[],
83
+ headerData: string[],
84
+ ): void;
76
85
  }
77
86
 
78
87
  export class ErrorLog {
@@ -104,6 +113,7 @@ export class Files {
104
113
  targetFileName: string | undefined,
105
114
  ): Promise<Headers>;
106
115
  static mkdir(dir: string): void;
116
+ static read(path: string): string;
107
117
  static rmDir(dir: string): void;
108
118
  static write(fileName: string, data: string, overwrite?: boolean): void;
109
119
  }
@@ -170,6 +180,7 @@ export class HTMLTaxon {
170
180
  export class Jekyll {
171
181
  static hasInclude(baseDir: string, path: string): boolean;
172
182
  static include(fileName: string): string;
183
+ static writeInclude(baseDir: string, path: string, data: string): void;
173
184
  }
174
185
 
175
186
  export class Photo {
@@ -187,11 +198,11 @@ export class Program {
187
198
 
188
199
  export class Taxa<T> {
189
200
  constructor(
190
- inclusionList: Record<string, TaxonData> | true,
201
+ inclusionList: Record<string, TaxonOverrides> | true,
191
202
  errorLog: ErrorLog,
192
203
  showFlowerErrors: boolean,
193
204
  taxonFactory?: (td: TaxonData, g: Genera) => T,
194
- extraTaxa?: TaxonData[],
205
+ extraTaxa?: TaxonOverrides[],
195
206
  extraSynonyms?: Record<string, string>[],
196
207
  );
197
208
  getTaxon(name: string): T;
@@ -184,9 +184,7 @@ class PageRenderer extends BasePageRenderer {
184
184
  {
185
185
  name: "Endangered Species",
186
186
  filename: "list_endangered",
187
- include: (t) =>
188
- t.getCESA() !== undefined ||
189
- t.getFESA() !== undefined,
187
+ include: (t) => !!t.getCESA() || !!t.getFESA(),
190
188
  columns: ENDANGERED_COLS,
191
189
  },
192
190
  ],
@@ -40,7 +40,7 @@ class Taxa {
40
40
  #isSubset;
41
41
 
42
42
  /**
43
- * @param {Object<string,import("../index.js").TaxonData>|true} inclusionList
43
+ * @param {Object<string,import("../index.js").TaxonOverrides>|true} inclusionList
44
44
  * @param {ErrorLog} errorLog
45
45
  * @param {boolean} showFlowerErrors
46
46
  * @param {function(import("../index.js").TaxonData,Genera):Taxon} taxonFactory
@@ -224,7 +224,7 @@ class Taxa {
224
224
 
225
225
  /**
226
226
  * @param {SynonymData[]} synCSV
227
- * @param {Object<string,import("../index.js").TaxonData>|boolean} inclusionList
227
+ * @param {Object<string,import("../index.js").TaxonOverrides>|boolean} inclusionList
228
228
  */
229
229
  #loadSyns(synCSV, inclusionList) {
230
230
  for (const syn of synCSV) {
@@ -246,7 +246,7 @@ class Taxa {
246
246
 
247
247
  /**
248
248
  * @param {import("../index.js").TaxonData[]} taxaCSV
249
- * @param {Object<string,import("../index.js").TaxonData>|true} inclusionList
249
+ * @param {Object<string,import("../index.js").TaxonOverrides>|true} inclusionList
250
250
  * @param {function(import("../index.js").TaxonData,Genera):Taxon} taxonFactory
251
251
  * @param {Genera} genera
252
252
  * @param {boolean} showFlowerErrors
@@ -255,7 +255,7 @@ class Taxa {
255
255
  for (const row of taxaCSV) {
256
256
  const name = row["taxon_name"];
257
257
 
258
- /** @type {import("../index.js").TaxonData|{status?:import("../index.js").NativeStatusCode}} */
258
+ /** @type {import("../index.js").TaxonOverrides} */
259
259
  let taxon_overrides = {};
260
260
  if (inclusionList !== true) {
261
261
  taxon_overrides = inclusionList[name];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ca-plant-list/ca-plant-list",
3
- "version": "0.4.23",
3
+ "version": "0.4.26",
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": {