Assignment 2 – JavaScript Enhancements
FloraHub uses one JavaScript file, script.js, for data transfer, form validation and the following enhancements. Enhancement 2 is an AI photo identification feature using the Pl@ntNet API, rather than the previously proposed local name filter.
1. Automatic Image Slideshow
Implemented on: Home (index.html)
Trigger: The right-hand slideshow moves automatically every 4 seconds; use arrow buttons or photo dots to navigate; hover to pause.
Implementation: initSlideshow() reads the 18 original images and captions in slideshowPhotos, uses createElement() for dot buttons and setInterval() for timed cycling. Modular arithmetic wraps the final photo to the beginning. Mouse and document visibility events pause the timer; CSS transitions fade the image.
Source/reference: Student's own Assignment 1 images; MDN setInterval(); MDN createElement().
2. AI Plant Identification with an External API
Implemented on: Identify (Identify.html)
Trigger: Select a JPEG or PNG photo, optionally choose Leaf / Flower / Fruit / Bark / Auto, then click Identify Plant. Visitors do not enter an API key.
Implementation: initPlantIdentification() in the one script.js previews the local image using URL.createObjectURL(), checks file MIME type and size, sends a fetch() POST with FormData to an HTTPS Cloudflare Worker endpoint. The Worker reads the private PLANTNET_API_KEY secret and forwards the image to Pl@ntNet /v2/identify/all, parses the JSON response, and uses createElement() / textContent to build an easy-to-read report with a prominent top candidate, alternative matches, confidence-score bars, genus/family, the user's image, and Pl@ntNet reference photos with source/author/license attribution when supplied. The API's default non-plant rejection (HTTP 404) is handled with a clear warning rather than invented species predictions; an unrecognisable plant may also give 404. The Cloudflare Worker validates upload type and size, handles CORS and returns predictable JSON errors; the interface handles loading, failed identification and service errors without showing a fake result.
Important: The public static website requires a separately deployed Cloudflare Worker with PLANTNET_API_KEY configured as a Worker Secret and an allowlist of authorized website origins. The key is never sent to visitors. The AI feature needs Internet access and will not run from file:// when origins are restricted. Calls consume the owner's Pl@ntNet quota; CORS restrictions alone do not prevent direct API abuse. The separate Worker implements Cloudflare Rate Limiting binding (4 requests per IP per minute per Cloudflare location); deployment requires the binding configuration. This is not a strict global or daily quota. Scores are not guaranteed probabilities and a 404 does not prove the photo lacks plants.
Source/reference: Pl@ntNet official identification API; Pl@ntNet getting started and CORS guidance; Pl@ntNet image attribution guide; MDN Fetch API; MDN FormData; Cloudflare Worker Secrets; Cloudflare Workers Fetch.
3. Form Preview & Professional Email Formatting
Implemented on: Enquiry form (Enquiry.html) and Contribution form (Contribution.html).
Trigger: In Enquiry, enter valid details and click Preview Before Submit, then Open Email Draft or Copy Email Text. In Contribution, complete the form and click Open Email Draft or Copy Email Text.
Implementation: initFormPreview() calls the existing Lecture 9-style validateEnquiryForm() and safely creates a review modal using createElement() and textContent. The new buildEnquiryEmailDraft() and buildContributionEmailDraft() functions group details into Contact Details, Plant Information and Message sections. openFormattedEmail() encodes a readable, plain-text email subject and body with encodeURIComponent() and opens a mailto: link. copyFormattedEmail() provides clipboard/manual-copy fallback. No server-side email sending is used.
Important: The email opens as a draft; users must press Send in their email application. Contribution photos must be attached manually because mailto: links cannot attach local files. The form does not guarantee mail app availability.
Source/reference: Lecturer's Lecture9Demo[1].md validation sample; MDN encodeURIComponent(); MDN Clipboard.writeText(); MDN textContent.
4. Automatic Active Navigation Highlight
Implemented on: Species, Families, Genera, Contributor and other pages.
Trigger: Open any page with the main navigation. The current item highlights automatically. The Explore parent also highlights on Families, Genera and Species pages.
Implementation: initActiveNavigation() reads window.location.pathname, compares it with navigation link href values, adds the is-current-page CSS class and sets aria-current on the exact destination. CSS provides a green background/highlight.
Source/reference: MDN Location.pathname; MDN classList.
5. Plant Image Lightbox / Zoom
Implemented on: Species, Genera and Families.
Trigger: Hover over a plant card to see its back, then select View Photo. Alternatively select the photo on its front. A large image opens over a dark background. Click arrows or press left/right arrow keys to navigate; use Esc or the close button to exit.
Implementation: initPlantLightbox() gathers each plant card's photo into a gallery, dynamically appends View Photo buttons to the back faces, creates a reusable dialog, and updates the enlarged img.src and caption. It manages keyboard events, modal visibility and focus restoration.
Source/reference: Student's original FloraHub photos; MDN keydown; MDN aria-modal.
6. Plant Identification History
Implemented on: Identify (Identify.html).
Trigger: After a successful Pl@ntNet identification, FloraHub automatically saves the six latest reports. Select View Result to review a previous identification, Remove to delete one, or Clear History to delete them all.
Implementation: The existing script.js uses localStorage with JSON.stringify() and JSON.parse(), limits the history to six entries and builds history cards with DOM methods. Saved summaries contain the timestamp, candidate names, family, genus and score only; uploaded photos, API keys and raw photo contents are not stored. Storage failures are handled without preventing identification. Viewing a saved result does not call Pl@ntNet or use the owner's quota.
Source/reference: MDN localStorage; MDN JSON.
7. Plant Candidate Comparison
Implemented on: Identify (Identify.html).
Trigger: After identification, select any two candidate plants using Select for Comparison. The comparison table updates with scientific name, common name, family, genus and score; use Clear Selection to reset. Comparison also works for a saved history report.
Implementation: The JavaScript keeps selected candidate indices in an array, uses includes() to toggle selection, checks a maximum of two candidates, and dynamically creates an accessible table with createElement() and textContent. Scores remain labelled as AI match scores, not guaranteed probabilities. Comparing candidates does not make additional API calls.
Source/reference: MDN createElement; MDN Array.includes.
8. Worker-Side API Rate Limiting
Implemented on: Separately deployed Cloudflare Worker protecting the Identify page's online API request.
Trigger: Every POST request to the public /identify endpoint is checked by Cloudflare Rate Limiting before the Pl@ntNet API is called. Excess requests receive HTTP 429 and the browser shows a readable warning.
Implementation: The Worker uses the IDENTIFY_RATE_LIMITER binding and env.IDENTIFY_RATE_LIMITER.limit({key: ...}), configured for four requests per IP per minute through wrangler.toml. Missing binding or secret fails closed. The Worker code is deployed outside the student website, keeping script.js as the only JavaScript file inside the Assignment 2 website ZIP. Rate limits are approximate and regional, so this is not a guaranteed daily quota cap.
Source/reference: Cloudflare Rate Limiting API; Cloudflare Secrets.
Back to FloraHub Home