179 lines
6.6 KiB
HTML
179 lines
6.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>{{.Title }} | ShowerLoop</title>
|
|
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<!-- Meta description -->
|
|
{{ with .Description }}
|
|
<meta name="description" content="{{ . }}">
|
|
{{ else }}
|
|
<meta name="description" content="ShowerLoop - Real-time filtration, purification, recycling & heat recovery system for showers. Open source and sustainable water conservation technology.">
|
|
{{ end }}
|
|
|
|
<!-- Back/Forward Cache Fix for Hugo Development Server -->
|
|
{{ if eq hugo.Environment "development" }}
|
|
<script>
|
|
// Store WebSocket connections to properly handle back/forward navigation
|
|
let liveReloadSocket = null;
|
|
|
|
// Override the default WebSocket constructor to control the livereload connection
|
|
const OriginalWebSocket = window.WebSocket;
|
|
window.WebSocket = function(url, protocols) {
|
|
// If this is Hugo's livereload WebSocket
|
|
if (url.includes('/__livereload')) {
|
|
// Don't create the WebSocket immediately if document is still loading
|
|
// This allows the bfcache to work
|
|
if (document.readyState === 'complete') {
|
|
liveReloadSocket = new OriginalWebSocket(url, protocols);
|
|
|
|
// Store the original handlers
|
|
const originalHandlers = {
|
|
onmessage: null,
|
|
onclose: null
|
|
};
|
|
|
|
// Proxy the event handlers
|
|
Object.defineProperty(liveReloadSocket, 'onmessage', {
|
|
set: function(handler) {
|
|
originalHandlers.onmessage = handler;
|
|
},
|
|
get: function() {
|
|
return originalHandlers.onmessage;
|
|
}
|
|
});
|
|
|
|
Object.defineProperty(liveReloadSocket, 'onclose', {
|
|
set: function(handler) {
|
|
originalHandlers.onclose = handler;
|
|
},
|
|
get: function() {
|
|
return originalHandlers.onclose;
|
|
}
|
|
});
|
|
|
|
// Implement our own handlers
|
|
liveReloadSocket.addEventListener('message', function(e) {
|
|
if (originalHandlers.onmessage) originalHandlers.onmessage(e);
|
|
});
|
|
|
|
liveReloadSocket.addEventListener('close', function(e) {
|
|
liveReloadSocket = null;
|
|
if (originalHandlers.onclose) originalHandlers.onclose(e);
|
|
});
|
|
|
|
return liveReloadSocket;
|
|
}
|
|
|
|
// Return a fake WebSocket object that does nothing
|
|
// This allows the page to be cached
|
|
return {
|
|
send: function() {},
|
|
close: function() {},
|
|
addEventListener: function() {},
|
|
removeEventListener: function() {},
|
|
set onmessage(handler) {},
|
|
set onclose(handler) {}
|
|
};
|
|
}
|
|
|
|
// For all other WebSockets, use the original constructor
|
|
return new OriginalWebSocket(url, protocols);
|
|
};
|
|
|
|
// Copy properties from the original WebSocket
|
|
for (const prop in OriginalWebSocket) {
|
|
if (OriginalWebSocket.hasOwnProperty(prop)) {
|
|
window.WebSocket[prop] = OriginalWebSocket[prop];
|
|
}
|
|
}
|
|
window.WebSocket.prototype = OriginalWebSocket.prototype;
|
|
|
|
// When page is shown (including from bfcache)
|
|
document.addEventListener('pageshow', function(event) {
|
|
// Check if page is coming from bfcache
|
|
if (event.persisted) {
|
|
console.log('Page restored from bfcache');
|
|
|
|
// Reconnect WebSocket for live reload
|
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
liveReloadSocket = new OriginalWebSocket(`${protocol}//${window.location.host}/__livereload`);
|
|
|
|
liveReloadSocket.onmessage = function(e) {
|
|
if (e.data === 'reload') {
|
|
window.location.reload();
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// Disconnect WebSocket before page unloads
|
|
window.addEventListener('pagehide', function() {
|
|
if (liveReloadSocket) {
|
|
liveReloadSocket.onclose = null; // Prevent auto-reconnect
|
|
liveReloadSocket.close();
|
|
liveReloadSocket = null;
|
|
}
|
|
});
|
|
</script>
|
|
{{ end }}
|
|
|
|
<!-- Critical CSS preloaded -->
|
|
<link rel="preload" href="/css/vendor/material-icons.css" as="style">
|
|
<!-- Removed VideoJS preload to reduce initial payload -->
|
|
|
|
<!-- Preload logo image -->
|
|
<link rel="preload" href="/images/logo2.webp" as="image">
|
|
|
|
<!-- Material Design -->
|
|
<link rel="stylesheet" href="/css/vendor/material-icons.css">
|
|
<link rel="stylesheet" href="/css/vendor/material.indigo-pink.min.css" media="print" onload="this.media='all'">
|
|
<noscript><link rel="stylesheet" href="/css/vendor/material.indigo-pink.min.css"></noscript>
|
|
|
|
<!-- Font Awesome - deferred loading -->
|
|
<link rel="stylesheet" href="/css/vendor/fontawesome.min.css" media="print" onload="this.media='all'">
|
|
<noscript><link rel="stylesheet" href="/css/vendor/fontawesome.min.css"></noscript>
|
|
|
|
<!-- Modern browsers - ES modules with efficient loading -->
|
|
{{ $hasVideo := or (eq .Layout "video") (isset .Params "hero_video_url") (findRE "<video.*class=\"video-js\".*>" .Content) }}
|
|
|
|
<!-- Essential utilities first - other scripts depend on this -->
|
|
<script type="module">
|
|
// Create a dynamic import that will resolve ./utils.js relative imports
|
|
import * as utils from '/js/utils.modern.min.js';
|
|
window.utilsModule = utils; // Make it globally available if needed
|
|
</script>
|
|
|
|
<!-- Core app script - always load this -->
|
|
<script type="module" src="/js/app.modern.min.js" defer></script>
|
|
|
|
<!-- Load only video initialization code (actual VideoJS loaded on demand) -->
|
|
{{ if $hasVideo }}
|
|
<script type="module" src="/js/videojs/video-init.min.js" defer></script>
|
|
{{ end }}
|
|
|
|
<!-- Accessibility script -->
|
|
<script type="module" src="/js/skip-to-content.modern.min.js" defer></script>
|
|
|
|
<!-- Material Design loaded with defer for non-critical UI -->
|
|
<script type="module" src="/js/material.modern.min.js" defer></script>
|
|
|
|
<!-- Legacy browsers support - with polyfills -->
|
|
<script nomodule src="/js/app.min.js" defer></script>
|
|
{{ if $hasVideo }}
|
|
<script nomodule src="/js/videojs/video-init.min.js" defer></script>
|
|
{{ end }}
|
|
<script nomodule src="/js/skip-to-content.min.js" defer></script>
|
|
<script nomodule src="/js/material.min.js" defer></script>
|
|
|
|
<!-- Custom CSS -->
|
|
<link rel="stylesheet" type="text/css" href="/css/app.min.css">
|
|
<link rel="stylesheet" type="text/css" href="/css/custom.css" media="print" onload="this.media='all'">
|
|
<noscript><link rel="stylesheet" href="/css/custom.css"></noscript>
|
|
|
|
<!-- Favicon -->
|
|
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
|
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
|
|
</head>
|