Loading...
window.SpotNotes = window.SpotNotes || {}; // Globals window.SpotNotes.map = null; window.SpotNotes.activeNotes = []; window.SpotNotes.userLocation = null; window.SpotNotes.activePadName = ""; window.SpotNotes.activePadId = null; window.SpotNotes.activePadIsPublic = false; window.SpotNotes.activePadOwnerId = null; window.SpotNotes.activePadCanWrite = false; window.SpotNotes.currentUser = null; window.SpotNotes.isTracking = false; window.SpotNotes.userMarker = null; window.SpotNotes.notifiedNoteIds = new Set(); window.SpotNotes.isLoggedIn = false; window.SpotNotes.currentMarkers = []; window.SpotNotes.topZIndex = 1000000; document.addEventListener('DOMContentLoaded', () => { window.SpotNotes.initMap(); window.SpotNotes.initSidebar(); window.SpotNotes.loadNotepads(true); const form = document.getElementById('form-add-collaborator'); if (form) { form.addEventListener('submit', async (e) => { e.preventDefault(); const padId = document.getElementById('share-pad-id').value; const email = document.getElementById('collaborator-email').value; const permission = document.getElementById('collaborator-permission').value; try { const res = await fetch(`/api/notepads/${padId}/collaborators`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, permission }) }); if (res.ok) { form.reset(); const padName = document.getElementById('share-pad-name').textContent; window.SpotNotes.openShareSettingsModal(padId, padName); } else { const err = await res.json(); alert(`Failed to add collaborator: ${err.error}`); } } catch (err) { console.error(err); alert('An error occurred.'); } }); } const formEditPad = document.getElementById('form-edit-pad'); if (formEditPad) { formEditPad.addEventListener('submit', async (e) => { e.preventDefault(); const padId = document.getElementById('pad-id-edit').value; const name = document.getElementById('pad-name-edit').value; const isPublic = document.getElementById('pad-public-edit').checked; const color = document.getElementById('pad-color-edit').value; try { const res = await fetch(`/api/notepads/${padId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, is_public: isPublic, color }) }); if (res.ok) { document.getElementById('modal-edit-pad').classList.add('hidden'); window.SpotNotes.loadNotepads(); // Refresh everything } else { alert('Failed to update notepad.'); } } catch (err) { console.error(err); } }); } const formEditNote = document.getElementById('form-edit-note'); if (formEditNote) { formEditNote.addEventListener('submit', async (e) => { e.preventDefault(); const noteId = document.getElementById('note-id-edit').value; const title = document.getElementById('note-title-edit').value; const content = document.getElementById('note-content-edit').value; try { const res = await fetch(`/api/notes/${noteId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title, content }) }); if (res.ok) { document.getElementById('modal-edit-note').classList.add('hidden'); // Refresh current view if (window.SpotNotes.activePadId) { window.SpotNotes.loadNotesForPad(window.SpotNotes.activePadId); } else { // This was likely from a search result, which is harder to refresh. // A full reload of notepads is a safe fallback. alert('Note updated. Refreshing list.'); window.SpotNotes.loadNotepads(); } } else { alert('Failed to update note.'); } } catch (err) { console.error(err); } }); } }); window.SpotNotes.loadNotepads = async function(isInitialLoad = false) { // Reset state window.SpotNotes.activeNotes = []; window.SpotNotes.currentMarkers.forEach(marker => window.SpotNotes.map.removeLayer(marker)); window.SpotNotes.currentMarkers = []; // Check auth first try { const authRes = await fetch('/auth/me'); const authData = await authRes.json(); window.SpotNotes.isLoggedIn = authData.authenticated; window.SpotNotes.currentUser = authData.user || null; } catch (e) { console.error(e); } // Reset Sidebar Header const header = document.querySelector('.sidebar-header'); if (header) { let headerHTML = '
Loading...
Loading...
Error loading data.
'; } } else { // Logged out: Only Public Notepads sidebarBody.innerHTML = `Loading...
Error loading data.
'; } } window.SpotNotes.loadNotesForPad = async function(padId, padName, isPublic, ownerId, centerOnFirst = false, token = null, canWrite = undefined) { window.SpotNotes.activePadId = padId; // If metadata is missing (e.g. from URL share), fetch it if (padName === undefined) { try { const res = await fetch(`/api/notepads/${padId}?token=${token || ''}`); const pad = await res.json(); padName = pad.name; isPublic = pad.is_public; ownerId = pad.owner_id; window.SpotNotes.activePadCanWrite = pad.can_write; } catch(e) { console.error(e); window.SpotNotes.activePadCanWrite = false; } } if (padName !== undefined) window.SpotNotes.activePadName = padName; if (isPublic !== undefined) window.SpotNotes.activePadIsPublic = isPublic; if (ownerId !== undefined) window.SpotNotes.activePadOwnerId = ownerId; if (canWrite !== undefined) { window.SpotNotes.activePadCanWrite = canWrite; } else if (padName !== undefined) { // Fallback if not passed: assume true only if owner window.SpotNotes.activePadCanWrite = (window.SpotNotes.currentUser && ownerId === window.SpotNotes.currentUser.id); } const currentName = window.SpotNotes.activePadName; const isOwner = window.SpotNotes.currentUser && window.SpotNotes.activePadOwnerId === window.SpotNotes.currentUser.id; // Clear existing markers window.SpotNotes.currentMarkers.forEach(marker => window.SpotNotes.map.removeLayer(marker)); window.SpotNotes.currentMarkers = []; window.SpotNotes.activeNotes = []; // Update Sidebar Header const header = document.querySelector('.sidebar-header'); if (header) { const addNoteBtn = window.SpotNotes.activePadCanWrite ? `` : ''; const manageShareBtn = isOwner ? `` : ''; const exportBtn = ``; header.innerHTML = `Loading notes...
'; try { const res = await fetch(`/api/notepads/${padId}/notes?token=${token || ''}`); const notes = await res.json(); window.SpotNotes.activeNotes = notes; // Render Sidebar List window.SpotNotes.renderActiveNotesList(); window.SpotNotes.renderMapMarkers(); const filterInput = document.getElementById('filter-active-notes'); if (filterInput) { filterInput.addEventListener('keyup', (e) => { window.SpotNotes.renderActiveNotesList(e.target.value); }); } if (centerOnFirst && notes.length > 0) { window.SpotNotes.map.setView([notes[0].lat, notes[0].lng], 13); } } catch (err) { console.error(err); sidebarBody.innerHTML = 'Error loading notes.
'; } } window.SpotNotes.loadSingleNote = async function(noteId, token = null) { // Reset state window.SpotNotes.activeNotes = []; window.SpotNotes.currentMarkers.forEach(marker => window.SpotNotes.map.removeLayer(marker)); window.SpotNotes.currentMarkers = []; window.SpotNotes.activePadId = null; const sidebarBody = document.getElementById('sidebar-body'); sidebarBody.innerHTML = 'Loading note...
'; try { const res = await fetch(`/api/notes/${noteId}?token=${token || ''}`); if (!res.ok) throw new Error("Note not found or private"); const note = await res.json(); // Set active notes to just this one so window.SpotNotes.renderMapMarkers works window.SpotNotes.activeNotes = [note]; // Update Header const header = document.querySelector('.sidebar-header'); if (header) { header.innerHTML = `Failed to load note. It may be private or deleted.
'; } } window.SpotNotes.displaySearchResults = function(notes, location, query) { // Reset Pad State to "Search Mode" window.SpotNotes.activePadId = null; window.SpotNotes.activePadName = `Search: ${query}`; window.SpotNotes.activePadIsPublic = false; // Mixed results window.SpotNotes.activePadOwnerId = null; // Mixed results window.SpotNotes.activeNotes = notes; // Update Sidebar Header const header = document.querySelector('.sidebar-header'); if (header) { header.innerHTML = `No results found.
'; window.SpotNotes.renderMapMarkers(); return; } else if (notes.length === 0 && location) { sidebarBody.innerHTML = 'Location found on window.SpotNotes.map.
'; window.SpotNotes.renderMapMarkers(); window.SpotNotes.map.setView([location.lat, location.lng], 13); return; } // Group notes by section and notepad const sections = { favorites: {}, // MapNo matching notes.
'; } else { padIds.forEach(padId => { const entry = dataMap[padId]; const pad = entry.pad; // Notepad Header, styled like the default view const padItem = document.createElement('div'); padItem.className = 'notepad-item'; padItem.style.backgroundColor = '#e9ecef'; // Differentiate as a sub-header padItem.style.cursor = 'default'; let icon = pad.is_public ? '🌍' : '🔒'; let favButton = ''; if(window.SpotNotes.isLoggedIn){ const isFavorite = pad.is_favorite; const action = isFavorite ? 'unfavorite' : 'favorite'; const label = isFavorite ? '♥' : '♡'; favButton = ``; } padItem.innerHTML = `Loading...
'; modal.classList.remove('hidden'); try { const res = await fetch(`/api/notepads/${padId}/collaborators`); if (!res.ok) { const err = await res.json(); throw new Error(err.error || 'Failed to load collaborators'); } const collaborators = await res.json(); listDiv.innerHTML = ''; if (collaborators.length === 0) { listDiv.innerHTML = 'No collaborators yet.
'; } else { collaborators.forEach(collab => { const item = document.createElement('div'); item.className = 'collaborator-item'; item.innerHTML = ` ${window.SpotNotes.escapeHtml(collab.email)} (${collab.permission}) `; listDiv.appendChild(item); }); } } catch (err) { listDiv.innerHTML = `${err.message}
`; } } window.SpotNotes.removeCollaborator = async function(padId, userId) { if (!confirm("Are you sure you want to remove this collaborator?")) return; try { const res = await fetch(`/api/notepads/${padId}/collaborators/${userId}`, { method: 'DELETE' }); if (res.ok) { const padName = document.getElementById('share-pad-name').textContent; window.SpotNotes.openShareSettingsModal(padId, padName); } else { const err = await res.json(); alert(`Failed to remove collaborator: ${err.error}`); } } catch (err) { console.error(err); alert('An error occurred.'); } } window.SpotNotes.loadAllMyNotes = async function() { window.SpotNotes.activePadId = null; window.SpotNotes.activePadName = "All My Notes"; window.SpotNotes.activePadIsPublic = false; window.SpotNotes.activePadCanWrite = true; if (window.SpotNotes.currentUser) { window.SpotNotes.activePadOwnerId = window.SpotNotes.currentUser.id; } // Clear existing markers window.SpotNotes.currentMarkers.forEach(marker => window.SpotNotes.map.removeLayer(marker)); window.SpotNotes.currentMarkers = []; window.SpotNotes.activeNotes = []; try { const res = await fetch('/api/notes/mine'); if (!res.ok) throw new Error("Failed to load"); const notes = await res.json(); window.SpotNotes.activeNotes = notes; window.SpotNotes.renderMapMarkers(); if (notes.length > 0) { // Auto-center window.SpotNotes.map bounds cleanly around all the user's notes const bounds = L.latLngBounds(notes.map(n => [n.lat, n.lng])); window.SpotNotes.map.fitBounds(bounds, { padding: [50, 50], maxZoom: 15 }); } } catch (err) { console.error("Error loading all my notes:", err); } } window.SpotNotes.shareNotepad = function(padId, token) { let url = `${window.location.origin}/?pad=${padId}`; if (token) url += `&token=${token}`; navigator.clipboard.writeText(url).then(() => { alert("Notepad link copied to clipboard!"); }); }; window.SpotNotes.shareNote = function(noteId, token) { let url = `${window.location.origin}/?note=${noteId}`; if (token) url += `&token=${token}`; navigator.clipboard.writeText(url).then(() => { alert("Note link copied to clipboard!"); }); }; window.SpotNotes.escapeHtml = function(text) { if (text === null || text === undefined) return ''; return String(text) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } window.SpotNotes.deleteNote = async function(noteId) { if (!confirm("Delete this note?")) return; try { const res = await fetch(`/api/notes/${noteId}`, { method: 'DELETE' }); if (res.ok) { // If we are in a pad view, reload the pad. if (window.SpotNotes.activePadId) { window.SpotNotes.loadNotesForPad(window.SpotNotes.activePadId); } else { // Otherwise (e.g., search results), the safest refresh is to return to the main list. alert("Note deleted. Returning to notepad list."); window.SpotNotes.loadNotepads(); } } else { alert("Failed to delete note."); } } catch (err) { console.error(err); } }; window.SpotNotes.renderActiveNotesList = function(filterText = '') { const sidebarBody = document.getElementById('sidebar-body'); if (!sidebarBody) return; sidebarBody.innerHTML = ''; const filterLower = filterText.toLowerCase(); const filteredNotes = window.SpotNotes.activeNotes.filter(note => { return note.title.toLowerCase().includes(filterLower) || note.content.toLowerCase().includes(filterLower); }); if (filteredNotes.length === 0) { sidebarBody.innerHTML = 'No notes match.
'; } filteredNotes.forEach(note => { const item = document.createElement('div'); item.className = 'sidebar-note-item'; let actionBtns = ''; if (window.SpotNotes.activePadCanWrite) { const escapedTitle = window.SpotNotes.escapeHtml(note.title).replace(/'/g, "\\'").replace(/"/g, '"'); const escapedContent = window.SpotNotes.escapeHtml(note.content).replace(/'/g, "\\'").replace(/"/g, '"').replace(/\n/g, '\\n'); actionBtns = ` `; } else if (window.SpotNotes.activePadIsPublic) { actionBtns = ``; } const dateStr = new Date(note.created_at).toLocaleString(); item.innerHTML = `