import * as note from './note.js'; import { state } from './state.js'; import * as ref from './reference.js'; // Get the link element var tabs = []; let controller; // Function to change the CSS file based on checkbox state /** * Method to toggle dark/light mode * * @param {*} event */ export function toggleDarkMode(event) { let mainCssLink = $('link[data-css="mode"'); if(mainCssLink.length > 0) { mainCssLink[0].href = (event.target.checked ? mainCssLink.data('dark') : mainCssLink.data('light')); } } /** * Toggles the visibility of the fields container and updates the active state of the show/hide button. * * @param Event e * @param boolean forceShow * @return {void} */ export function toggleFields(e, forceShow = false) { const fieldsContainer = document.querySelector('.fields-container'); let showHideBtn = document.getElementById('show-hide-btn'); let mobileShowHideBtn = document.getElementById('mobile-show-hide-btn'); if (forceShow || !fieldsContainer.classList.contains('show')) { fieldsContainer.classList.add('show'); fieldsContainer.style.display = 'flex'; showHideBtn.classList.add('active'); mobileShowHideBtn.classList.add('active'); } else { fieldsContainer.classList.remove('show'); fieldsContainer.style.display = 'none'; showHideBtn.classList.remove('active'); mobileShowHideBtn.classList.remove('active'); } } /** * Increases the font size of the element with the id 'ref' by 1 point. * * @return {void} This function does not return a value. */ export function increaseFont() { var currentSize = document.querySelector('#ref-text').style.fontSize; const newSize = parseInt(currentSize) + 1; document.querySelector('#ref-text').style.fontSize = newSize + 'pt'; document.querySelector('#notes').style.fontSize = newSize + 'pt'; document.querySelector('#notePreview').style.fontSize = newSize + 'pt'; } /** * Decreases the font size of the element with the id 'ref' by 1 point. * * @return {void} This function does not return a value. */ export function decreaseFont() { var currentSize = document.querySelector('#ref-text').style.fontSize; const newSize = parseInt(currentSize) - 1; document.querySelector('#ref-text').style.fontSize = newSize + 'pt'; document.querySelector('#notes').style.fontSize = newSize + 'pt'; document.querySelector('#notePreview').style.fontSize = newSize + 'pt'; } /** * Previews a note by rendering the markdown content of the note in a preview section. * Toggles between the note text and preview sections. * * @return {void} This function does not return anything. */ export function previewNote() { var noteText = document.querySelector('#notes'); var notePreview = document.querySelector('#notePreview'); var previewButton = document.querySelector('#previewBtn'); const title = document.querySelector('#noteTitle'); const speaker = document.querySelector('#speaker'); const passage = document.querySelector('#passage'); const markdownPreview = "# " + title.value + " - " + speaker.options[speaker.selectedIndex].text + " - " + passage.value + "\n\n" + noteText.value; notePreview.innerHTML = state.md.render(markdownPreview); if (previewButton.classList.contains('active')) { document.querySelector('.note-text').style.display = 'block'; notePreview.style.display = 'none'; previewButton.classList.remove('active'); } else { document.querySelector('.note-text').style.display = 'none'; notePreview.style.display = 'block'; previewButton.classList.add('active'); } note.findLinks(); } /** * Sets event listeners for keyup events on the document and the '#notes' element. * * @return {void} */ export function setEventListeners() { document.addEventListener('keyup', function (event) { if (event.key == "F3") { openRef(false); } }); document.querySelector('#notes').addEventListener('keyup', function (event) { let key = event.keyCode; if (key == 8 || key >= 48 && key <= 90 || key >= 96 && key <= 111 || key >= 186 && key <= 222) { state.textDirty = true; document.querySelector('.mobile-note-header h2').classList.add('dirty'); document.querySelector('.note-header h2').classList.add('dirty'); } }); document.getElementById('dark-mode-checkbox').addEventListener('click', toggleDarkMode); document.getElementById('mobile-dark-mode-checkbox').addEventListener('click', toggleDarkMode); document.getElementById('show-hide-btn').addEventListener('click', toggleFields); document.getElementById('mobile-show-hide-btn').addEventListener('click', toggleFields); document.getElementById('increaseFont').addEventListener('click', increaseFont); document.getElementById('decreaseFont').addEventListener('click', decreaseFont); document.getElementById('open-ref').addEventListener('click', openRef, {closeSidebar: false}); document.getElementById('mobile-open-ref').addEventListener('click', openRef, {closeSidebar: false}); document.getElementById('previewBtn').addEventListener('click', previewNote); document.getElementById('searchBtn').addEventListener('click', ref.queryRef); document.getElementById('closeSearch').addEventListener('click', closeRef); } export function initHome() { setBooks(); setEventListeners(); $('sidebar-link').on('click', function(event) { event.preventDefault(); event.stopPropagation(); $('#sidebar').toggleClass('inactive'); }); $('sidebar').on('click', 'a', function(event) { var $a = $(this), href = $a.attr('href'), target = $a.attr('target'); event.preventDefault(); event.stopPropagation(); $('sidebar').addClass('inactive'); setTimeout(function() { if (target == '_blank') window.open(href); else window.location.href = href; }, 500); }); $('#note-table').DataTable({ paging: false, ajax: { url: '/get-notes', type: 'POST' }, columns: [ { data: 'link' }, { data: 'speaker.name' }, { data: 'passage' }, { data: 'date.date', render: DataTable.render.date("L") }, ] }); $('#shareBtn').on('click', note.openShareNote); $('#modal-backdrop').on('click', note.closeShareNote); state.md = new markdownit({ html: true, linkify: true, breaks: true }); if ($('#noteDate')) { $('#noteDate').datepicker(); } if ($('#query')) { document.querySelector('#query').addEventListener('keyup', function (event) { if (event.key == "Enter") { search(); } }); } // Assuming 'to' and 'saveInterval' are declared globally elsewhere in your script if (typeof state.to === 'undefined' || !state.to) { state.to = setTimeout(note.saveNote, state.saveInterval); } } /** * Fetches data from '/js/data.json', assigns it to BOOKS, and handles errors. * * @return {void} */ export function setBooks() { fetch('js/data.json') .then((res) => { if (!res.ok) { throw new Error('HTTP Error: Status: ${res.status}'); } return res.json(); }) .then((data) => { state.BOOKS = data; }) .catch((error) => { console.log(error); }) } /** * Opens the reference with the option to close the sidebar. * * @param {boolean} closeSidebar - Indicates whether to close the sidebar when opening the reference. */ export function openRef(e, closeSidebar = true) { document.querySelector('#open-ref').classList.add('active'); document.querySelector('#mobile-open-ref').classList.add('active'); let refQuery = document.querySelector('#refQuery'); refQuery.style.display = 'block'; let ref = document.querySelector('#ref-text'); refQuery.style.left = ref.offsetLeft + 'px'; refQuery.style.top = ref.offsetTop + 'px'; if (closeSidebar) { document.querySelector('.toggle').click(); } } /** * Closes the reference query and resets the reference search form. * * @return {void} This function does not return anything. */ export function closeRef() { document.querySelector('#referenceSearch').value = ''; document.querySelector('#referenceSearch').style.display = ''; document.querySelector('#referenceType').value = ''; document.querySelector('#referenceBook').value = ''; document.querySelector('#referenceBook').style.display = 'none'; document.querySelector('#chapter-range').innerText = ''; document.querySelector('#verse-range').innerText = ''; document.querySelector('#refQuery').style.display = 'none'; document.querySelector('#open-ref').classList.remove('active'); document.querySelector('#mobile-open-ref').classList.remove('active'); } /** * Retrieves a note from the server based on the provided ID. * * @param {string} id - The ID of the note to retrieve. * @param {boolean} [runOpen=true] - Whether to open the note sidebar after retrieving the note. * @return {Promise} A promise that resolves when the note is successfully retrieved and the UI is updated. */ export function retrieveNote(id, runOpen = true) { fetch('/get-note', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ 'id': id }) }) .then(response => response.json()) .then(result => { var dt = new Date(result.date.date); document.querySelector('#notes').value = result.text; document.querySelector('#passage').value = result.passage; document.querySelector('#series').value = result.series.id; document.querySelector('#speaker').value = result.speaker.id; document.querySelector('#noteTitle').value = result.title; document.querySelector('#noteDate').value = ''; document.querySelector('#noteDate').value = (dt.getMonth() < 9 ? '0' + (dt.getMonth() + 1) : (dt.getMonth() + 1)) + '/' + (dt.getDate() < 10 ? '0' + dt.getDate() : dt.getDate()) + '/' + dt.getFullYear(); document.querySelector('#noteId').value = result.id; if (result.refs) { references = result.refs; } const list = document.querySelector('#ref-list'); list.innerHTML = ''; var newList = null; for (var x in references) { var newList = document.createElement('li'); newList.className = 'tab'; var button = makeButton(x); newList.appendChild(button); list.appendChild(newList); } if (runOpen) { note.openNote(false); } }); } /** * A function to create a button element with the specified title and event listeners for click and double click actions. * * @param {string} title - The title to be displayed on the button. * @return {Element} The created button element. */ export function makeButton(title) { var btn = document.createElement('button'); btn.innerText = title; btn.class = 'button'; btn.style = 'line-height:normal;' btn.addEventListener('click', function () { removeActiveRef(); document.querySelector('#ref-text').innerHTML = state.md.render(state.references[title]); this.classList.add('active'); note.findRefLinks(); }); btn.addEventListener('dblclick', function () { document.querySelector('#ref-text').innerHTML = ''; delete state.references[title]; var list = this.parentElement; list.remove(); state.saved = false; state.textDirty = true; note.saveNote(); }); removeActiveRef(); btn.classList.add('active'); return btn; } /** * Removes the 'active' class from all elements with the class 'active'. * * @return {void} This function does not return a value. */ export function removeActiveRef() { tabs = document.querySelectorAll('#ref-list .active'); for (var t in tabs) { if (isFinite(parseInt(t))) { tabs[t].classList.remove('active'); } } } /** * Generates a random UUIDv4 string. * * @return {string} The generated UUIDv4 string. */ export function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' .replace(/[xy]/g, function (c) { const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } /** * Searches for notes based on the query entered in the search field. * Sends a POST request to the '/search' endpoint with the query as a JSON payload. * Updates the '#old-notes' element with the search results. * * @return {Promise} A Promise that resolves with the search results. */ export function search() { query = document.querySelector('#query').value; fetch('/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ 'query': query }) }) .then(response => response.json()) .then(results => { var oldNotes = document.querySelector('#old-notes'); oldNotes.innerHTML = ''; for (var n in results) { var link = document.createElement('a'); link.href = '#'; link.setAttribute('onclick', "note.retrieveNote('" + results[n].id + "');note.openNote();"); link.innerHTML = results[n].title; var p = document.createElement('p'); p.innerHTML = results[n].passage; var article = document.createElement('article'); article.appendChild(link); article.appendChild(p); oldNotes.append(article); } }); } /** * Closes the passage popup by clearing its content and hiding it. * * @return {void} This function does not return anything. */ export function closePopup() { const popup = document.querySelector('#passage-popup'); popup.innerHTML = ''; popup.style.display = 'none'; }