import { state } from './state.js'; import { toggleFields } from './home.js'; /** * Toggles the visibility of the note list and reference elements. * * @param {boolean} [openSidebar=true] - Whether to open the sidebar after toggling the visibility. * @return {void} */ export function openNote(openSidebar = true) { const noteList = document.querySelector('#note-list'); const refs = document.querySelector('#ref'); if (noteList.style.display == 'block') { noteList.style.display = 'none'; refs.style.display = 'block'; } else { noteList.style.display = 'block'; refs.style.display = 'none'; } if (openSidebar) { document.querySelector('.toggle').click(); } } /** * Starts the save process by updating the save-check element's classList, removing error, fa-times-circle, and fa-save classes, * adding the 'saving' and 'fa-save' classes, and setting the opacity to 1. * * @return {void} This function does not return anything. */ export function startSave() { document.querySelector('#save-check').classList.remove('error', 'fa-times-circle', 'fa-save'); document.querySelector('#save-check').classList.add('saving', 'fa-save'); document.querySelector('#save-check').style.opacity = 1; } /** * Displays a checkmark animation on the screen. * * @param {none} - This function does not take any parameters. * @return {none} - This function does not return any value. */ export function showSave() { if (state.saved) { return; } var checkmark = document.getElementById("save-check"); checkmark.classList.add('fa-save'); // Schedule the animation to run every 1 second (which is equivalent to a 1-second delay between each iteration) var si = setInterval(function () { // Increment the opacity of the checkmark by 0.01 each time op = parseFloat(checkmark.style.opacity); checkmark.style.opacity = op - 0.1; // If the opacity is greater than or equal to 1, reset it back to 0 and stop the animation if (checkmark.style.opacity == 0.1) { checkmark.style.opacity = 0; clearInterval(si); state.saved = false; } }, 100); } /** * function to discard the note by clearing all input fields and closing the menu. */ export function deleteNote(noteId, link) { document.querySelector('#noteTitle').value = ''; document.querySelector('#speaker').value = 0; document.querySelector('#series').value = 0; document.querySelector('#template').value = 0; document.querySelector('#passage').value = ''; document.querySelector('#notes').value = ''; document.querySelector('#recording').value = ''; document.querySelector('#noteDate').value = ''; document.querySelector('#noteId').value = ''; var row = link.parentElement.parentElement; fetch('/delete-note', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ 'id': noteId }) }) .then(response => response.json()) .then(data => { if (data.msg != 'deleted') { return; } alert('Note deleted.'); row.remove(); }); } /** * Validates a note by checking if all required fields are filled. * * @return {boolean} Returns true if all required fields are filled, false otherwise. */ export function validateNote() { const note = document.querySelector('#notes'); const date = document.querySelector('#noteDate'); const speaker = document.querySelector('#speaker'); const series = document.querySelector('#series'); const title = document.querySelector('#noteTitle'); const psg = document.querySelector('#passage'); let ret = true; if (!title.value.length) { title.classList.add('input-error'); ret = false; } if (!date.value) { date.classList.add('input-error'); ret = false; } if (!parseInt(speaker.value)) { speaker.classList.add('input-error'); ret = false; } if (!parseInt(series.value)) { series.classList.add('input-error'); ret = false; } if (!psg.value) { psg.classList.add('input-error'); ret = false; } if (!note.value.length) { note.classList.add('input-error'); ret = false; } if (!ret) { toggleFields(null, true); } return ret; } /** * Resets the state of the note editor by clearing the text and form fields, * resetting the references, and removing any dirty classes. It also sets the * date to the current date, clears the speaker, series, template, passage, * recording, and note ID fields. Finally, it clears the reference list and * reference display. * * @return {void} This function does not return anything. */ export function newNote() { notes = document.querySelector('#notes'); notes.text = ''; notes.value = ''; state.references = {}; state.saved = true; state.textDirty = false; document.querySelector('#note-header-left h2').classList.remove('dirty'); dt = new Date(); document.querySelector('#noteDate').value = dt.getFullYear() + '-' + (dt.getMonth() < 9 ? '0' + (dt.getMonth() + 1) : (dt.getMonth() + 1)) + '-' + (dt.getDate() < 10 ? '0' + dt.getDate() : dt.getDate()); document.querySelector('#noteTitle').value = ''; document.querySelector('#speaker').value = 0; document.querySelector('#series').value = 0; document.querySelector('#template').value = 0; document.querySelector('#passage').value = ''; document.querySelector('#recording').value = ''; document.querySelector('#noteId').value = ''; document.querySelector('#ref-list').innerHTML = ''; document.querySelector('#ref').innerHTML = ''; document.querySelector('.toggle').click(); } /** * Save a note by sending it to the server for storage. * * @param {Event} event - The event object triggering the save action. * @return {void} No explicit return value. */ export function saveNote(event) { console.debug('called saveNote '+new Date()); if (event) { event.preventDefault(); } if (!state.textDirty) { clearTimeout(state.to); state.to = setTimeout(saveNote, state.saveInterval); return; } document.querySelector('#noteTitle').classList.remove('input-error'); document.querySelector('#noteDate').classList.remove('input-error'); document.querySelector('#speaker').classList.remove('input-error'); document.querySelector('#series').classList.remove('input-error'); document.querySelector('#passage').classList.remove('input-error'); document.querySelector('#notes').classList.remove('input-error'); if (!validateNote()) { clearTimeout(state.to); state.to = setTimeout(saveNote, state.saveInterval); return; } let saveCheck = document.querySelector('#save-check'); var noteText = document.querySelector('#notes').value; startSave(); var note = { id: document.querySelector("#noteId").value, date: document.querySelector('#noteDate').value, title: document.querySelector('#noteTitle').value, speaker: document.querySelector('#speaker').value, series: document.querySelector('#series').value, passage: document.querySelector('#passage').value, note: document.querySelector('#notes').value, recording: document.querySelector('#recording').value, refs: references }; $.ajax({ url: '/save-note', method: 'POST', contentType: 'application/json', data: JSON.stringify(note), dataType: 'json', timeout: state.saveTimeout }) .done(function (data) { if (data.msg == 'saved' && !state.saved) { state.saveFailureCount = SAVE_FAILURE_LIMIT; saveCheck.classList.remove('saving', 'error', 'fa-times-circle', 'fa-save'); showSave(); if (noteText == document.querySelector('#notes').value) { state.saved = true; state.textDirty = false; document.querySelector('note-header h2').classList.remove('dirty'); document.querySelector('mobile-note-header h2').classList.remove('dirty'); } if (data.new) { document.querySelector('#noteId').value = data.id; } } }) .fail(function (xhr, status, error) { state.saveFailureCount--; saveCheck.classList.remove('saving', 'fa-save'); saveCheck.classList.add('fa-times-circle', 'error'); console.error(error); }) .always(function (xhr, status) { if (status == 'timeout') { saveCheck.classList.remove('saving', 'fa-save'); saveCheck.classList.add('error', 'fa-times-circle'); } clearTimeout(to); if (state.saveFailureCount > 0) { state.to = setTimeout(saveNote, state.saveInterval); } else { state.saveFailureCount = SAVE_FAILURE_LIMIT; } }); } /** * Function that finds reference links and fetches passage data when clicked. */ export function findRefLinks() { var links = document.querySelector('#ref-text').querySelectorAll('a'); for (var i = 0; i < links.length; i++) { links[i].addEventListener('click', function (e) { e.preventDefault(); if (!this.href.includes('get-passage')) { return; } var passage = this.href.split('/'); passage = passage[passage.length - 1]; fetch(this.href, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ 'passage': passage }) }) .then(response => response.text()) .then(result => { passage = passage.replace(/\+/g, ' '); psg = passage.split(' '); if (psg.length > 2) { book = psg[0] + ' ' + psg[1]; cv = psg[2]; } else { book = psg[0]; cv = psg[1]; } showPassage( e, "  " + "
" + result); }); }); } } /** * Finds all links in the note preview and adds event listeners to them. * * @return {void} */ export function findLinks() { var links = document.querySelector('#notePreview').querySelectorAll('a'); for (var i = 0; i < links.length; i++) { links[i].addEventListener('click', function (e) { e.preventDefault(); if (!this.href.includes('get-passage')) { return; } var passage = this.href.split('/'); passage = passage[passage.length - 1]; fetch(this.href, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ 'passage': passage }) }) .then(response => response.text()) .then(result => { passage = passage.replace(/\+/g, ' '); psg = passage.split(' '); if (psg.length > 2) { book = psg[0] + ' ' + psg[1]; cv = psg[2]; } else { book = psg[0]; cv = psg[1]; } showPassage( e, "  " + "
" + result); }); }); } } /** * Opens the share note functionality. */ export function openShareNote() { var id = document.querySelector('#noteId').value; if (!id) { alert('No Open Note Found'); return; } bd = document.querySelector('#modal-backdrop'); bd.style.display = 'block'; cont = document.querySelector('#modal-container'); cont.style.display = bd.style.display; emailCont = document.querySelector('#modal-container'); emailCont.style.left = ((window.innerWidth / 2) - (emailCont.clientWidth / 2)) + 'px'; emailCont.style.top = ((window.innerHeight / 2) - (emailCont.clientHeight / 2)) + 'px'; } /** * Closes the share note modal by hiding the backdrop and container, * and clears the email input value. */ export function closeShareNote() { var bd = document.querySelector('#modal-backdrop'); var cont = document.querySelector('#modal-container'); bd.style.display = 'none'; cont.style.display = 'none'; document.querySelector('#shareEmail').value = ''; } /** * Function to share a note by sending the note ID and email to the server. */ export function shareNote(event) { var id = document.querySelector('#noteId').value; var email = document.querySelector('#shareEmail').value; if (!id || !email) { alert('Invalid Input'); return; } fetch('/share-note', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ 'id': id, 'email': email }) }) .then(response => response.json()) .then(result => { if (result) { alert(result.msg); } }); closeShareNote(); }