diff --git a/README.md b/README.md index 7addd95..d380b96 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,7 @@ This project contains my hand-written HTML homepage for the world-wide-web. It's meant to be deployed as a simple set of files on a server, and doesn't use anything fancy beyond what can be done with a normal text editor and an HTTP file server. + +To develop it locally, it can help to run a local server, so for that, I've +included `local-server.d`, a D script you can run to boot up a server on +`http://localhost:8080`. diff --git a/garden-data-gen/.gitignore b/garden-data-gen/.gitignore new file mode 100644 index 0000000..1706ba0 --- /dev/null +++ b/garden-data-gen/.gitignore @@ -0,0 +1,16 @@ +.dub +docs.json +__dummy.html +docs/ +/garden-data-gen +garden-data-gen.so +garden-data-gen.dylib +garden-data-gen.dll +garden-data-gen.a +garden-data-gen.lib +garden-data-gen-test-* +*.exe +*.pdb +*.o +*.obj +*.lst diff --git a/garden-data-gen/dub.json b/garden-data-gen/dub.json new file mode 100644 index 0000000..cd1852b --- /dev/null +++ b/garden-data-gen/dub.json @@ -0,0 +1,13 @@ +{ + "authors": [ + "Andrew Lalis" + ], + "copyright": "Copyright © 2024, Andrew Lalis", + "dependencies": { + "archive": "~>0.7.1", + "dxml": "~>0.4.4" + }, + "description": "Small app for dynamically generating garden site HTML from data.", + "license": "proprietary", + "name": "garden-data-gen" +} \ No newline at end of file diff --git a/garden-data-gen/dub.selections.json b/garden-data-gen/dub.selections.json new file mode 100644 index 0000000..6e1b9e6 --- /dev/null +++ b/garden-data-gen/dub.selections.json @@ -0,0 +1,7 @@ +{ + "fileVersion": 1, + "versions": { + "archive": "0.7.1", + "dxml": "0.4.4" + } +} diff --git a/garden-data-gen/source/app.d b/garden-data-gen/source/app.d new file mode 100644 index 0000000..65a6c39 --- /dev/null +++ b/garden-data-gen/source/app.d @@ -0,0 +1,29 @@ +import std.stdio; + +import plant_data; +import content_gen; + +import std.algorithm; +import std.array; +import std.path; +import std.file; + +const PLANT_DATA_FILE = "garden-plant-data.ods"; + +void main() { + // Navigate to the project root for all tasks, for simplicity. + while (!exists("index.html") && !exists("upload.sh")) { + string prev = getcwd(); + chdir(".."); + if (getcwd == prev) throw new Exception("Couldn't navigate to the project root."); + } + + writeln("Parsing plant data from " ~ PLANT_DATA_FILE ~ "..."); + PlantData data = parsePlantData(PLANT_DATA_FILE); + writefln!"Read %d species and %d plants."(data.species.length, data.plants.length); + ensureDirectories(data); + writeln("Generating thumbnails for all images..."); + generateAllThumbnails(false); + writeln("Rendering HTML components..."); + renderHTML(data); +} diff --git a/garden-data-gen/source/content_gen.d b/garden-data-gen/source/content_gen.d new file mode 100644 index 0000000..f2ca108 --- /dev/null +++ b/garden-data-gen/source/content_gen.d @@ -0,0 +1,142 @@ +module content_gen; + +import plant_data; +import dom_utils; + +import dxml.writer; +import dxml.util; + +import std.stdio; +import std.path; +import std.array; +import std.algorithm; +import std.file; +import std.conv; + +void renderHTML(PlantData data) { + renderSpeciesCards(data); + renderSpeciesPages(data); +} + +private void injectContent(string filename, string startTag, string endTag, string newContent) { + import std.file; + string data = std.file.readText(filename); + ptrdiff_t startIdx = indexOfStr(data, startTag); + if (startIdx == -1) throw new Exception("Couldn't find start tag " ~ startTag); + ptrdiff_t endIdx = indexOfStr(data, endTag); + if (endIdx == -1) throw new Exception("Couldn't find end tag " ~ endTag); + string prefix = data[0..(startIdx + startTag.length)]; + string suffix = data[endIdx..$]; + string newData = prefix ~ newContent ~ suffix; + std.file.write(filename, newData); +} + +private void renderSpeciesCards(PlantData data) { + string tpl = std.file.readText(buildPath( + "garden-data-gen", "templates", "species-card.html" + )); + Appender!string htmlApp; + foreach (s; data.species) { + string card = replaceAll(tpl, [ + "!NAME!": s.name, + "!SCIENTIFIC_NAME!": s.scientificName, + "!DESCRIPTION!": s.description, + "!LINK!": "garden/species/" ~ s.id ~ ".html", + "!REF_LINK!": s.referenceLink, + "!REF_LINK_TEXT!": s.referenceLink + ]); + ImageFilePair[] imagePairs = getSpeciesImages(s.id); + if (imagePairs.length > 0) { + string imageFile = imagePairs[0].filename; + string thumbnailFile = imagePairs[0].thumbnailFilename; + string imgTag = ""; + card = replaceFirst(card, "!IMAGE!", imgTag); + } else { + card = replaceFirst(card, "!IMAGE!", ""); + } + htmlApp ~= "\n" ~ card; + } + injectContent( + "garden.html", + "", + "", + htmlApp[] + ); +} + +private void renderSpeciesPages(PlantData data) { + string tpl = std.file.readText(buildPath( + "garden-data-gen", "templates", "species-page.html" + )); + string plantCardTpl = std.file.readText(buildPath( + "garden-data-gen", "templates", "plant-card.html" + )); + string speciesPagesDir = buildPath("garden", "species"); + if (!exists(speciesPagesDir)) mkdirRecurse(speciesPagesDir); + foreach (species; data.species) { + Appender!string plantDivsApp; + foreach (plant; data.plantsInSpecies(species)) { + string card = replaceAll(plantCardTpl, [ + "!IDENTIFIER!": plant.identifier, + "!GENERATION!": "F" ~ plant.generation.to!string, + "!DESCRIPTION!": plant.description, + "!PLANTING_INFO!": plant.plantingInfo + ]); + ImageFilePair[] imagePairs = getPlantImages(plant.identifier); + Appender!string imagesApp; + foreach (imagePair; imagePairs) { + import std.format; + const imgTpl = "\n" ~ + " \"\"/\n" ~ + "\n"; + imagesApp ~= format!(imgTpl)( + "../../" ~ imagePair.filename, + imagePair.width, imagePair.height, + "../../" ~ imagePair.thumbnailFilename, + ); + } + card = replaceFirst(card, "!IMAGES!", imagesApp[]); + plantDivsApp ~= card; + } + + string page = replaceAll(tpl, [ + "!HEAD_TITLE!": species.name, + "!PAGE_TITLE!": species.scientificName, + "!ABOUT_TITLE!": "About " ~ species.name, + "!ABOUT_TEXT!": species.description, + "!REF_LINK!": species.referenceLink, + "!REF_LINK_TEXT!": species.referenceLink, + "!PLANTS_DIVS!": plantDivsApp[] + ]); + string pagePath = buildPath(speciesPagesDir, species.id ~ ".html"); + std.file.write(pagePath, page); + } +} + +ptrdiff_t indexOfStr(string source, string target) { + for (size_t i = 0; i < source.length - target.length; i++) { + if (source[i..i+target.length] == target) { + return i; + } + } + return -1; +} + +string replaceFirst(string source, string from, string to) { + ptrdiff_t idx = indexOfStr(source, from); + if (idx == -1) return source; + string pre = idx == 0 ? "" : source[0..idx]; + string post = source[idx+from.length..$]; + return pre ~ to ~ post; +} + +unittest { + assert(replaceFirst("

!TEST

", "!TEST", "test") == "

test

"); +} + +string replaceAll(string source, string[string] values) { + foreach (k, v; values) { + source = replaceFirst(source, k, v); + } + return source; +} diff --git a/garden-data-gen/source/dom_utils.d b/garden-data-gen/source/dom_utils.d new file mode 100644 index 0000000..be3bf1a --- /dev/null +++ b/garden-data-gen/source/dom_utils.d @@ -0,0 +1,77 @@ +module dom_utils; + +import dxml.dom; +import dxml.writer; +import dxml.util; + +import std.array; +import std.algorithm; + +DOMEntity!string findDOMChild( + DOMEntity!string parent, + string elementName, + string[string] attributes = string[string].init +) { + foreach (child; parent.children) { + if (child.type == EntityType.elementStart && child.name == elementName) { + if (attributes.length == 0) return child; + bool attributesMatch = true; + foreach (attrName, attrValue; attributes) { + bool hasValue = false; + foreach (attr; child.attributes) { + if (attr.name == attrName && attr.value == attrValue) { + hasValue = true; + break; + } + } + if (!hasValue) { + attributesMatch = false; + break; + } + } + if (attributesMatch) return child; + } + } + throw new Exception("Could not find child element " ~ elementName ~ " in " ~ parent.name); +} + +DOMEntity!string[] findDOMChildren(DOMEntity!string parent, string name) { + DOMEntity!string[] matches; + auto app = appender(&matches); + foreach (child; parent.children) { + if (child.type == EntityType.elementStart && child.name == name) { + app ~= child; + } + } + return matches; +} + +string readTableCellText(DOMEntity!string cell) { + if ( + cell.type == EntityType.elementStart && + cell.children.length > 0 && + cell.children[0].type == EntityType.elementStart && + cell.children[0].children.length == 1 && + cell.children[0].children[0].type == EntityType.text + ) { + return cell.children[0].children[0].text.decodeXML; + } + return null; +} + +void writeStartTagWithAttrs(O)( + ref XMLWriter!O writer, + string tag, + string[string] attributes, + EmptyTag emptyTag = EmptyTag.no +) { + writer.openStartTag(tag); + foreach (k, v; attributes) { + writer.writeAttr(k, v); + } + writer.closeStartTag(emptyTag); +} + +void writeStartTagWithClass(O)(ref XMLWriter!O writer, string tag, string classValue) { + writer.writeStartTagWithAttrs(tag, ["class": classValue]); +} diff --git a/garden-data-gen/source/plant_data.d b/garden-data-gen/source/plant_data.d new file mode 100644 index 0000000..fc302d9 --- /dev/null +++ b/garden-data-gen/source/plant_data.d @@ -0,0 +1,270 @@ +module plant_data; + +import dom_utils; + +import std.stdio; +import std.array; +import std.algorithm; +import std.path; +import std.file; +import std.datetime; +import std.typecons; + +import dxml.dom; + +struct Species { + string id; + string name; + string scientificName; + string description; + string referenceLink; +} + +struct Plant { + string speciesScientificName; + string identifier; + uint generation; + string plantingInfo; + string description; +} + +string speciesId(string scientificName) { + import std.string; + import std.regex; + return scientificName + .replaceAll(ctRegex!(`\s+`), "-") + .replaceAll(ctRegex!(`ñ`), "n") + .replaceAll(ctRegex!(`["“”\.]`), "") + .toLower; +} + +struct PlantData { + Species[] species; + Plant[] plants; + + Plant[] plantsInSpecies(Species speciesItem) { + Appender!(Plant[]) app; + foreach (plant; plants) { + if (plant.speciesScientificName == speciesItem.scientificName) { + app ~= plant; + } + } + Plant[] results = app[]; + sort!((a, b) => a.identifier < b.identifier)(results); + return results; + } + + Species getSpecies(string name) { + foreach (s; species) { + if (s.scientificName == name) return s; + } + throw new Exception("No species with name " ~ name); + } + + Plant getPlant(string identifier) { + foreach (p; plants) { + if (p.identifier == identifier) return p; + } + throw new Exception("No plant with identifier " ~ identifier); + } +} + +struct ImageFilePair { + string filename; + uint width, height; + string thumbnailFilename; + uint thumbnailWidth, thumbnailHeight; +} + +PlantData parsePlantData(string filename) { + import archive.zip; + ZipArchive zip = new ZipArchive(std.file.read(filename)); + auto contentZipEntry = zip.getFile("content.xml"); + if (contentZipEntry is null) throw new Exception("Couldn't find content.xml in " ~ filename); + DOMEntity!string dom = parseDOM(cast(string) contentZipEntry.data()); + DOMEntity!string spreadsheet = dom.findDOMChild("office:document-content") + .findDOMChild("office:body") + .findDOMChild("office:spreadsheet"); + DOMEntity!string speciesTable = spreadsheet.findDOMChild("table:table", ["table:name": "Species"]); + DOMEntity!string[] speciesRows = speciesTable.findDOMChildren("table:table-row")[1..$]; + DOMEntity!string plantsTable = spreadsheet.findDOMChild("table:table", ["table:name": "Plants"]); + DOMEntity!string[] plantRows = plantsTable.findDOMChildren("table:table-row")[1..$]; + + PlantData result; + auto speciesAppender = appender(&result.species); + foreach (row; speciesRows) { + if (row.children.length < 4) continue; + Species species; + species.name = readTableCellText(row.children[0]); + species.scientificName = readTableCellText(row.children[1]); + species.description = readTableCellText(row.children[2]); + species.referenceLink = readTableCellText(row.children[3]); + species.id = speciesId(species.scientificName); + speciesAppender ~= species; + } + sort!((a, b) => a.name < b.name)(result.species); + + auto plantAppender = appender(&result.plants); + foreach (row; plantRows) { + if (row.children.length < 4) continue; + Plant plant; + plant.speciesScientificName = readTableCellText(row.children[0]); + plant.identifier = readTableCellText(row.children[1]); + string fGenStr = readTableCellText(row.children[2]); + import std.conv : to; + plant.generation = fGenStr[1..$].to!uint; + plant.plantingInfo = readTableCellText(row.children[3]); + if (row.children.length > 4) { + plant.description = readTableCellText(row.children[4]); + } + plantAppender ~= plant; + } + sort!((a, b) => a.identifier < b.identifier)(result.plants); + + return result; +} + +void ensureDirectories(PlantData data) { + string basePath = buildPath("images", "garden"); + if (!exists(basePath)) mkdirRecurse(basePath); + string speciesDir = buildPath(basePath, "species"); + if (!exists(speciesDir)) mkdir(speciesDir); + foreach (s; data.species) { + string thisSpeciesDir = buildPath(speciesDir, s.id); + if (!exists(thisSpeciesDir)) mkdir(thisSpeciesDir); + } + string plantsDir = buildPath(basePath, "plants"); + if (!exists(plantsDir)) mkdir(plantsDir); + foreach (p; data.plants) { + string thisPlantDir = buildPath(plantsDir, p.identifier); + if (!exists(thisPlantDir)) mkdir(thisPlantDir); + } +} + +ImageFilePair[] getPlantImages(string identifier) { + string plantDir = buildPath("images", "garden", "plants", identifier); + if (!exists(plantDir)) return []; + Appender!(ImageFilePair[]) app; + foreach (entry; dirEntries(plantDir, SpanMode.shallow, false)) { + if (entry.name.endsWith(".jpg") && !entry.name.endsWith(".thumb.jpg")) { + ImageFilePair pair; + pair.filename = entry.name; + getImageSize(entry.name, pair.width, pair.height); + string thumbnailFilename = buildPath(plantDir, baseName(entry.name, ".jpg") ~ ".thumb.jpg"); + if (exists(thumbnailFilename)) { + pair.thumbnailFilename = thumbnailFilename; + getImageSize(thumbnailFilename, pair.thumbnailWidth, pair.thumbnailHeight); + } + app ~= pair; + } + } + ImageFilePair[] images = app[]; + sort!((a, b) { + Nullable!DateTime tsA = getImageTimestamp(a.filename); + Nullable!DateTime tsB = getImageTimestamp(b.filename); + if (tsA.isNull && tsB.isNull) return a.filename < b.filename; + if (tsA.isNull) return true; + if (tsB.isNull) return false; + return tsA.get < tsB.get; + })(images); + return images; +} + +ImageFilePair[] getSpeciesImages(string speciesId) { + string speciesDir = buildPath("images", "garden", "species", speciesId); + if (!exists(speciesDir)) return []; + Appender!(ImageFilePair[]) app; + foreach (entry; dirEntries(speciesDir, SpanMode.shallow, false)) { + if (entry.name.endsWith(".jpg") && !entry.name.endsWith(".thumb.jpg")) { + ImageFilePair pair; + pair.filename = entry.name; + getImageSize(entry.name, pair.width, pair.height); + string thumbnailFilename = buildPath(speciesDir, baseName(entry.name, ".jpg") ~ ".thumb.jpg"); + if (exists(thumbnailFilename)) { + pair.thumbnailFilename = thumbnailFilename; + getImageSize(thumbnailFilename, pair.thumbnailWidth, pair.thumbnailHeight); + } + app ~= pair; + } + } + ImageFilePair[] images = app[]; + sort!((a, b) { + Nullable!DateTime tsA = getImageTimestamp(a.filename); + Nullable!DateTime tsB = getImageTimestamp(b.filename); + if (tsA.isNull && tsB.isNull) return a.filename < b.filename; + if (tsA.isNull) return true; + if (tsB.isNull) return false; + return tsA.get < tsB.get; + })(images); + return images; +} + +void getImageSize(string filePath, out uint width, out uint height) { + import std.process; + import std.format; + auto result = execute(["identify", "-ping", "-format", "'%w %h'", filePath]); + if (result.status != 0) throw new Exception("Failed to get image size of " ~ filePath); + formattedRead!"'%d %d'"(result.output, width, height); +} + +Nullable!DateTime getImageTimestamp(string filePath) { + import std.regex; + import std.conv; + auto r = ctRegex!(`\d{8}_\d{6}`); + auto cap = matchFirst(baseName(filePath), r); + if (cap.empty) return Nullable!DateTime.init; + string text = cap[0]; + return nullable(DateTime( + text[0..4].to!int, + text[4..6].to!int, + text[6..8].to!int, + text[9..11].to!int, + text[11..13].to!int, + text[13..15].to!int + )); +} + +void generateAllThumbnails(bool regen = false) { + string plantsDir = buildPath("images", "garden", "plants"); + foreach (entry; dirEntries(plantsDir, SpanMode.shallow, false)) { + generateThumbnails(entry.name, regen); + } + string speciesDir = buildPath("images", "garden", "species"); + foreach (entry; dirEntries(speciesDir, SpanMode.shallow, false)) { + generateThumbnails(entry.name, regen); + } +} + +void generateThumbnails(string dir, bool regen) { + import std.process; + if (regen) { + // Remove all thumbnails first. + foreach (entry; dirEntries(dir, SpanMode.shallow, false)) { + if (entry.name.endsWith(".thumb.jpg")) { + std.file.remove(entry.name); + } + } + } + foreach (entry; dirEntries(dir, SpanMode.shallow, false)) { + if (entry.name.endsWith(".jpg") && !entry.name.endsWith(".thumb.jpg")) { + string filenameWithoutExt = baseName(entry.name, ".jpg"); + string outputFilePath = buildPath(dir, filenameWithoutExt ~ ".thumb.jpg"); + if (exists(outputFilePath)) continue; + Pid pid = spawnProcess( + [ + "convert", + entry.name, + "-strip", + "-interlace", "JPEG", + "-sampling-factor", "4:2:0", + "-colorspace", "RGB", + "-quality", "85%", + "-geometry", "x200", + outputFilePath + ] + ); + int exitCode = wait(pid); + if (exitCode != 0) throw new Exception("Thumbnail generation process failed."); + } + } +} diff --git a/garden-data-gen/templates/plant-card.html b/garden-data-gen/templates/plant-card.html new file mode 100644 index 0000000..05f01e4 --- /dev/null +++ b/garden-data-gen/templates/plant-card.html @@ -0,0 +1,14 @@ +
+
+

!IDENTIFIER!

+

Generation !GENERATION!

+

!DESCRIPTION!

+

!PLANTING_INFO!

+
+
+

Images

+ +
+
\ No newline at end of file diff --git a/garden-data-gen/templates/species-card.html b/garden-data-gen/templates/species-card.html new file mode 100644 index 0000000..9c5ca52 --- /dev/null +++ b/garden-data-gen/templates/species-card.html @@ -0,0 +1,19 @@ +
+
+

+ + !NAME! + +

+

+ !SCIENTIFIC_NAME! +

+

!DESCRIPTION!

+ +
+ !IMAGE! +
\ No newline at end of file diff --git a/garden-data-gen/templates/species-page.html b/garden-data-gen/templates/species-page.html new file mode 100644 index 0000000..14b6fec --- /dev/null +++ b/garden-data-gen/templates/species-page.html @@ -0,0 +1,71 @@ + + + + + Andrew's Garden: !HEAD_TITLE! + + + + + + + + + + + + + + + + + +
+
+

!ABOUT_TITLE!

+

!ABOUT_TEXT!

+

+ + !REF_LINK_TEXT! + +

+
+
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+ !PLANTS_DIVS! +
+
+ diff --git a/garden-plant-data.ods b/garden-plant-data.ods new file mode 100644 index 0000000..b300ef3 Binary files /dev/null and b/garden-plant-data.ods differ diff --git a/garden.html b/garden.html index 8c88609..f36e397 100644 --- a/garden.html +++ b/garden.html @@ -12,6 +12,18 @@ + + + @@ -43,7 +55,16 @@

My garden is just a small patch of land outside the condo my wife and I call home. It's located in USDA hardiness zone 10a (actually, right on the border of 9b and 10a) in Florida, USA, and I've been trying to beautify the space with a mixture of native and non-invasive ornamental shrubs, herbs, and other plants. While currently there are quite a few non-native species, I'm trying to slowly migrate to entirely native plants and non-invasive food crops, especially those that can help rebuild the soil quality; it's pretty much just sandy clay here.

- The main area of my garden + +

If you'd like to leave feedback about the garden, or to request seeds, cuttings, please do contact me via the info on my contact page. I generally try to keep a supply of seeds for as many of my plants as possible, but as always, it depends.

@@ -53,253 +74,360 @@

Table of Contents

    -
  1. Plants
  2. +
  3. Species
  4. Hardscaping

-

Plants

+

Species

- Here's a detailed list of all the plants I've got in my garden. For each plant, I try to include its scientific name, place of origin, and a small description, usually taken from Wikipedia or other sources listed at the bottom of each plant's info. + Here's a detailed list of all the species I've got in my garden. For each species, I try to include its scientific name, place of origin, and a small description, usually taken from Wikipedia or other sources listed at the bottom of each species' info.

- -
-
-

Bird Pepper

-

Capsicum annuum var. glabriusculum

-

A small chili pepper variety native to southern North America and northern South America. It's the only pepper species native to the Floridian peninsula.

-

Transplanted from a 1 gallon pot, on February 10th, 2024.

- -
- Bird Pepper plant -
- -
-
-

Blue Pacific Juniper

-

Juniperus conferta

-

A species of Juniper native to Japan, that grows on sand dunes and other acidic/alkaline soils with good drainage. It forms a groundcover if left unattended.

-

Transplanted on the 2nd of March, 2024.

- -
- Blue Pacific Juniper plant -
- -
-
-

Browne's Savory

-

Clinopodium brownei

-

A sprawling perennial herb found natively in the coastal plains and marshes of the southeastern United States.

-

Transplanted on the 10th of February, 2024.

- -
- Browne's Savory plant -
- -
-
-

Catnip

-

Nepeta cataria

-

Species of mint that about 2/3rds of cats are attracted to.

-

Native to southern and eastern Europe, the Middle East, Central Asia, and parts of China.

-

Transplanted from a small pot bought at a pet store, sometime in October, 2023.

- -
- Catnip plant -
- -
-
-

Garlic Chives

-

Allium tuberosum

-

A clump-forming perennial herb native to the Chinese province of Shanxi, but now found pretty much worldwide.

-

Planted from seed on the 2nd of March, 2024.

- -
- Chives plant -
- -
-
-

Cilantro

-

Coriandrum sativum

-

Also known as Coriander, this is an annual herb that most people enjoy has having a tart, lemon/lime taste. It's native to the mediterranean basin, but is grown worldwide.

- -
- Cilantro plant -
- -
-
-

Creeping Sage

-

Salvia misella

-

Also known as tropical sage, it is an annual herb growing throughout the tropical Americas.

-

Transplanted on the 9th of March, 2024.

- -
- Creeping Sage plant -
- -
-
-

Dwarf Shiny-Leaf Coffee

-

Psychotria nervosa

-

A small shrub with shiny evergreen leaves that produces beans similar to coffee, but without any caffeine. Native to the southeastern United States.

-

Transplanted on the 10th of February, 2024.

- -
- Dwarf Coffee plant -
- -
-
-

Kimberley Queen Fern

-

Nephrolepis obliterata

-

A species of fern originating from Australia, but grown worldwide.

-

Transplanted on the 2nd of March, 2024.

- -
- Fern plant -
- -
-
-

Foxtail Fern

-

Asparagus aethiopicus

-

A plant native to South Africa that's grown ornamentally in many places. Its roots form water-storage tubers.

-

Transplanted in February, 2024.

- -
- Foxtail Fern plant -
- -
-
-

Inchplant

-

Tradescantia zebrina

-

A species of creeping vine plant that forms a dense groundcover in shaded areas. It's native to Mexico, Central America, and Colombia.

-

Transplanted on the 8th of March, 2024.

- -
- Inchplant -
- -
-
-

Jalapeño

-

Capsicum annuum var. jalapeño

-

A medium-sized chili pepper species with relatively mild pungency. It's commonly picked and consumed while still green, and were originally cultivated by the Aztecs.

-

Transplanted on the 2nd of March, 2024.

- -
- Jalapeño plant -
- -
-
-

Marigold

-

Tagetes erecta

-

A species of flowering plant native to the Americas that is widely used as an ornamental flower, and was originally called by its Nahuatl name, cempoalxóchitl.

-

Planted from seed in February, 2024.

- -
- Marigold plant -
- -
-
-

Perennial Petunia

-

Ruellia caroliniensis

-

A wild petunia with blue or violet flowers that's native to the southeastern United States.

-

Transplanted on the 10th of February, 2024.

- -
- Petunia plant -
- -
-
-

Mona Lavender

-

Plectranthus "Mona Lavender"

-

A hybrid of Plectranthus saccatus and Plectranthus hilliardiae, this is a broadleaf evergreen shrub in the mint family, which produces many small purple flowers.

-

Transplanted in February, 2024.

- -
- Plectranthus plant -
- -
-
-

English Thyme

-

Thymus vulgaris

-

A flowering plant in the mint family, native to southern Europe, that's commonly used as an herb.

-

Planted from seed in March, 2024.

- -
- Thyme plant -
- -
-
-

Tropical Milkweed

-

Asclepias curassavica

-

A flowering milkweed species native to the American tropics which is a food source for Monarch butterflies.

-

Note: Research suggests that this plant may disrupt migratory patterns in butterflies when planted in northern United States habitats. I'm working on replacing it with native milkweed variants.

-

Transplanted in February, 2024.

- -
- Tropical Milkweed plant -
- -
-
-

Wood Sage

-

Teucrium canadense

-

A perennial herb native to North America, growing in moist grasslands, forest edges, marshes, and on roadsides.

-

Transplanted on the 9th of March, 2024.

- -
- Wood Sage plant -
- + +
+
+

+ + Bird Pepper + +

+

+ Capsicum annuum var. glabriusculum +

+

A small chili pepper variety native to southern North America and northern South America. It's the only pepper species native to the Floridian peninsula.

+ +
+ +
+
+
+

+ + Blue Pacific Juniper + +

+

+ Juniperus conferta +

+

A species of Juniper native to Japan, that grows on sand dunes and other acidic/alkaline soils with good drainage. It forms a groundcover if left unattended.

+ +
+ +
+
+
+

+ + Browne’s Savory + +

+

+ Clinopodium brownei +

+

A sprawling perennial herb found natively in the coastal plains and marshes of the southeastern United States.

+ +
+ +
+
+
+

+ + Catnip + +

+

+ Nepeta cataria +

+

Species of mint that about 2/3rds of cats are attracted to. Native to southern and eastern Europe, the Middle East, Central Asia, and parts of China.

+ +
+ +
+
+
+

+ + Cilantro + +

+

+ Coriandrum sativum +

+

Also known as Coriander, this is an annual herb that most people enjoy has having a tart, lemon/lime taste. It's native to the Mediterranean basin, but is grown worldwide.

+ +
+ +
+
+
+

+ + Creeping Sage + +

+

+ Salvia misella +

+

Also known as tropical sage, it is an annual herb growing throughout the tropical Americas.

+ +
+ +
+
+
+

+ + Dwarf Shiny-Leaf Coffee + +

+

+ Psychotria nervosa +

+

A small shrub with shiny evergreen leaves that produces beans similar to coffee, but without any caffeine. Native to the southeastern United States.

+ +
+ +
+
+
+

+ + English Thyme + +

+

+ Thymus vulgaris +

+

A flowering plant in the mint family, native to southern Europe, that's commonly used as an herb.

+ +
+ +
+
+
+

+ + Foxtail Fern + +

+

+ Asparagus aethiopicus +

+

A plant native to South Africa that's grown ornamentally in many places. Its roots form water-storage tubers.

+ +
+ +
+
+
+

+ + Garlic Chives + +

+

+ Allium tuberosum +

+

A clump-forming perennial herb native to the Chinese province of Shanxi, but now found pretty much worldwide.

+ +
+ +
+
+
+

+ + Inchplant + +

+

+ Trandescantia zebrina +

+

A species of creeping vine plant that forms a dense groundcover in shaded areas. It's native to Mexico, Central America, and Colombia.

+ +
+ +
+
+
+

+ + Jalapeño + +

+

+ Capsicum annuum var. jalapeño +

+

A medium-sized chili pepper species with relatively mild pungency. It's commonly picked and consumed while still green, and were originally cultivated by the Aztecs.

+ +
+ +
+
+
+

+ + Kimberley Queen Fern + +

+

+ Nephrolepis obliterata +

+

A species of fern originating from Australia, but grown worldwide.

+ +
+ +
+
+
+

+ + Marigold + +

+

+ Tagetes erecta +

+

A species of flowering plant native to the Americas that is widely used as an ornamental flower, and was originally called by its Nahuatl name, cempoalxóchitl.

+ +
+ +
+
+
+

+ + Mona Lavender + +

+

+ Plectranthus “Mona Lavender” +

+

A hybrid of Plectranthus saccatus and Plectranthus hilliardiae, this is a broadleaf evergreen shrub in the mint family, which produces many small purple flowers.

+ +
+ +
+
+
+

+ + Perennial Petunia + +

+

+ Ruellia caroliniensis +

+

A wild petunia with blue or violet flowers that's native to the southeastern United States.

+ +
+ +
+
+
+

+ + Tropical Milkweed + +

+

+ Asclepias curassavica +

+

A flowering milkweed species native to the American tropics which is a food source for Monarch butterflies. Note: Research suggests that this plant may disrupt migratory patterns in butterflies when planted in northern United States habitats. I'm working on replacing it with native milkweed variants.

+ +
+ +
+
+
+

+ + Wood Sage + +

+

+ Teucrium canadense +

+

A perennial herb native to North America, growing in moist grasslands, forest edges, marshes, and on roadsides.

+ +
+ +
diff --git a/garden/species/allium-tuberosum.html b/garden/species/allium-tuberosum.html new file mode 100644 index 0000000..096ded8 --- /dev/null +++ b/garden/species/allium-tuberosum.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Garlic Chives + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

garlic-chives-001

+

Generation F1

+

+

Planted from seed on the 2nd of March, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/asclepias-curassavica.html b/garden/species/asclepias-curassavica.html new file mode 100644 index 0000000..f6acd00 --- /dev/null +++ b/garden/species/asclepias-curassavica.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Tropical Milkweed + + + + + + + + + + + + + + + + + +
+
+

About Tropical Milkweed

+

A flowering milkweed species native to the American tropics which is a food source for Monarch butterflies. Note: Research suggests that this plant may disrupt migratory patterns in butterflies when planted in northern United States habitats. I'm working on replacing it with native milkweed variants.

+

+ + https://en.wikipedia.org/wiki/Asclepias_curassavica + +

+
+
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

tropical-milkweed-001

+

Generation F1

+

+

Transplanted in February, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/asparagus-aethiopicus.html b/garden/species/asparagus-aethiopicus.html new file mode 100644 index 0000000..7fcad54 --- /dev/null +++ b/garden/species/asparagus-aethiopicus.html @@ -0,0 +1,100 @@ + + + + + Andrew's Garden: Foxtail Fern + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

foxtail-fern-001

+

Generation F1

+

The foxtail fern in the bottom-left corner of the garden, when looking from the front. It’s right by the sidewalk.

+

Transplanted in February, 2024.

+
+
+

Images

+ +
+
+
+

foxtail-fern-002

+

Generation F1

+

The foxtail fern that’s situated right in the middle of the garden, nearby the first bird-pepper plant.

+

Transplanted in February, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/capsicum-annuum-var-glabriusculum.html b/garden/species/capsicum-annuum-var-glabriusculum.html new file mode 100644 index 0000000..d73a109 --- /dev/null +++ b/garden/species/capsicum-annuum-var-glabriusculum.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Bird Pepper + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

bird-pepper-001

+

Generation F1

+

My first bird pepper plant, acquired from a local nursery.

+

Transplanted from a 1 gallon pot, on February 10th, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/capsicum-annuum-var-jalapeno.html b/garden/species/capsicum-annuum-var-jalapeno.html new file mode 100644 index 0000000..5b916e9 --- /dev/null +++ b/garden/species/capsicum-annuum-var-jalapeno.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Jalapeño + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

jalapeno-001

+

Generation F1

+

+

Transplanted on the 2nd of March, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/clinopodium-brownei.html b/garden/species/clinopodium-brownei.html new file mode 100644 index 0000000..ac3a179 --- /dev/null +++ b/garden/species/clinopodium-brownei.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Browne’s Savory + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

brownes-savory-001

+

Generation F1

+

+

Transplanted on the 10th of February, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/coriandrum-sativum.html b/garden/species/coriandrum-sativum.html new file mode 100644 index 0000000..5e602ea --- /dev/null +++ b/garden/species/coriandrum-sativum.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Cilantro + + + + + + + + + + + + + + + + + +
+
+

About Cilantro

+

Also known as Coriander, this is an annual herb that most people enjoy has having a tart, lemon/lime taste. It's native to the Mediterranean basin, but is grown worldwide.

+

+ + https://en.wikipedia.org/wiki/Coriander + +

+
+
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

coriander-001

+

Generation F1

+

+

Planted from seed on the 2nd of March, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/juniperus-conferta.html b/garden/species/juniperus-conferta.html new file mode 100644 index 0000000..f1a6ee5 --- /dev/null +++ b/garden/species/juniperus-conferta.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Blue Pacific Juniper + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

blue-juniper-001

+

Generation F1

+

A nice-looking juniper shrub I got at Lowe’s. It’s located right on the corner of my garden.

+

Transplanted on the 2nd of March, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/nepeta-cataria.html b/garden/species/nepeta-cataria.html new file mode 100644 index 0000000..5c130bf --- /dev/null +++ b/garden/species/nepeta-cataria.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Catnip + + + + + + + + + + + + + + + + + +
+
+

About Catnip

+

Species of mint that about 2/3rds of cats are attracted to. Native to southern and eastern Europe, the Middle East, Central Asia, and parts of China.

+

+ + https://en.wikipedia.org/wiki/Catnip + +

+
+
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

catnip-001

+

Generation F1

+

+

Transplanted from a small pot bought at a pet store, sometime in October, 2023.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/nephrolepis-obliterata.html b/garden/species/nephrolepis-obliterata.html new file mode 100644 index 0000000..939de46 --- /dev/null +++ b/garden/species/nephrolepis-obliterata.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Kimberley Queen Fern + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

kimberley-fern-001

+

Generation F1

+

+

Transplanted on the 2nd of March, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/plectranthus-mona-lavender.html b/garden/species/plectranthus-mona-lavender.html new file mode 100644 index 0000000..3fe4463 --- /dev/null +++ b/garden/species/plectranthus-mona-lavender.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Mona Lavender + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

mona-lavender-001

+

Generation F1

+

The mona lavender that’s on the right side of the tree.

+

Transplanted in February, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/psychotria-nervosa.html b/garden/species/psychotria-nervosa.html new file mode 100644 index 0000000..b885ba2 --- /dev/null +++ b/garden/species/psychotria-nervosa.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Dwarf Shiny-Leaf Coffee + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

dwarf-coffee-001

+

Generation F1

+

+

Transplanted on the 10th of February, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/ruellia-caroliniensis.html b/garden/species/ruellia-caroliniensis.html new file mode 100644 index 0000000..00fc4fb --- /dev/null +++ b/garden/species/ruellia-caroliniensis.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Perennial Petunia + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

carolina-petunia-001

+

Generation F1

+

+

Transplanted on the 10th of February, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/salvia-misella.html b/garden/species/salvia-misella.html new file mode 100644 index 0000000..50ba31a --- /dev/null +++ b/garden/species/salvia-misella.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Creeping Sage + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

creeping-sage-001

+

Generation F1

+

+

Transplanted on the 9th of March, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/tagetes-erecta.html b/garden/species/tagetes-erecta.html new file mode 100644 index 0000000..cb16dcf --- /dev/null +++ b/garden/species/tagetes-erecta.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Marigold + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

marigold-001

+

Generation F1

+

The second marigold from the left, when viewing the garden from the front.

+

Planted from seed in February, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/teucrium-canadense.html b/garden/species/teucrium-canadense.html new file mode 100644 index 0000000..23cf64c --- /dev/null +++ b/garden/species/teucrium-canadense.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Wood Sage + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

wood-sage-001

+

Generation F1

+

+

Transplanted on the 9th of March, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/thymus-vulgaris.html b/garden/species/thymus-vulgaris.html new file mode 100644 index 0000000..4714b17 --- /dev/null +++ b/garden/species/thymus-vulgaris.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: English Thyme + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

thyme-001

+

Generation F1

+

+

Planted from seed in March, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/garden/species/trandescantia-zebrina.html b/garden/species/trandescantia-zebrina.html new file mode 100644 index 0000000..d5c508f --- /dev/null +++ b/garden/species/trandescantia-zebrina.html @@ -0,0 +1,87 @@ + + + + + Andrew's Garden: Inchplant + + + + + + + + + + + + + + + + + +
+ +
+

Plants

+

+ Here's a detailed list of all plants I have of this species. +

+
+
+

inchplant-001

+

Generation F1

+

+

Transplanted on the 8th of March, 2024.

+
+
+

Images

+ +
+
+
+
+ diff --git a/images/garden/bird-pepper_20240310_113310.jpg b/images/garden/plants/bird-pepper-001/bird-pepper_20240310_113310.jpg similarity index 100% rename from images/garden/bird-pepper_20240310_113310.jpg rename to images/garden/plants/bird-pepper-001/bird-pepper_20240310_113310.jpg diff --git a/images/garden/plants/bird-pepper-001/bird-pepper_20240310_113310.thumb.jpg b/images/garden/plants/bird-pepper-001/bird-pepper_20240310_113310.thumb.jpg new file mode 100644 index 0000000..14dee19 Binary files /dev/null and b/images/garden/plants/bird-pepper-001/bird-pepper_20240310_113310.thumb.jpg differ diff --git a/images/garden/blue-juniper_20240310_113334.jpg b/images/garden/plants/blue-juniper-001/blue-juniper_20240310_113334.jpg similarity index 100% rename from images/garden/blue-juniper_20240310_113334.jpg rename to images/garden/plants/blue-juniper-001/blue-juniper_20240310_113334.jpg diff --git a/images/garden/plants/blue-juniper-001/blue-juniper_20240310_113334.thumb.jpg b/images/garden/plants/blue-juniper-001/blue-juniper_20240310_113334.thumb.jpg new file mode 100644 index 0000000..1b72f4b Binary files /dev/null and b/images/garden/plants/blue-juniper-001/blue-juniper_20240310_113334.thumb.jpg differ diff --git a/images/garden/browns-savory_20240310_113313.jpg b/images/garden/plants/brownes-savory-001/browns-savory_20240310_113313.jpg similarity index 100% rename from images/garden/browns-savory_20240310_113313.jpg rename to images/garden/plants/brownes-savory-001/browns-savory_20240310_113313.jpg diff --git a/images/garden/plants/brownes-savory-001/browns-savory_20240310_113313.thumb.jpg b/images/garden/plants/brownes-savory-001/browns-savory_20240310_113313.thumb.jpg new file mode 100644 index 0000000..1262fa7 Binary files /dev/null and b/images/garden/plants/brownes-savory-001/browns-savory_20240310_113313.thumb.jpg differ diff --git a/images/garden/perennial-petunia_20240310_113346.jpg b/images/garden/plants/carolina-petunia-001/perennial-petunia_20240310_113346.jpg similarity index 100% rename from images/garden/perennial-petunia_20240310_113346.jpg rename to images/garden/plants/carolina-petunia-001/perennial-petunia_20240310_113346.jpg diff --git a/images/garden/plants/carolina-petunia-001/perennial-petunia_20240310_113346.thumb.jpg b/images/garden/plants/carolina-petunia-001/perennial-petunia_20240310_113346.thumb.jpg new file mode 100644 index 0000000..d143dde Binary files /dev/null and b/images/garden/plants/carolina-petunia-001/perennial-petunia_20240310_113346.thumb.jpg differ diff --git a/images/garden/catnip_20240310_113153.jpg b/images/garden/plants/catnip-001/catnip_20240310_113153.jpg similarity index 100% rename from images/garden/catnip_20240310_113153.jpg rename to images/garden/plants/catnip-001/catnip_20240310_113153.jpg diff --git a/images/garden/plants/catnip-001/catnip_20240310_113153.thumb.jpg b/images/garden/plants/catnip-001/catnip_20240310_113153.thumb.jpg new file mode 100644 index 0000000..940fe59 Binary files /dev/null and b/images/garden/plants/catnip-001/catnip_20240310_113153.thumb.jpg differ diff --git a/images/garden/cilantro_20240310_113220.jpg b/images/garden/plants/coriander-001/cilantro_20240310_113220.jpg similarity index 100% rename from images/garden/cilantro_20240310_113220.jpg rename to images/garden/plants/coriander-001/cilantro_20240310_113220.jpg diff --git a/images/garden/plants/coriander-001/cilantro_20240310_113220.thumb.jpg b/images/garden/plants/coriander-001/cilantro_20240310_113220.thumb.jpg new file mode 100644 index 0000000..9a1f2a4 Binary files /dev/null and b/images/garden/plants/coriander-001/cilantro_20240310_113220.thumb.jpg differ diff --git a/images/garden/creeping-sage_20240310_113303.jpg b/images/garden/plants/creeping-sage-001/creeping-sage_20240310_113303.jpg similarity index 100% rename from images/garden/creeping-sage_20240310_113303.jpg rename to images/garden/plants/creeping-sage-001/creeping-sage_20240310_113303.jpg diff --git a/images/garden/plants/creeping-sage-001/creeping-sage_20240310_113303.thumb.jpg b/images/garden/plants/creeping-sage-001/creeping-sage_20240310_113303.thumb.jpg new file mode 100644 index 0000000..7b0c3a6 Binary files /dev/null and b/images/garden/plants/creeping-sage-001/creeping-sage_20240310_113303.thumb.jpg differ diff --git a/images/garden/dwarf-coffee_20240310_113259.jpg b/images/garden/plants/dwarf-coffee-001/dwarf-coffee_20240310_113259.jpg similarity index 100% rename from images/garden/dwarf-coffee_20240310_113259.jpg rename to images/garden/plants/dwarf-coffee-001/dwarf-coffee_20240310_113259.jpg diff --git a/images/garden/plants/dwarf-coffee-001/dwarf-coffee_20240310_113259.thumb.jpg b/images/garden/plants/dwarf-coffee-001/dwarf-coffee_20240310_113259.thumb.jpg new file mode 100644 index 0000000..f6ce599 Binary files /dev/null and b/images/garden/plants/dwarf-coffee-001/dwarf-coffee_20240310_113259.thumb.jpg differ diff --git a/images/garden/foxtail-fern_20240310_113229.jpg b/images/garden/plants/foxtail-fern-001/foxtail-fern_20240310_113229.jpg similarity index 100% rename from images/garden/foxtail-fern_20240310_113229.jpg rename to images/garden/plants/foxtail-fern-001/foxtail-fern_20240310_113229.jpg diff --git a/images/garden/plants/foxtail-fern-001/foxtail-fern_20240310_113229.thumb.jpg b/images/garden/plants/foxtail-fern-001/foxtail-fern_20240310_113229.thumb.jpg new file mode 100644 index 0000000..c790b83 Binary files /dev/null and b/images/garden/plants/foxtail-fern-001/foxtail-fern_20240310_113229.thumb.jpg differ diff --git a/images/garden/chives_20240310_113216.jpg b/images/garden/plants/garlic-chives-001/chives_20240310_113216.jpg similarity index 100% rename from images/garden/chives_20240310_113216.jpg rename to images/garden/plants/garlic-chives-001/chives_20240310_113216.jpg diff --git a/images/garden/plants/garlic-chives-001/chives_20240310_113216.thumb.jpg b/images/garden/plants/garlic-chives-001/chives_20240310_113216.thumb.jpg new file mode 100644 index 0000000..34b1290 Binary files /dev/null and b/images/garden/plants/garlic-chives-001/chives_20240310_113216.thumb.jpg differ diff --git a/images/garden/inchplant_20240310_113409.jpg b/images/garden/plants/inchplant-001/inchplant_20240310_113409.jpg similarity index 100% rename from images/garden/inchplant_20240310_113409.jpg rename to images/garden/plants/inchplant-001/inchplant_20240310_113409.jpg diff --git a/images/garden/plants/inchplant-001/inchplant_20240310_113409.thumb.jpg b/images/garden/plants/inchplant-001/inchplant_20240310_113409.thumb.jpg new file mode 100644 index 0000000..18dd060 Binary files /dev/null and b/images/garden/plants/inchplant-001/inchplant_20240310_113409.thumb.jpg differ diff --git a/images/garden/jalapeno_20240310_113329.jpg b/images/garden/plants/jalapeno-001/jalapeno_20240310_113329.jpg similarity index 100% rename from images/garden/jalapeno_20240310_113329.jpg rename to images/garden/plants/jalapeno-001/jalapeno_20240310_113329.jpg diff --git a/images/garden/plants/jalapeno-001/jalapeno_20240310_113329.thumb.jpg b/images/garden/plants/jalapeno-001/jalapeno_20240310_113329.thumb.jpg new file mode 100644 index 0000000..fd921c9 Binary files /dev/null and b/images/garden/plants/jalapeno-001/jalapeno_20240310_113329.thumb.jpg differ diff --git a/images/garden/fern_20240310_113359.jpg b/images/garden/plants/kimberley-fern-001/fern_20240310_113359.jpg similarity index 100% rename from images/garden/fern_20240310_113359.jpg rename to images/garden/plants/kimberley-fern-001/fern_20240310_113359.jpg diff --git a/images/garden/plants/kimberley-fern-001/fern_20240310_113359.thumb.jpg b/images/garden/plants/kimberley-fern-001/fern_20240310_113359.thumb.jpg new file mode 100644 index 0000000..b1ab2ff Binary files /dev/null and b/images/garden/plants/kimberley-fern-001/fern_20240310_113359.thumb.jpg differ diff --git a/images/garden/marigold_20240310_113234.jpg b/images/garden/plants/marigold-001/marigold_20240310_113234.jpg similarity index 100% rename from images/garden/marigold_20240310_113234.jpg rename to images/garden/plants/marigold-001/marigold_20240310_113234.jpg diff --git a/images/garden/plants/marigold-001/marigold_20240310_113234.thumb.jpg b/images/garden/plants/marigold-001/marigold_20240310_113234.thumb.jpg new file mode 100644 index 0000000..9182ead Binary files /dev/null and b/images/garden/plants/marigold-001/marigold_20240310_113234.thumb.jpg differ diff --git a/images/garden/plectranthus_20240310_113255.jpg b/images/garden/plants/mona-lavender-001/plectranthus_20240310_113255.jpg similarity index 100% rename from images/garden/plectranthus_20240310_113255.jpg rename to images/garden/plants/mona-lavender-001/plectranthus_20240310_113255.jpg diff --git a/images/garden/plants/mona-lavender-001/plectranthus_20240310_113255.thumb.jpg b/images/garden/plants/mona-lavender-001/plectranthus_20240310_113255.thumb.jpg new file mode 100644 index 0000000..9d4a22a Binary files /dev/null and b/images/garden/plants/mona-lavender-001/plectranthus_20240310_113255.thumb.jpg differ diff --git a/images/garden/thyme_20240310_113210.jpg b/images/garden/plants/thyme-001/thyme_20240310_113210.jpg similarity index 100% rename from images/garden/thyme_20240310_113210.jpg rename to images/garden/plants/thyme-001/thyme_20240310_113210.jpg diff --git a/images/garden/plants/thyme-001/thyme_20240310_113210.thumb.jpg b/images/garden/plants/thyme-001/thyme_20240310_113210.thumb.jpg new file mode 100644 index 0000000..6f0f890 Binary files /dev/null and b/images/garden/plants/thyme-001/thyme_20240310_113210.thumb.jpg differ diff --git a/images/garden/tropical-milkweed_20240310_113353.jpg b/images/garden/plants/tropical-milkweed-001/tropical-milkweed_20240310_113353.jpg similarity index 100% rename from images/garden/tropical-milkweed_20240310_113353.jpg rename to images/garden/plants/tropical-milkweed-001/tropical-milkweed_20240310_113353.jpg diff --git a/images/garden/plants/tropical-milkweed-001/tropical-milkweed_20240310_113353.thumb.jpg b/images/garden/plants/tropical-milkweed-001/tropical-milkweed_20240310_113353.thumb.jpg new file mode 100644 index 0000000..1dbb38a Binary files /dev/null and b/images/garden/plants/tropical-milkweed-001/tropical-milkweed_20240310_113353.thumb.jpg differ diff --git a/images/garden/woody-sage_20240310_113339.jpg b/images/garden/plants/wood-sage-001/woody-sage_20240310_113339.jpg similarity index 100% rename from images/garden/woody-sage_20240310_113339.jpg rename to images/garden/plants/wood-sage-001/woody-sage_20240310_113339.jpg diff --git a/images/garden/plants/wood-sage-001/woody-sage_20240310_113339.thumb.jpg b/images/garden/plants/wood-sage-001/woody-sage_20240310_113339.thumb.jpg new file mode 100644 index 0000000..8904d7d Binary files /dev/null and b/images/garden/plants/wood-sage-001/woody-sage_20240310_113339.thumb.jpg differ diff --git a/images/garden/species/allium-tuberosum/Allium_tuberosum-ingarden1.jpg b/images/garden/species/allium-tuberosum/Allium_tuberosum-ingarden1.jpg new file mode 100644 index 0000000..7e9701e Binary files /dev/null and b/images/garden/species/allium-tuberosum/Allium_tuberosum-ingarden1.jpg differ diff --git a/images/garden/species/allium-tuberosum/Allium_tuberosum-ingarden1.thumb.jpg b/images/garden/species/allium-tuberosum/Allium_tuberosum-ingarden1.thumb.jpg new file mode 100644 index 0000000..bd6dfdc Binary files /dev/null and b/images/garden/species/allium-tuberosum/Allium_tuberosum-ingarden1.thumb.jpg differ diff --git a/images/garden/species/asclepias-curassavica/Asclepias_curassavica-Thekkady-2016-12-03-001.jpg b/images/garden/species/asclepias-curassavica/Asclepias_curassavica-Thekkady-2016-12-03-001.jpg new file mode 100644 index 0000000..f98256e Binary files /dev/null and b/images/garden/species/asclepias-curassavica/Asclepias_curassavica-Thekkady-2016-12-03-001.jpg differ diff --git a/images/garden/species/asclepias-curassavica/Asclepias_curassavica-Thekkady-2016-12-03-001.thumb.jpg b/images/garden/species/asclepias-curassavica/Asclepias_curassavica-Thekkady-2016-12-03-001.thumb.jpg new file mode 100644 index 0000000..bc0c149 Binary files /dev/null and b/images/garden/species/asclepias-curassavica/Asclepias_curassavica-Thekkady-2016-12-03-001.thumb.jpg differ diff --git a/images/garden/species/asparagus-aethiopicus/foxtail_fern_asparagus_meyeri.jpg b/images/garden/species/asparagus-aethiopicus/foxtail_fern_asparagus_meyeri.jpg new file mode 100644 index 0000000..8dd0309 Binary files /dev/null and b/images/garden/species/asparagus-aethiopicus/foxtail_fern_asparagus_meyeri.jpg differ diff --git a/images/garden/species/asparagus-aethiopicus/foxtail_fern_asparagus_meyeri.thumb.jpg b/images/garden/species/asparagus-aethiopicus/foxtail_fern_asparagus_meyeri.thumb.jpg new file mode 100644 index 0000000..324cc88 Binary files /dev/null and b/images/garden/species/asparagus-aethiopicus/foxtail_fern_asparagus_meyeri.thumb.jpg differ diff --git a/images/garden/species/capsicum-annuum-var-glabriusculum/Capsicum_annuum-FWF.jpg b/images/garden/species/capsicum-annuum-var-glabriusculum/Capsicum_annuum-FWF.jpg new file mode 100644 index 0000000..5c5d03d Binary files /dev/null and b/images/garden/species/capsicum-annuum-var-glabriusculum/Capsicum_annuum-FWF.jpg differ diff --git a/images/garden/species/capsicum-annuum-var-glabriusculum/Capsicum_annuum-FWF.thumb.jpg b/images/garden/species/capsicum-annuum-var-glabriusculum/Capsicum_annuum-FWF.thumb.jpg new file mode 100644 index 0000000..245c445 Binary files /dev/null and b/images/garden/species/capsicum-annuum-var-glabriusculum/Capsicum_annuum-FWF.thumb.jpg differ diff --git a/images/garden/species/capsicum-annuum-var-jalapeno/jalapeno.jpg b/images/garden/species/capsicum-annuum-var-jalapeno/jalapeno.jpg new file mode 100644 index 0000000..450ad1a Binary files /dev/null and b/images/garden/species/capsicum-annuum-var-jalapeno/jalapeno.jpg differ diff --git a/images/garden/species/capsicum-annuum-var-jalapeno/jalapeno.thumb.jpg b/images/garden/species/capsicum-annuum-var-jalapeno/jalapeno.thumb.jpg new file mode 100644 index 0000000..5bed81c Binary files /dev/null and b/images/garden/species/capsicum-annuum-var-jalapeno/jalapeno.thumb.jpg differ diff --git a/images/garden/species/clinopodium-brownei/clinopodium_brownei-keim2-1-e1602619542587.jpg b/images/garden/species/clinopodium-brownei/clinopodium_brownei-keim2-1-e1602619542587.jpg new file mode 100644 index 0000000..33363c3 Binary files /dev/null and b/images/garden/species/clinopodium-brownei/clinopodium_brownei-keim2-1-e1602619542587.jpg differ diff --git a/images/garden/species/clinopodium-brownei/clinopodium_brownei-keim2-1-e1602619542587.thumb.jpg b/images/garden/species/clinopodium-brownei/clinopodium_brownei-keim2-1-e1602619542587.thumb.jpg new file mode 100644 index 0000000..1e0e77a Binary files /dev/null and b/images/garden/species/clinopodium-brownei/clinopodium_brownei-keim2-1-e1602619542587.thumb.jpg differ diff --git a/images/garden/species/coriandrum-sativum/Coriandrum_sativum--Forest-and-Kim-Starr--CC-BY.jpg b/images/garden/species/coriandrum-sativum/Coriandrum_sativum--Forest-and-Kim-Starr--CC-BY.jpg new file mode 100644 index 0000000..a5451c8 Binary files /dev/null and b/images/garden/species/coriandrum-sativum/Coriandrum_sativum--Forest-and-Kim-Starr--CC-BY.jpg differ diff --git a/images/garden/species/coriandrum-sativum/Coriandrum_sativum--Forest-and-Kim-Starr--CC-BY.thumb.jpg b/images/garden/species/coriandrum-sativum/Coriandrum_sativum--Forest-and-Kim-Starr--CC-BY.thumb.jpg new file mode 100644 index 0000000..ab789db Binary files /dev/null and b/images/garden/species/coriandrum-sativum/Coriandrum_sativum--Forest-and-Kim-Starr--CC-BY.thumb.jpg differ diff --git a/images/garden/species/juniperus-conferta/Juniperus_conferta_blue_pacific--Javier-Alejandro--CC-BY-NC-ND.jpg b/images/garden/species/juniperus-conferta/Juniperus_conferta_blue_pacific--Javier-Alejandro--CC-BY-NC-ND.jpg new file mode 100644 index 0000000..c2dd3a0 Binary files /dev/null and b/images/garden/species/juniperus-conferta/Juniperus_conferta_blue_pacific--Javier-Alejandro--CC-BY-NC-ND.jpg differ diff --git a/images/garden/species/juniperus-conferta/Juniperus_conferta_blue_pacific--Javier-Alejandro--CC-BY-NC-ND.thumb.jpg b/images/garden/species/juniperus-conferta/Juniperus_conferta_blue_pacific--Javier-Alejandro--CC-BY-NC-ND.thumb.jpg new file mode 100644 index 0000000..9cb2a2e Binary files /dev/null and b/images/garden/species/juniperus-conferta/Juniperus_conferta_blue_pacific--Javier-Alejandro--CC-BY-NC-ND.thumb.jpg differ diff --git a/images/garden/catnip-test.jpg b/images/garden/species/nepeta-cataria/catnip-test.jpg similarity index 100% rename from images/garden/catnip-test.jpg rename to images/garden/species/nepeta-cataria/catnip-test.jpg diff --git a/images/garden/species/nepeta-cataria/catnip-test.thumb.jpg b/images/garden/species/nepeta-cataria/catnip-test.thumb.jpg new file mode 100644 index 0000000..9c639fa Binary files /dev/null and b/images/garden/species/nepeta-cataria/catnip-test.thumb.jpg differ diff --git a/images/garden/species/nephrolepis-obliterata/nephrolepis-obliterata.jpg b/images/garden/species/nephrolepis-obliterata/nephrolepis-obliterata.jpg new file mode 100644 index 0000000..e2ecc07 Binary files /dev/null and b/images/garden/species/nephrolepis-obliterata/nephrolepis-obliterata.jpg differ diff --git a/images/garden/species/nephrolepis-obliterata/nephrolepis-obliterata.thumb.jpg b/images/garden/species/nephrolepis-obliterata/nephrolepis-obliterata.thumb.jpg new file mode 100644 index 0000000..b882100 Binary files /dev/null and b/images/garden/species/nephrolepis-obliterata/nephrolepis-obliterata.thumb.jpg differ diff --git a/images/garden/species/plectranthus-mona-lavender/plectranthus-0773400934.jpg b/images/garden/species/plectranthus-mona-lavender/plectranthus-0773400934.jpg new file mode 100644 index 0000000..5ce95cb Binary files /dev/null and b/images/garden/species/plectranthus-mona-lavender/plectranthus-0773400934.jpg differ diff --git a/images/garden/species/plectranthus-mona-lavender/plectranthus-0773400934.thumb.jpg b/images/garden/species/plectranthus-mona-lavender/plectranthus-0773400934.thumb.jpg new file mode 100644 index 0000000..bbb3030 Binary files /dev/null and b/images/garden/species/plectranthus-mona-lavender/plectranthus-0773400934.thumb.jpg differ diff --git a/images/garden/species/psychotria-nervosa/Psychotria_nervosa-JennyEvans_CCBY-NC2.0.jpg b/images/garden/species/psychotria-nervosa/Psychotria_nervosa-JennyEvans_CCBY-NC2.0.jpg new file mode 100644 index 0000000..ab8fec2 Binary files /dev/null and b/images/garden/species/psychotria-nervosa/Psychotria_nervosa-JennyEvans_CCBY-NC2.0.jpg differ diff --git a/images/garden/species/psychotria-nervosa/Psychotria_nervosa-JennyEvans_CCBY-NC2.0.thumb.jpg b/images/garden/species/psychotria-nervosa/Psychotria_nervosa-JennyEvans_CCBY-NC2.0.thumb.jpg new file mode 100644 index 0000000..3e601cc Binary files /dev/null and b/images/garden/species/psychotria-nervosa/Psychotria_nervosa-JennyEvans_CCBY-NC2.0.thumb.jpg differ diff --git a/images/garden/species/ruellia-caroliniensis/Ruellia-carolinensis-Carolina-Wild-Petunia-by-Marti-Webster-e1525962513541.jpg b/images/garden/species/ruellia-caroliniensis/Ruellia-carolinensis-Carolina-Wild-Petunia-by-Marti-Webster-e1525962513541.jpg new file mode 100644 index 0000000..1648ba5 Binary files /dev/null and b/images/garden/species/ruellia-caroliniensis/Ruellia-carolinensis-Carolina-Wild-Petunia-by-Marti-Webster-e1525962513541.jpg differ diff --git a/images/garden/species/ruellia-caroliniensis/Ruellia-carolinensis-Carolina-Wild-Petunia-by-Marti-Webster-e1525962513541.thumb.jpg b/images/garden/species/ruellia-caroliniensis/Ruellia-carolinensis-Carolina-Wild-Petunia-by-Marti-Webster-e1525962513541.thumb.jpg new file mode 100644 index 0000000..7fcd060 Binary files /dev/null and b/images/garden/species/ruellia-caroliniensis/Ruellia-carolinensis-Carolina-Wild-Petunia-by-Marti-Webster-e1525962513541.thumb.jpg differ diff --git a/images/garden/species/salvia-misella/salvia-misella.jpg b/images/garden/species/salvia-misella/salvia-misella.jpg new file mode 100644 index 0000000..a787354 Binary files /dev/null and b/images/garden/species/salvia-misella/salvia-misella.jpg differ diff --git a/images/garden/species/salvia-misella/salvia-misella.thumb.jpg b/images/garden/species/salvia-misella/salvia-misella.thumb.jpg new file mode 100644 index 0000000..e98f103 Binary files /dev/null and b/images/garden/species/salvia-misella/salvia-misella.thumb.jpg differ diff --git a/images/garden/species/tagetes-erecta/tagetes-erecta.jpg b/images/garden/species/tagetes-erecta/tagetes-erecta.jpg new file mode 100644 index 0000000..94e8e18 Binary files /dev/null and b/images/garden/species/tagetes-erecta/tagetes-erecta.jpg differ diff --git a/images/garden/species/tagetes-erecta/tagetes-erecta.thumb.jpg b/images/garden/species/tagetes-erecta/tagetes-erecta.thumb.jpg new file mode 100644 index 0000000..5019aa7 Binary files /dev/null and b/images/garden/species/tagetes-erecta/tagetes-erecta.thumb.jpg differ diff --git a/images/garden/species/teucrium-canadense/teucrium-canadense-perennials-20a__49095.jpg b/images/garden/species/teucrium-canadense/teucrium-canadense-perennials-20a__49095.jpg new file mode 100644 index 0000000..7664f3c Binary files /dev/null and b/images/garden/species/teucrium-canadense/teucrium-canadense-perennials-20a__49095.jpg differ diff --git a/images/garden/species/teucrium-canadense/teucrium-canadense-perennials-20a__49095.thumb.jpg b/images/garden/species/teucrium-canadense/teucrium-canadense-perennials-20a__49095.thumb.jpg new file mode 100644 index 0000000..253c89a Binary files /dev/null and b/images/garden/species/teucrium-canadense/teucrium-canadense-perennials-20a__49095.thumb.jpg differ diff --git a/images/garden/species/thymus-vulgaris/Thymus-vulgaris--CT-Arzneimittel-GmbH--CC-BY-ND.jpg b/images/garden/species/thymus-vulgaris/Thymus-vulgaris--CT-Arzneimittel-GmbH--CC-BY-ND.jpg new file mode 100644 index 0000000..65eaedd Binary files /dev/null and b/images/garden/species/thymus-vulgaris/Thymus-vulgaris--CT-Arzneimittel-GmbH--CC-BY-ND.jpg differ diff --git a/images/garden/species/thymus-vulgaris/Thymus-vulgaris--CT-Arzneimittel-GmbH--CC-BY-ND.thumb.jpg b/images/garden/species/thymus-vulgaris/Thymus-vulgaris--CT-Arzneimittel-GmbH--CC-BY-ND.thumb.jpg new file mode 100644 index 0000000..b122866 Binary files /dev/null and b/images/garden/species/thymus-vulgaris/Thymus-vulgaris--CT-Arzneimittel-GmbH--CC-BY-ND.thumb.jpg differ diff --git a/images/garden/species/trandescantia-zebrina/Tradescantia-zebrina_AdobeStock_492957504_1200px.jpg b/images/garden/species/trandescantia-zebrina/Tradescantia-zebrina_AdobeStock_492957504_1200px.jpg new file mode 100644 index 0000000..dabe0c4 Binary files /dev/null and b/images/garden/species/trandescantia-zebrina/Tradescantia-zebrina_AdobeStock_492957504_1200px.jpg differ diff --git a/images/garden/species/trandescantia-zebrina/Tradescantia-zebrina_AdobeStock_492957504_1200px.thumb.jpg b/images/garden/species/trandescantia-zebrina/Tradescantia-zebrina_AdobeStock_492957504_1200px.thumb.jpg new file mode 100644 index 0000000..0bf9804 Binary files /dev/null and b/images/garden/species/trandescantia-zebrina/Tradescantia-zebrina_AdobeStock_492957504_1200px.thumb.jpg differ diff --git a/local-server.d b/local-server.d new file mode 100755 index 0000000..7dcd2c1 --- /dev/null +++ b/local-server.d @@ -0,0 +1,20 @@ +#!/usr/bin/env dub +/+ dub.sdl: + dependency "handy-httpd" version="~>8" ++/ + +/** + * Run "./local-server.d" in your command-line to start up a server that simply + * serves these files on http://localhost:8080. This can be helpful when you + * need to simulate actual website behavior. + */ +module local_server; + +import handy_httpd; +import handy_httpd.handlers.file_resolving_handler; + +void main() { + ServerConfig cfg; + cfg.workerPoolSize = 5; + new HttpServer(new FileResolvingHandler("./"), cfg).start(); +} diff --git a/styles/garden.css b/styles/garden.css index a33d490..1ef79ac 100644 --- a/styles/garden.css +++ b/styles/garden.css @@ -1,4 +1,4 @@ -.plant-card { +.species-card { background-color: var(--background-color-2); padding: 0.5em; display: flex; @@ -6,32 +6,57 @@ gap: 1%; } -.plant-card + .plant-card { +.species-card + .species-card { margin-top: 1em; } -.plant-card > section { +.species-card > section { flex-grow: 1; max-width: 49.5%; } -.plant-card > section > h2 { +.species-card > section > h2 { margin: 0; } -.plant-card > section > .sci { +.species-card > section > .sci { margin: 0; font-style: italic; font-family: serif; font-size: smaller; } -.plant-card > section > p { +.species-card > section > p { margin: 0.5em 0; } -.plant-card > img { +.species-card > img { max-width: 49.5%; object-fit: scale-down; flex-grow: 0; } + + + +.plant-card { + background-color: var(--background-color-2); + padding: 0.5em; +} + +.plant-card + .plant-card { + margin-top: 1em; +} + +.plant-card > section > h2 { + margin: 0; +} + +.plant-card > section > .gen { + margin: 0; + font-family: monospace; + font-size: medium; +} + +.plant-card > section > p { + margin: 0.5em 0; +} diff --git a/upload.sh b/upload.sh index 8b3815c..8fc2b9f 100755 --- a/upload.sh +++ b/upload.sh @@ -3,7 +3,10 @@ # Helper script that copies files to the server. # You need to have authenticated access to the server via SSH to use this. +# Note: Quite a few files are excluded from being uploaded to the website. +# These are denoted with an "--exclude" parameter. + rsync -rav -e ssh --delete \ ---exclude .git/ --exclude *.sh --exclude README.md \ +--exclude .git/ --exclude *.sh --exclude README.md --exclude *.d --exclude *.ods --exclude garden-data-gen/* \ ./ \ root@andrewlalis.com:/var/www/andrewlalis.com/html diff --git a/vendor/photoswipe/photoswipe-lightbox.esm.min.js b/vendor/photoswipe/photoswipe-lightbox.esm.min.js new file mode 100644 index 0000000..16b01d5 --- /dev/null +++ b/vendor/photoswipe/photoswipe-lightbox.esm.min.js @@ -0,0 +1,5 @@ +/*! + * PhotoSwipe Lightbox 5.4.3 - https://photoswipe.com + * (c) 2023 Dmytro Semenov + */ +function t(t,i,s){const h=document.createElement(i);return t&&(h.className=t),s&&s.appendChild(h),h}function i(t,i,s){t.style.width="number"==typeof i?`${i}px`:i,t.style.height="number"==typeof s?`${s}px`:s}const s="idle",h="loading",e="loaded",n="error";function o(t,i,s=document){let h=[];if(t instanceof Element)h=[t];else if(t instanceof NodeList||Array.isArray(t))h=Array.from(t);else{const e="string"==typeof t?t:i;e&&(h=Array.from(s.querySelectorAll(e)))}return h}function r(){return!(!navigator.vendor||!navigator.vendor.match(/apple/i))}class l{constructor(t,i){this.type=t,this.defaultPrevented=!1,i&&Object.assign(this,i)}preventDefault(){this.defaultPrevented=!0}}class a{constructor(i,s){if(this.element=t("pswp__img pswp__img--placeholder",i?"img":"div",s),i){const t=this.element;t.decoding="async",t.alt="",t.src=i,t.setAttribute("role","presentation")}this.element.setAttribute("aria-hidden","true")}setDisplayedSize(t,s){this.element&&("IMG"===this.element.tagName?(i(this.element,250,"auto"),this.element.style.transformOrigin="0 0",this.element.style.transform=function(t,i,s){let h=`translate3d(${t}px,${i||0}px,0)`;return void 0!==s&&(h+=` scale3d(${s},${s},1)`),h}(0,0,t/250)):i(this.element,t,s))}destroy(){var t;null!==(t=this.element)&&void 0!==t&&t.parentNode&&this.element.remove(),this.element=null}}class d{constructor(t,i,h){this.instance=i,this.data=t,this.index=h,this.element=void 0,this.placeholder=void 0,this.slide=void 0,this.displayedImageWidth=0,this.displayedImageHeight=0,this.width=Number(this.data.w)||Number(this.data.width)||0,this.height=Number(this.data.h)||Number(this.data.height)||0,this.isAttached=!1,this.hasSlide=!1,this.isDecoding=!1,this.state=s,this.data.type?this.type=this.data.type:this.data.src?this.type="image":this.type="html",this.instance.dispatch("contentInit",{content:this})}removePlaceholder(){this.placeholder&&!this.keepPlaceholder()&&setTimeout((()=>{this.placeholder&&(this.placeholder.destroy(),this.placeholder=void 0)}),1e3)}load(i,s){if(this.slide&&this.usePlaceholder())if(this.placeholder){const t=this.placeholder.element;t&&!t.parentElement&&this.slide.container.prepend(t)}else{const t=this.instance.applyFilters("placeholderSrc",!(!this.data.msrc||!this.slide.isFirstSlide)&&this.data.msrc,this);this.placeholder=new a(t,this.slide.container)}this.element&&!s||this.instance.dispatch("contentLoad",{content:this,isLazy:i}).defaultPrevented||(this.isImageContent()?(this.element=t("pswp__img","img"),this.displayedImageWidth&&this.loadImage(i)):(this.element=t("pswp__content","div"),this.element.innerHTML=this.data.html||""),s&&this.slide&&this.slide.updateContentSize(!0))}loadImage(t){var i,s;if(!this.isImageContent()||!this.element||this.instance.dispatch("contentLoadImage",{content:this,isLazy:t}).defaultPrevented)return;const e=this.element;this.updateSrcsetSizes(),this.data.srcset&&(e.srcset=this.data.srcset),e.src=null!==(i=this.data.src)&&void 0!==i?i:"",e.alt=null!==(s=this.data.alt)&&void 0!==s?s:"",this.state=h,e.complete?this.onLoaded():(e.onload=()=>{this.onLoaded()},e.onerror=()=>{this.onError()})}setSlide(t){this.slide=t,this.hasSlide=!0,this.instance=t.pswp}onLoaded(){this.state=e,this.slide&&this.element&&(this.instance.dispatch("loadComplete",{slide:this.slide,content:this}),this.slide.isActive&&this.slide.heavyAppended&&!this.element.parentNode&&(this.append(),this.slide.updateContentSize(!0)),this.state!==e&&this.state!==n||this.removePlaceholder())}onError(){this.state=n,this.slide&&(this.displayError(),this.instance.dispatch("loadComplete",{slide:this.slide,isError:!0,content:this}),this.instance.dispatch("loadError",{slide:this.slide,content:this}))}isLoading(){return this.instance.applyFilters("isContentLoading",this.state===h,this)}isError(){return this.state===n}isImageContent(){return"image"===this.type}setDisplayedSize(t,s){if(this.element&&(this.placeholder&&this.placeholder.setDisplayedSize(t,s),!this.instance.dispatch("contentResize",{content:this,width:t,height:s}).defaultPrevented&&(i(this.element,t,s),this.isImageContent()&&!this.isError()))){const i=!this.displayedImageWidth&&t;this.displayedImageWidth=t,this.displayedImageHeight=s,i?this.loadImage(!1):this.updateSrcsetSizes(),this.slide&&this.instance.dispatch("imageSizeChange",{slide:this.slide,width:t,height:s,content:this})}}isZoomable(){return this.instance.applyFilters("isContentZoomable",this.isImageContent()&&this.state!==n,this)}updateSrcsetSizes(){if(!this.isImageContent()||!this.element||!this.data.srcset)return;const t=this.element,i=this.instance.applyFilters("srcsetSizesWidth",this.displayedImageWidth,this);(!t.dataset.largestUsedSize||i>parseInt(t.dataset.largestUsedSize,10))&&(t.sizes=i+"px",t.dataset.largestUsedSize=String(i))}usePlaceholder(){return this.instance.applyFilters("useContentPlaceholder",this.isImageContent(),this)}lazyLoad(){this.instance.dispatch("contentLazyLoad",{content:this}).defaultPrevented||this.load(!0)}keepPlaceholder(){return this.instance.applyFilters("isKeepingPlaceholder",this.isLoading(),this)}destroy(){this.hasSlide=!1,this.slide=void 0,this.instance.dispatch("contentDestroy",{content:this}).defaultPrevented||(this.remove(),this.placeholder&&(this.placeholder.destroy(),this.placeholder=void 0),this.isImageContent()&&this.element&&(this.element.onload=null,this.element.onerror=null,this.element=void 0))}displayError(){if(this.slide){var i,s;let h=t("pswp__error-msg","div");h.innerText=null!==(i=null===(s=this.instance.options)||void 0===s?void 0:s.errorMsg)&&void 0!==i?i:"",h=this.instance.applyFilters("contentErrorElement",h,this),this.element=t("pswp__content pswp__error-msg-container","div"),this.element.appendChild(h),this.slide.container.innerText="",this.slide.container.appendChild(this.element),this.slide.updateContentSize(!0),this.removePlaceholder()}}append(){if(this.isAttached||!this.element)return;if(this.isAttached=!0,this.state===n)return void this.displayError();if(this.instance.dispatch("contentAppend",{content:this}).defaultPrevented)return;const t="decode"in this.element;this.isImageContent()?t&&this.slide&&(!this.slide.isActive||r())?(this.isDecoding=!0,this.element.decode().catch((()=>{})).finally((()=>{this.isDecoding=!1,this.appendImage()}))):this.appendImage():this.slide&&!this.element.parentNode&&this.slide.container.appendChild(this.element)}activate(){!this.instance.dispatch("contentActivate",{content:this}).defaultPrevented&&this.slide&&(this.isImageContent()&&this.isDecoding&&!r()?this.appendImage():this.isError()&&this.load(!1,!0),this.slide.holderElement&&this.slide.holderElement.setAttribute("aria-hidden","false"))}deactivate(){this.instance.dispatch("contentDeactivate",{content:this}),this.slide&&this.slide.holderElement&&this.slide.holderElement.setAttribute("aria-hidden","true")}remove(){this.isAttached=!1,this.instance.dispatch("contentRemove",{content:this}).defaultPrevented||(this.element&&this.element.parentNode&&this.element.remove(),this.placeholder&&this.placeholder.element&&this.placeholder.element.remove())}appendImage(){this.isAttached&&(this.instance.dispatch("contentAppendImage",{content:this}).defaultPrevented||(this.slide&&this.element&&!this.element.parentNode&&this.slide.container.appendChild(this.element),this.state!==e&&this.state!==n||this.removePlaceholder()))}}function c(t,i,s,h,e){let n=0;if(i.paddingFn)n=i.paddingFn(s,h,e)[t];else if(i.padding)n=i.padding[t];else{const s="padding"+t[0].toUpperCase()+t.slice(1);i[s]&&(n=i[s])}return Number(n)||0}class u{constructor(t,i,s,h){this.pswp=h,this.options=t,this.itemData=i,this.index=s,this.panAreaSize=null,this.elementSize=null,this.fit=1,this.fill=1,this.vFill=1,this.initial=1,this.secondary=1,this.max=1,this.min=1}update(t,i,s){const h={x:t,y:i};this.elementSize=h,this.panAreaSize=s;const e=s.x/h.x,n=s.y/h.y;this.fit=Math.min(1,en?e:n),this.vFill=Math.min(1,n),this.initial=this.t(),this.secondary=this.i(),this.max=Math.max(this.initial,this.secondary,this.o()),this.min=Math.min(this.fit,this.initial,this.secondary),this.pswp&&this.pswp.dispatch("zoomLevelsUpdate",{zoomLevels:this,slideData:this.itemData})}l(t){const i=t+"ZoomLevel",s=this.options[i];if(s)return"function"==typeof s?s(this):"fill"===s?this.fill:"fit"===s?this.fit:Number(s)}i(){let t=this.l("secondary");return t||(t=Math.min(1,3*this.fit),this.elementSize&&t*this.elementSize.x>4e3&&(t=4e3/this.elementSize.x),t)}t(){return this.l("initial")||this.fit}o(){return this.l("max")||Math.max(1,4*this.fit)}}function p(t,i,s){const h=i.createContentFromData(t,s);let e;const{options:n}=i;if(n){let o;e=new u(n,t,-1),o=i.pswp?i.pswp.viewportSize:function(t,i){if(t.getViewportSizeFn){const s=t.getViewportSizeFn(t,i);if(s)return s}return{x:document.documentElement.clientWidth,y:window.innerHeight}}(n,i);const r=function(t,i,s,h){return{x:i.x-c("left",t,i,s,h)-c("right",t,i,s,h),y:i.y-c("top",t,i,s,h)-c("bottom",t,i,s,h)}}(n,o,t,s);e.update(h.width,h.height,r)}return h.lazyLoad(),e&&h.setDisplayedSize(Math.ceil(h.width*e.initial),Math.ceil(h.height*e.initial)),h}class v extends class extends class{constructor(){this.u={},this.p={},this.pswp=void 0,this.options=void 0}addFilter(t,i,s=100){var h,e,n;this.p[t]||(this.p[t]=[]),null===(h=this.p[t])||void 0===h||h.push({fn:i,priority:s}),null===(e=this.p[t])||void 0===e||e.sort(((t,i)=>t.priority-i.priority)),null===(n=this.pswp)||void 0===n||n.addFilter(t,i,s)}removeFilter(t,i){this.p[t]&&(this.p[t]=this.p[t].filter((t=>t.fn!==i))),this.pswp&&this.pswp.removeFilter(t,i)}applyFilters(t,...i){var s;return null===(s=this.p[t])||void 0===s||s.forEach((t=>{i[0]=t.fn.apply(this,i)})),i[0]}on(t,i){var s,h;this.u[t]||(this.u[t]=[]),null===(s=this.u[t])||void 0===s||s.push(i),null===(h=this.pswp)||void 0===h||h.on(t,i)}off(t,i){var s;this.u[t]&&(this.u[t]=this.u[t].filter((t=>i!==t))),null===(s=this.pswp)||void 0===s||s.off(t,i)}dispatch(t,i){var s;if(this.pswp)return this.pswp.dispatch(t,i);const h=new l(t,i);return null===(s=this.u[t])||void 0===s||s.forEach((t=>{t.call(this,h)})),h}}{getNumItems(){var t;let i=0;const s=null===(t=this.options)||void 0===t?void 0:t.dataSource;s&&"length"in s?i=s.length:s&&"gallery"in s&&(s.items||(s.items=this.v(s.gallery)),s.items&&(i=s.items.length));const h=this.dispatch("numItems",{dataSource:s,numItems:i});return this.applyFilters("numItems",h.numItems,s)}createContentFromData(t,i){return new d(t,this,i)}getItemData(t){var i;const s=null===(i=this.options)||void 0===i?void 0:i.dataSource;let h={};Array.isArray(s)?h=s[t]:s&&"gallery"in s&&(s.items||(s.items=this.v(s.gallery)),h=s.items[t]);let e=h;e instanceof Element&&(e=this.m(e));const n=this.dispatch("itemData",{itemData:e||{},index:t});return this.applyFilters("itemData",n.itemData,t)}v(t){var i,s;return null!==(i=this.options)&&void 0!==i&&i.children||null!==(s=this.options)&&void 0!==s&&s.childSelector?o(this.options.children,this.options.childSelector,t)||[]:[t]}m(t){const i={element:t},s="A"===t.tagName?t:t.querySelector("a");if(s){i.src=s.dataset.pswpSrc||s.href,s.dataset.pswpSrcset&&(i.srcset=s.dataset.pswpSrcset),i.width=s.dataset.pswpWidth?parseInt(s.dataset.pswpWidth,10):0,i.height=s.dataset.pswpHeight?parseInt(s.dataset.pswpHeight,10):0,i.w=i.width,i.h=i.height,s.dataset.pswpType&&(i.type=s.dataset.pswpType);const e=t.querySelector("img");var h;if(e)i.msrc=e.currentSrc||e.src,i.alt=null!==(h=e.getAttribute("alt"))&&void 0!==h?h:"";(s.dataset.pswpCropped||s.dataset.cropped)&&(i.thumbCropped=!0)}return this.applyFilters("domItemData",i,t,s)}lazyLoadData(t,i){return p(t,this,i)}}{constructor(t){super(),this.options=t||{},this.g=0,this.shouldOpen=!1,this._=void 0,this.onThumbnailsClick=this.onThumbnailsClick.bind(this)}init(){o(this.options.gallery,this.options.gallerySelector).forEach((t=>{t.addEventListener("click",this.onThumbnailsClick,!1)}))}onThumbnailsClick(t){if(function(t){return"button"in t&&1===t.button||t.ctrlKey||t.metaKey||t.altKey||t.shiftKey}(t)||window.pswp)return;let i={x:t.clientX,y:t.clientY};i.x||i.y||(i=null);let s=this.getClickedIndex(t);s=this.applyFilters("clickedIndex",s,t,this);const h={gallery:t.currentTarget};s>=0&&(t.preventDefault(),this.loadAndOpen(s,h,i))}getClickedIndex(t){if(this.options.getClickedIndexFn)return this.options.getClickedIndexFn.call(this,t);const i=t.target,s=o(this.options.children,this.options.childSelector,t.currentTarget).findIndex((t=>t===i||t.contains(i)));return-1!==s?s:this.options.children||this.options.childSelector?-1:0}loadAndOpen(t,i,s){if(window.pswp||!this.options)return!1;if(!i&&this.options.gallery&&this.options.children){const t=o(this.options.gallery);t[0]&&(i={gallery:t[0]})}return this.options.index=t,this.options.initialPointerPos=s,this.shouldOpen=!0,this.preload(t,i),!0}preload(t,i){const{options:s}=this;i&&(s.dataSource=i);const h=[],e=typeof s.pswpModule;if("function"==typeof(n=s.pswpModule)&&n.prototype&&n.prototype.goTo)h.push(Promise.resolve(s.pswpModule));else{if("string"===e)throw new Error("pswpModule as string is no longer supported");if("function"!==e)throw new Error("pswpModule is not valid");h.push(s.pswpModule())}var n;"function"==typeof s.openPromise&&h.push(s.openPromise()),!1!==s.preloadFirstSlide&&t>=0&&(this._=function(t,i){const s=i.getItemData(t);if(!i.dispatch("lazyLoadSlide",{index:t,itemData:s}).defaultPrevented)return p(s,i,t)}(t,this));const o=++this.g;Promise.all(h).then((t=>{if(this.shouldOpen){const i=t[0];this.I(i,o)}}))}I(t,i){if(i!==this.g&&this.shouldOpen)return;if(this.shouldOpen=!1,window.pswp)return;const s="object"==typeof t?new t.default(this.options):new t(this.options);this.pswp=s,window.pswp=s,Object.keys(this.u).forEach((t=>{var i;null===(i=this.u[t])||void 0===i||i.forEach((i=>{s.on(t,i)}))})),Object.keys(this.p).forEach((t=>{var i;null===(i=this.p[t])||void 0===i||i.forEach((i=>{s.addFilter(t,i.fn,i.priority)}))})),this._&&(s.contentLoader.addToCache(this._),this._=void 0),s.on("destroy",(()=>{this.pswp=void 0,delete window.pswp})),s.init()}destroy(){var t;null===(t=this.pswp)||void 0===t||t.destroy(),this.shouldOpen=!1,this.u={},o(this.options.gallery,this.options.gallerySelector).forEach((t=>{t.removeEventListener("click",this.onThumbnailsClick,!1)}))}}export{v as default}; diff --git a/vendor/photoswipe/photoswipe.css b/vendor/photoswipe/photoswipe.css new file mode 100644 index 0000000..686dfc3 --- /dev/null +++ b/vendor/photoswipe/photoswipe.css @@ -0,0 +1,420 @@ +/*! PhotoSwipe main CSS by Dmytro Semenov | photoswipe.com */ + +.pswp { + --pswp-bg: #000; + --pswp-placeholder-bg: #222; + + + --pswp-root-z-index: 100000; + + --pswp-preloader-color: rgba(79, 79, 79, 0.4); + --pswp-preloader-color-secondary: rgba(255, 255, 255, 0.9); + + /* defined via js: + --pswp-transition-duration: 333ms; */ + + --pswp-icon-color: #fff; + --pswp-icon-color-secondary: #4f4f4f; + --pswp-icon-stroke-color: #4f4f4f; + --pswp-icon-stroke-width: 2px; + + --pswp-error-text-color: var(--pswp-icon-color); +} + + +/* + Styles for basic PhotoSwipe (pswp) functionality (sliding area, open/close transitions) +*/ + +.pswp { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: var(--pswp-root-z-index); + display: none; + touch-action: none; + outline: 0; + opacity: 0.003; + contain: layout style size; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +/* Prevents focus outline on the root element, + (it may be focused initially) */ +.pswp:focus { + outline: 0; +} + +.pswp * { + box-sizing: border-box; +} + +.pswp img { + max-width: none; +} + +.pswp--open { + display: block; +} + +.pswp, +.pswp__bg { + transform: translateZ(0); + will-change: opacity; +} + +.pswp__bg { + opacity: 0.005; + background: var(--pswp-bg); +} + +.pswp, +.pswp__scroll-wrap { + overflow: hidden; +} + +.pswp__scroll-wrap, +.pswp__bg, +.pswp__container, +.pswp__item, +.pswp__content, +.pswp__img, +.pswp__zoom-wrap { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.pswp__img, +.pswp__zoom-wrap { + width: auto; + height: auto; +} + +.pswp--click-to-zoom.pswp--zoom-allowed .pswp__img { + cursor: -webkit-zoom-in; + cursor: -moz-zoom-in; + cursor: zoom-in; +} + +.pswp--click-to-zoom.pswp--zoomed-in .pswp__img { + cursor: move; + cursor: -webkit-grab; + cursor: -moz-grab; + cursor: grab; +} + +.pswp--click-to-zoom.pswp--zoomed-in .pswp__img:active { + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + cursor: grabbing; +} + +/* :active to override grabbing cursor */ +.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img, +.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img:active, +.pswp__img { + cursor: -webkit-zoom-out; + cursor: -moz-zoom-out; + cursor: zoom-out; +} + + +/* Prevent selection and tap highlights */ +.pswp__container, +.pswp__img, +.pswp__button, +.pswp__counter { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.pswp__item { + /* z-index for fade transition */ + z-index: 1; + overflow: hidden; +} + +.pswp__hidden { + display: none !important; +} + +/* Allow to click through pswp__content element, but not its children */ +.pswp__content { + pointer-events: none; +} +.pswp__content > * { + pointer-events: auto; +} + + +/* + + PhotoSwipe UI + +*/ + +/* + Error message appears when image is not loaded + (JS option errorMsg controls markup) +*/ +.pswp__error-msg-container { + display: grid; +} +.pswp__error-msg { + margin: auto; + font-size: 1em; + line-height: 1; + color: var(--pswp-error-text-color); +} + +/* +class pswp__hide-on-close is applied to elements that +should hide (for example fade out) when PhotoSwipe is closed +and show (for example fade in) when PhotoSwipe is opened + */ +.pswp .pswp__hide-on-close { + opacity: 0.005; + will-change: opacity; + transition: opacity var(--pswp-transition-duration) cubic-bezier(0.4, 0, 0.22, 1); + z-index: 10; /* always overlap slide content */ + pointer-events: none; /* hidden elements should not be clickable */ +} + +/* class pswp--ui-visible is added when opening or closing transition starts */ +.pswp--ui-visible .pswp__hide-on-close { + opacity: 1; + pointer-events: auto; +} + +/*