@ca-plant-list/ca-plant-list 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +4 -2
- package/data/exceptions.json +0 -3
- package/data/glossary/ovary.md +3 -0
- package/data/glossary/pistil.md +3 -0
- package/data/glossary/stigma.md +3 -0
- package/data/glossary/style.md +3 -0
- package/data/illustrations/inkscape/pistil.svg +156 -0
- package/data/synonyms.csv +8 -2
- package/data/taxa.csv +54 -52
- package/data/text/Ceanothus-cuneatus-var-cuneatus.md +1 -0
- package/data/text/Ceanothus-leucodermis.md +1 -0
- package/data/text/Delphinium-californicum-subsp-californicum.md +1 -0
- package/data/text/Delphinium-decorum-subsp-decorum.md +1 -0
- package/data/text/Delphinium-hesperium-subsp-hesperium.md +1 -0
- package/data/text/Delphinium-patens-subsp-patens.md +1 -0
- package/data/text/Galium-andrewsii-subsp-gatense.md +1 -0
- package/data/text/Helianthella-californica-var-californica.md +1 -0
- package/data/text/Helianthella-castanea.md +1 -0
- package/data/text/Iris-macrosiphon.md +1 -0
- package/data/text/Lasthenia-californica-subsp-californica.md +1 -0
- package/data/text/Lasthenia-gracilis.md +1 -0
- package/data/text/Lithophragma-affine.md +1 -1
- package/data/text/Lithophragma-parviflorum-var-parviflorum.md +1 -1
- package/data/text/Lomatium-californicum.md +0 -0
- package/data/text/Lomatium-dasycarpum-subsp-dasycarpum.md +1 -0
- package/data/text/Lomatium-utriculatum.md +1 -0
- package/data/text/Nemophila-heterophylla.md +1 -0
- package/data/text/Nemophila-parviflora-var-parviflora.md +1 -0
- package/data/text/Plectritis-ciliosa.md +1 -0
- package/data/text/Plectritis-macrocera.md +1 -0
- package/data/text/Primula-clevelandii-var-patula.md +1 -0
- package/data/text/Primula-hendersonii.md +1 -0
- package/data/text/Sidalcea-diploscypha.md +1 -0
- package/data/text/Thysanocarpus-curvipes.md +1 -0
- package/data/text/Thysanocarpus-laciniatus.md +1 -0
- package/data/text/Viola-douglasii.md +1 -0
- package/data/text/Viola-pedunculata.md +1 -0
- package/data/text/Viola-purpurea-subsp-quercetorum.md +1 -0
- package/ebook/css/main.css +5 -0
- package/lib/basepagerenderer.js +30 -28
- package/lib/dateutils.js +36 -16
- package/lib/ebook/pages/taxonpage.js +63 -36
- package/lib/errorlog.js +16 -11
- package/lib/families.js +109 -71
- package/lib/files.js +103 -45
- package/lib/generictaxaloader.js +15 -7
- package/lib/index.js +38 -3
- package/lib/taxa.js +164 -87
- package/lib/taxaloader.js +28 -15
- package/lib/taxaprocessor.js +6 -8
- package/lib/taxon.js +109 -56
- package/package.json +4 -6
- package/scripts/build-ebook.js +30 -25
- package/lib/index.d.ts +0 -345
@@ -0,0 +1 @@
|
|
1
|
+
Style 2–4 mm long. Lower leaves with deep lobes; lobes similar. Corolla 4-12 mm wide.
|
@@ -0,0 +1 @@
|
|
1
|
+
Style less than 2 mm long. Lower leaves cut or shallow-lobed; lobes dissimilar. Corolla 1-5 mm wide.
|
@@ -0,0 +1 @@
|
|
1
|
+
Corolla with 2 lips. Lower lip usually has 2 red spots.
|
@@ -0,0 +1 @@
|
|
1
|
+
Corolla symmetrical, circular.
|
@@ -0,0 +1 @@
|
|
1
|
+
Leaf blade usually more than twice as long as wide. Filament tube usually 3 to 4 mm wide. Anther usually dark purple. [Peduncle](./g/peduncle.html) usually green.
|
@@ -0,0 +1 @@
|
|
1
|
+
Leaf blade usually less than twice as long as wide. Filament tube usually less than 3 mm wide. [Peduncle](./g/peduncle.html) usually red.
|
@@ -0,0 +1 @@
|
|
1
|
+
Annual. Stipules on upper stem having two or more linear lobes.
|
@@ -0,0 +1 @@
|
|
1
|
+
Difficult to distinguish from _T. laciniatus_, with which it likely hybridizes.
|
@@ -0,0 +1 @@
|
|
1
|
+
Difficult to distinguish from _T. curvipes_, with which it likely hybridizes.
|
@@ -0,0 +1 @@
|
|
1
|
+
Leaves compound.
|
@@ -0,0 +1 @@
|
|
1
|
+
Leaves not compound. No basal leaves.
|
@@ -0,0 +1 @@
|
|
1
|
+
Leaves not compound.
|
package/ebook/css/main.css
CHANGED
package/lib/basepagerenderer.js
CHANGED
@@ -6,53 +6,55 @@ import { Taxa } from "./taxa.js";
|
|
6
6
|
import { GlossaryPages } from "./web/glossarypages.js";
|
7
7
|
|
8
8
|
class BasePageRenderer {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
/**
|
10
|
+
* @param {string} outputDir
|
11
|
+
* @param {Taxa} taxa
|
12
|
+
* @param {*} familyCols
|
13
|
+
*/
|
14
|
+
static render(outputDir, taxa, familyCols) {
|
15
|
+
const siteGenerator = new Jekyll(outputDir);
|
13
16
|
|
14
17
|
// Copy static files
|
15
18
|
// First copy default Jekyll files from package.
|
16
|
-
Files.copyDir(
|
19
|
+
Files.copyDir(Config.getPackageDir() + "/jekyll", outputDir);
|
17
20
|
// Then copy Jekyll files from current dir (which may override default files).
|
18
|
-
Files.copyDir(
|
21
|
+
Files.copyDir("jekyll", outputDir);
|
19
22
|
|
20
23
|
// Copy illustrations.
|
21
|
-
siteGenerator.copyIllustrations(
|
24
|
+
siteGenerator.copyIllustrations(Taxa.getFlowerColors());
|
22
25
|
|
23
|
-
Families.renderPages(
|
26
|
+
Families.renderPages(outputDir, familyCols);
|
24
27
|
|
25
|
-
new GlossaryPages(
|
26
|
-
|
27
|
-
this.renderTools( outputDir, taxa );
|
28
|
+
new GlossaryPages(siteGenerator).renderPages();
|
28
29
|
|
30
|
+
this.renderTools(outputDir, taxa);
|
29
31
|
}
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
+
/**
|
34
|
+
* @param {string} outputDir
|
35
|
+
* @param {Taxa} taxa
|
36
|
+
*/
|
37
|
+
static renderTools(outputDir, taxa) {
|
33
38
|
const names = [];
|
34
|
-
for (
|
39
|
+
for (const taxon of taxa.getTaxonList()) {
|
35
40
|
const row = [];
|
36
|
-
row.push(
|
37
|
-
const cn = taxon.getCommonNames().join(
|
38
|
-
if (
|
39
|
-
row.push(
|
41
|
+
row.push(taxon.getName());
|
42
|
+
const cn = taxon.getCommonNames().join(", ");
|
43
|
+
if (cn) {
|
44
|
+
row.push(cn);
|
40
45
|
}
|
41
46
|
const synonyms = [];
|
42
|
-
for (
|
43
|
-
synonyms.push(
|
47
|
+
for (const syn of taxon.getSynonyms()) {
|
48
|
+
synonyms.push(syn);
|
44
49
|
}
|
45
|
-
if (
|
46
|
-
row[
|
50
|
+
if (synonyms.length > 0) {
|
51
|
+
row[2] = synonyms;
|
47
52
|
}
|
48
|
-
names.push(
|
53
|
+
names.push(row);
|
49
54
|
}
|
50
55
|
|
51
|
-
Files.write(
|
52
|
-
|
56
|
+
Files.write(outputDir + "/_includes/names.json", JSON.stringify(names));
|
53
57
|
}
|
54
|
-
|
55
|
-
|
56
58
|
}
|
57
59
|
|
58
|
-
export { BasePageRenderer };
|
60
|
+
export { BasePageRenderer };
|
package/lib/dateutils.js
CHANGED
@@ -14,30 +14,50 @@ const MONTH_NAMES = [
|
|
14
14
|
];
|
15
15
|
|
16
16
|
class DateUtils {
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
/**
|
18
|
+
* @param {number} monthNum Starting with 1 for January.
|
19
|
+
* @returns {string}
|
20
|
+
*/
|
21
|
+
static getMonthName(monthNum) {
|
22
|
+
return MONTH_NAMES[monthNum - 1];
|
20
23
|
}
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
/**
|
26
|
+
* @param {[number,number]} r1
|
27
|
+
* @param {[number,number]} r2
|
28
|
+
* @returns {boolean}
|
29
|
+
*/
|
30
|
+
static monthRangesOverlap(r1, r2) {
|
31
|
+
/**
|
32
|
+
* @param {[number,number]} r1
|
33
|
+
* @param {[number,number]} r2
|
34
|
+
*/
|
35
|
+
function contains(r1, r2) {
|
36
|
+
/**
|
37
|
+
* @param {number} v
|
38
|
+
* @param {[number,number]} r
|
39
|
+
*/
|
40
|
+
function inRange(v, r) {
|
41
|
+
return v >= r[0] && v <= r[1];
|
27
42
|
}
|
28
|
-
return inRange(
|
43
|
+
return inRange(r1[0], r2) || inRange(r1[1], r2);
|
29
44
|
}
|
30
45
|
|
31
46
|
// If ranges cross into next year, split them in 2.
|
32
|
-
if (
|
33
|
-
return
|
47
|
+
if (r1[0] > r1[1]) {
|
48
|
+
return (
|
49
|
+
this.monthRangesOverlap([r1[0], 12], r2) ||
|
50
|
+
this.monthRangesOverlap([1, r1[1]], r2)
|
51
|
+
);
|
34
52
|
}
|
35
|
-
if (
|
36
|
-
return
|
53
|
+
if (r2[0] > r2[1]) {
|
54
|
+
return (
|
55
|
+
this.monthRangesOverlap(r1, [r2[0], 12]) ||
|
56
|
+
this.monthRangesOverlap(r1, [1, r2[1]])
|
57
|
+
);
|
37
58
|
}
|
38
|
-
return contains(
|
59
|
+
return contains(r1, r2) || contains(r2, r1);
|
39
60
|
}
|
40
|
-
|
41
61
|
}
|
42
62
|
|
43
|
-
export { DateUtils };
|
63
|
+
export { DateUtils };
|
@@ -4,88 +4,115 @@ import { EBookPage } from "../ebookpage.js";
|
|
4
4
|
import { XHTML } from "../xhtml.js";
|
5
5
|
import { Markdown } from "../../markdown.js";
|
6
6
|
import { DateUtils } from "../../dateutils.js";
|
7
|
+
// eslint-disable-next-line no-unused-vars
|
8
|
+
import { Taxon } from "../../taxon.js";
|
7
9
|
|
8
10
|
class TaxonPage extends EBookPage {
|
9
|
-
|
10
11
|
#outputDir;
|
11
12
|
#taxon;
|
12
13
|
#photos;
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
/**
|
16
|
+
*
|
17
|
+
* @param {string} outputDir
|
18
|
+
* @param {Taxon} taxon
|
19
|
+
* @param {*} photos
|
20
|
+
*/
|
21
|
+
constructor(outputDir, taxon, photos) {
|
22
|
+
super(outputDir + "/" + taxon.getFileName(), taxon.getName());
|
16
23
|
this.#outputDir = outputDir;
|
17
24
|
this.#taxon = taxon;
|
18
25
|
this.#photos = photos;
|
19
26
|
}
|
20
27
|
|
21
28
|
renderPageBody() {
|
22
|
-
|
23
|
-
|
29
|
+
/**
|
30
|
+
* @param {Taxon} taxon
|
31
|
+
*/
|
32
|
+
function renderBloomInfo(taxon) {
|
24
33
|
const colors = taxon.getFlowerColors();
|
25
34
|
const monthStart = taxon.getBloomStart();
|
26
35
|
const monthEnd = taxon.getBloomEnd();
|
27
|
-
if (
|
36
|
+
if (!colors && !monthStart) {
|
28
37
|
return "";
|
29
38
|
}
|
30
39
|
let html = "";
|
31
|
-
if (
|
32
|
-
for (
|
33
|
-
html += XHTML.textElement(
|
40
|
+
if (colors) {
|
41
|
+
for (const color of colors) {
|
42
|
+
html += XHTML.textElement("img", "", {
|
43
|
+
src: "./i/f-" + color + ".svg",
|
44
|
+
class: "flr",
|
45
|
+
});
|
34
46
|
}
|
35
47
|
}
|
36
|
-
if (
|
37
|
-
html += XHTML.textElement(
|
38
|
-
|
48
|
+
if (monthStart && monthEnd) {
|
49
|
+
html += XHTML.textElement(
|
50
|
+
"div",
|
51
|
+
DateUtils.getMonthName(monthStart) +
|
52
|
+
"-" +
|
53
|
+
DateUtils.getMonthName(monthEnd)
|
54
|
+
);
|
39
55
|
}
|
40
|
-
return XHTML.wrap(
|
56
|
+
return XHTML.wrap("div", html, { class: "section flr" });
|
41
57
|
}
|
42
58
|
|
43
|
-
|
44
|
-
|
59
|
+
/**
|
60
|
+
* @param {string} name
|
61
|
+
*/
|
62
|
+
function renderCustomText(name) {
|
45
63
|
// See if there is custom text.
|
46
|
-
const fileName =
|
47
|
-
|
64
|
+
const fileName =
|
65
|
+
Config.getPackageDir() + "/data/text/" + name + ".md";
|
66
|
+
if (!Files.exists(fileName)) {
|
48
67
|
return "";
|
49
68
|
}
|
50
|
-
const text = Files.read(
|
51
|
-
return Markdown.strToHTML(
|
69
|
+
const text = Files.read(fileName);
|
70
|
+
return Markdown.strToHTML(text);
|
52
71
|
}
|
53
72
|
|
54
73
|
const name = this.#taxon.getName();
|
55
|
-
let html = XHTML.textElement(
|
74
|
+
let html = XHTML.textElement("h1", name);
|
56
75
|
|
57
76
|
const family = this.#taxon.getFamily();
|
58
|
-
html += XHTML.wrap(
|
77
|
+
html += XHTML.wrap(
|
78
|
+
"div",
|
79
|
+
XHTML.getLink(family.getFileName(), family.getName()),
|
80
|
+
{ class: "section" }
|
81
|
+
);
|
59
82
|
|
60
83
|
const cn = this.#taxon.getCommonNames();
|
61
|
-
if (
|
62
|
-
html += XHTML.textElement(
|
84
|
+
if (cn && cn.length > 0) {
|
85
|
+
html += XHTML.textElement("div", cn.join(", "), {
|
86
|
+
class: "section",
|
87
|
+
});
|
63
88
|
}
|
64
89
|
|
65
|
-
html += renderBloomInfo(
|
90
|
+
html += renderBloomInfo(this.#taxon);
|
66
91
|
|
67
|
-
html += renderCustomText(
|
92
|
+
html += renderCustomText(this.#taxon.getBaseFileName());
|
68
93
|
|
69
|
-
if (
|
94
|
+
if (this.#photos) {
|
70
95
|
let photoHTML = "";
|
71
|
-
for (
|
96
|
+
for (const photo of this.#photos) {
|
72
97
|
const src = photo.getSrc();
|
73
|
-
const dimensions = sizeOf(
|
74
|
-
let img = XHTML.textElement(
|
98
|
+
const dimensions = sizeOf(this.#outputDir + "/" + src);
|
99
|
+
let img = XHTML.textElement("img", "", {
|
100
|
+
src: src,
|
101
|
+
style: "max-width:" + dimensions.width + "px",
|
102
|
+
});
|
75
103
|
const caption = photo.getCaption();
|
76
|
-
if (
|
77
|
-
img += XHTML.textElement(
|
104
|
+
if (caption) {
|
105
|
+
img += XHTML.textElement("figcaption", caption);
|
78
106
|
}
|
79
|
-
photoHTML += XHTML.wrap(
|
107
|
+
photoHTML += XHTML.wrap("figure", img);
|
80
108
|
}
|
81
|
-
if (
|
82
|
-
html += XHTML.wrap(
|
109
|
+
if (photoHTML) {
|
110
|
+
html += XHTML.wrap("div", photoHTML);
|
83
111
|
}
|
84
112
|
}
|
85
113
|
|
86
114
|
return html;
|
87
115
|
}
|
88
|
-
|
89
116
|
}
|
90
117
|
|
91
|
-
export { TaxonPage };
|
118
|
+
export { TaxonPage };
|
package/lib/errorlog.js
CHANGED
@@ -2,30 +2,35 @@ import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
3
3
|
|
4
4
|
class ErrorLog {
|
5
|
-
|
6
5
|
#fileName;
|
7
6
|
#echo;
|
7
|
+
/** @type string[] */
|
8
8
|
#errors = [];
|
9
9
|
|
10
|
-
|
10
|
+
/**
|
11
|
+
* @param {string} fileName
|
12
|
+
* @param {boolean} echo
|
13
|
+
*/
|
14
|
+
constructor(fileName, echo = false) {
|
11
15
|
this.#fileName = fileName;
|
12
16
|
this.#echo = echo;
|
13
17
|
}
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
|
19
|
+
/**
|
20
|
+
* @param {...string} args
|
21
|
+
*/
|
22
|
+
log(...args) {
|
23
|
+
if (this.#echo) {
|
24
|
+
console.log(args.join());
|
18
25
|
}
|
19
|
-
this.#errors.push(
|
26
|
+
this.#errors.push(args.join("\t"));
|
20
27
|
}
|
21
28
|
|
22
29
|
write() {
|
23
30
|
// Make sure directory exists.
|
24
|
-
fs.mkdirSync(
|
25
|
-
|
26
|
-
fs.writeFileSync( this.#fileName, this.#errors.join( "\n" ) );
|
31
|
+
fs.mkdirSync(path.dirname(this.#fileName), { recursive: true });
|
32
|
+
fs.writeFileSync(this.#fileName, this.#errors.join("\n"));
|
27
33
|
}
|
28
|
-
|
29
34
|
}
|
30
35
|
|
31
|
-
export { ErrorLog };
|
36
|
+
export { ErrorLog };
|