Blog Post 38
Old Deceased "Some Have Left Us" was written in PERL 25 years ago
The old PERL code was here https://ameshigh.org/cgi-bin/leftus.cgi/view
Goal: create a web page to make the Ames High School deceased alumni list searchable, robust and easier to export, import and maintain the data.
How the conversion began
The old leftus.cgi page had been doing its job for 25 years. My friend Nick wrote the original code and I (Ed) finished it, updated and maintained the PERl code over the next 25 years. It read the deceased alumni information and produced an HTML table on the server. It was dependable, but it was difficult for me to change. The search and sorting behavior belonged to the old PERL application, and every improvement meant working inside an application that I did not originally design, so a bit frustrating.
I wanted a page where visitors could quickly search by a name, choose a graduating year, sort by a column, and use the page comfortably on a phone. I decided to use Eleventy, JavaScript, JSON, and Bulma CSS.
Exporting JSON from FileMaker Pro
FileMaker Pro remains the source of the deceased alumni data. AHSAA has used FMP since 1990 for 36 years!! I though FMP would be an ancient non-functioning database but was very pleasantly surprised to find it well maintained and a very modern database. So I learned FMP scripting and wrote a FileMaker script that gathers the currently, as of today, 7730 deceased records and creates the JSON Deceased file. The export includes names, graduation year, birth date, death date, place of death, obituary information, and Hall of Fame information when it is present.
The FileMaker response has a structure like this:
{
"response": {
"data": [
{
"fieldData": {
"First": "Jane",
"Last": "Doe",
"Year": "1974",
"Birth": "08/05/1956",
"Death": "08/20/2026"
}
}
]
}
}
The important detail is that the alumni fields are inside fieldData. I copy the exported file into the Eleventy data directory as rawDeceasedDataFromFMP.json. Understanding this structure was one of the first important steps because the table cannot use the outer response object directly.
Using an Eleventy shortcode
The HTML table is created by JavaScript after the page loads, so the data needs to be available in the browser. I (and some early AI) wrote an Eleventy shortcode called embedDeceasedData for that purpose.
While Eleventy is building the site, the shortcode reads the JSON file, checks that response.data is an array, extracts each fieldData object, and places the cleaned records in a script tag:
<script>
window.alumniDeceasedData = [ /* the FileMaker records */ ];
</script>
The deceased template calls the shortcode like this from a Nunjucks call
{% embedDeceasedData %}
Speed and very fast searching / sorting times
This means the visitor does not need to wait for a database request or an API call. The static page already contains the data needed by the table. The tradeoff is that the generated HTML file is pretty large, so I had to improve the way the browser creates and updates the table. If viewing just a single graduation year the speed is very fast. Note: There is an option for the visitor to view all 7730 records at once and that does take a few extra seconds to load all 7730 records and it take extra seconds if a visitor clicks one of the column headings to alphabetically sort all 7730 records in the table.
Writing the new JavaScript table
The new deceased.js script has replaced the PERL scripts 100%. Deceased.js waits for the page to load, reads window.alumniDeceasedData, and creates the table rows. Each FileMaker record becomes one row in the table body. I kept the table headings in the Eleventy template so the column structure remains easy to edit, while JavaScript supplies the changing records.
The first iteration started in June of 2026 and only displayed the raw values with a droptdown to select the year of graduation. I then added the features visitors needed:
- Search by first name, middle name, last name, maiden or married surname, and the additional name fields.
- Filter by graduation year, including Teachers and Non-Grads. Example: Filter just the Ames High School class of 1974 deceased classmates https://AmesHigh.org/deceased/?year=1974
- Move one year or ten years at a time with previous and next buttons.
- Sort by the table headings, including names, dates, age, and place of death.
- Show only the year in the Birth & Death columns, then expand a value to its full date when clicked.
- Turn obituary URLs into links that open in a new tab.
- Normalize newspaper names and other obituary text without changing the URLs.
- Normalize Place of Death States to 2 letter abreviations.
- Calculate age from the birth and death dates instead of relying on an old stored value and put that data into a sortable Age column
The iterations that made it fast
My first attempt tried to add every record to the live table one at a time. It worked, but the page felt slow while the browser was repeatedly changing the document. The important improvement was using a DocumentFragment. The script builds the rows in a temporary container and adds them to the table once. This greatly reduces the amount of work the browser has to do while loading.
Filtering and sorting happen in the browser after the initial data load. A name search or year change does not contact the server. The script selects the matching records, clears the current table body, builds the new rows, and inserts them as one group. Once the initial page is loaded, these operations feel very fast.
There were several smaller problems to solve. Some years have no deceased records, so the year controls needed to handle an empty result without breaking. The FileMaker data contains inconsistent capitalization and place formats, so display normalization had to be done without changing the source data. Dates needed validation before calculating age because JavaScript can otherwise turn an impossible date such as February 31 into a different valid date.
Obituary fields could contain a URL, ordinary text, or both. I had to protect the URLs while normalizing the newspaper names and surrounding text. Even a small word such as or needed special handling when it appeared between two obituary links. The final code leaves the links unchanged and cleans only the plain text around them.
The table also had to work on a mobile device. I kept it horizontally usable, made the Birth and Death controls visibly clickable, and preserved the same expand behavior on desktop and mobile. These were small improvements, but together they made the new table feel finished rather than simply being a replacement for the old script.
The final workflow
The complete process is now:
- Update the deceased records in FileMaker Pro.
- Run my FileMaker export script to create the JSON Deceased file.
- Copy the updated JSON file into
src/_data/rawDeceasedDataFromFMP.json. - Run Eleventy to build the site.
- Eleventy uses
embedDeceasedDatato place the records in the generated page. - The browser loads
deceased.jsand creates the searchable, sortable table. - Publish the finished
builddirectory to the website.
The old PERL page was a useful starting point because it showed exactly what information the alumni association needed. The new version keeps that information but gives me a codebase I can understand and improve: FileMaker manages the data, Eleventy builds the static page, and JavaScript provides the interaction after the page loads.
This was not a one-step conversion. It was a series of small tests, broken builds, data checks, and improvements. AI helped me translate ideas into working JavaScript and helped find mistakes, but I still had to inspect the data, test the page, and decide what the alumni table should do. The result is a much more searchable, faster, and maintainable version of the Some Have Left Us list.