// Enable debug mode if the URL contains the debug parameter let debug = false; if (window.location.href.includes('debug')) { console.log('Debug mode enabled'); debug = true; } // Initialize the EventSource and the HTML elements const eventSource = new EventSource('https://ntfy.example.org/guestbook/sse?since=all'); const entryText = document.getElementById('entryText'); const name = document.getElementById('name'); const submitEntry = document.getElementById('submitEntry'); const entryTable = document.getElementById('entryTable'); const errorMessage = document.getElementById('error-message'); const bell = new Audio('/resources/sounds/notification.ogg'); // Disable the notification sound for the first 2 seconds until the guestbook is loaded initially let bellEnabled = false; // Event listener for the Enter key function handleKeydown(event) { if (event.key === 'Enter') { sendMessage(); } } // Check if the name is valid function checkName(name) { if (name.match(/:\s/)) { errorMessage.classList.add('visible'); console.error('Invalid name'); return false; } return true; } // Send new entry to the ntfy server function sendMessage() { const text = entryText.value.trim(); const author = name.value.trim() ? name.value.trim() : 'Anonymous'; if (checkName(author)) { errorMessage.classList.remove('visible'); const message = author + ': ' + text; if (text.length > 0) { fetch('https://ntfy.example.org/guestbook', { method: 'POST', body: message }) .then(() => { // Empty the input field entryText.value = ''; // Save the name in the local storage localStorage.setItem('name', author); }) .catch(error => console.error(error)); } } } // Calculate time passed since the message was sent function timeSince(date) { let seconds = Math.floor(Date.now() / 1000 - date); let interval = Math.floor(seconds / 3600); if (interval >= 2) { return interval + " hours ago"; } if (interval === 1) { return "1 hour ago" } interval = Math.floor(seconds / 60); if (interval > 1) { return interval + " minutes ago"; } if (interval === 1) { return "1 minute ago" } if (seconds > 10) { return Math.floor(seconds) + " seconds ago"; } if (seconds > -10) { return "just now" } if (seconds > -60) { return "in " + Math.floor(-seconds) + " seconds"; } interval = Math.floor(-seconds / 60); if (interval === 1) { return "in 1 minute"; } if (interval < 60) { return "in " + interval + " minutes"; } interval = Math.floor(-seconds / 3600); if (interval === 1) { return "in 1 hour"; } if (interval > 1) { return "in " + interval + " hours"; } } // Parse the time from the message function parseTimeString(data) { return ` | ${timeSince(data.time)}, expires in ${timeSince(data.expires)}` } // Update time function updateTime() { const timeElements = document.getElementsByClassName('time'); for (let i = 0; i < timeElements.length; i++) { const time = timeElements[i]; time.textContent = parseTimeString(time.dataset); } } // Add a new entry to the list function addEntry(data) { const row = document.createElement('tr'); // Create a new cell for the author const authorCell = document.createElement('td'); authorCell.classList.add('author'); authorCell.style.fontWeight = 'italic'; // Create a new cell for the message const messageCell = document.createElement('td'); messageCell.classList.add('message'); // Split the message into author and text const [name, message] = data.message.split(/:\s(.*)/); // Create span with the time that passed since the message was sent // and when the message will expire const time = document.createElement('span'); time.classList.add('time'); time.dataset.time = data.time; time.dataset.expires = data.expires; time.textContent = parseTimeString(data); // Set the cell content authorCell.textContent = name + ':'; messageCell.innerHTML = message + time.outerHTML; // Append the cells to the row and the row to the table row.appendChild(authorCell); row.appendChild(messageCell); entryTable.appendChild(row); } // Connect to the server to receive new entries function streamEntries() { eventSource.onmessage = (m) => { if (debug) { console.log(m.data); } const data = JSON.parse(m.data) addEntry(data); // Play a notification sound for new entries try { if (bellEnabled) { bell.play(); } } catch (DOMException) { console.log('The notification sound was blocked by the browser'); } }; } // Initialize the page function setup() { // Add event listeners submitEntry.addEventListener('click', sendMessage); entryText.addEventListener('keydown', handleKeydown); // Start streaming the entries streamEntries(); // Update the time values every 10 seconds setInterval(updateTime, 10000); // Load the name from the local storage name.value = localStorage.getItem('name'); // Enable the notification sound after 2 seconds setTimeout(() => { bellEnabled = true; }, 2000); // Close the connection when the page is closed window.addEventListener('beforeunload', function (e) { e.preventDefault(); eventSource.close(); }); } window.addEventListener('load', setup)