1240 lines
40 KiB
JavaScript
1240 lines
40 KiB
JavaScript
// Get the link element
|
|
var md = null;
|
|
var references = {};
|
|
var tabs = [];
|
|
let saved = false;
|
|
let textDirty = false;
|
|
var to = null;
|
|
let controller;
|
|
var BOOKS = {};
|
|
|
|
$(function () {
|
|
setHeight();
|
|
setBooks();
|
|
setEventListeners();
|
|
$('#note-table').DataTable({
|
|
paging: false,
|
|
ajax: {
|
|
url: '/index.php/get-notes',
|
|
type: 'POST'
|
|
},
|
|
columns: [
|
|
{ data: 'link' },
|
|
{ data: 'speaker.name' },
|
|
{ data: 'passage' },
|
|
{
|
|
data: 'date.date',
|
|
render: DataTable.render.date("L")
|
|
},
|
|
]
|
|
});
|
|
$('#shareBtn').on('click', openShareNote);
|
|
$('#modal-backdrop').on('click', closeShareNote);
|
|
});
|
|
|
|
/**
|
|
* Fetches data from '/js/data.json', assigns it to BOOKS, and handles errors.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
function setBooks() {
|
|
fetch('/js/data.json')
|
|
.then((res) => {
|
|
if (!res.ok) {
|
|
throw new Error('HTTP Error: Status: ${res.status}');
|
|
}
|
|
return res.json();
|
|
})
|
|
.then((data) => {
|
|
BOOKS = data;
|
|
})
|
|
.catch((error) => {
|
|
console.log(error);
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Sets event listeners for keyup events on the document and the '#notes' element.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
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 >= 48 && key <= 90 || key >= 96 && key <= 111 || key >= 186 && key <= 222) {
|
|
textDirty = true;
|
|
document.querySelector('#note-header-left h2').classList.add('dirty');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sets the height of various elements on the page based on the window's inner height.
|
|
* Also initializes a datepicker and event listener for the search input field.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
function setHeight() {
|
|
md = new markdownit({
|
|
html: true,
|
|
linkify: true,
|
|
breaks: true
|
|
});
|
|
|
|
body = document.querySelector('body');
|
|
body.style.height = window.innerHeight + 'px';
|
|
|
|
cont = document.querySelector('#main');
|
|
cont.style.height = (window.innerHeight) + 'px';
|
|
|
|
tabs = document.querySelector('.ref-tab');
|
|
tabs.style.height = (window.innerHeight - 13) + 'px';
|
|
|
|
ref = document.querySelector('.ref');
|
|
ref.style.height = (window.innerHeight - 50) + 'px';
|
|
|
|
noteList = document.querySelector('#note-list');
|
|
noteList.style.height = (window.innerHeight - 50) + 'px';
|
|
|
|
notes = document.querySelector('.notes');
|
|
notes.style.height = (window.innerHeight - 50) + 'px';
|
|
|
|
notePreview = document.querySelector('#notePreview');
|
|
notePreview.style.height = (window.innerHeight - 50) + 'px';
|
|
|
|
if ($('#noteDate')) {
|
|
$('#noteDate').datepicker();
|
|
}
|
|
|
|
if ($('#query')) {
|
|
document.querySelector('#query').addEventListener('keyup', function (event) {
|
|
if (event.key == "Enter") {
|
|
search();
|
|
}
|
|
});
|
|
}
|
|
if (!to) {
|
|
to = setTimeout(saveNote, saveInterval);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Searches for notes based on the query entered in the search field.
|
|
* Sends a POST request to the '/index.php/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.
|
|
*/
|
|
function search() {
|
|
query = document.querySelector('#query').value;
|
|
fetch('/index.php/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', "retrieveNote('" + results[n].id + "');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);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
function newNote() {
|
|
notes = document.querySelector('#notes');
|
|
notes.text = '';
|
|
notes.value = '';
|
|
references = {};
|
|
saved = true;
|
|
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.
|
|
*/
|
|
function saveNote(event) {
|
|
if (event) {
|
|
event.preventDefault();
|
|
}
|
|
|
|
if (!textDirty || !validateNote()) {
|
|
clearTimeout(to);
|
|
to = setTimeout(saveNote, 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: '/index.php/save-note',
|
|
method: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(note),
|
|
dataType: 'json',
|
|
timeout: saveTimeout
|
|
})
|
|
.done(function (data) {
|
|
if (data.msg == 'saved' && !saved) {
|
|
saveFailureCount = SAVE_FAILURE_LIMIT;
|
|
saveCheck.classList.remove('saving', 'error', 'fa-times-circle', 'fa-save');
|
|
showSave();
|
|
if (noteText == document.querySelector('#notes').value) {
|
|
saved = true;
|
|
textDirty = false;
|
|
document.querySelector('#note-header-left h2').classList.remove('dirty');
|
|
}
|
|
|
|
if (data.new) {
|
|
document.querySelector('#noteId').value = data.id;
|
|
}
|
|
}
|
|
})
|
|
.fail(function (xhr, status, error) {
|
|
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 (saveFailureCount > 0) {
|
|
to = setTimeout(saveNote, saveInterval);
|
|
} else {
|
|
saveFailureCount = SAVE_FAILURE_LIMIT;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Validates a note by checking if all required fields are filled.
|
|
*
|
|
* @return {boolean} Returns true if all required fields are filled, false otherwise.
|
|
*/
|
|
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');
|
|
|
|
if (!title.value.length) { return false; }
|
|
if (!date.value) { return false; }
|
|
if (!parseInt(speaker.value)) { return false; }
|
|
if (!parseInt(series.value)) { return false; }
|
|
if (!psg.value) { return false; }
|
|
if (!note.value.length) { return false; }
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks if a given UUID is valid.
|
|
*
|
|
* @param {string} uuid - The UUID to be validated.
|
|
* @return {boolean} Returns true if the UUID is valid, false otherwise.
|
|
*/
|
|
function isUuidValid(uuid) {
|
|
const regex = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[8|9|a|b][a-f0-9]{3}-[a-f0-9]{12}$/i;
|
|
return regex.test(uuid);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
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.
|
|
*/
|
|
function showSave() {
|
|
if (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);
|
|
saved = false;
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
/**
|
|
* Function to discard the note by clearing all input fields and closing the menu.
|
|
*/
|
|
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('/index.php/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();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Toggles the display of the new speaker input field and hides the speaker select field.
|
|
*
|
|
* @return {void} This function does not return anything.
|
|
*/
|
|
function newSpeaker() {
|
|
if (document.querySelector('#speaker').value == 'new') {
|
|
document.querySelector('#newSpeaker').style.display = 'inline-block';
|
|
document.querySelector('#speaker').style.display = 'none';
|
|
}
|
|
|
|
saved = false;
|
|
textDirty = true;
|
|
}
|
|
|
|
/**
|
|
* Saves a new speaker to the database and updates the UI with the new speaker option.
|
|
*
|
|
* @param {Event} event - The keydown event triggered by the user.
|
|
* @return {Promise} A Promise that resolves with the results of the fetch request.
|
|
*/
|
|
function saveSpeaker(event) {
|
|
if (event.keyCode == 13) {
|
|
fetch('/index.php/save-speaker', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
'speakerName': document.querySelector('#newSpeaker').value
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(results => {
|
|
var newSpeaker = document.createElement('option');
|
|
newSpeaker.text = document.querySelector('#newSpeaker').value;
|
|
newSpeaker.value = results.id;
|
|
document.querySelector('#speaker').add(newSpeaker);
|
|
|
|
alert(results.msg);
|
|
document.querySelector('#newSpeaker').style.display = 'none';
|
|
document.querySelector('#speaker').style.display = 'inline-block';
|
|
|
|
document.querySelector('#newSpeaker').value = '';
|
|
document.querySelector('#speaker').value = results.id;
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A description of the entire function.
|
|
*
|
|
*/
|
|
function newSeries() {
|
|
if (document.querySelector('#series').value == 'new') {
|
|
document.querySelector('#newSeries').style.display = 'inline-block';
|
|
document.querySelector('#series').style.display = 'none';
|
|
}
|
|
|
|
saved = false;
|
|
textDirty = true;
|
|
}
|
|
|
|
/**
|
|
* Saves a series by making a POST request to '/index.php/save-series' with the series name as the request body.
|
|
*
|
|
* @param {Event} event - The keydown event.
|
|
* @return {Promise} A Promise that resolves with the response from the server.
|
|
*/
|
|
function saveSeries(event) {
|
|
if (event.keyCode == 13) {
|
|
fetch('/index.php/save-series', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
'seriesName': document.querySelector('#newSeries').value
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(results => {
|
|
var newSeries = document.createElement('option');
|
|
newSeries.text = document.querySelector('#newSeries').value;
|
|
newSeries.value = results.id;
|
|
document.querySelector('#series').add(newSeries);
|
|
|
|
alert(results.msg);
|
|
document.querySelector('#newSeries').style.display = 'none';
|
|
document.querySelector('#series').style.display = 'inline-block';
|
|
|
|
document.querySelector('#newSeries').value = '';
|
|
document.querySelector('#series').value = results.id;
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Opens the reference with the option to close the sidebar.
|
|
*
|
|
* @param {boolean} closeSidebar - Indicates whether to close the sidebar when opening the reference.
|
|
*/
|
|
function openRef(closeSidebar = true) {
|
|
document.querySelector('#openRefBtn').classList.add('active');
|
|
|
|
refQuery = document.querySelector('#refQuery');
|
|
refQuery.style.display = 'block';
|
|
|
|
ref = document.querySelector('#ref');
|
|
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.
|
|
*/
|
|
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('#openRefBtn').classList.remove('active');
|
|
}
|
|
|
|
/**
|
|
* Fetches a reference based on the provided type, book, and input.
|
|
*
|
|
* @param {string} type - The type of reference.
|
|
* @param {string} book - The book of the reference.
|
|
* @param {string} input - The input for the reference.
|
|
* @return {void} This function does not return anything directly, but processes the fetched reference data.
|
|
*/
|
|
function queryRef(type = null, book = null, input = null) {
|
|
if (!input) {
|
|
var input = document.querySelector('#refQuery #referenceSearch').value;
|
|
}
|
|
if (!type) {
|
|
var type = document.querySelector('#referenceType').value;
|
|
}
|
|
if (!book) {
|
|
var book = document.querySelector('#referenceBook').value;
|
|
}
|
|
fetch('/index.php/retrieve-reference', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
'type': type,
|
|
'book': book,
|
|
'reference': input,
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(results => {
|
|
const list = document.querySelector('#ref-list');
|
|
var newList = document.createElement('li');
|
|
newList.className = 'tab';
|
|
button = makeButton(results.title);
|
|
newList.appendChild(button);
|
|
list.appendChild(newList);
|
|
|
|
const ref = document.querySelector('#ref');
|
|
ref.innerHTML = md.render(results.text);
|
|
|
|
references[results.title] = results.text;
|
|
|
|
closeRef();
|
|
|
|
saved = false;
|
|
textDirty = true;
|
|
saveNote();
|
|
findRefLinks();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
function makeButton(title) {
|
|
var btn = document.createElement('button');
|
|
btn.innerText = title;
|
|
btn.class = 'button';
|
|
btn.addEventListener('click', function () {
|
|
removeActiveRef();
|
|
document.querySelector('#ref').innerHTML = md.render(references[title]);
|
|
this.classList.add('active');
|
|
findRefLinks();
|
|
});
|
|
|
|
btn.addEventListener('dblclick', function () {
|
|
document.querySelector('#ref').innerHTML = '';
|
|
delete references[title];
|
|
var list = this.parentElement;
|
|
list.remove();
|
|
saved = false;
|
|
textDirty = true;
|
|
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.
|
|
*/
|
|
function removeActiveRef() {
|
|
tabs = document.querySelectorAll('.active');
|
|
for (var t in tabs) {
|
|
if (isFinite(parseInt(t))) {
|
|
tabs[t].classList.remove('active');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves a template from the server and sets it as the value of a specified destination element.
|
|
*
|
|
* @param {string} orig - The ID of the element containing the original template value.
|
|
* @param {string} dest - The ID of the destination element where the retrieved template will be set.
|
|
* @return {Promise} A Promise that resolves when the template is successfully retrieved and set as the value of the destination element.
|
|
*/
|
|
function retrieveTemplate(orig, dest) {
|
|
const temp = document.querySelector('#' + orig);
|
|
if (temp.value == '0') {
|
|
document.querySelector('#' + dest).value = '';
|
|
return;
|
|
}
|
|
fetch('/index.php/retrieve-template', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'plain/text'
|
|
},
|
|
body: JSON.stringify({
|
|
'template': temp.value
|
|
})
|
|
})
|
|
.then(response => response.text())
|
|
.then(results => {
|
|
const div = document.querySelector('#' + dest);
|
|
div.value = results;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Saves the template by sending a POST request to the server with template data.
|
|
*/
|
|
function saveTemplate() {
|
|
fetch('/index.php/save-template', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'plain/text'
|
|
},
|
|
body: JSON.stringify({
|
|
'template_id': document.querySelector('#template_id').value,
|
|
'template_name': document.querySelector('#template_name').value,
|
|
'template_value': document.querySelector('#template_value').value,
|
|
})
|
|
})
|
|
.then(response => response.text())
|
|
.then(results => {
|
|
alert(results);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Toggles the visibility of the fields container and updates the active state of the show/hide button.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
function toggleFields() {
|
|
const fieldsContainer = document.getElementById('fields-container');
|
|
const showHideBtn = document.getElementById('show-hide-btn');
|
|
|
|
if (fieldsContainer.classList.contains('show')) {
|
|
fieldsContainer.classList.remove('show');
|
|
fieldsContainer.style.display = 'none';
|
|
showHideBtn.classList.remove('active');
|
|
} else {
|
|
fieldsContainer.classList.add('show');
|
|
fieldsContainer.style.display = 'block';
|
|
showHideBtn.classList.add('active');
|
|
}
|
|
|
|
setHeight();
|
|
}
|
|
|
|
/**
|
|
* Retrieves the list of books based on the selected reference type.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
function retrieveBooks() {
|
|
document.querySelector('#chapter-range').innerText = '';
|
|
document.querySelector('#verse-range').innerText = '';
|
|
document.querySelector('#referenceSearch').value = '';
|
|
document.querySelector('#referenceSearch').style.display = 'none';
|
|
const selectedType = document.querySelector('#referenceType').value;
|
|
if (!selectedType) { return; }
|
|
|
|
var bookList = document.querySelector('#referenceBook');
|
|
bookList.style.display = "block";
|
|
bookList.innerHTML = '';
|
|
if (selectedType == 'bible') {
|
|
document.querySelector('#referenceSearch').style.display = 'block';
|
|
var none = document.createElement("option");
|
|
none.value = '';
|
|
none.text = '-- Select --';
|
|
bookList.appendChild(none);
|
|
for (var x in BOOKS.bible) {
|
|
var newBook = document.createElement("option");
|
|
newBook.text = x;
|
|
bookList.appendChild(newBook);
|
|
}
|
|
} else if (selectedType == 'creed') {
|
|
var none = document.createElement('option');
|
|
none.value = '';
|
|
none.text = '-- Select --';
|
|
bookList.appendChild(none);
|
|
for (var x in BOOKS.creed) {
|
|
var newBook = document.createElement('option');
|
|
newBook.value = x;
|
|
newBook.text = BOOKS.creed[x];
|
|
bookList.appendChild(newBook);
|
|
}
|
|
} else if (selectedType == 'cd') {
|
|
var none = document.createElement("option");
|
|
none.value = '';
|
|
none.text = '-- Select --';
|
|
bookList.appendChild(none);
|
|
for (var x in BOOKS.cd) {
|
|
var newBook = document.createElement("option");
|
|
newBook.text = BOOKS.cd[x];
|
|
bookList.appendChild(newBook);
|
|
}
|
|
} else if (selectedType == 'note') {
|
|
var none = document.createElement("option");
|
|
none.value = '';
|
|
none.text = '-- Select --';
|
|
bookList.appendChild(none);
|
|
|
|
fetch('/index.php/retrieve-reference', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
'type': 'note'
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(results => {
|
|
for (var x in results) {
|
|
var newBook = document.createElement("option");
|
|
newBook.value = results[x].id;
|
|
newBook.text = results[x].title;
|
|
bookList.appendChild(newBook);
|
|
}
|
|
});
|
|
} else {
|
|
var min = BOOKS[selectedType][0];
|
|
var max = BOOKS[selectedType][1];
|
|
var none = document.createElement("option");
|
|
none.value = '';
|
|
none.text = '-- Select --';
|
|
bookList.appendChild(none);
|
|
for (var x = min; x <= max; x++) {
|
|
var newBook = document.createElement("option");
|
|
newBook.value = x;
|
|
newBook.text = x;
|
|
bookList.appendChild(newBook);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filters the books based on the selected reference type and updates the chapter range.
|
|
*
|
|
* @return {void} This function does not return anything.
|
|
*/
|
|
function filterBooks() {
|
|
document.querySelector('#chapter-range').innerText = '';
|
|
document.querySelector('#verse-range').innerText = '';
|
|
if (document.querySelector('#referenceType').value != 'bible') {
|
|
return;
|
|
}
|
|
|
|
var bookList = document.querySelector('#referenceBook');
|
|
var book = BOOKS.bible[bookList.value];
|
|
var max = Object.keys(book).length;
|
|
|
|
var chapterRange = document.querySelector('#chapter-range');
|
|
chapterRange.innerText = 'Chapters: ' + max;
|
|
}
|
|
|
|
/**
|
|
* Filters the verse based on the selected book and chapter.
|
|
*
|
|
* @return {void} This function does not return anything.
|
|
*/
|
|
function filterVerse() {
|
|
if (document.querySelector('#referenceType').value != 'bible') {
|
|
return;
|
|
}
|
|
|
|
var bookList = document.querySelector('#referenceBook').value;
|
|
var search = document.querySelector('#referenceSearch').value;
|
|
var chapter = search.split(':')[0];
|
|
var verseRange = document.querySelector('#verse-range');
|
|
|
|
if (!BOOKS.bible[bookList] || !BOOKS.bible[bookList][chapter]) {
|
|
verseRange.innerText = 'Unknown Chapter';
|
|
return;
|
|
}
|
|
var verse = BOOKS.bible[bookList][chapter];
|
|
verseRange.innerText = 'Verse: ' + verse;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the reference type from the server and populates the reference series dropdown.
|
|
*
|
|
* @param {HTMLElement} el - The element that triggered the function.
|
|
* @return {Promise} A promise that resolves with the response from the server.
|
|
*/
|
|
function retrieveReferenceType(el) {
|
|
fetch('/index.php/reference/' + el.value, {
|
|
method: 'GET',
|
|
header: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(results => {
|
|
document.querySelector('#referenceSeries').innerHTML = '';
|
|
var none = document.createElement('option');
|
|
none.value = '';
|
|
none.text = '-- Select --';
|
|
document.querySelector('#referenceSeries').appendChild(none);
|
|
|
|
for (var x in results) {
|
|
var newSeries = document.createElement('option');
|
|
newSeries.value = results[x].id;
|
|
newSeries.text = results[x].label;
|
|
document.querySelector('#referenceSeries').appendChild(newSeries);
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Retrieves a reference based on the provided element value.
|
|
*
|
|
* @param {Element} el - The element triggering the reference retrieval
|
|
* @return {void} No return value
|
|
*/
|
|
function retrieveReference(el) {
|
|
if (el.value == 'new') {
|
|
document.querySelector('#refName').style.display = 'inline-block';
|
|
return;
|
|
}
|
|
fetch('/index.php/get-reference', {
|
|
method: "POST",
|
|
header: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: JSON.stringify({
|
|
id: el.value
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(results => {
|
|
document.querySelector('#reference').value = results.text;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Saves a reference by sending a POST request to the server with the selected type,
|
|
* file, and text values. Displays an alert with the response message, and clears
|
|
* the reference and file input fields.
|
|
*
|
|
* @return {Promise} A Promise that resolves with the response message from the server.
|
|
*/
|
|
function saveReference() {
|
|
var select = document.querySelector('#references');
|
|
fetch('/index.php/save-reference', {
|
|
method: 'POST',
|
|
header: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
type: select.options[select.selectedIndex].getAttribute('type'),
|
|
file: select.value,
|
|
text: document.querySelector('#reference').value
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(results => {
|
|
alert(results.msg);
|
|
|
|
document.querySelector('#reference').value = '';
|
|
document.querySelector('#references').value = '';
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
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 = md.render(markdownPreview);
|
|
|
|
if (previewButton.classList.contains('active')) {
|
|
noteText.style.display = 'block';
|
|
notePreview.style.display = 'none';
|
|
previewButton.classList.remove('active');
|
|
} else {
|
|
noteText.style.display = 'none';
|
|
notePreview.style.display = 'block';
|
|
previewButton.classList.add('active');
|
|
}
|
|
|
|
findLinks();
|
|
}
|
|
|
|
/**
|
|
* Finds all links in the note preview and adds event listeners to them.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
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,
|
|
"<button onclick='closePopup()'>Close</button> " +
|
|
"<button onclick=\"queryRef('bible', '" + book + "', '" + cv + "')\">Open Ref</button><br/>" +
|
|
result);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function that finds reference links and fetches passage data when clicked.
|
|
*/
|
|
function findRefLinks() {
|
|
var links = document.querySelector('#ref').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,
|
|
"<button onclick='closePopup()'>Close</button> " +
|
|
"<button onclick=\"queryRef('bible', '" + book + "', '" + cv + "')\">Open Ref</button><br/>" +
|
|
result);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shows a passage in a popup element relative to the cursor position.
|
|
*
|
|
* @param {Event} event - The event that triggered the function.
|
|
* @param {string} text - The text to be displayed in the popup.
|
|
* @return {void} This function does not return a value.
|
|
*/
|
|
function showPassage(event, text) {
|
|
// Create a new div element for the popup
|
|
const popup = document.querySelector('#passage-popup');
|
|
popup.innerHTML = md.render(text);
|
|
|
|
// Position the popup relative to the cursor
|
|
let x = event.clientX + window.scrollX;
|
|
let y = event.clientY + window.scrollY;
|
|
|
|
// Set the position of the popup element
|
|
popup.style.top = `${y}px`;
|
|
popup.style.left = `${x}px`;
|
|
popup.style.display = 'block';
|
|
}
|
|
|
|
/**
|
|
* Closes the passage popup by clearing its content and hiding it.
|
|
*
|
|
* @return {void} This function does not return anything.
|
|
*/
|
|
function closePopup() {
|
|
const popup = document.querySelector('#passage-popup');
|
|
popup.innerHTML = '';
|
|
popup.style.display = 'none';
|
|
}
|
|
|
|
/**
|
|
* 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}
|
|
*/
|
|
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();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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<void>} A promise that resolves when the note is successfully retrieved and the UI is updated.
|
|
*/
|
|
function retrieveNote(id, runOpen = true) {
|
|
fetch('/index.php/get-note', {
|
|
method: 'POST',
|
|
header: {
|
|
'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) {
|
|
openNote(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Opens the share note functionality.
|
|
*/
|
|
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.
|
|
*/
|
|
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.
|
|
*/
|
|
function shareNote(event) {
|
|
var id = document.querySelector('#noteId').value;
|
|
var email = document.querySelector('#shareEmail').value;
|
|
if (!id || !email) {
|
|
alert('Invalid Input');
|
|
return;
|
|
}
|
|
|
|
fetch('/index.php/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();
|
|
}
|
|
|
|
/**
|
|
* Increases the font size of the element with the id 'ref' by 1 point.
|
|
*
|
|
* @return {void} This function does not return a value.
|
|
*/
|
|
function increaseFont() {
|
|
var currentSize = document.querySelector('#ref').style.fontSize;
|
|
document.querySelector('#ref').style.fontSize = (parseInt(currentSize) + 1) + 'pt';
|
|
}
|
|
|
|
/**
|
|
* Decreases the font size of the element with the id 'ref' by 1 point.
|
|
*
|
|
* @return {void} This function does not return a value.
|
|
*/
|
|
function decreaseFont() {
|
|
var currentSize = document.querySelector('#ref').style.fontSize;
|
|
document.querySelector('#ref').style.fontSize = (parseInt(currentSize) - 1) + 'pt';
|
|
}
|
|
|
|
/**
|
|
* Generates a random UUIDv4 string.
|
|
*
|
|
* @return {string} The generated UUIDv4 string.
|
|
*/
|
|
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);
|
|
});
|
|
} |