Add page-specific JavaScript support
Update documentation for new JS variables and rendering pipeline Add script.js file support to content directories Implement cache-busting for JavaScript files Update static file serving to include JavaScript files Document page-specific script loading in base.php
This commit is contained in:
parent
f8a352afce
commit
f1447049e4
6 changed files with 70 additions and 7 deletions
|
|
@ -241,6 +241,33 @@ FolderWeb automatically loads and includes page-specific styles with cache-busti
|
||||||
<link rel="stylesheet" href="/portfolio/styles.css?v=abc123def">
|
<link rel="stylesheet" href="/portfolio/styles.css?v=abc123def">
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Page-Specific Scripts
|
||||||
|
|
||||||
|
For small progressive enhancements, you can add a `script.js` file to a content directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
content/portfolio/
|
||||||
|
├── index.md
|
||||||
|
├── styles.css
|
||||||
|
└── script.js
|
||||||
|
```
|
||||||
|
|
||||||
|
**script.js:**
|
||||||
|
```js
|
||||||
|
// Small enhancement for this page only
|
||||||
|
document.querySelector('.portfolio-grid')?.addEventListener('click', (e) => {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
FolderWeb automatically loads the script with `defer` (non-blocking) and cache-busting:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script defer src="/portfolio/script.js?v=abc123def"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
The script tag is placed before `</body>`, so it runs after the page has been parsed. This is ideal for progressive enhancement — the page works without JavaScript, but gets enhanced when it's available.
|
||||||
|
|
||||||
## Dark Mode
|
## Dark Mode
|
||||||
|
|
||||||
Add dark mode with CSS variables and `prefers-color-scheme`:
|
Add dark mode with CSS variables and `prefers-color-scheme`:
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,10 @@ Let's modify `base.php` to add your site name and custom navigation:
|
||||||
<small>Generated in <?= number_format($pageLoadTime, 4) ?>s</small>
|
<small>Generated in <?= number_format($pageLoadTime, 4) ?>s</small>
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<?php if (!empty($pageJsUrl)): ?>
|
||||||
|
<script defer src="<?= htmlspecialchars($pageJsUrl) ?>?v=<?= $pageJsHash ?? '' ?>"></script>
|
||||||
|
<?php endif; ?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
@ -301,6 +305,7 @@ $languageUrls // Links to other language versions
|
||||||
$translations // Translated UI strings
|
$translations // Translated UI strings
|
||||||
$cssHash // Cache-busting hash for CSS
|
$cssHash // Cache-busting hash for CSS
|
||||||
$pageCssUrl // Page-specific CSS URL (if exists)
|
$pageCssUrl // Page-specific CSS URL (if exists)
|
||||||
|
$pageJsUrl // Page-specific JS URL (if exists)
|
||||||
$pageLoadTime // Page generation time
|
$pageLoadTime // Page generation time
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,27 @@ MD5 hash of page-specific CSS for cache busting.
|
||||||
**Optional:** Only set if `$pageCssUrl` exists
|
**Optional:** Only set if `$pageCssUrl` exists
|
||||||
**Example:** See `$pageCssUrl` above
|
**Example:** See `$pageCssUrl` above
|
||||||
|
|
||||||
|
### `$pageJsUrl`
|
||||||
|
|
||||||
|
URL to page-specific JavaScript file.
|
||||||
|
|
||||||
|
**Type:** String (URL)
|
||||||
|
**Optional:** Only set if `script.js` exists in content directory
|
||||||
|
**Example:**
|
||||||
|
```php
|
||||||
|
<?php if (!empty($pageJsUrl)): ?>
|
||||||
|
<script defer src="<?= htmlspecialchars($pageJsUrl) ?>?v=<?= htmlspecialchars($pageJsHash ?? '') ?>"></script>
|
||||||
|
<?php endif; ?>
|
||||||
|
```
|
||||||
|
|
||||||
|
### `$pageJsHash`
|
||||||
|
|
||||||
|
MD5 hash of page-specific JavaScript for cache busting.
|
||||||
|
|
||||||
|
**Type:** String (MD5 hash)
|
||||||
|
**Optional:** Only set if `$pageJsUrl` exists
|
||||||
|
**Example:** See `$pageJsUrl` above
|
||||||
|
|
||||||
### `$feedUrl`
|
### `$feedUrl`
|
||||||
|
|
||||||
URL to the Atom feed for the current list page.
|
URL to the Atom feed for the current list page.
|
||||||
|
|
@ -415,6 +436,8 @@ Then use in templates:
|
||||||
| `$translations` | ✓ | — | — |
|
| `$translations` | ✓ | — | — |
|
||||||
| `$pageCssUrl` | ✓ | — | — |
|
| `$pageCssUrl` | ✓ | — | — |
|
||||||
| `$pageCssHash` | ✓ | — | — |
|
| `$pageCssHash` | ✓ | — | — |
|
||||||
|
| `$pageJsUrl` | ✓ | — | — |
|
||||||
|
| `$pageJsHash` | ✓ | — | — |
|
||||||
| `$feedUrl` | ✓ | — | — |
|
| `$feedUrl` | ✓ | — | — |
|
||||||
| `$metadata` | — | ✓ | ✓ |
|
| `$metadata` | — | ✓ | ✓ |
|
||||||
| `$pageContent` | — | — | ✓ |
|
| `$pageContent` | — | — | ✓ |
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,12 @@ Checks extensions in `COVER_IMAGE_EXTENSIONS` order: `jpg`, `jpeg`, `png`, `webp
|
||||||
|
|
||||||
Returns `['url' => string, 'hash' => string]` or null. Hash is MD5 of file content for cache busting.
|
Returns `['url' => string, 'hash' => string]` or null. Hash is MD5 of file content for cache busting.
|
||||||
|
|
||||||
|
## Page-Specific JavaScript
|
||||||
|
|
||||||
|
**`findPageJs(string $dirPath, string $contentDir): ?array`** — Checks for `script.js` in content directory.
|
||||||
|
|
||||||
|
Returns `['url' => string, 'hash' => string]` or null. Hash is MD5 of file content for cache busting. The script is loaded with the `defer` attribute in `base.php`, placed before `</body>` for non-blocking progressive enhancement.
|
||||||
|
|
||||||
## Meta Description Extraction
|
## Meta Description Extraction
|
||||||
|
|
||||||
**`extractMetaDescription(string $dirPath, ?array $metadata): ?string`**
|
**`extractMetaDescription(string $dirPath, ?array $metadata): ?string`**
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ Variables are injected via `extract()` — each array key becomes a local variab
|
||||||
| `$homeLabel` | string | yes | Home link text |
|
| `$homeLabel` | string | yes | Home link text |
|
||||||
| `$pageCssUrl` | ?string | no | Page-specific CSS URL |
|
| `$pageCssUrl` | ?string | no | Page-specific CSS URL |
|
||||||
| `$pageCssHash` | ?string | no | CSS cache-bust hash |
|
| `$pageCssHash` | ?string | no | CSS cache-bust hash |
|
||||||
|
| `$pageJsUrl` | ?string | no | Page-specific JS URL |
|
||||||
|
| `$pageJsHash` | ?string | no | JS cache-bust hash |
|
||||||
| `$feedUrl` | ?string | no | Atom feed URL (only on lists with `feed = true`) |
|
| `$feedUrl` | ?string | no | Atom feed URL (only on lists with `feed = true`) |
|
||||||
| `$currentLang` | string | plugin | Language code (from languages plugin) |
|
| `$currentLang` | string | plugin | Language code (from languages plugin) |
|
||||||
| `$langPrefix` | string | plugin | URL language prefix |
|
| `$langPrefix` | string | plugin | URL language prefix |
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ Used for both frontpage and page views:
|
||||||
1. Load metadata for `$pageDir`
|
1. Load metadata for `$pageDir`
|
||||||
2. Load page plugins (from metadata `plugins` field)
|
2. Load page plugins (from metadata `plugins` field)
|
||||||
3. Render all content files, concatenate HTML
|
3. Render all content files, concatenate HTML
|
||||||
4. Compute: `$pageTitle`, `$metaDescription`, `$pageCssUrl`/`$pageCssHash`, `$socialImageUrl`
|
4. Compute: `$pageTitle`, `$metaDescription`, `$pageCssUrl`/`$pageCssHash`, `$pageJsUrl`/`$pageJsHash`, `$socialImageUrl`
|
||||||
5. Fire `Hook::TEMPLATE_VARS` with all variables
|
5. Fire `Hook::TEMPLATE_VARS` with all variables
|
||||||
6. `extract()` variables → render `page.php` → capture output as `$content`
|
6. `extract()` variables → render `page.php` → capture output as `$content`
|
||||||
7. Render `base.php` with `$content` + base variables
|
7. Render `base.php` with `$content` + base variables
|
||||||
|
|
@ -37,12 +37,12 @@ Handled directly in `router.php` (not a separate function):
|
||||||
2. Load metadata, check `hide_list`
|
2. Load metadata, check `hide_list`
|
||||||
3. Select list template from `page_template` metadata
|
3. Select list template from `page_template` metadata
|
||||||
4. Call `buildListItems()` from `helpers.php` (builds + sorts items)
|
4. Call `buildListItems()` from `helpers.php` (builds + sorts items)
|
||||||
5. Store `pageTitle`, `metaDescription`, `pageCssUrl`, `pageCssHash`, `feedUrl` on context
|
5. Store `pageTitle`, `metaDescription`, `pageCssUrl`, `pageCssHash`, `pageJsUrl`, `pageJsHash`, `feedUrl` on context
|
||||||
6. Fire `Hook::TEMPLATE_VARS`
|
6. Fire `Hook::TEMPLATE_VARS`
|
||||||
7. Render list template → capture as `$content`
|
7. Render list template → capture as `$content`
|
||||||
8. Pass to `renderTemplate()` which renders `base.php`
|
8. Pass to `renderTemplate()` which renders `base.php`
|
||||||
|
|
||||||
**`renderTemplate(Context $ctx, string $content, int $statusCode = 200): void`** — Wraps content in base template. Used for list views and error pages. Reads `pageTitle`, `metaDescription`, `pageCssUrl`, `pageCssHash`, and `feedUrl` from the context object (set by the list case in `router.php`). For error pages, these context keys are unset, so base.php receives nulls.
|
**`renderTemplate(Context $ctx, string $content, int $statusCode = 200): void`** — Wraps content in base template. Used for list views and error pages. Reads `pageTitle`, `metaDescription`, `pageCssUrl`, `pageCssHash`, `pageJsUrl`, `pageJsHash`, and `feedUrl` from the context object (set by the list case in `router.php`). For error pages, these context keys are unset, so base.php receives nulls.
|
||||||
|
|
||||||
## Atom Feed Rendering
|
## Atom Feed Rendering
|
||||||
|
|
||||||
|
|
@ -79,9 +79,9 @@ setCachedMarkdown(string $filePath, string $html, string $langPrefix = ''): void
|
||||||
|
|
||||||
Before content routing, the router serves static files from content directory with an explicit MIME type allowlist:
|
Before content routing, the router serves static files from content directory with an explicit MIME type allowlist:
|
||||||
|
|
||||||
`css`, `jpg`, `jpeg`, `png`, `gif`, `webp`, `svg`, `pdf`, `woff`, `woff2`, `ttf`, `otf`
|
`css`, `jpg`, `jpeg`, `png`, `gif`, `webp`, `svg`, `pdf`, `woff`, `woff2`, `ttf`, `otf`, `js`
|
||||||
|
|
||||||
Files not in this list are not served as static assets. Notably, `.js` files are excluded — JavaScript must be placed in `custom/assets/` to be served (at the document root URL), or linked from an external source.
|
Files not in this list are not served as static assets.
|
||||||
|
|
||||||
### Custom Assets (router.php)
|
### Custom Assets (router.php)
|
||||||
|
|
||||||
|
|
@ -100,9 +100,9 @@ Files in `custom/assets/` are served at the document root URL. Example: `custom/
|
||||||
|
|
||||||
MIME types resolved from extension map, falling back to `mime_content_type()`.
|
MIME types resolved from extension map, falling back to `mime_content_type()`.
|
||||||
|
|
||||||
## CSS Cache Busting
|
## CSS and JS Cache Busting
|
||||||
|
|
||||||
Page-specific CSS gets an MD5 hash appended: `?v={hash}`. Computed by `findPageCss()`. The default theme's CSS is linked directly without hash (uses browser caching).
|
Page-specific CSS and JS get an MD5 hash appended: `?v={hash}`. Computed by `findPageCss()` and `findPageJs()` respectively. The default theme's CSS is linked directly without hash (uses browser caching).
|
||||||
|
|
||||||
## Parsedown
|
## Parsedown
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue