175 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			6.5 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">
 | 
						|
<!-- 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>
 | 
						|
 | 
						|
<!-- 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>
 | 
						|
 | 
						|
<!-- 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>
 | 
						|
<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">
 | 
						|
 | 
						|
<!-- VideoJS for HLS -->
 | 
						|
<link rel="stylesheet" href="/static/css/video-js.min.css">
 | 
						|
<link rel="stylesheet" href="/static/css/custom-video.css">
 | 
						|
<link rel="stylesheet" href="/static/css/chrome-video-fallback.css">
 | 
						|
<script src="/static/js/vendor/video.min.js"></script>
 | 
						|
<script src="/static/js/vendor/videojs-http-streaming.min.js"></script>
 | 
						|
<script src="/static/js/videojs-player.min.js"></script>
 | 
						|
<script src="/static/js/chrome-video-fallback.min.js"></script>
 | 
						|
</head>
 |