@ca-plant-list/ca-plant-list 0.1.2 → 0.1.4
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/jekyll/_includes/plantlists.html +0 -0
- package/jekyll/_layouts/html.html +1 -1
- package/jekyll/assets/css/main.css +6 -0
- package/jekyll/assets/js/name_search.js +156 -0
- package/jekyll/name_search.html +18 -0
- package/lib/basepagerenderer.js +46 -0
- package/lib/dataloader.js +10 -4
- package/lib/exceptions.js +4 -2
- package/lib/files.js +17 -0
- package/lib/genera.js +6 -1
- package/lib/index.js +10 -0
- package/lib/pagerenderer.js +3 -32
- package/lib/pagetaxon.js +3 -10
- package/lib/taxa.js +1 -0
- package/lib/taxon.js +48 -7
- package/package.json +2 -16
- package/jekyll/assets/js/common_name_search.js +0 -87
- package/jekyll/common_name_search.html +0 -18
File without changes
|
@@ -29,7 +29,7 @@
|
|
29
29
|
<a class="nav-link" href="{{site.baseurl}}/rare_plants.html">Rare Plants</a>
|
30
30
|
</li>
|
31
31
|
<li class="nav-item">
|
32
|
-
<a class="nav-link" href="{{site.baseurl}}/
|
32
|
+
<a class="nav-link" href="{{site.baseurl}}/name_search.html">Name Search</a>
|
33
33
|
</li>
|
34
34
|
{%include menu_extra.html %}
|
35
35
|
</ul>
|
@@ -28,6 +28,7 @@ span.label {
|
|
28
28
|
|
29
29
|
td {
|
30
30
|
border: solid 1px;
|
31
|
+
padding: 0 .5rem;
|
31
32
|
vertical-align: top;
|
32
33
|
}
|
33
34
|
|
@@ -85,4 +86,9 @@ div.section li {
|
|
85
86
|
|
86
87
|
div.section ul.indent {
|
87
88
|
padding-left: 1rem;
|
89
|
+
}
|
90
|
+
|
91
|
+
/* Forms */
|
92
|
+
input {
|
93
|
+
margin-right: .5em;
|
88
94
|
}
|
@@ -0,0 +1,156 @@
|
|
1
|
+
const MIN_LEN = 2;
|
2
|
+
const MAX_RESULTS = 50;
|
3
|
+
|
4
|
+
class Search {
|
5
|
+
|
6
|
+
static #debounceTimer;
|
7
|
+
static #searchData;
|
8
|
+
|
9
|
+
static #debounce( func ) {
|
10
|
+
clearTimeout( this.#debounceTimer );
|
11
|
+
this.#debounceTimer = setTimeout( func, 500 );
|
12
|
+
}
|
13
|
+
|
14
|
+
static #doSearch() {
|
15
|
+
|
16
|
+
function matchTaxon( taxon, value ) {
|
17
|
+
|
18
|
+
function matchSynonyms( syns, value ) {
|
19
|
+
const matchedIndexes = [];
|
20
|
+
if ( syns ) {
|
21
|
+
for ( let index = 0; index < syns.length; index++ ) {
|
22
|
+
if ( syns[ index ].includes( value ) ) {
|
23
|
+
matchedIndexes.push( index );
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
return matchedIndexes;
|
28
|
+
}
|
29
|
+
|
30
|
+
const rawData = taxon[ 0 ];
|
31
|
+
const name = taxon[ 1 ];
|
32
|
+
const cn = taxon[ 2 ];
|
33
|
+
const syns = matchSynonyms( taxon[ 3 ], value );
|
34
|
+
if ( syns.length > 0 ) {
|
35
|
+
// Include any matching synonyms.
|
36
|
+
for ( const index of syns ) {
|
37
|
+
matches.push( [ rawData[ 0 ], rawData[ 1 ], rawData[ 2 ][ index ] ] );
|
38
|
+
}
|
39
|
+
} else {
|
40
|
+
// No synonyms match; see if the scientific or common names match.
|
41
|
+
const namesMatch = name.includes( value ) || ( cn && cn.includes( value ) );
|
42
|
+
if ( namesMatch ) {
|
43
|
+
matches.push( [ rawData[ 0 ], rawData[ 1 ] ] );
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
}
|
48
|
+
|
49
|
+
Search.#debounceTimer = undefined;
|
50
|
+
|
51
|
+
const value = document.getElementById( "name" ).value.toLowerCase();
|
52
|
+
|
53
|
+
const matches = [];
|
54
|
+
const shouldSearch = ( value.length >= MIN_LEN );
|
55
|
+
|
56
|
+
if ( shouldSearch ) {
|
57
|
+
|
58
|
+
// If the search data is not done generating, try again later.
|
59
|
+
if ( !Search.#searchData ) {
|
60
|
+
this.#debounce( Search.#doSearch );
|
61
|
+
}
|
62
|
+
|
63
|
+
for ( const taxon of Search.#searchData ) {
|
64
|
+
matchTaxon( taxon, value );
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
const eBody = document.createElement( "tbody" );
|
69
|
+
if ( matches.length <= MAX_RESULTS ) {
|
70
|
+
for ( const match of matches ) {
|
71
|
+
|
72
|
+
const tr = document.createElement( "tr" );
|
73
|
+
|
74
|
+
// Scientific name.
|
75
|
+
const name = match[ 0 ];
|
76
|
+
const syn = match[ 2 ];
|
77
|
+
const td1 = document.createElement( "td" );
|
78
|
+
const link = document.createElement( "a" );
|
79
|
+
link.setAttribute( "href", "./" + name.replaceAll( ".", "" ).replaceAll( " ", "-" ) + ".html" );
|
80
|
+
link.textContent = name;
|
81
|
+
td1.appendChild( link );
|
82
|
+
if ( syn ) {
|
83
|
+
td1.appendChild( document.createTextNode( " (" + syn + ")" ) );
|
84
|
+
}
|
85
|
+
tr.appendChild( td1 );
|
86
|
+
|
87
|
+
const cn = match[ 1 ];
|
88
|
+
const td2 = document.createElement( "td" );
|
89
|
+
if ( cn ) {
|
90
|
+
td2.textContent = cn;
|
91
|
+
}
|
92
|
+
tr.appendChild( td2 );
|
93
|
+
|
94
|
+
eBody.appendChild( tr );
|
95
|
+
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
// Delete current message
|
100
|
+
const eMessage = document.getElementById( "message" );
|
101
|
+
if ( eMessage.firstChild ) {
|
102
|
+
eMessage.removeChild( eMessage.firstChild );
|
103
|
+
}
|
104
|
+
if ( shouldSearch ) {
|
105
|
+
if ( matches.length === 0 ) {
|
106
|
+
eMessage.textContent = "Nothing found.";
|
107
|
+
}
|
108
|
+
if ( matches.length > MAX_RESULTS ) {
|
109
|
+
eMessage.textContent = "Too many results.";
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
// Delete current results
|
114
|
+
const eTable = document.getElementById( "results" );
|
115
|
+
if ( eTable.firstChild ) {
|
116
|
+
eTable.removeChild( eTable.firstChild );
|
117
|
+
}
|
118
|
+
|
119
|
+
eTable.appendChild( eBody );
|
120
|
+
}
|
121
|
+
|
122
|
+
static async generateSearchData() {
|
123
|
+
const searchData = [];
|
124
|
+
// eslint-disable-next-line no-undef
|
125
|
+
for ( const taxon of NAMES ) {
|
126
|
+
const taxonData = [ taxon ];
|
127
|
+
taxonData.push( taxon[ 0 ].toLowerCase().replace( / (subsp|var)\./, "" ) );
|
128
|
+
if ( taxon[ 1 ] ) {
|
129
|
+
taxonData.push( taxon[ 1 ].toLowerCase() );
|
130
|
+
}
|
131
|
+
if ( taxon[ 2 ] ) {
|
132
|
+
const syns = [];
|
133
|
+
for ( const syn of taxon[ 2 ] ) {
|
134
|
+
syns.push( syn.toLowerCase().replace( / (subsp|var)\./, "" ) );
|
135
|
+
}
|
136
|
+
taxonData[ 3 ] = syns;
|
137
|
+
}
|
138
|
+
searchData.push( taxonData );
|
139
|
+
}
|
140
|
+
this.#searchData = searchData;
|
141
|
+
}
|
142
|
+
|
143
|
+
static #handleChange() {
|
144
|
+
this.#debounce( Search.#doSearch );
|
145
|
+
}
|
146
|
+
|
147
|
+
static init() {
|
148
|
+
this.generateSearchData();
|
149
|
+
const eName = document.getElementById( "name" );
|
150
|
+
eName.focus();
|
151
|
+
eName.oninput = ( ev ) => { return this.#handleChange( ev ); };
|
152
|
+
}
|
153
|
+
|
154
|
+
}
|
155
|
+
|
156
|
+
Search.init();
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
title: Name Search
|
3
|
+
js: name_search.js
|
4
|
+
---
|
5
|
+
<h1>Search</h1>
|
6
|
+
|
7
|
+
<script>
|
8
|
+
const NAMES = {% include names.json%};
|
9
|
+
</script>
|
10
|
+
|
11
|
+
<form>
|
12
|
+
<input type="text" id="name"><label for="name">Search for scientific name, common name, or synonym</label>
|
13
|
+
</form>
|
14
|
+
|
15
|
+
<div class="section" id="message"></div>
|
16
|
+
|
17
|
+
<table id="results">
|
18
|
+
</table>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import * as fs from "node:fs";
|
2
|
+
import { Config } from "./config.js";
|
3
|
+
|
4
|
+
class BasePageRenderer {
|
5
|
+
|
6
|
+
static render( outputDir, Taxa ) {
|
7
|
+
|
8
|
+
// Copy static files
|
9
|
+
fs.rmSync( outputDir, { force: true, recursive: true, maxRetries: 2, retryDelay: 200 } );
|
10
|
+
// First copy default Jekyll files from package.
|
11
|
+
fs.cpSync( Config.getPackageDir() + "/jekyll", outputDir, { recursive: true } );
|
12
|
+
// Then copy Jekyll files from current dir (which may override default files).
|
13
|
+
fs.cpSync( "jekyll", outputDir, { recursive: true } );
|
14
|
+
|
15
|
+
this.renderTools( outputDir, Taxa );
|
16
|
+
|
17
|
+
}
|
18
|
+
|
19
|
+
static renderTools( outputDir, Taxa ) {
|
20
|
+
|
21
|
+
const names = [];
|
22
|
+
for ( const taxon of Taxa.getTaxa() ) {
|
23
|
+
const row = [];
|
24
|
+
row.push( taxon.getName() );
|
25
|
+
const cn = taxon.getCommonNames().join( "," );
|
26
|
+
if ( cn ) {
|
27
|
+
row.push( cn );
|
28
|
+
}
|
29
|
+
const synonyms = [];
|
30
|
+
for ( const syn of taxon.getSynonyms() ) {
|
31
|
+
synonyms.push( syn );
|
32
|
+
}
|
33
|
+
if ( synonyms.length > 0 ) {
|
34
|
+
row[ 2 ] = synonyms;
|
35
|
+
}
|
36
|
+
names.push( row );
|
37
|
+
}
|
38
|
+
|
39
|
+
fs.writeFileSync( outputDir + "/_includes/names.json", JSON.stringify( names ) );
|
40
|
+
|
41
|
+
}
|
42
|
+
|
43
|
+
|
44
|
+
}
|
45
|
+
|
46
|
+
export { BasePageRenderer };
|
package/lib/dataloader.js
CHANGED
@@ -14,16 +14,22 @@ class DataLoader {
|
|
14
14
|
return OPTION_DEFS;
|
15
15
|
}
|
16
16
|
|
17
|
-
static
|
17
|
+
static init( taxaDir ) {
|
18
18
|
|
19
19
|
const defaultDataDir = Config.getPackageDir() + "/data";
|
20
|
-
const taxaDir = options.datadir;
|
21
|
-
|
22
|
-
console.log( "loading data" );
|
23
20
|
|
24
21
|
Exceptions.init( taxaDir );
|
25
22
|
Families.init( defaultDataDir );
|
26
23
|
Genera.init( defaultDataDir );
|
24
|
+
}
|
25
|
+
|
26
|
+
static load( options ) {
|
27
|
+
|
28
|
+
const taxaDir = options.datadir;
|
29
|
+
|
30
|
+
console.log( "loading data" );
|
31
|
+
|
32
|
+
this.init( taxaDir );
|
27
33
|
Taxa.init( taxaDir );
|
28
34
|
|
29
35
|
}
|
package/lib/exceptions.js
CHANGED
@@ -15,14 +15,16 @@ class Exceptions {
|
|
15
15
|
return false;
|
16
16
|
}
|
17
17
|
|
18
|
-
static getValue( name, cat, subcat ) {
|
18
|
+
static getValue( name, cat, subcat, defaultValue ) {
|
19
19
|
const taxonData = this.#exceptions[ name ];
|
20
20
|
if ( taxonData ) {
|
21
21
|
const catData = taxonData[ cat ];
|
22
22
|
if ( catData ) {
|
23
|
-
|
23
|
+
const val = catData[ subcat ];
|
24
|
+
return ( val === undefined ) ? defaultValue : val;
|
24
25
|
}
|
25
26
|
}
|
27
|
+
return defaultValue;
|
26
28
|
}
|
27
29
|
|
28
30
|
static init( dir ) {
|
package/lib/files.js
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
import * as fs from "node:fs";
|
2
|
+
|
3
|
+
class Files {
|
4
|
+
|
5
|
+
static async fetch( url, targetFileName ) {
|
6
|
+
const response = await fetch( url );
|
7
|
+
const data = await response.text();
|
8
|
+
fs.writeFileSync( targetFileName, data );
|
9
|
+
}
|
10
|
+
|
11
|
+
static read( path ) {
|
12
|
+
return fs.readFileSync( path, "utf8" );
|
13
|
+
}
|
14
|
+
|
15
|
+
}
|
16
|
+
|
17
|
+
export { Files };
|
package/lib/genera.js
CHANGED
@@ -9,6 +9,11 @@ class Genera {
|
|
9
9
|
|
10
10
|
const genusName = taxon.getGenusName();
|
11
11
|
const genusData = this.#genera[ genusName ];
|
12
|
+
if ( !genusData ) {
|
13
|
+
console.log( taxon.getName() + " genus not found" );
|
14
|
+
return;
|
15
|
+
}
|
16
|
+
|
12
17
|
if ( genusData.taxa === undefined ) {
|
13
18
|
genusData.taxa = [];
|
14
19
|
}
|
@@ -16,7 +21,7 @@ class Genera {
|
|
16
21
|
|
17
22
|
const family = this.getFamily( genusName );
|
18
23
|
if ( !family ) {
|
19
|
-
console.log( taxon.getName() + "
|
24
|
+
console.log( taxon.getName() + " family not found" );
|
20
25
|
return;
|
21
26
|
}
|
22
27
|
family.addTaxon( taxon );
|
package/lib/index.js
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
import { BasePageRenderer } from "./basepagerenderer.js";
|
2
|
+
import { Config } from "./config.js";
|
3
|
+
import { CSV } from "./csv.js";
|
4
|
+
import { DataLoader } from "./dataloader.js";
|
5
|
+
import { ErrorLog } from "./errorlog.js";
|
6
|
+
import { Exceptions } from "./exceptions.js";
|
7
|
+
import { HTML, HTML_OPTIONS } from "./html.js";
|
8
|
+
import { Taxon } from "./taxon.js";
|
9
|
+
|
10
|
+
export { BasePageRenderer, Config, CSV, DataLoader, ErrorLog, Exceptions, HTML, HTML_OPTIONS, Taxon };
|
package/lib/pagerenderer.js
CHANGED
@@ -6,20 +6,15 @@ import { HTMLPage } from "./htmlpage.js";
|
|
6
6
|
import { PageTaxon } from "./pagetaxon.js";
|
7
7
|
import { Config } from "./config.js";
|
8
8
|
import { RarePlants } from "./rareplants.js";
|
9
|
+
import { BasePageRenderer } from "./basepagerenderer.js";
|
9
10
|
|
10
|
-
class PageRenderer {
|
11
|
+
class PageRenderer extends BasePageRenderer {
|
11
12
|
|
12
13
|
static render( outputDir ) {
|
13
14
|
|
14
|
-
|
15
|
-
fs.rmSync( outputDir, { force: true, recursive: true } );
|
16
|
-
// First copy default Jekyll files from package.
|
17
|
-
fs.cpSync( Config.getPackageDir() + "/jekyll", outputDir, { recursive: true } );
|
18
|
-
// Then copy Jekyll files from current dir (which may override default files).
|
19
|
-
fs.cpSync( "jekyll", outputDir, { recursive: true } );
|
15
|
+
super.render( outputDir, Taxa );
|
20
16
|
|
21
17
|
this.renderLists( outputDir );
|
22
|
-
this.renderTools( outputDir );
|
23
18
|
|
24
19
|
Families.renderPages( outputDir );
|
25
20
|
|
@@ -174,30 +169,6 @@ class PageRenderer {
|
|
174
169
|
|
175
170
|
}
|
176
171
|
|
177
|
-
static renderTools( outputDir ) {
|
178
|
-
|
179
|
-
const commonNames = [];
|
180
|
-
for ( const taxon of Taxa.getTaxa() ) {
|
181
|
-
for ( const commonName of taxon.getCommonNames() ) {
|
182
|
-
commonNames.push( [ commonName, taxon.getName() ] );
|
183
|
-
}
|
184
|
-
}
|
185
|
-
|
186
|
-
const cnObj = {};
|
187
|
-
for ( const commonName of commonNames.sort( ( a, b ) => a[ 0 ].localeCompare( b[ 0 ] ) ) ) {
|
188
|
-
const normalizedName = commonName[ 0 ].toLowerCase().replaceAll( "-", " " ).replaceAll( "'", "" );
|
189
|
-
let data = cnObj[ normalizedName ];
|
190
|
-
if ( !data ) {
|
191
|
-
data = { cn: commonName[ 0 ], names: [] };
|
192
|
-
cnObj[ normalizedName ] = data;
|
193
|
-
}
|
194
|
-
data.names.push( commonName[ 1 ] );
|
195
|
-
}
|
196
|
-
fs.writeFileSync( outputDir + "/_includes/common_names.json", JSON.stringify( cnObj ) );
|
197
|
-
|
198
|
-
|
199
|
-
}
|
200
|
-
|
201
172
|
}
|
202
173
|
|
203
174
|
class PageTaxonList extends HTMLPage {
|
package/lib/pagetaxon.js
CHANGED
@@ -31,16 +31,9 @@ class PageTaxon extends HTMLPage {
|
|
31
31
|
)
|
32
32
|
);
|
33
33
|
}
|
34
|
-
const
|
35
|
-
if (
|
36
|
-
links.push(
|
37
|
-
HTML.getLink(
|
38
|
-
"https://www.inaturalist.org/taxa/" + iNatID,
|
39
|
-
"iNaturalist",
|
40
|
-
{},
|
41
|
-
HTML_OPTIONS.OPEN_NEW
|
42
|
-
)
|
43
|
-
);
|
34
|
+
const iNatLink = this.#taxon.getINatTaxonLink();
|
35
|
+
if ( iNatLink ) {
|
36
|
+
links.push( iNatLink );
|
44
37
|
}
|
45
38
|
return links;
|
46
39
|
}
|
package/lib/taxa.js
CHANGED
package/lib/taxon.js
CHANGED
@@ -12,13 +12,15 @@ class Taxon {
|
|
12
12
|
#status;
|
13
13
|
#jepsonID;
|
14
14
|
#calRecNum;
|
15
|
+
#cfSyn;
|
15
16
|
#iNatID;
|
16
17
|
#iNatSyn;
|
18
|
+
#rpiID;
|
17
19
|
#rankRPI;
|
18
20
|
#cesa;
|
19
21
|
#synonyms = [];
|
20
22
|
|
21
|
-
constructor( name, commonNames, status, jepsonID, calRecNum, iNatID, rankRPI, cesa ) {
|
23
|
+
constructor( name, commonNames, status, jepsonID, calRecNum, iNatID, rpiID, rankRPI, cesa ) {
|
22
24
|
this.#name = name;
|
23
25
|
this.#genus = name.split( " " )[ 0 ];
|
24
26
|
this.#commonNames = commonNames ? commonNames.split( "," ).map( t => t.trim() ) : [];
|
@@ -26,6 +28,7 @@ class Taxon {
|
|
26
28
|
this.#jepsonID = jepsonID;
|
27
29
|
this.#calRecNum = calRecNum;
|
28
30
|
this.#iNatID = iNatID;
|
31
|
+
this.#rpiID = rpiID;
|
29
32
|
this.#rankRPI = rankRPI;
|
30
33
|
this.#cesa = cesa;
|
31
34
|
Genera.addTaxon( this );
|
@@ -39,14 +42,23 @@ class Taxon {
|
|
39
42
|
|
40
43
|
addSynonym( syn, type ) {
|
41
44
|
this.#synonyms.push( syn );
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
+
switch ( type ) {
|
46
|
+
case "CF":
|
47
|
+
// Synonym is in Calflora format.
|
48
|
+
this.#cfSyn = syn;
|
49
|
+
break;
|
50
|
+
case "INAT":
|
51
|
+
// Synonyms should be in Jepson format, but store iNatName in iNat format (no var or subsp, space after x).
|
52
|
+
this.#iNatSyn = syn;
|
53
|
+
break;
|
45
54
|
}
|
46
55
|
}
|
47
56
|
|
48
57
|
getCalfloraName() {
|
49
|
-
|
58
|
+
if ( this.#cfSyn ) {
|
59
|
+
return this.#cfSyn;
|
60
|
+
}
|
61
|
+
return this.getName().replace( " subsp.", " ssp." ).replace( "×", "X" );
|
50
62
|
}
|
51
63
|
|
52
64
|
getCalfloraID() {
|
@@ -65,9 +77,13 @@ class Taxon {
|
|
65
77
|
return Genera.getFamily( this.#genus );
|
66
78
|
}
|
67
79
|
|
68
|
-
getFileName( ext
|
80
|
+
getFileName( ext ) {
|
81
|
+
return Taxon.getFileName( this.getName(), ext );
|
82
|
+
}
|
83
|
+
|
84
|
+
static getFileName( name, ext = "html" ) {
|
69
85
|
// Convert spaces to "-" and remove ".".
|
70
|
-
return
|
86
|
+
return name.replaceAll( " ", "-" ).replaceAll( ".", "" ) + "." + ext;
|
71
87
|
}
|
72
88
|
|
73
89
|
getGenus() {
|
@@ -97,6 +113,15 @@ class Taxon {
|
|
97
113
|
return name.replace( / (subsp|var)\./, "" ).replace( "×", "× " );
|
98
114
|
}
|
99
115
|
|
116
|
+
getINatTaxonLink() {
|
117
|
+
const iNatID = this.getINatID();
|
118
|
+
if ( !iNatID ) {
|
119
|
+
return "";
|
120
|
+
}
|
121
|
+
const link = HTML.getLink( "https://www.inaturalist.org/taxa/" + iNatID, "iNaturalist", {}, HTML_OPTIONS.OPEN_NEW );
|
122
|
+
return this.#iNatSyn ? ( link + " (" + this.#iNatSyn + ")" ) : link;
|
123
|
+
}
|
124
|
+
|
100
125
|
getJepsonID() {
|
101
126
|
return this.#jepsonID;
|
102
127
|
}
|
@@ -105,6 +130,10 @@ class Taxon {
|
|
105
130
|
return this.#name;
|
106
131
|
}
|
107
132
|
|
133
|
+
getRPIID() {
|
134
|
+
return this.#rpiID;
|
135
|
+
}
|
136
|
+
|
108
137
|
getRPIRank() {
|
109
138
|
if ( !this.#rankRPI ) {
|
110
139
|
return this.#rankRPI;
|
@@ -144,6 +173,18 @@ class Taxon {
|
|
144
173
|
return this.getRPIRank() !== undefined;
|
145
174
|
}
|
146
175
|
|
176
|
+
setCalfloraID( id ) {
|
177
|
+
this.#calRecNum = id;
|
178
|
+
}
|
179
|
+
|
180
|
+
setINatID( id ) {
|
181
|
+
this.#iNatID = id;
|
182
|
+
}
|
183
|
+
|
184
|
+
setJepsonID( id ) {
|
185
|
+
this.#jepsonID = id;
|
186
|
+
}
|
187
|
+
|
147
188
|
}
|
148
189
|
|
149
190
|
export { Taxon };
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ca-plant-list/ca-plant-list",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.4",
|
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": {
|
@@ -10,21 +10,7 @@
|
|
10
10
|
"homepage": "https://github.com/ca-plants/ca-plant-list",
|
11
11
|
"type": "module",
|
12
12
|
"exports": {
|
13
|
-
"
|
14
|
-
"import": "./lib/config.js"
|
15
|
-
},
|
16
|
-
"./CSV": {
|
17
|
-
"import": "./lib/csv.js"
|
18
|
-
},
|
19
|
-
"./DataLoader": {
|
20
|
-
"import": "./lib/dataloader.js"
|
21
|
-
},
|
22
|
-
"./Exceptions": {
|
23
|
-
"import": "./lib/exceptions.js"
|
24
|
-
},
|
25
|
-
"./Taxa": {
|
26
|
-
"import": "./lib/taxa.js"
|
27
|
-
}
|
13
|
+
".": "./lib/index.js"
|
28
14
|
},
|
29
15
|
"bin": {
|
30
16
|
"ca-plant-list": "build-site.js"
|
@@ -1,87 +0,0 @@
|
|
1
|
-
const MIN_LEN = 2;
|
2
|
-
const MAX_RESULTS = 50;
|
3
|
-
|
4
|
-
class Search {
|
5
|
-
|
6
|
-
static #debounceTimer;
|
7
|
-
|
8
|
-
static #debounce( func ) {
|
9
|
-
clearTimeout( this.#debounceTimer );
|
10
|
-
this.#debounceTimer = setTimeout( func, 500 );
|
11
|
-
}
|
12
|
-
|
13
|
-
static #doSearch() {
|
14
|
-
|
15
|
-
Search.#debounceTimer = undefined;
|
16
|
-
|
17
|
-
const value = document.getElementById( "common-name" ).value.toLowerCase();
|
18
|
-
|
19
|
-
const matches = [];
|
20
|
-
const shouldSearch = ( value.length >= MIN_LEN );
|
21
|
-
|
22
|
-
if ( shouldSearch ) {
|
23
|
-
// eslint-disable-next-line no-undef
|
24
|
-
for ( const key of Object.keys( COMMON_NAMES ) ) {
|
25
|
-
if ( key.includes( value ) ) {
|
26
|
-
matches.push( key );
|
27
|
-
}
|
28
|
-
}
|
29
|
-
}
|
30
|
-
|
31
|
-
const eBody = document.createElement( "tbody" );
|
32
|
-
if ( matches.length <= MAX_RESULTS ) {
|
33
|
-
for ( const match of matches ) {
|
34
|
-
// eslint-disable-next-line no-undef
|
35
|
-
const data = COMMON_NAMES[ match ];
|
36
|
-
for ( const name of data.names ) {
|
37
|
-
const tr = document.createElement( "tr" );
|
38
|
-
const td1 = document.createElement( "td" );
|
39
|
-
const td2 = document.createElement( "td" );
|
40
|
-
const link = document.createElement( "a" );
|
41
|
-
td1.textContent = data.cn;
|
42
|
-
link.setAttribute( "href", "./" + name.replaceAll( ".", "" ).replaceAll( " ", "-" ) + ".html" );
|
43
|
-
link.textContent = name;
|
44
|
-
td2.appendChild( link );
|
45
|
-
tr.appendChild( td1 );
|
46
|
-
tr.appendChild( td2 );
|
47
|
-
eBody.appendChild( tr );
|
48
|
-
}
|
49
|
-
}
|
50
|
-
}
|
51
|
-
|
52
|
-
// Delete current message
|
53
|
-
const eMessage = document.getElementById( "message" );
|
54
|
-
if ( eMessage.firstChild ) {
|
55
|
-
eMessage.removeChild( eMessage.firstChild );
|
56
|
-
}
|
57
|
-
if ( shouldSearch ) {
|
58
|
-
if ( matches.length === 0 ) {
|
59
|
-
eMessage.textContent = "Nothing found.";
|
60
|
-
}
|
61
|
-
if ( matches.length > MAX_RESULTS ) {
|
62
|
-
eMessage.textContent = "Too many results.";
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
// Delete current results
|
67
|
-
const eTable = document.getElementById( "results" );
|
68
|
-
if ( eTable.firstChild ) {
|
69
|
-
eTable.removeChild( eTable.firstChild );
|
70
|
-
}
|
71
|
-
|
72
|
-
eTable.appendChild( eBody );
|
73
|
-
}
|
74
|
-
|
75
|
-
static #handleChange() {
|
76
|
-
this.#debounce( Search.#doSearch );
|
77
|
-
}
|
78
|
-
|
79
|
-
static init() {
|
80
|
-
const eName = document.getElementById( "common-name" );
|
81
|
-
eName.focus();
|
82
|
-
eName.oninput = ( ev ) => { return this.#handleChange( ev ); };
|
83
|
-
}
|
84
|
-
|
85
|
-
}
|
86
|
-
|
87
|
-
Search.init();
|
@@ -1,18 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: Common Name Search
|
3
|
-
js: common_name_search.js
|
4
|
-
---
|
5
|
-
<h1>Search for Common Name</h1>
|
6
|
-
|
7
|
-
<script>
|
8
|
-
const COMMON_NAMES = {% include common_names.json%};
|
9
|
-
</script>
|
10
|
-
|
11
|
-
<form>
|
12
|
-
<input type="text" id="common-name">
|
13
|
-
</form>
|
14
|
-
|
15
|
-
<div class="section" id="message"></div>
|
16
|
-
|
17
|
-
<table id="results">
|
18
|
-
</table>
|